FIX Stock balance truncated to int for fractional qty (#38806)

calculateBalanceForProductBefore() cast the SUM of stock movement values
to (int), which truncated the fractional part. For products tracked in
fractional units (kg, liters, ...), the before/after period stock balance
displayed in the stock movement list (product/stock/movement_list.php) was
wrong (for example a real balance of 10.5 was shown as 10).

Cast the computed balance to (float) instead and update the method return
type accordingly.

Add a PHPUnit test (MouvementStockTest::testCalculateBalanceForProductBefore)
that records a fractional reception and checks the returned balance is not
truncated.
This commit is contained in:
Tanguy Chénier 2026-06-15 14:18:44 +02:00 committed by GitHub
parent acfef04a06
commit 17076a1e47
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 42 additions and 2 deletions

View file

@ -900,7 +900,7 @@ class MouvementStock extends CommonObject
*
* @param int $productidselected Id of product to count
* @param integer $datebefore Date limit
* @return int Number
* @return float Number
*/
public function calculateBalanceForProductBefore($productidselected, $datebefore)
{
@ -915,7 +915,7 @@ class MouvementStock extends CommonObject
if ($resql) {
$obj = $this->db->fetch_object($resql);
if ($obj) {
$nb = (int) $obj->nb;
$nb = (float) $obj->nb;
}
return (empty($nb) ? 0 : $nb);
} else {

View file

@ -48,6 +48,10 @@ $conf->global->MAIN_DISABLE_ALL_MAILS = 1;
* @backupGlobals disabled
* @backupStaticAttributes enabled
* @remarks backupGlobals must be disabled to have db,conf,user and lang not erased.
* @phan-file-suppress PhanUndeclaredClass
* @phan-file-suppress PhanUndeclaredExtendedClass
* @phan-file-suppress PhanUndeclaredMethod
* @phan-file-suppress PhanUndeclaredProperty
*/
class MouvementStockTest extends CommonClassTest
{
@ -239,4 +243,40 @@ class MouvementStockTest extends CommonClassTest
return $localobject;
}
/**
* testCalculateBalanceForProductBefore
*
* @return void
*/
public function testCalculateBalanceForProductBefore()
{
global $conf,$user,$langs,$db;
$conf = $this->savconf;
$user = $this->savuser;
$langs = $this->savlangs;
$db = $this->savdb;
$product = new Product($db);
$product->initAsSpecimen();
$product->ref .= ' phpunit balance';
$product->label .= ' phpunit balance';
$productid = $product->create($user);
$this->assertGreaterThan(0, $productid, 'Failed to create product');
$warehouse = new Entrepot($db);
$warehouse->initAsSpecimen();
$warehouse->label .= ' phpunit balance';
$warehouseid = $warehouse->create($user);
$this->assertGreaterThan(0, $warehouseid, 'Failed to create warehouse');
$movement = new MouvementStock($db);
$movementdate = dol_mktime(0, 0, 0, 1, 1, 2020);
$result = $movement->reception($user, $productid, $warehouseid, 2.5, 0, 'Fractional qty for unit test', '', '', '', $movementdate, 0, 'Inventory Code Balance');
$this->assertGreaterThan(0, $result, 'Failed to create fractional stock movement');
$balance = $movement->calculateBalanceForProductBefore($productid, dol_now());
print __METHOD__." balance=".$balance."\n";
$this->assertEquals(2.5, $balance, 'Fractional stock balance must not be truncated to an integer');
}
}