. * --------------------------------------------------------------------- */ use Glpi\Event; if (!defined('GLPI_ROOT')) { die("Sorry. You can't access this file directly"); } //! Consumable Class /** This class is used to manage the consumables. @see ConsumableItem @author Julien Dombre **/ class Consumable extends CommonDBChild { use Glpi\Features\Clonable; // From CommonDBTM static protected $forward_entity_to = ['Infocom']; public $no_form_page = true; static $rightname = 'consumable'; // From CommonDBChild static public $itemtype = 'ConsumableItem'; static public $items_id = 'consumableitems_id'; public function getCloneRelations() :array { return [ Infocom::class ]; } function getForbiddenStandardMassiveAction() { $forbidden = parent::getForbiddenStandardMassiveAction(); $forbidden[] = 'update'; return $forbidden; } static function getNameField() { return 'id'; } static function getTypeName($nb = 0) { return _n('Consumable', 'Consumables', $nb); } function cleanDBonPurge() { $this->deleteChildrenAndRelationsFromDb( [ Infocom::class, ] ); } function prepareInputForAdd($input) { $item = new ConsumableItem(); if ($item->getFromDB($input["consumableitems_id"])) { return ["consumableitems_id" => $item->fields["id"], "entities_id" => $item->getEntityID(), "date_in" => date("Y-m-d")]; } return []; } /** * send back to stock **/ function backToStock(array $input, $history = 1) { global $DB; $result = $DB->update( $this->getTable(), [ 'date_out' => 'NULL' ], [ 'id' => $input['id'] ] ); if ($result) { return true; } return false; } function getPreAdditionalInfosForName() { $ci = new ConsumableItem(); if ($ci->getFromDB($this->fields['consumableitems_id'])) { return $ci->getName(); } return ''; } /** * UnLink a consumable linked to a printer * * UnLink the consumable identified by $ID * * @param integer $ID consumable identifier * @param string $itemtype itemtype of who we give the consumable * @param integer $items_id ID of the item giving the consumable * * @return boolean **/ function out($ID, $itemtype = '', $items_id = 0) { global $DB; if (!empty($itemtype) && ($items_id > 0)) { $result = $DB->update( $this->getTable(), [ 'date_out' => date('Y-m-d'), 'itemtype' => $itemtype, 'items_id' => $items_id ], [ 'id' => $ID ] ); if ($result) { return true; } } return false; } static function showMassiveActionsSubForm(MassiveAction $ma) { global $CFG_GLPI; $input = $ma->getInput(); switch ($ma->getAction()) { case 'give' : if (isset($input["entities_id"])) { Dropdown::showSelectItemFromItemtypes(['itemtype_name' => 'give_itemtype', 'items_id_name' => 'give_items_id', 'entity_restrict' => $input["entities_id"], 'itemtypes' => $CFG_GLPI["consumables_types"]]); echo "

