Exemple #1
0
 if (count($prods_arbo)) {
     foreach ($prods_arbo as $value) {
         $productstatic->id = $value['id'];
         $productstatic->type = $value['type'];
         $productstatic->label = $value['label'];
         if ($value['level'] <= 1) {
             $class = $class == 'impair' ? 'pair' : 'impair';
             print '<tr class="' . $class . '">';
             $notdefined = 0;
             $productstatic->ref = $value['ref'];
             $nb_of_subproduct = $value['nb'];
             print '<td>' . $productstatic->getNomUrl(1, 'composition') . '</td>';
             print '<td>' . $productstatic->label . '</td>';
             // Best buying price
             print '<td align="right">';
             if ($product_fourn->find_min_price_product_fournisseur($productstatic->id) > 0) {
                 print ' &nbsp; ' . $langs->trans("BuyingPriceMinShort") . ': ';
                 if ($product_fourn->product_fourn_price_id > 0) {
                     print $product_fourn->display_price_product_fournisseur(0, 0);
                 } else {
                     print $langs->trans("NotDefined");
                     $notdefined++;
                     $atleastonenotdefined++;
                 }
             }
             print '</td>';
             $totalline = price2num($value['nb'] * $product_fourn->fourn_unitprice, 'MT');
             $total += $totalline;
             print '<td align="right">';
             print $notdefined ? '' : ($value['nb'] > 1 ? $value['nb'] . 'x' : '') . price($product_fourn->fourn_unitprice, '', '', 0, 0, -1, $conf->currency);
             print '</td>';
Exemple #2
0
     print '<td align="right">';
     if ($objp->tosell) {
         if ($objp->price_base_type == 'TTC') {
             print price($objp->price_ttc) . ' ' . $langs->trans("TTC");
         } else {
             print price($objp->price) . ' ' . $langs->trans("HT");
         }
     }
     print '</td>';
 }
 // Better buy price
 if (!empty($arrayfields['p.minbuyprice']['checked'])) {
     print '<td align="right">';
     if ($objp->tobuy && $objp->minsellprice != '') {
         //print price($objp->minsellprice).' '.$langs->trans("HT");
         if ($product_fourn->find_min_price_product_fournisseur($objp->rowid) > 0) {
             if ($product_fourn->product_fourn_price_id > 0) {
                 if (!empty($conf->fournisseur->enabled) && $user->rights->fournisseur->lire) {
                     $htmltext = $product_fourn->display_price_product_fournisseur(1, 1, 0, 1);
                     print $form->textwithpicto(price($product_fourn->fourn_unitprice) . ' ' . $langs->trans("HT"), $htmltext);
                 } else {
                     print price($product_fourn->fourn_unitprice) . ' ' . $langs->trans("HT");
                 }
             }
         }
     }
     print '</td>';
 }
 if (!empty($conf->stock->enabled) && $user->rights->stock->lire && $type != 1) {
     if ($objp->fk_product_type != 1) {
         $product_static->id = $objp->rowid;
Exemple #3
0
 /**
  *  Update a proposal line
  *
  *  @param      int			$rowid           	Id de la ligne
  *  @param      float		$pu		     	  	Prix unitaire (HT ou TTC selon price_base_type)
  *  @param      float		$qty            	Quantity
  *  @param      float		$remise_percent  	Remise effectuee sur le produit
  *  @param      float		$txtva	          	Taux de TVA
  * 	@param	  	float		$txlocaltax1		Local tax 1 rate
  *  @param	  	float		$txlocaltax2		Local tax 2 rate
  *  @param      string		$desc            	Description
  *	@param	  	string		$price_base_type	HT ou TTC
  *	@param      int			$info_bits        	Miscellaneous informations
  *	@param		int			$special_code		Special code (also used by externals modules!)
  * 	@param		int			$fk_parent_line		Id of parent line (0 in most cases, used by modules adding sublevels into lines).
  * 	@param		int			$skip_update_total	Keep fields total_xxx to 0 (used for special lines by some modules)
  *  @param		int			$fk_fournprice		Id of origin supplier price
  *  @param		int			$pa_ht				Price (without tax) of product when it was bought
  *  @param		string		$label				???
  *  @param		int			$type				0/1=Product/service
  *	@param      int			$date_start       	Start date of the line
  *	@param      int			$date_end         	End date of the line
  *  @param		array		$array_option		extrafields array
  *  @return     int     		        		0 if OK, <0 if KO
  */
 function updateline($rowid, $pu, $qty, $remise_percent, $txtva, $txlocaltax1 = 0.0, $txlocaltax2 = 0.0, $desc = '', $price_base_type = 'HT', $info_bits = 0, $special_code = 0, $fk_parent_line = 0, $skip_update_total = 0, $fk_fournprice = 0, $pa_ht = 0, $label = '', $type = 0, $date_start = '', $date_end = '', $array_option = 0)
 {
     global $mysoc;
     dol_syslog(get_class($this) . "::updateLine rowid={$rowid}, pu={$pu}, qty={$qty}, remise_percent={$remise_percent}, txtva={$txtva}, desc={$desc}, price_base_type={$price_base_type}, info_bits={$info_bits}, special_code={$special_code}, fk_parent_line={$fk_parent_line}, pa_ht={$a_ht}, type={$type}, date_start={$date_start}, date_end={$date_end}");
     include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php';
     // Clean parameters
     $remise_percent = price2num($remise_percent);
     $qty = price2num($qty);
     $pu = price2num($pu);
     $txtva = price2num($txtva);
     $txlocaltax1 = price2num($txlocaltax1);
     $txlocaltax2 = price2num($txlocaltax2);
     $pa_ht = price2num($pa_ht);
     if (empty($qty) && empty($special_code)) {
         $special_code = 3;
     }
     // Set option tag
     if (!empty($qty) && $special_code == 3) {
         $special_code = 0;
     }
     // Remove option tag
     if ($this->statut == 0) {
         $this->db->begin();
         // Calcul du total TTC et de la TVA pour la ligne a partir de
         // qty, pu, remise_percent et txtva
         // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
         // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
         $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
         $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $mysoc, $localtaxes_type);
         $total_ht = $tabprice[0];
         $total_tva = $tabprice[1];
         $total_ttc = $tabprice[2];
         $total_localtax1 = $tabprice[9];
         $total_localtax2 = $tabprice[10];
         // Anciens indicateurs: $price, $remise (a ne plus utiliser)
         $price = $pu;
         if ($remise_percent > 0) {
             $remise = round($pu * $remise_percent / 100, 2);
             $price = $pu - $remise;
         }
         // Update line
         $this->line = new PropaleLigne($this->db);
         $this->line->context = $this->context;
         // Stock previous line records
         $staticline = new PropaleLigne($this->db);
         $staticline->fetch($rowid);
         $this->line->oldline = $staticline;
         // Reorder if fk_parent_line change
         if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
             $rangmax = $this->line_max($fk_parent_line);
             $this->line->rang = $rangmax + 1;
         }
         $this->line->rowid = $rowid;
         $this->line->label = $label;
         $this->line->desc = $desc;
         $this->line->qty = $qty;
         $this->line->product_type = $type;
         $this->line->tva_tx = $txtva;
         $this->line->localtax1_tx = $txlocaltax1;
         $this->line->localtax2_tx = $txlocaltax2;
         $this->line->localtax1_type = $localtaxes_type[0];
         $this->line->localtax2_type = $localtaxes_type[2];
         $this->line->remise_percent = $remise_percent;
         $this->line->subprice = $pu;
         $this->line->info_bits = $info_bits;
         $this->line->total_ht = $total_ht;
         $this->line->total_tva = $total_tva;
         $this->line->total_localtax1 = $total_localtax1;
         $this->line->total_localtax2 = $total_localtax2;
         $this->line->total_ttc = $total_ttc;
         $this->line->special_code = $special_code;
         $this->line->fk_parent_line = $fk_parent_line;
         $this->line->skip_update_total = $skip_update_total;
         // infos marge
         if (!empty($fk_product) && empty($fk_fournprice) && empty($pa_ht)) {
             // by external module, take lowest buying price
             include_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
             $productFournisseur = new ProductFournisseur($this->db);
             $productFournisseur->find_min_price_product_fournisseur($fk_product);
             $this->line->fk_fournprice = $productFournisseur->product_fourn_price_id;
         } else {
             $this->line->fk_fournprice = $fk_fournprice;
         }
         $this->line->pa_ht = $pa_ht;
         $this->line->date_start = $date_start;
         $this->line->date_end = $date_end;
         // TODO deprecated
         $this->line->price = $price;
         $this->line->remise = $remise;
         if (is_array($array_option) && count($array_option) > 0) {
             $this->line->array_options = $array_option;
         }
         $result = $this->line->update();
         if ($result > 0) {
             // Reorder if child line
             if (!empty($fk_parent_line)) {
                 $this->line_order(true, 'DESC');
             }
             $this->update_price(1);
             $this->fk_propal = $this->id;
             $this->rowid = $rowid;
             $this->db->commit();
             return $result;
         } else {
             $this->error = $this->line->error;
             $this->db->rollback();
             return -1;
         }
     } else {
         dol_syslog(get_class($this) . "::updateline Erreur -2 Propal en mode incompatible pour cette action");
         return -2;
     }
 }
Exemple #4
0
 }
 if ($result) {
     if ($action != 'edit' && $action != 're-edit') {
         $head = product_prepare_head($object);
         $titre = $langs->trans("CardProduct" . $object->type);
         $picto = $object->type == Product::TYPE_SERVICE ? 'service' : 'product';
         dol_fiche_head($head, 'suppliers', $titre, 0, $picto);
         dol_banner_tab($object, 'ref', '', $user->societe_id ? 0 : 1, 'ref');
         print '<div class="fichecenter">';
         print '<div class="underbanner clearboth"></div>';
         print '<table class="border tableforfield" width="100%">';
         // Minimum Price
         print '<tr><td class="titlefield">' . $langs->trans("BuyingPriceMin") . '</td>';
         print '<td colspan="2">';
         $product_fourn = new ProductFournisseur($db);
         if ($product_fourn->find_min_price_product_fournisseur($object->id) > 0) {
             if ($product_fourn->product_fourn_price_id > 0) {
                 print $product_fourn->display_price_product_fournisseur();
             } else {
                 print $langs->trans("NotDefined");
             }
         }
         print '</td></tr>';
         // Cost price. Can be used for margin module for option "calculate margin on explicit cost price
         // Accountancy sell code
         print '<tr><td>';
         $textdesc = $langs->trans("CostPriceDescription");
         $textdesc .= "<br>" . $langs->trans("CostPriceUsage");
         $text = $form->textwithpicto($langs->trans("CostPrice"), $textdesc, 1, 'help', '');
         print $form->editfieldkey($text, 'cost_price', $object->cost_price, $object, $user->rights->produit->creer || $user->rights->service->creer, 'amount:6');
         print '</td><td colspan="2">';
	/**
     *	Add or update supplier price according to result of proposal
     *
	 *	@param     User	    $user       Object user
	 *  @return    int                  > 0 if OK
     */
	function updateOrCreatePriceFournisseur($user)
	{
		$productsupplier = new ProductFournisseur($this->db);

		dol_syslog(get_class($this)."::updateOrCreatePriceFournisseur", LOG_DEBUG);
		foreach ($this->lines as $product) 
		{
			if ($product->subprice <= 0) continue;

			$idProductFourn = $productsupplier->find_min_price_product_fournisseur($product->fk_product, $product->qty);
			$res = $productsupplier->fetch($idProductFourn);

			if ($productsupplier->id) {
				if ($productsupplier->fourn_qty == $product->qty) {
					$this->updatePriceFournisseur($productsupplier->product_fourn_price_id, $product, $user);
				} else {
					$this->createPriceFournisseur($product, $user);
				}
			} else {
				$this->createPriceFournisseur($product, $user);
			}
		}
		
		return 1;
	}
 /**
  * Return the max number delivery delay in day
  *
  * @param	Translate	$langs		Language object
  * @return 							Translated string
  */
 function getMaxDeliveryTimeDay($langs)
 {
     if (empty($this->lines)) {
         return '';
     }
     $obj = new ProductFournisseur($this->db);
     $nb = 0;
     foreach ($this->lines as $line) {
         if ($line->fk_product > 0) {
             $idp = $obj->find_min_price_product_fournisseur($line->fk_product, $line->qty);
             if ($idp) {
                 $obj->fetch($idp);
                 if ($obj->delivery_time_days > $nb) {
                     $nb = $obj->delivery_time_days;
                 }
             }
         }
     }
     if ($nb === 0) {
         return '';
     } else {
         return $nb . ' ' . $langs->trans('Days');
     }
 }
Exemple #7
0
     if (empty($lines[$i]->subprice) || $lines[$i]->qty <= 0) {
         continue;
     }
     $label = !empty($lines[$i]->label) ? $lines[$i]->label : '';
     $desc = !empty($lines[$i]->desc) ? $lines[$i]->desc : $lines[$i]->libelle;
     $product_type = !empty($lines[$i]->product_type) ? $lines[$i]->product_type : 0;
     // Reset fk_parent_line for no child products and special product
     if ($lines[$i]->product_type != 9 && empty($lines[$i]->fk_parent_line) || $lines[$i]->product_type == 9) {
         $fk_parent_line = 0;
     }
     // Extrafields
     if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && method_exists($lines[$i], 'fetch_optionals')) {
         $lines[$i]->fetch_optionals($lines[$i]->rowid);
         $array_option = $lines[$i]->array_options;
     }
     $idprod = $productsupplier->find_min_price_product_fournisseur($lines[$i]->fk_product, $lines[$i]->qty);
     $res = $productsupplier->fetch($idProductFourn);
     $result = $object->addline($desc, $lines[$i]->subprice, $lines[$i]->qty, $lines[$i]->tva_tx, $lines[$i]->localtax1_tx, $lines[$i]->localtax2_tx, $lines[$i]->fk_product, $productsupplier->product_fourn_price_id, $productsupplier->ref_fourn, $lines[$i]->remise_percent, 'HT', 0, $lines[$i]->product_type, '', '', null, null, array(), $lines[$i]->fk_unit);
     if ($result < 0) {
         $error++;
         break;
     }
     // Defined the new fk_parent_line
     if ($result > 0 && $lines[$i]->product_type == 9) {
         $fk_parent_line = $result;
     }
 }
 // Hooks
 $parameters = array('objFrom' => $srcobject);
 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $object, $action);
 // Note that $action and $object may have been
 /**
  * define buy price if not defined
  *	set buy price = sell price if ForceBuyingPriceIfNull configured,
  *	 else if calculation MARGIN_TYPE = 'pmp' and pmp is calculated, set pmp as buyprice
  *	 else set min buy price as buy price
  *	 
  * @param float		$unitPrice		 product unit price
  * @param float		$discountPercent line discount percent
  * @param int		$fk_product		 product id
  *
  * @return	float <0 if ko, buyprice if ok
  */
 public function defineBuyPrice($unitPrice = 0, $discountPercent = 0, $fk_product = 0)
 {
     global $conf;
     $buyPrice = 0;
     if ($unitPrice > 0 && (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 1)) {
         $buyPrice = $unitPrice * (1 - $discountPercent / 100);
     } else {
         // Get PMP
         if (!empty($fk_product)) {
             if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'pmp') {
                 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
                 $product = new Product($this->db);
                 $result = $product->fetch($fk_product);
                 if ($result <= 0) {
                     $this->errors[] = 'ErrorProductIdDoesNotExists';
                     return -1;
                 }
                 if ($product->pmp > 0) {
                     $buyPrice = $product->pmp;
                 }
                 // TODO add option to set PMP of product
             } else {
                 if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == '1') {
                     require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
                     $productFournisseur = new ProductFournisseur($this->db);
                     if (($result = $productFournisseur->find_min_price_product_fournisseur($fk_product)) > 0) {
                         $buyPrice = $productFournisseur->fourn_price;
                     } else {
                         if ($result < 0) {
                             $this->errors[] = $productFournisseur->error;
                             return -2;
                         }
                     }
                 }
             }
         }
     }
     return $buyPrice;
 }
 /**
  *	Calculates product price based on product id and string expression
  *
  *	@param	Product				$product    	The Product object to get information
  *	@param	string 				$expression     The expression to parse
  *	@param	array 				$extra_values   Any aditional values for expression
  *  @return int 				> 0 if OK, < 1 if KO
  */
 public function parseProductExpression($product, $expression, $extra_values = array())
 {
     //Get the supplier min
     $productFournisseur = new ProductFournisseur($this->db);
     $supplier_min_price = $productFournisseur->find_min_price_product_fournisseur($product->id);
     //Accessible values by expressions
     $extra_values = array_merge($extra_values, array("supplier_min_price" => $supplier_min_price));
     //Parse the expression and return the price, if not error occurred check if price is higher than min
     $result = $this->parseExpression($product, $expression, $extra_values);
     if (empty($this->error)) {
         if ($result < $product->price_min) {
             $result = $product->price_min;
         }
     }
     return $result;
 }
