dolibarr  17.0.4
supplier_proposal.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2002-2004 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3  * Copyright (C) 2004 Eric Seigne <eric.seigne@ryxeo.com>
4  * Copyright (C) 2004-2011 Laurent Destailleur <eldy@users.sourceforge.net>
5  * Copyright (C) 2005 Marc Barilley <marc@ocebo.com>
6  * Copyright (C) 2005-2013 Regis Houssin <regis.houssin@inodbox.com>
7  * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
8  * Copyright (C) 2008 Raphael Bertrand <raphael.bertrand@resultic.fr>
9  * Copyright (C) 2010-2020 Juanjo Menent <jmenent@2byte.es>
10  * Copyright (C) 2010-2018 Philippe Grand <philippe.grand@atoo-net.com>
11  * Copyright (C) 2012-2014 Christophe Battarel <christophe.battarel@altairis.fr>
12  * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
13  * Copyright (C) 2014 Marcos García <marcosgdf@gmail.com>
14  * Copyright (C) 2016 Ferran Marcet <fmarcet@2byte.es>
15  * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
16  * Copyright (C) 2019-2022 Frédéric France <frederic.france@netlogic.fr>
17  * Copyright (C) 2020 Tobias Sekan <tobias.sekan@startmail.com>
18  * Copyright (C) 2022 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
19  *
20  * This program is free software; you can redistribute it and/or modify
21  * it under the terms of the GNU General Public License as published by
22  * the Free Software Foundation; either version 3 of the License, or
23  * (at your option) any later version.
24  *
25  * This program is distributed in the hope that it will be useful,
26  * but WITHOUT ANY WARRANTY; without even the implied warranty of
27  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28  * GNU General Public License for more details.
29  *
30  * You should have received a copy of the GNU General Public License
31  * along with this program. If not, see <https://www.gnu.org/licenses/>.
32  */
33 
39 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
40 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
41 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
42 require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
43 require_once DOL_DOCUMENT_ROOT.'/margin/lib/margins.lib.php';
44 require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
45 require_once DOL_DOCUMENT_ROOT.'/core/class/commonincoterm.class.php';
46 
51 {
52  use CommonIncoterm;
53 
57  public $element = 'supplier_proposal';
58 
62  public $table_element = 'supplier_proposal';
63 
67  public $table_element_line = 'supplier_proposaldet';
68 
72  public $fk_element = 'fk_supplier_proposal';
73 
77  public $picto = 'supplier_proposal';
78 
83  public $ismultientitymanaged = 1;
84 
89  public $restrictiononfksoc = 1;
90 
94  protected $table_ref_field = 'ref';
95 
96  public $socid; // Id client
97 
102  public $author;
103 
104  public $ref_fourn; //Reference saisie lors de l'ajout d'une ligne à la demande
105  public $ref_supplier; //Reference saisie lors de l'ajout d'une ligne à la demande
106  public $statut; // 0 (draft), 1 (validated), 2 (signed), 3 (not signed), 4 (processed/billed)
107 
111  public $date;
112 
117  public $date_livraison;
118 
122  public $delivery_date;
123 
128  public $datec;
129 
133  public $date_creation;
134 
139  public $datev;
140 
144  public $date_validation;
145 
146 
147  public $user_author_id;
148  public $user_valid_id;
149  public $user_close_id;
150 
155  public $price;
156 
161  public $tva;
162 
167  public $total;
168 
169  public $cond_reglement_code;
170  public $mode_reglement_code;
171  public $remise = 0;
172  public $remise_percent = 0;
173  public $remise_absolue = 0;
174 
175  public $extraparams = array();
176  public $lines = array();
177  public $line;
178 
179  public $labelStatus = array();
180  public $labelStatusShort = array();
181 
182  public $nbtodo;
183  public $nbtodolate;
184 
185  // Multicurrency
189  public $fk_multicurrency;
190 
191  public $multicurrency_code;
192  public $multicurrency_tx;
193  public $multicurrency_total_ht;
194  public $multicurrency_total_tva;
195  public $multicurrency_total_ttc;
196 
200  const STATUS_DRAFT = 0;
201 
205  const STATUS_VALIDATED = 1;
206 
210  const STATUS_SIGNED = 2;
211 
215  const STATUS_NOTSIGNED = 3;
216 
220  const STATUS_CLOSE = 4;
221 
222 
223 
231  public function __construct($db, $socid = "", $supplier_proposalid = 0)
232  {
233  global $conf, $langs;
234 
235  $this->db = $db;
236 
237  $this->socid = $socid;
238  $this->id = $supplier_proposalid;
239  }
240 
241 
242  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
253  public function add_product($idproduct, $qty, $remise_percent = 0)
254  {
255  // phpcs:enable
256  global $conf, $mysoc;
257 
258  if (!$qty) {
259  $qty = 1;
260  }
261 
262  dol_syslog(get_class($this)."::add_product $idproduct, $qty, $remise_percent");
263  if ($idproduct > 0) {
264  $prod = new Product($this->db);
265  $prod->fetch($idproduct);
266 
267  $productdesc = $prod->description;
268 
269  $tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
270  $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
271  if (empty($tva_tx)) {
272  $tva_npr = 0;
273  }
274  $localtax1_tx = get_localtax($tva_tx, 1, $mysoc, $this->thirdparty, $tva_npr);
275  $localtax2_tx = get_localtax($tva_tx, 2, $mysoc, $this->thirdparty, $tva_npr);
276 
277  // multiprix
278  if ($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level) {
279  $price = $prod->multiprices[$this->thirdparty->price_level];
280  } else {
281  $price = $prod->price;
282  }
283 
284  $line = new SupplierProposalLine($this->db);
285 
286  $line->fk_product = $idproduct;
287  $line->desc = $productdesc;
288  $line->qty = $qty;
289  $line->subprice = $price;
290  $line->remise_percent = $remise_percent;
291  $line->tva_tx = $tva_tx;
292 
293  $this->lines[] = $line;
294  return 1;
295  }
296  return -1;
297  }
298 
299  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
306  public function insert_discount($idremise)
307  {
308  // phpcs:enable
309  global $langs;
310 
311  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
312  include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
313 
314  $this->db->begin();
315 
316  $remise = new DiscountAbsolute($this->db);
317  $result = $remise->fetch($idremise);
318 
319  if ($result > 0) {
320  if ($remise->fk_facture) { // Protection against multiple submission
321  $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
322  $this->db->rollback();
323  return -5;
324  }
325 
326  $supplier_proposalligne = new SupplierProposalLine($this->db);
327  $supplier_proposalligne->fk_supplier_proposal = $this->id;
328  $supplier_proposalligne->fk_remise_except = $remise->id;
329  $supplier_proposalligne->desc = $remise->description; // Description ligne
330  $supplier_proposalligne->tva_tx = $remise->tva_tx;
331  $supplier_proposalligne->subprice = -$remise->amount_ht;
332  $supplier_proposalligne->fk_product = 0; // Id produit predefini
333  $supplier_proposalligne->qty = 1;
334  $supplier_proposalligne->remise_percent = 0;
335  $supplier_proposalligne->rang = -1;
336  $supplier_proposalligne->info_bits = 2;
337 
338  $supplier_proposalligne->total_ht = -$remise->amount_ht;
339  $supplier_proposalligne->total_tva = -$remise->amount_tva;
340  $supplier_proposalligne->total_ttc = -$remise->amount_ttc;
341 
342  $result = $supplier_proposalligne->insert();
343  if ($result > 0) {
344  $result = $this->update_price(1);
345  if ($result > 0) {
346  $this->db->commit();
347  return 1;
348  } else {
349  $this->db->rollback();
350  return -1;
351  }
352  } else {
353  $this->error = $supplier_proposalligne->error;
354  $this->db->rollback();
355  return -2;
356  }
357  } else {
358  $this->db->rollback();
359  return -2;
360  }
361  }
362 
400  public function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1 = 0, $txlocaltax2 = 0, $fk_product = 0, $remise_percent = 0, $price_base_type = 'HT', $pu_ttc = 0, $info_bits = 0, $type = 0, $rang = -1, $special_code = 0, $fk_parent_line = 0, $fk_fournprice = 0, $pa_ht = 0, $label = '', $array_options = 0, $ref_supplier = '', $fk_unit = '', $origin = '', $origin_id = 0, $pu_ht_devise = 0, $date_start = 0, $date_end = 0)
401  {
402  global $mysoc, $conf, $langs;
403 
404  dol_syslog(get_class($this)."::addline supplier_proposalid=$this->id, desc=$desc, pu_ht=$pu_ht, qty=$qty, txtva=$txtva, fk_product=$fk_product, remise_except=$remise_percent, price_base_type=$price_base_type, pu_ttc=$pu_ttc, info_bits=$info_bits, type=$type");
405  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
406 
407  // Clean parameters
408  if (empty($remise_percent)) {
409  $remise_percent = 0;
410  }
411  if (empty($qty)) {
412  $qty = 0;
413  }
414  if (empty($info_bits)) {
415  $info_bits = 0;
416  }
417  if (empty($rang)) {
418  $rang = 0;
419  }
420  if (empty($fk_parent_line) || $fk_parent_line < 0) {
421  $fk_parent_line = 0;
422  }
423  if (empty($pu_ht)) {
424  $pu_ht = 0;
425  }
426 
427  $remise_percent = price2num($remise_percent);
428  $qty = price2num($qty);
429  $pu_ht = price2num($pu_ht);
430  $pu_ttc = price2num($pu_ttc);
431  if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
432  $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
433  }
434  $txlocaltax1 = price2num($txlocaltax1);
435  $txlocaltax2 = price2num($txlocaltax2);
436  $pa_ht = price2num($pa_ht);
437  if ($price_base_type == 'HT') {
438  $pu = $pu_ht;
439  } else {
440  $pu = $pu_ttc;
441  }
442 
443  // Check parameters
444  if ($type < 0) {
445  return -1;
446  }
447 
448  if ($this->statut == self::STATUS_DRAFT) {
449  $this->db->begin();
450 
451  if ($fk_product > 0) {
452  if (!empty($conf->global->SUPPLIER_PROPOSAL_WITH_PREDEFINED_PRICES_ONLY)) {
453  // Check quantity is enough
454  dol_syslog(get_class($this)."::addline we check supplier prices fk_product=".$fk_product." fk_fournprice=".$fk_fournprice." qty=".$qty." ref_supplier=".$ref_supplier);
455  $productsupplier = new ProductFournisseur($this->db);
456  if ($productsupplier->fetch($fk_product) > 0) {
457  $product_type = $productsupplier->type;
458  $label = $productsupplier->label;
459  $fk_prod_fourn_price = $fk_fournprice;
460 
461  // We use 'none' instead of $ref_supplier, because fourn_ref may not exists anymore. So we will take the first supplier price ok.
462  // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
463  $result = $productsupplier->get_buyprice($fk_prod_fourn_price, $qty, $fk_product, 'none', ($this->fk_soc ? $this->fk_soc : $this->socid)); // Search on couple $fk_prod_fourn_price/$qty first, then on triplet $qty/$fk_product/$ref_supplier/$this->fk_soc
464  if ($result > 0) {
465  $pu = $productsupplier->fourn_pu; // Unit price supplier price set by get_buyprice
466  $ref_supplier = $productsupplier->ref_supplier; // Ref supplier price set by get_buyprice
467  // is remise percent not keyed but present for the product we add it
468  if ($remise_percent == 0 && $productsupplier->remise_percent != 0) {
469  $remise_percent = $productsupplier->remise_percent;
470  }
471  }
472  if ($result == 0) { // If result == 0, we failed to found the supplier reference price
473  $langs->load("errors");
474  $this->error = "Ref ".$productsupplier->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
475  $this->db->rollback();
476  dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
477  //$pu = $productsupplier->fourn_pu; // We do not overwrite unit price
478  //$ref = $productsupplier_fourn; // We do not overwrite ref supplier price
479  return -1;
480  }
481  if ($result == -1) {
482  $langs->load("errors");
483  $this->error = "Ref ".$productsupplier->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
484  $this->db->rollback();
485  dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
486  return -1;
487  }
488  if ($result < -1) {
489  $this->error = $productsupplier->error;
490  $this->errors = $productsupplier->errors;
491  $this->db->rollback();
492  dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
493  return -1;
494  }
495  } else {
496  $this->error = $productsupplier->error;
497  $this->errors = $productsupplier->errors;
498  $this->db->rollback();
499  return -1;
500  }
501  }
502  } else {
503  $product_type = $type;
504  }
505 
506  // Calcul du total TTC et de la TVA pour la ligne a partir de
507  // qty, pu, remise_percent et txtva
508  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
509  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
510 
511  $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
512 
513  // Clean vat code
514  $reg = array();
515  $vat_src_code = '';
516  if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
517  $vat_src_code = $reg[1];
518  $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
519  }
520 
521  if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
522  $pu = 0;
523  }
524 
525  $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
526  $total_ht = $tabprice[0];
527  $total_tva = $tabprice[1];
528  $total_ttc = $tabprice[2];
529  $total_localtax1 = $tabprice[9];
530  $total_localtax2 = $tabprice[10];
531  $pu = $pu_ht = $tabprice[3];
532 
533  // MultiCurrency
534  $multicurrency_total_ht = $tabprice[16];
535  $multicurrency_total_tva = $tabprice[17];
536  $multicurrency_total_ttc = $tabprice[18];
537  $pu_ht_devise = $tabprice[19];
538 
539  // Rang to use
540  $ranktouse = $rang;
541  if ($ranktouse == -1) {
542  $rangmax = $this->line_max($fk_parent_line);
543  $ranktouse = $rangmax + 1;
544  }
545 
546  // TODO A virer
547  // Anciens indicateurs: $price, $remise (a ne plus utiliser)
548  $price = $pu;
549  $remise = 0;
550  if ($remise_percent > 0) {
551  $remise = round(($pu * $remise_percent / 100), 2);
552  $price = $pu - $remise;
553  }
554 
555  // Insert line
556  $this->line = new SupplierProposalLine($this->db);
557 
558  $this->line->fk_supplier_proposal = $this->id;
559  $this->line->label = $label;
560  $this->line->desc = $desc;
561  $this->line->qty = $qty;
562 
563  $this->line->vat_src_code = $vat_src_code;
564  $this->line->tva_tx = $txtva;
565  $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
566  $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
567  $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
568  $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
569  $this->line->fk_product = $fk_product;
570  $this->line->remise_percent = $remise_percent;
571  $this->line->subprice = $pu_ht;
572  $this->line->rang = $ranktouse;
573  $this->line->info_bits = $info_bits;
574  $this->line->total_ht = $total_ht;
575  $this->line->total_tva = $total_tva;
576  $this->line->total_localtax1 = $total_localtax1;
577  $this->line->total_localtax2 = $total_localtax2;
578  $this->line->total_ttc = $total_ttc;
579  $this->line->product_type = $type;
580  $this->line->special_code = $special_code;
581  $this->line->fk_parent_line = $fk_parent_line;
582  $this->line->fk_unit = $fk_unit;
583  $this->line->origin = $origin;
584  $this->line->origin_id = $origin_id;
585  $this->line->ref_fourn = $this->db->escape($ref_supplier);
586  $this->line->date_start = $date_start;
587  $this->line->date_end = $date_end;
588 
589  // infos marge
590  if (!empty($fk_product) && $fk_product > 0 && empty($fk_fournprice) && empty($pa_ht)) {
591  // When fk_fournprice is 0, we take the lowest buying price
592  include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
593  $productFournisseur = new ProductFournisseur($this->db);
594  $productFournisseur->find_min_price_product_fournisseur($fk_product);
595  $this->line->fk_fournprice = $productFournisseur->product_fourn_price_id;
596  } else {
597  $this->line->fk_fournprice = ($fk_fournprice > 0 ? $fk_fournprice : 0); // If fk_fournprice is -1, we will not use fk_fournprice
598  }
599  $this->line->pa_ht = $pa_ht;
600  //var_dump($this->line->fk_fournprice);exit;
601 
602  // Multicurrency
603  $this->line->fk_multicurrency = $this->fk_multicurrency;
604  $this->line->multicurrency_code = $this->multicurrency_code;
605  $this->line->multicurrency_subprice = $pu_ht_devise;
606  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
607  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
608  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
609 
610  // Mise en option de la ligne
611  if (empty($qty) && empty($special_code)) {
612  $this->line->special_code = 3;
613  }
614 
615  if (is_array($array_options) && count($array_options) > 0) {
616  $this->line->array_options = $array_options;
617  }
618 
619  $result = $this->line->insert();
620  if ($result > 0) {
621  // Reorder if child line
622  if (!empty($fk_parent_line)) {
623  $this->line_order(true, 'DESC');
624  } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) { // Update all rank of all other lines
625  $linecount = count($this->lines);
626  for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
627  $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
628  }
629  }
630 
631  // Mise a jour informations denormalisees au niveau de la propale meme
632  $result = $this->update_price(1, 'auto', 0, $this->thirdparty); // This method is designed to add line from user input so total calculation must be done using 'auto' mode.
633  if ($result > 0) {
634  $this->db->commit();
635  return $this->line->id;
636  } else {
637  $this->error = $this->error();
638  $this->errors = $this->errors();
639  $this->db->rollback();
640  return -1;
641  }
642  } else {
643  $this->error = $this->line->error;
644  $this->errors = $this->line->errors;
645  $this->db->rollback();
646  return -2;
647  }
648  } else {
649  $this->error = 'BadStatusOfObjectToAddLine';
650  return -5;
651  }
652  }
653 
654 
681  public function updateline($rowid, $pu, $qty, $remise_percent, $txtva, $txlocaltax1 = 0, $txlocaltax2 = 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, $array_options = 0, $ref_supplier = '', $fk_unit = '', $pu_ht_devise = 0)
682  {
683  global $conf, $user, $langs, $mysoc;
684 
685  dol_syslog(get_class($this)."::updateLine $rowid, $pu, $qty, $remise_percent, $txtva, $desc, $price_base_type, $info_bits");
686  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
687 
688  // Clean parameters
689  $remise_percent = price2num($remise_percent);
690  $qty = price2num($qty);
691  $pu = price2num($pu);
692  if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
693  $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
694  }
695  $txlocaltax1 = price2num($txlocaltax1);
696  $txlocaltax2 = price2num($txlocaltax2);
697  $pa_ht = price2num($pa_ht);
698  if (empty($qty) && empty($special_code)) {
699  $special_code = 3; // Set option tag
700  }
701  if (!empty($qty) && $special_code == 3) {
702  $special_code = 0; // Remove option tag
703  }
704 
705  if ($this->statut == 0) {
706  $this->db->begin();
707 
708  // Calcul du total TTC et de la TVA pour la ligne a partir de
709  // qty, pu, remise_percent et txtva
710  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
711  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
712 
713  $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
714 
715  // Clean vat code
716  $reg = array();
717  $vat_src_code = '';
718  if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
719  $vat_src_code = $reg[1];
720  $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
721  }
722 
723  if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
724  $pu = 0;
725  }
726 
727  $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
728  $total_ht = $tabprice[0];
729  $total_tva = $tabprice[1];
730  $total_ttc = $tabprice[2];
731  $total_localtax1 = $tabprice[9];
732  $total_localtax2 = $tabprice[10];
733  $pu_ht = $tabprice[3];
734  $pu_tva = $tabprice[4];
735  $pu_ttc = $tabprice[5];
736 
737  // MultiCurrency
738  $multicurrency_total_ht = $tabprice[16];
739  $multicurrency_total_tva = $tabprice[17];
740  $multicurrency_total_ttc = $tabprice[18];
741  $pu_ht_devise = $tabprice[19];
742 
743  $pu = $pu_ht;
744  if ($price_base_type == 'TTC') {
745  $pu = $pu_ttc;
746  }
747 
748  //Fetch current line from the database and then clone the object and set it in $oldline property
749  $line = new SupplierProposalLine($this->db);
750  $line->fetch($rowid);
751  $line->fetch_optionals();
752 
753  // Stock previous line records
754  $staticline = clone $line;
755 
756  $line->oldline = $staticline;
757  $this->line = $line;
758  $this->line->context = $this->context;
759 
760  // Reorder if fk_parent_line change
761  if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
762  $rangmax = $this->line_max($fk_parent_line);
763  $this->line->rang = $rangmax + 1;
764  }
765 
766  $this->line->id = $rowid;
767  $this->line->label = $label;
768  $this->line->desc = $desc;
769  $this->line->qty = $qty;
770  $this->line->product_type = $type;
771 
772  $this->line->vat_src_code = $vat_src_code;
773  $this->line->tva_tx = $txtva;
774  $this->line->localtax1_tx = $txlocaltax1;
775  $this->line->localtax2_tx = $txlocaltax2;
776  $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
777  $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
778  $this->line->remise_percent = $remise_percent;
779  $this->line->subprice = $pu;
780  $this->line->info_bits = $info_bits;
781  $this->line->total_ht = $total_ht;
782  $this->line->total_tva = $total_tva;
783  $this->line->total_localtax1 = $total_localtax1;
784  $this->line->total_localtax2 = $total_localtax2;
785  $this->line->total_ttc = $total_ttc;
786  $this->line->special_code = $special_code;
787  $this->line->fk_parent_line = $fk_parent_line;
788  $this->line->skip_update_total = $skip_update_total;
789  $this->line->ref_fourn = $ref_supplier;
790  $this->line->fk_unit = $fk_unit;
791 
792  // infos marge
793  if (!empty($fk_product) && $fk_product > 0 && empty($fk_fournprice) && empty($pa_ht)) {
794  // by external module, take lowest buying price
795  include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
796  $productFournisseur = new ProductFournisseur($this->db);
797  $productFournisseur->find_min_price_product_fournisseur($fk_product);
798  $this->line->fk_fournprice = $productFournisseur->product_fourn_price_id;
799  } else {
800  $this->line->fk_fournprice = $fk_fournprice;
801  }
802  $this->line->pa_ht = $pa_ht;
803 
804  if (is_array($array_options) && count($array_options) > 0) {
805  // We replace values in this->line->array_options only for entries defined into $array_options
806  foreach ($array_options as $key => $value) {
807  $this->line->array_options[$key] = $array_options[$key];
808  }
809  }
810 
811  // Multicurrency
812  $this->line->multicurrency_subprice = $pu_ht_devise;
813  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
814  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
815  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
816 
817  $result = $this->line->update();
818  if ($result > 0) {
819  // Reorder if child line
820  if (!empty($fk_parent_line)) {
821  $this->line_order(true, 'DESC');
822  }
823 
824  $this->update_price(1);
825 
826  $this->fk_supplier_proposal = $this->id;
827 
828  $this->db->commit();
829  return $result;
830  } else {
831  $this->error = $this->db->error();
832  $this->db->rollback();
833  return -1;
834  }
835  } else {
836  dol_syslog(get_class($this)."::updateline Erreur -2 SupplierProposal en mode incompatible pour cette action");
837  return -2;
838  }
839  }
840 
841 
848  public function deleteline($lineid)
849  {
850  if ($this->statut == 0) {
851  $line = new SupplierProposalLine($this->db);
852 
853  // For triggers
854  $line->fetch($lineid);
855 
856  if ($line->delete() > 0) {
857  $this->update_price(1);
858 
859  return 1;
860  } else {
861  return -1;
862  }
863  } else {
864  return -2;
865  }
866  }
867 
868 
877  public function create($user, $notrigger = 0)
878  {
879  global $langs, $conf, $mysoc, $hookmanager;
880  $error = 0;
881 
882  $now = dol_now();
883 
884  dol_syslog(get_class($this)."::create");
885 
886  // Check parameters
887  $result = $this->fetch_thirdparty();
888  if ($result < 0) {
889  $this->error = "Failed to fetch company";
890  dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
891  return -3;
892  }
893  if (!empty($this->ref)) { // We check that ref is not already used
894  $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
895  if ($result > 0) {
896  $this->error = 'ErrorRefAlreadyExists';
897  dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
898  $this->db->rollback();
899  return -1;
900  }
901  }
902 
903  // Set tmp vars
904  $delivery_date = empty($this->delivery_date) ? $this->date_livraison : $this->delivery_date;
905 
906  // Multicurrency
907  if (!empty($this->multicurrency_code)) {
908  list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $now);
909  }
910  if (empty($this->fk_multicurrency)) {
911  $this->multicurrency_code = $conf->currency;
912  $this->fk_multicurrency = 0;
913  $this->multicurrency_tx = 1;
914  }
915 
916  $this->db->begin();
917 
918  // Insert into database
919  $sql = "INSERT INTO ".MAIN_DB_PREFIX."supplier_proposal (";
920  $sql .= "fk_soc";
921  $sql .= ", price";
922  $sql .= ", remise";
923  $sql .= ", remise_percent";
924  $sql .= ", remise_absolue";
925  $sql .= ", total_tva";
926  $sql .= ", total_ttc";
927  $sql .= ", datec";
928  $sql .= ", ref";
929  $sql .= ", fk_user_author";
930  $sql .= ", note_private";
931  $sql .= ", note_public";
932  $sql .= ", model_pdf";
933  $sql .= ", fk_cond_reglement";
934  $sql .= ", fk_mode_reglement";
935  $sql .= ", fk_account";
936  $sql .= ", date_livraison";
937  $sql .= ", fk_shipping_method";
938  $sql .= ", fk_projet";
939  $sql .= ", entity";
940  $sql .= ", fk_multicurrency";
941  $sql .= ", multicurrency_code";
942  $sql .= ", multicurrency_tx";
943  $sql .= ") ";
944  $sql .= " VALUES (";
945  $sql .= ((int) $this->socid);
946  $sql .= ", 0";
947  $sql .= ", ".((double) $this->remise);
948  $sql .= ", ".($this->remise_percent ? ((double) $this->remise_percent) : 'null');
949  $sql .= ", ".($this->remise_absolue ? ((double) $this->remise_absolue) : 'null');
950  $sql .= ", 0";
951  $sql .= ", 0";
952  $sql .= ", '".$this->db->idate($now)."'";
953  $sql .= ", '(PROV)'";
954  $sql .= ", ".($user->id > 0 ? ((int) $user->id) : "null");
955  $sql .= ", '".$this->db->escape($this->note_private)."'";
956  $sql .= ", '".$this->db->escape($this->note_public)."'";
957  $sql .= ", '".$this->db->escape($this->model_pdf)."'";
958  $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : 'NULL');
959  $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : 'NULL');
960  $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
961  $sql .= ", ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : "null");
962  $sql .= ", ".($this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : 'NULL');
963  $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
964  $sql .= ", ".((int) $conf->entity);
965  $sql .= ", ".((int) $this->fk_multicurrency);
966  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
967  $sql .= ", ".((double) $this->multicurrency_tx);
968  $sql .= ")";
969 
970  dol_syslog(get_class($this)."::create", LOG_DEBUG);
971  $resql = $this->db->query($sql);
972  if ($resql) {
973  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."supplier_proposal");
974 
975  if ($this->id) {
976  $this->ref = '(PROV'.$this->id.')';
977  $sql = 'UPDATE '.MAIN_DB_PREFIX."supplier_proposal SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
978 
979  dol_syslog(get_class($this)."::create", LOG_DEBUG);
980  $resql = $this->db->query($sql);
981  if (!$resql) {
982  $error++;
983  }
984 
985  if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
986  $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
987  }
988 
989  // Add object linked
990  if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
991  foreach ($this->linked_objects as $origin => $tmp_origin_id) {
992  if (is_array($tmp_origin_id)) { // New behaviour, if linked_object can have several links per type, so is something like array('contract'=>array(id1, id2, ...))
993  foreach ($tmp_origin_id as $origin_id) {
994  $ret = $this->add_object_linked($origin, $origin_id);
995  if (!$ret) {
996  dol_print_error($this->db);
997  $error++;
998  }
999  }
1000  }
1001  }
1002  }
1003 
1004  /*
1005  * Insertion du detail des produits dans la base
1006  */
1007  if (!$error) {
1008  $fk_parent_line = 0;
1009  $num = count($this->lines);
1010 
1011  for ($i = 0; $i < $num; $i++) {
1012  // Reset fk_parent_line for no child products and special product
1013  if (($this->lines[$i]->product_type != 9 && empty($this->lines[$i]->fk_parent_line)) || $this->lines[$i]->product_type == 9) {
1014  $fk_parent_line = 0;
1015  }
1016 
1017  $result = $this->addline(
1018  $this->lines[$i]->desc,
1019  $this->lines[$i]->subprice,
1020  $this->lines[$i]->qty,
1021  $this->lines[$i]->tva_tx,
1022  $this->lines[$i]->localtax1_tx,
1023  $this->lines[$i]->localtax2_tx,
1024  $this->lines[$i]->fk_product,
1025  $this->lines[$i]->remise_percent,
1026  'HT',
1027  0,
1028  0,
1029  $this->lines[$i]->product_type,
1030  $this->lines[$i]->rang,
1031  $this->lines[$i]->special_code,
1032  $fk_parent_line,
1033  $this->lines[$i]->fk_fournprice,
1034  $this->lines[$i]->pa_ht,
1035  empty($this->lines[$i]->label) ? '' : $this->lines[$i]->label, // deprecated
1036  $this->lines[$i]->array_options,
1037  $this->lines[$i]->ref_fourn,
1038  $this->lines[$i]->fk_unit,
1039  'supplier_proposal',
1040  $this->lines[$i]->rowid
1041  );
1042 
1043  if ($result < 0) {
1044  $error++;
1045  $this->error = $this->db->error;
1046  dol_print_error($this->db);
1047  break;
1048  }
1049  // Defined the new fk_parent_line
1050  if ($result > 0 && $this->lines[$i]->product_type == 9) {
1051  $fk_parent_line = $result;
1052  }
1053  }
1054  }
1055 
1056  if (!$error) {
1057  // Mise a jour infos denormalisees
1058  $resql = $this->update_price(1);
1059  if ($resql) {
1060  $action = 'update';
1061 
1062  // Actions on extra fields
1063  if (!$error) {
1064  $result = $this->insertExtraFields();
1065  if ($result < 0) {
1066  $error++;
1067  }
1068  }
1069 
1070  if (!$error && !$notrigger) {
1071  // Call trigger
1072  $result = $this->call_trigger('PROPOSAL_SUPPLIER_CREATE', $user);
1073  if ($result < 0) {
1074  $error++;
1075  }
1076  // End call triggers
1077  }
1078  } else {
1079  $this->error = $this->db->lasterror();
1080  $error++;
1081  }
1082  }
1083  } else {
1084  $this->error = $this->db->lasterror();
1085  $error++;
1086  }
1087 
1088  if (!$error) {
1089  $this->db->commit();
1090  dol_syslog(get_class($this)."::create done id=".$this->id);
1091  return $this->id;
1092  } else {
1093  $this->db->rollback();
1094  return -2;
1095  }
1096  } else {
1097  $this->error = $this->db->lasterror();
1098  $this->db->rollback();
1099  return -1;
1100  }
1101  }
1102 
1110  public function createFromClone(User $user, $fromid = 0)
1111  {
1112  global $conf, $hookmanager;
1113 
1114  $error = 0;
1115  $now = dol_now();
1116 
1117  $this->db->begin();
1118 
1119  // get extrafields so they will be clone
1120  foreach ($this->lines as $line) {
1121  $line->fetch_optionals();
1122  }
1123 
1124  // Load source object
1125  $objFrom = clone $this;
1126 
1127  $objsoc = new Societe($this->db);
1128 
1129  // Change socid if needed
1130  if (!empty($fromid) && $fromid != $this->socid) {
1131  if ($objsoc->fetch($fromid) > 0) {
1132  $this->socid = $objsoc->id;
1133  $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1134  $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1135  $this->fk_project = '';
1136  }
1137 
1138  // TODO Change product price if multi-prices
1139  } else {
1140  $objsoc->fetch($this->socid);
1141  }
1142 
1143  $this->id = 0;
1144  $this->statut = 0;
1145 
1146  if (empty($conf->global->SUPPLIER_PROPOSAL_ADDON) || !is_readable(DOL_DOCUMENT_ROOT."/core/modules/supplier_proposal/".$conf->global->SUPPLIER_PROPOSAL_ADDON.".php")) {
1147  $this->error = 'ErrorSetupNotComplete';
1148  return -1;
1149  }
1150 
1151  // Clear fields
1152  $this->user_author = $user->id; // deprecated
1153  $this->user_author_id = $user->id;
1154  $this->user_valid = 0; // deprecated
1155  $this->user_valid_id = 0;
1156  $this->date = $now;
1157 
1158  // Set ref
1159  require_once DOL_DOCUMENT_ROOT."/core/modules/supplier_proposal/".$conf->global->SUPPLIER_PROPOSAL_ADDON.'.php';
1160  $obj = $conf->global->SUPPLIER_PROPOSAL_ADDON;
1161  $modSupplierProposal = new $obj;
1162  $this->ref = $modSupplierProposal->getNextValue($objsoc, $this);
1163 
1164  // Create clone
1165  $this->context['createfromclone'] = 'createfromclone';
1166  $result = $this->create($user);
1167  if ($result < 0) {
1168  $error++;
1169  }
1170 
1171  if (!$error) {
1172  // Hook of thirdparty module
1173  if (is_object($hookmanager)) {
1174  $parameters = array('objFrom'=>$objFrom);
1175  $action = '';
1176  $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1177  if ($reshook < 0) {
1178  $this->errors += $hookmanager->errors;
1179  $this->error = $hookmanager->error;
1180  $error++;
1181  }
1182  }
1183  }
1184 
1185  unset($this->context['createfromclone']);
1186 
1187  // End
1188  if (!$error) {
1189  $this->db->commit();
1190  return $this->id;
1191  } else {
1192  $this->db->rollback();
1193  return -1;
1194  }
1195  }
1196 
1204  public function fetch($rowid, $ref = '')
1205  {
1206  global $conf;
1207 
1208  $sql = "SELECT p.rowid, p.entity, p.ref, p.remise, p.remise_percent, p.remise_absolue, p.fk_soc";
1209  $sql .= ", p.total_ttc, p.total_tva, p.localtax1, p.localtax2, p.total_ht";
1210  $sql .= ", p.datec";
1211  $sql .= ", p.date_valid as datev";
1212  $sql .= ", p.date_livraison as delivery_date";
1213  $sql .= ", p.model_pdf, p.extraparams";
1214  $sql .= ", p.note_private, p.note_public";
1215  $sql .= ", p.fk_projet as fk_project, p.fk_statut";
1216  $sql .= ", p.fk_user_author, p.fk_user_valid, p.fk_user_cloture";
1217  $sql .= ", p.fk_cond_reglement";
1218  $sql .= ", p.fk_mode_reglement";
1219  $sql .= ', p.fk_account';
1220  $sql .= ", p.fk_shipping_method";
1221  $sql .= ", p.fk_multicurrency, p.multicurrency_code, p.multicurrency_tx, p.multicurrency_total_ht, p.multicurrency_total_tva, p.multicurrency_total_ttc";
1222  $sql .= ", c.label as statut_label";
1223  $sql .= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc";
1224  $sql .= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement";
1225  $sql .= " FROM ".MAIN_DB_PREFIX."c_propalst as c, ".MAIN_DB_PREFIX."supplier_proposal as p";
1226  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id';
1227  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON p.fk_cond_reglement = cr.rowid';
1228  $sql .= " WHERE p.fk_statut = c.id";
1229  $sql .= " AND p.entity IN (".getEntity('supplier_proposal').")";
1230  if ($ref) {
1231  $sql .= " AND p.ref = '".$this->db->escape($ref)."'";
1232  } else {
1233  $sql .= " AND p.rowid = ".((int) $rowid);
1234  }
1235 
1236  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1237  $resql = $this->db->query($sql);
1238  if ($resql) {
1239  if ($this->db->num_rows($resql)) {
1240  $obj = $this->db->fetch_object($resql);
1241 
1242  $this->id = $obj->rowid;
1243  $this->entity = $obj->entity;
1244 
1245  $this->ref = $obj->ref;
1246  $this->remise = $obj->remise;
1247  $this->remise_percent = $obj->remise_percent;
1248  $this->remise_absolue = $obj->remise_absolue;
1249  $this->total_ht = $obj->total_ht;
1250  $this->total_tva = $obj->total_tva;
1251  $this->total_localtax1 = $obj->localtax1;
1252  $this->total_localtax2 = $obj->localtax2;
1253  $this->total_ttc = $obj->total_ttc;
1254  $this->socid = $obj->fk_soc;
1255  $this->fk_project = $obj->fk_project;
1256  $this->model_pdf = $obj->model_pdf;
1257  $this->modelpdf = $obj->model_pdf; // deprecated
1258  $this->note = $obj->note_private; // TODO deprecated
1259  $this->note_private = $obj->note_private;
1260  $this->note_public = $obj->note_public;
1261  $this->statut = (int) $obj->fk_statut;
1262  $this->status = (int) $obj->fk_statut;
1263  $this->statut_libelle = $obj->statut_label;
1264  $this->datec = $this->db->jdate($obj->datec); // TODO deprecated
1265  $this->datev = $this->db->jdate($obj->datev); // TODO deprecated
1266  $this->date_creation = $this->db->jdate($obj->datec); // Creation date
1267  $this->date = $this->date_creation;
1268  $this->date_validation = $this->db->jdate($obj->datev); // Validation date
1269  $this->date_livraison = $this->db->jdate($obj->delivery_date); // deprecated
1270  $this->delivery_date = $this->db->jdate($obj->delivery_date);
1271  $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1272 
1273  $this->mode_reglement_id = $obj->fk_mode_reglement;
1274  $this->mode_reglement_code = $obj->mode_reglement_code;
1275  $this->mode_reglement = $obj->mode_reglement;
1276  $this->fk_account = ($obj->fk_account > 0) ? $obj->fk_account : null;
1277  $this->cond_reglement_id = $obj->fk_cond_reglement;
1278  $this->cond_reglement_code = $obj->cond_reglement_code;
1279  $this->cond_reglement = $obj->cond_reglement;
1280  $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1281 
1282  $this->extraparams = (array) json_decode($obj->extraparams, true);
1283 
1284  $this->user_author_id = $obj->fk_user_author;
1285  $this->user_valid_id = $obj->fk_user_valid;
1286  $this->user_close_id = $obj->fk_user_cloture;
1287 
1288  // Multicurrency
1289  $this->fk_multicurrency = $obj->fk_multicurrency;
1290  $this->multicurrency_code = $obj->multicurrency_code;
1291  $this->multicurrency_tx = $obj->multicurrency_tx;
1292  $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1293  $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1294  $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1295 
1296  if ($obj->fk_statut == 0) {
1297  $this->brouillon = 1;
1298  }
1299 
1300  // Retrieve all extrafield
1301  // fetch optionals attributes and labels
1302  $this->fetch_optionals();
1303 
1304  $this->db->free($resql);
1305 
1306  $this->lines = array();
1307 
1308  // Lines of supplier proposals
1309  $sql = "SELECT d.rowid, d.fk_supplier_proposal, d.fk_parent_line, d.label as custom_label, d.description, d.price, d.tva_tx, d.localtax1_tx, d.localtax2_tx, d.qty, d.fk_remise_except, d.remise_percent, d.subprice, d.fk_product,";
1310  $sql .= " d.info_bits, d.total_ht, d.total_tva, d.total_localtax1, d.total_localtax2, d.total_ttc, d.fk_product_fournisseur_price as fk_fournprice, d.buy_price_ht as pa_ht, d.special_code, d.rang, d.product_type,";
1311  $sql .= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label,';
1312  $sql .= ' d.ref_fourn as ref_produit_fourn,';
1313  $sql .= ' d.fk_multicurrency, d.multicurrency_code, d.multicurrency_subprice, d.multicurrency_total_ht, d.multicurrency_total_tva, d.multicurrency_total_ttc, d.fk_unit';
1314  $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposaldet as d";
1315  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON d.fk_product = p.rowid";
1316  $sql .= " WHERE d.fk_supplier_proposal = ".((int) $this->id);
1317  $sql .= " ORDER by d.rang";
1318 
1319  $result = $this->db->query($sql);
1320  if ($result) {
1321  $num = $this->db->num_rows($result);
1322  $i = 0;
1323 
1324  while ($i < $num) {
1325  $objp = $this->db->fetch_object($result);
1326 
1327  $line = new SupplierProposalLine($this->db);
1328 
1329  $line->rowid = $objp->rowid; // deprecated
1330  $line->id = $objp->rowid;
1331  $line->fk_supplier_proposal = $objp->fk_supplier_proposal;
1332  $line->fk_parent_line = $objp->fk_parent_line;
1333  $line->product_type = $objp->product_type;
1334  $line->label = $objp->custom_label;
1335  $line->desc = $objp->description; // Description ligne
1336  $line->qty = $objp->qty;
1337  $line->tva_tx = $objp->tva_tx;
1338  $line->localtax1_tx = $objp->localtax1_tx;
1339  $line->localtax2_tx = $objp->localtax2_tx;
1340  $line->subprice = $objp->subprice;
1341  $line->fk_remise_except = $objp->fk_remise_except;
1342  $line->remise_percent = $objp->remise_percent;
1343 
1344  $line->info_bits = $objp->info_bits;
1345  $line->total_ht = $objp->total_ht;
1346  $line->total_tva = $objp->total_tva;
1347  $line->total_localtax1 = $objp->total_localtax1;
1348  $line->total_localtax2 = $objp->total_localtax2;
1349  $line->total_ttc = $objp->total_ttc;
1350  $line->fk_fournprice = $objp->fk_fournprice;
1351  $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1352  $line->pa_ht = $marginInfos[0];
1353  $line->marge_tx = $marginInfos[1];
1354  $line->marque_tx = $marginInfos[2];
1355  $line->special_code = $objp->special_code;
1356  $line->rang = $objp->rang;
1357 
1358  $line->fk_product = $objp->fk_product;
1359 
1360  $line->ref = $objp->product_ref; // deprecated
1361  $line->product_ref = $objp->product_ref;
1362  $line->libelle = $objp->product_label; // deprecated
1363  $line->product_label = $objp->product_label;
1364  $line->product_desc = $objp->product_desc; // Description produit
1365  $line->fk_product_type = $objp->fk_product_type;
1366 
1367  $line->ref_fourn = $objp->ref_produit_fourn;
1368 
1369  // Multicurrency
1370  $line->fk_multicurrency = $objp->fk_multicurrency;
1371  $line->multicurrency_code = $objp->multicurrency_code;
1372  $line->multicurrency_subprice = $objp->multicurrency_subprice;
1373  $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
1374  $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
1375  $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
1376  $line->fk_unit = $objp->fk_unit;
1377 
1378  $this->lines[$i] = $line;
1379 
1380  $i++;
1381  }
1382  $this->db->free($result);
1383  } else {
1384  $this->error = $this->db->error();
1385  return -1;
1386  }
1387 
1388  // Retrieve all extrafield
1389  // fetch optionals attributes and labels
1390  $this->fetch_optionals();
1391 
1392  return 1;
1393  }
1394 
1395  $this->error = "Record Not Found";
1396  return 0;
1397  } else {
1398  $this->error = $this->db->error();
1399  return -1;
1400  }
1401  }
1402 
1410  public function valid($user, $notrigger = 0)
1411  {
1412  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1413 
1414  global $conf, $langs;
1415 
1416  $error = 0;
1417  $now = dol_now();
1418 
1419  if ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->supplier_proposal->creer))
1420  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->supplier_proposal->validate_advance))) {
1421  $this->db->begin();
1422 
1423  // Numbering module definition
1424  $soc = new Societe($this->db);
1425  $result = $soc->fetch($this->socid);
1426 
1427  if ($result < 0) {
1428  return -1;
1429  }
1430 
1431  // Define new ref
1432  if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1433  $num = $this->getNextNumRef($soc);
1434  } else {
1435  $num = $this->ref;
1436  }
1437  $this->newref = dol_sanitizeFileName($num);
1438 
1439  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1440  $sql .= " SET ref = '".$this->db->escape($num)."',";
1441  $sql .= " fk_statut = 1, date_valid='".$this->db->idate($now)."', fk_user_valid=".((int) $user->id);
1442  $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1443 
1444  dol_syslog(get_class($this)."::valid", LOG_DEBUG);
1445  $resql = $this->db->query($sql);
1446  if (!$resql) {
1447  dol_print_error($this->db);
1448  $error++;
1449  }
1450 
1451  // Trigger calls
1452  if (!$error && !$notrigger) {
1453  // Call trigger
1454  $result = $this->call_trigger('PROPOSAL_SUPPLIER_VALIDATE', $user);
1455  if ($result < 0) {
1456  $error++;
1457  }
1458  // End call triggers
1459  }
1460 
1461  if (!$error) {
1462  $this->oldref = $this->ref;
1463 
1464  // Rename directory if dir was a temporary ref
1465  if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1466  // Now we rename also files into index
1467  $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'supplier_proposal/".$this->db->escape($this->newref)."'";
1468  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1469  $resql = $this->db->query($sql);
1470  if (!$resql) {
1471  $error++; $this->error = $this->db->lasterror();
1472  }
1473 
1474  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1475  $oldref = dol_sanitizeFileName($this->ref);
1476  $newref = dol_sanitizeFileName($num);
1477  $dirsource = $conf->supplier_proposal->dir_output.'/'.$oldref;
1478  $dirdest = $conf->supplier_proposal->dir_output.'/'.$newref;
1479  if (!$error && file_exists($dirsource)) {
1480  dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
1481  if (@rename($dirsource, $dirdest)) {
1482  dol_syslog("Rename ok");
1483  // Rename docs starting with $oldref with $newref
1484  $listoffiles = dol_dir_list($conf->supplier_proposal->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1485  foreach ($listoffiles as $fileentry) {
1486  $dirsource = $fileentry['name'];
1487  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1488  $dirsource = $fileentry['path'].'/'.$dirsource;
1489  $dirdest = $fileentry['path'].'/'.$dirdest;
1490  @rename($dirsource, $dirdest);
1491  }
1492  }
1493  }
1494  }
1495 
1496  $this->ref = $num;
1497  $this->brouillon = 0;
1498  $this->statut = 1;
1499  $this->user_valid_id = $user->id;
1500  $this->datev = $now;
1501 
1502  $this->db->commit();
1503  return 1;
1504  } else {
1505  $this->db->rollback();
1506  return -1;
1507  }
1508  } else {
1509  dol_syslog("You don't have permission to validate supplier proposal", LOG_WARNING);
1510  return -2;
1511  }
1512  }
1513 
1514  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1523  public function set_date_livraison($user, $delivery_date)
1524  {
1525  // phpcs:enable
1526  return $this->setDeliveryDate($user, $delivery_date);
1527  }
1528 
1536  public function setDeliveryDate($user, $delivery_date)
1537  {
1538  if (!empty($user->rights->supplier_proposal->creer)) {
1539  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1540  $sql .= " SET date_livraison = ".($delivery_date != '' ? "'".$this->db->idate($delivery_date)."'" : 'null');
1541  $sql .= " WHERE rowid = ".((int) $this->id);
1542 
1543  if ($this->db->query($sql)) {
1544  $this->date_livraison = $delivery_date;
1545  $this->delivery_date = $delivery_date;
1546  return 1;
1547  } else {
1548  $this->error = $this->db->error();
1549  dol_syslog(get_class($this)."::setDeliveryDate Erreur SQL");
1550  return -1;
1551  }
1552  }
1553  return 0;
1554  }
1555 
1556  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1564  public function set_remise_percent($user, $remise)
1565  {
1566  // phpcs:enable
1567  $remise = trim($remise) ?trim($remise) : 0;
1568 
1569  if (!empty($user->rights->supplier_proposal->creer)) {
1570  $remise = price2num($remise, 2);
1571 
1572  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal SET remise_percent = ".((float) $remise);
1573  $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1574 
1575  if ($this->db->query($sql)) {
1576  $this->remise_percent = ((float) $remise);
1577  $this->update_price(1);
1578  return 1;
1579  } else {
1580  $this->error = $this->db->error();
1581  return -1;
1582  }
1583  }
1584  return 0;
1585  }
1586 
1587 
1588  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1596  public function set_remise_absolue($user, $remise)
1597  {
1598  // phpcs:enable
1599  if (empty($remise)) {
1600  $remise = 0;
1601  }
1602 
1603  $remise = price2num($remise);
1604 
1605  if (!empty($user->rights->supplier_proposal->creer)) {
1606  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1607  $sql .= " SET remise_absolue = ".((float) $remise);
1608  $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1609 
1610  if ($this->db->query($sql)) {
1611  $this->remise_absolue = $remise;
1612  $this->update_price(1);
1613  return 1;
1614  } else {
1615  $this->error = $this->db->error();
1616  return -1;
1617  }
1618  }
1619  return 0;
1620  }
1621 
1622 
1623 
1633  public function reopen($user, $statut, $note = '', $notrigger = 0)
1634  {
1635  global $langs, $conf;
1636 
1637  $this->statut = $statut;
1638  $error = 0;
1639 
1640  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1641  $sql .= " SET fk_statut = ".((int) $this->statut).",";
1642  if (!empty($note)) {
1643  $sql .= " note_private = '".$this->db->escape($note)."',";
1644  }
1645  $sql .= " date_cloture=NULL, fk_user_cloture=NULL";
1646  $sql .= " WHERE rowid = ".((int) $this->id);
1647 
1648  $this->db->begin();
1649 
1650  dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
1651  $resql = $this->db->query($sql);
1652  if (!$resql) {
1653  $error++; $this->errors[] = "Error ".$this->db->lasterror();
1654  }
1655  if (!$error) {
1656  if (!$notrigger) {
1657  // Call trigger
1658  $result = $this->call_trigger('PROPOSAL_SUPPLIER_REOPEN', $user);
1659  if ($result < 0) {
1660  $error++;
1661  }
1662  // End call triggers
1663  }
1664  }
1665 
1666  // Commit or rollback
1667  if ($error) {
1668  if (!empty($this->errors)) {
1669  foreach ($this->errors as $errmsg) {
1670  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1671  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1672  }
1673  }
1674  $this->db->rollback();
1675  return -1 * $error;
1676  } else {
1677  $this->db->commit();
1678  return 1;
1679  }
1680  }
1681 
1682 
1691  public function cloture($user, $status, $note)
1692  {
1693  global $langs, $conf;
1694  $hidedetails = 0;
1695  $hidedesc = 0;
1696  $hideref = 0;
1697  $this->statut = $status;
1698  $error = 0;
1699  $now = dol_now();
1700 
1701  $this->db->begin();
1702 
1703  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1704  $sql .= " SET fk_statut = ".((int) $status).", note_private = '".$this->db->escape($note)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".$user->id;
1705  $sql .= " WHERE rowid = ".((int) $this->id);
1706 
1707  $resql = $this->db->query($sql);
1708  if ($resql) {
1709  $modelpdf = $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED ? $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED : (empty($this->model_pdf) ? '' : $this->model_pdf);
1710  $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_REFUSED';
1711 
1712  if ($status == 2) {
1713  $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_SIGNED';
1714  $modelpdf = $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL ? $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL : (empty($this->model_pdf) ? '' : $this->model_pdf);
1715 
1716  if (!empty($conf->global->SUPPLIER_PROPOSAL_UPDATE_PRICE_ON_SUPPlIER_PROPOSAL)) { // TODO This option was not tested correctly. Error if product ref does not exists
1717  $result = $this->updateOrCreatePriceFournisseur($user);
1718  }
1719  }
1720  if ($status == 4) {
1721  $triggerName = 'PROPOSAL_SUPPLIER_CLASSIFY_BILLED';
1722  }
1723 
1724  if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE)) {
1725  // Define output language
1726  $outputlangs = $langs;
1727  if (getDolGlobalInt('MAIN_MULTILANGS')) {
1728  $outputlangs = new Translate("", $conf);
1729  $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
1730  $outputlangs->setDefaultLang($newlang);
1731  }
1732  //$ret=$object->fetch($id); // Reload to get new records
1733  $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1734  }
1735 
1736  // Call trigger
1737  $result = $this->call_trigger($triggerName, $user);
1738  if ($result < 0) {
1739  $error++;
1740  }
1741  // End call triggers
1742 
1743  if (!$error) {
1744  $this->db->commit();
1745  return 1;
1746  } else {
1747  $this->db->rollback();
1748  return -1;
1749  }
1750  } else {
1751  $this->error = $this->db->lasterror();
1752  $this->errors[] = $this->db->lasterror();
1753  $this->db->rollback();
1754  return -1;
1755  }
1756  }
1757 
1764  public function updateOrCreatePriceFournisseur($user)
1765  {
1766  global $conf;
1767 
1768  dol_syslog(get_class($this)."::updateOrCreatePriceFournisseur", LOG_DEBUG);
1769  foreach ($this->lines as $product) {
1770  if ($product->subprice <= 0) {
1771  continue;
1772  }
1773  $productsupplier = new ProductFournisseur($this->db);
1774 
1775  $multicurrency_tx = 1;
1776  $fk_multicurrency = 0;
1777 
1778  if (empty($this->thirdparty)) {
1779  $this->fetch_thirdparty();
1780  }
1781 
1782  $ref_fourn = $product->ref_fourn;
1783  if (empty($ref_fourn)) {
1784  $ref_fourn = $product->ref_supplier;
1785  }
1786  if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1787  list($fk_multicurrency, $multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $product->multicurrency_code);
1788  }
1789  $productsupplier->id = $product->fk_product;
1790 
1791  $productsupplier->update_buyprice($product->qty, $product->total_ht, $user, 'HT', $this->thirdparty, '', $ref_fourn, $product->tva_tx, 0, 0, 0, $product->info_bits, '', '', array(), '', $product->multicurrency_total_ht, 'HT', $multicurrency_tx, $product->multicurrency_code, '', '', '');
1792  }
1793 
1794  return 1;
1795  }
1796 
1805  public function updatePriceFournisseur($idProductFournPrice, $product, $user)
1806  {
1807  $price = price2num($product->subprice * $product->qty, 'MU');
1808  $unitPrice = price2num($product->subprice, 'MU');
1809 
1810  $sql = 'UPDATE '.MAIN_DB_PREFIX.'product_fournisseur_price SET '.(!empty($product->ref_fourn) ? 'ref_fourn = "'.$this->db->escape($product->ref_fourn).'", ' : '').' price ='.((float) $price).', unitprice ='.((float) $unitPrice).' WHERE rowid = '.((int) $idProductFournPrice);
1811 
1812  $resql = $this->db->query($sql);
1813  if (!$resql) {
1814  $this->error = $this->db->error();
1815  $this->db->rollback();
1816  return -1;
1817  }
1818  return 1;
1819  }
1820 
1828  public function createPriceFournisseur($product, $user)
1829  {
1830  global $conf;
1831 
1832  $price = price2num($product->subprice * $product->qty, 'MU');
1833  $qty = price2num($product->qty);
1834  $unitPrice = price2num($product->subprice, 'MU');
1835 
1836  $now = dol_now();
1837 
1838  $values = array(
1839  "'".$this->db->idate($now)."'",
1840  $product->fk_product,
1841  $this->thirdparty->id,
1842  "'".$product->ref_fourn."'",
1843  $price,
1844  $qty,
1845  $unitPrice,
1846  $product->tva_tx,
1847  $user->id
1848  );
1849  if (isModEnabled("multicurrency")) {
1850  if (!empty($product->multicurrency_code)) {
1851  include_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
1852  $multicurrency = new MultiCurrency($this->db); //need to fetch because empty fk_multicurrency and rate
1853  $multicurrency->fetch(0, $product->multicurrency_code);
1854  if (!empty($multicurrency->id)) {
1855  $values[] = $multicurrency->id;
1856  $values[] = "'".$product->multicurrency_code."'";
1857  $values[] = $product->multicurrency_subprice;
1858  $values[] = $product->multicurrency_total_ht;
1859  $values[] = $multicurrency->rate->rate;
1860  } else {
1861  for ($i = 0; $i < 5; $i++) {
1862  $values[] = 'NULL';
1863  }
1864  }
1865  }
1866  }
1867 
1868  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'product_fournisseur_price ';
1869  $sql .= '(datec, fk_product, fk_soc, ref_fourn, price, quantity, unitprice, tva_tx, fk_user';
1870  if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1871  $sql .= ',fk_multicurrency, multicurrency_code, multicurrency_unitprice, multicurrency_price, multicurrency_tx';
1872  }
1873  $sql .= ') VALUES ('.implode(',', $values).')';
1874 
1875  $resql = $this->db->query($sql);
1876  if (!$resql) {
1877  $this->error = $this->db->error();
1878  $this->db->rollback();
1879  return -1;
1880  }
1881  return 1;
1882  }
1883 
1884  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1891  public function setDraft($user)
1892  {
1893  // phpcs:enable
1894  global $conf, $langs;
1895 
1896  $error = 0;
1897 
1898  if ($this->statut == self::STATUS_DRAFT) {
1899  dol_syslog(get_class($this)."::setDraft already draft status", LOG_WARNING);
1900  return 0;
1901  }
1902 
1903  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1904  $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1905  $sql .= " WHERE rowid = ".((int) $this->id);
1906 
1907  if ($this->db->query($sql)) {
1908  if (!$error) {
1909  $this->oldcopy = clone $this;
1910  }
1911 
1912  if (!$error) {
1913  // Call trigger
1914  $result = $this->call_trigger('PROPOSAL_SUPPLIER_UNVALIDATE', $user);
1915  if ($result < 0) {
1916  $error++;
1917  }
1918  }
1919 
1920  if (!$error) {
1921  $this->statut = self::STATUS_DRAFT;
1922  $this->brouillon = 1;
1923  $this->db->commit();
1924  return 1;
1925  } else {
1926  $this->db->rollback();
1927  return -1;
1928  }
1929  } else {
1930  return -1;
1931  }
1932  }
1933 
1934 
1935  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1949  public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datec', $sortorder = 'DESC')
1950  {
1951  // phpcs:enable
1952  global $conf, $user;
1953 
1954  $ga = array();
1955 
1956  $sql = "SELECT s.rowid, s.nom as name, s.client,";
1957  $sql .= " p.rowid as supplier_proposalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
1958  $sql .= " p.datep as dp, p.fin_validite as datelimite";
1959  if (empty($user->rights->societe->client->voir) && !$socid) {
1960  $sql .= ", sc.fk_soc, sc.fk_user";
1961  }
1962  $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."supplier_proposal as p, ".MAIN_DB_PREFIX."c_propalst as c";
1963  if (empty($user->rights->societe->client->voir) && !$socid) {
1964  $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
1965  }
1966  $sql .= " WHERE p.entity IN (".getEntity('supplier_proposal').")";
1967  $sql .= " AND p.fk_soc = s.rowid";
1968  $sql .= " AND p.fk_statut = c.id";
1969  if (empty($user->rights->societe->client->voir) && !$socid) { //restriction
1970  $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
1971  }
1972  if ($socid) {
1973  $sql .= " AND s.rowid = ".((int) $socid);
1974  }
1975  if ($draft) {
1976  $sql .= " AND p.fk_statut = 0";
1977  }
1978  if ($notcurrentuser > 0) {
1979  $sql .= " AND p.fk_user_author <> ".$user->id;
1980  }
1981  $sql .= $this->db->order($sortfield, $sortorder);
1982  $sql .= $this->db->plimit($limit, $offset);
1983 
1984  $result = $this->db->query($sql);
1985  if ($result) {
1986  $num = $this->db->num_rows($result);
1987  if ($num) {
1988  $i = 0;
1989  while ($i < $num) {
1990  $obj = $this->db->fetch_object($result);
1991 
1992  if ($shortlist == 1) {
1993  $ga[$obj->supplier_proposalid] = $obj->ref;
1994  } elseif ($shortlist == 2) {
1995  $ga[$obj->supplier_proposalid] = $obj->ref.' ('.$obj->name.')';
1996  } else {
1997  $ga[$i]['id'] = $obj->supplier_proposalid;
1998  $ga[$i]['ref'] = $obj->ref;
1999  $ga[$i]['name'] = $obj->name;
2000  }
2001 
2002  $i++;
2003  }
2004  }
2005  return $ga;
2006  } else {
2007  dol_print_error($this->db);
2008  return -1;
2009  }
2010  }
2011 
2019  public function delete($user, $notrigger = 0)
2020  {
2021  global $conf, $langs;
2022  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2023 
2024  $error = 0;
2025 
2026  $this->db->begin();
2027 
2028  if (!$notrigger) {
2029  // Call trigger
2030  $result = $this->call_trigger('PROPOSAL_SUPPLIER_DELETE', $user);
2031  if ($result < 0) {
2032  $error++;
2033  }
2034  // End call triggers
2035  }
2036 
2037  if (!$error) {
2038  $main = MAIN_DB_PREFIX.'supplier_proposaldet';
2039  $ef = $main."_extrafields";
2040  $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_supplier_proposal = ".((int) $this->id).")";
2041  $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet WHERE fk_supplier_proposal = ".((int) $this->id);
2042  if ($this->db->query($sql)) {
2043  $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposal WHERE rowid = ".((int) $this->id);
2044  if ($this->db->query($sqlef) && $this->db->query($sql)) {
2045  // Delete linked object
2046  $res = $this->deleteObjectLinked();
2047  if ($res < 0) {
2048  $error++;
2049  }
2050 
2051  if (!$error) {
2052  // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2053  $this->deleteEcmFiles();
2054 
2055  // We remove directory
2056  $ref = dol_sanitizeFileName($this->ref);
2057  if ($conf->supplier_proposal->dir_output && !empty($this->ref)) {
2058  $dir = $conf->supplier_proposal->dir_output."/".$ref;
2059  $file = $dir."/".$ref.".pdf";
2060  if (file_exists($file)) {
2061  dol_delete_preview($this);
2062 
2063  if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2064  $this->error = 'ErrorFailToDeleteFile';
2065  $this->errors = array('ErrorFailToDeleteFile');
2066  $this->db->rollback();
2067  return 0;
2068  }
2069  }
2070  if (file_exists($dir)) {
2071  $res = @dol_delete_dir_recursive($dir);
2072  if (!$res) {
2073  $this->error = 'ErrorFailToDeleteDir';
2074  $this->errors = array('ErrorFailToDeleteDir');
2075  $this->db->rollback();
2076  return 0;
2077  }
2078  }
2079  }
2080  }
2081 
2082  // Removed extrafields
2083  if (!$error) {
2084  $result = $this->deleteExtraFields();
2085  if ($result < 0) {
2086  $error++;
2087  $errorflag = -4;
2088  dol_syslog(get_class($this)."::delete erreur ".$errorflag." ".$this->error, LOG_ERR);
2089  }
2090  }
2091 
2092  if (!$error) {
2093  dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
2094  $this->db->commit();
2095  return 1;
2096  } else {
2097  $this->error = $this->db->lasterror();
2098  $this->db->rollback();
2099  return 0;
2100  }
2101  } else {
2102  $this->error = $this->db->lasterror();
2103  $this->db->rollback();
2104  return -3;
2105  }
2106  } else {
2107  $this->error = $this->db->lasterror();
2108  $this->db->rollback();
2109  return -2;
2110  }
2111  } else {
2112  $this->db->rollback();
2113  return -1;
2114  }
2115  }
2116 
2123  public function info($id)
2124  {
2125  $sql = "SELECT c.rowid, ";
2126  $sql .= " c.datec, c.date_valid as datev, c.date_cloture as dateo,";
2127  $sql .= " c.fk_user_author, c.fk_user_valid, c.fk_user_cloture";
2128  $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as c";
2129  $sql .= " WHERE c.rowid = ".((int) $id);
2130 
2131  $result = $this->db->query($sql);
2132 
2133  if ($result) {
2134  if ($this->db->num_rows($result)) {
2135  $obj = $this->db->fetch_object($result);
2136 
2137  $this->id = $obj->rowid;
2138 
2139  $this->date_creation = $this->db->jdate($obj->datec);
2140  $this->date_validation = $this->db->jdate($obj->datev);
2141  $this->date_cloture = $this->db->jdate($obj->dateo);
2142 
2143  $cuser = new User($this->db);
2144  $cuser->fetch($obj->fk_user_author);
2145  $this->user_creation = $cuser;
2146 
2147  if ($obj->fk_user_valid) {
2148  $vuser = new User($this->db);
2149  $vuser->fetch($obj->fk_user_valid);
2150  $this->user_validation = $vuser;
2151  }
2152 
2153  if ($obj->fk_user_cloture) {
2154  $cluser = new User($this->db);
2155  $cluser->fetch($obj->fk_user_cloture);
2156  $this->user_cloture = $cluser;
2157  }
2158  }
2159  $this->db->free($result);
2160  } else {
2161  dol_print_error($this->db);
2162  }
2163  }
2164 
2165 
2172  public function getLibStatut($mode = 0)
2173  {
2174  return $this->LibStatut((isset($this->statut) ? $this->statut : $this->status), $mode);
2175  }
2176 
2177  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2185  public function LibStatut($status, $mode = 1)
2186  {
2187  // phpcs:enable
2188 
2189  // Init/load array of translation of status
2190  if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
2191  global $langs;
2192  $langs->load("supplier_proposal");
2193  $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraft");
2194  $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidated");
2195  $this->labelStatus[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSigned");
2196  $this->labelStatus[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSigned");
2197  $this->labelStatus[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosed");
2198  $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraftShort");
2199  $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidatedShort");
2200  $this->labelStatusShort[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSignedShort");
2201  $this->labelStatusShort[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSignedShort");
2202  $this->labelStatusShort[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosedShort");
2203  }
2204 
2205  $statusnew = '';
2206  if ($status == self::STATUS_DRAFT) {
2207  $statusnew = 'status0';
2208  } elseif ($status == self::STATUS_VALIDATED) {
2209  $statusnew = 'status1';
2210  } elseif ($status == self::STATUS_SIGNED) {
2211  $statusnew = 'status4';
2212  } elseif ($status == self::STATUS_NOTSIGNED) {
2213  $statusnew = 'status9';
2214  } elseif ($status == self::STATUS_CLOSE) {
2215  $statusnew = 'status6';
2216  }
2217 
2218  return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusnew, $mode);
2219  }
2220 
2221 
2222  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2230  public function load_board($user, $mode)
2231  {
2232  // phpcs:enable
2233  global $conf, $user, $langs;
2234 
2235  $now = dol_now();
2236 
2237  $this->nbtodo = $this->nbtodolate = 0;
2238  $clause = " WHERE";
2239 
2240  $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.date_cloture as datefin";
2241  $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2242  if (empty($user->rights->societe->client->voir) && !$user->socid) {
2243  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
2244  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2245  $clause = " AND";
2246  }
2247  $sql .= $clause." p.entity IN (".getEntity('supplier_proposal').")";
2248  if ($mode == 'opened') {
2249  $sql .= " AND p.fk_statut = 1";
2250  }
2251  if ($mode == 'signed') {
2252  $sql .= " AND p.fk_statut = 2";
2253  }
2254  if ($user->socid) {
2255  $sql .= " AND p.fk_soc = ".((int) $user->socid);
2256  }
2257 
2258  $resql = $this->db->query($sql);
2259  if ($resql) {
2260  $label = $labelShort = '';
2261  $status = '';
2262  if ($mode == 'opened') {
2263  $delay_warning = !empty($conf->supplier_proposal->cloture->warning_delay) ? $conf->supplier_proposal->cloture->warning_delay : 0;
2264  $status = self::STATUS_VALIDATED;
2265  $label = $langs->trans("SupplierProposalsToClose");
2266  $labelShort = $langs->trans("ToAcceptRefuse");
2267  }
2268  if ($mode == 'signed') {
2269  $delay_warning = !empty($conf->supplier_proposal->facturation->warning_delay) ? $conf->supplier_proposal->facturation->warning_delay : 0;
2270  $status = self::STATUS_SIGNED;
2271  $label = $langs->trans("SupplierProposalsToProcess"); // May be billed or ordered
2272  $labelShort = $langs->trans("ToClose");
2273  }
2274 
2275  $response = new WorkboardResponse();
2276  $response->warning_delay = $delay_warning / 60 / 60 / 24;
2277  $response->label = $label;
2278  $response->labelShort = $labelShort;
2279  $response->url = DOL_URL_ROOT.'/supplier_proposal/list.php?search_status='.$status;
2280  $response->img = img_object('', "propal");
2281 
2282  // This assignment in condition is not a bug. It allows walking the results.
2283  while ($obj = $this->db->fetch_object($resql)) {
2284  $response->nbtodo++;
2285  if ($mode == 'opened') {
2286  $datelimit = $this->db->jdate($obj->datefin);
2287  if ($datelimit < ($now - $delay_warning)) {
2288  $response->nbtodolate++;
2289  }
2290  }
2291  // TODO Definir regle des propales a facturer en retard
2292  // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
2293  }
2294  return $response;
2295  } else {
2296  $this->error = $this->db->lasterror();
2297  return -1;
2298  }
2299  }
2300 
2301 
2309  public function initAsSpecimen()
2310  {
2311  global $user, $langs, $conf;
2312 
2313  // Load array of products prodids
2314  $num_prods = 0;
2315  $prodids = array();
2316  $sql = "SELECT rowid";
2317  $sql .= " FROM ".MAIN_DB_PREFIX."product";
2318  $sql .= " WHERE entity IN (".getEntity('product').")";
2319  $sql .= $this->db->plimit(100);
2320 
2321  $resql = $this->db->query($sql);
2322  if ($resql) {
2323  $num_prods = $this->db->num_rows($resql);
2324  $i = 0;
2325  while ($i < $num_prods) {
2326  $i++;
2327  $row = $this->db->fetch_row($resql);
2328  $prodids[$i] = $row[0];
2329  }
2330  }
2331 
2332  // Initialise parametres
2333  $this->id = 0;
2334  $this->ref = 'SPECIMEN';
2335  $this->specimen = 1;
2336  $this->socid = 1;
2337  $this->date = time();
2338  $this->cond_reglement_id = 1;
2339  $this->cond_reglement_code = 'RECEP';
2340  $this->mode_reglement_id = 7;
2341  $this->mode_reglement_code = 'CHQ';
2342  $this->note_public = 'This is a comment (public)';
2343  $this->note_private = 'This is a comment (private)';
2344  // Lines
2345  $nbp = 5;
2346  $xnbp = 0;
2347  while ($xnbp < $nbp) {
2348  $line = new SupplierProposalLine($this->db);
2349  $line->desc = $langs->trans("Description")." ".$xnbp;
2350  $line->qty = 1;
2351  $line->subprice = 100;
2352  $line->tva_tx = 19.6;
2353  $line->localtax1_tx = 0;
2354  $line->localtax2_tx = 0;
2355  if ($xnbp == 2) {
2356  $line->total_ht = 50;
2357  $line->total_ttc = 59.8;
2358  $line->total_tva = 9.8;
2359  $line->remise_percent = 50;
2360  } else {
2361  $line->total_ht = 100;
2362  $line->total_ttc = 119.6;
2363  $line->total_tva = 19.6;
2364  $line->remise_percent = 00;
2365  }
2366 
2367  if ($num_prods > 0) {
2368  $prodid = mt_rand(1, $num_prods);
2369  $line->fk_product = $prodids[$prodid];
2370  }
2371 
2372  $this->lines[$xnbp] = $line;
2373 
2374  $this->total_ht += $line->total_ht;
2375  $this->total_tva += $line->total_tva;
2376  $this->total_ttc += $line->total_ttc;
2377 
2378  $xnbp++;
2379  }
2380  }
2381 
2382  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2388  public function load_state_board()
2389  {
2390  // phpcs:enable
2391  global $conf, $user;
2392 
2393  $this->nb = array();
2394  $clause = "WHERE";
2395 
2396  $sql = "SELECT count(p.rowid) as nb";
2397  $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2398  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
2399  if (empty($user->rights->societe->client->voir) && !$user->socid) {
2400  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2401  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2402  $clause = "AND";
2403  }
2404  $sql .= " ".$clause." p.entity IN (".getEntity('supplier_proposal').")";
2405 
2406  $resql = $this->db->query($sql);
2407  if ($resql) {
2408  // This assignment in condition is not a bug. It allows walking the results.
2409  while ($obj = $this->db->fetch_object($resql)) {
2410  $this->nb["supplier_proposals"] = $obj->nb;
2411  }
2412  $this->db->free($resql);
2413  return 1;
2414  } else {
2415  dol_print_error($this->db);
2416  $this->error = $this->db->lasterror();
2417  return -1;
2418  }
2419  }
2420 
2421 
2429  public function getNextNumRef($soc)
2430  {
2431  global $conf, $db, $langs;
2432  $langs->load("supplier_proposal");
2433 
2434  if (!empty($conf->global->SUPPLIER_PROPOSAL_ADDON)) {
2435  $mybool = false;
2436 
2437  $file = $conf->global->SUPPLIER_PROPOSAL_ADDON.".php";
2438  $classname = $conf->global->SUPPLIER_PROPOSAL_ADDON;
2439 
2440  // Include file with class
2441  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2442  foreach ($dirmodels as $reldir) {
2443  $dir = dol_buildpath($reldir."core/modules/supplier_proposal/");
2444 
2445  // Load file with numbering class (if found)
2446  $mybool |= @include_once $dir.$file;
2447  }
2448 
2449  if (!$mybool) {
2450  dol_print_error('', "Failed to include file ".$file);
2451  return '';
2452  }
2453 
2454  $obj = new $classname();
2455  $numref = "";
2456  $numref = $obj->getNextValue($soc, $this);
2457 
2458  if ($numref != "") {
2459  return $numref;
2460  } else {
2461  $this->error = $obj->error;
2462  return "";
2463  }
2464  } else {
2465  $langs->load("errors");
2466  print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("SupplierProposal"));
2467  return "";
2468  }
2469  }
2470 
2482  public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2483  {
2484  global $langs, $conf, $user, $hookmanager;
2485 
2486  if (!empty($conf->dol_no_mouse_hover)) {
2487  $notooltip = 1; // Force disable tooltips
2488  }
2489 
2490  $url = '';
2491  $result = '';
2492 
2493  $label = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("SupplierProposal").'</u>';
2494  if (isset($this->status)) {
2495  $label .= ' '.$this->getLibStatut(5);
2496  }
2497  if (!empty($this->ref)) {
2498  $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2499  }
2500  if (!empty($this->ref_fourn)) {
2501  $label .= '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_fourn;
2502  }
2503  if (!empty($this->total_ht)) {
2504  $label .= '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2505  }
2506  if (!empty($this->total_tva)) {
2507  $label .= '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2508  }
2509  if (!empty($this->total_ttc)) {
2510  $label .= '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2511  }
2512 
2513  if ($option == '') {
2514  $url = DOL_URL_ROOT.'/supplier_proposal/card.php?id='.$this->id.$get_params;
2515  }
2516  if ($option == 'document') {
2517  $url = DOL_URL_ROOT.'/supplier_proposal/document.php?id='.$this->id.$get_params;
2518  }
2519 
2520  if ($option !== 'nolink') {
2521  // Add param to save lastsearch_values or not
2522  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2523  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2524  $add_save_lastsearch_values = 1;
2525  }
2526  if ($add_save_lastsearch_values) {
2527  $url .= '&save_lastsearch_values=1';
2528  }
2529  }
2530 
2531  $linkclose = '';
2532  if (empty($notooltip) && $user->rights->propal->lire) {
2533  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
2534  $label = $langs->trans("ShowSupplierProposal");
2535  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
2536  }
2537  $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
2538  $linkclose .= ' class="classfortooltip"';
2539  }
2540 
2541  $linkstart = '<a href="'.$url.'"';
2542  $linkstart .= $linkclose.'>';
2543  $linkend = '</a>';
2544 
2545  $result .= $linkstart;
2546  if ($withpicto) {
2547  $result .= img_object(($notooltip ? '' : $label), $this->picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
2548  }
2549  if ($withpicto != 2) {
2550  $result .= $this->ref;
2551  }
2552  $result .= $linkend;
2553 
2554  if ($addlinktonotes) {
2555  $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2556  if ($txttoshow) {
2557  $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2558  $result .= ' <span class="note inline-block">';
2559  $result .= '<a href="'.DOL_URL_ROOT.'/supplier_proposal/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2560  $result .= img_picto('', 'note');
2561  $result .= '</a>';
2562  //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
2563  //$result.='</a>';
2564  $result .= '</span>';
2565  }
2566  }
2567  global $action;
2568  $hookmanager->initHooks(array($this->element . 'dao'));
2569  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
2570  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2571  if ($reshook > 0) {
2572  $result = $hookmanager->resPrint;
2573  } else {
2574  $result .= $hookmanager->resPrint;
2575  }
2576  return $result;
2577  }
2578 
2584  public function getLinesArray()
2585  {
2586  // For other object, here we call fetch_lines. But fetch_lines does not exists on supplier proposal
2587 
2588  $sql = 'SELECT pt.rowid, pt.label as custom_label, pt.description, pt.fk_product, pt.fk_remise_except,';
2589  $sql .= ' pt.qty, pt.tva_tx, pt.vat_src_code, pt.remise_percent, pt.subprice, pt.info_bits,';
2590  $sql .= ' pt.total_ht, pt.total_tva, pt.total_ttc, pt.fk_product_fournisseur_price as fk_fournprice, pt.buy_price_ht as pa_ht, pt.special_code, pt.localtax1_tx, pt.localtax2_tx,';
2591  $sql .= ' pt.product_type, pt.rang, pt.fk_parent_line,';
2592  $sql .= ' p.label as product_label, p.ref, p.fk_product_type, p.rowid as prodid,';
2593  $sql .= ' p.description as product_desc, pt.ref_fourn as ref_supplier,';
2594  $sql .= ' pt.fk_multicurrency, pt.multicurrency_code, pt.multicurrency_subprice, pt.multicurrency_total_ht, pt.multicurrency_total_tva, pt.multicurrency_total_ttc, pt.fk_unit';
2595  $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pt';
2596  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pt.fk_product=p.rowid';
2597  $sql .= ' WHERE pt.fk_supplier_proposal = '.((int) $this->id);
2598  $sql .= ' ORDER BY pt.rang ASC, pt.rowid';
2599 
2600  dol_syslog(get_class($this).'::getLinesArray', LOG_DEBUG);
2601  $resql = $this->db->query($sql);
2602  if ($resql) {
2603  $num = $this->db->num_rows($resql);
2604  $i = 0;
2605 
2606  while ($i < $num) {
2607  $obj = $this->db->fetch_object($resql);
2608 
2609  $this->lines[$i] = new SupplierProposalLine($this->db);
2610  $this->lines[$i]->id = $obj->rowid; // for backward compatibility
2611  $this->lines[$i]->rowid = $obj->rowid;
2612  $this->lines[$i]->label = $obj->custom_label;
2613  $this->lines[$i]->description = $obj->description;
2614  $this->lines[$i]->fk_product = $obj->fk_product;
2615  $this->lines[$i]->ref = $obj->ref;
2616  $this->lines[$i]->product_label = $obj->product_label;
2617  $this->lines[$i]->product_desc = $obj->product_desc;
2618  $this->lines[$i]->fk_product_type = $obj->fk_product_type; // deprecated
2619  $this->lines[$i]->product_type = $obj->product_type;
2620  $this->lines[$i]->qty = $obj->qty;
2621  $this->lines[$i]->subprice = $obj->subprice;
2622  $this->lines[$i]->fk_remise_except = $obj->fk_remise_except;
2623  $this->lines[$i]->remise_percent = $obj->remise_percent;
2624  $this->lines[$i]->tva_tx = $obj->tva_tx;
2625  $this->lines[$i]->vat_src_code = $obj->vat_src_code;
2626  $this->lines[$i]->info_bits = $obj->info_bits;
2627  $this->lines[$i]->total_ht = $obj->total_ht;
2628  $this->lines[$i]->total_tva = $obj->total_tva;
2629  $this->lines[$i]->total_ttc = $obj->total_ttc;
2630  $this->lines[$i]->fk_fournprice = $obj->fk_fournprice;
2631  $marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->lines[$i]->fk_fournprice, $obj->pa_ht);
2632  $this->lines[$i]->pa_ht = $marginInfos[0];
2633  $this->lines[$i]->marge_tx = $marginInfos[1];
2634  $this->lines[$i]->marque_tx = $marginInfos[2];
2635  $this->lines[$i]->fk_parent_line = $obj->fk_parent_line;
2636  $this->lines[$i]->special_code = $obj->special_code;
2637  $this->lines[$i]->rang = $obj->rang;
2638 
2639  $this->lines[$i]->ref_fourn = $obj->ref_supplier; // deprecated
2640  $this->lines[$i]->ref_supplier = $obj->ref_supplier;
2641 
2642  // Multicurrency
2643  $this->lines[$i]->fk_multicurrency = $obj->fk_multicurrency;
2644  $this->lines[$i]->multicurrency_code = $obj->multicurrency_code;
2645  $this->lines[$i]->multicurrency_subprice = $obj->multicurrency_subprice;
2646  $this->lines[$i]->multicurrency_total_ht = $obj->multicurrency_total_ht;
2647  $this->lines[$i]->multicurrency_total_tva = $obj->multicurrency_total_tva;
2648  $this->lines[$i]->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
2649  $this->lines[$i]->fk_unit = $obj->fk_unit;
2650 
2651  $i++;
2652  }
2653  $this->db->free($resql);
2654 
2655  return 1;
2656  } else {
2657  $this->error = $this->db->error();
2658  return -1;
2659  }
2660  }
2661 
2673  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2674  {
2675  global $conf, $langs;
2676 
2677  $langs->load("supplier_proposal");
2678  $outputlangs->load("products");
2679 
2680  if (!dol_strlen($modele)) {
2681  $modele = 'aurore';
2682 
2683  if ($this->model_pdf) {
2684  $modele = $this->model_pdf;
2685  } elseif (!empty($conf->global->SUPPLIER_PROPOSAL_ADDON_PDF)) {
2686  $modele = $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF;
2687  }
2688  }
2689 
2690  $modelpath = "core/modules/supplier_proposal/doc/";
2691 
2692  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2693  }
2694 
2695 
2704  public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
2705  {
2706  $tables = array(
2707  'supplier_proposal'
2708  );
2709 
2710  return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
2711  }
2712 
2721  public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
2722  {
2723  $tables = array(
2724  'supplier_proposaldet'
2725  );
2726 
2727  return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
2728  }
2729 }
2730 
2731 
2736 {
2740  public $db;
2741 
2745  public $error = '';
2746 
2750  public $element = 'supplier_proposaldet';
2751 
2755  public $table_element = 'supplier_proposaldet';
2756 
2757  public $oldline;
2758 
2762  public $id;
2763 
2767  public $fk_supplier_proposal;
2768 
2772  public $fk_parent_line;
2773 
2774  public $desc; // Description ligne
2775 
2779  public $fk_product; // Id produit predefini
2780 
2791  public $product_type = Product::TYPE_PRODUCT;
2792 
2793  public $qty;
2794  public $tva_tx;
2795  public $vat_src_code;
2796 
2797  public $subprice;
2798  public $remise_percent;
2799 
2803  public $fk_remise_except;
2804 
2805  public $rang = 0;
2806 
2810  public $fk_fournprice;
2811 
2812  public $pa_ht;
2813  public $marge_tx;
2814  public $marque_tx;
2815 
2816  public $special_code; // Tag for special lines (exlusive tags)
2817  // 1: frais de port
2818  // 2: ecotaxe
2819  // 3: option line (when qty = 0)
2820 
2821  public $info_bits = 0; // Liste d'options cumulables:
2822  // Bit 0: 0 si TVA normal - 1 si TVA NPR
2823  // Bit 1: 0 ligne normale - 1 si ligne de remise fixe
2824 
2825  public $total_ht; // Total HT de la ligne toute quantite et incluant la remise ligne
2826  public $total_tva; // Total TVA de la ligne toute quantite et incluant la remise ligne
2827  public $total_ttc; // Total TTC de la ligne toute quantite et incluant la remise ligne
2828 
2829  public $date_start;
2830  public $date_end;
2831 
2832  // From llx_product
2837  public $ref;
2838 
2843  public $product_ref;
2844 
2849  public $libelle;
2850 
2855  public $product_label;
2856 
2861  public $product_desc;
2862 
2863  public $localtax1_tx; // Local tax 1
2864  public $localtax2_tx; // Local tax 2
2865  public $localtax1_type; // Local tax 1 type
2866  public $localtax2_type; // Local tax 2 type
2867  public $total_localtax1; // Line total local tax 1
2868  public $total_localtax2; // Line total local tax 2
2869 
2870  public $skip_update_total; // Skip update price total for special lines
2871 
2872  public $ref_fourn;
2873  public $ref_supplier;
2874 
2875  // Multicurrency
2879  public $fk_multicurrency;
2880 
2881  public $multicurrency_code;
2882  public $multicurrency_subprice;
2883  public $multicurrency_total_ht;
2884  public $multicurrency_total_tva;
2885  public $multicurrency_total_ttc;
2886 
2892  public function __construct($db)
2893  {
2894  $this->db = $db;
2895  }
2896 
2903  public function fetch($rowid)
2904  {
2905  $sql = 'SELECT pd.rowid, pd.fk_supplier_proposal, pd.fk_parent_line, pd.fk_product, pd.label as custom_label, pd.description, pd.price, pd.qty, pd.tva_tx,';
2906  $sql .= ' pd.date_start, pd.date_end,';
2907  $sql .= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice,';
2908  $sql .= ' pd.info_bits, pd.total_ht, pd.total_tva, pd.total_ttc, pd.fk_product_fournisseur_price as fk_fournprice, pd.buy_price_ht as pa_ht, pd.special_code, pd.rang,';
2909  $sql .= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
2910  $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
2911  $sql .= ' pd.product_type, pd.ref_fourn as ref_produit_fourn,';
2912  $sql .= ' pd.fk_multicurrency, pd.multicurrency_code, pd.multicurrency_subprice, pd.multicurrency_total_ht, pd.multicurrency_total_tva, pd.multicurrency_total_ttc, pd.fk_unit';
2913  $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pd';
2914  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
2915  $sql .= ' WHERE pd.rowid = '.((int) $rowid);
2916 
2917  $result = $this->db->query($sql);
2918  if ($result) {
2919  $objp = $this->db->fetch_object($result);
2920 
2921  $this->id = $objp->rowid;
2922  $this->fk_supplier_proposal = $objp->fk_supplier_proposal;
2923  $this->fk_parent_line = $objp->fk_parent_line;
2924  $this->label = $objp->custom_label;
2925  $this->desc = $objp->description;
2926  $this->qty = $objp->qty;
2927  $this->subprice = $objp->subprice;
2928  $this->tva_tx = $objp->tva_tx;
2929  $this->remise_percent = $objp->remise_percent;
2930  $this->fk_remise_except = $objp->fk_remise_except;
2931  $this->fk_product = $objp->fk_product;
2932  $this->info_bits = $objp->info_bits;
2933  $this->date_start = $this->db->jdate($objp->date_start);
2934  $this->date_end = $this->db->jdate($objp->date_end);
2935 
2936  $this->total_ht = $objp->total_ht;
2937  $this->total_tva = $objp->total_tva;
2938  $this->total_ttc = $objp->total_ttc;
2939 
2940  $this->fk_fournprice = $objp->fk_fournprice;
2941 
2942  $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
2943  $this->pa_ht = $marginInfos[0];
2944  $this->marge_tx = $marginInfos[1];
2945  $this->marque_tx = $marginInfos[2];
2946 
2947  $this->special_code = $objp->special_code;
2948  $this->product_type = $objp->product_type;
2949  $this->rang = $objp->rang;
2950 
2951  $this->ref = $objp->product_ref; // deprecated
2952  $this->product_ref = $objp->product_ref;
2953  $this->libelle = $objp->product_label; // deprecated
2954  $this->product_label = $objp->product_label;
2955  $this->product_desc = $objp->product_desc;
2956 
2957  $this->ref_fourn = $objp->ref_produit_fourn;
2958 
2959  // Multicurrency
2960  $this->fk_multicurrency = $objp->fk_multicurrency;
2961  $this->multicurrency_code = $objp->multicurrency_code;
2962  $this->multicurrency_subprice = $objp->multicurrency_subprice;
2963  $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
2964  $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
2965  $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
2966  $this->fk_unit = $objp->fk_unit;
2967 
2968  $this->db->free($result);
2969  return 1;
2970  } else {
2971  dol_print_error($this->db);
2972  return -1;
2973  }
2974  }
2975 
2982  public function insert($notrigger = 0)
2983  {
2984  global $conf, $langs, $user;
2985 
2986  $error = 0;
2987 
2988  dol_syslog(get_class($this)."::insert rang=".$this->rang);
2989 
2990  // Clean parameters
2991  if (empty($this->tva_tx)) {
2992  $this->tva_tx = 0;
2993  }
2994  if (empty($this->vat_src_code)) {
2995  $this->vat_src_code = '';
2996  }
2997  if (empty($this->localtax1_tx)) {
2998  $this->localtax1_tx = 0;
2999  }
3000  if (empty($this->localtax2_tx)) {
3001  $this->localtax2_tx = 0;
3002  }
3003  if (empty($this->localtax1_type)) {
3004  $this->localtax1_type = 0;
3005  }
3006  if (empty($this->localtax2_type)) {
3007  $this->localtax2_type = 0;
3008  }
3009  if (empty($this->total_localtax1)) {
3010  $this->total_localtax1 = 0;
3011  }
3012  if (empty($this->total_localtax2)) {
3013  $this->total_localtax2 = 0;
3014  }
3015  if (empty($this->rang)) {
3016  $this->rang = 0;
3017  }
3018  if (empty($this->remise_percent)) {
3019  $this->remise_percent = 0;
3020  }
3021  if (empty($this->info_bits)) {
3022  $this->info_bits = 0;
3023  }
3024  if (empty($this->special_code)) {
3025  $this->special_code = 0;
3026  }
3027  if (empty($this->fk_parent_line)) {
3028  $this->fk_parent_line = 0;
3029  }
3030  if (empty($this->fk_fournprice)) {
3031  $this->fk_fournprice = 0;
3032  }
3033  if (empty($this->fk_unit)) {
3034  $this->fk_unit = 0;
3035  }
3036  if (empty($this->subprice)) {
3037  $this->subprice = 0;
3038  }
3039 
3040  if (empty($this->pa_ht)) {
3041  $this->pa_ht = 0;
3042  }
3043 
3044  // if buy price not defined, define buyprice as configured in margin admin
3045  if ($this->pa_ht == 0) {
3046  $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3047  if ($result < 0) {
3048  return $result;
3049  } else {
3050  $this->pa_ht = $result;
3051  }
3052  }
3053 
3054  // Check parameters
3055  if ($this->product_type < 0) {
3056  return -1;
3057  }
3058 
3059  $this->db->begin();
3060 
3061  // Insert line into database
3062  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'supplier_proposaldet';
3063  $sql .= ' (fk_supplier_proposal, fk_parent_line, label, description, fk_product, product_type,';
3064  $sql .= ' date_start, date_end,';
3065  $sql .= ' fk_remise_except, qty, tva_tx, vat_src_code, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3066  $sql .= ' subprice, remise_percent, ';
3067  $sql .= ' info_bits, ';
3068  $sql .= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
3069  $sql .= ' ref_fourn,';
3070  $sql .= ' fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc, fk_unit)';
3071  $sql .= " VALUES (".$this->fk_supplier_proposal.",";
3072  $sql .= " ".($this->fk_parent_line > 0 ? ((int) $this->fk_parent_line) : "null").",";
3073  $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
3074  $sql .= " '".$this->db->escape($this->desc)."',";
3075  $sql .= " ".($this->fk_product ? ((int) $this->fk_product) : "null").",";
3076  $sql .= " '".$this->db->escape($this->product_type)."',";
3077  $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3078  $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3079  $sql .= " ".($this->fk_remise_except ? ((int) $this->fk_remise_except) : "null").",";
3080  $sql .= " ".price2num($this->qty, 'MS').",";
3081  $sql .= " ".price2num($this->tva_tx).",";
3082  $sql .= " '".$this->db->escape($this->vat_src_code)."',";
3083  $sql .= " ".price2num($this->localtax1_tx).",";
3084  $sql .= " ".price2num($this->localtax2_tx).",";
3085  $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3086  $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3087  $sql .= " ".price2num($this->subprice, 'MU') .",";
3088  $sql .= " ".((float) $this->remise_percent).",";
3089  $sql .= " ".(isset($this->info_bits) ? ((int) $this->info_bits) : "null").",";
3090  $sql .= " ".price2num($this->total_ht, 'MT').",";
3091  $sql .= " ".price2num($this->total_tva, 'MT').",";
3092  $sql .= " ".price2num($this->total_localtax1, 'MT').",";
3093  $sql .= " ".price2num($this->total_localtax2, 'MT').",";
3094  $sql .= " ".price2num($this->total_ttc, 'MT').",";
3095  $sql .= " ".(!empty($this->fk_fournprice) ? ((int) $this->fk_fournprice) : "null").",";
3096  $sql .= " ".(isset($this->pa_ht) ? price2num($this->pa_ht, 'MU') : "null").",";
3097  $sql .= ' '.((int) $this->special_code).',';
3098  $sql .= ' '.((int) $this->rang).',';
3099  $sql .= " '".$this->db->escape($this->ref_fourn)."'";
3100  $sql .= ", ".($this->fk_multicurrency > 0 ? ((int) $this->fk_multicurrency) : 'null');
3101  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3102  $sql .= ", ".price2num($this->multicurrency_subprice, 'CU');
3103  $sql .= ", ".price2num($this->multicurrency_total_ht, 'CT');
3104  $sql .= ", ".price2num($this->multicurrency_total_tva, 'CT');
3105  $sql .= ", ".price2num($this->multicurrency_total_ttc, 'CT');
3106  $sql .= ", ".($this->fk_unit ? ((int) $this->fk_unit) : 'null');
3107  $sql .= ')';
3108 
3109  dol_syslog(get_class($this).'::insert', LOG_DEBUG);
3110  $resql = $this->db->query($sql);
3111  if ($resql) {
3112  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'supplier_proposaldet');
3113 
3114  if (!$error) {
3115  $result = $this->insertExtraFields();
3116  if ($result < 0) {
3117  $error++;
3118  }
3119  }
3120 
3121  if (!$error && !$notrigger) {
3122  // Call trigger
3123  $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_INSERT', $user);
3124  if ($result < 0) {
3125  $this->db->rollback();
3126  return -1;
3127  }
3128  // End call triggers
3129  }
3130 
3131  $this->db->commit();
3132  return 1;
3133  } else {
3134  $this->error = $this->db->error()." sql=".$sql;
3135  $this->db->rollback();
3136  return -1;
3137  }
3138  }
3139 
3145  public function delete()
3146  {
3147  global $conf, $langs, $user;
3148 
3149  $error = 0;
3150  $this->db->begin();
3151 
3152  $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet WHERE rowid = ".((int) $this->id);
3153  dol_syslog("SupplierProposalLine::delete", LOG_DEBUG);
3154  if ($this->db->query($sql)) {
3155  // Remove extrafields
3156  if (!$error) {
3157  $result = $this->deleteExtraFields();
3158  if ($result < 0) {
3159  $error++;
3160  dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3161  }
3162  }
3163 
3164  // Call trigger
3165  $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_DELETE', $user);
3166  if ($result < 0) {
3167  $this->db->rollback();
3168  return -1;
3169  }
3170  // End call triggers
3171 
3172  $this->db->commit();
3173 
3174  return 1;
3175  } else {
3176  $this->error = $this->db->error()." sql=".$sql;
3177  $this->db->rollback();
3178  return -1;
3179  }
3180  }
3181 
3188  public function update($notrigger = 0)
3189  {
3190  global $conf, $langs, $user;
3191 
3192  $error = 0;
3193 
3194  // Clean parameters
3195  if (empty($this->tva_tx)) {
3196  $this->tva_tx = 0;
3197  }
3198  if (empty($this->localtax1_tx)) {
3199  $this->localtax1_tx = 0;
3200  }
3201  if (empty($this->localtax2_tx)) {
3202  $this->localtax2_tx = 0;
3203  }
3204  if (empty($this->total_localtax1)) {
3205  $this->total_localtax1 = 0;
3206  }
3207  if (empty($this->total_localtax2)) {
3208  $this->total_localtax2 = 0;
3209  }
3210  if (empty($this->localtax1_type)) {
3211  $this->localtax1_type = 0;
3212  }
3213  if (empty($this->localtax2_type)) {
3214  $this->localtax2_type = 0;
3215  }
3216  if (empty($this->marque_tx)) {
3217  $this->marque_tx = 0;
3218  }
3219  if (empty($this->marge_tx)) {
3220  $this->marge_tx = 0;
3221  }
3222  if (empty($this->remise_percent)) {
3223  $this->remise_percent = 0;
3224  }
3225  if (empty($this->info_bits)) {
3226  $this->info_bits = 0;
3227  }
3228  if (empty($this->special_code)) {
3229  $this->special_code = 0;
3230  }
3231  if (empty($this->fk_parent_line)) {
3232  $this->fk_parent_line = 0;
3233  }
3234  if (empty($this->fk_fournprice)) {
3235  $this->fk_fournprice = 0;
3236  }
3237  if (empty($this->fk_unit)) {
3238  $this->fk_unit = 0;
3239  }
3240  if (empty($this->subprice)) {
3241  $this->subprice = 0;
3242  }
3243 
3244  if (empty($this->pa_ht)) {
3245  $this->pa_ht = 0;
3246  }
3247 
3248  // if buy price not defined, define buyprice as configured in margin admin
3249  if ($this->pa_ht == 0) {
3250  $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3251  if ($result < 0) {
3252  return $result;
3253  } else {
3254  $this->pa_ht = $result;
3255  }
3256  }
3257 
3258  $this->db->begin();
3259 
3260  // Mise a jour ligne en base
3261  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3262  $sql .= " description='".$this->db->escape($this->desc)."'";
3263  $sql .= " , label=".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
3264  $sql .= " , product_type=".((int) $this->product_type);
3265  $sql .= " , date_start=".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null");
3266  $sql .= " , date_end=".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null");
3267  $sql .= " , tva_tx='".price2num($this->tva_tx)."'";
3268  $sql .= " , localtax1_tx=".price2num($this->localtax1_tx);
3269  $sql .= " , localtax2_tx=".price2num($this->localtax2_tx);
3270  $sql .= " , localtax1_type='".$this->db->escape($this->localtax1_type)."'";
3271  $sql .= " , localtax2_type='".$this->db->escape($this->localtax2_type)."'";
3272  $sql .= " , qty='".price2num($this->qty)."'";
3273  $sql .= " , subprice=".price2num($this->subprice)."";
3274  $sql .= " , remise_percent=".price2num($this->remise_percent)."";
3275  $sql .= " , info_bits='".$this->db->escape($this->info_bits)."'";
3276  if (empty($this->skip_update_total)) {
3277  $sql .= " , total_ht=".price2num($this->total_ht)."";
3278  $sql .= " , total_tva=".price2num($this->total_tva)."";
3279  $sql .= " , total_ttc=".price2num($this->total_ttc)."";
3280  $sql .= " , total_localtax1=".price2num($this->total_localtax1)."";
3281  $sql .= " , total_localtax2=".price2num($this->total_localtax2)."";
3282  }
3283  $sql .= " , fk_product_fournisseur_price=".(!empty($this->fk_fournprice) ? "'".$this->db->escape($this->fk_fournprice)."'" : "null");
3284  $sql .= " , buy_price_ht=".price2num($this->pa_ht);
3285  if (strlen($this->special_code)) {
3286  $sql .= " , special_code=".((int) $this->special_code);
3287  }
3288  $sql .= " , fk_parent_line=".($this->fk_parent_line > 0 ? $this->fk_parent_line : "null");
3289  if (!empty($this->rang)) {
3290  $sql .= ", rang=".((int) $this->rang);
3291  }
3292  $sql .= " , ref_fourn=".(!empty($this->ref_fourn) ? "'".$this->db->escape($this->ref_fourn)."'" : "null");
3293  $sql .= " , fk_unit=".($this->fk_unit ? $this->fk_unit : 'null');
3294 
3295  // Multicurrency
3296  $sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice)."";
3297  $sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht)."";
3298  $sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva)."";
3299  $sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc)."";
3300 
3301  $sql .= " WHERE rowid = ".((int) $this->id);
3302 
3303  dol_syslog(get_class($this)."::update", LOG_DEBUG);
3304  $resql = $this->db->query($sql);
3305  if ($resql) {
3306  if (!$error) {
3307  $result = $this->insertExtraFields();
3308  if ($result < 0) {
3309  $error++;
3310  }
3311  }
3312 
3313  if (!$error && !$notrigger) {
3314  // Call trigger
3315  $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_MODIFY', $user);
3316  if ($result < 0) {
3317  $this->db->rollback();
3318  return -1;
3319  }
3320  // End call triggers
3321  }
3322 
3323  $this->db->commit();
3324  return 1;
3325  } else {
3326  $this->error = $this->db->error();
3327  $this->db->rollback();
3328  return -2;
3329  }
3330  }
3331 
3332  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3339  public function update_total()
3340  {
3341  // phpcs:enable
3342  $this->db->begin();
3343 
3344  // Mise a jour ligne en base
3345  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3346  $sql .= " total_ht=".price2num($this->total_ht, 'MT');
3347  $sql .= ",total_tva=".price2num($this->total_tva, 'MT');
3348  $sql .= ",total_ttc=".price2num($this->total_ttc, 'MT');
3349  $sql .= " WHERE rowid = ".((int) $this->id);
3350 
3351  dol_syslog("SupplierProposalLine::update_total", LOG_DEBUG);
3352 
3353  $resql = $this->db->query($sql);
3354  if ($resql) {
3355  $this->db->commit();
3356  return 1;
3357  } else {
3358  $this->error = $this->db->error();
3359  $this->db->rollback();
3360  return -2;
3361  }
3362  }
3363 }
$object ref
Definition: info.php:78
Parent class of all other business classes (invoices, contracts, proposals, orders,...
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
Save a new position (field rang) for details lines.
deleteEcmFiles($mode=0)
Delete related files of object in database.
add_object_linked($origin=null, $origin_id=null, $f_user=null, $notrigger=0)
Add an object link into llx_element_element.
defineBuyPrice($unitPrice=0.0, $discountPercent=0.0, $fk_product=0)
Get buy price to use for margin calculation.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty.
static isExistingObject($element, $id, $ref='', $ref_ext='')
Check an object id/ref exists If you don't need/want to instantiate object and just need to know if o...
updateRangOfLine($rowid, $rang)
Update position of line (rang)
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid='', $f_user=null, $notrigger=0)
Delete all links between an object $this.
update_price($exclspec=0, $roundingadjust='none', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
deleteExtraFields()
Delete all extra fields values for the current object.
static commonReplaceProduct(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product id with another one.
static commonReplaceThirdparty(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
line_max($fk_parent_line=0)
Get max value used for position of line (rang)
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
call_trigger($triggerName, $user)
Call trigger based on this instance.
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
Class to manage absolute discounts.
Class to manage Dolibarr database access.
Class Currency.
static getIdAndTxFromCode($dbs, $code, $date_document='')
Get id and rate of currency from code.
Class to manage predefined suppliers products.
Class to manage products or services.
const TYPE_PRODUCT
Regular product.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage price ask supplier.
updatePriceFournisseur($idProductFournPrice, $product, $user)
Upate ProductFournisseur.
cloture($user, $status, $note)
Close the askprice.
set_remise_percent($user, $remise)
Set an overall discount on the proposal.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template module.
deleteline($lineid)
Delete detail line.
info($id)
Object SupplierProposal Information.
create($user, $notrigger=0)
Create commercial proposal into database this->ref can be set or empty.
insert_discount($idremise)
Adding line of fixed discount in the proposal in DB.
static replaceProduct(DoliDB $db, $origin_id, $dest_id)
Function used to replace a product id with another one.
updateline($rowid, $pu, $qty, $remise_percent, $txtva, $txlocaltax1=0, $txlocaltax2=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, $array_options=0, $ref_supplier='', $fk_unit='', $pu_ht_devise=0)
Update a proposal line.
getNextNumRef($soc)
Returns the reference to the following non used Proposal used depending on the active numbering modul...
fetch($rowid, $ref='')
Load a proposal from database and its ligne array.
load_board($user, $mode)
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
createFromClone(User $user, $fromid=0)
Load an object from its id and create a new one in database.
initAsSpecimen()
Initialise an instance with random values.
const STATUS_NOTSIGNED
Not signed quote, canceled.
addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1=0, $txlocaltax2=0, $fk_product=0, $remise_percent=0, $price_base_type='HT', $pu_ttc=0, $info_bits=0, $type=0, $rang=-1, $special_code=0, $fk_parent_line=0, $fk_fournprice=0, $pa_ht=0, $label='', $array_options=0, $ref_supplier='', $fk_unit='', $origin='', $origin_id=0, $pu_ht_devise=0, $date_start=0, $date_end=0)
Add a proposal line into database (linked to product/service or not) Les parametres sont deja cense e...
const STATUS_DRAFT
Draft status.
liste_array($shortlist=0, $draft=0, $notcurrentuser=0, $socid=0, $limit=0, $offset=0, $sortfield='p.datec', $sortorder='DESC')
Return list of askprice (eventually filtered on user) into an array.
setDeliveryDate($user, $delivery_date)
Set delivery date.
LibStatut($status, $mode=1)
Return label of a status (draft, validated, ...)
__construct($db, $socid="", $supplier_proposalid=0)
Constructor.
updateOrCreatePriceFournisseur($user)
Add or update supplier price according to result of proposal.
load_state_board()
Load indicator this->nb of global stats widget.
reopen($user, $statut, $note='', $notrigger=0)
Reopen the commercial proposal.
const STATUS_VALIDATED
Validated status.
createPriceFournisseur($product, $user)
Create ProductFournisseur.
getLibStatut($mode=0)
Return label of status of proposal (draft, validated, ...)
set_remise_absolue($user, $remise)
Set an absolute overall discount on the proposal.
static replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
add_product($idproduct, $qty, $remise_percent=0)
Add line into array ->lines.
set_date_livraison($user, $delivery_date)
Set delivery date.
const STATUS_SIGNED
Signed quote.
getNomUrl($withpicto=0, $option='', $get_params='', $notooltip=0, $save_lastsearch_value=-1, $addlinktonotes=0)
Return clicable link of object (with eventually picto)
valid($user, $notrigger=0)
Set status to validated.
const STATUS_CLOSE
Billed or closed/processed quote.
getLinesArray()
Retrieve an array of supplier proposal lines.
setDraft($user)
Set draft status.
Class to manage supplier_proposal lines.
fetch($rowid)
Retrieve the propal line object.
insert($notrigger=0)
Insert object line propal in database.
update_total()
Update DB line fields total_xxx Used by migration.
update($notrigger=0)
Update propal line object into DB.
__construct($db)
Class line Contructor.
Class to manage translations.
Class to manage Dolibarr users.
Definition: user.class.php:47
trait CommonIncoterm
Superclass for incoterm classes.
if(isModEnabled('facture') &&!empty($user->rights->facture->lire)) if((isModEnabled('fournisseur') &&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') &&!empty($user->rights->don->lire)) if(isModEnabled('tax') &&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture') &&isModEnabled('commande') && $user->hasRight("commande", "lire") &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $resql
Social contributions to pay.
Definition: index.php:745
print *****$script_file(".$version.") pid c cd cd cd description as p label as s rowid
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
Definition: files.lib.php:1402
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1, $nolog=0)
Remove a file or several files with a mask.
Definition: files.lib.php:1251
dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition: files.lib.php:61
dol_delete_preview($object)
Delete all preview files linked to object instance.
Definition: files.lib.php:1454
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0)
Returns text escaped for inclusion in HTML alt or title tags, or into values of HTML input fields.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Fonction qui renvoie si tva doit etre tva percue recuperable.
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
get_localtax($vatrate, $local, $thirdparty_buyer="", $thirdparty_seller="", $vatnpr=0)
Return localtax rate for a particular vat, when selling a product with vat $vatrate,...
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
isModEnabled($module)
Is Dolibarr module enabled.
get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that return vat rate of a product line (according to seller, buyer and product vat rate) VAT...
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
getMarginInfos($pvht, $remise_percent, $tva_tx, $localtax1_tx, $localtax2_tx, $fk_pa, $paht)
Return an array with margins information of a line.
div float
Buy price without taxes.
Definition: style.css.php:913
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller='', $localtaxes_array='', $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition: price.lib.php:86
$conf db
API class for accounts.
Definition: inc.php:41