function _autocomplete_lot_number(&$PDOdb, $productid)
{
    global $db, $conf, $langs;
    $langs->load('other');
    dol_include_once('/core/lib/product.lib.php');
    $sql = "SELECT DISTINCT(lot_number),rowid, SUM(contenancereel_value) as qty, contenancereel_units as unit\n\t\t\tFROM " . MAIN_DB_PREFIX . "asset\n\t\t\tWHERE fk_product = " . $productid . " GROUP BY lot_number,contenancereel_units,rowid";
    $PDOdb->Execute($sql);
    $TLotNumber = array('');
    $PDOdb->Execute($sql);
    $Tres = $PDOdb->Get_All();
    foreach ($Tres as $res) {
        $asset = new TAsset();
        $asset->load($PDOdb, $res->rowid);
        $asset->load_asset_type($PDOdb);
        //pre($asset,true);exit;
        $TLotNumber[$res->lot_number]['lot_number'] = $res->lot_number;
        $TLotNumber[$res->lot_number]['label'] = $res->lot_number . " / " . $res->qty . " " . ($asset->assetType->measuring_units == 'unit' ? 'unité(s)' : measuring_units_string($res->unit, $asset->assetType->measuring_units));
    }
    return $TLotNumber;
}
 /** Overloading the doActions function : replacing the parent's function with the one below 
  *  @param      parameters  meta datas of the hook (context, etc...) 
  *  @param      object             the object you want to process (an invoice if you are in invoice module, a propale in propale's module, etc...) 
  *  @param      action             current action (if set). Generally create or edit or null 
  *  @return       void 
  */
 function beforePDFCreation($parameters, &$object, &$action, $hookmanager)
 {
     // pour implementation dans Dolibarr 3.7
     if (in_array('pdfgeneration', explode(':', $parameters['context']))) {
         define('INC_FROM_DOLIBARR', true);
         dol_include_once('/dispatch/config.php');
         dol_include_once('/asset/class/asset.class.php');
         dol_include_once('/dispatch/class/dispatchdetail.class.php');
         global $conf;
         if (isset($parameters['object']) && get_class($object) == 'Expedition') {
             $PDOdb = new TPDOdb();
             foreach ($object->lines as &$line) {
                 $sql = 'SELECT DISTINCT(lot_number),rowid FROM ' . MAIN_DB_PREFIX . 'expeditiondet_asset WHERE fk_expeditiondet = ' . $line->line_id;
                 $PDOdb->Execute($sql);
                 $TRes = $PDOdb->Get_All();
                 if (count($TRes) > 0) {
                     $line->desc .= "<br>Lot(s) expédié(s) : ";
                     foreach ($TRes as $res) {
                         $dispatchDetail = new TDispatchDetail();
                         $dispatchDetail->load($PDOdb, $res->rowid);
                         $asset = new TAsset();
                         $asset->load($PDOdb, $dispatchDetail->fk_asset);
                         $asset->load_asset_type($PDOdb);
                         $unite = $asset->assetType->measuring_units == 'unit' ? 'unité(s)' : measuring_units_string($dispatchDetail->weight_reel_unit, $asset->assetType->measuring_units);
                         $desc = "<br>- " . $res->lot_number . " x " . $dispatchDetail->weight_reel . " " . $unite;
                         if (empty($conf->global->DISPATCH_HIDE_DLUO_PDF)) {
                             $desc .= ' (DLUO : ' . $asset->get_date('dluo') . ')';
                         }
                         $line->desc .= $desc;
                     }
                 }
             }
         }
         //pre($object,true);exit;
     }
 }
