From 1f02b3319589aa8ecf84b445f23473107d6e0470 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 5 Aug 2020 12:25:26 +0200 Subject: [PATCH] Work on inventory --- htdocs/core/actions_addupdatedelete.inc.php | 24 ++-- htdocs/core/class/commonobjectline.class.php | 10 ++ htdocs/langs/en_US/main.lang | 1 + htdocs/langs/en_US/stocks.lang | 3 +- htdocs/product/inventory/card.php | 47 +++---- .../inventory/class/inventory.class.php | 126 +++++++++++++++++- htdocs/product/inventory/inventory.php | 66 +++++---- 7 files changed, 212 insertions(+), 65 deletions(-) diff --git a/htdocs/core/actions_addupdatedelete.inc.php b/htdocs/core/actions_addupdatedelete.inc.php index 82a76cf8718..1114e33f6f6 100644 --- a/htdocs/core/actions_addupdatedelete.inc.php +++ b/htdocs/core/actions_addupdatedelete.inc.php @@ -285,18 +285,20 @@ if ($action == 'confirm_validate' && $confirm == 'yes' && $permissiontoadd) // Define output language if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE)) { - $outputlangs = $langs; - $newlang = ''; - if ($conf->global->MAIN_MULTILANGS && empty($newlang) && GETPOST('lang_id', 'aZ09')) $newlang = GETPOST('lang_id', 'aZ09'); - if ($conf->global->MAIN_MULTILANGS && empty($newlang)) $newlang = $object->thirdparty->default_lang; - if (!empty($newlang)) { - $outputlangs = new Translate("", $conf); - $outputlangs->setDefaultLang($newlang); - } - $model = $object->modelpdf; - $ret = $object->fetch($id); // Reload to get new records + if (method_exists($object, 'generateDocument')) { + $outputlangs = $langs; + $newlang = ''; + if ($conf->global->MAIN_MULTILANGS && empty($newlang) && GETPOST('lang_id', 'aZ09')) $newlang = GETPOST('lang_id', 'aZ09'); + if ($conf->global->MAIN_MULTILANGS && empty($newlang)) $newlang = $object->thirdparty->default_lang; + if (!empty($newlang)) { + $outputlangs = new Translate("", $conf); + $outputlangs->setDefaultLang($newlang); + } + $model = $object->modelpdf; + $ret = $object->fetch($id); // Reload to get new records - $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref); + $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref); + } } } else { setEventMessages($object->error, $object->errors, 'errors'); diff --git a/htdocs/core/class/commonobjectline.class.php b/htdocs/core/class/commonobjectline.class.php index 59b0acee232..148c9baa67e 100644 --- a/htdocs/core/class/commonobjectline.class.php +++ b/htdocs/core/class/commonobjectline.class.php @@ -50,6 +50,16 @@ abstract class CommonObjectLine extends CommonObject public $fk_unit; + /** + * Constructor + * + * @param DoliDB $db Database handler + */ + public function __construct($db) + { + $this->db = $db; + } + /** * Returns the translation key from units dictionary. * A langs->trans() must be called on result to get translated value. diff --git a/htdocs/langs/en_US/main.lang b/htdocs/langs/en_US/main.lang index d52478dbbdb..de8288b23ea 100644 --- a/htdocs/langs/en_US/main.lang +++ b/htdocs/langs/en_US/main.lang @@ -688,6 +688,7 @@ Method=Method Receive=Receive CompleteOrNoMoreReceptionExpected=Complete or nothing more expected ExpectedValue=Expected Value +ExpectedQty=Expected Qty PartialWoman=Partial TotalWoman=Total NeverReceived=Never received diff --git a/htdocs/langs/en_US/stocks.lang b/htdocs/langs/en_US/stocks.lang index 994eae20e04..4098024f651 100644 --- a/htdocs/langs/en_US/stocks.lang +++ b/htdocs/langs/en_US/stocks.lang @@ -236,4 +236,5 @@ ForceTo=Force to AlwaysShowFullArbo=Display full tree of warehouse on popup of warehouse links (Warning: This may decrease dramatically performances) StockAtDatePastDesc=You can view here the stock (real stock) at a given date in the past StockAtDateFutureDesc=You can view here the stock (virtual stock) at a given date in future -CurrentStock=Current stock \ No newline at end of file +CurrentStock=Current stock +InventoryRealQtyHelp=Set value to 0 to reset qty
Keep field empty, or remove line, to keep unchanged \ No newline at end of file diff --git a/htdocs/product/inventory/card.php b/htdocs/product/inventory/card.php index 00401fa590b..6a2406d74b5 100644 --- a/htdocs/product/inventory/card.php +++ b/htdocs/product/inventory/card.php @@ -115,7 +115,7 @@ if (empty($reshook)) } } $triggermodname = 'STOCK_INVENTORY_MODIFY'; // Name of trigger action code to execute when we modify record - + // Actions cancel, add, update, update_extras, confirm_validate, confirm_delete, confirm_deleteline, confirm_clone, confirm_close, confirm_setdraft, confirm_reopen include DOL_DOCUMENT_ROOT.'/core/actions_addupdatedelete.inc.php'; @@ -127,10 +127,10 @@ if (empty($reshook)) // Action to move up and down lines of object //include DOL_DOCUMENT_ROOT.'/core/actions_lineupdown.inc.php'; - + // Action to build doc include DOL_DOCUMENT_ROOT.'/core/actions_builddoc.inc.php'; - + /*if ($action == 'set_thirdparty' && $permissiontoadd) { $object->setValueFrom('fk_soc', GETPOST('fk_soc', 'int'), '', '', 'date', '', $user, 'MYOBJECT_MODIFY'); @@ -139,7 +139,7 @@ if (empty($reshook)) { $object->setProject(GETPOST('projectid', 'int')); } - + // Actions to send emails $triggersendname = 'INVENTORY_SENTBYMAIL'; $autocopy='MAIN_MAIL_AUTOCOPY_INVENTORY_TO'; @@ -188,12 +188,12 @@ if ($action == 'create') print ''; if ($backtopage) print ''; if ($backtopageforcancel) print ''; - + dol_fiche_head(array(), ''); // Set some default values //if (! GETPOSTISSET('fieldname')) $_POST['fieldname'] = 'myvalue'; - + print ''."\n"; // Common attributes @@ -228,7 +228,7 @@ if (($id || $ref) && $action == 'edit') print ''; if ($backtopage) print ''; if ($backtopageforcancel) print ''; - + dol_fiche_head(); print '
'."\n"; @@ -292,7 +292,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea */ $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('XXX'), $text, 'confirm_xxx', $formquestion, 0, 1, 220); } - + // Call Hook formConfirm $parameters = array('formConfirm' => $formconfirm, 'lineid' => $lineid); $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook @@ -395,36 +395,33 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea print ''.$langs->trans("SetToDraft").''; } } - + // Modify - if ($permissiontoadd) + if ($object->status == $object::STATUS_DRAFT) { - print ''.$langs->trans("Modify").''."\n"; - } else { - print ''.$langs->trans('Modify').''."\n"; + if ($permissiontoadd) + { + print ''.$langs->trans("Modify").''."\n"; + } else { + print ''.$langs->trans('Modify').''."\n"; + } } - + // Validate if ($object->status == $object::STATUS_DRAFT) { if ($permissiontoadd) { - if (empty($object->table_element_line) || (is_array($object->lines) && count($object->lines) > 0)) - { - print ''.$langs->trans("Validate").''; - } else { - $langs->load("errors"); - print ''.$langs->trans("Validate").''; - } + print ''.$langs->trans("Validate").' ('.$langs->trans("Start").')'; } } - + // Clone /*if ($permissiontoadd) { print ''.$langs->trans("ToClone").''."\n"; }*/ - + // Delete (need delete permission, or if draft, just need create/modify permission) if ($permissiontodelete || ($object->status == $object::STATUS_DRAFT && $permissiontoadd)) { @@ -457,7 +454,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea $delallowed = $user->rights->mymodule->myobject->write; // If you can create/edit, you can remove a file on card print $formfile->showdocuments('mymodule:MyObject', $object->element.'/'.$objref, $filedir, $urlsource, $genallowed, $delallowed, $object->model_pdf, 1, 0, 0, 28, 0, '', '', '', $langs->defaultlang); } - + // Show links to link elements $linktoelem = $form->showLinkToObjectBlock($object, null, array('inventory')); $somethingshown = $form->showLinkedObjectBlock($object, $linktoelem); @@ -482,7 +479,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea //Select mail models is same action as presend if (GETPOST('modelselected')) $action = 'presend'; - + // Presend form $modelmail='inventory'; $defaulttopic='InformationMessage'; diff --git a/htdocs/product/inventory/class/inventory.class.php b/htdocs/product/inventory/class/inventory.class.php index 983a7a4acb9..eedafd5497d 100644 --- a/htdocs/product/inventory/class/inventory.class.php +++ b/htdocs/product/inventory/class/inventory.class.php @@ -228,7 +228,101 @@ class Inventory extends CommonObject */ public function create(User $user, $notrigger = false) { - return $this->createCommon($user, $notrigger); + $result = $this->createCommon($user, $notrigger); + + return $result; + } + + /** + * Validate inventory (start it) + * + * @param User $user User that creates + * @param bool $notrigger false=launch triggers after, true=disable triggers + * @return int <0 if KO, Id of created object if OK + */ + public function validate(User $user, $notrigger = false) + { + $this->db->begin(); + + $result = 0; + + if ($result >= 0) { + // Scan existing stock to prefill the inventory + $sql = 'SELECT ps.rowid, ps.fk_entrepot as fk_warehouse, ps.fk_product, ps.reel,'; + $sql .= ' pb.batch, pb.qty'; + $sql .= ' FROM '.MAIN_DB_PREFIX.'product_stock as ps'; + $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_batch as pb ON pb.fk_product_stock = ps.rowid,'; + $sql .= ' '.MAIN_DB_PREFIX.'product as p, '.MAIN_DB_PREFIX.'entrepot as e'; + $sql .= ' WHERE p.entity IN ('.getEntity('product').')'; + $sql .= ' AND ps.fk_product = p.rowid AND ps.fk_entrepot = e.rowid'; + if (empty($conf->global->STOCK_SUPPORTS_SERVICES)) $sql .= " AND p.fk_product_type = 0"; + if ($object->fk_product > 0) $sql .= ' AND ps.fk_product = '.$object->fk_product; + if ($object->fk_warehouse > 0) $sql .= ' AND ps.fk_entrepot = '.$object->fk_warehouse; + + $inventoryline = new InventoryLine($this->db); + + $resql = $this->db->query($sql); + if ($resql) + { + $num = $this->db->num_rows($resql); + + $i = 0; + while ($i < $num) + { + $obj = $this->db->fetch_object($resql); + + $inventoryline->fk_inventory = $this->id; + $inventoryline->fk_warehouse = $obj->fk_warehouse; + $inventoryline->fk_product = $obj->fk_product; + $inventoryline->batch = $obj->batch; + $inventoryline->qty_stock = ($obj->batch ? $obj->qty : $obj->reel); // If there is batch detail, we take qty for batch, else global qty + $inventoryline->datec = dol_now(); + + $resultline = $inventoryline->create($user); + if ($resultline <= 0) { + $this->error = $inventoryline->error; + $this->errors = $inventoryline->errors; + $result = -1; + break; + } + + $i++; + } + } else { + $result = -1; + $this->error = $this->db->lasterror(); + } + } + + if ($result >= 0) { + $result = $this->setStatut($this::STATUS_VALIDATED, null, '', 'INVENTORY_VALIDATED'); + } + + if ($result > 0) { + $this->db->commit(); + } else { + $this->db->rollback(); + } + } + + /** + * Go back to draft + * + * @param User $user User that creates + * @param bool $notrigger false=launch triggers after, true=disable triggers + * @return int <0 if KO, Id of created object if OK + */ + public function setDraft(User $user, $notrigger = false) + { + $this->db->begin(); + + $result = $this->setStatut($this::STATUS_DRAFT, null, '', 'INVENTORY_DRAFT'); + + if ($result > 0) { + $this->db->commit(); + } else { + $this->db->rollback(); + } } /** @@ -412,10 +506,13 @@ class Inventory extends CommonObject $labelStatus = array(); $labelStatus[self::STATUS_DRAFT] = $langs->trans('Draft'); - $labelStatus[self::STATUS_VALIDATED] = $langs->trans('Enabled'); + $labelStatus[self::STATUS_VALIDATED] = $langs->trans('Validated').' ('.$langs->trans('Started').')'; $labelStatus[self::STATUS_CANCELED] = $langs->trans('Canceled'); + $labelStatusShort[self::STATUS_DRAFT] = $langs->trans('Draft'); + $labelStatusShort[self::STATUS_VALIDATED] = $langs->trans('Started'); + $labelStatusShort[self::STATUS_CANCELED] = $langs->trans('Canceled'); - return dolGetStatus($labelStatus[$status], $labelStatus[$status], '', 'status'.$status, $mode); + return dolGetStatus($labelStatus[$status], $labelStatusShort[$status], '', 'status'.$status, $mode); } /** @@ -506,7 +603,6 @@ class InventoryLine extends CommonObjectLine */ public $picto = 'stock'; - /** * 'type' if the field format. * 'label' the translation key. @@ -546,6 +642,28 @@ class InventoryLine extends CommonObjectLine */ public $rowid; + public $fk_inventory; + public $fk_warehouse; + public $fk_product; + public $batch; + public $datec; + public $tms; + public $qty_stock; + public $qty_view; + public $qty_regulated; + + + /** + * Create object in database + * + * @param User $user User that creates + * @param bool $notrigger false=launch triggers after, true=disable triggers + * @return int <0 if KO, >0 if OK + */ + public function create(User $user, $notrigger = false) + { + return $this->createCommon($user, $notrigger); + } /** * Load object in memory from the database diff --git a/htdocs/product/inventory/inventory.php b/htdocs/product/inventory/inventory.php index 5261b0432b8..0d91546ad56 100644 --- a/htdocs/product/inventory/inventory.php +++ b/htdocs/product/inventory/inventory.php @@ -250,7 +250,7 @@ if ($object->id > 0) // Buttons for actions - if ($action == 'edit') { + if ($action == 'record') { print ''; print ''; print ''; @@ -276,19 +276,36 @@ if ($object->id > 0) { if ($permissiontoadd) { - print ''.$langs->trans("Edit").''."\n"; + print ''.$langs->trans("Validate").' ('.$langs->trans("Start").')'."\n"; } else { - print ''.$langs->trans('Edit').''."\n"; + print ''.$langs->trans('Validate').' ('.$langs->trans("Start").')'."\n"; } } - if ($object->status == Inventory::STATUS_DRAFT) + if ($object->status == Inventory::STATUS_VALIDATED) + { + if ($permissiontoadd) + { + if ($conf->barcode->enabled) { + print ''.$langs->trans("UpdateByScanningProductBarcode").''; + } + if ($conf->productbatch->enabled) { + print ''.$langs->trans('UpdateByScaningLot').''; + } + + //print ''.$langs->trans("Save").''."\n"; + } else { + print ''.$langs->trans('Save').''."\n"; + } + } + + if ($object->status == Inventory::STATUS_VALIDATED) { if ($permissiontoadd) { - print ''.$langs->trans("Validate").''."\n"; + print ''.$langs->trans("Finish").''."\n"; } else { - print ''.$langs->trans('Validate').''."\n"; + print ''.$langs->trans('Finish').''."\n"; } } @@ -325,24 +342,25 @@ if ($object->id > 0) print $langs->trans("Batch"); print ''; } - print ''; - print ''; - print ''; + print ''; + // Actions + print ''; print ''; - - $sql = 'SELECT ps.rowid, ps.fk_entrepot as fk_warehouse, ps.fk_product'; - $sql .= ' FROM '.MAIN_DB_PREFIX.'product_stock as ps, '.MAIN_DB_PREFIX.'product as p, '.MAIN_DB_PREFIX.'entrepot as e'; - $sql .= ' WHERE p.entity IN ('.getEntity('product').')'; - $sql .= ' AND ps.fk_product = p.rowid AND ps.fk_entrepot = e.rowid'; - if (empty($conf->global->STOCK_SUPPORTS_SERVICES)) $sql .= " AND p.fk_product_type = 0"; - if ($object->fk_product > 0) $sql .= ' AND ps.fk_product = '.$object->fk_product; - if ($object->fk_warehouse > 0) $sql .= ' AND ps.fk_entrepot = '.$object->fk_warehouse; + // Request to show lines of inventory (prefilled during creation) + $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 = '.$object->id; $cacheOfProducts = array(); $cacheOfWarehouses = array(); + //$sql = ''; $resql = $db->query($sql); if ($resql) { @@ -386,18 +404,18 @@ if ($object->id > 0) if ($conf->productbatch->enabled) { print ''; } - print ''; - print ''; - print ''; print '';
'.$langs->trans("RecordedQty").''.$langs->trans("RealQty").''; + print ''.$langs->trans("ExpectedQty").''; + print $form->textwithpicto($langs->trans("RealQty"), $langs->trans("InventoryRealQtyHelp")); + print ''; print '
'; - print ''; + print $obj->batch; print ''; - print ''; + print ''; + print 'TODO'; print ''; - print ''; + print ''; + print 'rowid).'">'; print ''; - print ''; + print ''; + print img_delete(); print '