From c27146f44c42bc242b20617b4b0fd5f5f7695674 Mon Sep 17 00:00:00 2001 From: atm-florian Date: Tue, 2 Dec 2025 14:42:15 +0100 Subject: [PATCH 1/7] 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 2/7] 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 c5b0c1c318f7155166bc7976c8f511d0d477e0c6 Mon Sep 17 00:00:00 2001 From: atm-florian Date: Wed, 3 Dec 2025 15:13:39 +0100 Subject: [PATCH 3/7] 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 4/7] 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 5/7] 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 6/7] 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 7/7] 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() {