".Html::submit(_x('button', 'Give'), ['name' => 'massiveaction']); return true; } } return parent::showMassiveActionsSubForm($ma); } static function processMassiveActionsForOneItemtype(MassiveAction $ma, CommonDBTM $item, array $ids) { switch ($ma->getAction()) { case 'backtostock' : foreach ($ids as $id) { if ($item->can($id, UPDATE)) { if ($item->backToStock(["id" => $id])) { $ma->itemDone($item->getType(), $id, MassiveAction::ACTION_OK); } else { $ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO); $ma->addMessage($item->getErrorMessage(ERROR_ON_ACTION)); } } else { $ma->itemDone($item->getType(), $id, MassiveAction::ACTION_NORIGHT); $ma->addMessage($item->getErrorMessage(ERROR_RIGHT)); } } return; case 'give' : $input = $ma->getInput(); if (($input["give_items_id"] > 0) && !empty($input['give_itemtype'])) { foreach ($ids as $key) { if ($item->can($key, UPDATE)) { if ($item->out($key, $input['give_itemtype'], $input["give_items_id"])) { $ma->itemDone($item->getType(), $key, MassiveAction::ACTION_OK); } else { $ma->itemDone($item->getType(), $key, MassiveAction::ACTION_KO); $ma->addMessage($item->getErrorMessage(ERROR_ON_ACTION)); } } else { $ma->itemDone($item->getType(), $key, MassiveAction::ACTION_NORIGHT); $ma->addMessage($item->getErrorMessage(ERROR_RIGHT)); } } Event::log($item->fields['consumableitems_id'], "consumableitems", 5, "inventory", //TRANS: %s is the user login sprintf(__('%s gives a consumable'), $_SESSION["glpiname"])); } else { $ma->itemDone($item->getType(), $ids, MassiveAction::ACTION_KO); } return; } parent::processMassiveActionsForOneItemtype($ma, $item, $ids); } /** * count how many consumable for the consumable item $tID * * @param integer $tID consumable item identifier. * * @return integer number of consumable counted. **/ static function getTotalNumber($tID) { global $DB; $result = $DB->request([ 'COUNT' => 'cpt', 'FROM' => 'glpi_consumables', 'WHERE' => ['consumableitems_id' => $tID] ])->next(); return (int)$result['cpt']; } /** * count how many old consumable for the consumable item $tID * * @param integer $tID consumable item identifier. * * @return integer number of old consumable counted. **/ static function getOldNumber($tID) { global $DB; $result = $DB->request([ 'COUNT' => 'cpt', 'FROM' => 'glpi_consumables', 'WHERE' => [ 'consumableitems_id' => $tID, 'NOT' => ['date_out' => null] ] ])->next(); return (int)$result['cpt']; } /** * count how many consumable unused for the consumable item $tID * * @param integer $tID consumable item identifier. * * @return integer number of consumable unused counted. **/ static function getUnusedNumber($tID) { global $DB; $result = $DB->request([ 'COUNT' => 'cpt', 'FROM' => 'glpi_consumables', 'WHERE' => [ 'consumableitems_id' => $tID, 'date_out' => null ] ])->next(); return(int) $result['cpt']; } /** * Get the consumable count HTML array for a defined consumable type * * @param integer $tID consumable item identifier. * @param integer $alarm_threshold threshold alarm value. * @param boolean $nohtml Return value without HTML tags. * * @return string to display **/ static function getCount($tID, $alarm_threshold, $nohtml = 0) { // Get total $total = self::getTotalNumber($tID); if ($total != 0) { $unused = self::getUnusedNumber($tID); $old = self::getOldNumber($tID); $highlight = ""; if ($unused <= $alarm_threshold) { $highlight = "class='tab_bg_1_2'"; } //TRANS: For consumable. %1$d is total number, %2$d is unused number, %3$d is old number $tmptxt = sprintf(__('Total: %1$d, New: %2$d, Used: %3$d'), $total, $unused, $old); if ($nohtml) { $out = $tmptxt; } else { $out = "
".$tmptxt."
"; } } else { if ($nohtml) { $out = __('No consumable'); } else { $out = "
".__('No consumable')."
"; } } return $out; } /** * Check if a Consumable is New (not used, in stock) * * @param integer $cID consumable ID. * * @return boolean **/ static function isNew($cID) { global $DB; $result = $DB->request([ 'COUNT' => 'cpt', 'FROM' => 'glpi_consumables', 'WHERE' => [ 'id' => $cID, 'date_out' => null ] ])->next(); return $result['cpt'] == 1; } /** * Check if a consumable is Old (used, not in stock) * * @param integer $cID consumable ID. * * @return boolean **/ static function isOld($cID) { global $DB; $result = $DB->request([ 'COUNT' => 'cpt', 'FROM' => 'glpi_consumables', 'WHERE' => [ 'id' => $cID, 'NOT' => ['date_out' => null] ] ])->next(); return $result['cpt'] == 1; } /** * Get the localized string for the status of a consumable * * @param integer $cID consumable ID. * * @return string **/ static function getStatus($cID) { if (self::isNew($cID)) { return _nx('consumable', 'New', 'New', 1); } else if (self::isOld($cID)) { return _nx('consumable', 'Used', 'Used', 1); } } /** * Print out a link to add directly a new consumable from a consumable item. * * @param ConsumableItem $consitem * * @return void **/ static function showAddForm(ConsumableItem $consitem) { $ID = $consitem->getField('id'); if (!$consitem->can($ID, UPDATE)) { return; } if ($ID > 0) { echo "
"; echo "
"; echo ""; echo ""; echo "
"; echo "\n"; Dropdown::showNumber('to_add', ['value' => 1, 'min' => 1, 'max' => 100]); echo " "; echo "
"; Html::closeForm(); echo "
"; } } /** * Print out the consumables of a defined type * * @param ConsumableItem $consitem * @param boolean $show_old show old consumables or not. (default 0) * * @return void **/ static function showForConsumableItem(ConsumableItem $consitem, $show_old = 0) { global $DB; $tID = $consitem->getField('id'); if (!$consitem->can($tID, READ)) { return; } if (isset($_GET["start"])) { $start = $_GET["start"]; } else { $start = 0; } $canedit = $consitem->can($tID, UPDATE); $rand = mt_rand(); $where = ['consumableitems_id' => $tID]; $order = ['date_in', 'id']; if (!$show_old) { // NEW $where += ['date_out' => 'NULL']; } else { //OLD $where += ['NOT' => ['date_out' => 'NULL']]; $order = ['date_out DESC'] + $order; } $number = countElementsInTable("glpi_consumables", $where); $iterator = $DB->request([ 'FROM' => self::getTable(), 'WHERE' => $where, 'ORDER' => $order, 'START' => (int)$start, 'LIMIT' => (int)$_SESSION['glpilist_limit'] ]); echo "
"; // Display the pager Html::printAjaxPager(Consumable::getTypeName(Session::getPluralNumber()), $start, $number); if ($canedit && $number) { Html::openMassiveActionsForm('mass'.__CLASS__.$rand); $actions = []; if ($consitem->can($tID, PURGE)) { $actions['delete'] = _x('button', 'Delete permanently'); } $actions['Infocom'.MassiveAction::CLASS_ACTION_SEPARATOR.'activate'] = __('Enable the financial and administrative information'); if ($show_old) { $actions['Consumable'.MassiveAction::CLASS_ACTION_SEPARATOR.'backtostock'] = __('Back to stock'); } else { $actions[__CLASS__.MassiveAction::CLASS_ACTION_SEPARATOR.'give'] = _x('button', 'Give'); } $entparam = ['entities_id' => $consitem->getEntityID()]; if ($consitem->isRecursive()) { $entparam = ['entities_id' => getSonsOf('glpi_entities', $consitem->getEntityID())]; } $massiveactionparams = ['num_displayed' => min($_SESSION['glpilist_limit'], $number), 'specific_actions' => $actions, 'container' => 'mass'.__CLASS__.$rand, 'extraparams' => $entparam]; Html::showMassiveActions($massiveactionparams); echo "\n"; } echo ""; if (!$show_old) { echo ""; } else { // Old echo ""; } if ($number) { $header_begin = ""; $header_top = ''; $header_bottom = ''; $header_end = ''; if ($canedit) { $header_begin .= ""; } $header_end .= ""; $header_end .= ""; $header_end .= ""; if ($show_old) { $header_end .= ""; $header_end .= ""; } $header_end .= ""; $header_end .= ""; echo $header_begin.$header_top.$header_end; while ($data = $iterator->next()) { $date_in = Html::convDate($data["date_in"]); $date_out = Html::convDate($data["date_out"]); echo ""; if ($canedit) { echo ""; } echo ""; echo ""; echo ""; if ($show_old) { echo ""; echo ""; } echo ""; echo ""; } echo $header_begin.$header_bottom.$header_end; } echo "
"; echo self::getCount($tID, -1); echo "
".__('Used consumables')."
"; $header_top .= Html::getCheckAllAsCheckbox('mass'.__CLASS__.$rand); $header_bottom .= Html::getCheckAllAsCheckbox('mass'.__CLASS__.$rand); $header_end .= "".__('ID').""._x('item', 'State')."".__('Add date')."".__('Use date')."".__('Given to')."".__('Financial and administrative information')."
"; Html::showMassiveActionCheckBox(__CLASS__, $data["id"]); echo "".$data["id"]."".self::getStatus($data["id"])."".$date_in."".$date_out.""; if ($item = getItemForItemtype($data['itemtype'])) { if ($item->getFromDB($data['items_id'])) { echo $item->getLink(); } } echo ""; Infocom::showDisplayLink('Consumable', $data["id"]); echo "
"; if ($canedit && $number) { $massiveactionparams['ontop'] = false; Html::showMassiveActions($massiveactionparams); Html::closeForm(); } echo "
"; } /** * Show the usage summary of consumables by user * * @return void **/ static function showSummary() { global $DB; if (!Consumable::canView()) { return; } $iterator = $DB->request([ 'SELECT' => [ 'COUNT' => ['* AS count'], 'consumableitems_id', 'itemtype', 'items_id' ], 'FROM' => 'glpi_consumables', 'WHERE' => [ 'NOT' => ['date_out' => null], 'consumableitems_id' => new \QuerySubQuery([ 'SELECT' => 'id', 'FROM' => 'glpi_consumableitems', 'WHERE' => getEntitiesRestrictCriteria('glpi_consumableitems') ]) ], 'GROUP' => ['itemtype', 'items_id', 'consumableitems_id'] ]); $used = []; while ($data = $iterator->next()) { $used[$data['itemtype'].'####'.$data['items_id']][$data["consumableitems_id"]] = $data["count"]; } $iterator = $DB->request([ 'SELECT' => [ 'COUNT' => '* AS count', 'consumableitems_id', ], 'FROM' => 'glpi_consumables', 'WHERE' => [ 'date_out' => null, 'consumableitems_id' => new \QuerySubQuery([ 'SELECT' => 'id', 'FROM' => 'glpi_consumableitems', 'WHERE' => getEntitiesRestrictCriteria('glpi_consumableitems') ]) ], 'GROUP' => ['consumableitems_id'] ]); $new = []; while ($data = $iterator->next()) { $new[$data["consumableitems_id"]] = $data["count"]; } $iterator = $DB->request([ 'FROM' => 'glpi_consumableitems', 'WHERE' => getEntitiesRestrictCriteria('glpi_consumableitems') ]); $types = []; while ($data = $iterator->next()) { $types[$data["id"]] = $data["name"]; } asort($types); $total = []; if (count($types) > 0) { // Produce headline echo "
"; // Type echo ""; foreach ($types as $key => $type) { echo ""; $total[$key] = 0; } echo ""; echo ""; // new echo ""; $tot = 0; foreach ($types as $id_type => $type) { if (!isset($new[$id_type])) { $new[$id_type] = 0; } echo ""; $total[$id_type] += $new[$id_type]; $tot += $new[$id_type]; } echo ""; echo ""; foreach ($used as $itemtype_items_id => $val) { echo ""; $tot = 0; foreach ($types as $id_type => $type) { if (!isset($val[$id_type])) { $val[$id_type] = 0; } echo ""; $total[$id_type] += $val[$id_type]; $tot += $val[$id_type]; } echo ""; echo ""; } echo ""; $tot = 0; foreach ($types as $id_type => $type) { $tot += $total[$id_type]; echo ""; } echo ""; echo ""; echo "
".__('Give to')."$type".__('Total')."
".__('In stock')."".$new[$id_type]."".$tot."
"; list($itemtype,$items_id) = explode('####', $itemtype_items_id); $item = new $itemtype(); if ($item->getFromDB($items_id)) { //TRANS: %1$s is a type name - %2$s is a name printf(__('%1$s - %2$s'), $item->getTypeName(1), $item->getNameID()); } echo "".$val[$id_type]."".$tot."
".__('Total')."".$total[$id_type]."".$tot."
"; } else { echo "
".__('No consumable found')."
"; } } function getTabNameForItem(CommonGLPI $item, $withtemplate = 0) { if (!$withtemplate && Consumable::canView()) { $nb = 0; switch ($item->getType()) { case 'ConsumableItem' : if ($_SESSION['glpishow_count_on_tabs']) { $nb = self::countForConsumableItem($item); } return self::createTabEntry(self::getTypeName(Session::getPluralNumber()), $nb); } } return ''; } /** * @param ConsumableItem $item * * @return integer **/ static function countForConsumableItem(ConsumableItem $item) { return countElementsInTable(['glpi_consumables'], ['glpi_consumables.consumableitems_id' => $item->getField('id')]); } static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtemplate = 0) { switch ($item->getType()) { case 'ConsumableItem' : self::showAddForm($item); self::showForConsumableItem($item); self::showForConsumableItem($item, 1); return true; } } function getRights($interface = 'central') { $ci = new ConsumableItem(); return $ci->getRights($interface); } static function getIcon() { return "fas fa-box-open"; } }