From 39d7ffae8dfcc988ccc3ca7db9946dd2eb05d84d Mon Sep 17 00:00:00 2001 From: ldestailleur Date: Mon, 6 Oct 2025 14:05:01 +0200 Subject: [PATCH] FIX #33741 FIX #35632 --- .../stock/class/mouvementstock.class.php | 44 ++++++++++------- htdocs/product/stock/movement_list.php | 49 +++++++++++-------- 2 files changed, 55 insertions(+), 38 deletions(-) diff --git a/htdocs/product/stock/class/mouvementstock.class.php b/htdocs/product/stock/class/mouvementstock.class.php index e7b0755c3cd..821ea3b780f 100644 --- a/htdocs/product/stock/class/mouvementstock.class.php +++ b/htdocs/product/stock/class/mouvementstock.class.php @@ -213,8 +213,8 @@ class MouvementStock extends CommonObject * @param int|'' $sellby sell-by date. Will be used if lot does not exists yet and will be created. * @param string $batch batch number * @param bool $skip_batch If set to true, stock movement is done without impacting batch record - * @param int $id_product_batch Id product_batch (when skip_batch is false and we already know which record of product_batch to use) - * @param int<0,1> $disablestockchangeforsubproduct Disable stock change for sub-products of kit (useful only if product is a subproduct) + * @param int $id_product_batch Id product_batch (when skip_batch is false and we already know which record of product_batch table to use) + * @param int<0,1> $disablestockchangeforsubproduct Disable stock change for sub-products of kit (useful only if product is a kit) * @param int<0,1> $donotcleanemptylines Do not clean lines in stock table with qty=0 (because we want to have this done by the caller) * @param bool $force_update_batch Allows to add batch stock movement even if $product doesn't use batch anymore * @return int Return integer <0 if KO, 0 if fk_product is null or product id does not exists, >0 if OK @@ -966,7 +966,6 @@ class MouvementStock extends CommonObject $result = -1; } - $fk_product_stock = 0; if ($result >= 0) { // No error if ($pdluo->id > 0) { // product_batch record found @@ -1170,7 +1169,7 @@ class MouvementStock extends CommonObject */ public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $maxlen = 24, $morecss = '') { - global $langs, $conf, $db; + global $langs; $result = ''; @@ -1263,7 +1262,7 @@ class MouvementStock extends CommonObject */ public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0) { - global $conf, $user, $langs; + global $langs; $langs->load("stocks"); $outputlangs->load("products"); @@ -1330,27 +1329,38 @@ class MouvementStock extends CommonObject } /** - * reverse movement for object by updating infos + * Reverse movement for object by updating infos + * * @return int 1 if OK,-1 if KO */ - public function reverseMouvement() + public function reverseMovement() { - $formattedDate = "REVERTMV" .dol_print_date($this->datem, '%Y%m%d%His'); - if ($this->label == 'Annulation movement ID'.$this->id) { - return -1; - } + global $user; + + $formattedDate = "REVERT-" .($this->inventorycode ? $this->inventorycode : dol_print_date($this->datem, '%Y%m%d%His')); if ($this->inventorycode == $formattedDate) { return -1; } - $sql = "UPDATE ".$this->db->prefix()."stock_mouvement SET"; - $sql .= " label = 'Annulation movement ID ".((int) $this->id)."',"; - $sql .= "inventorycode = '".($formattedDate)."'"; - $sql .= " WHERE rowid = ".((int) $this->id); + $newlabel = 'Revert '.$this->label; + // type is 0=input (stock increase by a stock transfer), 1=output (stock decrease by a stock transfer), 2=output (stock decrease), 3=input (stock increase) + // Note that qty should be > 0 with 0 or 3, < 0 with 1 or 2. + if ($this->type == 0) { + $newtype = 1; + } elseif ($this->type == 1) { + $newtype = 0; + } elseif ($this->type == 2) { + $newtype = 3; + } elseif ($this->type == 3) { + $newtype = 2; + } + $newqty = - $this->qty; - $resql = $this->db->query($sql); + $this->db->begin(); - if ($resql) { + $result = $this->_create($user, $this->product_id, $this->warehouse_id, $newqty, $newtype, 0, $newlabel, $formattedDate, '', 0, 0, $this->batch); + + if ($result > 0) { $this->db->commit(); return 1; } else { diff --git a/htdocs/product/stock/movement_list.php b/htdocs/product/stock/movement_list.php index 5078482b153..dbedf216fdb 100644 --- a/htdocs/product/stock/movement_list.php +++ b/htdocs/product/stock/movement_list.php @@ -619,41 +619,48 @@ if ($action == "transfert_stock" && $permissiontoadd && !$cancel) { if ($action == 'confirm_reverse' && $confirm == "yes" && $permissiontoadd) { $toselect = array_map('intval', $toselect); + $db->begin(); + $sql = "SELECT rowid, label, inventorycode, datem"; $sql .= " FROM ".MAIN_DB_PREFIX."stock_mouvement"; - $sql .= " WHERE rowid IN ("; - foreach ($toselect as $id) { - $sql .= ((int) $id).","; - } - $sql = rtrim($sql, ','); - $sql .= ")"; + $sql .= " WHERE rowid IN (".$db->sanitize(implode(',', $toselect)).")"; $resql = $db->query($sql); if ($resql) { $num = $db->num_rows($resql); $i = 0; - $hasSuccess = false; - $hasError = false; + $error =0; while ($i < $num) { $obj = $db->fetch_object($resql); - $object->fetch($obj->rowid); - $reverse = $object->reverseMouvement(); + + $object->id = 0; + $object->fetch($obj->rowid); // $object is MouvementStock + + // TODO Add a protection to disallow reversion if type of movement is not the same value for all selected lines + + // Create the reverse movement + $reverse = $object->reverseMovement(); if ($reverse < 0) { - $hasError = true; - } else { - $hasSuccess = true; + setEventMessages($object->error, $object->errors, 'errors'); + $error++; + break; } $i++; } - if ($hasError) { - setEventMessages($langs->trans("WarningAlreadyReverse", $langs->transnoentities($idAlreadyReverse)), null, 'warnings'); - } - if ($hasSuccess) { - setEventMessages($langs->trans("ReverseConfirmed"), null); - } - header("Location: ".$_SERVER["PHP_SELF"]); - exit; + } else { + setEventMessages($db->lasterror(), null, 'errors'); + $error++; } + + if (!$error) { + setEventMessages($langs->trans("ReverseConfirmed"), null); + $db->commit(); + } else { + $db->rollback(); + } + + header("Location: ".$_SERVER["PHP_SELF"]); + exit; } /*