function tabImport(&$TImport, &$expedition)
{
    global $langs, $db;
    $form = new TFormCore();
    $formDoli = new Form($db);
    $formproduct = new FormProduct($db);
    $PDOdb = new TPDOdb();
    print count($TImport) . ' équipement(s) dans votre expédition';
    ?>
	<table width="100%" class="border">
		<tr class="liste_titre">
			<td>Produit</td>
			<td>Numéro de série</td>
			<td>Numéro de Lot</td>
			<td>Quantité</td>
			<td>&nbsp;</td>
		</tr>
		
	<?php 
    $prod = new Product($db);
    $form->Set_typeaff('view');
    if (is_array($TImport)) {
        foreach ($TImport as $k => $line) {
            if ($prod->id == 0 || $line['ref'] != $prod->ref) {
                if (!empty($line['fk_product'])) {
                    $prod->fetch($line['fk_product']);
                } else {
                    $prod->fetch('', $line['ref']);
                }
            }
            $asset = new TAsset();
            $asset->loadBy($PDOdb, $line['numserie'], 'serial_number');
            $asset->load_asset_type($PDOdb);
            $assetLot = new TAssetLot();
            $assetLot->loadBy($PDOdb, $line['lot_number'], 'lot_number');
            $Trowid = TRequeteCore::get_id_from_what_you_want($PDOdb, MAIN_DB_PREFIX . "expeditiondet_asset", array('fk_asset' => $asset->rowid, 'fk_expeditiondet' => $line['fk_expeditiondet']));
            ?>
<tr>
					<td><?php 
            echo $prod->getNomUrl(1) . $form->hidden('TLine[' . $k . '][fk_product]', $prod->id) . $form->hidden('TLine[' . $k . '][ref]', $prod->ref);
            ?>
</td>
					<td><a href="<?php 
            echo dol_buildpath('/asset/fiche.php?id=' . $asset->rowid, 1);
            ?>
"><?php 
            echo $form->texte('', 'TLine[' . $k . '][numserie]', $line['numserie'], 30);
            ?>
</a></td>
					<td><a href="<?php 
            echo dol_buildpath('/asset/fiche_lot.php?id=' . $assetLot->rowid, 1);
            ?>
"><?php 
            echo $form->texte('', 'TLine[' . $k . '][lot_number]', $line['lot_number'], 30);
            ?>
</a></td>
					<td><?php 
            echo $line['quantity'] . " " . ($asset->assetType->measuring_units == 'unit' ? 'unité(s)' : measuring_units_string($line['quantity_unit'], $asset->assetType->measuring_units));
            ?>
</td>
					<td>
						<?php 
            if ($expedition->statut != 1) {
                echo '<a href="?action=DELETE_LINE&k=' . $k . '&id=' . $expedition->id . '&rowid=' . $Trowid[0] . '">' . img_delete() . '</a>';
            }
            ?>
					</td>
				</tr>
				
				<?php 
        }
    }
    ?>
			
		
	</table>
	<br>
	<?php 
}
 /**
  *      Function called when a Dolibarrr business event is done.
  *      All functions "run_trigger" are triggered if file is inside directory htdocs/core/triggers
  *
  *      @param	string		$action		Event action code
  *      @param  Object		$object     Object
  *      @param  User		$user       Object user
  *      @param  Translate	$langs      Object langs
  *      @param  conf		$conf       Object conf
  *      @return int         			<0 if KO, 0 if no triggered ran, >0 if OK
  */
 function run_trigger($action, $object, $user, $langs, $conf)
 {
     global $conf, $db;
     if (!defined('INC_FROM_DOLIBARR')) {
         define('INC_FROM_DOLIBARR', true);
     }
     if ($action == 'SHIPPING_VALIDATE') {
         dol_include_once('/dispatch/config.php');
         dol_include_once('/dispatch/class/dispatchdetail.class.php');
         $PDOdb = new TPDOdb();
         // Pour chaque ligne de l'expédition
         foreach ($object->lines as $line) {
             // Chargement de l'objet detail dispatch relié à la ligne d'expédition
             $dd = new TDispatchDetail();
             $TIdExpeditionDet = TRequeteCore::get_id_from_what_you_want($PDOdb, MAIN_DB_PREFIX . 'expeditiondet', array('fk_expedition' => $object->id, 'fk_origin_line' => $line->fk_origin_line));
             $idExpeditionDet = $TIdExpeditionDet[0];
             //if(!empty($idExpeditionDet) && $dd->loadBy($PDOdb, $idExpeditionDet, 'fk_expeditiondet')) {
             if (!empty($idExpeditionDet)) {
                 $dd->loadLines($PDOdb, $idExpeditionDet);
                 if ($conf->asset->enabled) {
                     // Création des mouvements de stock de flacon
                     foreach ($dd->lines as $detail) {
                         // Création du mouvement de stock standard
                         $poids_destocke = $this->create_flacon_stock_mouvement($PDOdb, $detail, $object->ref, empty($object->fk_soc) ? $object->socid : $object->fk_soc);
                         //$this->create_standard_stock_mouvement($line, $poids_destocke, $object->ref);
                         if ($conf->clinomadic->enabled) {
                             $asset = new TAsset();
                             $asset->load($PDOdb, $detail->fk_asset);
                             $prod = new Product($db);
                             $prod->fetch($asset->fk_product);
                             //Affectation du type d'équipement pour avoir accès aux extrafields équipement
                             $asset->fk_asset_type = $asset->get_asset_type($PDOdb, $prod->id);
                             $asset->load_asset_type($PDOdb);
                             //Localisation client
                             $asset->fk_societe_localisation = $object->socid;
                             if (!empty($object->linkedObjects['commande'][0]->array_options['options_duree_pret'])) {
                                 $asset->etat = 2;
                                 //Prêté
                                 $asset->set_date('date_deb_pret', $object->date_valid);
                                 $asset->set_date('date_fin_pret', strtotime('+' . $object->commande[0]->array_options['options_duree_pret'] . 'year', $object->date_valid));
                             } else {
                                 $asset->etat = 1;
                                 //Vendu
                             }
                             foreach ($object->linkedObjects['commande'][0]->lines as $line) {
                                 if ($line->fk_product == $asset->fk_product) {
                                     $extension_garantie = $line->array_options['options_extension_garantie'];
                                 }
                             }
                             $nb_year_garantie += $prod->array_options['options_duree_garantie_client'];
                             $asset->date_fin_garantie_cli = strtotime('+' . $nb_year_garantie . 'year', $object->date_valid);
                             $asset->date_fin_garantie_cli = strtotime('+' . $extension_garantie . 'year', $asset->date_fin_garantie_cli);
                             $asset->save($PDOdb);
                         }
                     }
                 }
                 //exit;
             }
             /* else { // Pas de détail, on déstocke la quantité comme Dolibarr standard
             				$this->create_standard_stock_mouvement($line, $line->qty, $object->ref);
             			}*/
         }
         dol_syslog("Trigger '" . $this->name . "' for action '{$action}' launched by " . __FILE__ . ". id=" . $object->id);
     }
     return 0;
 }
 $time_date_recep = Tools::get_time($_POST['date_recep']);
 //Tableau provisoir qui permettra la ventilation standard Dolibarr après la création des équipements
 $TProdVentil = array();
 $TAssetVentil = array();
 //Use to calculated corrected order status at the end of dispatch/serialize process
 $TQtyDispatch = array();
 $TQtyWished = array();
 //var_dump($TImport);
 foreach ($TImport as $k => &$line) {
     $asset = new TAsset();
     if (!empty($conf->global->DISPATCH_CREATE_NUMSERIE_ON_RECEPTION_IF_LOT) && empty($line['numserie']) && !empty($line['lot_number'])) {
         $product = new Product($db);
         $product->fetch($line['fk_product']);
         $asset->fk_asset_type = $product->array_options['options_type_asset'];
         if ($asset->fk_asset_type > 0) {
             $asset->load_asset_type($PDOdb);
             $line['numserie'] = $asset->getNextValue($PDOdb);
             setEventMessage($langs->trans('createNumSerieOnTheFly', $line['numserie']), "warning");
             $TImport = _addCommandedetLine($PDOdb, $TImport, $commandefourn, $product->ref, $line['numserie'], $line['imei'], $line['firmware'], $line['lot_number'], $line['quantity'] ? $line['quantity'] : 1, $line['quantity_unit'], $line['dluo'], $k, $line['entrepot']);
         }
     }
     if (empty($line['numserie'])) {
         setEventMessage("Pas de numéro de série : impossible de créer l'équipement pour " . $line['ref'] . ". Si vous ne voulez pas sérialiser ce produit, supprimez les lignes de numéro de série et faites une réception simple. ", "errors");
     } else {
         if (!$asset->loadReference($PDOdb, $line['numserie'])) {
             // si inexistant
             //Seulement si nouvelle ligne
             if ($k == -1) {
                 _addCommandedetLine($PDOdb, $TImport, $commandefourn, $line['ref'], $line['numserie'], $line['$imei'], $line['$firmware'], $line['lot_number'], $line['quantity'], $line['quantity_unit'], null, null, $line['fk_warehouse']);
             }
             $prod = new Product($db);
 function makeAsset(&$PDOdb, &$AssetOf, $fk_product, $qty_to_make, $idAsset = 0, $lot_number = '')
 {
     global $user, $conf;
     //INFO : si on utilise pas les lots on a pas besoin de créer des équipements => on gère uniquement des mvt de stock
     if (empty($conf->asset->enabled) || empty($conf->global->USE_LOT_IN_OF)) {
         return true;
     }
     if (!dol_include_once('/asset/class/asset.class.php')) {
         return true;
     }
     $assetType = new TAsset_type();
     if ($assetType->load_by_fk_product($PDOdb, $fk_product)) {
         /* On fabrique de la contenance et non pas une quantité de produit au sens strict
          * Si on fabrique un produit au sens strict, le type d'équipement de ce produit aura une contenance max à 1, donc ça marche pareil
          * En revanche, on a un sac de sable à moitier vide, s'il est réutilisable on va le remplir puis en créer si besoin
          *
          * A penser :	   [1] [2] [3]
          * Périsable :		1	0	1
          * Réutilisable :	0	1	1
          *
          * [1] => on crée de nouveaux équipements
          * [2] => on réutilise
          * [3] => si la qté de l'équipement courant = 0 alors on réutilise
          *
          * Dans le process de la validation de la production, les TO_MAKE doivent pré-séléctionner les équipements possibles à la réutilisation
          * Quand on "Termine" l'OF il faudra prendre la liste des équipements liés pour les remplir puis en créer si nécessaire
          */
         $contenance_max = $assetType->contenance_value;
         $nb_asset_to_create = ceil($qty_to_make / $contenance_max);
         //Qté restante a fabriquer
         $qty_to_make_rest = $qty_to_make;
         for ($i = 0; $i < $nb_asset_to_create; $i++) {
             $TAsset = new TAsset();
             $TAsset->fk_soc = $AssetOf->fk_soc;
             $TAsset->fk_societe_localisation = $conf->global->ASSET_DEFAULT_LOCATION;
             $TAsset->fk_product = $fk_product;
             $TAsset->entity = $conf->entity;
             if (!empty($conf->global->ASSET_DEFAULT_DLUO)) {
                 $TAsset->dluo = strtotime(date('Y-m-d') . ' +' . $conf->global->ASSET_DEFAULT_DLUO . ' days');
             } else {
                 $TAsset->dluo = strtotime(date('Y-m-d'));
             }
             //pre($assetType,true);exit;
             $TAsset->fk_asset_type = $assetType->getId();
             $TAsset->load_asset_type($PDOdb);
             if ($qty_to_make_rest > $TAsset->contenance_value) {
                 $qty_to_make_asset = $TAsset->contenance_value;
             } else {
                 $qty_to_make_asset = $qty_to_make_rest;
             }
             $qty_to_make_rest -= $qty_to_make_asset;
             $TAsset->contenancereel_value = $qty_to_make_asset;
             $TAsset->lot_number = $lot_number;
             if (!empty($conf->global->ASSET_USE_DEFAULT_WAREHOUSE)) {
                 $fk_entrepot = $conf->global->ASSET_DEFAULT_WAREHOUSE_ID_TO_MAKE;
             }
             if (!$fk_entrepot) {
                 exit('ASSET_USE_DEFAULT_WAREHOUSE non définis dans la configuration du module');
             }
             $TAsset->fk_entrepot = $fk_entrepot;
             $TAsset->save($PDOdb, '', '', 0, false, 0, true, $fk_entrepot);
             //Save une première fois pour avoir le serial_number + 2ème save pour mvt de stock
             $this->addAssetLink($TAsset);
         }
         return true;
     } else {
         return false;
     }
 }