From 8ddf9f6075e12b7754248741bd549092bb2b22d0 Mon Sep 17 00:00:00 2001
From: tnegre
Date: Thu, 16 Oct 2025 11:41:52 +0200
Subject: [PATCH 01/58] FIX : remove useless condition to create credit on
situation invoice (#35786)
---
htdocs/compta/facture/card.php | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/htdocs/compta/facture/card.php b/htdocs/compta/facture/card.php
index 39c271ea0ca..007e2c89d2d 100644
--- a/htdocs/compta/facture/card.php
+++ b/htdocs/compta/facture/card.php
@@ -5796,10 +5796,9 @@ if ($action == 'create') {
}
}
- // For situation invoice with excess received
+ // For situation invoice
if ($object->statut > Facture::STATUS_DRAFT
&& $object->type == Facture::TYPE_SITUATION
- && ($object->total_ttc - $totalpaid - $totalcreditnotes - $totaldeposits) > 0
&& $usercancreate
&& !$objectidnext
&& $object->is_last_in_cycle()
From 1cd3ca32222880242d94530a9d9d4a605de4ad32 Mon Sep 17 00:00:00 2001
From: VESSILLER
Date: Mon, 24 Nov 2025 10:30:08 +0100
Subject: [PATCH 02/58] FIX undefined variables on create invoice from shipment
card (backport from v20)
---
htdocs/compta/facture/card.php | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/htdocs/compta/facture/card.php b/htdocs/compta/facture/card.php
index 5aa884f61f5..9ce5fd95f98 100644
--- a/htdocs/compta/facture/card.php
+++ b/htdocs/compta/facture/card.php
@@ -1710,25 +1710,25 @@ if (empty($reshook)) {
// Date start
$date_start = false;
- if ($lines[$i]->date_debut_prevue) {
+ if (isset($lines[$i]->date_debut_prevue)) {
$date_start = $lines[$i]->date_debut_prevue;
}
- if ($lines[$i]->date_debut_reel) {
+ if (isset($lines[$i]->date_debut_reel)) {
$date_start = $lines[$i]->date_debut_reel;
}
- if ($lines[$i]->date_start) {
+ if (isset($lines[$i]->date_start)) {
$date_start = $lines[$i]->date_start;
}
// Date end
$date_end = false;
- if ($lines[$i]->date_fin_prevue) {
+ if (isset($lines[$i]->date_fin_prevue)) {
$date_end = $lines[$i]->date_fin_prevue;
}
- if ($lines[$i]->date_fin_reel) {
+ if (isset($lines[$i]->date_fin_reel)) {
$date_end = $lines[$i]->date_fin_reel;
}
- if ($lines[$i]->date_end) {
+ if (isset($lines[$i]->date_end)) {
$date_end = $lines[$i]->date_end;
}
From c1c2358e75418d494a527c245489a488aad5f93c Mon Sep 17 00:00:00 2001
From: Adrien Raze
Date: Wed, 26 Nov 2025 12:30:22 +0100
Subject: [PATCH 03/58] FIX : Implementation of multi-company compatibility
with inventory/warehouse management
---
htdocs/core/lib/security.lib.php | 4 ++--
htdocs/product/inventory/card.php | 4 ++--
htdocs/product/inventory/inventory.php | 4 ++--
htdocs/product/stock/card.php | 3 +--
4 files changed, 7 insertions(+), 8 deletions(-)
diff --git a/htdocs/core/lib/security.lib.php b/htdocs/core/lib/security.lib.php
index 2d46f6b3c8b..cdceab83dc1 100644
--- a/htdocs/core/lib/security.lib.php
+++ b/htdocs/core/lib/security.lib.php
@@ -902,14 +902,14 @@ function checkUserAccessToObject($user, array $featuresarray, $object = 0, $tabl
$checkonentitydone = 0;
// Array to define rules of checks to do
- $check = array('adherent', 'banque', 'bom', 'don', 'mrp', 'user', 'usergroup', 'payment', 'payment_supplier', 'payment_sc', 'product', 'produit', 'service', 'produit|service', 'categorie', 'resource', 'expensereport', 'holiday', 'salaries', 'website', 'recruitment', 'chargesociales', 'knowledgemanagement'); // Test on entity only (Objects with no link to company)
+ $check = array('adherent', 'banque', 'bom', 'don', 'mrp', 'user', 'usergroup', 'payment', 'payment_supplier', 'payment_sc', 'product', 'produit', 'service', 'produit|service', 'categorie', 'resource', 'expensereport', 'holiday', 'salaries', 'website', 'recruitment', 'chargesociales', 'knowledgemanagement', 'stock'); // Test on entity only (Objects with no link to company)
$checksoc = array('societe'); // Test for object Societe
$checkparentsoc = array('agenda', 'contact', 'contrat'); // Test on entity + link to third party on field $dbt_keyfield. Allowed if link is empty (Ex: contacts...).
$checkproject = array('projet', 'project'); // Test for project object
$checktask = array('projet_task', 'project_task'); // Test for task object
$checkhierarchy = array('expensereport', 'holiday', 'hrm'); // check permission among the hierarchy of user
$checkuser = array('bookmark'); // check permission among the fk_user (must be myself or null)
- $nocheck = array('barcode', 'stock'); // No test
+ $nocheck = array('barcode'); // No test
//$checkdefault = 'all other not already defined'; // Test on entity + link to third party on field $dbt_keyfield. Not allowed if link is empty (Ex: invoice, orders...).
diff --git a/htdocs/product/inventory/card.php b/htdocs/product/inventory/card.php
index 79d7b21757e..37285af1974 100644
--- a/htdocs/product/inventory/card.php
+++ b/htdocs/product/inventory/card.php
@@ -42,9 +42,9 @@ $contextpage = GETPOST('contextpage', 'aZ') ?GETPOST('contextpage', 'aZ') : 'inv
$backtopage = GETPOST('backtopage', 'alpha');
if (empty($conf->global->MAIN_USE_ADVANCED_PERMS)) {
- $result = restrictedArea($user, 'stock', $id);
+ $result = restrictedArea($user, 'stock', $id, 'inventory&stock');
} else {
- $result = restrictedArea($user, 'stock', $id, '', 'inventory_advance');
+ $result = restrictedArea($user, 'stock', $id, 'inventory&stock', 'inventory_advance');
}
// Initialize technical objects
diff --git a/htdocs/product/inventory/inventory.php b/htdocs/product/inventory/inventory.php
index e9ddf7ad877..1bd941aa1fe 100644
--- a/htdocs/product/inventory/inventory.php
+++ b/htdocs/product/inventory/inventory.php
@@ -59,9 +59,9 @@ $batch = GETPOST('batch', 'alphanohtml');
$totalExpectedValuation = 0;
$totalRealValuation = 0;
if (empty($conf->global->MAIN_USE_ADVANCED_PERMS)) {
- $result = restrictedArea($user, 'stock', $id);
+ $result = restrictedArea($user, 'stock', $id, 'inventory&stock');
} else {
- $result = restrictedArea($user, 'stock', $id, '', 'inventory_advance');
+ $result = restrictedArea($user, 'stock', $id, 'inventory&stock', 'inventory_advance');
}
// Initialize technical objects
diff --git a/htdocs/product/stock/card.php b/htdocs/product/stock/card.php
index 5baf343556e..3736b0296f7 100644
--- a/htdocs/product/stock/card.php
+++ b/htdocs/product/stock/card.php
@@ -70,8 +70,7 @@ if (!$sortorder) {
$backtopage = GETPOST('backtopage', 'alpha');
// Security check
-//$result=restrictedArea($user,'stock', $id, 'entrepot&stock');
-$result = restrictedArea($user, 'stock');
+$result=restrictedArea($user,'stock', $id, 'entrepot&stock');
// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
$hookmanager->initHooks(array('warehousecard', 'globalcard'));
From 4585facfcc95470757b99a080a21b560d2f40538 Mon Sep 17 00:00:00 2001
From: ATM-Lucas
Date: Wed, 26 Nov 2025 16:47:32 +0100
Subject: [PATCH 04/58] Missing AND on fields filter on evaluation class
---
htdocs/hrm/class/evaluation.class.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/htdocs/hrm/class/evaluation.class.php b/htdocs/hrm/class/evaluation.class.php
index 51b0c78ed3a..2969446f26d 100644
--- a/htdocs/hrm/class/evaluation.class.php
+++ b/htdocs/hrm/class/evaluation.class.php
@@ -225,7 +225,7 @@ class Evaluation extends CommonObject
}
if (!$user->hasRight('hrm', 'evaluation', 'readall')) {
- $this->fields['fk_user']['type'] .= ':t.rowid:IN:'.$this->db->sanitize(implode(",", $user->getAllChildIds(1)));
+ $this->fields['fk_user']['type'] .= ' AND (t.rowid:IN:'.$this->db->sanitize(implode(",", $user->getAllChildIds(1))) .')';
}
$this->date_eval = dol_now();
From b2f9de74898e42917cc70232a5beb6124c4a861a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alo=C3=AFs=20Micard?=
Date: Fri, 28 Nov 2025 17:27:32 +0100
Subject: [PATCH 05/58] Add new workflow to trigger Docker build
---
.github/workflows/ci-on-release.yml | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
create mode 100644 .github/workflows/ci-on-release.yml
diff --git a/.github/workflows/ci-on-release.yml b/.github/workflows/ci-on-release.yml
new file mode 100644
index 00000000000..7c3b0d716e3
--- /dev/null
+++ b/.github/workflows/ci-on-release.yml
@@ -0,0 +1,22 @@
+name: "CI-RELEASE"
+
+on:
+ release:
+ types: [published]
+
+jobs:
+ trigger-docker:
+ steps:
+ - name: Generate a token
+ id: generate-token
+ uses: actions/create-github-app-token@v2
+ with:
+ app-id: ${{ vars.RELEASE_DOCKER_ID }}
+ private-key: ${{ secrets.RELEASE_DOCKER_SECRET }}
+
+ - uses: peter-evans/repository-dispatch@v4
+ with:
+ token: ${{ steps.generate-token.outputs.token }}
+ repository: Dolibarr/dolibarr-docker
+ event-type: new-release
+ client-payload: '{"version": "${{ github.event.release.tag_name }}"}'
From 5c6b4f62c8121ca410d08d148426896daeb59bb1 Mon Sep 17 00:00:00 2001
From: marc
Date: Fri, 28 Nov 2025 18:25:00 +0100
Subject: [PATCH 06/58] FIX #35061
Signed-off-by: marc
---
htdocs/core/lib/functions.lib.php | 36 ++++---------------------------
1 file changed, 4 insertions(+), 32 deletions(-)
diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php
index 3b9979dd9fc..5a79377c573 100644
--- a/htdocs/core/lib/functions.lib.php
+++ b/htdocs/core/lib/functions.lib.php
@@ -3943,46 +3943,18 @@ function dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetwor
$htmllink .= ($link ? ' '.$link : '');
}
} else {
- $networkconstname = 'MAIN_INFO_SOCIETE_'.strtoupper($type).'_URL';
- if (getDolGlobalString($networkconstname)) {
- $link = str_replace('{socialid}', $value, getDolGlobalString($networkconstname));
- if (preg_match('/^https?:\/\//i', $link)) {
- $htmllink .= ''.dol_escape_htmltag($value).'';
- } else {
- $htmllink .= ''.dol_escape_htmltag($value).'';
- }
- } elseif (!empty($dictsocialnetworks[$type]['url'])) {
- $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
- if ($tmpvirginurl) {
- $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
- $value = preg_replace('/^'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
-
- $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
- if ($tmpvirginurl3) {
- $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
- $value = preg_replace('/^'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
- }
-
- $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
- if ($tmpvirginurl2) {
- $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
- $value = preg_replace('/^'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
- }
- }
+ if (!empty($dictsocialnetworks[$type]['url'])) {
$link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
- if (preg_match('/^https?:\/\//i', $link)) {
- $htmllink .= ''.dol_escape_htmltag($value).'';
+ if (preg_match('/^https?:\/\//i', $value)) {
+ $htmllink .= ''.dol_escape_htmltag($value).'';
} else {
- $htmllink .= ''.dol_escape_htmltag($value).'';
+ $htmllink .= ''.dol_escape_htmltag($value).'';
}
} else {
$htmllink .= dol_escape_htmltag($value);
}
}
$htmllink .= '';
- } else {
- $langs->load("errors");
- $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
}
return $htmllink;
}
From 8664f7415408b8480a4be067d5f40bbc83c6e541 Mon Sep 17 00:00:00 2001
From: Lucas Marcouiller
Date: Sat, 29 Nov 2025 21:55:45 +0100
Subject: [PATCH 07/58] Fix #36475 bad value for project gantt start
---
htdocs/projet/ganttchart.inc.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/htdocs/projet/ganttchart.inc.php b/htdocs/projet/ganttchart.inc.php
index c2a68c65d7c..91f6a5ce082 100644
--- a/htdocs/projet/ganttchart.inc.php
+++ b/htdocs/projet/ganttchart.inc.php
@@ -140,8 +140,8 @@ if (g.getDivId() != null)
'task_alternate_id' => (int) -$t['task_project_id'],
'task_name' => $projecttmp->ref.' '.$projecttmp->title,
'task_resources' => '',
- 'task_start_date' => 0,
- 'task_end_date' => 0,
+ 'task_start_date' => $projecttmp->date_start,
+ 'task_end_date' => $projecttmp->date_end,
'task_is_group' => 1, 'task_position' => 0, 'task_css' => 'ggroupblack', 'task_milestone' => 0, 'task_parent' => 0, 'task_parent_alternate_id' => 0,
'note' => '',
'task_planned_workload' => 0
From d16f8b68db7214611de2af081dcf47ebd8433fe4 Mon Sep 17 00:00:00 2001
From: VESSILLER
Date: Mon, 1 Dec 2025 11:14:16 +0100
Subject: [PATCH 08/58] FIX not remove value of others extra-fields on update
extras action
---
htdocs/core/actions_addupdatedelete.inc.php | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/htdocs/core/actions_addupdatedelete.inc.php b/htdocs/core/actions_addupdatedelete.inc.php
index 25458a612e5..63029dbdb83 100644
--- a/htdocs/core/actions_addupdatedelete.inc.php
+++ b/htdocs/core/actions_addupdatedelete.inc.php
@@ -351,9 +351,11 @@ if (preg_match('/^set(\w+)$/', $action, $reg) && GETPOST('id', 'int') > 0 && !em
if ($action == "update_extras" && GETPOST('id', 'int') > 0 && !empty($permissiontoadd)) {
$object->fetch(GETPOST('id', 'int'));
+ $attribute = GETPOST('attribute', 'aZ09');
+
$error = 0;
- $ret = $extrafields->setOptionalsFromPost(null, $object, '@GETPOSTISSET');
+ $ret = $extrafields->setOptionalsFromPost(null, $object, $attribute);
if ($ret < 0) {
$error++;
setEventMessages($extrafields->error, $object->errors, 'errors');
From 0e1c580f114f95acee03e52232ba1ee9203722bd Mon Sep 17 00:00:00 2001
From: atm-florian
Date: Mon, 1 Dec 2025 13:47:50 +0100
Subject: [PATCH 09/58] FIX 18.0: GETPOSTDATE and buildParamDate assumed HTTP
param names 'minute' and 'second' instead of 'min' and 'sec'
---
htdocs/core/lib/functions.lib.php | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php
index 3de8219199a..2ee32dfae53 100644
--- a/htdocs/core/lib/functions.lib.php
+++ b/htdocs/core/lib/functions.lib.php
@@ -12996,8 +12996,8 @@ function GETPOSTDATE($prefix, $hourTime = '', $gm = 'auto')
{
if ($hourTime === 'getpost') {
$hour = GETPOSTINT($prefix . 'hour');
- $minute = GETPOSTINT($prefix . 'minute');
- $second = GETPOSTINT($prefix . 'second');
+ $minute = GETPOSTINT($prefix . 'min');
+ $second = GETPOSTINT($prefix . 'sec');
} elseif (preg_match('/^(\d\d):(\d\d):(\d\d)$/', $hourTime, $m)) {
$hour = intval($m[1]);
$minute = intval($m[2]);
@@ -13034,8 +13034,8 @@ function buildParamDate($prefix, $timestamp = null, $hourTime = '', $gm = 'auto'
if ($hourTime === 'getpost' || ($timestamp !== null && dol_print_date($timestamp, '%H:%M:%S') !== '00:00:00')) {
$TParam = array_merge($TParam, array(
$prefix . 'hour' => intval(dol_print_date($timestamp, '%H')),
- $prefix . 'minute' => intval(dol_print_date($timestamp, '%M')),
- $prefix . 'second' => intval(dol_print_date($timestamp, '%S'))
+ $prefix . 'min' => intval(dol_print_date($timestamp, '%M')),
+ $prefix . 'sec' => intval(dol_print_date($timestamp, '%S'))
));
}
From dad4bbddd24e14b20d8e5a9470445fb3d7b7cbb9 Mon Sep 17 00:00:00 2001
From: Pierre Ardoin <32256817+mapiolca@users.noreply.github.com>
Date: Mon, 1 Dec 2025 14:00:38 +0100
Subject: [PATCH 10/58] Report fix on 21.0
Fix an issue that have been fixed by Charlene Benke on v22
---
htdocs/fichinter/card.php | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/htdocs/fichinter/card.php b/htdocs/fichinter/card.php
index 5375c3a1831..e4dca1482d8 100644
--- a/htdocs/fichinter/card.php
+++ b/htdocs/fichinter/card.php
@@ -5,7 +5,7 @@
* Copyright (C) 2011-2020 Juanjo Menent
* Copyright (C) 2013 Florian Henry
* Copyright (C) 2014-2018 Ferran Marcet
- * Copyright (C) 2014-2022 Charlene Benke
+ * Copyright (C) 2014-2025 Charlene Benke
* Copyright (C) 2015-2016 Abbes Bahfir
* Copyright (C) 2018-2022 Philippe Grand
* Copyright (C) 2020-2024 Frédéric France
@@ -13,6 +13,7 @@
* Copyright (C) 2023-2024 William Mead
* Copyright (C) 2024 MDW
* Copyright (C) 2024 Alexandre Spangaro
+ * Copyright (C) 2024 Pierre Ardoin
*
* 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
@@ -1224,6 +1225,7 @@ if ($action == 'create') {
$formquestion[] = [
'type' => 'select',
'name' => 'signed_status',
+ 'select_show_empty' => 0,
'label' => ''.$langs->trans('SignStatus').'',
'values' => $object->getSignedStatusLocalisedArray()
];
From 1c94462405bea01b5fbeaa2bd7e9c9ba648ff02d Mon Sep 17 00:00:00 2001
From: Pierre Ardoin <32256817+mapiolca@users.noreply.github.com>
Date: Mon, 1 Dec 2025 14:03:40 +0100
Subject: [PATCH 11/58] Update copyright year for Pierre Ardoin
---
htdocs/fichinter/card.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/htdocs/fichinter/card.php b/htdocs/fichinter/card.php
index e4dca1482d8..ae0e59426b2 100644
--- a/htdocs/fichinter/card.php
+++ b/htdocs/fichinter/card.php
@@ -13,7 +13,7 @@
* Copyright (C) 2023-2024 William Mead
* Copyright (C) 2024 MDW
* Copyright (C) 2024 Alexandre Spangaro
- * Copyright (C) 2024 Pierre Ardoin
+ * Copyright (C) 2025 Pierre Ardoin
*
* 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
From 792070b5f5d9d7ea823f0184d86c89f7a30a477a Mon Sep 17 00:00:00 2001
From: Lucas Marcouiller
Date: Tue, 2 Dec 2025 08:34:41 +0100
Subject: [PATCH 12/58] fix a potential warning
---
htdocs/projet/ganttchart.inc.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/htdocs/projet/ganttchart.inc.php b/htdocs/projet/ganttchart.inc.php
index 91f6a5ce082..598ab6572ff 100644
--- a/htdocs/projet/ganttchart.inc.php
+++ b/htdocs/projet/ganttchart.inc.php
@@ -141,7 +141,7 @@ if (g.getDivId() != null)
'task_name' => $projecttmp->ref.' '.$projecttmp->title,
'task_resources' => '',
'task_start_date' => $projecttmp->date_start,
- 'task_end_date' => $projecttmp->date_end,
+ 'task_end_date' => !empty($projecttmp->date_end) ? $projecttmp->date_end : 0,
'task_is_group' => 1, 'task_position' => 0, 'task_css' => 'ggroupblack', 'task_milestone' => 0, 'task_parent' => 0, 'task_parent_alternate_id' => 0,
'note' => '',
'task_planned_workload' => 0
From 55977d16f1a9585f1452303b1e88de5f65283e81 Mon Sep 17 00:00:00 2001
From: Lucas Marcouiller
Date: Tue, 2 Dec 2025 08:35:53 +0100
Subject: [PATCH 13/58] fix warning
---
htdocs/projet/ganttchart.inc.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/htdocs/projet/ganttchart.inc.php b/htdocs/projet/ganttchart.inc.php
index 598ab6572ff..f23d99ec95e 100644
--- a/htdocs/projet/ganttchart.inc.php
+++ b/htdocs/projet/ganttchart.inc.php
@@ -141,7 +141,7 @@ if (g.getDivId() != null)
'task_name' => $projecttmp->ref.' '.$projecttmp->title,
'task_resources' => '',
'task_start_date' => $projecttmp->date_start,
- 'task_end_date' => !empty($projecttmp->date_end) ? $projecttmp->date_end : 0,
+ 'task_end_date' => (!empty($projecttmp->date_end) ? $projecttmp->date_end : 0),
'task_is_group' => 1, 'task_position' => 0, 'task_css' => 'ggroupblack', 'task_milestone' => 0, 'task_parent' => 0, 'task_parent_alternate_id' => 0,
'note' => '',
'task_planned_workload' => 0
From a560d4f8864a16b5b500ea85439115639012af9c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20FRANCE?=
Date: Tue, 2 Dec 2025 09:11:56 +0100
Subject: [PATCH 14/58] clean code
---
htdocs/expedition/card.php | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/htdocs/expedition/card.php b/htdocs/expedition/card.php
index a1018a8f10e..3e1d15d09f0 100644
--- a/htdocs/expedition/card.php
+++ b/htdocs/expedition/card.php
@@ -329,7 +329,7 @@ if (empty($reshook)) {
$object->date_shipping = $date_shipping; // Sending date
$object->shipping_method_id = GETPOSTINT('shipping_method_id');
$object->tracking_number = GETPOST('tracking_number', 'alpha');
- $object->note = GETPOST('note', 'restricthtml'); // deprecated
+ $object->note = GETPOST('note_private', 'restricthtml'); // deprecated
$object->note_private = GETPOST('note_private', 'restricthtml');
$object->note_public = GETPOST('note_public', 'restricthtml');
$object->fk_incoterms = GETPOSTINT('incoterm_id');
@@ -337,8 +337,6 @@ if (empty($reshook)) {
$product = new Product($db);
-
-
// Fill array 'array_options' with data from add form
$ret = $extrafields->setOptionalsFromPost(null, $object);
if ($ret < 0) {
@@ -371,7 +369,7 @@ if (empty($reshook)) {
$object->date_shipping = $date_shipping; // Sending date
$object->shipping_method_id = GETPOSTINT('shipping_method_id');
$object->tracking_number = GETPOST('tracking_number', 'alpha');
- $object->note = GETPOST('note', 'restricthtml'); // deprecated
+ $object->note = GETPOST('note_private', 'restricthtml'); // deprecated
$object->note_private = GETPOST('note_private', 'restricthtml');
$object->note_public = GETPOST('note_public', 'restricthtml');
$object->fk_incoterms = GETPOSTINT('incoterm_id');
From c27146f44c42bc242b20617b4b0fd5f5f7695674 Mon Sep 17 00:00:00 2001
From: atm-florian
Date: Tue, 2 Dec 2025 14:42:15 +0100
Subject: [PATCH 15/58] FIX: now there are two values for INVOICE_USE_SITUATION
(1 = legacy, 2 = new), leading to two interpretations of situation_percent;
in some algorithms, only the old algorithm was applied even when the new mode
was on
---
htdocs/compta/journal/sellsjournal.php | 16 +++++++++++---
htdocs/core/lib/pdf.lib.php | 30 ++++++++++++++++++++------
2 files changed, 36 insertions(+), 10 deletions(-)
diff --git a/htdocs/compta/journal/sellsjournal.php b/htdocs/compta/journal/sellsjournal.php
index 625c177b2b3..7c4df5a894a 100644
--- a/htdocs/compta/journal/sellsjournal.php
+++ b/htdocs/compta/journal/sellsjournal.php
@@ -204,12 +204,22 @@ if ($result) {
$line->fetch($obj->id); // id of line
$prev_progress = 0;
if ($obj->situation_cycle_ref > 0) { // It is a situation invoice
+ $prev_progress = $line->get_prev_progress($obj->rowid); // id on invoice
+ if (getDolGlobalInt('INVOICE_USE_SITUATION') === 1) {
+ // backward compat: old behavior => line's situation_percent was cumulative
+ // (it reflected the line's progress state, not the line progress delta)
+ $progressDelta = $obj->situation_percent - $prev_progress;
+ $progressState = $obj->situation_percent;
+ } else {
+ $progressDelta = $obj->situation_percent;
+ $progressState = $prev_progress + $progressDelta;
+ }
+
// Avoid divide by 0
- if ($obj->situation_percent == 0) {
+ if ($progressState == 0) {
$situation_ratio = 0;
} else {
- $prev_progress = $line->get_prev_progress($obj->rowid); // id on invoice
- $situation_ratio = ($obj->situation_percent - $prev_progress) / $obj->situation_percent;
+ $situation_ratio = $progressDelta / $progressState;
}
} else {
$situation_ratio = 1;
diff --git a/htdocs/core/lib/pdf.lib.php b/htdocs/core/lib/pdf.lib.php
index 47acd88bb6f..8497175d558 100644
--- a/htdocs/core/lib/pdf.lib.php
+++ b/htdocs/core/lib/pdf.lib.php
@@ -2410,15 +2410,31 @@ function pdf_getlineprogress($object, $i, $outputlangs, $hidedetails = 0, $hookm
return '';
}
if (empty($hidedetails) || $hidedetails > 1) {
- if (getDolGlobalString('SITUATION_DISPLAY_DIFF_ON_PDF')) {
- $prev_progress = 0;
- if (method_exists($object->lines[$i], 'get_prev_progress')) {
- $prev_progress = $object->lines[$i]->get_prev_progress($object->id);
- }
- $result = round($object->lines[$i]->situation_percent - $prev_progress, 1).'%';
+ // 2 = situation_percent is non-cumulative (delta of current situation)
+ // 1 = (old mode): situation_percent is cumulative (state at situation)
+ $isCumulative = getDolGlobalInt('INVOICE_USE_SITUATION') === 1;
+ $showDelta = (bool) getDolGlobalInt('SITUATION_DISPLAY_DIFF_ON_PDF');
+
+ if ($isCumulative xor $showDelta) {
+ // Either:
+ // - old mode and we want to show a total or
+ // - new mode and we want to show a delta
+ $result = $object->lines[$i]->situation_percent;
} else {
- $result = round($object->lines[$i]->situation_percent, 1).'%';
+ // Either:
+ // - old mode but we want to show a delta or
+ // - new mode but we want to show a total
+ $prev_progress = 0;
+ if (method_exists($object->lines[$i], 'get_prev_progress')) {
+ $prev_progress = $object->lines[$i]->get_prev_progress($object->id);
+ }
+ $result = $isCumulative ?
+ // old mode: we need to compute the delta (total - sum of previous)
+ $object->lines[$i]->situation_percent - $prev_progress :
+ // new mode: we need to compute the total (sum of previous + delta)
+ $prev_progress + $object->lines[$i]->situation_percent;
}
+ $result = round($result, 1).'%';
}
}
return $result;
From c48b719473345186db47225c393d10cdf9b66d8c Mon Sep 17 00:00:00 2001
From: atm-florian
Date: Tue, 2 Dec 2025 14:42:15 +0100
Subject: [PATCH 16/58] FIX: now there are two values for INVOICE_USE_SITUATION
(1 = legacy, 2 = new), leading to two interpretations of situation_percent;
in some algorithms, only the old algorithm was applied even when the new mode
was on
---
htdocs/compta/journal/sellsjournal.php | 16 +++++++++++++---
htdocs/core/lib/pdf.lib.php | 24 ++++++++++++++++++++----
2 files changed, 33 insertions(+), 7 deletions(-)
diff --git a/htdocs/compta/journal/sellsjournal.php b/htdocs/compta/journal/sellsjournal.php
index 625c177b2b3..7c4df5a894a 100644
--- a/htdocs/compta/journal/sellsjournal.php
+++ b/htdocs/compta/journal/sellsjournal.php
@@ -204,12 +204,22 @@ if ($result) {
$line->fetch($obj->id); // id of line
$prev_progress = 0;
if ($obj->situation_cycle_ref > 0) { // It is a situation invoice
+ $prev_progress = $line->get_prev_progress($obj->rowid); // id on invoice
+ if (getDolGlobalInt('INVOICE_USE_SITUATION') === 1) {
+ // backward compat: old behavior => line's situation_percent was cumulative
+ // (it reflected the line's progress state, not the line progress delta)
+ $progressDelta = $obj->situation_percent - $prev_progress;
+ $progressState = $obj->situation_percent;
+ } else {
+ $progressDelta = $obj->situation_percent;
+ $progressState = $prev_progress + $progressDelta;
+ }
+
// Avoid divide by 0
- if ($obj->situation_percent == 0) {
+ if ($progressState == 0) {
$situation_ratio = 0;
} else {
- $prev_progress = $line->get_prev_progress($obj->rowid); // id on invoice
- $situation_ratio = ($obj->situation_percent - $prev_progress) / $obj->situation_percent;
+ $situation_ratio = $progressDelta / $progressState;
}
} else {
$situation_ratio = 1;
diff --git a/htdocs/core/lib/pdf.lib.php b/htdocs/core/lib/pdf.lib.php
index 47acd88bb6f..1b866b5b2a4 100644
--- a/htdocs/core/lib/pdf.lib.php
+++ b/htdocs/core/lib/pdf.lib.php
@@ -2410,15 +2410,31 @@ function pdf_getlineprogress($object, $i, $outputlangs, $hidedetails = 0, $hookm
return '';
}
if (empty($hidedetails) || $hidedetails > 1) {
- if (getDolGlobalString('SITUATION_DISPLAY_DIFF_ON_PDF')) {
+ // 2 = situation_percent is non-cumulative (delta of current situation)
+ // 1 = (old mode): situation_percent is cumulative (state at situation)
+ $isCumulative = getDolGlobalInt('INVOICE_USE_SITUATION') === 1;
+ $showDelta = (bool) getDolGlobalInt('SITUATION_DISPLAY_DIFF_ON_PDF');
+
+ if ($isCumulative xor $showDelta) {
+ // Either:
+ // - old mode and we want to show a total or
+ // - new mode and we want to show a delta
+ $result = $object->lines[$i]->situation_percent;
+ } else {
+ // Either:
+ // - old mode but we want to show a delta or
+ // - new mode but we want to show a total
$prev_progress = 0;
if (method_exists($object->lines[$i], 'get_prev_progress')) {
$prev_progress = $object->lines[$i]->get_prev_progress($object->id);
}
- $result = round($object->lines[$i]->situation_percent - $prev_progress, 1).'%';
- } else {
- $result = round($object->lines[$i]->situation_percent, 1).'%';
+ $result = $isCumulative ?
+ // old mode: we need to compute the delta (total - sum of previous)
+ $object->lines[$i]->situation_percent - $prev_progress :
+ // new mode: we need to compute the total (sum of previous + delta)
+ $prev_progress + $object->lines[$i]->situation_percent;
}
+ $result = round($result, 1).'%';
}
}
return $result;
From 6545957cbd04d807ac17bc97d63d6cc1eedf663b Mon Sep 17 00:00:00 2001
From: tcesar
Date: Tue, 2 Dec 2025 16:37:30 +0100
Subject: [PATCH 17/58] Fix traduction for import error
---
htdocs/imports/import.php | 4 ++--
htdocs/langs/en_US/exports.lang | 1 +
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/htdocs/imports/import.php b/htdocs/imports/import.php
index 2f851a34b92..b78d44263ee 100644
--- a/htdocs/imports/import.php
+++ b/htdocs/imports/import.php
@@ -1898,7 +1898,7 @@ if ($step == 5 && $datatoimport) {
//dol_syslog("line ".$sourcelinenb.' - '.$nboflines.' - '.$excludefirstline.' - '.$endatlinenb);
$arrayrecord = $obj->import_read_record();
if ($arrayrecord === false) {
- $arrayofwarnings[$sourcelinenb][0] = array('lib' => 'File has '.$nboflines.' lines. However we reach the end of file or an empty line at record '.$sourcelinenb.'. This may occurs when some records are split onto several lines and not correctly delimited by the "Char delimiter", or if there is line with no data on all fields.', 'type' => 'EOF_RECORD_ON_SEVERAL_LINES');
+ $arrayofwarnings[$sourcelinenb][0] = array('lib' => $langs->trans('ErrorFileLinesReachEOF', $nboflines, $sourcelinenb), 'type' => 'EOF_RECORD_ON_SEVERAL_LINES');
$endoffile++;
continue;
}
@@ -2321,7 +2321,7 @@ if ($step == 6 && $datatoimport) {
$sourcelinenb++;
$arrayrecord = $obj->import_read_record();
if ($arrayrecord === false) {
- $arrayofwarnings[$sourcelinenb][0] = array('lib' => 'File has '.$nboflines.' lines. However we reach the end of file or an empty line at record '.$sourcelinenb.'. This may occurs when some records are split onto several lines and not correctly delimited by the "Char delimiter", or if there is line with no data on all fields.', 'type' => 'EOF_RECORD_ON_SEVERAL_LINES');
+ $arrayofwarnings[$sourcelinenb][0] = array('lib' => $langs->trans('ErrorFileLinesReachEOF', $nboflines, $sourcelinenb), 'type' => 'EOF_RECORD_ON_SEVERAL_LINES');
$endoffile++;
continue;
}
diff --git a/htdocs/langs/en_US/exports.lang b/htdocs/langs/en_US/exports.lang
index e701d3ae495..521e6dc136a 100644
--- a/htdocs/langs/en_US/exports.lang
+++ b/htdocs/langs/en_US/exports.lang
@@ -152,3 +152,4 @@ MandatoryTargetFieldsNotMapped=Some mandatory target fields are not mapped
AllTargetMandatoryFieldsAreMapped=All target fields that need a mandatory value are mapped
ResultOfSimulationNoError=Result of simulation: No error
NumberOfLinesLimited=Number of lines limited
+ErrorFileLinesReachEOF=File has %s lines. However we reach the end of file or an empty line at record %s. This may occurs when some records are split onto several lines and not correctly delimited by the "Char delimiter", or if there is line with no data on all fields.
From 9941a20fa828b0efc0ba349f92b5c703e03a7081 Mon Sep 17 00:00:00 2001
From: Jon Bendtsen
Date: Thu, 30 Oct 2025 23:12:54 +0100
Subject: [PATCH 18/58] Check if API user has rights to see all thirdparties
Check if API user has rights to see all thirdparties - though perhaps we should check if the user has rights to this particular thirdparty in this contract?
---
htdocs/contrat/class/api_contracts.class.php | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/htdocs/contrat/class/api_contracts.class.php b/htdocs/contrat/class/api_contracts.class.php
index d6ecec7a5ef..3be95c75c73 100644
--- a/htdocs/contrat/class/api_contracts.class.php
+++ b/htdocs/contrat/class/api_contracts.class.php
@@ -210,6 +210,9 @@ class Contracts extends DolibarrApi
if (!DolibarrApiAccess::$user->hasRight('contrat', 'creer')) {
throw new RestException(403, "Insufficient rights");
}
+ if (!DolibarrApiAccess::$user->hasRight('societe', 'client', 'voir')) {
+ throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login.'. No read permission on thirdparties.');
+ }
// Check mandatory fields
$result = $this->_validate($request_data);
@@ -673,7 +676,9 @@ class Contracts extends DolibarrApi
if (!DolibarrApiAccess::$user->hasRight('contrat', 'creer')) {
throw new RestException(403);
}
-
+ if (!DolibarrApiAccess::$user->hasRight('societe', 'client', 'voir')) {
+ throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login.'. No read permission on thirdparties.');
+ }
$result = $this->contract->fetch($id);
if (!$result) {
throw new RestException(404, 'Contrat not found');
From 36415f1fd2a85a2e5f24da57cb60ff253e53a53b Mon Sep 17 00:00:00 2001
From: Jon Bendtsen
Date: Thu, 30 Oct 2025 23:19:13 +0100
Subject: [PATCH 19/58] giving a slight better error message
---
htdocs/contrat/class/api_contracts.class.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/htdocs/contrat/class/api_contracts.class.php b/htdocs/contrat/class/api_contracts.class.php
index 3be95c75c73..7e11e16861e 100644
--- a/htdocs/contrat/class/api_contracts.class.php
+++ b/htdocs/contrat/class/api_contracts.class.php
@@ -211,7 +211,7 @@ class Contracts extends DolibarrApi
throw new RestException(403, "Insufficient rights");
}
if (!DolibarrApiAccess::$user->hasRight('societe', 'client', 'voir')) {
- throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login.'. No read permission on thirdparties.');
+ throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login.'. No read permission on all thirdparties.');
}
// Check mandatory fields
$result = $this->_validate($request_data);
@@ -677,7 +677,7 @@ class Contracts extends DolibarrApi
throw new RestException(403);
}
if (!DolibarrApiAccess::$user->hasRight('societe', 'client', 'voir')) {
- throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login.'. No read permission on thirdparties.');
+ throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login.'. No read permission on all thirdparties.');
}
$result = $this->contract->fetch($id);
if (!$result) {
From 1c697607b1e09995f828c1c1228e36f7b04af6a6 Mon Sep 17 00:00:00 2001
From: Jon Bendtsen
Date: Fri, 31 Oct 2025 19:55:17 +0100
Subject: [PATCH 20/58] default deny access, allow access if user has the right
combination of permissions and/or is the sales representative for the
thirdparty
---
htdocs/contrat/class/api_contracts.class.php | 23 +++++++++++++++++---
1 file changed, 20 insertions(+), 3 deletions(-)
diff --git a/htdocs/contrat/class/api_contracts.class.php b/htdocs/contrat/class/api_contracts.class.php
index 7e11e16861e..71ab5d56841 100644
--- a/htdocs/contrat/class/api_contracts.class.php
+++ b/htdocs/contrat/class/api_contracts.class.php
@@ -676,14 +676,31 @@ class Contracts extends DolibarrApi
if (!DolibarrApiAccess::$user->hasRight('contrat', 'creer')) {
throw new RestException(403);
}
- if (!DolibarrApiAccess::$user->hasRight('societe', 'client', 'voir')) {
- throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login.'. No read permission on all thirdparties.');
- }
$result = $this->contract->fetch($id);
if (!$result) {
throw new RestException(404, 'Contrat not found');
}
+ $deny_access = true;
+ $why_deny_access = '';
+ if (DolibarrApiAccess::$user->hasRight('societe', 'lire')) {
+ if (DolibarrApiAccess::$user->hasRight('societe', 'client', 'voir')) {
+ $deny_access = false;
+ } else {
+ $why_deny_access = 'Extend access to all third parties';
+ if (DolibarrApi::_checkAccessToResource('societe', $this->contract->socid)) {
+ $deny_access = false;
+ } else {
+ $why_deny_access = $why_deny_access.' and NOT sales representative for this thirdparty='.$this->contract->socid;
+ }
+ }
+ } else {
+ $why_deny_access = 'Read third parties linked to user';
+ }
+ if ($deny_access) {
+ throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login.' - missing permissions: '.$why_deny_access);
+ }
+
if (!DolibarrApi::_checkAccessToResource('contrat', $this->contract->id)) {
throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
}
From 6f18235bab7cdc604eacec83f37a00adc8ff4251 Mon Sep 17 00:00:00 2001
From: Jon Bendtsen
Date: Fri, 31 Oct 2025 20:46:50 +0100
Subject: [PATCH 21/58] both socid exists and access to is checked in
api_thirdparty GET :-)
---
htdocs/contrat/class/api_contracts.class.php | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/htdocs/contrat/class/api_contracts.class.php b/htdocs/contrat/class/api_contracts.class.php
index 71ab5d56841..559eff02d89 100644
--- a/htdocs/contrat/class/api_contracts.class.php
+++ b/htdocs/contrat/class/api_contracts.class.php
@@ -21,6 +21,7 @@
use Luracast\Restler\RestException;
require_once DOL_DOCUMENT_ROOT.'/contrat/class/contrat.class.php';
+require_once DOL_DOCUMENT_ROOT.'/societe/class/api_thirdparties.class.php';
/**
* API class for contracts
@@ -208,11 +209,15 @@ class Contracts extends DolibarrApi
public function post($request_data = null)
{
if (!DolibarrApiAccess::$user->hasRight('contrat', 'creer')) {
- throw new RestException(403, "Insufficient rights");
- }
- if (!DolibarrApiAccess::$user->hasRight('societe', 'client', 'voir')) {
- throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login.'. No read permission on all thirdparties.');
+ throw new RestException(403, "Missing permission: Create/modify contracts/subscriptions");
}
+
+ $socid = (int) $request_data['socid'];
+ $thirdparties = new Thirdparties($this->db);
+ $thirdparty_result = $thirdparties->get((int) $socid);
+ // if there is no such thirdparty, then $thirdparties->get((int) $socid); returns 404 "Not Found: Thirdparty not found"
+ // if this user does not have access to this thirdparty, then $thirdparties->get((int) $socid); returns 403 Forbidden: Access not allowed for login ***** on this thirdparty"
+
// Check mandatory fields
$result = $this->_validate($request_data);
@@ -681,6 +686,7 @@ class Contracts extends DolibarrApi
throw new RestException(404, 'Contrat not found');
}
+ $socid = $this->contract->socid;
$deny_access = true;
$why_deny_access = '';
if (DolibarrApiAccess::$user->hasRight('societe', 'lire')) {
@@ -688,10 +694,10 @@ class Contracts extends DolibarrApi
$deny_access = false;
} else {
$why_deny_access = 'Extend access to all third parties';
- if (DolibarrApi::_checkAccessToResource('societe', $this->contract->socid)) {
+ if (DolibarrApi::_checkAccessToResource('societe', $socid)) {
$deny_access = false;
} else {
- $why_deny_access = $why_deny_access.' and NOT sales representative for this thirdparty='.$this->contract->socid;
+ $why_deny_access = $why_deny_access.' and NOT sales representative for this thirdparty='.$socid;
}
}
} else {
From 26a39d1cc86e6488bb40befd10531198fcef54c4 Mon Sep 17 00:00:00 2001
From: Jon Bendtsen
Date: Fri, 31 Oct 2025 20:52:40 +0100
Subject: [PATCH 22/58] creating Thirdparties object takes no arguments
---
htdocs/contrat/class/api_contracts.class.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/htdocs/contrat/class/api_contracts.class.php b/htdocs/contrat/class/api_contracts.class.php
index 559eff02d89..87bfce28ab7 100644
--- a/htdocs/contrat/class/api_contracts.class.php
+++ b/htdocs/contrat/class/api_contracts.class.php
@@ -213,7 +213,7 @@ class Contracts extends DolibarrApi
}
$socid = (int) $request_data['socid'];
- $thirdparties = new Thirdparties($this->db);
+ $thirdparties = new Thirdparties();
$thirdparty_result = $thirdparties->get((int) $socid);
// if there is no such thirdparty, then $thirdparties->get((int) $socid); returns 404 "Not Found: Thirdparty not found"
// if this user does not have access to this thirdparty, then $thirdparties->get((int) $socid); returns 403 Forbidden: Access not allowed for login ***** on this thirdparty"
From b48fed17191d2698ce50eb8a62f89aaf11a3f738 Mon Sep 17 00:00:00 2001
From: Jon Bendtsen
Date: Sun, 2 Nov 2025 11:22:45 +0100
Subject: [PATCH 23/58] PUT now checks both existing socid and any potentially
updated socid for access
---
htdocs/contrat/class/api_contracts.class.php | 36 +++++++++-----------
1 file changed, 16 insertions(+), 20 deletions(-)
diff --git a/htdocs/contrat/class/api_contracts.class.php b/htdocs/contrat/class/api_contracts.class.php
index 87bfce28ab7..0f29f1943fe 100644
--- a/htdocs/contrat/class/api_contracts.class.php
+++ b/htdocs/contrat/class/api_contracts.class.php
@@ -686,26 +686,11 @@ class Contracts extends DolibarrApi
throw new RestException(404, 'Contrat not found');
}
- $socid = $this->contract->socid;
- $deny_access = true;
- $why_deny_access = '';
- if (DolibarrApiAccess::$user->hasRight('societe', 'lire')) {
- if (DolibarrApiAccess::$user->hasRight('societe', 'client', 'voir')) {
- $deny_access = false;
- } else {
- $why_deny_access = 'Extend access to all third parties';
- if (DolibarrApi::_checkAccessToResource('societe', $socid)) {
- $deny_access = false;
- } else {
- $why_deny_access = $why_deny_access.' and NOT sales representative for this thirdparty='.$socid;
- }
- }
- } else {
- $why_deny_access = 'Read third parties linked to user';
- }
- if ($deny_access) {
- throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login.' - missing permissions: '.$why_deny_access);
- }
+ $old_socid = $this->contract->socid;
+ $thirdparties = new Thirdparties();
+ $thirdparty_result = $thirdparties->get((int) $old_socid);
+ // if there is no such thirdparty, then $thirdparties->get((int) $old_socid); returns 404 "Not Found: Thirdparty not found"
+ // if this user does not have access to this thirdparty, then $thirdparties->get((int) $old_socid); returns 403 Forbidden: Access not allowed for login ***** on this thirdparty"
if (!DolibarrApi::_checkAccessToResource('contrat', $this->contract->id)) {
throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
@@ -726,6 +711,17 @@ class Contracts extends DolibarrApi
continue;
}
+ if ($field == 'socid') {
+ $new_socid = (int) $request_data['socid'];
+ $thirdparties = new Thirdparties();
+ $thirdparty_result = $thirdparties->get((int) $new_socid);
+ // if there is no such thirdparty, then $thirdparties->get((int) $old_socid); returns 404 "Not Found: Thirdparty not found"
+ // if this user does not have access to this thirdparty, then $thirdparties->get((int) $old_socid); returns 403 Forbidden: Access not allowed for login ***** on this thirdparty"
+ if ($thirdparty_result->id != $new_socid) {
+ throw new RestException(404, 'New thirdparty with id='.$new_socid.' not found OR this user/api key does not have access to the new thirdparty');
+ }
+ }
+
$this->contract->$field = $this->_checkValForAPI($field, $value, $this->contract);
}
From ddd2927e31d05f8ac2a364b707fc8fa9ecb29616 Mon Sep 17 00:00:00 2001
From: Jon Bendtsen
Date: Sun, 9 Nov 2025 22:20:59 +0100
Subject: [PATCH 24/58] This commit has errors, if you use PUT in the API with
a new socid that the user does not have permission to, then it STILL updates
the contract, and then it gets the contract after update and tells me I do
not have access
---
htdocs/contrat/class/api_contracts.class.php | 62 +++++++++++---------
1 file changed, 34 insertions(+), 28 deletions(-)
diff --git a/htdocs/contrat/class/api_contracts.class.php b/htdocs/contrat/class/api_contracts.class.php
index 0f29f1943fe..d56df54fe71 100644
--- a/htdocs/contrat/class/api_contracts.class.php
+++ b/htdocs/contrat/class/api_contracts.class.php
@@ -21,7 +21,7 @@
use Luracast\Restler\RestException;
require_once DOL_DOCUMENT_ROOT.'/contrat/class/contrat.class.php';
-require_once DOL_DOCUMENT_ROOT.'/societe/class/api_thirdparties.class.php';
+require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
/**
* API class for contracts
@@ -77,7 +77,7 @@ class Contracts extends DolibarrApi
}
if (!DolibarrApi::_checkAccessToResource('contrat', $this->contract->id)) {
- throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
+ throw new RestException(403, 'Access to this contract is not allowed for login '.DolibarrApiAccess::$user->login);
}
$this->contract->fetchObjectLinked();
@@ -213,10 +213,11 @@ class Contracts extends DolibarrApi
}
$socid = (int) $request_data['socid'];
- $thirdparties = new Thirdparties();
- $thirdparty_result = $thirdparties->get((int) $socid);
- // if there is no such thirdparty, then $thirdparties->get((int) $socid); returns 404 "Not Found: Thirdparty not found"
- // if this user does not have access to this thirdparty, then $thirdparties->get((int) $socid); returns 403 Forbidden: Access not allowed for login ***** on this thirdparty"
+ $thirdpartytmp = new Societe($this->db);
+ $thirdparty_result = $thirdpartytmp->fetch($socid);
+ if ($thirdparty_result < 1) {
+ throw new RestException(404, 'Thirdparty with id='.$socid.' not found or not allowed');
+ }
// Check mandatory fields
$result = $this->_validate($request_data);
@@ -276,7 +277,7 @@ class Contracts extends DolibarrApi
}
if (!DolibarrApi::_checkAccessToResource('contrat', $this->contract->id)) {
- throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
+ throw new RestException(403, 'Access to this contract is not allowed for login '.DolibarrApiAccess::$user->login);
}
$obj_ret = [];
@@ -368,7 +369,7 @@ class Contracts extends DolibarrApi
}
if (!DolibarrApi::_checkAccessToResource('contrat', $this->contract->id)) {
- throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
+ throw new RestException(403, 'Access to this contract is not allowed for login '.DolibarrApiAccess::$user->login);
}
$request_data = (object) $request_data;
@@ -428,7 +429,7 @@ class Contracts extends DolibarrApi
}
if (!DolibarrApi::_checkAccessToResource('contrat', $this->contract->id)) {
- throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
+ throw new RestException(403, 'Access to this contract is not allowed for login '.DolibarrApiAccess::$user->login);
}
$request_data = (object) $request_data;
@@ -576,7 +577,7 @@ class Contracts extends DolibarrApi
}
if (!DolibarrApi::_checkAccessToResource('contrat', $this->contract->id)) {
- throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
+ throw new RestException(403, 'Access to this contract is not allowed for login '.DolibarrApiAccess::$user->login);
}
$updateRes = $this->contract->active_line(DolibarrApiAccess::$user, $lineid, (int) $datestart, $dateend, $comment);
@@ -614,7 +615,7 @@ class Contracts extends DolibarrApi
}
if (!DolibarrApi::_checkAccessToResource('contrat', $this->contract->id)) {
- throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
+ throw new RestException(403, 'Access to this contract is not allowed for login '.DolibarrApiAccess::$user->login);
}
$updateRes = $this->contract->close_line(DolibarrApiAccess::$user, $lineid, (int) $datestart, $comment);
@@ -654,7 +655,7 @@ class Contracts extends DolibarrApi
}
if (!DolibarrApi::_checkAccessToResource('contrat', $this->contract->id)) {
- throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
+ throw new RestException(403, 'Access to this contract is not allowed for login '.DolibarrApiAccess::$user->login);
}
// TODO Check the lineid $lineid is a line of object
@@ -687,13 +688,20 @@ class Contracts extends DolibarrApi
}
$old_socid = $this->contract->socid;
- $thirdparties = new Thirdparties();
- $thirdparty_result = $thirdparties->get((int) $old_socid);
- // if there is no such thirdparty, then $thirdparties->get((int) $old_socid); returns 404 "Not Found: Thirdparty not found"
- // if this user does not have access to this thirdparty, then $thirdparties->get((int) $old_socid); returns 403 Forbidden: Access not allowed for login ***** on this thirdparty"
+ $oldthirdpartytmp = new Societe($this->db);
+ $old_thirdparty_result = $oldthirdpartytmp->fetch($old_socid);
+ if ($old_thirdparty_result < 1) {
+ throw new RestException(404, 'Thirdparty with id='.$old_socid.' not found or not allowed');
+ }
+ $new_socid = (int) $request_data['socid'];
+ $newthirdpartytmp = new Societe($this->db);
+ $new_thirdparty_result = $newthirdpartytmp->fetch($new_socid);
+ if ($new_thirdparty_result < 1) {
+ throw new RestException(404, 'Thirdparty with id='.$new_socid.' not found or not allowed');
+ }
if (!DolibarrApi::_checkAccessToResource('contrat', $this->contract->id)) {
- throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
+ throw new RestException(403, 'Access to this contract is not allowed for login '.DolibarrApiAccess::$user->login);
}
foreach ($request_data as $field => $value) {
if ($field == 'id') {
@@ -712,13 +720,11 @@ class Contracts extends DolibarrApi
}
if ($field == 'socid') {
- $new_socid = (int) $request_data['socid'];
- $thirdparties = new Thirdparties();
- $thirdparty_result = $thirdparties->get((int) $new_socid);
- // if there is no such thirdparty, then $thirdparties->get((int) $old_socid); returns 404 "Not Found: Thirdparty not found"
- // if this user does not have access to this thirdparty, then $thirdparties->get((int) $old_socid); returns 403 Forbidden: Access not allowed for login ***** on this thirdparty"
- if ($thirdparty_result->id != $new_socid) {
- throw new RestException(404, 'New thirdparty with id='.$new_socid.' not found OR this user/api key does not have access to the new thirdparty');
+ $new_socid = (int) $value;
+ $loopthirdpartytmp = new Societe($this->db);
+ $new_thirdparty_result = $loopthirdpartytmp->fetch($new_socid);
+ if ($new_thirdparty_result < 1) {
+ throw new RestException(404, 'Thirdparty with id='.$new_socid.' not found or not allowed');
}
}
@@ -744,7 +750,7 @@ class Contracts extends DolibarrApi
public function delete($id)
{
if (!DolibarrApiAccess::$user->hasRight('contrat', 'supprimer')) {
- throw new RestException(403);
+ throw new RestException(403, 'Missing permission: Delete contracts/subscriptions');
}
$result = $this->contract->fetch($id);
if (!$result) {
@@ -752,7 +758,7 @@ class Contracts extends DolibarrApi
}
if (!DolibarrApi::_checkAccessToResource('contrat', $this->contract->id)) {
- throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
+ throw new RestException(403, 'Access to this contract is not allowed for login '.DolibarrApiAccess::$user->login);
}
if (!$this->contract->delete(DolibarrApiAccess::$user)) {
@@ -797,7 +803,7 @@ class Contracts extends DolibarrApi
}
if (!DolibarrApi::_checkAccessToResource('contrat', $this->contract->id)) {
- throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
+ throw new RestException(403, 'Access to this contract is not allowed for login '.DolibarrApiAccess::$user->login);
}
$result = $this->contract->validate(DolibarrApiAccess::$user, '', $notrigger);
@@ -846,7 +852,7 @@ class Contracts extends DolibarrApi
}
if (!DolibarrApi::_checkAccessToResource('contrat', $this->contract->id)) {
- throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
+ throw new RestException(403, 'Access to this contract is not allowed for login '.DolibarrApiAccess::$user->login);
}
$result = $this->contract->closeAll(DolibarrApiAccess::$user, $notrigger);
From 268996c67223dcc7d474752583ccb09b0b2ddf90 Mon Sep 17 00:00:00 2001
From: Jon Bendtsen
Date: Mon, 24 Nov 2025 21:14:24 +0100
Subject: [PATCH 25/58] testing for access to the thirdpartytmp
---
htdocs/contrat/class/api_contracts.class.php | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/htdocs/contrat/class/api_contracts.class.php b/htdocs/contrat/class/api_contracts.class.php
index d56df54fe71..75e2b7972d5 100644
--- a/htdocs/contrat/class/api_contracts.class.php
+++ b/htdocs/contrat/class/api_contracts.class.php
@@ -218,6 +218,13 @@ class Contracts extends DolibarrApi
if ($thirdparty_result < 1) {
throw new RestException(404, 'Thirdparty with id='.$socid.' not found or not allowed');
}
+ if (!DolibarrApi::_checkAccessToResource('societe', $thirdpartytmp->id)) {
+ throw new RestException(404, 'Thirdparty with id='.$thirdpartytmp->id.' not found or not allowed');
+ }
+
+ dol_syslog("socid=".$socid);
+ dol_syslog("thirdparty_result=".$thirdparty_result);
+ dol_syslog("thirdpartytmp->id=".$thirdpartytmp->id);
// Check mandatory fields
$result = $this->_validate($request_data);
From 4c597d650b6e5b99a04233d97190b6c20b1a57af Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Charl=C3=A8ne=20Benke?=
<1179011+defrance@users.noreply.github.com>
Date: Wed, 3 Dec 2025 07:40:57 +0100
Subject: [PATCH 26/58] Update addTimeSpent api method to use default progress
with null parameters, swagger validator fail
---
htdocs/projet/class/api_tasks.class.php | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/htdocs/projet/class/api_tasks.class.php b/htdocs/projet/class/api_tasks.class.php
index 4bf54b81b03..acc112844de 100644
--- a/htdocs/projet/class/api_tasks.class.php
+++ b/htdocs/projet/class/api_tasks.class.php
@@ -4,6 +4,7 @@
* Copyright (C) 2024-2025 Frédéric France
* Copyright (C) 2025 MDW
* Copyright (C) 2025 Jessica Kowal
+ * 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
@@ -623,7 +624,7 @@ class Tasks extends DolibarrApi
* @phan-return array{success:array{code:int,message:string}}
* @phpstan-return array{success:array{code:int,message:string}}
*/
- public function addTimeSpent($id, $date, $duration, $product_id = null, $user_id = 0, $note = '', $progress = null)
+ public function addTimeSpent($id, $date, $duration, $product_id = null, $user_id = 0, $note = '', $progress = -1)
{
if (!DolibarrApiAccess::$user->hasRight('projet', 'creer')) {
throw new RestException(403);
@@ -651,7 +652,7 @@ class Tasks extends DolibarrApi
$this->task->timespent_fk_product = $product_id;
$this->task->timespent_fk_user = $uid;
$this->task->timespent_note = $note;
- if (!empty($this->task->progress))
+ if (!empty($progress) && $progress >= 0 && $progress <= 100) {
$this->task->progress = $progress;
$result = $this->task->addTimeSpent(DolibarrApiAccess::$user, 0);
From dc6c593187821bd3cfdab6ccb1ef27160352f369 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Charl=C3=A8ne=20Benke?=
<1179011+defrance@users.noreply.github.com>
Date: Wed, 3 Dec 2025 09:29:48 +0100
Subject: [PATCH 27/58] Fix permission check from 'lire' to 'read'
---
htdocs/api/class/api_setup.class.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/htdocs/api/class/api_setup.class.php b/htdocs/api/class/api_setup.class.php
index fbd951ba5b8..0e8a6c6f2ed 100644
--- a/htdocs/api/class/api_setup.class.php
+++ b/htdocs/api/class/api_setup.class.php
@@ -1048,7 +1048,7 @@ class Setup extends DolibarrApi
global $langs;
$langs->loadLangs(array('holiday'));
- if (!DolibarrApiAccess::$user->hasRight('holiday', 'lire')) {
+ if (!DolibarrApiAccess::$user->hasRight('holiday', 'read')) {
throw new RestException(403);
}
From f6e667da1a6c1d6984bcb0da07754f7f3580696c Mon Sep 17 00:00:00 2001
From: lelex86 <75493227+lelex86@users.noreply.github.com>
Date: Wed, 3 Dec 2025 09:59:03 +0100
Subject: [PATCH 28/58] FIX: use suspended status in
FactureFournisseurRec::updateline()
- The method checks `$this->status == self::STATUS_SUSPENDED`.
- For recurring **supplier** invoices, `$this->status` is not populated (it is always `null`).
- The real field used to store the suspended flag of the template is `$this->suspended`.
---
htdocs/fourn/class/fournisseur.facture-rec.class.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/htdocs/fourn/class/fournisseur.facture-rec.class.php b/htdocs/fourn/class/fournisseur.facture-rec.class.php
index 1f3e6ed01b7..88cf6b2d894 100644
--- a/htdocs/fourn/class/fournisseur.facture-rec.class.php
+++ b/htdocs/fourn/class/fournisseur.facture-rec.class.php
@@ -1186,7 +1186,7 @@ class FactureFournisseurRec extends CommonInvoice
return -1;
}
- if ($this->status == self::STATUS_SUSPENDED) {
+ if ($this->suspended == self::STATUS_SUSPENDED) {
// Clean parameters
$fk_product = empty($fk_product) ? 0 : $fk_product;
$label = empty($label) ? '' : $label;
From 656255eecd5dbf61d44950dc59f038700410a685 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Charl=C3=A8ne=20Benke?=
<1179011+defrance@users.noreply.github.com>
Date: Wed, 3 Dec 2025 13:03:09 +0100
Subject: [PATCH 29/58] Fix progress assignment in addTimeSpent method
---
htdocs/projet/class/api_tasks.class.php | 1 +
1 file changed, 1 insertion(+)
diff --git a/htdocs/projet/class/api_tasks.class.php b/htdocs/projet/class/api_tasks.class.php
index acc112844de..6cc0df8f3ed 100644
--- a/htdocs/projet/class/api_tasks.class.php
+++ b/htdocs/projet/class/api_tasks.class.php
@@ -654,6 +654,7 @@ class Tasks extends DolibarrApi
$this->task->timespent_note = $note;
if (!empty($progress) && $progress >= 0 && $progress <= 100) {
$this->task->progress = $progress;
+ }
$result = $this->task->addTimeSpent(DolibarrApiAccess::$user, 0);
if ($result == 0) {
From c5b0c1c318f7155166bc7976c8f511d0d477e0c6 Mon Sep 17 00:00:00 2001
From: atm-florian
Date: Wed, 3 Dec 2025 15:13:39 +0100
Subject: [PATCH 30/58] FIX: INVOICE_USE_SITUATION (1 = legacy, 2 = new): some
functions still use only the legacy algorithm
---
.../facture/class/factureligne.class.php | 29 +++++++++++++++++++
htdocs/core/lib/pdf.lib.php | 26 ++++-------------
2 files changed, 34 insertions(+), 21 deletions(-)
diff --git a/htdocs/compta/facture/class/factureligne.class.php b/htdocs/compta/facture/class/factureligne.class.php
index 60af2f33c36..b0897f4acad 100644
--- a/htdocs/compta/facture/class/factureligne.class.php
+++ b/htdocs/compta/facture/class/factureligne.class.php
@@ -973,4 +973,33 @@ class FactureLigne extends CommonInvoiceLine
return $cumulated_percent;
}
}
+
+ /**
+ * Determines if we are using situation invoices.
+ * If so, determines if we are using the new mode (2) or legacy mode (1).
+ *
+ * Legacy mode means invoice line fields store the state of the cycle at the current
+ * situation (a cumulative value) rather than the delta between the previous situation
+ * and the current one. In that case, we need a ratio to convert those values.
+ *
+ * New mode = the values on the line already represent the delta between the previous
+ * state and the current state, so we don't need a conversion (we return 1).
+ *
+ * @return int
+ */
+ public function getSituationRatio()
+ {
+ if (getDolGlobalInt('INVOICE_USE_SITUATION') === 1) {
+ // in legacy mode, the situation invoice line stores the (cumulative) state of the
+ // cycle at the current situation. To get the delta, we need to subtract the
+ // state at the previous situation (if applicable).
+ $prevProgress = $this->get_prev_progress($this->fk_facture);
+
+ return ($this->situation_percent - $prevProgress) / 100;
+ }
+ // new mode (INVOICE_USE_SITUATION == 2):
+ // no ratio needed (data stored on line is already a delta)
+ // or not a situation invoice: no ratio needed either
+ return 1;
+ }
}
diff --git a/htdocs/core/lib/pdf.lib.php b/htdocs/core/lib/pdf.lib.php
index 8497175d558..9ceb5cf5e2f 100644
--- a/htdocs/core/lib/pdf.lib.php
+++ b/htdocs/core/lib/pdf.lib.php
@@ -2451,7 +2451,7 @@ function pdf_getlineprogress($object, $i, $outputlangs, $hidedetails = 0, $hookm
*/
function pdf_getlinetotalexcltax($object, $i, $outputlangs, $hidedetails = 0)
{
- global $conf, $hookmanager;
+ global $hookmanager;
$sign = 1;
if (isset($object->type) && $object->type == 2 && getDolGlobalString('INVOICE_POSITIVE_CREDIT_NOTE')) {
@@ -2480,17 +2480,9 @@ function pdf_getlinetotalexcltax($object, $i, $outputlangs, $hidedetails = 0)
} elseif (empty($hidedetails) || $hidedetails > 1) {
$total_ht = (isModEnabled("multicurrency") && $object->multicurrency_tx != 1 ? $object->lines[$i]->multicurrency_total_ht : $object->lines[$i]->total_ht);
if (!empty($object->lines[$i]->situation_percent) && $object->lines[$i]->situation_percent > 0) {
- // TODO Remove this. The total should be saved correctly in database instead of being modified here.
- $prev_progress = 0;
- $progress = 1;
- if (method_exists($object->lines[$i], 'get_prev_progress')) {
- $prev_progress = $object->lines[$i]->get_prev_progress($object->id);
- $progress = ($object->lines[$i]->situation_percent - $prev_progress) / 100;
- }
- $result .= price($sign * ($total_ht / ($object->lines[$i]->situation_percent / 100)) * $progress, 0, $outputlangs);
- } else {
- $result .= price($sign * $total_ht, 0, $outputlangs);
+ $total_ht *= $object->lines[$i]->getSituationRatio();
}
+ $result .= price($sign * $total_ht, 0, $outputlangs);
}
}
return $result;
@@ -2536,17 +2528,9 @@ function pdf_getlinetotalwithtax($object, $i, $outputlangs, $hidedetails = 0)
} elseif (empty($hidedetails) || $hidedetails > 1) {
$total_ttc = (isModEnabled("multicurrency") && $object->multicurrency_tx != 1 ? $object->lines[$i]->multicurrency_total_ttc : $object->lines[$i]->total_ttc);
if (isset($object->lines[$i]->situation_percent) && $object->lines[$i]->situation_percent > 0) {
- // TODO Remove this. The total should be saved correctly in database instead of being modified here.
- $prev_progress = 0;
- $progress = 1;
- if (method_exists($object->lines[$i], 'get_prev_progress')) {
- $prev_progress = $object->lines[$i]->get_prev_progress($object->id);
- $progress = ($object->lines[$i]->situation_percent - $prev_progress) / 100;
- }
- $result .= price($sign * ($total_ttc / ($object->lines[$i]->situation_percent / 100)) * $progress, 0, $outputlangs);
- } else {
- $result .= price($sign * $total_ttc, 0, $outputlangs);
+ $total_ttc *= $object->lines[$i]->getSituationRatio();
}
+ $result .= price($sign * $total_ttc, 0, $outputlangs);
}
}
return $result;
From 9737acc75aae082fb14dcbabe0a2348ca51281d0 Mon Sep 17 00:00:00 2001
From: atm-florian
Date: Wed, 3 Dec 2025 15:34:12 +0100
Subject: [PATCH 31/58] FIX wrong indentation
---
htdocs/core/lib/pdf.lib.php | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/htdocs/core/lib/pdf.lib.php b/htdocs/core/lib/pdf.lib.php
index 9ceb5cf5e2f..6327c41bd39 100644
--- a/htdocs/core/lib/pdf.lib.php
+++ b/htdocs/core/lib/pdf.lib.php
@@ -2413,7 +2413,7 @@ function pdf_getlineprogress($object, $i, $outputlangs, $hidedetails = 0, $hookm
// 2 = situation_percent is non-cumulative (delta of current situation)
// 1 = (old mode): situation_percent is cumulative (state at situation)
$isCumulative = getDolGlobalInt('INVOICE_USE_SITUATION') === 1;
- $showDelta = (bool) getDolGlobalInt('SITUATION_DISPLAY_DIFF_ON_PDF');
+ $showDelta = (bool)getDolGlobalInt('SITUATION_DISPLAY_DIFF_ON_PDF');
if ($isCumulative xor $showDelta) {
// Either:
@@ -2425,14 +2425,14 @@ function pdf_getlineprogress($object, $i, $outputlangs, $hidedetails = 0, $hookm
// - old mode but we want to show a delta or
// - new mode but we want to show a total
$prev_progress = 0;
- if (method_exists($object->lines[$i], 'get_prev_progress')) {
- $prev_progress = $object->lines[$i]->get_prev_progress($object->id);
- }
- $result = $isCumulative ?
- // old mode: we need to compute the delta (total - sum of previous)
- $object->lines[$i]->situation_percent - $prev_progress :
- // new mode: we need to compute the total (sum of previous + delta)
- $prev_progress + $object->lines[$i]->situation_percent;
+ if (method_exists($object->lines[$i], 'get_prev_progress')) {
+ $prev_progress = $object->lines[$i]->get_prev_progress($object->id);
+ }
+ $result = $isCumulative ?
+ // old mode: we need to compute the delta (total - sum of previous)
+ $object->lines[$i]->situation_percent - $prev_progress :
+ // new mode: we need to compute the total (sum of previous + delta)
+ $prev_progress + $object->lines[$i]->situation_percent;
}
$result = round($result, 1).'%';
}
From a2b1d1ed029528a807c0299f72fc22582ada33d4 Mon Sep 17 00:00:00 2001
From: atm-florian
Date: Wed, 3 Dec 2025 16:01:11 +0100
Subject: [PATCH 32/58] FIX missing space before cast
---
htdocs/core/lib/pdf.lib.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/htdocs/core/lib/pdf.lib.php b/htdocs/core/lib/pdf.lib.php
index 6327c41bd39..49e13f645f2 100644
--- a/htdocs/core/lib/pdf.lib.php
+++ b/htdocs/core/lib/pdf.lib.php
@@ -2413,7 +2413,7 @@ function pdf_getlineprogress($object, $i, $outputlangs, $hidedetails = 0, $hookm
// 2 = situation_percent is non-cumulative (delta of current situation)
// 1 = (old mode): situation_percent is cumulative (state at situation)
$isCumulative = getDolGlobalInt('INVOICE_USE_SITUATION') === 1;
- $showDelta = (bool)getDolGlobalInt('SITUATION_DISPLAY_DIFF_ON_PDF');
+ $showDelta = (bool) getDolGlobalInt('SITUATION_DISPLAY_DIFF_ON_PDF');
if ($isCumulative xor $showDelta) {
// Either:
From c464150b4ba9e1869b120ff8af4302122f986f0f Mon Sep 17 00:00:00 2001
From: atm-florian
Date: Wed, 3 Dec 2025 16:43:11 +0100
Subject: [PATCH 33/58] FIX bad ratio calculation
---
htdocs/compta/facture/class/factureligne.class.php | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/htdocs/compta/facture/class/factureligne.class.php b/htdocs/compta/facture/class/factureligne.class.php
index b0897f4acad..4bb64025dea 100644
--- a/htdocs/compta/facture/class/factureligne.class.php
+++ b/htdocs/compta/facture/class/factureligne.class.php
@@ -995,7 +995,12 @@ class FactureLigne extends CommonInvoiceLine
// state at the previous situation (if applicable).
$prevProgress = $this->get_prev_progress($this->fk_facture);
- return ($this->situation_percent - $prevProgress) / 100;
+ if ($this->situation_percent == 0) {
+ // should not happen
+ return 0;
+ }
+
+ return ($this->situation_percent - $prevProgress) / $this->situation_percent;
}
// new mode (INVOICE_USE_SITUATION == 2):
// no ratio needed (data stored on line is already a delta)
From a49047b0d8e5f1d9e4487173327e950d15d3af57 Mon Sep 17 00:00:00 2001
From: atm-florian
Date: Wed, 3 Dec 2025 16:57:09 +0100
Subject: [PATCH 34/58] phpdoc: wrong return type
---
htdocs/compta/facture/class/factureligne.class.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/htdocs/compta/facture/class/factureligne.class.php b/htdocs/compta/facture/class/factureligne.class.php
index 4bb64025dea..324791d2393 100644
--- a/htdocs/compta/facture/class/factureligne.class.php
+++ b/htdocs/compta/facture/class/factureligne.class.php
@@ -985,7 +985,7 @@ class FactureLigne extends CommonInvoiceLine
* New mode = the values on the line already represent the delta between the previous
* state and the current state, so we don't need a conversion (we return 1).
*
- * @return int
+ * @return float
*/
public function getSituationRatio()
{
From a1476fd2217d95cd5a546635222d5db10059ab64 Mon Sep 17 00:00:00 2001
From: atm-florian
Date: Wed, 3 Dec 2025 17:11:14 +0100
Subject: [PATCH 35/58] SEC: permissions not checked on other tabs of HRM
evaluation card
---
htdocs/hrm/evaluation_agenda.php | 5 +++--
htdocs/hrm/evaluation_contact.php | 6 ++----
htdocs/hrm/evaluation_document.php | 8 ++++----
htdocs/hrm/evaluation_note.php | 8 ++++----
4 files changed, 13 insertions(+), 14 deletions(-)
diff --git a/htdocs/hrm/evaluation_agenda.php b/htdocs/hrm/evaluation_agenda.php
index 628e93a3c23..96598297cba 100644
--- a/htdocs/hrm/evaluation_agenda.php
+++ b/htdocs/hrm/evaluation_agenda.php
@@ -96,8 +96,9 @@ $permissiontoread = $user->rights->hrm->evaluation->read; // Used by the include
// Security check (enable the most restrictive one)
//if ($user->socid > 0) accessforbidden();
//if ($user->socid > 0) $socid = $user->socid;
-//$isdraft = (($object->status == $object::STATUS_DRAFT) ? 1 : 0);
-//restrictedArea($user, $object->module, $object->id, $object->table_element, $object->element, 'fk_soc', 'rowid', $isdraft);
+$isdraft = $object->status == Evaluation::STATUS_DRAFT ? 1 : 0;
+restrictedArea($user, $object->element, $object, $object->table_element, '', 'fk_soc', 'rowid', $isdraft);
+
if (!isModEnabled('hrm')) {
accessforbidden();
}
diff --git a/htdocs/hrm/evaluation_contact.php b/htdocs/hrm/evaluation_contact.php
index ff41c152080..3db6fcc8361 100644
--- a/htdocs/hrm/evaluation_contact.php
+++ b/htdocs/hrm/evaluation_contact.php
@@ -62,10 +62,8 @@ $permission = $user->rights->hrm->evaluation->write;
// Security check (enable the most restrictive one)
//if ($user->socid > 0) accessforbidden();
//if ($user->socid > 0) $socid = $user->socid;
-//$isdraft = (($object->status == $object::STATUS_DRAFT) ? 1 : 0);
-//restrictedArea($user, $object->element, $object->id, $object->table_element, '', 'fk_soc', 'rowid', $isdraft);
-//if (empty($conf->hrm->enabled)) accessforbidden();
-//if (!$permissiontoread) accessforbidden();
+$isdraft = $object->status == Evaluation::STATUS_DRAFT ? 1 : 0;
+restrictedArea($user, $object->element, $object, $object->table_element, '', 'fk_soc', 'rowid', $isdraft);
diff --git a/htdocs/hrm/evaluation_document.php b/htdocs/hrm/evaluation_document.php
index a707913c3c3..70a35db0273 100644
--- a/htdocs/hrm/evaluation_document.php
+++ b/htdocs/hrm/evaluation_document.php
@@ -85,10 +85,10 @@ $permissiontoadd = $user->rights->hrm->evaluation->write; // Used by the includ
$permissiontoread = $user->rights->hrm->evaluation->read;
// Security check (enable the most restrictive one)
-//if ($user->socid > 0) accessforbidden();
-//if ($user->socid > 0) $socid = $user->socid;
-//$isdraft = (($object->status == $object::STATUS_DRAFT) ? 1 : 0);
-//restrictedArea($user, $object->element, $object->id, $object->table_element, '', 'fk_soc', 'rowid', $isdraft);
+
+$isdraft = $object->status == Evaluation::STATUS_DRAFT ? 1 : 0;
+restrictedArea($user, $object->element, $object, $object->table_element, '', 'fk_soc', 'rowid', $isdraft);
+
if (empty($conf->hrm->enabled)) accessforbidden();
if (!$permissiontoread) accessforbidden();
diff --git a/htdocs/hrm/evaluation_note.php b/htdocs/hrm/evaluation_note.php
index ba0ec02d616..947bca9eb03 100644
--- a/htdocs/hrm/evaluation_note.php
+++ b/htdocs/hrm/evaluation_note.php
@@ -66,10 +66,10 @@ $permissiontoread = $user->rights->hrm->evaluation->read; // Used by the includ
// Security check (enable the most restrictive one)
//if ($user->socid > 0) accessforbidden();
//if ($user->socid > 0) $socid = $user->socid;
-//$isdraft = (($object->status == $object::STATUS_DRAFT) ? 1 : 0);
-//restrictedArea($user, $object->element, $object->id, $object->table_element, '', 'fk_soc', 'rowid', $isdraft);
-//if (empty($conf->hrm->enabled)) accessforbidden();
-//if (!$permissiontoread) accessforbidden();
+$isdraft = (($object->status == Evaluation::STATUS_DRAFT) ? 1 : 0);
+restrictedArea($user, $object->element, $object, $object->table_element, '', 'fk_soc', 'rowid', $isdraft);
+if (empty($conf->hrm->enabled)) accessforbidden();
+if (!$permissiontoread) accessforbidden();
/*
From 66d3df4cc67c78e25d5273a3127440a99a66de72 Mon Sep 17 00:00:00 2001
From: Jon Bendtsen
Date: Wed, 3 Dec 2025 21:06:13 +0100
Subject: [PATCH 36/58] Fix broken commit 9801f02 mentioned in issue #36507
---
htdocs/projet/class/api_tasks.class.php | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/htdocs/projet/class/api_tasks.class.php b/htdocs/projet/class/api_tasks.class.php
index 929df66cea9..ab8dcf851b1 100644
--- a/htdocs/projet/class/api_tasks.class.php
+++ b/htdocs/projet/class/api_tasks.class.php
@@ -536,7 +536,7 @@ class Tasks extends DolibarrApi
*
* @url GET {id}/getTimeSpent/{timespent_id}
*
- * @return TimeSpent
+ * @return array|mixed data without useless information
*
* @throws RestException
*/
@@ -945,8 +945,4 @@ class Tasks extends DolibarrApi
}
return $object;
}
-
-
- // \todo
- // getSummaryOfTimeSpent
}
From 8dc2eb0fe43553ea6285282404cdf04c9a716fff Mon Sep 17 00:00:00 2001
From: Jon Bendtsen
Date: Wed, 3 Dec 2025 21:52:35 +0100
Subject: [PATCH 37/58] Checking for access to both new and old socid
---
htdocs/contrat/class/api_contracts.class.php | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/htdocs/contrat/class/api_contracts.class.php b/htdocs/contrat/class/api_contracts.class.php
index 75e2b7972d5..e27d47b98f1 100644
--- a/htdocs/contrat/class/api_contracts.class.php
+++ b/htdocs/contrat/class/api_contracts.class.php
@@ -700,11 +700,8 @@ class Contracts extends DolibarrApi
if ($old_thirdparty_result < 1) {
throw new RestException(404, 'Thirdparty with id='.$old_socid.' not found or not allowed');
}
- $new_socid = (int) $request_data['socid'];
- $newthirdpartytmp = new Societe($this->db);
- $new_thirdparty_result = $newthirdpartytmp->fetch($new_socid);
- if ($new_thirdparty_result < 1) {
- throw new RestException(404, 'Thirdparty with id='.$new_socid.' not found or not allowed');
+ if (!DolibarrApi::_checkAccessToResource('societe', $old_socid)) {
+ throw new RestException(403, 'Access to old thirdparty='.$old_socid.' is not allowed for login '.DolibarrApiAccess::$user->login);
}
if (!DolibarrApi::_checkAccessToResource('contrat', $this->contract->id)) {
@@ -733,6 +730,9 @@ class Contracts extends DolibarrApi
if ($new_thirdparty_result < 1) {
throw new RestException(404, 'Thirdparty with id='.$new_socid.' not found or not allowed');
}
+ if (!DolibarrApi::_checkAccessToResource('societe', $new_socid)) {
+ throw new RestException(403, 'Access to new thirdparty='.$new_socid.' is not allowed for login '.DolibarrApiAccess::$user->login);
+ }
}
$this->contract->$field = $this->_checkValForAPI($field, $value, $this->contract);
From 11cd186e9a83d7f28c811e1c74b11df1f58208ff Mon Sep 17 00:00:00 2001
From: Jon Bendtsen
Date: Wed, 3 Dec 2025 21:54:33 +0100
Subject: [PATCH 38/58] remove debugging dol_syslog
---
htdocs/contrat/class/api_contracts.class.php | 6 ------
1 file changed, 6 deletions(-)
diff --git a/htdocs/contrat/class/api_contracts.class.php b/htdocs/contrat/class/api_contracts.class.php
index e27d47b98f1..7b9c32f66ef 100644
--- a/htdocs/contrat/class/api_contracts.class.php
+++ b/htdocs/contrat/class/api_contracts.class.php
@@ -159,7 +159,6 @@ class Contracts extends DolibarrApi
$sql .= $this->db->plimit($limit + 1, $offset);
}
- dol_syslog("API Rest request");
$result = $this->db->query($sql);
if ($result) {
@@ -222,10 +221,6 @@ class Contracts extends DolibarrApi
throw new RestException(404, 'Thirdparty with id='.$thirdpartytmp->id.' not found or not allowed');
}
- dol_syslog("socid=".$socid);
- dol_syslog("thirdparty_result=".$thirdparty_result);
- dol_syslog("thirdpartytmp->id=".$thirdpartytmp->id);
-
// Check mandatory fields
$result = $this->_validate($request_data);
@@ -316,7 +311,6 @@ class Contracts extends DolibarrApi
$sql .= $this->db->plimit($limit + 1, $offset);
}
- dol_syslog("API Rest request");
$result = $this->db->query($sql);
if ($result) {
$num = $this->db->num_rows($result);
From 23694d67f10380b1477ebc8c29e2b33228c27af9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20FRANCE?=
Date: Thu, 4 Dec 2025 08:48:01 +0100
Subject: [PATCH 39/58] Update blockedlog.php
---
htdocs/blockedlog/admin/blockedlog.php | 1 +
1 file changed, 1 insertion(+)
diff --git a/htdocs/blockedlog/admin/blockedlog.php b/htdocs/blockedlog/admin/blockedlog.php
index 1ff57280382..3eebd71138e 100644
--- a/htdocs/blockedlog/admin/blockedlog.php
+++ b/htdocs/blockedlog/admin/blockedlog.php
@@ -118,6 +118,7 @@ print '
';
print ''.$langs->trans("BlockedLogDesc")."
\n";
+$htmltext = $langs->trans("UnalterableLogTool1").'
';
if ($mysoc->country_code == 'FR') {
$htmltext = $langs->trans("UnalterableLogTool1FR").'
';
}
From a38ffa02ac52e4446460db6b28e3c446e9483e18 Mon Sep 17 00:00:00 2001
From: Florian HENRY
Date: Thu, 4 Dec 2025 10:18:01 +0100
Subject: [PATCH 40/58] fix: subtotal feature button availablity according
setup
---
htdocs/comm/propal/card.php | 7 ++++---
htdocs/commande/card.php | 7 ++++---
htdocs/compta/facture/card-rec.php | 7 ++++---
htdocs/compta/facture/card.php | 7 ++++---
4 files changed, 16 insertions(+), 12 deletions(-)
diff --git a/htdocs/comm/propal/card.php b/htdocs/comm/propal/card.php
index d273223d628..38fca9278e3 100644
--- a/htdocs/comm/propal/card.php
+++ b/htdocs/comm/propal/card.php
@@ -3456,14 +3456,15 @@ if ($action == 'create') {
if (empty($reshook)) {
if ($action != 'editline') {
// Subtotal
- if ($object->status == Propal::STATUS_DRAFT && isModEnabled('subtotals') && getDolGlobalString('SUBTOTAL_TITLE_'.strtoupper($object->element))) {
+ if ($object->status == Propal::STATUS_DRAFT && isModEnabled('subtotals')
+ && (getDolGlobalInt('SUBTOTAL_TITLE_'.strtoupper($object->element)) || getDolGlobalInt('SUBTOTAL_'.strtoupper($object->element)))) {
$langs->load('subtotals');
$url_button = array();
$url_button[] = array(
'lang' => 'subtotals',
- 'enabled' => (isModEnabled('propal') && $object->status == Propal::STATUS_DRAFT),
+ 'enabled' => (isModEnabled('propal') && $object->status == Propal::STATUS_DRAFT && getDolGlobalInt('SUBTOTAL_TITLE_'.strtoupper($object->element))),
'perm' => (bool) $usercancreate,
'label' => $langs->trans('AddTitleLine'),
'url' => '/comm/propal/card.php?id='.$object->id.'&action=add_title_line&token='.newToken()
@@ -3471,7 +3472,7 @@ if ($action == 'create') {
$url_button[] = array(
'lang' => 'subtotals',
- 'enabled' => (isModEnabled('propal') && $object->status == Propal::STATUS_DRAFT),
+ 'enabled' => (isModEnabled('propal') && $object->status == Propal::STATUS_DRAFT && getDolGlobalInt('SUBTOTAL_'.strtoupper($object->element))),
'perm' => (bool) $usercancreate,
'label' => $langs->trans('AddSubtotalLine'),
'url' => '/comm/propal/card.php?id='.$object->id.'&action=add_subtotal_line&token='.newToken()
diff --git a/htdocs/commande/card.php b/htdocs/commande/card.php
index 25fc87772b5..b67dc1ccbbc 100644
--- a/htdocs/commande/card.php
+++ b/htdocs/commande/card.php
@@ -3445,14 +3445,15 @@ if ($action == 'create' && $usercancreate) {
}
// Subtotal
- if ($object->status == Commande::STATUS_DRAFT && isModEnabled('subtotals') && getDolGlobalString('SUBTOTAL_TITLE_'.strtoupper($object->element))) {
+ if ($object->status == Commande::STATUS_DRAFT && isModEnabled('subtotals')
+ && (getDolGlobalInt('SUBTOTAL_TITLE_'.strtoupper($object->element)) || getDolGlobalInt('SUBTOTAL_'.strtoupper($object->element)))) {
$langs->load('subtotals');
$url_button = array();
$url_button[] = array(
'lang' => 'subtotals',
- 'enabled' => (isModEnabled('order') && $object->status == Commande::STATUS_DRAFT),
+ 'enabled' => (isModEnabled('order') && $object->status == Commande::STATUS_DRAFT && getDolGlobalInt('SUBTOTAL_TITLE_'.strtoupper($object->element))),
'perm' => (bool) $usercancreate,
'label' => $langs->trans('AddTitleLine'),
'url' => '/commande/card.php?id='.$object->id.'&action=add_title_line&token='.newToken()
@@ -3460,7 +3461,7 @@ if ($action == 'create' && $usercancreate) {
$url_button[] = array(
'lang' => 'subtotals',
- 'enabled' => (isModEnabled('order') && $object->status == Commande::STATUS_DRAFT),
+ 'enabled' => (isModEnabled('order') && $object->status == Commande::STATUS_DRAFT && getDolGlobalInt('SUBTOTAL_'.strtoupper($object->element))),
'perm' => (bool) $usercancreate,
'label' => $langs->trans('AddSubtotalLine'),
'url' => '/commande/card.php?id='.$object->id.'&action=add_subtotal_line&token='.newToken()
diff --git a/htdocs/compta/facture/card-rec.php b/htdocs/compta/facture/card-rec.php
index d342afd0fed..687a10d0f32 100644
--- a/htdocs/compta/facture/card-rec.php
+++ b/htdocs/compta/facture/card-rec.php
@@ -2076,14 +2076,15 @@ if ($action == 'create') {
);
// Subtotal
- if (empty($object->suspended) && isModEnabled('subtotals') && getDolGlobalString('SUBTOTAL_TITLE_'.strtoupper($object->element))) {
+ if (empty($object->suspended) && isModEnabled('subtotals')
+ && (getDolGlobalInt('SUBTOTAL_TITLE_'.strtoupper($object->element)) || getDolGlobalInt('SUBTOTAL_'.strtoupper($object->element)))) {
$langs->load("subtotals");
$url_button = array();
$url_button[] = array(
'lang' => 'subtotals',
- 'enabled' => (isModEnabled('invoice') && $object->status == Facture::STATUS_DRAFT),
+ 'enabled' => (isModEnabled('invoice') && $object->status == Facture::STATUS_DRAFT && getDolGlobalInt('SUBTOTAL_TITLE_'.strtoupper($object->element))),
'perm' => (bool) $usercancreate,
'label' => $langs->trans('AddTitleLine'),
'url' => '/compta/facture/card-rec.php?id='.$object->id.'&action=add_title_line&token='.newToken()
@@ -2091,7 +2092,7 @@ if ($action == 'create') {
$url_button[] = array(
'lang' => 'subtotals',
- 'enabled' => (isModEnabled('invoice') && $object->status == Facture::STATUS_DRAFT),
+ 'enabled' => (isModEnabled('invoice') && $object->status == Facture::STATUS_DRAFT && getDolGlobalInt('SUBTOTAL_'.strtoupper($object->element))),
'perm' => (bool) $usercancreate,
'label' => $langs->trans('AddSubtotalLine'),
'url' => '/compta/facture/card-rec.php?id='.$object->id.'&action=add_subtotal_line&token='.newToken()
diff --git a/htdocs/compta/facture/card.php b/htdocs/compta/facture/card.php
index d83c4b4fb0e..41f1f015c84 100644
--- a/htdocs/compta/facture/card.php
+++ b/htdocs/compta/facture/card.php
@@ -6390,14 +6390,15 @@ if ($action == 'create') {
}
// Subtotal
- if ($object->status == Facture::STATUS_DRAFT && isModEnabled('subtotals') && getDolGlobalString('SUBTOTAL_TITLE_'.strtoupper($object->element))) {
+ if ($object->status == Facture::STATUS_DRAFT && isModEnabled('subtotals')
+ && (getDolGlobalInt('SUBTOTAL_TITLE_'.strtoupper($object->element)) || getDolGlobalInt('SUBTOTAL_'.strtoupper($object->element)))) {
$langs->load("subtotals");
$url_button = array();
$url_button[] = array(
'lang' => 'subtotals',
- 'enabled' => (isModEnabled('invoice') && $object->status == Facture::STATUS_DRAFT),
+ 'enabled' => (isModEnabled('invoice') && $object->status == Facture::STATUS_DRAFT && getDolGlobalInt('SUBTOTAL_TITLE_'.strtoupper($object->element))),
'perm' => (bool) $usercancreate,
'label' => $langs->trans('AddTitleLine'),
'url' => '/compta/facture/card.php?facid='.$object->id.'&action=add_title_line&token='.newToken()
@@ -6405,7 +6406,7 @@ if ($action == 'create') {
$url_button[] = array(
'lang' => 'subtotals',
- 'enabled' => (isModEnabled('invoice') && $object->status == Facture::STATUS_DRAFT),
+ 'enabled' => (isModEnabled('invoice') && $object->status == Facture::STATUS_DRAFT && getDolGlobalInt('SUBTOTAL_'.strtoupper($object->element))),
'perm' => (bool) $usercancreate,
'label' => $langs->trans('AddSubtotalLine'),
'url' => '/compta/facture/card.php?facid='.$object->id.'&action=add_subtotal_line&token='.newToken()
From 6b05561ee8a0075637eecb08960683c2ba742086 Mon Sep 17 00:00:00 2001
From: Florian HENRY
Date: Thu, 4 Dec 2025 10:22:47 +0100
Subject: [PATCH 41/58] new: defaut to True show unit price on title and sub
total
---
htdocs/core/tpl/subtotal_create.tpl.php | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/htdocs/core/tpl/subtotal_create.tpl.php b/htdocs/core/tpl/subtotal_create.tpl.php
index 1f212f05b59..806a0fb7dc3 100644
--- a/htdocs/core/tpl/subtotal_create.tpl.php
+++ b/htdocs/core/tpl/subtotal_create.tpl.php
@@ -50,14 +50,14 @@ if ($type == 'title') {
$formquestion = array(
array('type' => 'text', 'name' => 'subtotallinedesc', 'label' => $langs->trans("SubtotalLineDesc"), 'moreattr' => 'placeholder="' . $langs->trans("Description") . '"'),
array('type' => 'select', 'name' => 'subtotallinelevel', 'label' => $langs->trans("SubtotalLineLevel"), 'values' => $depth_array, 'default' => 1, 'select_show_empty' => 0),
- array('type' => 'checkbox', 'value' => false, 'name' => 'titleshowuponpdf', 'label' => $langs->trans("ShowUPOnPDF")),
- array('type' => 'checkbox', 'value' => false, 'name' => 'titleshowtotalexludingvatonpdf', 'label' => $langs->trans("ShowTotalExludingVATOnPDF")),
+ array('type' => 'checkbox', 'value' => true, 'name' => 'titleshowuponpdf', 'label' => $langs->trans("ShowUPOnPDF")),
+ array('type' => 'checkbox', 'value' => true, 'name' => 'titleshowtotalexludingvatonpdf', 'label' => $langs->trans("ShowTotalExludingVATOnPDF")),
array('type' => 'checkbox', 'value' => false, 'name' => 'titleforcepagebreak', 'label' => $langs->trans("ForcePageBreak")),
);
} elseif ($type == 'subtotal') {
$formquestion = array(
array('type' => 'select', 'name' => 'subtotaltitleline', 'label' => $langs->trans("CorrespondingTitleLine"), 'values' => $titles, 'select_show_empty' => 0),
- array('type' => 'checkbox', 'value' => false, 'name' => 'subtotalshowtotalexludingvatonpdf', 'label' => $langs->trans("ShowTotalExludingVATOnPDF")),
+ array('type' => 'checkbox', 'value' => true, 'name' => 'subtotalshowtotalexludingvatonpdf', 'label' => $langs->trans("ShowTotalExludingVATOnPDF")),
);
}
From 68004a40091ec0017912d0691f19a8734c35d99f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Benjamin=20Fali=C3=A8re?=
Date: Thu, 4 Dec 2025 10:35:42 +0100
Subject: [PATCH 42/58] FIX(propal): Incorrect HT/TTC recalculation when
editing proposal lines
---
htdocs/comm/propal/card.php | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/htdocs/comm/propal/card.php b/htdocs/comm/propal/card.php
index d273223d628..ecc7957b797 100644
--- a/htdocs/comm/propal/card.php
+++ b/htdocs/comm/propal/card.php
@@ -19,6 +19,7 @@
* Copyright (C) 2023 William Mead
* Copyright (C) 2024-2025 MDW
* Copyright (C) 2024 Alexandre Spangaro
+ * Copyright (C) 2025 Benjamin Falière
*
* 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
@@ -1932,6 +1933,12 @@ if (empty($reshook)) {
$type = $product->type;
$price_base_type = $product->price_base_type;
+
+ // If base type TTc, we change pu value to define the TTC one
+ if ($price_base_type == 'TTC') {
+ $pu = $pu_ttc;
+ }
+
$label = ((GETPOST('update_label') && GETPOST('product_label')) ? GETPOST('product_label') : '');
$price_min = $product->price_min;
From 9e63298b2ee0427b0980ad3dda2245e5e30a50e0 Mon Sep 17 00:00:00 2001
From: Laurent Destailleur
Date: Thu, 4 Dec 2025 14:20:08 +0100
Subject: [PATCH 43/58] CSS
---
htdocs/theme/eldy/global.inc.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/htdocs/theme/eldy/global.inc.php b/htdocs/theme/eldy/global.inc.php
index 404c780b2ef..f6b0af468c1 100644
--- a/htdocs/theme/eldy/global.inc.php
+++ b/htdocs/theme/eldy/global.inc.php
@@ -7967,8 +7967,8 @@ div.kanban.column div.ui-sortable-helper {
/* ============================================================================== */
.searchpage .tagtr .tagtd {
- padding-top: 2px;
- padding-bottom: 2px;
+ padding-top: 5px;
+ padding-bottom: 5px;
}
.searchpage .tagtr .tagtd .button {
background: unset;
From 966bdc84e574f362305fcdc0f359278451b925e1 Mon Sep 17 00:00:00 2001
From: Laurent Destailleur
Date: Thu, 4 Dec 2025 14:46:10 +0100
Subject: [PATCH 44/58] Fix warning
---
htdocs/blockedlog/admin/blockedlog.php | 3 +--
htdocs/core/lib/functions.lib.php | 4 ++--
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/htdocs/blockedlog/admin/blockedlog.php b/htdocs/blockedlog/admin/blockedlog.php
index 1ff57280382..732dfcf3c90 100644
--- a/htdocs/blockedlog/admin/blockedlog.php
+++ b/htdocs/blockedlog/admin/blockedlog.php
@@ -120,10 +120,9 @@ print ''.$langs->trans("BlockedLogDesc")."
country_code == 'FR') {
$htmltext = $langs->trans("UnalterableLogTool1FR").'
';
+ print info_admin($htmltext, 0, 0, 'warning');
}
-print info_admin($htmltext, 0, 0, 'warning');
-
print '
';
print ''; // You can use div-table-responsive-no-min if you don't need reserved height for your table
diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php
index 4e98eb38c74..5736d0a50d9 100644
--- a/htdocs/core/lib/functions.lib.php
+++ b/htdocs/core/lib/functions.lib.php
@@ -7579,7 +7579,7 @@ function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $
//print $amount."-";
$data = explode('.', $amount);
$decpart = isset($data[1]) ? $data[1] : '';
- $decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale
+ $decpart = preg_replace('/0+$/i', '', $decpart); // Remove 0 at end of decimal part
//print "decpart=".$decpart."
";
$end = '';
@@ -7662,7 +7662,7 @@ function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $
*/
function price2num($amount, $rounding = '', $option = 0)
{
- global $langs, $conf;
+ global $langs;
// Clean parameters
if (is_null($amount)) {
From 998d8c4b502b91f015822b28bfb99a8784bcad1f Mon Sep 17 00:00:00 2001
From: Laurent Destailleur
Date: Thu, 4 Dec 2025 14:53:33 +0100
Subject: [PATCH 45/58] Fix warning
---
htdocs/margin/admin/margin.php | 3 +++
1 file changed, 3 insertions(+)
diff --git a/htdocs/margin/admin/margin.php b/htdocs/margin/admin/margin.php
index 399dc03cc14..fd34f14bad6 100644
--- a/htdocs/margin/admin/margin.php
+++ b/htdocs/margin/admin/margin.php
@@ -74,14 +74,17 @@ if ($action == 'update') {
$error = 0;
if (dolibarr_set_const($db, 'MARGIN_METHODE_FOR_DISCOUNT', GETPOST('MARGIN_METHODE_FOR_DISCOUNT'), 'chaine', 0, '', $conf->entity) <= 0) {
dol_print_error($db);
+ $error++;
}
if (dolibarr_set_const($db, 'MARGIN_TYPE', GETPOST('MARGIN_TYPE'), 'chaine', 0, '', $conf->entity) <= 0) {
dol_print_error($db);
+ $error++;
}
if (dolibarr_set_const($db, 'AGENT_CONTACT_TYPE', GETPOST('AGENT_CONTACT_TYPE'), 'chaine', 0, '', $conf->entity) <= 0) {
dol_print_error($db);
+ $error++;
}
if (!$error) {
From 8be39d4f5a4a9baeac68884a805c039315f846f4 Mon Sep 17 00:00:00 2001
From: William Mead
Date: Thu, 4 Dec 2025 15:05:10 +0100
Subject: [PATCH 46/58] Fixed shipping and availability updating. Updated
contributor details.
---
htdocs/comm/propal/class/propal.class.php | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/htdocs/comm/propal/class/propal.class.php b/htdocs/comm/propal/class/propal.class.php
index 9de54f75831..e1925348e5b 100644
--- a/htdocs/comm/propal/class/propal.class.php
+++ b/htdocs/comm/propal/class/propal.class.php
@@ -18,7 +18,7 @@
* Copyright (C) 2022 ATM Consulting
* Copyright (C) 2022 OpenDSI
* Copyright (C) 2022 Gauthier VERDOL
- * Copyright (C) 2023 William Mead
+ * Copyright (C) 2023 William Mead
* Copyright (C) 2024-2025 MDW
*
* This program is free software; you can redistribute it and/or modify
@@ -1879,6 +1879,8 @@ class Propal extends CommonObject
$sql .= " deposit_percent = ".(!empty($this->deposit_percent) ? "'".$this->db->escape($this->deposit_percent)."'" : "null").",";
$sql .= " fk_mode_reglement = ".(!empty($this->mode_reglement_id) ? (int) $this->mode_reglement_id : "null").",";
$sql .= " fk_input_reason = ".(!empty($this->demand_reason_id) ? (int) $this->demand_reason_id : "null").",";
+ $sql .= " fk_shipping_method = ".(isset($this->shipping_method_id) ? (int) $this->shipping_method_id : "null").",";
+ $sql .= " fk_availability = ".(!empty($this->availability_id) ? (int) $this->availability_id : "null").",";
$sql .= " note_private = ".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
$sql .= " note_public = ".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
$sql .= " model_pdf = ".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
From 8deec3f91805630e1ecf97d57f6927d5ee725924 Mon Sep 17 00:00:00 2001
From: Jon Bendtsen
Date: Thu, 4 Dec 2025 16:23:13 +0100
Subject: [PATCH 47/58] it returns an Object, but it can not be TimeSpent
because that creates a infinite loop
---
htdocs/projet/class/api_tasks.class.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/htdocs/projet/class/api_tasks.class.php b/htdocs/projet/class/api_tasks.class.php
index ab8dcf851b1..13204640e7f 100644
--- a/htdocs/projet/class/api_tasks.class.php
+++ b/htdocs/projet/class/api_tasks.class.php
@@ -536,7 +536,7 @@ class Tasks extends DolibarrApi
*
* @url GET {id}/getTimeSpent/{timespent_id}
*
- * @return array|mixed data without useless information
+ * @return Object data without useless information
*
* @throws RestException
*/
From 3d2a826865af6630ce35aa50fccf61b203867e40 Mon Sep 17 00:00:00 2001
From: Jon Bendtsen
Date: Thu, 4 Dec 2025 16:51:47 +0100
Subject: [PATCH 48/58] FIX #36407 BUG: API POST contact to a non-existing
socid gives DB error
---
htdocs/societe/class/api_contacts.class.php | 23 +++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/htdocs/societe/class/api_contacts.class.php b/htdocs/societe/class/api_contacts.class.php
index f497704568d..17cf3cdccc4 100644
--- a/htdocs/societe/class/api_contacts.class.php
+++ b/htdocs/societe/class/api_contacts.class.php
@@ -22,6 +22,7 @@ use Luracast\Restler\RestException;
//require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
//require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
+require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
/**
@@ -365,6 +366,17 @@ class Contacts extends DolibarrApi
}
continue;
}
+ if ($field == 'socid') {
+ $new_socid = (int) $value;
+ $loopthirdpartytmp = new Societe($this->db);
+ $new_thirdparty_result = $loopthirdpartytmp->fetch($new_socid);
+ if ($new_thirdparty_result < 1) {
+ throw new RestException(404, 'Thirdparty with id='.$new_socid.' not found or not allowed');
+ }
+ if (!DolibarrApi::_checkAccessToResource('societe', $new_socid)) {
+ throw new RestException(403, 'Access to socid/thirdparty='.$new_socid.' is not allowed for login '.DolibarrApiAccess::$user->login);
+ }
+ }
$this->contact->$field = $this->_checkValForAPI($field, $value, $this->contact);
}
@@ -422,6 +434,17 @@ class Contacts extends DolibarrApi
}
continue;
}
+ if ($field == 'socid') {
+ $new_socid = (int) $value;
+ $loopthirdpartytmp = new Societe($this->db);
+ $new_thirdparty_result = $loopthirdpartytmp->fetch($new_socid);
+ if ($new_thirdparty_result < 1) {
+ throw new RestException(404, 'Thirdparty with id='.$new_socid.' not found or not allowed');
+ }
+ if (!DolibarrApi::_checkAccessToResource('societe', $new_socid)) {
+ throw new RestException(403, 'Access to socid/thirdparty='.$new_socid.' is not allowed for login '.DolibarrApiAccess::$user->login);
+ }
+ }
$this->contact->$field = $this->_checkValForAPI($field, $value, $this->contact);
}
From 50c27b304ef4989f88e737ce242318f0bf8ddd37 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20FRANCE?=
Date: Thu, 4 Dec 2025 17:27:15 +0100
Subject: [PATCH 49/58] Update blockedlog.php
---
htdocs/blockedlog/admin/blockedlog.php | 1 -
1 file changed, 1 deletion(-)
diff --git a/htdocs/blockedlog/admin/blockedlog.php b/htdocs/blockedlog/admin/blockedlog.php
index e2699ca51d8..732dfcf3c90 100644
--- a/htdocs/blockedlog/admin/blockedlog.php
+++ b/htdocs/blockedlog/admin/blockedlog.php
@@ -118,7 +118,6 @@ print '
';
print ''.$langs->trans("BlockedLogDesc")."
\n";
-$htmltext = $langs->trans("UnalterableLogTool1").'
';
if ($mysoc->country_code == 'FR') {
$htmltext = $langs->trans("UnalterableLogTool1FR").'
';
print info_admin($htmltext, 0, 0, 'warning');
From c2902827a9253582da6a92f455f74c1933d307b9 Mon Sep 17 00:00:00 2001
From: Laurent Destailleur
Date: Thu, 4 Dec 2025 18:09:43 +0100
Subject: [PATCH 50/58] Fix CI
---
htdocs/projet/tasks/note.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/htdocs/projet/tasks/note.php b/htdocs/projet/tasks/note.php
index bb44a662ad6..52e64cc3338 100644
--- a/htdocs/projet/tasks/note.php
+++ b/htdocs/projet/tasks/note.php
@@ -88,7 +88,7 @@ if (!empty($project_ref) && !empty($withproject)) {
$id = $tasksarray[0]->id;
$object->fetch($id);
} else {
- header("Location: ".DOL_URL_ROOT.'/projet/tasks.php?id='.$projectstatic->id.($withproject ? '&withproject=1' : '').(empty($mode) ? '' : '&mode='.$mode));
+ header("Location: ".DOL_URL_ROOT.'/projet/tasks.php?id='.$projectstatic->id.($withproject ? '&withproject=1' : ''));
exit;
}
}
From bb9f381e9fe127946b9740a5011ed6087050f155 Mon Sep 17 00:00:00 2001
From: Laurent Destailleur
Date: Thu, 4 Dec 2025 18:31:31 +0100
Subject: [PATCH 51/58] Fix CI
---
htdocs/projet/tasks/note.php | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/htdocs/projet/tasks/note.php b/htdocs/projet/tasks/note.php
index 52e64cc3338..79cfb7e513e 100644
--- a/htdocs/projet/tasks/note.php
+++ b/htdocs/projet/tasks/note.php
@@ -88,7 +88,7 @@ if (!empty($project_ref) && !empty($withproject)) {
$id = $tasksarray[0]->id;
$object->fetch($id);
} else {
- header("Location: ".DOL_URL_ROOT.'/projet/tasks.php?id='.$projectstatic->id.($withproject ? '&withproject=1' : ''));
+ header("Location: ".DOL_URL_ROOT.'/projet/tasks.php?id='.$projectstatic->id.'&withproject=1');
exit;
}
}
@@ -145,7 +145,8 @@ if ($object->id > 0) {
$head = project_prepare_head($projectstatic);
print dol_get_fiche_head($head, $tab, $langs->trans("Project"), -1, ($projectstatic->public ? 'projectpub' : 'project'));
- $param = (isset($mode) && $mode == 'mine' ? '&mode=mine' : '');
+ $param = '';
+
// Project card
$linkback = ''.$langs->trans("BackToList").'';
From 193a42cd4756f2965c601d4f765767f2f7550d22 Mon Sep 17 00:00:00 2001
From: Laurent Destailleur
Date: Thu, 4 Dec 2025 19:02:57 +0100
Subject: [PATCH 52/58] Fix #36520
---
htdocs/fourn/class/fournisseur.orderline.class.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/htdocs/fourn/class/fournisseur.orderline.class.php b/htdocs/fourn/class/fournisseur.orderline.class.php
index c6711084932..cb8ecac2d59 100644
--- a/htdocs/fourn/class/fournisseur.orderline.class.php
+++ b/htdocs/fourn/class/fournisseur.orderline.class.php
@@ -358,8 +358,8 @@ class CommandeFournisseurLigne extends CommonOrderLine
} else {
$sql .= "null,";
}
- $sql .= "'".$this->db->escape((string) $this->product_type)."',";
- $sql .= ((int) $this->special_code) . ",";
+ $sql .= ((int) $this->product_type).",";
+ $sql .= ((int) $this->special_code).",";
$sql .= "'".$this->db->escape((string) $this->rang)."',";
$sql .= "'".$this->db->escape((string) $this->qty)."', ";
$sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape((string) $this->vat_src_code)."'").",";
From 1cd7de6e21dbd93b19caa39513aa1186da8dc96c Mon Sep 17 00:00:00 2001
From: Laurent Destailleur
Date: Thu, 4 Dec 2025 20:57:43 +0100
Subject: [PATCH 53/58] Better compatibility
---
htdocs/fourn/class/fournisseur.facture-rec.class.php | 1 +
1 file changed, 1 insertion(+)
diff --git a/htdocs/fourn/class/fournisseur.facture-rec.class.php b/htdocs/fourn/class/fournisseur.facture-rec.class.php
index 1f3e6ed01b7..96d5fb6fee9 100644
--- a/htdocs/fourn/class/fournisseur.facture-rec.class.php
+++ b/htdocs/fourn/class/fournisseur.facture-rec.class.php
@@ -707,6 +707,7 @@ class FactureFournisseurRec extends CommonInvoice
$this->socid = $obj->fk_soc;
$this->date_creation = $obj->datec;
$this->date_modification = $obj->tms;
+ $this->status = $obj->suspended;
$this->suspended = $obj->suspended;
$this->libelle = $obj->label;
$this->label = $obj->label;
From 810ac047dca6c5e6e08785bfdd6c97d102d6f578 Mon Sep 17 00:00:00 2001
From: Laurent Destailleur
Date: Thu, 4 Dec 2025 21:08:17 +0100
Subject: [PATCH 54/58] Fix CI
---
.github/workflows/ci-on-release.yml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.github/workflows/ci-on-release.yml b/.github/workflows/ci-on-release.yml
index 7c3b0d716e3..958b8865b58 100644
--- a/.github/workflows/ci-on-release.yml
+++ b/.github/workflows/ci-on-release.yml
@@ -6,6 +6,8 @@ on:
jobs:
trigger-docker:
+ runs-on: ubuntu-latest
+
steps:
- name: Generate a token
id: generate-token
From 8b51ff85fd946340164e6977a1c23842ea6bed74 Mon Sep 17 00:00:00 2001
From: Laurent Destailleur
Date: Thu, 4 Dec 2025 21:58:10 +0100
Subject: [PATCH 55/58] Fix CI
---
htdocs/core/lib/functions.lib.php | 1 +
htdocs/core/lib/price.lib.php | 3 ++-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php
index dc781a719ca..3bf8248767c 100644
--- a/htdocs/core/lib/functions.lib.php
+++ b/htdocs/core/lib/functions.lib.php
@@ -12713,6 +12713,7 @@ function printCommonFooter($zone = 'private')
$parameters = array('zone' => $zone);
$tmpobject = null;
+ // @phan-suppress-next-line PhanPluginConstantVariableNull
$reshook = $hookmanager->executeHooks('printCommonFooter', $parameters, $tmpobject, $action); // Note that $action and $object may have been modified by some hooks
if (empty($reshook)) {
if (getDolGlobalString('MAIN_HTML_FOOTER')) {
diff --git a/htdocs/core/lib/price.lib.php b/htdocs/core/lib/price.lib.php
index 1d39ec0c7a7..3571167be9e 100644
--- a/htdocs/core/lib/price.lib.php
+++ b/htdocs/core/lib/price.lib.php
@@ -458,7 +458,8 @@ function calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocalt
// Allow an external module to bypass the calculation of prices
$parameters = array('result' => $result);
- $tmpobject = null; $tmpaction = '';
+ $tmpobject = null;
+ $tmpaction = '';
// @phan-suppress-next-line PhanPluginConstantVariableNull
$reshook = $hookmanager->executeHooks('calcul_price_total', $parameters, $tmpobject, $tmpaction); // @phan-suppress-current-line PhanPluginConstantVariableNull
if ($reshook > 0 && !empty($hookmanager->resArray['result'])) {
From d6ae74b3a9ea7cb9f5d2fe05a08414bc8fba75c5 Mon Sep 17 00:00:00 2001
From: Laurent Destailleur
Date: Fri, 5 Dec 2025 02:12:35 +0100
Subject: [PATCH 56/58] CSS
---
htdocs/core/lib/usergroups.lib.php | 18 +++++++++---------
htdocs/langs/en_US/admin.lang | 2 ++
htdocs/theme/eldy/global.inc.php | 3 +++
htdocs/theme/md/style.css.php | 3 +++
4 files changed, 17 insertions(+), 9 deletions(-)
diff --git a/htdocs/core/lib/usergroups.lib.php b/htdocs/core/lib/usergroups.lib.php
index 0a164de2404..f1e24d565cb 100644
--- a/htdocs/core/lib/usergroups.lib.php
+++ b/htdocs/core/lib/usergroups.lib.php
@@ -524,7 +524,7 @@ function showSkins($fuser, $edit = 0, $foruserprofile = false)
print ''.$langs->trans("DarkThemeMode").' | ';
print '';
if ($edit) {
- print $form->selectarray('THEME_DARKMODEENABLED', $listofdarkmodes, getDolGlobalInt('THEME_DARKMODEENABLED'), 0, 0, 0, '', 0, 0, 0, '', 'minwidth150 maxwidth250');
+ print $form->selectarray('THEME_DARKMODEENABLED', $listofdarkmodes, getDolGlobalInt('THEME_DARKMODEENABLED'), 0, 0, 0, '', 0, 0, 0, '', 'minwidth200 maxwidth250');
} else {
print $listofdarkmodes[getDolGlobalInt('THEME_DARKMODEENABLED')];
}
@@ -624,19 +624,19 @@ function showSkins($fuser, $edit = 0, $foruserprofile = false)
if (getDolGlobalString('THEME_ELDY_USEBORDERONTABLE')) {
$listofborderradius = array(
- 0 => $langs->transnoentitiesnoconv("No"),
- 4 => $langs->transnoentitiesnoconv("Size").' 4',
- 6 => $langs->transnoentitiesnoconv("Size").' 6',
- 8 => $langs->transnoentitiesnoconv("Size").' 8',
- 10 => $langs->transnoentitiesnoconv("Size").' 10',
- 20 => $langs->transnoentitiesnoconv("Size").' 20',
+ 0 => array('label' => $langs->transnoentitiesnoconv("NoRoundedCorners")),
+ 4 => array('label' => $langs->transnoentitiesnoconv("CornerRadius").' 4'),
+ 6 => array('label' => $langs->transnoentitiesnoconv("CornerRadius").' 6'),
+ 8 => array('label' => $langs->transnoentitiesnoconv("CornerRadius").' 8'),
+ 10 => array('label' => $langs->transnoentitiesnoconv("CornerRadius").' 10'),
+ 20 => array('label' => $langs->transnoentitiesnoconv("CornerRadius").' 20'),
);
print ' ';
if ($edit) {
- print $form->selectarray('THEME_ELDY_BORDER_RADIUS', $listofborderradius, getDolGlobalInt('THEME_ELDY_BORDER_RADIUS'), 0, 0, 0, '', 0, 0, 0, '', 'widthcentpercentminusx maxwidth100');
+ print $form->selectarray('THEME_ELDY_BORDER_RADIUS', $listofborderradius, getDolGlobalInt('THEME_ELDY_BORDER_RADIUS'), 0, 0, 0, '', 0, 0, 0, '', 'miwdith150 widthcentpercentminusx maxwidth200');
} else {
- print $listofborderradius[getDolGlobalInt('THEME_ELDY_BORDER_RADIUS')];
+ print $listofborderradius[getDolGlobalInt('THEME_ELDY_BORDER_RADIUS')]['label'];
}
//print $form->textwithpicto('', $langs->trans("NotSupportedByAllThemes"), 1, 'help', 'inline-block');
}
diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang
index 4b121a2a1f0..7542e7d9915 100644
--- a/htdocs/langs/en_US/admin.lang
+++ b/htdocs/langs/en_US/admin.lang
@@ -293,6 +293,8 @@ Orientation=Orientation
SpaceX=Space X
SpaceY=Space Y
FontSize=Font size
+NoRoundedCorners=No rounder corners
+CornerRadius=Corner radius
Content=Content
ContentForLines=Content to display for each product or service (from variable __LINES__ of Content)
NoticePeriod=Notice period
diff --git a/htdocs/theme/eldy/global.inc.php b/htdocs/theme/eldy/global.inc.php
index acecfd2ef9e..2a1eae09af1 100644
--- a/htdocs/theme/eldy/global.inc.php
+++ b/htdocs/theme/eldy/global.inc.php
@@ -735,6 +735,9 @@ input.pageplusone {
margin-right: 4px;
margin-left: 3px;
width: 32px;
+ border-top: 0;
+ border-left: 0;
+ border-right: 0;
}
.paginationlastpage a {
padding-left: 6px;
diff --git a/htdocs/theme/md/style.css.php b/htdocs/theme/md/style.css.php
index 92b4e1c5297..344c51d99ff 100644
--- a/htdocs/theme/md/style.css.php
+++ b/htdocs/theme/md/style.css.php
@@ -911,6 +911,9 @@ input.pageplusone {
margin-right: 4px;
margin-left: 3px;
width: 32px;
+ border-top: 0;
+ border-left: 0;
+ border-right: 0;
}
.paginationlastpage a {
padding-left: 6px;
From 8041cdee1a56e3f3959ee0ac85a96c4f8b57e3f7 Mon Sep 17 00:00:00 2001
From: John BOTELLA <68917336+thersane-john@users.noreply.github.com>
Date: Fri, 5 Dec 2025 10:27:29 +0100
Subject: [PATCH 57/58] Fix dialog for iframe
---
htdocs/public/webportal/css/btn.css | 56 ++++++++++++
htdocs/public/webportal/css/dialog.css | 90 +++++++++++++++++++
htdocs/public/webportal/css/style.css.php | 2 +
htdocs/public/webportal/tpl/footer.tpl.php | 62 ++++++++-----
htdocs/public/webportal/tpl/header.tpl.php | 16 +++-
.../abstractcard.controller.class.php | 5 +-
6 files changed, 206 insertions(+), 25 deletions(-)
create mode 100644 htdocs/public/webportal/css/btn.css
create mode 100644 htdocs/public/webportal/css/dialog.css
diff --git a/htdocs/public/webportal/css/btn.css b/htdocs/public/webportal/css/btn.css
new file mode 100644
index 00000000000..035d55a143f
--- /dev/null
+++ b/htdocs/public/webportal/css/btn.css
@@ -0,0 +1,56 @@
+
+/*
+ * BUTTON With Low emphasis
+ */
+
+button.btn-low-emphasis{
+ text-align: center;
+ display: inline-block;
+ border: none;
+ outline: none;
+ cursor: pointer;
+ margin: 0;
+ padding: 0;
+ width: auto;
+ min-width: 1.5em;
+ min-height: 1.5em;
+ line-height: 1.5em;
+
+ overflow: visible;
+ background: transparent;
+ background-position: center; /* used for hover ripple effect */
+ background-size: 0%;
+ color: var(--colortextlink, inherit);
+ font: inherit;
+ line-height: normal;
+
+ /* Corrects font smoothing for webkit */
+ -webkit-font-smoothing: inherit;
+ -moz-osx-font-smoothing: inherit;
+
+ /* Corrects inability to style clickable input types in iOS */
+ -webkit-appearance: none;
+
+
+ transition: background 0.8s;/* used for hover ripple effect */
+ background: transparent radial-gradient(circle, transparent 1%, hsla(var(--colortextlink-h),var(--colortextlink-s) ,var(--colortextlink-l) , 0.1) 1%, transparent 10%) center/15000%;
+}
+
+button.btn-low-emphasis.--btn-icon{
+ border-radius: 100%;
+}
+
+button.btn-low-emphasis :is(.fa, .fas){
+ color: var(--colortextlink, inherit);
+ opacity: 0.4;
+}
+
+button.btn-low-emphasis:is(:focus,:hover) :is(.fa, .fas){
+ opacity: 0.8;
+}
+
+button.btn-low-emphasis.--btn-icon:active {
+ background-color: hsla(var(--colortextlink-h),var(--colortextlink-s) ,var(--colortextlink-l) , 0.1);
+ background-size: 100%;
+ transition: background 0s;/* used for hover ripple effect */
+}
diff --git a/htdocs/public/webportal/css/dialog.css b/htdocs/public/webportal/css/dialog.css
new file mode 100644
index 00000000000..1a84db8b600
--- /dev/null
+++ b/htdocs/public/webportal/css/dialog.css
@@ -0,0 +1,90 @@
+/* Dialog container */
+.dialog-full-screen article {
+ width: 80vw;
+ max-width: 95vw;
+ height: 90vh;
+ max-height: 95vh;
+
+ position: fixed;
+ margin: 0 auto;
+
+ display: flex;
+ flex-direction: column;
+ flex-wrap: nowrap;
+ justify-content: normal;
+ align-items: normal;
+ align-content: normal;
+}
+
+.dialog-full-screen article:has(.dialog-header) {
+ padding: 0;
+}
+
+
+.dialog-full-screen article .dialog-body {
+ display: block;
+ flex-grow: 2;
+ flex-shrink: 1;
+ flex-basis: auto;
+ align-self: auto;
+ order: 0;
+}
+
+@media (min-width: 576px) {
+ dialog.dialog-full-screen article {
+ max-width: 95%;
+ }
+}
+@media (min-width: 768px) {
+ dialog.dialog-full-screen article {
+ max-width: 95%;
+ }
+}
+
+/* Iframe */
+.dialog-iframe {
+ border: none;
+ width: 100%;
+ height: 100%;
+}
+
+
+
+/*!* Header *!*/
+.dialog-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0.5em 1em;
+ border-bottom: 1px solid rgba(0,0,0,0.1);
+}
+
+.dialog-header-title-container {
+ flex: 1;
+ overflow: hidden;
+}
+
+.dialog-header-title {
+ margin: 0;
+ text-overflow: ellipsis;
+ overflow: hidden;
+}
+
+.dialog-header-action-container {
+ flex: 0 0 auto;
+ white-space: nowrap;
+ display: flex;
+ flex-direction: row-reverse;
+ gap: 0.5em;
+}
+
+/* Header title */
+.dialog-header h2 {
+ margin: 0;
+ font-size: 1.2em;
+}
+
+/* Close button */
+.dialog-header-btn.dialog-close-btn {
+ font-size: 1.2em;
+}
diff --git a/htdocs/public/webportal/css/style.css.php b/htdocs/public/webportal/css/style.css.php
index 84b52b4c378..1e034f2ae97 100644
--- a/htdocs/public/webportal/css/style.css.php
+++ b/htdocs/public/webportal/css/style.css.php
@@ -99,6 +99,8 @@ if (empty($dolibarr_nocache)) {
@import "badges.css";
@import "title-buttons.css";
@import "nav.css";
+@import "dialog.css";
+@import "btn.css";
/**
This file can overwrite default pico css
*/
diff --git a/htdocs/public/webportal/tpl/footer.tpl.php b/htdocs/public/webportal/tpl/footer.tpl.php
index 75f0a7e1c3d..48634cd4656 100644
--- a/htdocs/public/webportal/tpl/footer.tpl.php
+++ b/htdocs/public/webportal/tpl/footer.tpl.php
@@ -117,44 +117,60 @@ if (empty($conf->browser->layout) || $conf->browser->layout != 'phone') { ?>
jQuery(document).ready(function () {
jQuery(".modal_card").click(function (event) {
console.log("We click on card link for element with href=" + $(this).attr('href'));
- modal_card($(this).attr('href'));
+ const modalTitle = $(this).data('modal-title') || $(this).attr('title') || '';
+ modal_card($(this).attr('href'), modalTitle);
event.preventDefault();
return false;
});
});
/**
- * Function show modal card. It uses the "modal" function.
+ * Show modal card using Pico.css modal system
*
- * @param url Url
- * @param title Dialog box title
- * @return void
- * @see newpopup()
+ * @param {string} url - URL to load in the iframe
+ * @param {string} title - Modal title
*/
- function modal_card(url, title) {
- console.log("modal_card A click was done: url=" + url);
+ function modal_card(url, title = '') {
+ console.log("modal_card called: url=" + url);
- let width = "85%";
- let object_width = '100%';
- let height = ($(window).height() - 60) * 0.90;
- let object_height = '98%';
- let newElem = '';
+ const modal = document.getElementById('modalforcard'); // Pico.css modal container
+ if (!modal) {
+ console.error('Modal container not found');
+ return;
+ }
- $('#modalforcard article').css('width', width).css('height', height).html(newElem);
- let modal = document.getElementById('modalforcard');
+ // Insert structured modal content
+ modal.querySelector('article').innerHTML = `
+
+
+
+
+ `;
+
+ // Add close button handler
+ const closeBtn = modal.querySelector('.dialog-close-btn');
+ if (closeBtn) {
+ closeBtn.addEventListener('click', () => close_modal_card());
+ }
+
+ // Open modal via Pico.css function
openModal(modal);
}
/**
- * Function close modal card. It uses the "modal" function.
- *
- * @return void
+ * Close modal card using Pico.css modal system
*/
function close_modal_card() {
- console.log("close_modal_card A click was done");
+ const modal = document.getElementById('modalforcard');
+ if (!modal) return;
- let modal = document.getElementById('modalforcard');
closeModal(modal);
}
@@ -252,8 +268,8 @@ if (empty($conf->browser->layout) || $conf->browser->layout != 'phone') { ?>
-
-
+
+
|
+ >
diff --git a/htdocs/webportal/controllers/abstractcard.controller.class.php b/htdocs/webportal/controllers/abstractcard.controller.class.php
index 709570f67f9..ccc4f507201 100644
--- a/htdocs/webportal/controllers/abstractcard.controller.class.php
+++ b/htdocs/webportal/controllers/abstractcard.controller.class.php
@@ -47,7 +47,10 @@ abstract class AbstractCardController extends Controller
return;
}
- $this->loadTemplate('header');
+ $this->loadTemplate('header', [
+ 'body-class' => empty($this->formCard->modal) ? '' : '--is-modal'
+ ]);
+
if (empty($this->formCard->modal)) {
$this->loadTemplate('menu');
$this->loadTemplate('hero-header-banner');
From 9cfab852b822b0113249fb2e3b8bef464b44f78f Mon Sep 17 00:00:00 2001
From: John BOTELLA <68917336+thersane-john@users.noreply.github.com>
Date: Fri, 5 Dec 2025 10:47:03 +0100
Subject: [PATCH 58/58] restaure times
---
htdocs/public/webportal/css/dialog.css | 5 +++--
htdocs/public/webportal/tpl/footer.tpl.php | 3 ++-
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/htdocs/public/webportal/css/dialog.css b/htdocs/public/webportal/css/dialog.css
index 1a84db8b600..50e42c99e82 100644
--- a/htdocs/public/webportal/css/dialog.css
+++ b/htdocs/public/webportal/css/dialog.css
@@ -85,6 +85,7 @@
}
/* Close button */
-.dialog-header-btn.dialog-close-btn {
- font-size: 1.2em;
+dialog article .dialog-header-btn.close {
+ margin-top: 0;
+ margin-bottom: 0;
}
diff --git a/htdocs/public/webportal/tpl/footer.tpl.php b/htdocs/public/webportal/tpl/footer.tpl.php
index 48634cd4656..f4358235dfc 100644
--- a/htdocs/public/webportal/tpl/footer.tpl.php
+++ b/htdocs/public/webportal/tpl/footer.tpl.php
@@ -146,7 +146,7 @@ if (empty($conf->browser->layout) || $conf->browser->layout != 'phone') { ?>
@@ -231,6 +231,7 @@ if (empty($conf->browser->layout) || $conf->browser->layout != 'phone') { ?>
}
function show_preview(mode) {
+ // TODO : rebuild dialog tpl show modal_card
let newElem = '
';
if (mode == 'image' && showOriginalSizeButton) {