Exemple #10
0
 /**
  *	Insert line into database
  *
  *	@param      int		$notrigger		1 no triggers
  *	@return		int						<0 if KO, >0 if OK
  */
 function insert($notrigger = 0)
 {
     global $langs, $user, $conf;
     $error = 0;
     dol_syslog(get_class($this) . "::insert rang=" . $this->rang, LOG_DEBUG);
     // Clean parameters
     $this->desc = trim($this->desc);
     if (empty($this->tva_tx)) {
         $this->tva_tx = 0;
     }
     if (empty($this->localtax1_tx)) {
         $this->localtax1_tx = 0;
     }
     if (empty($this->localtax2_tx)) {
         $this->localtax2_tx = 0;
     }
     if (empty($this->localtax1_type)) {
         $this->localtax1_type = 0;
     }
     if (empty($this->localtax2_type)) {
         $this->localtax2_type = 0;
     }
     if (empty($this->total_localtax1)) {
         $this->total_localtax1 = 0;
     }
     if (empty($this->total_localtax2)) {
         $this->total_localtax2 = 0;
     }
     if (empty($this->rang)) {
         $this->rang = 0;
     }
     if (empty($this->remise_percent)) {
         $this->remise_percent = 0;
     }
     if (empty($this->info_bits)) {
         $this->info_bits = 0;
     }
     if (empty($this->subprice)) {
         $this->subprice = 0;
     }
     if (empty($this->special_code)) {
         $this->special_code = 0;
     }
     if (empty($this->fk_parent_line)) {
         $this->fk_parent_line = 0;
     }
     if (empty($this->pa_ht)) {
         $this->pa_ht = 0;
     }
     // si prix d'achat non renseigne et utilise pour calcul des marges alors prix achat = prix vente
     if ($this->pa_ht == 0) {
         if ($this->subprice > 0 && (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 1)) {
             $this->pa_ht = $this->subprice * (1 - $this->remise_percent / 100);
         }
     }
     // Check parameters
     if ($this->product_type < 0) {
         $this->error = 'ErrorProductTypeMustBe0orMore';
         return -1;
     }
     if (!empty($this->fk_product)) {
         // Check product exists
         $result = Product::isExistingObject('product', $this->fk_product);
         if ($result <= 0) {
             $this->error = 'ErrorProductIdDoesNotExists';
             return -1;
         }
     }
     // POS or by external module, take lowest buying price
     if (!empty($this->fk_product) && empty($this->fk_fournprice) && empty($this->pa_ht)) {
         include_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
         $productFournisseur = new ProductFournisseur($this->db);
         $productFournisseur->find_min_price_product_fournisseur($this->fk_product);
         $this->fk_fournprice = $productFournisseur->product_fourn_price_id;
     }
     $this->db->begin();
     // Insertion dans base de la ligne
     $sql = 'INSERT INTO ' . MAIN_DB_PREFIX . 'facturedet';
     $sql .= ' (fk_facture, fk_parent_line, label, description, qty,';
     $sql .= ' tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
     $sql .= ' fk_product, product_type, remise_percent, subprice, fk_remise_except,';
     $sql .= ' date_start, date_end, fk_code_ventilation, ';
     $sql .= ' rang, special_code, fk_product_fournisseur_price, buy_price_ht,';
     $sql .= ' info_bits, total_ht, total_tva, total_ttc, total_localtax1, total_localtax2)';
     $sql .= " VALUES (" . $this->fk_facture . ",";
     $sql .= " " . ($this->fk_parent_line > 0 ? "'" . $this->fk_parent_line . "'" : "null") . ",";
     $sql .= " " . (!empty($this->label) ? "'" . $this->db->escape($this->label) . "'" : "null") . ",";
     $sql .= " '" . $this->db->escape($this->desc) . "',";
     $sql .= " " . price2num($this->qty) . ",";
     $sql .= " " . price2num($this->tva_tx) . ",";
     $sql .= " " . price2num($this->localtax1_tx) . ",";
     $sql .= " " . price2num($this->localtax2_tx) . ",";
     $sql .= " '" . $this->localtax1_type . "',";
     $sql .= " '" . $this->localtax2_type . "',";
     $sql .= ' ' . (!empty($this->fk_product) ? $this->fk_product : "null") . ',';
     $sql .= " " . $this->product_type . ",";
     $sql .= " " . price2num($this->remise_percent) . ",";
     $sql .= " " . price2num($this->subprice) . ",";
     $sql .= ' ' . (!empty($this->fk_remise_except) ? $this->fk_remise_except : "null") . ',';
     $sql .= " " . (!empty($this->date_start) ? "'" . $this->db->idate($this->date_start) . "'" : "null") . ",";
     $sql .= " " . (!empty($this->date_end) ? "'" . $this->db->idate($this->date_end) . "'" : "null") . ",";
     $sql .= ' ' . $this->fk_code_ventilation . ',';
     $sql .= ' ' . $this->rang . ',';
     $sql .= ' ' . $this->special_code . ',';
     $sql .= ' ' . (!empty($this->fk_fournprice) ? $this->fk_fournprice : "null") . ',';
     $sql .= ' ' . price2num($this->pa_ht) . ',';
     $sql .= " '" . $this->info_bits . "',";
     $sql .= " " . price2num($this->total_ht) . ",";
     $sql .= " " . price2num($this->total_tva) . ",";
     $sql .= " " . price2num($this->total_ttc) . ",";
     $sql .= " " . price2num($this->total_localtax1) . ",";
     $sql .= " " . price2num($this->total_localtax2);
     $sql .= ')';
     dol_syslog(get_class($this) . "::insert", LOG_DEBUG);
     $resql = $this->db->query($sql);
     if ($resql) {
         $this->rowid = $this->db->last_insert_id(MAIN_DB_PREFIX . 'facturedet');
         if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) {
             $this->id = $this->rowid;
             $result = $this->insertExtraFields();
             if ($result < 0) {
                 $error++;
             }
         }
         // Si fk_remise_except defini, on lie la remise a la facture
         // ce qui la flague comme "consommee".
         if ($this->fk_remise_except) {
             $discount = new DiscountAbsolute($this->db);
             $result = $discount->fetch($this->fk_remise_except);
             if ($result >= 0) {
                 // Check if discount was found
                 if ($result > 0) {
                     // Check if discount not already affected to another invoice
                     if ($discount->fk_facture) {
                         $this->error = $langs->trans("ErrorDiscountAlreadyUsed", $discount->id);
                         dol_syslog(get_class($this) . "::insert Error " . $this->error, LOG_ERR);
                         $this->db->rollback();
                         return -3;
                     } else {
                         $result = $discount->link_to_invoice($this->rowid, 0);
                         if ($result < 0) {
                             $this->error = $discount->error;
                             dol_syslog(get_class($this) . "::insert Error " . $this->error, LOG_ERR);
                             $this->db->rollback();
                             return -3;
                         }
                     }
                 } else {
                     $this->error = $langs->trans("ErrorADiscountThatHasBeenRemovedIsIncluded");
                     dol_syslog(get_class($this) . "::insert Error " . $this->error, LOG_ERR);
                     $this->db->rollback();
                     return -3;
                 }
             } else {
                 $this->error = $discount->error;
                 dol_syslog(get_class($this) . "::insert Error " . $this->error, LOG_ERR);
                 $this->db->rollback();
                 return -3;
             }
         }
         if (!$notrigger) {
             // Call trigger
             $result = $this->call_trigger('LINEBILL_INSERT', $user);
             if ($result < 0) {
                 $this->db->rollback();
                 return -2;
             }
             // End call triggers
         }
         $this->db->commit();
         return $this->rowid;
     } else {
         $this->error = $this->db->error();
         $this->db->rollback();
         return -2;
     }
 }
 /**
  *  Update a line in database
  *
  *  @param    	int				$rowid            	Id of line to update
  *  @param    	string			$desc             	Description de la ligne
  *  @param    	float			$pu               	Prix unitaire
  *  @param    	float			$qty              	Quantity
  *  @param    	float			$remise_percent   	Pourcentage de remise de la ligne
  *  @param    	float			$txtva           	Taux TVA
  * 	@param		float			$txlocaltax1		Local tax 1 rate
  *  @param		float			$txlocaltax2		Local tax 2 rate
  *  @param    	string			$price_base_type	HT or TTC
  *  @param    	int				$info_bits        	Miscellaneous informations on line
  *  @param    	int		$date_start        	Start date of the line
  *  @param    	int		$date_end          	End date of the line
  * 	@param		int				$type				Type of line (0=product, 1=service)
  * 	@param		int				$fk_parent_line		Id of parent line (0 in most cases, used by modules adding sublevels into lines).
  * 	@param		int				$skip_update_total	Keep fields total_xxx to 0 (used for special lines by some modules)
  *  @param		int				$fk_fournprice		Id of origin supplier price
  *  @param		int				$pa_ht				Price (without tax) of product when it was bought
  *  @param		string			$label				Label
  *  @param		int				$special_code		Special code (also used by externals modules!)
  *  @param		array			$array_options		extrafields array
  * 	@param 		string			$fk_unit 			Code of the unit to use. Null to use the default one
  *  @return   	int              					< 0 if KO, > 0 if OK
  */
 function updateline($rowid, $desc, $pu, $qty, $remise_percent, $txtva, $txlocaltax1 = 0.0, $txlocaltax2 = 0.0, $price_base_type = 'HT', $info_bits = 0, $date_start = '', $date_end = '', $type = 0, $fk_parent_line = 0, $skip_update_total = 0, $fk_fournprice = null, $pa_ht = 0, $label = '', $special_code = 0, $array_options = 0, $fk_unit = null)
 {
     global $conf, $mysoc;
     dol_syslog(get_class($this) . "::updateline id={$rowid}, desc={$desc}, pu={$pu}, qty={$qty}, remise_percent={$remise_percent}, txtva={$txtva}, txlocaltax1={$txlocaltax1}, txlocaltax2={$txlocaltax2}, price_base_type={$price_base_type}, info_bits={$info_bits}, date_start={$date_start}, date_end={$date_end}, type={$type}, fk_parent_line={$fk_parent_line}, pa_ht={$pa_ht}, special_code={$special_code}");
     include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php';
     if (!empty($this->brouillon)) {
         $this->db->begin();
         // Clean parameters
         if (empty($qty)) {
             $qty = 0;
         }
         if (empty($info_bits)) {
             $info_bits = 0;
         }
         if (empty($txtva)) {
             $txtva = 0;
         }
         if (empty($txlocaltax1)) {
             $txlocaltax1 = 0;
         }
         if (empty($txlocaltax2)) {
             $txlocaltax2 = 0;
         }
         if (empty($remise)) {
             $remise = 0;
         }
         if (empty($remise_percent)) {
             $remise_percent = 0;
         }
         if (empty($special_code) || $special_code == 3) {
             $special_code = 0;
         }
         $remise_percent = price2num($remise_percent);
         $qty = price2num($qty);
         $pu = price2num($pu);
         $pa_ht = price2num($pa_ht);
         $txtva = price2num($txtva);
         $txlocaltax1 = price2num($txlocaltax1);
         $txlocaltax2 = price2num($txlocaltax2);
         // Calcul du total TTC et de la TVA pour la ligne a partir de
         // qty, pu, remise_percent et txtva
         // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
         // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
         $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
         $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $mysoc, $localtaxes_type);
         $total_ht = $tabprice[0];
         $total_tva = $tabprice[1];
         $total_ttc = $tabprice[2];
         $total_localtax1 = $tabprice[9];
         $total_localtax2 = $tabprice[10];
         // Anciens indicateurs: $price, $subprice, $remise (a ne plus utiliser)
         $price = $pu;
         $subprice = $pu;
         $remise = 0;
         if ($remise_percent > 0) {
             $remise = round($pu * $remise_percent / 100, 2);
             $price = $pu - $remise;
         }
         //Fetch current line from the database and then clone the object and set it in $oldline property
         $line = new OrderLine($this->db);
         $line->fetch($rowid);
         $staticline = clone $line;
         $line->oldline = $staticline;
         $this->line = $line;
         $this->line->context = $this->context;
         // Reorder if fk_parent_line change
         if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
             $rangmax = $this->line_max($fk_parent_line);
             $this->line->rang = $rangmax + 1;
         }
         $this->line->rowid = $rowid;
         $this->line->label = $label;
         $this->line->desc = $desc;
         $this->line->qty = $qty;
         $this->line->tva_tx = $txtva;
         $this->line->localtax1_tx = $txlocaltax1;
         $this->line->localtax2_tx = $txlocaltax2;
         $this->line->localtax1_type = $localtaxes_type[0];
         $this->line->localtax2_type = $localtaxes_type[2];
         $this->line->remise_percent = $remise_percent;
         $this->line->subprice = $subprice;
         $this->line->info_bits = $info_bits;
         $this->line->special_code = $special_code;
         $this->line->total_ht = $total_ht;
         $this->line->total_tva = $total_tva;
         $this->line->total_localtax1 = $total_localtax1;
         $this->line->total_localtax2 = $total_localtax2;
         $this->line->total_ttc = $total_ttc;
         $this->line->date_start = $date_start;
         $this->line->date_end = $date_end;
         $this->line->product_type = $type;
         $this->line->fk_parent_line = $fk_parent_line;
         $this->line->skip_update_total = $skip_update_total;
         $this->line->fk_unit = $fk_unit;
         // infos marge
         if (!empty($fk_product) && empty($fk_fournprice) && empty($pa_ht)) {
             //by external module, take lowest buying price
             include_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
             $productFournisseur = new ProductFournisseur($this->db);
             $productFournisseur->find_min_price_product_fournisseur($fk_product);
             $this->line->fk_fournprice = $productFournisseur->product_fourn_price_id;
         } else {
             $this->line->fk_fournprice = $fk_fournprice;
         }
         $this->line->pa_ht = $pa_ht;
         // TODO deprecated
         $this->line->price = $price;
         $this->line->remise = $remise;
         if (is_array($array_options) && count($array_options) > 0) {
             $this->line->array_options = $array_options;
         }
         $result = $this->line->update();
         if ($result > 0) {
             // Reorder if child line
             if (!empty($fk_parent_line)) {
                 $this->line_order(true, 'DESC');
             }
             // Mise a jour info denormalisees
             $this->update_price(1);
             $this->db->commit();
             return $result;
         } else {
             $this->error = $this->line->error;
             $this->db->rollback();
             return -1;
         }
     } else {
         $this->error = get_class($this) . "::updateline Order status makes operation forbidden";
         $this->errors = array('OrderStatusMakeOperationForbidden');
         return -2;
     }
 }