From 9b512f565f9f8c3fe4c3f52d45a1b4a11c71eb2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20FRANCE?= Date: Tue, 28 Oct 2025 00:57:01 +0100 Subject: [PATCH 01/16] try to make new checks (#35976) * try to make new checks * try to make new checks * try to make new checks * try to make new checks * try to make new checks * try to make new checks --- dev/build/phpstan/phpstan-baseline.neon | 8 +++++++- htdocs/commande/list.php | 2 +- htdocs/compta/facture/list.php | 2 ++ htdocs/core/lib/functions.lib.php | 1 + htdocs/expedition/list.php | 2 +- htdocs/fourn/commande/list.php | 8 ++++---- htdocs/reception/list.php | 4 ++-- htdocs/website/index.php | 4 ++-- htdocs/workstation/class/workstation.class.php | 4 ++-- 9 files changed, 22 insertions(+), 13 deletions(-) diff --git a/dev/build/phpstan/phpstan-baseline.neon b/dev/build/phpstan/phpstan-baseline.neon index 3f2c62aaa71..115e70be9ea 100644 --- a/dev/build/phpstan/phpstan-baseline.neon +++ b/dev/build/phpstan/phpstan-baseline.neon @@ -2328,6 +2328,12 @@ parameters: count: 1 path: ../../../htdocs/commande/customer.php + - + message: '#^Call to function is_array\(\) with array\ will always evaluate to true\.$#' + identifier: function.alreadyNarrowedType + count: 1 + path: ../../../htdocs/commande/list.php + - message: '#^Call to function method_exists\(\) with Commande and ''fetch_lines'' will always evaluate to true\.$#' identifier: function.alreadyNarrowedType @@ -11055,7 +11061,7 @@ parameters: - message: '#^Ternary operator condition is always true\.$#' identifier: ternary.alwaysTrue - count: 33 + count: 32 path: ../../../htdocs/main.inc.php - diff --git a/htdocs/commande/list.php b/htdocs/commande/list.php index d15c61bc4e8..b511f8f1124 100644 --- a/htdocs/commande/list.php +++ b/htdocs/commande/list.php @@ -465,7 +465,7 @@ if (empty($reshook)) { $objecttmp->date = $datefacture; $objecttmp->origin = 'commande'; - $objecttmp->origin_id = $id_order; + $objecttmp->origin_id = (int) $id_order; $objecttmp->array_options = $cmd->array_options; // Copy extrafields diff --git a/htdocs/compta/facture/list.php b/htdocs/compta/facture/list.php index 55623aee0fe..0958bb07dfc 100644 --- a/htdocs/compta/facture/list.php +++ b/htdocs/compta/facture/list.php @@ -176,6 +176,8 @@ if (GETPOSTISSET('formfilteraction')) { $searchCategoryInvoiceOperator = getDolGlobalString('MAIN_SEARCH_CAT_OR_BY_DEFAULT'); } $searchCategoryInvoiceList = GETPOST('search_category_invoice_list', 'array:int'); +// to dump type seen by phpstan during analyse +// \PHPStan\dumpType($searchCategoryInvoiceList); $search_product_category = GETPOST('search_product_category', 'intcomma'); $search_fac_rec_source_title = GETPOST("search_fac_rec_source_title", 'alpha'); $search_fk_fac_rec_source = GETPOST('search_fk_fac_rec_source', 'int'); diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index 8abcab0464e..54de5dbe9e9 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -845,6 +845,7 @@ function GETPOSTISARRAY($paramname, $method = 0) * @param mixed $options Options to pass to filter_var when $check is set to 'custom' * @param int $noreplace Force disable of replacement of __xxx__ strings. * @return string|array Value found (string or array), or '' if check fails + * @phpstan-return ($check is 'array:int' ? numeric-string[]|array{} : ($check is 'array:az09' ? string[] : ($check is 'array:restricthtml' ? string[] : string|array))) */ function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0) { diff --git a/htdocs/expedition/list.php b/htdocs/expedition/list.php index 962a5fb2396..109c2157f3d 100644 --- a/htdocs/expedition/list.php +++ b/htdocs/expedition/list.php @@ -295,7 +295,7 @@ if (empty($reshook)) { $objecttmp->date = $datefacture; $objecttmp->origin_type = 'shipping'; - $objecttmp->origin_id = $id_sending; + $objecttmp->origin_id = (int) $id_sending; $objecttmp->array_options = $expd->array_options; // Copy extrafields diff --git a/htdocs/fourn/commande/list.php b/htdocs/fourn/commande/list.php index 07a5f1fee6a..3dec150bfd6 100644 --- a/htdocs/fourn/commande/list.php +++ b/htdocs/fourn/commande/list.php @@ -433,8 +433,8 @@ if (empty($reshook)) { } $objecttmp->socid = $cmd->socid; $objecttmp->type = $objecttmp::TYPE_STANDARD; - $objecttmp->cond_reglement_id = $cmd->cond_reglement_id; - $objecttmp->mode_reglement_id = $cmd->mode_reglement_id; + $objecttmp->cond_reglement_id = $cmd->cond_reglement_id; + $objecttmp->mode_reglement_id = $cmd->mode_reglement_id; $objecttmp->fk_project = $cmd->fk_project; $objecttmp->multicurrency_code = $cmd->multicurrency_code; $objecttmp->ref_supplier = !empty($cmd->ref_supplier) ? $cmd->ref_supplier : $default_ref_supplier; @@ -446,8 +446,8 @@ if (empty($reshook)) { } $objecttmp->date = $datefacture; - $objecttmp->origin = 'order_supplier'; - $objecttmp->origin_id = $id_order; + $objecttmp->origin = 'order_supplier'; + $objecttmp->origin_id = (int) $id_order; $res = $objecttmp->create($user); diff --git a/htdocs/reception/list.php b/htdocs/reception/list.php index 575caa109d0..c2863f63a48 100644 --- a/htdocs/reception/list.php +++ b/htdocs/reception/list.php @@ -375,8 +375,8 @@ if (empty($reshook)) { } $objecttmp->date = $datefacture; - $objecttmp->origin = 'reception'; - $objecttmp->origin_id = $id_reception; + $objecttmp->origin = 'reception'; + $objecttmp->origin_id = (int) $id_reception; // Auto calculation of date due if not filled by user if (empty($objecttmp->date_echeance)) { diff --git a/htdocs/website/index.php b/htdocs/website/index.php index d96c210356c..651b543c78f 100644 --- a/htdocs/website/index.php +++ b/htdocs/website/index.php @@ -537,7 +537,7 @@ if ($massaction == 'setcategory' && GETPOST('confirmmassaction', 'alpha') && $us $category->fetch($categoryid); foreach ($toselect as $tmpid) { - $tmpwebsitepage->id = $tmpid; + $tmpwebsitepage->id = (int) $tmpid; $result = $category->add_type($tmpwebsitepage, 'website_page'); if ($result < 0 && $result != -3) { $error++; @@ -576,7 +576,7 @@ if ($massaction == 'delcategory' && GETPOST('confirmmassaction', 'alpha') && $us $category->fetch($categoryid); foreach ($toselect as $tmpid) { - $tmpwebsitepage->id = $tmpid; + $tmpwebsitepage->id = (int) $tmpid; $result = $category->del_type($tmpwebsitepage, 'website_page'); if ($result < 0 && $result != -3) { $error++; diff --git a/htdocs/workstation/class/workstation.class.php b/htdocs/workstation/class/workstation.class.php index 6ce132d16df..90e06c40e77 100644 --- a/htdocs/workstation/class/workstation.class.php +++ b/htdocs/workstation/class/workstation.class.php @@ -462,7 +462,7 @@ class Workstation extends CommonObject foreach ($groups as $id_group) { $ws_usergroup = new WorkstationUserGroup($this->db); $ws_usergroup->fk_workstation = $this->id; - $ws_usergroup->fk_usergroup = $id_group; + $ws_usergroup->fk_usergroup = (int) $id_group; $ws_usergroup->createCommon($user); $this->usergroups[] = $id_group; } @@ -475,7 +475,7 @@ class Workstation extends CommonObject foreach ($resources as $id_resource) { $ws_resource = new WorkstationResource($this->db); $ws_resource->fk_workstation = $this->id; - $ws_resource->fk_resource = $id_resource; + $ws_resource->fk_resource = (int) $id_resource; $ws_resource->createCommon($user); $this->resources[] = $id_resource; } From ce7e9f4027fd308d70ab86826adfc1e681fa6509 Mon Sep 17 00:00:00 2001 From: Jyhere Date: Tue, 28 Oct 2025 01:05:34 +0100 Subject: [PATCH 02/16] FIX: warning on invoice list when no extrafield (#35968) --- htdocs/compta/facture/list.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/compta/facture/list.php b/htdocs/compta/facture/list.php index 0958bb07dfc..627767ed772 100644 --- a/htdocs/compta/facture/list.php +++ b/htdocs/compta/facture/list.php @@ -222,7 +222,7 @@ $extrafields = new ExtraFields($db); // Fetch optionals attributes and labels $extrafields->fetch_name_optionals_label($object->table_element); -$search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_'); +$search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_') ?: []; // List of fields to search into when doing a "search in all" $fieldstosearchall = array( From 210328dd332a5f9ad9d7d11479c8207c418ae5eb Mon Sep 17 00:00:00 2001 From: Regis Houssin Date: Tue, 28 Oct 2025 01:07:22 +0100 Subject: [PATCH 03/16] FIX avoid php warnings (#35967) --- htdocs/expedition/card.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/htdocs/expedition/card.php b/htdocs/expedition/card.php index 2dc153a5f7f..d4fb779f199 100644 --- a/htdocs/expedition/card.php +++ b/htdocs/expedition/card.php @@ -1251,12 +1251,18 @@ if ($action == 'create') { $objectsav = $object; // Because Expedition is $expe and not $object that is wrongly a duplicate of $objectsrc. $object = $expe; // Propagate extrafieldsvalue from source object to shipment object - foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $val) { - if (array_key_exists('options_'.$key, $objectsav->array_options)) { // We take value from order only if extrafield has the same name/key. - $object->array_options['options_'.$key] = $objectsav->array_options['options_'.$key]; + if (isset($extrafields->attributes[$object->table_element]['label']) && is_array($extrafields->attributes[$object->table_element]['label']) && !empty($extrafields->attributes[$object->table_element]['label'])) { + foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $val) { + if (array_key_exists('options_'.$key, $objectsav->array_options)) { // We take value from order only if extrafield has the same name/key. + $object->array_options['options_'.$key] = $objectsav->array_options['options_'.$key]; + } } } - $parameters = array('objectsrc' => isset($objectsrc) ? $objectsrc : '', 'cols' => '3', 'socid' => $socid); + $parameters = array( + 'objectsrc' => isset($objectsrc) ? $objectsrc : '', + 'cols' => '3', + 'socid' => $socid + ); include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_add.tpl.php'; $object = $objectsav; From 8ed78a31b1da3e56bb3eff2543372e6723e4ebde Mon Sep 17 00:00:00 2001 From: sonikf <93765174+sonikf@users.noreply.github.com> Date: Tue, 28 Oct 2025 02:10:11 +0200 Subject: [PATCH 04/16] Add tpl files for standalone reception (#35966) * Add tpl for standalone reception * try to fix phan --- .../reception/tpl/objectline_create.tpl.php | 186 ++++++++++++++++++ htdocs/reception/tpl/objectline_edit.tpl.php | 165 ++++++++++++++++ htdocs/reception/tpl/objectline_title.tpl.php | 101 ++++++++++ htdocs/reception/tpl/objectline_view.tpl.php | 181 +++++++++++++++++ 4 files changed, 633 insertions(+) create mode 100644 htdocs/reception/tpl/objectline_create.tpl.php create mode 100644 htdocs/reception/tpl/objectline_edit.tpl.php create mode 100644 htdocs/reception/tpl/objectline_title.tpl.php create mode 100644 htdocs/reception/tpl/objectline_view.tpl.php diff --git a/htdocs/reception/tpl/objectline_create.tpl.php b/htdocs/reception/tpl/objectline_create.tpl.php new file mode 100644 index 00000000000..344279239c4 --- /dev/null +++ b/htdocs/reception/tpl/objectline_create.tpl.php @@ -0,0 +1,186 @@ + + * Copyright (C) 2010-2014 Laurent Destailleur + * Copyright (C) 2012-2013 Christophe Battarel + * Copyright (C) 2012 Cédric Salvador + * Copyright (C) 2014 Florian Henry + * Copyright (C) 2014 Raphaël Doursenaud + * Copyright (C) 2015-2016 Marcos García + * Copyright (C) 2018-2024 Frédéric France + * Copyright (C) 2018 Ferran Marcet + * Copyright (C) 2024 Vincent Maury + * Copyright (C) 2024-2025 MDW + * Copyright (C) 2025 Nick Fragoulis + * + * 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 . + * + * Need to have the following variables defined: + * $object (invoice, order, ...) + * $conf + * $langs + * $forceall (0 by default, 1 for supplier invoices/orders) + */ + +require_once DOL_DOCUMENT_ROOT."/product/class/html.formproduct.class.php"; + +/** + * @var CommonObject $this + * @var CommonObject $object + * @var Form $form + * @var Societe $buyer + * @var Translate $langs + */ + +// Protection to avoid direct call of template +if (empty($object) || !is_object($object)) { + print "Error: this template page cannot be called directly as an URL"; + exit; +} + +' +@phan-var-force CommonObject $this +@phan-var-force CommonObject $object +@phan-var-force Societe $buyer +'; + +global $forceall, $forcetoshowtitlelines, $filtertype; + +if (empty($forceall)) { + $forceall = 0; +} + +if (empty($filtertype)) { + $filtertype = 0; +} + +$formproduct = new FormProduct($object->db); + +// Define colspan for the button 'Add' +$colspan = 3; + + +// Lines for extrafield +$objectline = new ReceptionLineBatch($this->db); + +print "\n"; + +$nolinesbefore = (count($this->lines) == 0 || $forcetoshowtitlelines); + +if ($nolinesbefore) { + print ''; + if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER')) { + print ''; + } + print ''; + print '
'.$langs->trans('AddNewLine').''; + print ''; + print ''.$langs->trans('Qty').''; + + if (getDolGlobalInt('PRODUCT_USE_UNITS')) { + print ''; + print ''; + print $langs->trans('Unit'); + print ''; + } + + print ''; +} + +print ''; +$coldisplay = 0; + +// Adds a line numbering column +if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER')) { + $coldisplay++; + echo ''; +} + +// Product +$coldisplay++; +print ''; + +// Predefined product/service +if (isModEnabled("product")) { + if ($filtertype == 1) { + print $langs->trans("Service"); + } else { + print $langs->trans("Product"); + } + + echo ''; + + $statustoshow = -1; + + echo ''; +} + + +if (!empty($extrafields)) { + $temps = $objectline->showOptionals($extrafields, 'create', array(), '', '', '1', 'line'); + + if (!empty($temps)) { + print '
'; + print $temps; + print '
'; + } +} +print ''; + +// Qty +$coldisplay++; +print ''; +print ''; + +// Unit +if (getDolGlobalInt('PRODUCT_USE_UNITS')) { + $coldisplay++; + print ''; + print ''; +} + +$coldisplay += $colspan; +print ''; +print ''; +print ''; +print ''; + +?> + + + + diff --git a/htdocs/reception/tpl/objectline_edit.tpl.php b/htdocs/reception/tpl/objectline_edit.tpl.php new file mode 100644 index 00000000000..4f22caeac22 --- /dev/null +++ b/htdocs/reception/tpl/objectline_edit.tpl.php @@ -0,0 +1,165 @@ + + * Copyright (C) 2010-2012 Laurent Destailleur + * Copyright (C) 2012 Christophe Battarel + * Copyright (C) 2012 Cédric Salvador + * Copyright (C) 2012-2014 Raphaël Doursenaud + * Copyright (C) 2013 Florian Henry + * Copyright (C) 2018-2024 Frédéric France + * Copyright (C) 2024 Vincent Maury + * Copyright (C) 2024 MDW + * Copyright (C) 2025 Nick Fragoulis + * + * 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 . + * + * Need to have the following variables defined: + * $object (invoice, order, ...) + * $conf + * $langs + * $seller, $buyer + * $dateSelector + * $forceall (0 by default, 1 for supplier invoices/orders) + * $senderissupplier (0 by default, 1 for supplier invoices/orders) + * $inputalsopricewithtax (0 by default, 1 to also show column with unit price including tax) + */ + +require_once DOL_DOCUMENT_ROOT."/product/class/html.formproduct.class.php"; + +/** + * @var CommonObject $this + * @var CommonObject $object + * @var HookManager $hookmanager + * @var CommonObjectLine $line + * @var Societe $buyer + * @var Societe $seller + * @var Translate $langs + * + * @var string $action + * @var int $i + * @var bool $var + */ + +// Protection to avoid direct call of template +if (empty($object) || !is_object($object)) { + print "Error, template page can't be called as URL"; + exit(1); +} + +' +@phan-var-force receptionlinebatch $line +@phan-var-force CommonObject $this +@phan-var-force CommonObject $object +@phan-var-force int $i +@phan-var-force bool $var +@phan-var-force Societe $buyer +@phan-var-force Societe $seller +'; + +global $forceall, $filtertype; + +if (empty($forceall)) { + $forceall = 0; +} + +if (empty($filtertype)) { + $filtertype = 0; +} + +$formproduct = new FormProduct($object->db); +$form = new Form($object->db); + +// Define colspan for the button 'Add' +$colspan = 3; + +// Lines for extrafield +$objectline = new ReceptionLineBatch($this->db); + +print "\n"; + +$coldisplay = 0; +print ''; +// Adds a line numbering column +if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER')) { + print ''.($i + 1).''; + $coldisplay++; +} + +$coldisplay++; +?> + +
+ + + + + + +fk_product > 0) { + $tmpproduct = new Product($object->db); + $tmpproduct->fetch($line->fk_product); + print $tmpproduct->getNomUrl(1); + print ' - '.$tmpproduct->label; +} + +//Line extrafield +if (!empty($extrafields)) { + $temps = $line->showOptionals($extrafields, 'edit', array('class' => 'tredited'), '', '', '1', 'line'); + if (!empty($temps)) { + print '
'; + print $temps; + print '
'; + } +} + +print ''; + +$coldisplay++; + +print ''; + +if (((int) $line->info_bits & 2) != 2) { + print ''; +} +print ''; + + +if (getDolGlobalString('PRODUCT_USE_UNITS')) { + $unit_type = false; + // limit unit select to unit type + if (!empty($line->fk_unit) && !getDolGlobalString('MAIN_EDIT_LINE_ALLOW_ALL_UNIT_TYPE')) { + include_once DOL_DOCUMENT_ROOT.'/core/class/cunits.class.php'; + $cUnit = new CUnits($line->db); + if ($cUnit->fetch((int) $line->fk_unit) > 0) { + if (!empty($cUnit->unit_type)) { + $unit_type = $cUnit->unit_type; + } + } + } + $coldisplay++; + print ''; + print $form->selectUnits(GETPOSTISSET('units') ? GETPOST('units') : $line->fk_unit, "units", 0, $unit_type); + print ''; +} + +$coldisplay += $colspan; +print ''; +$coldisplay += $colspan; +print ''; +print ''; +print ''; +print ''; + +print "\n"; diff --git a/htdocs/reception/tpl/objectline_title.tpl.php b/htdocs/reception/tpl/objectline_title.tpl.php new file mode 100644 index 00000000000..fc3d09be0fa --- /dev/null +++ b/htdocs/reception/tpl/objectline_title.tpl.php @@ -0,0 +1,101 @@ + + * Copyright (C) 2010-2011 Laurent Destailleur + * Copyright (C) 2012-2013 Christophe Battarel + * Copyright (C) 2012 Cédric Salvador + * Copyright (C) 2012-2014 Raphaël Doursenaud + * Copyright (C) 2013 Florian Henry + * Copyright (C) 2017 Juanjo Menent + * Copyright (C) 2024 Frédéric France + * Copyright (C) 2025 Nick Fragoulis + * + * 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 . + * + * Need to have the following variables defined: + * $object (invoice, order, ...) + * $conf + * $langs + * $element (used to test $user->hasRight($element, 'creer')) + * $permtoedit (used to replace test $user->hasRight($element, 'creer')) + * $inputalsopricewithtax (0 by default, 1 to also show column with unit price including tax) + * $outputalsopricetotalwithtax + * $usemargins (0 to disable all margins columns, 1 to show according to margin setup) + * + * $type, $text, $description, $line + */ +/** + * @var CommonObject $this + * @var CommonObject $object + * @var Form $form + * @var Translate $langs + * + * @var string $action + */ + +// Protection to avoid direct call of template +if (empty($object) || !is_object($object)) { + print "Error, template page can't be called as URL"; + exit(1); +} + +'@phan-var-force CommonObject $this + @phan-var-force CommonObject $object'; + +global $filtertype; +if (empty($filtertype)) { + $filtertype = 0; +} + +print "\n"; + + +// Title line +print "\n"; + +print ''; + +// Adds a line numbering column +if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER')) { + print ' '; +} + +// Product +print ''.$langs->trans('Description'); + +// Qty +print ''.$langs->trans('Qty').''; + + +// Unit +if (getDolGlobalString('PRODUCT_USE_UNITS')) { + print ''.$langs->trans('Unit').''; +} + +print ''; // No width to allow autodim + +print ''; + +print ''; + +if ($action == 'selectlines') { + print ''; + print ''; + print ''; + print ''; +} + +print "\n"; +print "\n"; + +print "\n"; diff --git a/htdocs/reception/tpl/objectline_view.tpl.php b/htdocs/reception/tpl/objectline_view.tpl.php new file mode 100644 index 00000000000..cb2770facd0 --- /dev/null +++ b/htdocs/reception/tpl/objectline_view.tpl.php @@ -0,0 +1,181 @@ + + * Copyright (C) 2010-2011 Laurent Destailleur + * Copyright (C) 2012-2013 Christophe Battarel + * Copyright (C) 2012 Cédric Salvador + * Copyright (C) 2012-2014 Raphaël Doursenaud + * Copyright (C) 2013 Florian Henry + * Copyright (C) 2017 Juanjo Menent + * Copyright (C) 2024-2025 MDW + * Copyright (C) 2024 Frédéric France + * Copyright (C) 2025 Nick Fragoulis + * + * 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 . + * + * Need to have the following variables defined: + * $object (invoice, order, ...) + * $conf + * $langs + * $forceall (0 by default, 1 for supplier invoices/orders) + * $element (used to test $user->hasRight($element, 'creer')) + * $permtoedit (used to replace test $user->hasRight($element, 'creer')) + * $inputalsopricewithtax (0 by default, 1 to also show column with unit price including tax) + * $disableedit, $disablemove, $disableremove + * + * $type, $text, $description, $line + */ + +/** + * @var Conf $conf + * @var CommonObject $this + * @var CommonObject $object + * @var CommonObjectLine $line + * @var Translate $langs + * @var User $user + * + * @var int $i + * @var int $num + * @var string $action + */ +' +@phan-var-force receptionlinebatch $line +@phan-var-force int $num +@phan-var-force int $i +@phan-var-force CommonObject $this +@phan-var-force CommonObject $object +'; + +// Protection to avoid direct call of template +if (empty($object) || !is_object($object)) { + print "Error, template page can't be called as URL"; + exit(1); +} + +global $filtertype; +if (empty($filtertype)) { + $filtertype = 0; +} + + +global $forceall, $senderissupplier, $inputalsopricewithtax, $outputalsopricetotalwithtax, $langs; + +if (empty($dateSelector)) { + $dateSelector = 0; +} +if (empty($forceall)) { + $forceall = 0; +} + + +// add html5 elements +$domData = ' data-element="'.$line->element.'"'; +$domData .= ' data-id="'.$line->id.'"'; +$domData .= ' data-qty="'.$line->qty.'"'; +$domData .= ' data-product_type="'.$line->product_type.'"'; + +// Lines for extrafield +$objectline = new ReceptionLineBatch($this->db); + +$coldisplay = 0; +print "\n"; +print ''; + +// Line nb +if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER')) { + print ''.($i + 1).''; + $coldisplay++; +} + +// Product +print ''; +print '
'; +$coldisplay++; +$tmpproduct = new Product($object->db); +$tmpproduct->fetch($line->fk_product); +$tmprecep = new Reception($object->db); +if ($line->fk_product > 0) { + print $tmpproduct->getNomUrl(1); + print ' - '.$tmpproduct->label; +} else { + print ' - '.$line->description; +} +print ''; + +// Qty +print ''; +$coldisplay++; +echo price($line->qty, 0, '', 0, 0); // Yes, it is a quantity, not a price, but we just want the formatting role of function price +print ''; + +// Unit +if (getDolGlobalInt('PRODUCT_USE_UNITS')) { // For product, unit is shown only if option PRODUCT_USE_UNITS is on + print ''; + $coldisplay++; + $label = measuringUnitString((int) $line->fk_unit, '', null, 1); + if ($label !== '') { + print $langs->trans($label); + } + print ''; +} + +if ($this->status == 0 && $user->hasRight('reception', 'write') && $action != 'selectlines') { + print ''; + $coldisplay++; + if (((int) $line->info_bits & 2) == 2 || !empty($disableedit)) { + } else { + print 'id.'">'.img_edit().''; + } + print ''; + + print ''; + $coldisplay++; + + print 'id.'">'; + print img_delete(); + print ''; + + print ''; + + if ($num > 1 && $conf->browser->layout != 'phone' && empty($disablemove)) { + print ''; + $coldisplay++; + if ($i > 0) { + print 'id.'">'; + echo img_up('default', 0, 'imgupforline'); + print ''; + } + if ($i < $num - 1) { + print 'id.'">'; + echo img_down('default', 0, 'imgdownforline'); + print ''; + } + print ''; + } else { + print 'browser->layout != 'phone' && empty($disablemove)) ? ' class="linecolmove tdlineupdown center"' : ' class="linecolmove center"').'>'; + $coldisplay++; + } +} else { + print ''; + $coldisplay += 3; +} + +if ($action == 'selectlines') { + print ''; + print ''; + print ''; +} + +print ''; + +print "\n"; From 8ac1fca7617d9ea7fef9d8e6be031e1cd6ea5277 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Oct 2025 01:12:07 +0100 Subject: [PATCH 05/16] Bump actions/upload-artifact from 4 to 5 (#35960) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/phpstan.yml | 2 +- .github/workflows/pre-commit.yml | 2 +- .github/workflows/windows-ci.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index f3ae726b435..d41d022a643 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -81,7 +81,7 @@ jobs: key: phpstan-cache-${{ matrix.php-version }}-${{ env.CACHE_KEY_PART }}-${{ github.run_id }} - name: Provide phpstan log as artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 if: ${{ always() }} with: name: phpstan-srcrt diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index b5759d56810..788a0a3bb35 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -166,7 +166,7 @@ jobs: key: pre-commit-4|${{ env.pythonLocation }}|${{ hashFiles('.pre-commit-config.yaml') }} # Upload result log files of precommit into the Artifact shared store - name: Provide log as artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 if: ${{ ! cancelled() }} with: name: precommit-logs diff --git a/.github/workflows/windows-ci.yml b/.github/workflows/windows-ci.yml index 602c8883c8f..7ca606f7a02 100644 --- a/.github/workflows/windows-ci.yml +++ b/.github/workflows/windows-ci.yml @@ -173,7 +173,7 @@ jobs: in: ${{ env.PHPUNIT_LOG }} - name: Provide dolibarr and phpunit logs as artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 if: ${{ ! cancelled() }} with: name: win-ci-logs From a487ce492388c52047f37afcdadc7e766b1e90e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20FRANCE?= Date: Tue, 28 Oct 2025 01:12:45 +0100 Subject: [PATCH 06/16] add missing translation (#35959) * add missing translation * add missing translation * clean missing --- dev/translation/ignore_translation_keys.lst | 2 -- htdocs/langs/en_US/projects.lang | 1 + htdocs/public/opensurvey/studs.php | 2 +- htdocs/public/project/new.php | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/dev/translation/ignore_translation_keys.lst b/dev/translation/ignore_translation_keys.lst index d49d384b256..1aaa57e0f6f 100644 --- a/dev/translation/ignore_translation_keys.lst +++ b/dev/translation/ignore_translation_keys.lst @@ -402,7 +402,6 @@ Filename FilteredFrom Flashy ForcedByGlobalSetup -Form for public lead registration has not been enabled Free FreeLegalTextOnReceptions FrequencyPer_ @@ -804,7 +803,6 @@ TicketTypeShort TimeOnly Timezone ToGetBack -ToPay Token Tooltip TooltipEditAndRevertStockMovement diff --git a/htdocs/langs/en_US/projects.lang b/htdocs/langs/en_US/projects.lang index 8a439e8a947..75322ea00ac 100644 --- a/htdocs/langs/en_US/projects.lang +++ b/htdocs/langs/en_US/projects.lang @@ -331,3 +331,4 @@ ConfirmCloneTask=Do you want to clone this task? TaskBackToDraft=Task status is back to draft TaskValidated=Task validated EventStartOrEndDateNotDefined=Event start or end date not defined +FormForPublicLeadRegistrationHasNotBeenEnabled=Form for public lead registration has not been enabled diff --git a/htdocs/public/opensurvey/studs.php b/htdocs/public/opensurvey/studs.php index f1e94595c99..aede8764b1c 100644 --- a/htdocs/public/opensurvey/studs.php +++ b/htdocs/public/opensurvey/studs.php @@ -69,7 +69,7 @@ $canbemodified = ((empty($object->date_fin) || dol_get_last_hour($object->date_f // Security check if (!isModEnabled('opensurvey')) { - httponly_accessforbidden('Module Survey not enabled'); + httponly_accessforbidden('Module Opensurvey not enabled'); } diff --git a/htdocs/public/project/new.php b/htdocs/public/project/new.php index 99377091941..b261f9227e5 100644 --- a/htdocs/public/project/new.php +++ b/htdocs/public/project/new.php @@ -77,7 +77,7 @@ $action = GETPOST('action', 'aZ09'); $langs->loadLangs(array("members", "companies", "install", "other", "projects")); if (!getDolGlobalString('PROJECT_ENABLE_PUBLIC')) { - print $langs->trans("Form for public lead registration has not been enabled"); + print $langs->trans("FormForPublicLeadRegistrationHasNotBeenEnabled"); exit; } From 972295ea3cbcaa58bbc9d75a28d92ca55f9b015d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20FRANCE?= Date: Tue, 28 Oct 2025 01:13:14 +0100 Subject: [PATCH 07/16] add missing translation (#35958) --- dev/translation/ignore_translation_keys.lst | 1 - htdocs/langs/en_US/website.lang | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/translation/ignore_translation_keys.lst b/dev/translation/ignore_translation_keys.lst index 1aaa57e0f6f..910f9634e30 100644 --- a/dev/translation/ignore_translation_keys.lst +++ b/dev/translation/ignore_translation_keys.lst @@ -325,7 +325,6 @@ ErrorFailedToGetListOfNotificationsToSend ErrorFailedToLoadBankAccount ErrorFailedToLoadDiscount ErrorFailedToSetNewPassword -ErrorFaviconMustBeASquaredImage ErrorFileNameInvalid ErrorImportOfChartLimitedToCurrentChart ErrorMAIN_ROUNDING_RULE_TOTCanMAIN_MAX_DECIMALS_TOT diff --git a/htdocs/langs/en_US/website.lang b/htdocs/langs/en_US/website.lang index cf5c7dc1161..2847151aaf8 100644 --- a/htdocs/langs/en_US/website.lang +++ b/htdocs/langs/en_US/website.lang @@ -154,6 +154,7 @@ SitemapGenerated=Sitemap file %s generated ImportFavicon=Favicon ErrorFaviconType=Favicon must be png ErrorFaviconSize=Favicon must be sized 16x16, 32x32 or 64x64 +ErrorFaviconMustBeASquaredImage=Favicon must be a squared image FaviconTooltip=Upload an image which needs to be a png (16x16, 32x32 or 64x64) NextContainer=Next page/container PreviousContainer=Previous page/container From cac1058cafbfb87c2d25a70eda61cded949b4783 Mon Sep 17 00:00:00 2001 From: Regis Houssin Date: Tue, 28 Oct 2025 01:22:02 +0100 Subject: [PATCH 08/16] NEW add extraparams field in llx_categorie (#35975) --- htdocs/install/mysql/migration/22.0.0-23.0.0.sql | 1 + htdocs/install/mysql/tables/llx_categorie.sql | 15 ++++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/htdocs/install/mysql/migration/22.0.0-23.0.0.sql b/htdocs/install/mysql/migration/22.0.0-23.0.0.sql index 240d9bc2201..8391017c2b4 100644 --- a/htdocs/install/mysql/migration/22.0.0-23.0.0.sql +++ b/htdocs/install/mysql/migration/22.0.0-23.0.0.sql @@ -190,6 +190,7 @@ ALTER TABLE llx_commande_fournisseur ADD COLUMN deposit_percent varchar(63) DEFA -- import key for subscriptions ALTER TABLE llx_subscription ADD COLUMN import_key varchar(14) NULL; +ALTER TABLE llx_categorie ADD COLUMN extraparams varchar(255) AFTER fk_soc; CREATE TABLE llx_categorie_propal ( diff --git a/htdocs/install/mysql/tables/llx_categorie.sql b/htdocs/install/mysql/tables/llx_categorie.sql index 53590b4fa92..ba5eb8f1a69 100644 --- a/htdocs/install/mysql/tables/llx_categorie.sql +++ b/htdocs/install/mysql/tables/llx_categorie.sql @@ -1,7 +1,7 @@ -- ============================================================================ -- Copyright (C) 2005 Brice Davoleau -- Copyright (C) 2005 Matthieu Valleton --- Copyright (C) 2005-2012 Regis Houssin +-- Copyright (C) 2005-2025 Regis Houssin -- Copyright (C) 2017-2024 Laurent Destailleur -- -- This program is free software; you can redistribute it and/or modify @@ -29,12 +29,13 @@ create table llx_categorie type integer DEFAULT 1 NOT NULL, -- Category type (0=product, 1=supplier, 2=customer, 3=member, ...). See array $MAP_ID into categorie.class.php for possible values. description text, -- description of the category color varchar(8), -- Color - position integer DEFAULT 0, -- Position fk_soc integer DEFAULT NULL, -- Not used by default. Used when option CATEGORY_ASSIGNED_TO_A_CUSTOMER is set. + extraparams varchar(255), -- to stock other parameters in json format + date_creation datetime, -- Creation date + fk_user_creat integer, -- User id making creation + fk_user_modif integer, -- User id making last change + tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -- Last modification date visible tinyint DEFAULT 1 NOT NULL, -- Determine if the products are visible or not - date_creation datetime, -- Creation date - tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -- Last modification date - fk_user_creat integer, -- User id making creation - fk_user_modif integer, -- User id making last change - import_key varchar(14) -- Import key + position integer DEFAULT 0, -- Position + import_key varchar(14) -- Import key )ENGINE=innodb; From e37c7678a4eacc5e1fe7814c43d19d3f70945a62 Mon Sep 17 00:00:00 2001 From: Regis Houssin Date: Tue, 28 Oct 2025 01:24:02 +0100 Subject: [PATCH 09/16] FIX avoid warning if NOREQUIRETRAN is defined (eg ajax) (#35954) --- htdocs/main.inc.php | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/htdocs/main.inc.php b/htdocs/main.inc.php index b41ad9adda6..6c231cffb83 100644 --- a/htdocs/main.inc.php +++ b/htdocs/main.inc.php @@ -222,17 +222,19 @@ if (isset($_SERVER["HTTP_USER_AGENT"])) { // accesskey is for Windows or Linux: ALT + key for chrome, ALT + SHIFT + KEY for firefox // accesskey is for Mac: CTRL + Option + key for all browsers -$langs->load('main'); -$conf->browser->stringforfirstkey = $langs->trans("KeyboardShortcut"); -if ($conf->browser->os == 'macintosh') { - $conf->browser->stringforfirstkey .= ' CTRL + Option +'; -} else { - if ($conf->browser->name == 'chrome') { - $conf->browser->stringforfirstkey .= ' ALT +'; - } elseif ($conf->browser->name == 'firefox') { - $conf->browser->stringforfirstkey .= ' ALT + SHIFT +'; +if (!defined('NOREQUIRETRAN')) { + $langs->load('main'); + $conf->browser->stringforfirstkey = $langs->trans("KeyboardShortcut"); + if ($conf->browser->os == 'macintosh') { + $conf->browser->stringforfirstkey .= ' CTRL + Option +'; } else { - $conf->browser->stringforfirstkey .= ' CTL +'; + if ($conf->browser->name == 'chrome') { + $conf->browser->stringforfirstkey .= ' ALT +'; + } elseif ($conf->browser->name == 'firefox') { + $conf->browser->stringforfirstkey .= ' ALT + SHIFT +'; + } else { + $conf->browser->stringforfirstkey .= ' CTL +'; + } } } From 03ea0cda015af72eb94ea36ea51f74aa9b386147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20FRANCE?= Date: Tue, 28 Oct 2025 01:25:30 +0100 Subject: [PATCH 10/16] clean code class commondict (#35952) * add fetchAll in abstract class commondict * add fetchAll in abstract class commondict * add fetchAll in abstract class commondict * add fetchAll in abstract class commondict * add fetchAll in abstract class commondict * add fetchAll in abstract class commondict * add fetchAll in abstract class commondict * clean code --- dev/build/phpstan/phpstan-baseline.neon | 108 ------------------ htdocs/api/class/api_setup.class.php | 4 +- htdocs/bom/bom_net_needs.php | 4 +- htdocs/bom/tpl/objectline_view.tpl.php | 2 +- htdocs/core/class/cgenericdic.class.php | 8 +- htdocs/core/class/commondict.class.php | 4 +- htdocs/core/class/cunits.class.php | 2 +- htdocs/core/lib/product.lib.php | 4 +- htdocs/product/card.php | 2 +- .../product/class/html.formproduct.class.php | 6 +- htdocs/public/members/new.php | 2 +- 11 files changed, 19 insertions(+), 127 deletions(-) diff --git a/dev/build/phpstan/phpstan-baseline.neon b/dev/build/phpstan/phpstan-baseline.neon index 115e70be9ea..091061e9512 100644 --- a/dev/build/phpstan/phpstan-baseline.neon +++ b/dev/build/phpstan/phpstan-baseline.neon @@ -1476,12 +1476,6 @@ parameters: count: 3 path: ../../../htdocs/bom/bom_net_needs.php - - - message: '#^Property CommonDict\:\:\$label \(string\) in isset\(\) is not nullable\.$#' - identifier: isset.property - count: 1 - path: ../../../htdocs/bom/bom_net_needs.php - - message: '#^Left side of && is always false\.$#' identifier: booleanAnd.leftAlwaysFalse @@ -1518,12 +1512,6 @@ parameters: count: 1 path: ../../../htdocs/bom/tpl/objectline_title.tpl.php - - - message: '#^Property CommonDict\:\:\$label \(string\) in isset\(\) is not nullable\.$#' - identifier: isset.property - count: 1 - path: ../../../htdocs/bom/tpl/objectline_view.tpl.php - - message: '#^If condition is always false\.$#' identifier: if.alwaysFalse @@ -3390,24 +3378,12 @@ parameters: count: 1 path: ../../../htdocs/compta/paiement/class/cpaiement.class.php - - - message: '#^Property CommonDict\:\:\$code \(string\) in isset\(\) is not nullable\.$#' - identifier: isset.property - count: 4 - path: ../../../htdocs/compta/paiement/class/cpaiement.class.php - - message: '#^Property CommonDict\:\:\$id \(int\) in isset\(\) is not nullable\.$#' identifier: isset.property count: 1 path: ../../../htdocs/compta/paiement/class/cpaiement.class.php - - - message: '#^Property CommonDict\:\:\$label \(string\) in isset\(\) is not nullable\.$#' - identifier: isset.property - count: 2 - path: ../../../htdocs/compta/paiement/class/cpaiement.class.php - - message: '#^Property Cpaiement\:\:\$accountancy_code \(string\) in isset\(\) is not nullable\.$#' identifier: isset.property @@ -5100,42 +5076,12 @@ parameters: count: 4 path: ../../../htdocs/core/class/ccountry.class.php - - - message: '#^Property CommonDict\:\:\$code \(string\) in isset\(\) is not nullable\.$#' - identifier: isset.property - count: 4 - path: ../../../htdocs/core/class/ccountry.class.php - - - - message: '#^Property CommonDict\:\:\$label \(string\) in isset\(\) is not nullable\.$#' - identifier: isset.property - count: 4 - path: ../../../htdocs/core/class/ccountry.class.php - - - - message: '#^Method CGenericDic\:\:fetchAll\(\) has parameter \$filter with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: ../../../htdocs/core/class/cgenericdic.class.php - - message: '#^Negated boolean expression is always true\.$#' identifier: booleanNot.alwaysTrue count: 1 path: ../../../htdocs/core/class/cgenericdic.class.php - - - message: '#^Property CGenericDic\:\:\$code \(string\) in isset\(\) is not nullable\.$#' - identifier: isset.property - count: 4 - path: ../../../htdocs/core/class/cgenericdic.class.php - - - - message: '#^Property CGenericDic\:\:\$label \(string\) in isset\(\) is not nullable\.$#' - identifier: isset.property - count: 4 - path: ../../../htdocs/core/class/cgenericdic.class.php - - message: '#^Method CLeadStatus\:\:fetchAll\(\) return type has no value type specified in iterable type array\.$#' identifier: missingType.iterableValue @@ -5154,24 +5100,12 @@ parameters: count: 2 path: ../../../htdocs/core/class/cleadstatus.class.php - - - message: '#^Property CommonDict\:\:\$code \(string\) in isset\(\) is not nullable\.$#' - identifier: isset.property - count: 2 - path: ../../../htdocs/core/class/cleadstatus.class.php - - message: '#^Property CommonDict\:\:\$id \(int\) in isset\(\) is not nullable\.$#' identifier: isset.property count: 1 path: ../../../htdocs/core/class/cleadstatus.class.php - - - message: '#^Property CommonDict\:\:\$label \(string\) in isset\(\) is not nullable\.$#' - identifier: isset.property - count: 3 - path: ../../../htdocs/core/class/cleadstatus.class.php - - message: '#^Property Comment\:\:\$description \(string\) in isset\(\) is not nullable\.$#' identifier: isset.property @@ -5604,24 +5538,12 @@ parameters: count: 1 path: ../../../htdocs/core/class/conf.class.php - - - message: '#^Property CommonDict\:\:\$code \(string\) in isset\(\) is not nullable\.$#' - identifier: isset.property - count: 2 - path: ../../../htdocs/core/class/cproductnature.class.php - - message: '#^Property CommonDict\:\:\$id \(int\) in isset\(\) is not nullable\.$#' identifier: isset.property count: 1 path: ../../../htdocs/core/class/cproductnature.class.php - - - message: '#^Property CommonDict\:\:\$label \(string\) in isset\(\) is not nullable\.$#' - identifier: isset.property - count: 2 - path: ../../../htdocs/core/class/cproductnature.class.php - - message: '#^Property Cstate\:\:\$code_departement \(string\) in isset\(\) is not nullable\.$#' identifier: isset.property @@ -5646,12 +5568,6 @@ parameters: count: 1 path: ../../../htdocs/core/class/cstate.class.php - - - message: '#^Property CommonDict\:\:\$code \(string\) in isset\(\) is not nullable\.$#' - identifier: isset.property - count: 4 - path: ../../../htdocs/core/class/ctypent.class.php - - message: '#^Property CommonDict\:\:\$id \(int\) in isset\(\) is not nullable\.$#' identifier: isset.property @@ -5676,18 +5592,6 @@ parameters: count: 1 path: ../../../htdocs/core/class/ctyperesource.class.php - - - message: '#^Property CommonDict\:\:\$code \(string\) in isset\(\) is not nullable\.$#' - identifier: isset.property - count: 4 - path: ../../../htdocs/core/class/ctyperesource.class.php - - - - message: '#^Property CommonDict\:\:\$label \(string\) in isset\(\) is not nullable\.$#' - identifier: isset.property - count: 4 - path: ../../../htdocs/core/class/ctyperesource.class.php - - message: '#^Property CUnits\:\:\$short_label \(string\) in isset\(\) is not nullable\.$#' identifier: isset.property @@ -5706,24 +5610,12 @@ parameters: count: 4 path: ../../../htdocs/core/class/cunits.class.php - - - message: '#^Property CommonDict\:\:\$code \(string\) in isset\(\) is not nullable\.$#' - identifier: isset.property - count: 4 - path: ../../../htdocs/core/class/cunits.class.php - - message: '#^Property CommonDict\:\:\$id \(int\) in isset\(\) is not nullable\.$#' identifier: isset.property count: 2 path: ../../../htdocs/core/class/cunits.class.php - - - message: '#^Property CommonDict\:\:\$label \(string\) in isset\(\) is not nullable\.$#' - identifier: isset.property - count: 4 - path: ../../../htdocs/core/class/cunits.class.php - - message: '#^Constructor of class DolEditor has an unused parameter \$toolbarlocation\.$#' identifier: constructor.unusedParameter diff --git a/htdocs/api/class/api_setup.class.php b/htdocs/api/class/api_setup.class.php index 0940b2418fe..e0fe2c9c01f 100644 --- a/htdocs/api/class/api_setup.class.php +++ b/htdocs/api/class/api_setup.class.php @@ -506,7 +506,7 @@ class Setup extends DolibarrApi $obj = $this->db->fetch_object($result); $state = new Cstate($this->db); if ($state->fetch($obj->rowid) > 0) { - if (empty($filter) || stripos($state->label, $filter) !== false) { + if (empty($filter) || stripos((string) $state->label, $filter) !== false) { $list[] = $this->_cleanObjectDatas($state); } } @@ -619,7 +619,7 @@ class Setup extends DolibarrApi // and then apply the filter if there is one. $this->translateLabel($country, $lang, 'Country'); - if (empty($filter) || stripos($country->label, $filter) !== false) { + if (empty($filter) || stripos((string) $country->label, $filter) !== false) { $list[] = $this->_cleanObjectDatas($country); } } diff --git a/htdocs/bom/bom_net_needs.php b/htdocs/bom/bom_net_needs.php index 7d429702060..b5ff6475352 100644 --- a/htdocs/bom/bom_net_needs.php +++ b/htdocs/bom/bom_net_needs.php @@ -1,6 +1,6 @@ - * Copyright (C) 2019-2024 Frédéric France + * Copyright (C) 2019-2025 Frédéric France * Copyright (C) 2025 MDW * * This program is free software; you can redistribute it and/or modify @@ -332,7 +332,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea require_once DOL_DOCUMENT_ROOT.'/core/class/cunits.class.php'; $unit = new CUnits($db); $unit->fetch((int) $elem['fk_unit']); - print(isset($unit->label) ? " ".$langs->trans(ucwords($unit->label))." " : ''); + print(isset($unit->label) ? " ".$langs->trans(ucwords((string) $unit->label))." " : ''); } print ''; print ''.price(price2num($prod->stock_reel, 'MS')).''; diff --git a/htdocs/bom/tpl/objectline_view.tpl.php b/htdocs/bom/tpl/objectline_view.tpl.php index d9162882023..297885e4bdd 100644 --- a/htdocs/bom/tpl/objectline_view.tpl.php +++ b/htdocs/bom/tpl/objectline_view.tpl.php @@ -168,7 +168,7 @@ if ($filtertype != 1) { // Product require_once DOL_DOCUMENT_ROOT.'/core/class/cunits.class.php'; $unit = new CUnits($this->db); $unit->fetch($line->fk_unit); - print(isset($unit->label) ? " ".$langs->trans(ucwords($unit->label))." " : ''); + print(isset($unit->label) ? " ".$langs->trans(ucwords((string) $unit->label))." " : ''); } print ''; diff --git a/htdocs/core/class/cgenericdic.class.php b/htdocs/core/class/cgenericdic.class.php index 137b828db78..840326011e4 100644 --- a/htdocs/core/class/cgenericdic.class.php +++ b/htdocs/core/class/cgenericdic.class.php @@ -49,12 +49,12 @@ class CGenericDic extends CommonDict public $lines = array(); /** - * @var string + * @var ?string */ public $code; /** - * @var string Label + * @var ?string Label */ public $label; @@ -225,7 +225,7 @@ class CGenericDic extends CommonDict * @param string $sortfield Sort field * @param int $limit Limit * @param int $offset offset limit - * @param string|array $filter filter USF + * @param string|string[] $filter filter USF * @param string $filtermode filter mode (AND or OR) * @return int Return integer <0 if KO, >0 if OK */ @@ -444,7 +444,7 @@ class CGenericDic extends CommonDict dol_syslog(__METHOD__, LOG_DEBUG); $error = 0; - $object = new Ctyperesource($this->db); + $object = new CGenericDic($this->db); $this->db->begin(); diff --git a/htdocs/core/class/commondict.class.php b/htdocs/core/class/commondict.class.php index 240fded7ea9..c1ee447dff2 100644 --- a/htdocs/core/class/commondict.class.php +++ b/htdocs/core/class/commondict.class.php @@ -55,12 +55,12 @@ abstract class CommonDict public $entity; /** - * @var string The code + * @var ?string The code */ public $code; /** - * @var string The label + * @var ?string The label */ public $label; diff --git a/htdocs/core/class/cunits.class.php b/htdocs/core/class/cunits.class.php index 2a9fa4f0c8f..c7b7de24908 100644 --- a/htdocs/core/class/cunits.class.php +++ b/htdocs/core/class/cunits.class.php @@ -1,7 +1,7 @@ * Copyright (C) 2024-2025 MDW - * Copyright (C) 2024 Frédéric France + * Copyright (C) 2024-2025 Frédéric France * * 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 diff --git a/htdocs/core/lib/product.lib.php b/htdocs/core/lib/product.lib.php index 70a7029982d..3db8392b363 100644 --- a/htdocs/core/lib/product.lib.php +++ b/htdocs/core/lib/product.lib.php @@ -957,9 +957,9 @@ function measuringUnitString($unitid, $measuring_style = '', $unitscale = null, if ($use_short_label == 1) { $labeltoreturn = $measuringUnits->records[key($measuringUnits->records)]->short_label; } elseif ($use_short_label == 2) { - $labeltoreturn = $outputlangs->transnoentitiesnoconv(ucfirst($measuringUnits->records[key($measuringUnits->records)]->label).'Short'); + $labeltoreturn = $outputlangs->transnoentitiesnoconv(ucfirst((string) $measuringUnits->records[key($measuringUnits->records)]->label).'Short'); } else { - $labeltoreturn = $outputlangs->transnoentitiesnoconv($measuringUnits->records[key($measuringUnits->records)]->label); + $labeltoreturn = $outputlangs->transnoentitiesnoconv((string) $measuringUnits->records[key($measuringUnits->records)]->label); } } else { $labeltoreturn = ''; diff --git a/htdocs/product/card.php b/htdocs/product/card.php index ae9cc460a94..e771e54d548 100644 --- a/htdocs/product/card.php +++ b/htdocs/product/card.php @@ -2873,7 +2873,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($canvasdisplayactio $result = $measuringUnits->fetchAll('', 'scale', 0, 0, ['t.active' => 1, 't.unit_type' => 'time']); if ($result !== -1) { foreach ($measuringUnits->records as $record) { - $durations[$record->short_label] = dol_ucfirst($record->label) . $plural; + $durations[$record->short_label] = dol_ucfirst((string) $record->label) . $plural; } } print ''.$langs->trans("Duration").''; diff --git a/htdocs/product/class/html.formproduct.class.php b/htdocs/product/class/html.formproduct.class.php index 63c23c4f77f..1bb4e0ca8e3 100644 --- a/htdocs/product/class/html.formproduct.class.php +++ b/htdocs/product/class/html.formproduct.class.php @@ -624,9 +624,9 @@ class FormProduct } $return .= '>'; if ($measuring_style == 'time') { - $return .= $langs->trans(ucfirst($lines->label)); + $return .= $langs->trans(ucfirst((string) $lines->label)); } else { - $return .= $langs->trans($lines->label); + $return .= $langs->trans((string) $lines->label); } $return .= ''; } @@ -695,7 +695,7 @@ class FormProduct } $return .= '>'; - $return .= $langs->trans($lines->label); + $return .= $langs->trans((string) $lines->label); $return .= ''; } } diff --git a/htdocs/public/members/new.php b/htdocs/public/members/new.php index 8af2dd74993..ecea04a1ade 100644 --- a/htdocs/public/members/new.php +++ b/htdocs/public/members/new.php @@ -1060,7 +1060,7 @@ if (getDolGlobalString('MEMBER_SKIP_TABLE') || getDolGlobalString('MEMBER_NEWFOR $result = $measuringUnits->fetchAll('', '', 0, 0, array('t.active' => 1)); $units = array(); foreach ($measuringUnits->records as $lines) { - $units[$lines->short_label] = $langs->trans(ucfirst($lines->label)); + $units[$lines->short_label] = $langs->trans(ucfirst((string) $lines->label)); } $publiccounters = getDolGlobalString("MEMBER_COUNTERS_ARE_PUBLIC"); From 7342a580441eeff87aff2381247a4ce2e8509857 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20FRANCE?= Date: Tue, 28 Oct 2025 01:25:46 +0100 Subject: [PATCH 11/16] clean phpstan baseline for supplier invoice class (#35945) * clean phpstan baseline for supplier invoice class * clean phpstan baseline for supplier invoice class --- dev/build/phpstan/phpstan-baseline.neon | 30 --------- .../fourn/class/fournisseur.facture.class.php | 66 +++++++++---------- 2 files changed, 31 insertions(+), 65 deletions(-) diff --git a/dev/build/phpstan/phpstan-baseline.neon b/dev/build/phpstan/phpstan-baseline.neon index 091061e9512..6e16364b72b 100644 --- a/dev/build/phpstan/phpstan-baseline.neon +++ b/dev/build/phpstan/phpstan-baseline.neon @@ -9444,36 +9444,6 @@ parameters: count: 2 path: ../../../htdocs/fourn/class/fournisseur.facture-rec.ligne.class.php - - - message: '#^Call to function method_exists\(\) with \$this\(FactureFournisseur\) and ''getLibStatut'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: ../../../htdocs/fourn/class/fournisseur.facture.class.php - - - - message: '#^Call to function method_exists\(\) with \$this\(FactureFournisseur\) and ''getNomUrl'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: ../../../htdocs/fourn/class/fournisseur.facture.class.php - - - - message: '#^Call to function property_exists\(\) with \$this\(FactureFournisseur\) and ''date'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: ../../../htdocs/fourn/class/fournisseur.facture.class.php - - - - message: '#^Call to function property_exists\(\) with \$this\(FactureFournisseur\) and ''total_ht'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: ../../../htdocs/fourn/class/fournisseur.facture.class.php - - - - message: '#^Negated boolean expression is always true\.$#' - identifier: booleanNot.alwaysTrue - count: 6 - path: ../../../htdocs/fourn/class/fournisseur.facture.class.php - - message: '#^Parameter \#3 \$remise_percent_ligne of function calcul_price_total expects float, string given\.$#' identifier: argument.type diff --git a/htdocs/fourn/class/fournisseur.facture.class.php b/htdocs/fourn/class/fournisseur.facture.class.php index 2880e10d866..47a94e3a0de 100644 --- a/htdocs/fourn/class/fournisseur.facture.class.php +++ b/htdocs/fourn/class/fournisseur.facture.class.php @@ -1473,7 +1473,7 @@ class FactureFournisseur extends CommonInvoice $error = 0; $this->db->begin(); - if (!$error && !$notrigger) { + if (!$notrigger) { // Call trigger $result = $this->call_trigger('BILL_SUPPLIER_DELETE', $user); if ($result < 0) { @@ -1483,30 +1483,28 @@ class FactureFournisseur extends CommonInvoice // Fin appel triggers } - if (!$error) { - // If invoice was converted into a discount not yet consumed, we remove discount - $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'societe_remise_except'; - $sql .= ' WHERE fk_invoice_supplier_source = '.((int) $rowid); - $sql .= ' AND fk_invoice_supplier_line IS NULL'; - $resql = $this->db->query($sql); + // If invoice was converted into a discount not yet consumed, we remove discount + $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'societe_remise_except'; + $sql .= ' WHERE fk_invoice_supplier_source = '.((int) $rowid); + $sql .= ' AND fk_invoice_supplier_line IS NULL'; + $resql = $this->db->query($sql); - // If invoice has consumned discounts - $this->fetch_lines(); - $list_rowid_det = array(); - foreach ($this->lines as $key => $invoiceline) { - $list_rowid_det[] = $invoiceline->id; - } + // If invoice has consumned discounts + $this->fetch_lines(); + $list_rowid_det = array(); + foreach ($this->lines as $key => $invoiceline) { + $list_rowid_det[] = $invoiceline->id; + } - // Consumned discounts are freed - if (count($list_rowid_det)) { - $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except'; - $sql .= ' SET fk_invoice_supplier = NULL, fk_invoice_supplier_line = NULL'; - $sql .= ' WHERE fk_invoice_supplier_line IN ('.$this->db->sanitize(implode(',', $list_rowid_det)).')'; + // Consumned discounts are freed + if (count($list_rowid_det)) { + $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except'; + $sql .= ' SET fk_invoice_supplier = NULL, fk_invoice_supplier_line = NULL'; + $sql .= ' WHERE fk_invoice_supplier_line IN ('.$this->db->sanitize(implode(',', $list_rowid_det)).')'; - dol_syslog(get_class($this)."::delete", LOG_DEBUG); - if (!$this->db->query($sql)) { - $error++; - } + dol_syslog(get_class($this)."::delete", LOG_DEBUG); + if (!$this->db->query($sql)) { + $error++; } } @@ -1907,7 +1905,7 @@ class FactureFournisseur extends CommonInvoice $resql = $this->db->query($sql); if ($resql) { // Si on incrémente le produit principal et ses composants à la validation de facture fournisseur - if (!$error && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_BILL')) { + if (isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_BILL')) { require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php'; $langs->load("agenda"); @@ -1939,7 +1937,7 @@ class FactureFournisseur extends CommonInvoice } // Triggers call - if (!$error && empty($notrigger)) { + if (empty($notrigger)) { // Call trigger $result = $this->call_trigger('BILL_SUPPLIER_VALIDATE', $user); if ($result < 0) { @@ -2044,9 +2042,7 @@ class FactureFournisseur extends CommonInvoice $result = $this->db->query($sql); if ($result) { - if (!$error) { - $this->oldcopy = clone $this; - } + $this->oldcopy = clone $this; // Si on incremente le produit principal et ses composants a la validation de facture fournisseur, on decremente if ($result >= 0 && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_BILL')) { @@ -2069,7 +2065,7 @@ class FactureFournisseur extends CommonInvoice } } // Triggers call - if (!$error && empty($notrigger)) { + if (empty($notrigger)) { // Call trigger $result = $this->call_trigger('BILL_SUPPLIER_UNVALIDATE', $user); if ($result < 0) { @@ -3438,28 +3434,28 @@ class FactureFournisseur extends CommonInvoice $return .= img_picto('', $picto); $return .= ''; $return .= '
'; - $return .= ''.(method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref).''; + $return .= '' . $this->getNomUrl(1) . ''; if ($selected >= 0) { $return .= ''; } if (!empty($arraydata['thirdparty'])) { $return .= '
'.$arraydata['thirdparty'].''; } - if (property_exists($this, 'date')) { + if (!empty($this->date)) { $return .= '
'.dol_print_date($this->date, 'day').''; } - if (property_exists($this, 'total_ht')) { + if (!empty($this->total_ht)) { $return .= '   '.price($this->total_ht); $return .= ' '.$langs->trans("HT"); $return .= ''; } - if (method_exists($this, 'getLibStatut')) { - $alreadypaid = (empty($arraydata['alreadypaid']) ? 0 : $arraydata['alreadypaid']); - $return .= '
'.$this->getLibStatut(3, $alreadypaid).'
'; - } + $alreadypaid = (empty($arraydata['alreadypaid']) ? 0 : $arraydata['alreadypaid']); + $return .= '
'.$this->getLibStatut(3, $alreadypaid).'
'; + $return .= '
'; $return .= ''; $return .= ''; + return $return; } From 8cbe1b55afb687e8ffa7024dd4d0b0c7eb6a8767 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20FRANCE?= Date: Tue, 28 Oct 2025 01:40:02 +0100 Subject: [PATCH 12/16] add dolBuildUrl (#35947) * add dolBuildUrl * add dolBuildUrl * add dolBuildUrl * add dolBuildUrl * add dolBuildUrl * add dolBuildUrl * add dolBuildUrl * add dolBuildUrl * add dolBuildUrl * add dolBuildUrl * add dolBuildUrl * clean --- htdocs/adherents/agenda.php | 4 +-- htdocs/adherents/card.php | 34 +++++++++---------- htdocs/adherents/note.php | 14 ++++---- htdocs/adherents/partnership.php | 8 ++--- htdocs/adherents/stats/geo.php | 8 ++--- htdocs/adherents/type.php | 16 ++++----- htdocs/adherents/type_translation.php | 10 +++--- htdocs/core/lib/member.lib.php | 6 ++-- htdocs/core/lib/prelevement.lib.php | 13 +++---- .../stocktransfer/stocktransfer_card.php | 14 ++++---- 10 files changed, 64 insertions(+), 63 deletions(-) diff --git a/htdocs/adherents/agenda.php b/htdocs/adherents/agenda.php index a9e3d211440..be8e016718f 100644 --- a/htdocs/adherents/agenda.php +++ b/htdocs/adherents/agenda.php @@ -155,9 +155,9 @@ if ($object->id > 0) { print dol_get_fiche_head($head, 'agenda', $langs->trans("Member"), -1, 'user'); - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; - $morehtmlref = ''; + $morehtmlref = ''; $morehtmlref .= img_picto($langs->trans("Download").' '.$langs->trans("VCard"), 'vcard', 'class="valignmiddle marginleftonly paddingrightonly"'); $morehtmlref .= ''; diff --git a/htdocs/adherents/card.php b/htdocs/adherents/card.php index 8d9f8119448..c7f0b1d9acc 100644 --- a/htdocs/adherents/card.php +++ b/htdocs/adherents/card.php @@ -1680,7 +1680,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) { if (!empty($labeltouse) && is_object($arraydefaultmessage) && $arraydefaultmessage->id > 0) { $subject = (string) $arraydefaultmessage->topic; - $msg = (string) $arraydefaultmessage->content; + $msg = (string) $arraydefaultmessage->content; } $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $object); @@ -1879,9 +1879,9 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) { $rowspan++; } - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; - $morehtmlref = ''; + $morehtmlref = ''; $morehtmlref .= img_picto($langs->trans("Download").' '.$langs->trans("VCard"), 'vcard', 'class="valignmiddle marginleftonly paddingrightonly"'); $morehtmlref .= ''; @@ -2077,7 +2077,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) { // Send if (empty($user->socid)) { if (Adherent::STATUS_VALIDATED == $object->status) { - print ''.$langs->trans('SendMail').''."\n"; + print ' $object->id, 'action' => 'presend', 'mode' => 'init'], true).'#formmailbeforetitle">'.$langs->trans('SendMail').''."\n"; } } @@ -2097,7 +2097,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) { // Modify if ($user->hasRight('adherent', 'creer')) { - print ''.$langs->trans("Modify").''."\n"; + print ' $object->id, 'action' => 'edit'], true).'">'.$langs->trans("Modify").''."\n"; } else { print ''.$langs->trans("Modify").''."\n"; } @@ -2105,7 +2105,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) { // Validate if (Adherent::STATUS_DRAFT == $object->status) { if ($user->hasRight('adherent', 'creer')) { - print ''.$langs->trans("Validate").''."\n"; + print ' $object->id, 'action' => 'valid'], true).'">'.$langs->trans("Validate").''."\n"; } else { print ''.$langs->trans("Validate").''."\n"; } @@ -2114,7 +2114,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) { // Reactivate if (Adherent::STATUS_RESILIATED == $object->status || Adherent::STATUS_EXCLUDED == $object->status) { if ($user->hasRight('adherent', 'creer')) { - print ''.$langs->trans("Reenable")."\n"; + print ' $object->id, 'action' => 'valid'], true).'">'.$langs->trans("Reenable")."\n"; } else { print ''.$langs->trans("Reenable").''."\n"; } @@ -2123,7 +2123,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) { // Resiliate if (Adherent::STATUS_VALIDATED == $object->status) { if ($user->hasRight('adherent', 'supprimer')) { - print ''.$langs->trans("Resiliate")."\n"; + print ' $object->id, 'action' => 'resiliate'], true).'">'.$langs->trans("Resiliate")."\n"; } else { print ''.$langs->trans("Resiliate").''."\n"; } @@ -2132,7 +2132,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) { // Exclude if (Adherent::STATUS_VALIDATED == $object->status) { if ($user->hasRight('adherent', 'supprimer')) { - print ''.$langs->trans("Exclude")."\n"; + print ' $object->id, 'action' => 'exclude'], true).'">'.$langs->trans("Exclude")."\n"; } else { print ''.$langs->trans("Exclude").''."\n"; } @@ -2142,7 +2142,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) { if (isModEnabled('societe') && !$object->socid) { if ($user->hasRight('societe', 'creer')) { if (Adherent::STATUS_DRAFT != $object->status) { - print ''.$langs->trans("CreateDolibarrThirdParty").''."\n"; + print ' $object->id, 'action' => 'create_thirdparty'], true).'" title="'.dol_escape_htmltag($langs->trans("CreateDolibarrThirdPartyDesc")).'">'.$langs->trans("CreateDolibarrThirdParty").''."\n"; } else { print ''.$langs->trans("CreateDolibarrThirdParty").''."\n"; } @@ -2155,7 +2155,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) { if (!$user->socid && !$object->user_id) { if ($user->hasRight('user', 'user', 'creer')) { if (Adherent::STATUS_DRAFT != $object->status) { - print ''.$langs->trans("CreateDolibarrLogin").''."\n"; + print ' $object->id, 'action' => 'create_user'], true).'" title="'.dol_escape_htmltag($langs->trans("CreateDolibarrLoginDesc")).'">'.$langs->trans("CreateDolibarrLogin").''."\n"; } else { print ''.$langs->trans("CreateDolibarrLogin").''."\n"; } @@ -2169,19 +2169,19 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) { $isinspip = $mailmanspip->is_in_spip($object); if ($isinspip == 1) { - print ''.$langs->trans("DeleteIntoSpip").''."\n"; + print ' $object->id, 'action' => 'del_spip'], true).'">'.$langs->trans("DeleteIntoSpip").''."\n"; } if ($isinspip == 0) { - print ''.$langs->trans("AddIntoSpip").''."\n"; + print ' $object->id, 'action' => 'add_spip'], true).'">'.$langs->trans("AddIntoSpip").''."\n"; } } // Merge - print dolGetButtonAction($langs->trans('MergeMembers'), $langs->trans('Merge'), 'danger', $_SERVER["PHP_SELF"].'?id='.$object->id.'&action=merge&token='.newToken(), '', $user->hasRight('adherent', 'supprimer')); + print dolGetButtonAction($langs->trans('MergeMembers'), $langs->trans('Merge'), 'danger', dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id, 'action' => 'merge'], true), '', $user->hasRight('adherent', 'supprimer')); // Delete if ($user->hasRight('adherent', 'supprimer')) { - print ''.$langs->trans("Delete").''."\n"; + print ' $object->id, 'action' => 'delete'], true).'">'.$langs->trans("Delete").''."\n"; } else { print ''.$langs->trans("Delete").''."\n"; } @@ -2240,9 +2240,9 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) { $MAXEVENT = 10; $morehtmlcenter = ''; - $messagingUrl = DOL_URL_ROOT.'/adherents/messaging.php?rowid='.$object->id; + $messagingUrl = dolBuildUrl(DOL_URL_ROOT.'/adherents/messaging.php', ['id' => $object->id]); $morehtmlcenter .= dolGetButtonTitle($langs->trans('ShowAsConversation'), '', 'fa fa-comments imgforviewmode', $messagingUrl, '', 1); - $morehtmlcenter .= dolGetButtonTitle($langs->trans('SeeAll'), '', 'fa fa-bars imgforviewmode', DOL_URL_ROOT.'/adherents/agenda.php?id='.$object->id); + $morehtmlcenter .= dolGetButtonTitle($langs->trans('SeeAll'), '', 'fa fa-bars imgforviewmode', dolBuildUrl(DOL_URL_ROOT.'/adherents/agenda.php', ['id' => $object->id])); // List of actions on element include_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php'; diff --git a/htdocs/adherents/note.php b/htdocs/adherents/note.php index 396fd4c7adc..817220acc66 100644 --- a/htdocs/adherents/note.php +++ b/htdocs/adherents/note.php @@ -28,11 +28,6 @@ // Load Dolibarr environment require '../main.inc.php'; -require_once DOL_DOCUMENT_ROOT.'/core/lib/member.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php'; -require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent_type.class.php'; - - /** * @var Conf $conf * @var DoliDB $db @@ -41,6 +36,11 @@ require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent_type.class.php'; * @var User $user */ +require_once DOL_DOCUMENT_ROOT.'/core/lib/member.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php'; +require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent_type.class.php'; + + // Load translation files required by the page $langs->loadLangs(array("companies", "members", "bills")); @@ -128,9 +128,9 @@ if (is_object($adht)) { print '
'; print ''; - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; - $morehtmlref = ''; + $morehtmlref = ''; $morehtmlref .= img_picto($langs->trans("Download").' '.$langs->trans("VCard"), 'vcard', 'class="valignmiddle marginleftonly paddingrightonly"'); $morehtmlref .= ''; diff --git a/htdocs/adherents/partnership.php b/htdocs/adherents/partnership.php index 11a94d5657f..a9873aa6a08 100644 --- a/htdocs/adherents/partnership.php +++ b/htdocs/adherents/partnership.php @@ -2,7 +2,7 @@ /* Copyright (C) 2017 Laurent Destailleur * Copyright (C) 2021 NextGestion * Copyright (C) 2024 Alexandre Spangaro - * Copyright (C) 2024 Frédéric France + * Copyright (C) 2024-2025 Frédéric France * * 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 @@ -175,7 +175,7 @@ if ($id > 0) { print dol_get_fiche_head($head, 'partnership', $langs->trans("ThirdParty"), -1, 'user'); - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; dol_banner_tab($object, 'rowid', $linkback); @@ -272,12 +272,12 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea } else { if (!$adht->subscription) { print $langs->trans("SubscriptionNotRecorded"); - if ($fadherent->statut > 0) { + if ($fadherent->status > 0) { print " ".img_warning($langs->trans("Late")); // Display a delay picto only if it is not a draft and is not canceled } } else { print $langs->trans("SubscriptionNotReceived"); - if ($fadherent->statut > 0) { + if ($fadherent->status > 0) { print " ".img_warning($langs->trans("Late")); // Display a delay picto only if it is not a draft and is not canceled } } diff --git a/htdocs/adherents/stats/geo.php b/htdocs/adherents/stats/geo.php index 31416a8f55c..84eb32b7330 100644 --- a/htdocs/adherents/stats/geo.php +++ b/htdocs/adherents/stats/geo.php @@ -184,7 +184,7 @@ if ($mode) { $obj = $db->fetch_object($resql); if ($mode == 'memberbycountry') { $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 : ''.$langs->trans("Unknown").'')), + 'label' => (string) (($obj->code && $langs->trans("Country".$obj->code) != "Country".$obj->code) ? picto_from_langcode($obj->code, 'class="saturatemedium paddingrightonly"', 1).' '.$langs->trans("Country".$obj->code) : ($obj->label ? $obj->label : ''.$langs->trans("Unknown").'')), 'label_en' => (string) (($obj->code && $langsen->transnoentitiesnoconv("Country".$obj->code) != "Country".$obj->code) ? $langsen->transnoentitiesnoconv("Country".$obj->code) : ($obj->label ? $obj->label : ''.$langs->trans("Unknown").'')), 'code' => (string) $obj->code, 'nb' => (int) $obj->nb, @@ -194,7 +194,7 @@ if ($mode) { } if ($mode == 'memberbyregion') { //+ $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 : ''.$langs->trans("Unknown").'')), + 'label' => (string) (($obj->code && $langs->trans("Country".$obj->code) != "Country".$obj->code) ? picto_from_langcode($obj->code, 'class="saturatemedium paddingrightonly"', 1).' '.$langs->trans("Country".$obj->code) : ($obj->label ? $obj->label : ''.$langs->trans("Unknown").'')), 'label_en' => (string) (($obj->code && $langsen->transnoentitiesnoconv("Country".$obj->code) != "Country".$obj->code) ? $langsen->transnoentitiesnoconv("Country".$obj->code) : ($obj->label ? $obj->label : ''.$langs->trans("Unknown").'')), 'label2' => ($obj->label2 ? $obj->label2 : ''.$langs->trans("Unknown").''), 'nb' => (int) $obj->nb, @@ -204,7 +204,7 @@ if ($mode) { } if ($mode == 'memberbystate') { $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 : ''.$langs->trans("Unknown").'')), + 'label' => (string) (($obj->code && $langs->trans("Country".$obj->code) != "Country".$obj->code) ? picto_from_langcode($obj->code, 'class="saturatemedium paddingrightonly"', 1).' '.$langs->trans("Country".$obj->code) : ($obj->label ? $obj->label : ''.$langs->trans("Unknown").'')), 'label_en' => (string) (($obj->code && $langsen->transnoentitiesnoconv("Country".$obj->code) != "Country".$obj->code) ? $langsen->transnoentitiesnoconv("Country".$obj->code) : ($obj->label ? $obj->label : ''.$langs->trans("Unknown").'')), 'label2' => ($obj->label2 ? $obj->label2 : ''.$langs->trans("Unknown").''), 'nb' => (int) $obj->nb, @@ -214,7 +214,7 @@ if ($mode) { } if ($mode == 'memberbytown') { $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 : ''.$langs->trans("Unknown").'')), + 'label' => (string) (($obj->code && $langs->trans("Country".$obj->code) != "Country".$obj->code) ? picto_from_langcode($obj->code, 'class="saturatemedium paddingrightonly"', 1).' '.$langs->trans("Country".$obj->code) : ($obj->label ? $obj->label : ''.$langs->trans("Unknown").'')), 'label_en' => (string) (($obj->code && $langsen->transnoentitiesnoconv("Country".$obj->code) != "Country".$obj->code) ? $langsen->transnoentitiesnoconv("Country".$obj->code) : ($obj->label ? $obj->label : ''.$langs->trans("Unknown").'')), 'label2' => (string) ($obj->label2 ? $obj->label2 : ''.$langs->trans("Unknown").''), 'nb' => (int) $obj->nb, diff --git a/htdocs/adherents/type.php b/htdocs/adherents/type.php index 30cdbf6d961..02a39779016 100644 --- a/htdocs/adherents/type.php +++ b/htdocs/adherents/type.php @@ -33,12 +33,6 @@ // Load Dolibarr environment require '../main.inc.php'; -require_once DOL_DOCUMENT_ROOT.'/core/lib/member.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php'; -require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent_type.class.php'; -require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php'; -require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php'; - /** * @var Conf $conf * @var DoliDB $db @@ -47,14 +41,20 @@ require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php'; * @var User $user */ +require_once DOL_DOCUMENT_ROOT.'/core/lib/member.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php'; +require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent_type.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php'; +require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php'; + // Load translation files required by the page $langs->load("members"); -$rowid = GETPOSTINT('rowid'); +$rowid = GETPOSTINT('rowid'); $action = GETPOST('action', 'aZ09'); $massaction = GETPOST('massaction', 'alpha'); $cancel = GETPOST('cancel', 'alpha'); -$toselect = GETPOST('toselect', 'array:int'); +$toselect = GETPOST('toselect', 'array:int'); $contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : str_replace('_', '', basename(dirname(__FILE__)).basename(__FILE__, '.php')); // To manage different context of search $backtopage = GETPOST('backtopage', 'alpha'); $mode = GETPOST('mode', 'alpha'); diff --git a/htdocs/adherents/type_translation.php b/htdocs/adherents/type_translation.php index dea20260bfb..839d0c1715e 100644 --- a/htdocs/adherents/type_translation.php +++ b/htdocs/adherents/type_translation.php @@ -29,11 +29,6 @@ // Load Dolibarr environment require '../main.inc.php'; -require_once DOL_DOCUMENT_ROOT.'/core/lib/member.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent_type.class.php'; -require_once DOL_DOCUMENT_ROOT.'/core/class/html.formadmin.class.php'; - /** * @var Conf $conf * @var DoliDB $db @@ -42,6 +37,11 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/html.formadmin.class.php'; * @var User $user */ +require_once DOL_DOCUMENT_ROOT.'/core/lib/member.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent_type.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/html.formadmin.class.php'; + // Load translation files required by the page $langs->loadLangs(array('members', 'languages')); diff --git a/htdocs/core/lib/member.lib.php b/htdocs/core/lib/member.lib.php index ba01f38a2b2..836abcd0cb8 100644 --- a/htdocs/core/lib/member.lib.php +++ b/htdocs/core/lib/member.lib.php @@ -39,7 +39,7 @@ function member_prepare_head(Adherent $object) $h = 0; $head = array(); - $head[$h][0] = dolBuildUrl(DOL_URL_ROOT . '/adherents/card.php', ['rowid' => $object->id]); + $head[$h][0] = dolBuildUrl(DOL_URL_ROOT . '/adherents/card.php', ['id' => $object->id]); $head[$h][1] = $langs->trans("Member"); $head[$h][2] = 'general'; $h++; @@ -56,7 +56,7 @@ function member_prepare_head(Adherent $object) if ($user->hasRight('adherent', 'cotisation', 'lire')) { $nbSubscription = is_array($object->subscriptions) ? count($object->subscriptions) : 0; - $head[$h][0] = dolBuildUrl(DOL_URL_ROOT . '/adherents/subscription.php', ['rowid' => $object->id]); + $head[$h][0] = dolBuildUrl(DOL_URL_ROOT . '/adherents/subscription.php', ['id' => $object->id]); $head[$h][1] = $langs->trans("Subscriptions"); $head[$h][2] = 'subscription'; if ($nbSubscription > 0) { @@ -68,7 +68,7 @@ function member_prepare_head(Adherent $object) if (getDolGlobalString('PARTNERSHIP_IS_MANAGED_FOR') == 'member') { if ($user->hasRight('partnership', 'read')) { $nbPartnership = is_array($object->partnerships) ? count($object->partnerships) : 0; - $head[$h][0] = dolBuildUrl(DOL_URL_ROOT . '/partnership/partnership_list.php', ['rowid' => $object->id]); + $head[$h][0] = dolBuildUrl(DOL_URL_ROOT . '/partnership/partnership_list.php', ['id' => $object->id]); $head[$h][1] = $langs->trans("Partnerships"); $nbNote = 0; $sql = "SELECT COUNT(n.rowid) as nb"; diff --git a/htdocs/core/lib/prelevement.lib.php b/htdocs/core/lib/prelevement.lib.php index 830369fe846..67e7dbf68d2 100644 --- a/htdocs/core/lib/prelevement.lib.php +++ b/htdocs/core/lib/prelevement.lib.php @@ -3,6 +3,7 @@ * Copyright (C) 2010 Laurent Destailleur * Copyright (C) 2011 Regis Houssin * Copyright (C) 2024 MDW + * Copyright (C) 2025 Frédéric France * * 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 @@ -48,7 +49,7 @@ function prelevement_prepare_head(BonPrelevement $object) $titleoftab = "BankTransferReceipts"; } - $head[$h][0] = DOL_URL_ROOT.'/compta/prelevement/card.php?id='.$object->id; + $head[$h][0] = dolBuildUrl(DOL_URL_ROOT.'/compta/prelevement/card.php', ['id' => $object->id]); $head[$h][1] = $langs->trans($titleoftab); $head[$h][2] = 'prelevement'; $h++; @@ -61,17 +62,17 @@ function prelevement_prepare_head(BonPrelevement $object) $titleoftab = $langs->trans("Salaries"); } - $head[$h][0] = DOL_URL_ROOT.'/compta/prelevement/factures.php?id='.$object->id; + $head[$h][0] = dolBuildUrl(DOL_URL_ROOT.'/compta/prelevement/factures.php', ['id' => $object->id]); $head[$h][1] = $titleoftab; $head[$h][2] = 'invoices'; $h++; - $head[$h][0] = DOL_URL_ROOT.'/compta/prelevement/fiche-rejet.php?id='.$object->id; + $head[$h][0] = dolBuildUrl(DOL_URL_ROOT.'/compta/prelevement/fiche-rejet.php', ['id' => $object->id]); $head[$h][1] = $langs->trans("Rejects"); $head[$h][2] = 'rejects'; $h++; - $head[$h][0] = DOL_URL_ROOT.'/compta/prelevement/fiche-stat.php?id='.$object->id; + $head[$h][0] = dolBuildUrl(DOL_URL_ROOT.'/compta/prelevement/fiche-stat.php', ['id' => $object->id]); $head[$h][1] = $langs->trans("Statistics"); $head[$h][2] = 'statistics'; $h++; @@ -132,14 +133,14 @@ function bon_prelevement_prepare_head(BonPrelevement $object, $nbOfInvoices, $nb $h = 0; $head = array(); - $head[$h][0] = DOL_URL_ROOT.'/compta/prelevement/create.php?type=bank-transfer'; + $head[$h][0] = dolBuildUrl(DOL_URL_ROOT.'/compta/prelevement/create.php', ['type' => 'bank-transfer']); $head[$h][1] = ($nbOfInvoices <= 0 ? $langs->trans("Invoices") : $langs->trans("Invoices").''.$nbOfInvoices.''); $head[$h][2] = 'invoice'; $h++; // Salaries - $head[$h][0] = DOL_URL_ROOT."/compta/prelevement/create.php?type=bank-transfer&sourcetype=salary"; + $head[$h][0] = dolBuildUrl(DOL_URL_ROOT.'/compta/prelevement/create.php', ['type' => 'bank-transfer', 'sourcetype' => 'salary']); $head[$h][1] = ($nbOfSalaryInvoice <= 0 ? $langs->trans("Salaries") : $langs->trans("Salaries").''.$nbOfSalaryInvoice.''); $head[$h][2] = 'salary'; $h++; diff --git a/htdocs/product/stock/stocktransfer/stocktransfer_card.php b/htdocs/product/stock/stocktransfer/stocktransfer_card.php index 77c6a85c4dd..886a5f829b6 100644 --- a/htdocs/product/stock/stocktransfer/stocktransfer_card.php +++ b/htdocs/product/stock/stocktransfer/stocktransfer_card.php @@ -1,7 +1,7 @@ * Copyright (C) 2024-2025 MDW - * Copyright (C) 2024-2025 Frédéric France + * Copyright (C) 2024-2025 Frédéric France * Copyright (C) 2025 Pierre Ardoin * * This program is free software; you can redistribute it and/or modify @@ -54,11 +54,11 @@ if (isModEnabled('incoterm')) { // Get parameters $id = GETPOSTINT('id'); -$ref = GETPOST('ref', 'alpha'); +$ref = GETPOST('ref', 'alpha'); $action = GETPOST('action', 'aZ09'); -$confirm = GETPOST('confirm', 'alpha'); -$cancel = GETPOST('cancel', 'aZ09'); +$confirm = GETPOST('confirm', 'alpha'); +$cancel = GETPOST('cancel', 'aZ09'); $contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : str_replace('_', '', basename(dirname(__FILE__)).basename(__FILE__, '.php')); // To manage different context of search $backtopage = GETPOST('backtopage', 'alpha'); // if not set, a default page will be used $backtopageforcancel = GETPOST('backtopageforcancel', 'alpha'); // if not set, $backtopage will be used @@ -66,7 +66,7 @@ $qty = GETPOSTINT('qty'); $fk_product = GETPOSTINT('fk_product'); $fk_warehouse_source = GETPOSTINT('fk_warehouse_source'); $fk_warehouse_destination = GETPOSTINT('fk_warehouse_destination'); -$lineid = GETPOSTINT('lineid'); +$lineid = GETPOSTINT('lineid'); $label = GETPOST('label', 'alpha'); $batch = GETPOST('batch', 'alpha'); $code_inv = GETPOST('inventorycode', 'alphanohtml'); @@ -132,14 +132,14 @@ if ($reshook < 0) { if (empty($reshook)) { $error = 0; - $backurlforlist = dol_buildpath('/product/stock/stocktransfer/stocktransfer_list.php', 1); + $backurlforlist = dolBuildUrl(DOL_URL_ROOT.'/product/stock/stocktransfer/stocktransfer_list.php'); if (empty($backtopage) || ($cancel && empty($id))) { if (empty($backtopage) || ($cancel && strpos($backtopage, '__ID__'))) { if (empty($id) && (($action != 'add' && $action != 'create') || $cancel)) { $backtopage = $backurlforlist; } else { - $backtopage = dol_buildpath('/product/stock/stocktransfer/stocktransfer_card.php', 1).'?id='.($id > 0 ? $id : '__ID__'); + $backtopage = dolBuildUrl(DOL_URL_ROOT.'/product/stock/stocktransfer/stocktransfer_card.php', ['id' => ($id > 0 ? $id : '__ID__')]); } } } From 34a1c824b7b7ddb9fe6fba65023c3e5f1c6cd1fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20FRANCE?= Date: Tue, 28 Oct 2025 02:21:06 +0100 Subject: [PATCH 13/16] NEW public pages donation, ticket and member use captcha setup (#35913) * donation use captcha setup * donation use captcha setup * member use captcha setup * member use captcha setup * member use captcha setup * member use captcha setup * member use captcha setup * ticket use captcha setup * ticket use captcha setup * clean * clean --- dev/build/phpstan/phpstan-baseline.neon | 6 --- htdocs/public/donations/new.php | 63 +++++++++++++++++------ htdocs/public/members/new.php | 67 +++++++++++++++++++------ htdocs/public/ticket/create_ticket.php | 51 ++++++++++++++++--- 4 files changed, 143 insertions(+), 44 deletions(-) diff --git a/dev/build/phpstan/phpstan-baseline.neon b/dev/build/phpstan/phpstan-baseline.neon index 6e16364b72b..86c5528547d 100644 --- a/dev/build/phpstan/phpstan-baseline.neon +++ b/dev/build/phpstan/phpstan-baseline.neon @@ -13836,12 +13836,6 @@ parameters: count: 2 path: ../../../htdocs/public/fichinter/agendaexport.php - - - message: '#^Variable \$php_self might not be defined\.$#' - identifier: variable.undefined - count: 1 - path: ../../../htdocs/public/members/new.php - - message: '#^Call to function is_numeric\(\) with int will always evaluate to true\.$#' identifier: function.alreadyNarrowedType diff --git a/htdocs/public/donations/new.php b/htdocs/public/donations/new.php index 8f2f7d02ca0..4585c048217 100644 --- a/htdocs/public/donations/new.php +++ b/htdocs/public/donations/new.php @@ -105,6 +105,39 @@ $object = new Don($db); $user->loadDefaultValues(); +$captchaobj = null; +if (getDolGlobalString('MAIN_SECURITY_ENABLECAPTCHA_DONATION')) { + require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php'; + $captcha = getDolGlobalString('MAIN_SECURITY_ENABLECAPTCHA_HANDLER', 'standard'); + // List of directories where we can find captcha handlers + $dirModCaptcha = array_merge( + array( + 'main' => '/core/modules/security/captcha/' + ), + is_array($conf->modules_parts['captcha']) ? $conf->modules_parts['captcha'] : array() + ); + $fullpathclassfile = ''; + foreach ($dirModCaptcha as $dir) { + $fullpathclassfile = dol_buildpath($dir."modCaptcha".ucfirst($captcha).'.class.php', 0, 2); + if ($fullpathclassfile) { + break; + } + } + if ($fullpathclassfile) { + include_once $fullpathclassfile; + // Charging the numbering class + $classname = "modCaptcha".ucfirst($captcha); + if (class_exists($classname)) { + $captchaobj = new $classname($db, $conf, $langs, $user); + '@phan-var-force ModeleCaptcha $captchaobj'; + /** @var ModeleCaptcha $captchaobj */ + } else { + print 'Error, the captcha handler class '.$classname.' was not found after the include'; + } + } else { + print 'Error, the captcha handler '.$captcha.' has no class file found modCaptcha'.ucfirst($captcha); + } +} /** * Show header for new donation @@ -188,9 +221,13 @@ if (empty($reshook) && $action == 'add') { // Test on permission not required he } // Check Captcha code if is enabled - if (getDolGlobalString('MAIN_SECURITY_ENABLECAPTCHA_DONATION')) { - $sessionkey = 'dol_antispam_value'; - $ok = (array_key_exists($sessionkey, $_SESSION) && (strtolower($_SESSION[$sessionkey]) == strtolower(GETPOST('code')))); + $ok = false; + if (getDolGlobalString('MAIN_SECURITY_ENABLECAPTCHA_DONATION') && is_object($captchaobj)) { + if (method_exists($captchaobj, 'validateCodeAfterLoginSubmit')) { + $ok = $captchaobj->validateCodeAfterLoginSubmit(); // @phan-suppress-current-line PhanUndeclaredMethod + } else { + print 'Error, the captcha handler '.get_class($captchaobj).' does not have any method validateCodeAfterLoginSubmit()'; + } if (!$ok) { $error++; $langs->load("errors"); @@ -524,19 +561,15 @@ if (!$action || $action == 'create') { print ''."\n"; // Display Captcha code if is enabled - if (getDolGlobalString('MAIN_SECURITY_ENABLECAPTCHA_DONATION')) { - require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php'; - print ''; - print ''; - print ''; - print ''; - print ''; - print ''; - print ''.img_picto($langs->trans("Refresh"), 'refresh', 'id="captcha_refresh_img"').''; - print ''; - print ''; + if (getDolGlobalString('MAIN_SECURITY_ENABLECAPTCHA_DONATION') && is_object($captchaobj)) { + print '
'; + if (method_exists($captchaobj, 'getCaptchaCodeForForm')) { + print $captchaobj->getCaptchaCodeForForm(''); // @phan-suppress-current-line PhanUndeclaredMethod + } else { + print 'Error, the captcha handler '.get_class($captchaobj).' does not have any method getCaptchaCodeForForm()'; + } + print '
'; } - print "\n"; print dol_get_fiche_end(); diff --git a/htdocs/public/members/new.php b/htdocs/public/members/new.php index ecea04a1ade..6aaef140ce7 100644 --- a/htdocs/public/members/new.php +++ b/htdocs/public/members/new.php @@ -113,6 +113,40 @@ $object = new Adherent($db); $user->loadDefaultValues(); +$captchaobj = null; +if (getDolGlobalString('MAIN_SECURITY_ENABLECAPTCHA_MEMBER')) { + require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php'; + $captcha = getDolGlobalString('MAIN_SECURITY_ENABLECAPTCHA_HANDLER', 'standard'); + // List of directories where we can find captcha handlers + $dirModCaptcha = array_merge( + array( + 'main' => '/core/modules/security/captcha/' + ), + is_array($conf->modules_parts['captcha']) ? $conf->modules_parts['captcha'] : array() + ); + $fullpathclassfile = ''; + foreach ($dirModCaptcha as $dir) { + $fullpathclassfile = dol_buildpath($dir."modCaptcha".ucfirst($captcha).'.class.php', 0, 2); + if ($fullpathclassfile) { + break; + } + } + if ($fullpathclassfile) { + include_once $fullpathclassfile; + // Charging the numbering class + $classname = "modCaptcha".ucfirst($captcha); + if (class_exists($classname)) { + $captchaobj = new $classname($db, $conf, $langs, $user); + '@phan-var-force ModeleCaptcha $captchaobj'; + /** @var ModeleCaptcha $captchaobj */ + } else { + print 'Error, the captcha handler class '.$classname.' was not found after the include'; + } + } else { + print 'Error, the captcha handler '.$captcha.' has no class file found modCaptcha'.ucfirst($captcha); + } +} + /** * Force switching conf of entity, even if user is connected * Fox example when trying to go on public form of an other entity @@ -341,9 +375,13 @@ if (empty($reshook) && $action == 'add') { // Test on permission not required he } // Check Captcha code if is enabled - if (getDolGlobalString('MAIN_SECURITY_ENABLECAPTCHA_MEMBER')) { - $sessionkey = 'dol_antispam_value'; - $ok = (array_key_exists($sessionkey, $_SESSION) && (strtolower($_SESSION[$sessionkey]) == strtolower(GETPOST('code')))); + $ok = false; + if (getDolGlobalString('MAIN_SECURITY_ENABLECAPTCHA_MEMBER') && is_object($captchaobj)) { + if (method_exists($captchaobj, 'validateCodeAfterLoginSubmit')) { + $ok = $captchaobj->validateCodeAfterLoginSubmit(); // @phan-suppress-current-line PhanUndeclaredMethod + } else { + print 'Error, the captcha handler '.get_class($captchaobj).' does not have any method validateCodeAfterLoginSubmit()'; + } if (!$ok) { $error++; $langs->load("errors"); @@ -371,8 +409,8 @@ if (empty($reshook) && $action == 'add') { // Test on permission not required he $adh->town = GETPOST('town'); $adh->email = GETPOST('member_email', 'aZ09arobase'); if (!getDolGlobalString('ADHERENT_LOGIN_NOT_REQUIRED')) { - $adh->login = GETPOST('login'); - $adh->pass = GETPOST('pass1', 'password'); + $adh->login = GETPOST('login'); + $adh->pass = GETPOST('pass1', 'password'); } $adh->photo = GETPOST('photo'); $adh->country_id = getDolGlobalInt("MEMBER_NEWFORM_FORCECOUNTRYCODE", GETPOSTINT('country_id')); @@ -1025,17 +1063,14 @@ if (getDolGlobalString('MEMBER_SKIP_TABLE') || getDolGlobalString('MEMBER_NEWFOR } // Display Captcha code if is enabled - if (getDolGlobalString('MAIN_SECURITY_ENABLECAPTCHA_MEMBER')) { - require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php'; - print ''; - print ''; - print ''; - print ''; - print ''; - print ''; - print ''.img_picto($langs->trans("Refresh"), 'refresh', 'id="captcha_refresh_img"').''; - print ''; - print ''; + if (getDolGlobalString('MAIN_SECURITY_ENABLECAPTCHA_MEMBER') && is_object($captchaobj)) { + print '
'; + if (method_exists($captchaobj, 'getCaptchaCodeForForm')) { + print $captchaobj->getCaptchaCodeForForm(''); // @phan-suppress-current-line PhanUndeclaredMethod + } else { + print 'Error, the captcha handler '.get_class($captchaobj).' does not have any method getCaptchaCodeForForm()'; + } + print '
'; } print "\n"; diff --git a/htdocs/public/ticket/create_ticket.php b/htdocs/public/ticket/create_ticket.php index 6b8cf0c4a51..967ea64f878 100644 --- a/htdocs/public/ticket/create_ticket.php +++ b/htdocs/public/ticket/create_ticket.php @@ -107,6 +107,43 @@ if (!isModEnabled('ticket')) { httponly_accessforbidden('Module Ticket not enabled'); } +if (!is_object($user)) { + $user = new User($db); +} + +$captchaobj = null; +if (getDolGlobalString('MAIN_SECURITY_ENABLECAPTCHA_TICKET')) { + require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php'; + $captcha = getDolGlobalString('MAIN_SECURITY_ENABLECAPTCHA_HANDLER', 'standard'); + // List of directories where we can find captcha handlers + $dirModCaptcha = array_merge( + array( + 'main' => '/core/modules/security/captcha/' + ), + is_array($conf->modules_parts['captcha']) ? $conf->modules_parts['captcha'] : array() + ); + $fullpathclassfile = ''; + foreach ($dirModCaptcha as $dir) { + $fullpathclassfile = dol_buildpath($dir."modCaptcha".ucfirst($captcha).'.class.php', 0, 2); + if ($fullpathclassfile) { + break; + } + } + if ($fullpathclassfile) { + include_once $fullpathclassfile; + // Charging the numbering class + $classname = "modCaptcha".ucfirst($captcha); + if (class_exists($classname)) { + $captchaobj = new $classname($db, $conf, $langs, $user); + '@phan-var-force ModeleCaptcha $captchaobj'; + /** @var ModeleCaptcha $captchaobj */ + } else { + print 'Error, the captcha handler class '.$classname.' was not found after the include'; + } + } else { + print 'Error, the captcha handler '.$captcha.' has no class file found modCaptcha'.ucfirst($captcha); + } +} /* * Actions @@ -243,9 +280,13 @@ if (empty($reshook)) { } // Check Captcha code if is enabled - if (getDolGlobalInt('MAIN_SECURITY_ENABLECAPTCHA_TICKET')) { - $sessionkey = 'dol_antispam_value'; - $ok = (array_key_exists($sessionkey, $_SESSION) && (strtolower($_SESSION[$sessionkey]) === strtolower(GETPOST('code', 'restricthtml')))); + $ok = false; + if (getDolGlobalString('MAIN_SECURITY_ENABLECAPTCHA_TICKET') && is_object($captchaobj)) { + if (method_exists($captchaobj, 'validateCodeAfterLoginSubmit')) { + $ok = $captchaobj->validateCodeAfterLoginSubmit(); // @phan-suppress-current-line PhanUndeclaredMethod + } else { + print 'Error, the captcha handler '.get_class($captchaobj).' does not have any method validateCodeAfterLoginSubmit()'; + } if (!$ok) { $error++; array_push($object->errors, $langs->trans("ErrorBadValueForCode")); @@ -295,10 +336,6 @@ if (empty($reshook)) { $object->category_code = GETPOST("category_code", 'aZ09'); $object->severity_code = GETPOST("severity_code", 'aZ09'); - if (!is_object($user)) { - $user = new User($db); - } - // create third-party with contact $usertoassign = 0; if ($with_contact && !($with_contact->id > 0)) { From 88cf4654b4a2742dea1fa866813ada6eb09f3b82 Mon Sep 17 00:00:00 2001 From: Mathieu Pellegrin Date: Tue, 28 Oct 2025 02:22:15 +0100 Subject: [PATCH 14/16] FIX #35884 (#35885) Change GETPOST method for password fields to use "password" instead of "alpha". Co-authored-by: Laurent Destailleur --- htdocs/install/step5.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/install/step5.php b/htdocs/install/step5.php index 28d5e8dc35b..5dc79612d83 100644 --- a/htdocs/install/step5.php +++ b/htdocs/install/step5.php @@ -101,8 +101,8 @@ if (!empty($action) && preg_match('/upgrade/i', $action)) { $langs->loadLangs(array("admin", "install")); $login = GETPOST('login', 'alpha') ? GETPOST('login', 'alpha') : (empty($argv[5]) ? '' : $argv[5]); -$pass = GETPOST('pass', 'alpha') ? GETPOST('pass', 'alpha') : (empty($argv[6]) ? '' : $argv[6]); -$pass_verif = GETPOST('pass_verif', 'alpha') ? GETPOST('pass_verif', 'alpha') : (empty($argv[7]) ? '' : $argv[7]); +$pass = GETPOST('pass', 'password') ? GETPOST('pass', 'password') : (empty($argv[6]) ? '' : $argv[6]); +$pass_verif = GETPOST('pass_verif', 'password') ? GETPOST('pass_verif', 'password') : (empty($argv[7]) ? '' : $argv[7]); $success = 0; From e69f56acbefa3936770da4e52f2540765a434f53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Charl=C3=A8ne=20Benke?= <1179011+defrance@users.noreply.github.com> Date: Tue, 28 Oct 2025 02:26:49 +0100 Subject: [PATCH 15/16] New Add color fields on user group (#35646) * Add color column to llx_usergroup table * Add color column to llx_usergroup table * Add color property to user group class * Add color attribute to user group card * Add ColorGroup translation to users.lang * Add ColorGroup translation in French language file * Fix SQL syntax for adding color column in llx_usergroup * Fix SQL syntax for adding color column in llx_usergroup * Fix color column addition in llx_usergroup table --------- Co-authored-by: Laurent Destailleur --- .../install/mysql/migration/22.0.0-23.0.0.sql | 1 + htdocs/install/mysql/tables/llx_usergroup.sql | 1 + htdocs/langs/en_US/users.lang | 1 + htdocs/langs/fr_FR/users.lang | 1 + htdocs/user/class/usergroup.class.php | 7 ++++++ htdocs/user/group/card.php | 25 +++++++++++++++++++ 6 files changed, 36 insertions(+) diff --git a/htdocs/install/mysql/migration/22.0.0-23.0.0.sql b/htdocs/install/mysql/migration/22.0.0-23.0.0.sql index 8391017c2b4..bfb3dca7908 100644 --- a/htdocs/install/mysql/migration/22.0.0-23.0.0.sql +++ b/htdocs/install/mysql/migration/22.0.0-23.0.0.sql @@ -42,6 +42,7 @@ ALTER TABLE llx_opensurvey_user_studs ADD COLUMN tms timestamp DEFAULT CURRENT_T -- V23 migration +ALTER TABLE llx_usergroup ADD color VARCHAR(6) AFTER tms; UPDATE llx_actioncomm SET elementtype = 'project_task' WHERE elementtype = 'task'; diff --git a/htdocs/install/mysql/tables/llx_usergroup.sql b/htdocs/install/mysql/tables/llx_usergroup.sql index 959dfbca3d9..e826728df5c 100644 --- a/htdocs/install/mysql/tables/llx_usergroup.sql +++ b/htdocs/install/mysql/tables/llx_usergroup.sql @@ -25,6 +25,7 @@ create table llx_usergroup entity integer DEFAULT 1 NOT NULL, -- multi company id datec datetime, tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + color varchar(6), note text, model_pdf varchar(255) DEFAULT NULL )ENGINE=innodb; diff --git a/htdocs/langs/en_US/users.lang b/htdocs/langs/en_US/users.lang index 5f3120b67d1..8eddb0d1734 100644 --- a/htdocs/langs/en_US/users.lang +++ b/htdocs/langs/en_US/users.lang @@ -151,3 +151,4 @@ TotalAPICall=Total API Calls TOTP=TOTP (Time-base One-Time Password) UserRemovedFromGroup=User removed from group UserSetInGroup=User set in group +ColorGroup=Color of group diff --git a/htdocs/langs/fr_FR/users.lang b/htdocs/langs/fr_FR/users.lang index 462369b7057..dce43a8099c 100644 --- a/htdocs/langs/fr_FR/users.lang +++ b/htdocs/langs/fr_FR/users.lang @@ -149,3 +149,4 @@ SocialNetworksUser=Réseaux sociaux de l'utilisateur MyTeam=Mon équipe TotalAPICall=Nombre total d'appels API TOTP=TOTP (mot de passe à usage unique basé sur le temps) +ColorGroup=Couleur du groupe diff --git a/htdocs/user/class/usergroup.class.php b/htdocs/user/class/usergroup.class.php index 8ddb3a05416..328f6a641a5 100644 --- a/htdocs/user/class/usergroup.class.php +++ b/htdocs/user/class/usergroup.class.php @@ -9,6 +9,7 @@ * Copyright (C) 2019 Abbes Bahfir * Copyright (C) 2023-2025 Frédéric France * Copyright (C) 2024-2025 MDW + * Copyright (C) 2025 Charlene Benke * * 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 @@ -90,6 +91,11 @@ class UserGroup extends CommonObject */ public $note; + /** + * @var string Color + */ + public $color; + /** * @var User[] Array of users */ @@ -127,6 +133,7 @@ class UserGroup extends CommonObject 'note' => array('type' => 'html', 'label' => 'Description', 'enabled' => 1, 'visible' => 1, 'position' => 20, 'notnull' => -1, 'searchall' => 1), 'datec' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -2, 'position' => 50, 'notnull' => 1,), 'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -2, 'position' => 60, 'notnull' => 1,), + 'color' => array('type' => 'varchar(6)', 'label' => 'Color', 'enabled' => 1, 'visible' => 1, 'position' => 70, 'notnull' => -1, 'comment' => 'Color for pictogram'), 'model_pdf' => array('type' => 'varchar(255)', 'label' => 'ModelPDF', 'enabled' => 1, 'visible' => 0, 'position' => 100), ); diff --git a/htdocs/user/group/card.php b/htdocs/user/group/card.php index 6bdfff1a4ae..614c3f6d634 100644 --- a/htdocs/user/group/card.php +++ b/htdocs/user/group/card.php @@ -7,6 +7,7 @@ * Copyright (C) 2018 Juanjo Menent * Copyright (C) 2024-2025 MDW * Copyright (C) 2024 Frédéric France + * Copyright (C) 2025 Charlene Benke * * 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 @@ -33,6 +34,7 @@ require_once DOL_DOCUMENT_ROOT.'/user/class/usergroup.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/usergroups.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php'; /** * @var Conf $conf @@ -137,6 +139,7 @@ if (empty($reshook)) { } else { $object->name = GETPOST("nom", 'alphanohtml'); $object->note = dol_htmlcleanlastbr(trim(GETPOST("note", 'restricthtml'))); + $object->color = GETPOST("color", 'alphanohtml'); // Fill array 'array_options' with data from add form $ret = $extrafields->setOptionalsFromPost(null, $object); @@ -207,6 +210,7 @@ if (empty($reshook)) { $object->name = GETPOST("nom", 'alphanohtml'); $object->note = dol_htmlcleanlastbr(trim(GETPOST("note", 'restricthtml'))); + $object->color = GETPOST("color", 'alphanohtml'); $object->tms = dol_now(); // Fill array 'array_options' with data from add form @@ -254,6 +258,7 @@ $form = new Form($db); $fuserstatic = new User($db); $form = new Form($db); $formfile = new FormFile($db); +$formother = new FormOther($db); if ($action == 'create') { print load_fiche_titre($langs->trans("NewGroup"), '', 'object_group'); @@ -280,9 +285,16 @@ if ($action == 'create') { } } + unset($object->fields['color']); + // Common attributes include DOL_DOCUMENT_ROOT.'/core/tpl/commonfields_add.tpl.php'; + print ''.$langs->trans("ColorGroup").''; + print ''; + print $formother->selectColor(GETPOSTISSET('color') ? GETPOST('color', 'alphanohtml') : $object->color, 'color', null, 1, array(), 'hideifnotset'); + print ''; + // Other attributes include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_add.tpl.php'; @@ -350,11 +362,17 @@ if ($action == 'create') { } unset($object->fields['nom']); // Name already displayed in banner + unset($object->fields['color']); // Common attributes $keyforbreak = ''; include DOL_DOCUMENT_ROOT.'/core/tpl/commonfields_view.tpl.php'; + print ''.$langs->trans("ColorGroup").''; + print ''; + print $formother->showColor($object->color, ''); + print ''; + // Other attributes include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_view.tpl.php'; @@ -533,9 +551,16 @@ if ($action == 'create') { } } + unset($object->fields['color']); + // Common attributes include DOL_DOCUMENT_ROOT.'/core/tpl/commonfields_edit.tpl.php'; + print ''.$langs->trans("ColorGroup").''; + print ''; + print $formother->selectColor(GETPOSTISSET('color') ? GETPOST('color', 'alphanohtml') : $object->color, 'color', null, 1, array(), 'hideifnotset'); + print ''; + // Other attributes include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_edit.tpl.php'; From 982e0ed64e96bbe01f32d0d34ab4ec71628c2e0a Mon Sep 17 00:00:00 2001 From: Jon Bendtsen Date: Tue, 28 Oct 2025 10:23:06 +0100 Subject: [PATCH 16/16] NEW: API endpoint for getting products in a warehouse (#35918) * mostly updating fetch with all the fields and adding 2 missing variables * adding throw RestException numbers, @url and a test for if you try with id=0 * Adding hurl file that do some testing og warehouses API endpoints * Class for API prevented ever seeing model_pdf in API json * Check for auth before doing other tests on the request * New function list Products in warehouse. Work done by defrance, revived by JonB * API PUT/update now allows for more database fields * hurl tests for the new list products in warehouse endpoint * also rejecting warehouse_id in a post * pre-commit fix api warehouses * copy paste error from api_usergroup * Just return the lines, no cleaning needed * PHAN fixes * trying to fix PHAN error * fix api update so it returns error when error, and fix object->update with import_key * this->db not global db * Advice from hregis * Typo this d not dB * Array of strings * Changed listProducts in warehouse, uses api_product->get with variables from that * hurl tests for API warehouses, and the new function get products in warehouse * some STAN fixes * more phan fixes * changes revealed necessary by hurl testing --------- Co-authored-by: Jon Bendtsen --- htdocs/api/class/api.class.php | 2 +- htdocs/product/class/api_products.class.php | 1 + .../stock/class/api_warehouses.class.php | 234 +++++++++++++++++- htdocs/product/stock/class/entrepot.class.php | 32 ++- test/hurl/api/warehouses/10_warehouses.hurl | 176 +++++++++++++ 5 files changed, 433 insertions(+), 12 deletions(-) create mode 100644 test/hurl/api/warehouses/10_warehouses.hurl diff --git a/htdocs/api/class/api.class.php b/htdocs/api/class/api.class.php index 796aab61a70..14c93462a52 100644 --- a/htdocs/api/class/api.class.php +++ b/htdocs/api/class/api.class.php @@ -239,7 +239,7 @@ class DolibarrApi unset($object->timespent_fk_user); unset($object->timespent_note); unset($object->fk_delivery_address); - unset($object->model_pdf); + //unset($object->model_pdf); unset($object->sendtoid); unset($object->name_bis); unset($object->newref); diff --git a/htdocs/product/class/api_products.class.php b/htdocs/product/class/api_products.class.php index b99e38cd445..f554d7d8883 100644 --- a/htdocs/product/class/api_products.class.php +++ b/htdocs/product/class/api_products.class.php @@ -2238,6 +2238,7 @@ class Products extends DolibarrApi unset($object->stock_warehouse); } + unset($object->module); return $object; } diff --git a/htdocs/product/stock/class/api_warehouses.class.php b/htdocs/product/stock/class/api_warehouses.class.php index 5b58f7e9fe4..a63bc07338c 100644 --- a/htdocs/product/stock/class/api_warehouses.class.php +++ b/htdocs/product/stock/class/api_warehouses.class.php @@ -20,7 +20,7 @@ use Luracast\Restler\RestException; require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php'; -require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; +require_once DOL_DOCUMENT_ROOT.'/product/class/api_products.class.php'; /** * API class for warehouses @@ -60,14 +60,20 @@ class Warehouses extends DolibarrApi * @param int $id ID of warehouse * @return Object Object with cleaned properties * - * @throws RestException + * @url GET {id} + * + * @throws RestException 400 Bad Request + * @throws RestException 403 Not allowed + * @throws RestException 404 Not found */ public function get($id) { if (!DolibarrApiAccess::$user->hasRight('stock', 'lire')) { throw new RestException(403); } - + if ($id == 0) { + throw new RestException(400, 'No warehouse with id=0 can exist'); + } $result = $this->warehouse->fetch($id); if (!$result) { throw new RestException(404, 'warehouse not found'); @@ -100,7 +106,11 @@ class Warehouses extends DolibarrApi * @phan-return Entrepot[] * @phpstan-return Entrepot[] * - * @throws RestException + * @url GET + * + * @throws RestException 400 Bad Request + * @throws RestException 403 Not allowed + * @throws RestException 500 Internal Server Error */ public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $category = 0, $sqlfilters = '', $properties = '', $pagination_data = false) { @@ -188,6 +198,13 @@ class Warehouses extends DolibarrApi * @phan-param ?array $request_data * @phpstan-param ?array $request_data * @return int ID of warehouse + * + * @url POST + * + * @throws RestException 400 Bad Request + * @throws RestException 403 Not allowed + * @throws RestException 500 Internal Server Error + * */ public function post($request_data = null) { @@ -205,6 +222,13 @@ class Warehouses extends DolibarrApi continue; } + if ($field == 'id' || $field == 'warehouse_id') { + throw new RestException(400, 'Creating with id field is forbidden'); + } + if ($field == 'entity' && $value != $this->warehouse->entity) { + throw new RestException(403, 'Changing entity of a user using the APIs is not possible'); + } + $this->warehouse->$field = $this->_checkValForAPI($field, $value, $this->warehouse); } if ($this->warehouse->create(DolibarrApiAccess::$user) < 0) { @@ -221,13 +245,23 @@ class Warehouses extends DolibarrApi * @phan-param ?array $request_data * @phpstan-param ?array $request_data * @return Object Updated object + * + * @url PUT {id} + * + * @throws RestException 400 Bad Request + * @throws RestException 403 Not allowed + * @throws RestException 404 Not found + * @throws RestException 500 Internal Server Error + * */ public function put($id, $request_data = null) { if (!DolibarrApiAccess::$user->hasRight('stock', 'creer')) { throw new RestException(403); } - + if ($id == 0) { + throw new RestException(400, 'No warehouse with id=0 can exist'); + } $result = $this->warehouse->fetch($id); if (!$result) { throw new RestException(404, 'warehouse not found'); @@ -238,9 +272,16 @@ class Warehouses extends DolibarrApi } foreach ($request_data as $field => $value) { - if ($field == 'id') { - continue; + if ($field == 'id' || $field == 'warehouse_id') { + throw new RestException(400, 'Updating with id field is forbidden'); } + if ($field == 'entity' && $value != $this->warehouse->entity) { + throw new RestException(403, 'Changing entity of a user using the APIs is not possible'); + } + if ($field == 'ref') { + throw new RestException(400, 'Deprecated, use label, not ref'); + } + if ($field === 'caller') { // Add a mention of caller so on trigger called after action, we can filter to avoid a loop if we try to sync back again with the caller $this->warehouse->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09'); @@ -257,7 +298,8 @@ class Warehouses extends DolibarrApi $this->warehouse->$field = $this->_checkValForAPI($field, $value, $this->warehouse); } - if ($this->warehouse->update($id, DolibarrApiAccess::$user)) { + $updateresult = $this->warehouse->update($id, DolibarrApiAccess::$user); + if ($updateresult > 0) { return $this->get($id); } else { throw new RestException(500, $this->warehouse->error); @@ -271,12 +313,23 @@ class Warehouses extends DolibarrApi * @return array * @phan-return array{success:array{code:int,message:string}} * @phpstan-return array{success:array{code:int,message:string}} + * + * @url DELETE {id} + * + * @throws RestException 400 Bad Request + * @throws RestException 403 Not allowed + * @throws RestException 404 Not found + * @throws RestException 500 Internal Server Error + * */ public function delete($id) { if (!DolibarrApiAccess::$user->hasRight('stock', 'supprimer')) { throw new RestException(403); } + if ($id == 0) { + throw new RestException(400, 'No warehouse with id=0 can exist'); + } $result = $this->warehouse->fetch($id); if (!$result) { throw new RestException(404, 'warehouse not found'); @@ -287,7 +340,7 @@ class Warehouses extends DolibarrApi } if (!$this->warehouse->delete(DolibarrApiAccess::$user)) { - throw new RestException(403, 'error when delete warehouse'); + throw new RestException(500, 'error when delete warehouse'); } return array( @@ -298,6 +351,104 @@ class Warehouses extends DolibarrApi ); } + /** + * List Product in warehouses + * + * Get a list of product in a warehouse + * + * @since 23.0.0 Initial implementation + * + * @param int $id warehouse ID + * @param string $sortfield Sort field + * @param string $sortorder Sort order + * @param int $limit Limit for list + * @param int $page Page number + * @param int $includestockdata 1=Load also information about stock (slower), 0=No stock data (faster) (default) + * @param bool $includesubproducts Load information about subproducts + * @param bool $includeparentid Load also ID of parent product (if product is a variant of a parent product) + * @param bool $includetrans Load also the translations of product label and description + * @param string $properties Restrict the data returned to these properties. Ignored if empty. Comma separated list of properties names + * @param bool $pagination_data If this parameter is set to true the response will include pagination data. Default value is false. Page starts from 0 + * @return array Array of product in warehouse + * + * @phan-return Product[] + * @phpstan-return Product[] + * + * @url GET /{id}/products + * + * @throws RestException 400 Bad Request + * @throws RestException 403 Not allowed + * @throws RestException 404 Not found + * @throws RestException 500 Internal Server Error + * + */ + public function listProducts($id = 0, $sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $includestockdata = 0, $includesubproducts = false, $includeparentid = false, $includetrans = false, $properties = '', $pagination_data = false) + { + if (!DolibarrApiAccess::$user->hasRight('stock', 'lire')) { + throw new RestException(403); + } + if ((int) $id == 0) { + throw new RestException(400, 'No warehouse with id=0 can exist'); + } + $existsresult = $this->warehouse->fetch($id); + if (!$existsresult) { + throw new RestException(404, 'warehouse not found'); + } + $obj_ret = array(); + + $sql = "SELECT t.rowid FROM ".MAIN_DB_PREFIX."product_stock as ps"; + $sql.= " INNER JOIN ".MAIN_DB_PREFIX."product as t ON ps.fk_product = t.rowid"; + $sql.= " WHERE ps.fk_entrepot =".((int) $id); + $sql.= " AND t.entity IN (".getEntity('stock').")"; + + //this query will return total warehouses with the filters given + $sqlTotals = str_replace('SELECT t.rowid', 'SELECT count(t.rowid) as total', $sql); + + $sql .= $this->db->order($sortfield, $sortorder); + if ($limit) { + if ($page < 0) { + $page = 0; + } + $offset = $limit * $page; + + $sql .= $this->db->plimit($limit + 1, $offset); + } + + $result = $this->db->query($sql); + if ($result) { + $i = 0; + $num = $this->db->num_rows($result); + while ($i < $num) { + $obj = $this->db->fetch_object($result); + $api_products_static = new Products(); + if ($api_products_static->get($obj->rowid, $includestockdata, $includesubproducts, $includeparentid, $includetrans)) { + $obj_ret[] = $this->_filterObjectProperties($api_products_static->product, $properties); + } + $i++; + } + } else { + throw new RestException(500, 'Error when retrieve warehouse product list : '.$this->db->lasterror()); + } + + //if $pagination_data is true the response will contain element data with all values and element pagination with pagination data(total,page,limit) + if ($pagination_data) { + $totalsResult = $this->db->query($sqlTotals); + $total = $this->db->fetch_object($totalsResult)->total; + + $tmp = $obj_ret; + $obj_ret = []; + + $obj_ret['data'] = $tmp; + $obj_ret['pagination'] = [ + 'total' => (int) $total, + 'page' => $page, //count starts from 0 + 'page_count' => ceil((int) $total / $limit), + 'limit' => $limit + ]; + } + + return $obj_ret; + } // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore /** @@ -311,6 +462,69 @@ class Warehouses extends DolibarrApi // phpcs:enable $object = parent::_cleanObjectDatas($object); + unset($object->actiontypecode); + unset($object->canvas); + unset($object->civility_code); + unset($object->civility_id); + unset($object->cond_reglement_id); + unset($object->cond_reglement_supplier_id); + unset($object->contact_id); + unset($object->contacts_ids); + unset($object->contacts_ids_internal); + unset($object->country_code); + unset($object->date_cloture); + unset($object->date_validation); + unset($object->demand_reason_id); + unset($object->deposit_percent); + unset($object->extraparams); + unset($object->firstname); + unset($object->fk_account); + unset($object->fk_multicurrency); + unset($object->fk_user_creat); + unset($object->fk_user_modif); + unset($object->last_main_doc); + unset($object->lastname); + unset($object->libelle); + unset($object->lines); + unset($object->linkedObjectsIds); + unset($object->mode_reglement_id); + unset($object->module); + unset($object->multicurrency_code); + unset($object->multicurrency_total_ht); + unset($object->multicurrency_total_localtax1); + unset($object->multicurrency_total_localtax2); + unset($object->multicurrency_total_ttc); + unset($object->multicurrency_total_tva); + unset($object->multicurrency_tx); + unset($object->name); + unset($object->nb_rights); + unset($object->note_private); + unset($object->note_public); + unset($object->origin_id); + unset($object->origin_type); + unset($object->product); + unset($object->ref_ext); + unset($object->region_id); + unset($object->retained_warranty_fk_cond_reglement); + unset($object->shipping_method); + unset($object->shipping_method_id); + unset($object->specimen); + unset($object->state_id); + unset($object->tms); + unset($object->total_ht); + unset($object->total_localtax1); + unset($object->total_localtax2); + unset($object->total_ttc); + unset($object->total_tva); + unset($object->totalpaid); + unset($object->totalpaid_multicurrency); + unset($object->transport_mode_id); + unset($object->TRIGGER_PREFIX); + unset($object->user); + unset($object->user_closing_id); + unset($object->user_modification_id); + unset($object->user_validation_id); + return $object; } @@ -321,7 +535,7 @@ class Warehouses extends DolibarrApi * @param ?array $data Data to validate * @return array * - * @throws RestException + * @throws RestException 400 Bad Request */ private function _validate($data) { diff --git a/htdocs/product/stock/class/entrepot.class.php b/htdocs/product/stock/class/entrepot.class.php index 32b3411466c..d83b6a56791 100644 --- a/htdocs/product/stock/class/entrepot.class.php +++ b/htdocs/product/stock/class/entrepot.class.php @@ -62,11 +62,21 @@ class Entrepot extends CommonObject */ public $label; + /** + * @var string barcode + */ + public $barcode; + /** * @var string description */ public $description; + /** + * @var int + */ + public $fk_departement; + /** * @var int */ @@ -351,6 +361,8 @@ class Entrepot extends CommonObject } $this->label = trim($this->label); + $this->barcode = trim($this->barcode); + $this->model_pdf = trim($this->model_pdf); $this->description = trim($this->description); @@ -374,6 +386,13 @@ class Entrepot extends CommonObject $sql .= ", fk_pays = ".((int) $this->country_id); $sql .= ", phone = '".$this->db->escape($this->phone)."'"; $sql .= ", fax = '".$this->db->escape($this->fax)."'"; + $sql .= ", barcode = '".$this->db->escape($this->barcode)."'"; + $sql .= ", fk_departement = ".((int) $this->fk_departement); + $sql .= ", fk_barcode_type = ".((int) $this->barcode_type); + $sql .= ", warehouse_usage = ".((int) $this->warehouse_usage); + $sql .= ", fk_user_author = ".((int) $this->user_creation_id); + $sql .= ", model_pdf = '".$this->db->escape($this->model_pdf)."'"; + $sql .= ", import_key = ".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null"); $sql .= " WHERE rowid = ".((int) $id); $this->db->begin(); @@ -527,7 +546,7 @@ class Entrepot extends CommonObject } $sql = "SELECT rowid, entity, fk_parent, fk_project, ref as label, description, statut, lieu, address, zip, town, fk_pays as country_id, phone, fax,"; - $sql .= " model_pdf, import_key"; + $sql .= " model_pdf, import_key, datec as date_creation, tms as date_modification, fk_departement, barcode, fk_barcode_type, warehouse_usage, fk_user_author as user_creation_id"; $sql .= " FROM ".$this->db->prefix()."entrepot"; if ($id) { $sql .= " WHERE rowid = ".((int) $id); @@ -551,6 +570,7 @@ class Entrepot extends CommonObject $this->label = $obj->label; $this->description = $obj->description; $this->statut = $obj->statut; + $this->status = $obj->statut; $this->lieu = $obj->lieu; $this->address = $obj->address; $this->zip = $obj->zip; @@ -559,6 +579,16 @@ class Entrepot extends CommonObject $this->phone = $obj->phone; $this->fax = $obj->fax; + $this->date_creation = $obj->date_creation; + $this->barcode = $obj->barcode; + $this->fk_departement = $obj->fk_departement; + $this->barcode_type = $obj->fk_barcode_type; + $this->warehouse_id = $obj->rowid; + + $this->warehouse_usage = $obj->warehouse_usage; + $this->user_creation_id = $obj->user_creation_id; + $this->date_modification = $obj->date_modification; + $this->model_pdf = $obj->model_pdf; $this->import_key = $obj->import_key; diff --git a/test/hurl/api/warehouses/10_warehouses.hurl b/test/hurl/api/warehouses/10_warehouses.hurl new file mode 100644 index 00000000000..453a4badb1f --- /dev/null +++ b/test/hurl/api/warehouses/10_warehouses.hurl @@ -0,0 +1,176 @@ +# GET warehouses +GET http://{{hostnport}}/api/index.php/warehouses +HTTP 200 + +# GET sorted warehouses +GET http://{{hostnport}}/api/index.php/warehouses?sortfield=t.rowid&sortorder=ASC +HTTP 200 + +# GET sorted warehouses +GET http://{{hostnport}}/api/index.php/warehouses?sortfield=t.rowid&sortorder=DSC +HTTP 200 + +# GET with limit and page +GET http://{{hostnport}}/api/index.php/warehouses?limit=100&page=1 +HTTP 200 + +# GET with fk_user +GET http://{{hostnport}}/api/index.php/warehouses?fk_user=0 +HTTP 200 + +# GET with properties=id%2Cstatus +GET http://{{hostnport}}/api/index.php/warehouses?properties=id%2Cstatus +HTTP 200 + +# GET with pagination_data=false +GET http://{{hostnport}}/api/index.php/warehouses?pagination_data=false +HTTP 200 + +# GET with pagination_data=true +GET http://{{hostnport}}/api/index.php/warehouses?pagination_data=true +HTTP 200 +[Asserts] +jsonpath "$.data" exists +jsonpath "$.pagination.total" >= 0 +jsonpath "$.pagination.page" >= 0 +jsonpath "$.pagination.page_count" >= 0 +jsonpath "$.pagination.limit" == 100 + +# GET warehouse with ID 0 - which should not exist +GET http://{{hostnport}}/api/index.php/warehouses/0 +HTTP 400 +{"error":{"code":400,"message":"Bad Request: No warehouse with id=0 can exist"}} + +# POST {} +POST http://{{hostnport}}/api/index.php/warehouses +{} +HTTP 400 +{"error":{"code":400,"message":"Bad Request: label field missing"}} + +# POST topic required +POST http://{{hostnport}}/api/index.php/warehouses +{ "id" : 42, "label" : "automatic test using 10_warehouses.hurl"} +HTTP 400 +{"error":{"code":400,"message":"Bad Request: Creating with id field is forbidden"}} + +# POST type_template field required +POST http://{{hostnport}}/api/index.php/warehouses +{ "id" : 42, "label" : "automatic test using 10_warehouses.hurl", "topic" : "automatic test using 10_warehouses.hurl" } +HTTP 400 +{"error":{"code":400,"message":"Bad Request: Creating with id field is forbidden"}} + +# POST No id in post request data +POST http://{{hostnport}}/api/index.php/warehouses +{ "id" : 42, "label" : "automatic test using 10_warehouses.hurl", "topic" : "automatic test using 10_warehouses.hurl", "type_template" : "all" } +HTTP 400 +{"error":{"code":400,"message":"Bad Request: Creating with id field is forbidden"}} + +# DELETE +DELETE http://{{hostnport}}/api/index.php/warehouses/ +HTTP 405 + +# DELETE warehouse with ID 0 - which should not exist +DELETE http://{{hostnport}}/api/index.php/warehouses/0 +HTTP 400 +{"error":{"code":400,"message":"Bad Request: No warehouse with id=0 can exist"}} + +# PUT +PUT http://{{hostnport}}/api/index.php/warehouses/ +{} +HTTP 405 + +# PUT +PUT http://{{hostnport}}/api/index.php/warehouses/0 +{} +HTTP 400 +{"error":{"code":400,"message":"Bad Request: No warehouse with id=0 can exist"}} + +# GET products in warehouse with ID non integer ID - which should not exist +GET http://{{hostnport}}/api/index.php/warehouses/0products +HTTP 400 +{"error":{"code":400,"message":"Bad Request: invalid value specified for `id`"}} + +# GET products in warehouse with ID 0 - which should not exist +GET http://{{hostnport}}/api/index.php/warehouses/0/products +HTTP 400 +{"error":{"code":400,"message":"Bad Request: No warehouse with id=0 can exist"}} + + +# GET warehouses DOLAPIENTITY: 1 +GET http://{{hostnport}}/api/index.php/warehouses +DOLAPIENTITY: 1 +HTTP 200 + +# GET warehouses DOLAPIENTITY: 2 +GET http://{{hostnport}}/api/index.php/warehouses +DOLAPIENTITY: 2 +HTTP 401 +{"error":{"code":401,"message":"Unauthorized: Error user not valid (not found with api key or bad status or bad validity dates) (conf->entity=2)"}} + +# http://localhost:8080/api/index.php/warehouses?sortfield=t.rowid&sortorder=ASC&limit=1 + +# Capture a warehouse ID number +GET http://{{hostnport}}/api/index.php/warehouses?limit=1 +HTTP 200 +[Captures] +warehouse_id: jsonpath "$[0]['id']" + +# GET products in warehouse with captured ID - which should exist +GET http://{{hostnport}}/api/index.php/warehouses/{{warehouse_id}}/products +HTTP 200 + +# GET products in warehouse with captured ID - limit 1 +GET http://{{hostnport}}/api/index.php/warehouses/{{warehouse_id}}/products?limit=1 +HTTP 200 + + +# GET products in warehouse with captured ID - with stockdata +GET http://{{hostnport}}/api/index.php/warehouses/{{warehouse_id}}/products?includestockdata=1 +HTTP 200 + +# GET products in warehouse with captured ID - no stockdata +GET http://{{hostnport}}/api/index.php/warehouses/{{warehouse_id}}/products?includestockdata=0 +HTTP 200 + + +# GET products in warehouse with captured ID - with subproducts +GET http://{{hostnport}}/api/index.php/warehouses/{{warehouse_id}}/products?includesubproducts=true +HTTP 200 + +# GET products in warehouse with captured ID - no subproducts +GET http://{{hostnport}}/api/index.php/warehouses/{{warehouse_id}}/products?includesubproducts=false +HTTP 200 + + +# GET products in warehouse with captured ID - with includeparentid +GET http://{{hostnport}}/api/index.php/warehouses/{{warehouse_id}}/products?includeparentid=true +HTTP 200 + +# GET products in warehouse with captured ID - no includeparentid +GET http://{{hostnport}}/api/index.php/warehouses/{{warehouse_id}}/products?includeparentid=false +HTTP 200 + + +# GET products in warehouse with captured ID - with includetrans +GET http://{{hostnport}}/api/index.php/warehouses/{{warehouse_id}}/products?includetrans=true +HTTP 200 + +# GET products in warehouse with captured ID - no includetrans +GET http://{{hostnport}}/api/index.php/warehouses/{{warehouse_id}}/products?includetrans=false +HTTP 200 + + + +# GET products with pagination_data=false +GET http://{{hostnport}}/api/index.php/warehouses/{{warehouse_id}}/products?pagination_data=false +HTTP 200 + +# GET products with pagination_data=true +GET http://{{hostnport}}/api/index.php/warehouses/{{warehouse_id}}/products?pagination_data=true +HTTP 200 +[Asserts] +jsonpath "$.data" exists +jsonpath "$.pagination.total" >= 0 +jsonpath "$.pagination.page" exists +jsonpath "$.pagination.page_count" >= 0 +jsonpath "$.pagination.limit" == 100