From 3abb3f50185accabd1e9351522abc3b4ee6a5a2d Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 12 Dec 2021 00:05:31 +0100 Subject: [PATCH] Debug inventory feature --- .../install/mysql/migration/14.0.0-15.0.0.sql | 3 + htdocs/install/mysql/tables/llx_inventory.sql | 2 +- .../install/mysql/tables/llx_inventorydet.sql | 9 +- htdocs/product/inventory/card.php | 2 + htdocs/product/inventory/inventory.php | 288 ++++++++++-------- 5 files changed, 172 insertions(+), 132 deletions(-) diff --git a/htdocs/install/mysql/migration/14.0.0-15.0.0.sql b/htdocs/install/mysql/migration/14.0.0-15.0.0.sql index 5ec019cf253..263e94e4575 100644 --- a/htdocs/install/mysql/migration/14.0.0-15.0.0.sql +++ b/htdocs/install/mysql/migration/14.0.0-15.0.0.sql @@ -476,3 +476,6 @@ ALTER TABLE llx_propal ADD COLUMN online_sign_name varchar(64); ALTER TABLE llx_entrepot ADD COLUMN warehouse_usage integer DEFAULT 1; ALTER TABLE llx_session MODIFY COLUMN user_agent VARCHAR(255) NULL; + +ALTER TABLE llx_inventorydet ADD COLUMN fk_movement integer NULL; + diff --git a/htdocs/install/mysql/tables/llx_inventory.sql b/htdocs/install/mysql/tables/llx_inventory.sql index aa35ebb0c42..c25ccb9767b 100644 --- a/htdocs/install/mysql/tables/llx_inventory.sql +++ b/htdocs/install/mysql/tables/llx_inventory.sql @@ -21,7 +21,7 @@ CREATE TABLE llx_inventory ( rowid integer NOT NULL AUTO_INCREMENT PRIMARY KEY, entity integer DEFAULT 0, - ref varchar(48), + ref varchar(48), -- We will also use this code as inventory code date_creation datetime DEFAULT NULL, tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, fk_user_creat integer, -- user making creation diff --git a/htdocs/install/mysql/tables/llx_inventorydet.sql b/htdocs/install/mysql/tables/llx_inventorydet.sql index ce23fe0f749..8b75f68460d 100644 --- a/htdocs/install/mysql/tables/llx_inventorydet.sql +++ b/htdocs/install/mysql/tables/llx_inventorydet.sql @@ -25,9 +25,10 @@ CREATE TABLE llx_inventorydet fk_inventory integer DEFAULT 0, fk_warehouse integer DEFAULT 0, fk_product integer DEFAULT 0, - batch varchar(128) DEFAULT NULL, -- Lot or serial number - qty_stock double DEFAULT NULL, -- The targeted value. can be filled during draft edition - qty_view double DEFAULT NULL, -- must be filled once regulation is done - qty_regulated double DEFAULT NULL -- must be filled once regulation is done + batch varchar(128) DEFAULT NULL, -- Lot or serial number + qty_stock double DEFAULT NULL, -- Value or real stock we have, when we start the inventory (may be updated during intermediary steps). + qty_view double DEFAULT NULL, -- Quantity found during inventory. It is the targeted value, filled during edition of inventory. + qty_regulated double DEFAULT NULL, -- Never used. Deprecated because we already have the fk_movement now. + fk_movement integer NULL -- can contain the id of stock movement we recorded to make the inventory regulation of this line ) ENGINE=innodb; diff --git a/htdocs/product/inventory/card.php b/htdocs/product/inventory/card.php index 1f036e1fa3c..49ce49958d5 100644 --- a/htdocs/product/inventory/card.php +++ b/htdocs/product/inventory/card.php @@ -207,6 +207,8 @@ if ($action == 'create') { // Other attributes include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_add.tpl.php'; + //print ''.$langs->trans("InventoryCode").'INV'.$object->id.''; + print ''."\n"; print dol_get_fiche_end(); diff --git a/htdocs/product/inventory/inventory.php b/htdocs/product/inventory/inventory.php index 5346e37aec4..c6abe95bc80 100644 --- a/htdocs/product/inventory/inventory.php +++ b/htdocs/product/inventory/inventory.php @@ -103,122 +103,6 @@ if ($cancel) { $action = ''; } -if ($action == 'cancel_record' && $permissiontoadd) { - $object->setCanceled($user); -} - -if ($action == 'update' && !empty($user->rights->stock->mouvement->creer)) { - $stockmovment = new MouvementStock($db); - $stockmovment->setOrigin($object->element, $object->id); - - $db->begin(); - - $sql = 'SELECT id.rowid, id.datec as date_creation, id.tms as date_modification, id.fk_inventory, id.fk_warehouse,'; - $sql .= ' id.fk_product, id.batch, id.qty_stock, id.qty_view, id.qty_regulated'; - $sql .= ' FROM '.MAIN_DB_PREFIX.'inventorydet as id'; - $sql .= ' WHERE id.fk_inventory = '.((int) $object->id); - - $resql = $db->query($sql); - if ($resql) { - $num = $db->num_rows($resql); - $i = 0; - $totalarray = array(); - while ($i < $num) { - $line = $db->fetch_object($resql); - $qty_stock = $line->qty_stock; - $qty_view = $line->qty_view; // The quantity viewed by inventorier, the qty we target - - if (!is_null($qty_view)) { - $stock_movement_qty = price2num($qty_view - $qty_stock, 'MS'); - if ($stock_movement_qty != 0) { - if ($stock_movement_qty < 0) { - $movement_type = 1; - } else { - $movement_type = 0; - } - - $datemovement = ''; - - $idstockmove = $stockmovment->_create($user, $line->fk_product, $line->fk_warehouse, $stock_movement_qty, $movement_type, 0, $langs->trans('LabelOfInventoryMovemement', $object->id), 'INV'.$object->id, $datemovement, '', '', $line->batch); - if ($idstockmove < 0) { - $error++; - setEventMessages($stockmovment->error, $stockmovment->errors, 'errors'); - break; - } - } - } - $i++; - } - - if (!$error) { - $object->setRecorded($user); - } - } else { - setEventMessages($db->lasterror, null, 'errors'); - $error++; - } - - if (! $error) { - $db->commit(); - } else { - $db->rollback(); - } -} - -if ($action =='updateinventorylines' && $permissiontoadd) { - $sql = 'SELECT id.rowid, id.datec as date_creation, id.tms as date_modification, id.fk_inventory, id.fk_warehouse,'; - $sql .= ' id.fk_product, id.batch, id.qty_stock, id.qty_view, id.qty_regulated'; - $sql .= ' FROM '.MAIN_DB_PREFIX.'inventorydet as id'; - $sql .= ' WHERE id.fk_inventory = '.((int) $object->id); - - $db->begin(); - - $resql = $db->query($sql); - if ($resql) { - $num = $db->num_rows($resql); - $i = 0; - $totalarray = array(); - $inventoryline = new InventoryLine($db); - - while ($i < $num) { - $line = $db->fetch_object($resql); - $lineid = $line->rowid; - - if (GETPOST("id_".$lineid, 'alpha') != '') { // If a value was set ('0' or something else) - $qtytoupdate = price2num(GETPOST("id_".$lineid, 'alpha'), 'MS'); - $result = $inventoryline->fetch($lineid); - if ($qtytoupdate < 0) { - $result = -1; - setEventMessages($langs->trans("FieldCannotBeNegative", $langs->transnoentitiesnoconv("RealQty")), null, 'errors'); - } - if ($result > 0) { - $inventoryline->qty_view = $qtytoupdate; - $resultupdate = $inventoryline->update($user); - } - } else { - // Delete record - $result = $inventoryline->fetch($lineid); - if ($result > 0) { - $inventoryline->qty_view = null; - $resultupdate = $inventoryline->update($user); - } - } - - if ($result < 0 || $resultupdate < 0) { - $error++; - } - - $i++; - } - } - - if (!$error) { - $db->commit(); - } else { - $db->rollback(); - } -} - $parameters = array(); $reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks if ($reshook < 0) { @@ -228,6 +112,138 @@ if ($reshook < 0) { if (empty($reshook)) { $error = 0; + if ($action == 'cancel_record' && $permissiontoadd) { + $object->setCanceled($user); + } + + // Close inventory by recording the stock movements + if ($action == 'update' && !empty($user->rights->stock->mouvement->creer)) { + $stockmovment = new MouvementStock($db); + $stockmovment->setOrigin($object->element, $object->id); + + $db->begin(); + + $sql = 'SELECT id.rowid, id.datec as date_creation, id.tms as date_modification, id.fk_inventory, id.fk_warehouse,'; + $sql .= ' id.fk_product, id.batch, id.qty_stock, id.qty_view, id.qty_regulated'; + $sql .= ' FROM '.MAIN_DB_PREFIX.'inventorydet as id'; + $sql .= ' WHERE id.fk_inventory = '.((int) $object->id); + + $resql = $db->query($sql); + if ($resql) { + $num = $db->num_rows($resql); + $i = 0; + $totalarray = array(); + while ($i < $num) { + $line = $db->fetch_object($resql); + + $qty_stock = $line->qty_stock; + $qty_view = $line->qty_view; // The quantity viewed by inventorier, the qty we target + + if (!is_null($qty_view)) { + $stock_movement_qty = price2num($qty_view - $qty_stock, 'MS'); + if ($stock_movement_qty != 0) { + if ($stock_movement_qty < 0) { + $movement_type = 1; + } else { + $movement_type = 0; + } + + $datemovement = ''; + //$inventorycode = 'INV'.$object->id; + $inventorycode = 'INV-'.$object->ref; + + $idstockmove = $stockmovment->_create($user, $line->fk_product, $line->fk_warehouse, $stock_movement_qty, $movement_type, 0, $langs->trans('LabelOfInventoryMovemement', $object->id), $inventorycode, $datemovement, '', '', $line->batch); + if ($idstockmove < 0) { + $error++; + setEventMessages($stockmovment->error, $stockmovment->errors, 'errors'); + break; + } + + // Update line with id of stock movement + $sqlupdate = 'UPDATE '.MAIN_DB_PREFIX.'inventorydet SET fk_movement = '.((int) $idstockmove).' WHERE rowid = '.$line->rowid; + $resqlupdate = $db->query($sqlupdate); + if (! $resqlupdate) { + $error++; + setEventMessages($db->lasterror(), null, 'errors'); + break; + } + } + } + $i++; + } + + if (!$error) { + $object->setRecorded($user); + } + } else { + setEventMessages($db->lasterror, null, 'errors'); + $error++; + } + + if (! $error) { + $db->commit(); + } else { + $db->rollback(); + } + } + + // Save quantity found during inventory + if ($action =='updateinventorylines' && $permissiontoadd) { + $sql = 'SELECT id.rowid, id.datec as date_creation, id.tms as date_modification, id.fk_inventory, id.fk_warehouse,'; + $sql .= ' id.fk_product, id.batch, id.qty_stock, id.qty_view, id.qty_regulated'; + $sql .= ' FROM '.MAIN_DB_PREFIX.'inventorydet as id'; + $sql .= ' WHERE id.fk_inventory = '.((int) $object->id); + + $db->begin(); + + $resql = $db->query($sql); + if ($resql) { + $num = $db->num_rows($resql); + $i = 0; + $totalarray = array(); + $inventoryline = new InventoryLine($db); + + while ($i < $num) { + $line = $db->fetch_object($resql); + $lineid = $line->rowid; + + if (GETPOST("id_".$lineid, 'alpha') != '') { // If a value was set ('0' or something else) + $qtytoupdate = price2num(GETPOST("id_".$lineid, 'alpha'), 'MS'); + $result = $inventoryline->fetch($lineid); + if ($qtytoupdate < 0) { + $result = -1; + setEventMessages($langs->trans("FieldCannotBeNegative", $langs->transnoentitiesnoconv("RealQty")), null, 'errors'); + } + if ($result > 0) { + $inventoryline->qty_stock = price2num(GETPOST('stock_qty_'.$lineid, 'alpha'), 'MS'); // The new value we want + $inventoryline->qty_view = $qtytoupdate; // The new value we want + $resultupdate = $inventoryline->update($user); + } + } else { + // Delete record + $result = $inventoryline->fetch($lineid); + if ($result > 0) { + $inventoryline->qty_view = null; // The new value we want + $resultupdate = $inventoryline->update($user); + } + } + + if ($result < 0 || $resultupdate < 0) { + $error++; + } + + $i++; + } + } + + if (!$error) { + $db->commit(); + } else { + $db->rollback(); + } + } + + $backurlforlist = DOL_URL_ROOT.'/product/inventory/list.php'; $backtopage = DOL_URL_ROOT.'/product/inventory/inventory.php?id='.$object->id; @@ -443,6 +459,8 @@ if ($object->id > 0) { // Other attributes. Fields from hook formObjectOptions and Extrafields. include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_view.tpl.php'; + //print ''.$langs->trans("InventoryCode").'INV'.$object->id.''; + print ''; print ''; print ''; @@ -493,6 +511,10 @@ if ($object->id > 0) { } } print ''."\n"; + + if ($object->status != Inventory::STATUS_DRAFT && $object->status != Inventory::STATUS_VALIDATED) { + print '

'; + } } @@ -743,15 +765,15 @@ if ($object->id > 0) { print ''; print $form->textwithpicto($langs->trans("RealQty"), $langs->trans("InventoryRealQtyHelp")); print ''; - if ($object->status == $object::STATUS_VALIDATED) { + if ($object->status == $object::STATUS_DRAFT || $object->status == $object::STATUS_VALIDATED) { // Actions print ''; print ''; - print ''; } + print ''; // Line to add a new line in inventory - if ($object->status == $object::STATUS_VALIDATED) { + if ($object->status == $object::STATUS_DRAFT || $object->status == $object::STATUS_VALIDATED) { print ''; print ''; print $formproduct->selectWarehouses((GETPOSTISSET('fk_warehouse') ? GETPOST('fk_warehouse', 'int') : $object->fk_warehouse), 'fk_warehouse', 'warehouseopen', 1, 0, 0, '', 0, 0, array(), 'maxwidth300'); @@ -810,7 +832,7 @@ if ($object->id > 0) { $product_static = new Product($db); $result = $product_static->fetch($obj->fk_product, '', '', '', 1, 1, 1); - $option = 'nobatch'; + //$option = 'nobatch'; $option .= ',novirtual'; $product_static->load_stock($option); // Load stock_reel + stock_warehouse. This can also call load_virtual_stock() @@ -831,14 +853,24 @@ if ($object->id > 0) { print ''; } - // Expected quantity - print ''; - print $obj->qty_stock; + // Expected quantity = Quantity in stock when we start inventory + print ''; + if ($object->status == $object::STATUS_DRAFT || $object->status == $object::STATUS_VALIDATED) { + if ($conf->productbatch->enabled && $product_static->hasbatch()) { + $valuetoshow = $product_static->stock_warehouse[$obj->fk_warehouse]->detail_batch[$obj->batch]->qty; + } else { + $valuetoshow = $product_static->stock_warehouse[$obj->fk_warehouse]->real; + } + } else { + $valuetoshow = $obj->qty_stock; + } + print price2num($valuetoshow, 'MS'); + print ''; print ''; // Real quantity - print ''; - if ($object->status == $object::STATUS_VALIDATED) { + if ($object->status == $object::STATUS_DRAFT || $object->status == $object::STATUS_VALIDATED) { + print ''; $qty_view = GETPOST("id_".$obj->rowid) && price2num(GETPOST("id_".$obj->rowid), 'MS') >= 0 ? GETPOST("id_".$obj->rowid) : $obj->qty_view; //if (!$hasinput && $qty_view !== null && $obj->qty_stock != $qty_view) { @@ -852,13 +884,15 @@ if ($object->id > 0) { print ''; print ''; + // Picto delete line print ''; print ''.img_delete().''; - print ''; $qty_tmp = price2num(GETPOST("id_".$obj->rowid."_input_tmp", 'MS')) >= 0 ? GETPOST("id_".$obj->rowid."_input_tmp") : $qty_view; print ''; + print ''; } else { - print $obj->qty_view; + print ''; + print $obj->qty_view; // qty found print ''; } print '';