dolibarr  18.0.6
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-2023 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->setErrorsFromObject($hookmanager);
1179  $error++;
1180  }
1181  }
1182  }
1183 
1184  unset($this->context['createfromclone']);
1185 
1186  // End
1187  if (!$error) {
1188  $this->db->commit();
1189  return $this->id;
1190  } else {
1191  $this->db->rollback();
1192  return -1;
1193  }
1194  }
1195 
1203  public function fetch($rowid, $ref = '')
1204  {
1205  global $conf;
1206 
1207  $sql = "SELECT p.rowid, p.entity, p.ref, p.remise, p.remise_percent, p.remise_absolue, p.fk_soc";
1208  $sql .= ", p.total_ttc, p.total_tva, p.localtax1, p.localtax2, p.total_ht";
1209  $sql .= ", p.datec";
1210  $sql .= ", p.date_valid as datev";
1211  $sql .= ", p.date_livraison as delivery_date";
1212  $sql .= ", p.model_pdf, p.extraparams";
1213  $sql .= ", p.note_private, p.note_public";
1214  $sql .= ", p.fk_projet as fk_project, p.fk_statut";
1215  $sql .= ", p.fk_user_author, p.fk_user_valid, p.fk_user_cloture";
1216  $sql .= ", p.fk_cond_reglement";
1217  $sql .= ", p.fk_mode_reglement";
1218  $sql .= ', p.fk_account';
1219  $sql .= ", p.fk_shipping_method";
1220  $sql .= ", p.fk_multicurrency, p.multicurrency_code, p.multicurrency_tx, p.multicurrency_total_ht, p.multicurrency_total_tva, p.multicurrency_total_ttc";
1221  $sql .= ", c.label as statut_label";
1222  $sql .= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc";
1223  $sql .= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement";
1224  $sql .= " FROM ".MAIN_DB_PREFIX."c_propalst as c, ".MAIN_DB_PREFIX."supplier_proposal as p";
1225  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id';
1226  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON p.fk_cond_reglement = cr.rowid';
1227  $sql .= " WHERE p.fk_statut = c.id";
1228  $sql .= " AND p.entity IN (".getEntity('supplier_proposal').")";
1229  if ($ref) {
1230  $sql .= " AND p.ref = '".$this->db->escape($ref)."'";
1231  } else {
1232  $sql .= " AND p.rowid = ".((int) $rowid);
1233  }
1234 
1235  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1236  $resql = $this->db->query($sql);
1237  if ($resql) {
1238  if ($this->db->num_rows($resql)) {
1239  $obj = $this->db->fetch_object($resql);
1240 
1241  $this->id = $obj->rowid;
1242  $this->entity = $obj->entity;
1243 
1244  $this->ref = $obj->ref;
1245  $this->remise = $obj->remise;
1246  $this->remise_percent = $obj->remise_percent;
1247  $this->remise_absolue = $obj->remise_absolue;
1248  $this->total_ht = $obj->total_ht;
1249  $this->total_tva = $obj->total_tva;
1250  $this->total_localtax1 = $obj->localtax1;
1251  $this->total_localtax2 = $obj->localtax2;
1252  $this->total_ttc = $obj->total_ttc;
1253  $this->socid = $obj->fk_soc;
1254  $this->fk_project = $obj->fk_project;
1255  $this->model_pdf = $obj->model_pdf;
1256  $this->modelpdf = $obj->model_pdf; // deprecated
1257  $this->note = $obj->note_private; // TODO deprecated
1258  $this->note_private = $obj->note_private;
1259  $this->note_public = $obj->note_public;
1260  $this->statut = (int) $obj->fk_statut;
1261  $this->status = (int) $obj->fk_statut;
1262  $this->datec = $this->db->jdate($obj->datec); // TODO deprecated
1263  $this->datev = $this->db->jdate($obj->datev); // TODO deprecated
1264  $this->date_creation = $this->db->jdate($obj->datec); // Creation date
1265  $this->date = $this->date_creation;
1266  $this->date_validation = $this->db->jdate($obj->datev); // Validation date
1267  $this->date_livraison = $this->db->jdate($obj->delivery_date); // deprecated
1268  $this->delivery_date = $this->db->jdate($obj->delivery_date);
1269  $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1270 
1271  $this->mode_reglement_id = $obj->fk_mode_reglement;
1272  $this->mode_reglement_code = $obj->mode_reglement_code;
1273  $this->mode_reglement = $obj->mode_reglement;
1274  $this->fk_account = ($obj->fk_account > 0) ? $obj->fk_account : null;
1275  $this->cond_reglement_id = $obj->fk_cond_reglement;
1276  $this->cond_reglement_code = $obj->cond_reglement_code;
1277  $this->cond_reglement = $obj->cond_reglement;
1278  $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1279 
1280  $this->extraparams = (array) json_decode($obj->extraparams, true);
1281 
1282  $this->user_author_id = $obj->fk_user_author;
1283  $this->user_valid_id = $obj->fk_user_valid;
1284  $this->user_close_id = $obj->fk_user_cloture;
1285 
1286  // Multicurrency
1287  $this->fk_multicurrency = $obj->fk_multicurrency;
1288  $this->multicurrency_code = $obj->multicurrency_code;
1289  $this->multicurrency_tx = $obj->multicurrency_tx;
1290  $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1291  $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1292  $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1293 
1294  if ($obj->fk_statut == 0) {
1295  $this->brouillon = 1;
1296  }
1297 
1298  // Retrieve all extrafield
1299  // fetch optionals attributes and labels
1300  $this->fetch_optionals();
1301 
1302  $this->db->free($resql);
1303 
1304  $this->lines = array();
1305 
1306  // Lines of supplier proposals
1307  $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,";
1308  $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,";
1309  $sql .= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label,';
1310  $sql .= ' d.ref_fourn as ref_produit_fourn,';
1311  $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';
1312  $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposaldet as d";
1313  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON d.fk_product = p.rowid";
1314  $sql .= " WHERE d.fk_supplier_proposal = ".((int) $this->id);
1315  $sql .= " ORDER by d.rang";
1316 
1317  $result = $this->db->query($sql);
1318  if ($result) {
1319  $num = $this->db->num_rows($result);
1320  $i = 0;
1321 
1322  while ($i < $num) {
1323  $objp = $this->db->fetch_object($result);
1324 
1325  $line = new SupplierProposalLine($this->db);
1326 
1327  $line->rowid = $objp->rowid; // deprecated
1328  $line->id = $objp->rowid;
1329  $line->fk_supplier_proposal = $objp->fk_supplier_proposal;
1330  $line->fk_parent_line = $objp->fk_parent_line;
1331  $line->product_type = $objp->product_type;
1332  $line->label = $objp->custom_label;
1333  $line->desc = $objp->description; // Description ligne
1334  $line->qty = $objp->qty;
1335  $line->tva_tx = $objp->tva_tx;
1336  $line->localtax1_tx = $objp->localtax1_tx;
1337  $line->localtax2_tx = $objp->localtax2_tx;
1338  $line->subprice = $objp->subprice;
1339  $line->fk_remise_except = $objp->fk_remise_except;
1340  $line->remise_percent = $objp->remise_percent;
1341 
1342  $line->info_bits = $objp->info_bits;
1343  $line->total_ht = $objp->total_ht;
1344  $line->total_tva = $objp->total_tva;
1345  $line->total_localtax1 = $objp->total_localtax1;
1346  $line->total_localtax2 = $objp->total_localtax2;
1347  $line->total_ttc = $objp->total_ttc;
1348  $line->fk_fournprice = $objp->fk_fournprice;
1349  $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1350  $line->pa_ht = $marginInfos[0];
1351  $line->marge_tx = $marginInfos[1];
1352  $line->marque_tx = $marginInfos[2];
1353  $line->special_code = $objp->special_code;
1354  $line->rang = $objp->rang;
1355 
1356  $line->fk_product = $objp->fk_product;
1357 
1358  $line->ref = $objp->product_ref; // deprecated
1359  $line->product_ref = $objp->product_ref;
1360  $line->libelle = $objp->product_label; // deprecated
1361  $line->product_label = $objp->product_label;
1362  $line->product_desc = $objp->product_desc; // Description produit
1363  $line->fk_product_type = $objp->fk_product_type;
1364 
1365  $line->ref_fourn = $objp->ref_produit_fourn;
1366 
1367  // Multicurrency
1368  $line->fk_multicurrency = $objp->fk_multicurrency;
1369  $line->multicurrency_code = $objp->multicurrency_code;
1370  $line->multicurrency_subprice = $objp->multicurrency_subprice;
1371  $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
1372  $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
1373  $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
1374  $line->fk_unit = $objp->fk_unit;
1375 
1376  $this->lines[$i] = $line;
1377 
1378  $i++;
1379  }
1380  $this->db->free($result);
1381  } else {
1382  $this->error = $this->db->error();
1383  return -1;
1384  }
1385 
1386  // Retrieve all extrafield
1387  // fetch optionals attributes and labels
1388  $this->fetch_optionals();
1389 
1390  return 1;
1391  }
1392 
1393  $this->error = "Record Not Found";
1394  return 0;
1395  } else {
1396  $this->error = $this->db->error();
1397  return -1;
1398  }
1399  }
1400 
1408  public function valid($user, $notrigger = 0)
1409  {
1410  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1411 
1412  global $conf, $langs;
1413 
1414  $error = 0;
1415  $now = dol_now();
1416 
1417  if ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->supplier_proposal->creer))
1418  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->supplier_proposal->validate_advance))) {
1419  $this->db->begin();
1420 
1421  // Numbering module definition
1422  $soc = new Societe($this->db);
1423  $result = $soc->fetch($this->socid);
1424 
1425  if ($result < 0) {
1426  return -1;
1427  }
1428 
1429  // Define new ref
1430  if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1431  $num = $this->getNextNumRef($soc);
1432  } else {
1433  $num = $this->ref;
1434  }
1435  $this->newref = dol_sanitizeFileName($num);
1436 
1437  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1438  $sql .= " SET ref = '".$this->db->escape($num)."',";
1439  $sql .= " fk_statut = 1, date_valid='".$this->db->idate($now)."', fk_user_valid=".((int) $user->id);
1440  $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1441 
1442  dol_syslog(get_class($this)."::valid", LOG_DEBUG);
1443  $resql = $this->db->query($sql);
1444  if (!$resql) {
1445  dol_print_error($this->db);
1446  $error++;
1447  }
1448 
1449  // Trigger calls
1450  if (!$error && !$notrigger) {
1451  // Call trigger
1452  $result = $this->call_trigger('PROPOSAL_SUPPLIER_VALIDATE', $user);
1453  if ($result < 0) {
1454  $error++;
1455  }
1456  // End call triggers
1457  }
1458 
1459  if (!$error) {
1460  $this->oldref = $this->ref;
1461 
1462  // Rename directory if dir was a temporary ref
1463  if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1464  // Now we rename also files into index
1465  $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)."'";
1466  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1467  $resql = $this->db->query($sql);
1468  if (!$resql) {
1469  $error++; $this->error = $this->db->lasterror();
1470  }
1471  $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'supplier_proposal/".$this->db->escape($this->newref)."'";
1472  $sql .= " WHERE filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1473  $resql = $this->db->query($sql);
1474  if (!$resql) {
1475  $error++; $this->error = $this->db->lasterror();
1476  }
1477 
1478  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1479  $oldref = dol_sanitizeFileName($this->ref);
1480  $newref = dol_sanitizeFileName($num);
1481  $dirsource = $conf->supplier_proposal->dir_output.'/'.$oldref;
1482  $dirdest = $conf->supplier_proposal->dir_output.'/'.$newref;
1483  if (!$error && file_exists($dirsource)) {
1484  dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
1485  if (@rename($dirsource, $dirdest)) {
1486  dol_syslog("Rename ok");
1487  // Rename docs starting with $oldref with $newref
1488  $listoffiles = dol_dir_list($conf->supplier_proposal->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1489  foreach ($listoffiles as $fileentry) {
1490  $dirsource = $fileentry['name'];
1491  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1492  $dirsource = $fileentry['path'].'/'.$dirsource;
1493  $dirdest = $fileentry['path'].'/'.$dirdest;
1494  @rename($dirsource, $dirdest);
1495  }
1496  }
1497  }
1498  }
1499 
1500  $this->ref = $num;
1501  $this->brouillon = 0;
1502  $this->statut = 1;
1503  $this->user_valid_id = $user->id;
1504  $this->datev = $now;
1505 
1506  $this->db->commit();
1507  return 1;
1508  } else {
1509  $this->db->rollback();
1510  return -1;
1511  }
1512  } else {
1513  dol_syslog("You don't have permission to validate supplier proposal", LOG_WARNING);
1514  return -2;
1515  }
1516  }
1517 
1518  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1527  public function set_date_livraison($user, $delivery_date)
1528  {
1529  // phpcs:enable
1530  return $this->setDeliveryDate($user, $delivery_date);
1531  }
1532 
1540  public function setDeliveryDate($user, $delivery_date)
1541  {
1542  if (!empty($user->rights->supplier_proposal->creer)) {
1543  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1544  $sql .= " SET date_livraison = ".($delivery_date != '' ? "'".$this->db->idate($delivery_date)."'" : 'null');
1545  $sql .= " WHERE rowid = ".((int) $this->id);
1546 
1547  if ($this->db->query($sql)) {
1548  $this->date_livraison = $delivery_date;
1549  $this->delivery_date = $delivery_date;
1550  return 1;
1551  } else {
1552  $this->error = $this->db->error();
1553  dol_syslog(get_class($this)."::setDeliveryDate Erreur SQL");
1554  return -1;
1555  }
1556  }
1557  return 0;
1558  }
1559 
1560  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1568  public function set_remise_percent($user, $remise)
1569  {
1570  // phpcs:enable
1571  $remise = trim($remise) ?trim($remise) : 0;
1572 
1573  if (!empty($user->rights->supplier_proposal->creer)) {
1574  $remise = price2num($remise, 2);
1575 
1576  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal SET remise_percent = ".((float) $remise);
1577  $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1578 
1579  if ($this->db->query($sql)) {
1580  $this->remise_percent = ((float) $remise);
1581  $this->update_price(1);
1582  return 1;
1583  } else {
1584  $this->error = $this->db->error();
1585  return -1;
1586  }
1587  }
1588  return 0;
1589  }
1590 
1591 
1592  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1600  public function set_remise_absolue($user, $remise)
1601  {
1602  // phpcs:enable
1603  if (empty($remise)) {
1604  $remise = 0;
1605  }
1606 
1607  $remise = price2num($remise);
1608 
1609  if (!empty($user->rights->supplier_proposal->creer)) {
1610  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1611  $sql .= " SET remise_absolue = ".((float) $remise);
1612  $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1613 
1614  if ($this->db->query($sql)) {
1615  $this->remise_absolue = $remise;
1616  $this->update_price(1);
1617  return 1;
1618  } else {
1619  $this->error = $this->db->error();
1620  return -1;
1621  }
1622  }
1623  return 0;
1624  }
1625 
1626 
1627 
1637  public function reopen($user, $statut, $note = '', $notrigger = 0)
1638  {
1639  global $langs, $conf;
1640 
1641  $this->statut = $statut;
1642  $error = 0;
1643 
1644  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1645  $sql .= " SET fk_statut = ".((int) $this->statut).",";
1646  if (!empty($note)) {
1647  $sql .= " note_private = '".$this->db->escape($note)."',";
1648  }
1649  $sql .= " date_cloture=NULL, fk_user_cloture=NULL";
1650  $sql .= " WHERE rowid = ".((int) $this->id);
1651 
1652  $this->db->begin();
1653 
1654  dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
1655  $resql = $this->db->query($sql);
1656  if (!$resql) {
1657  $error++; $this->errors[] = "Error ".$this->db->lasterror();
1658  }
1659  if (!$error) {
1660  if (!$notrigger) {
1661  // Call trigger
1662  $result = $this->call_trigger('PROPOSAL_SUPPLIER_REOPEN', $user);
1663  if ($result < 0) {
1664  $error++;
1665  }
1666  // End call triggers
1667  }
1668  }
1669 
1670  // Commit or rollback
1671  if ($error) {
1672  if (!empty($this->errors)) {
1673  foreach ($this->errors as $errmsg) {
1674  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1675  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1676  }
1677  }
1678  $this->db->rollback();
1679  return -1 * $error;
1680  } else {
1681  $this->db->commit();
1682  return 1;
1683  }
1684  }
1685 
1686 
1695  public function cloture($user, $status, $note)
1696  {
1697  global $langs, $conf;
1698  $hidedetails = 0;
1699  $hidedesc = 0;
1700  $hideref = 0;
1701  $this->statut = $status;
1702  $error = 0;
1703  $now = dol_now();
1704 
1705  $this->db->begin();
1706 
1707  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1708  $sql .= " SET fk_statut = ".((int) $status).", note_private = '".$this->db->escape($note)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".$user->id;
1709  $sql .= " WHERE rowid = ".((int) $this->id);
1710 
1711  $resql = $this->db->query($sql);
1712  if ($resql) {
1713  $modelpdf = $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED ? $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED : (empty($this->model_pdf) ? '' : $this->model_pdf);
1714  $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_REFUSED';
1715 
1716  if ($status == 2) {
1717  $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_SIGNED';
1718  $modelpdf = $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL ? $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL : (empty($this->model_pdf) ? '' : $this->model_pdf);
1719 
1720  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
1721  $result = $this->updateOrCreatePriceFournisseur($user);
1722  }
1723  }
1724  if ($status == 4) {
1725  $triggerName = 'PROPOSAL_SUPPLIER_CLASSIFY_BILLED';
1726  }
1727 
1728  if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE)) {
1729  // Define output language
1730  $outputlangs = $langs;
1731  if (getDolGlobalInt('MAIN_MULTILANGS')) {
1732  $outputlangs = new Translate("", $conf);
1733  $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
1734  $outputlangs->setDefaultLang($newlang);
1735  }
1736  //$ret=$object->fetch($id); // Reload to get new records
1737  $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1738  }
1739 
1740  // Call trigger
1741  $result = $this->call_trigger($triggerName, $user);
1742  if ($result < 0) {
1743  $error++;
1744  }
1745  // End call triggers
1746 
1747  if (!$error) {
1748  $this->db->commit();
1749  return 1;
1750  } else {
1751  $this->db->rollback();
1752  return -1;
1753  }
1754  } else {
1755  $this->error = $this->db->lasterror();
1756  $this->errors[] = $this->db->lasterror();
1757  $this->db->rollback();
1758  return -1;
1759  }
1760  }
1761 
1768  public function updateOrCreatePriceFournisseur($user)
1769  {
1770  global $conf;
1771 
1772  dol_syslog(get_class($this)."::updateOrCreatePriceFournisseur", LOG_DEBUG);
1773  foreach ($this->lines as $product) {
1774  if ($product->subprice <= 0) {
1775  continue;
1776  }
1777  $productsupplier = new ProductFournisseur($this->db);
1778 
1779  $multicurrency_tx = 1;
1780  $fk_multicurrency = 0;
1781 
1782  if (empty($this->thirdparty)) {
1783  $this->fetch_thirdparty();
1784  }
1785 
1786  $ref_fourn = $product->ref_fourn;
1787  if (empty($ref_fourn)) {
1788  $ref_fourn = $product->ref_supplier;
1789  }
1790  if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1791  list($fk_multicurrency, $multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $product->multicurrency_code);
1792  }
1793  $productsupplier->id = $product->fk_product;
1794 
1795  $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, '', '', '');
1796  }
1797 
1798  return 1;
1799  }
1800 
1809  public function updatePriceFournisseur($idProductFournPrice, $product, $user)
1810  {
1811  $price = price2num($product->subprice * $product->qty, 'MU');
1812  $unitPrice = price2num($product->subprice, 'MU');
1813 
1814  $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);
1815 
1816  $resql = $this->db->query($sql);
1817  if (!$resql) {
1818  $this->error = $this->db->error();
1819  $this->db->rollback();
1820  return -1;
1821  }
1822  return 1;
1823  }
1824 
1832  public function createPriceFournisseur($product, $user)
1833  {
1834  global $conf;
1835 
1836  $price = price2num($product->subprice * $product->qty, 'MU');
1837  $qty = price2num($product->qty);
1838  $unitPrice = price2num($product->subprice, 'MU');
1839 
1840  $now = dol_now();
1841 
1842  $values = array(
1843  "'".$this->db->idate($now)."'",
1844  $product->fk_product,
1845  $this->thirdparty->id,
1846  "'".$product->ref_fourn."'",
1847  $price,
1848  $qty,
1849  $unitPrice,
1850  $product->tva_tx,
1851  $user->id
1852  );
1853  if (isModEnabled("multicurrency")) {
1854  if (!empty($product->multicurrency_code)) {
1855  include_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
1856  $multicurrency = new MultiCurrency($this->db); //need to fetch because empty fk_multicurrency and rate
1857  $multicurrency->fetch(0, $product->multicurrency_code);
1858  if (!empty($multicurrency->id)) {
1859  $values[] = $multicurrency->id;
1860  $values[] = "'".$product->multicurrency_code."'";
1861  $values[] = $product->multicurrency_subprice;
1862  $values[] = $product->multicurrency_total_ht;
1863  $values[] = $multicurrency->rate->rate;
1864  } else {
1865  for ($i = 0; $i < 5; $i++) {
1866  $values[] = 'NULL';
1867  }
1868  }
1869  }
1870  }
1871 
1872  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'product_fournisseur_price ';
1873  $sql .= '(datec, fk_product, fk_soc, ref_fourn, price, quantity, unitprice, tva_tx, fk_user';
1874  if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1875  $sql .= ',fk_multicurrency, multicurrency_code, multicurrency_unitprice, multicurrency_price, multicurrency_tx';
1876  }
1877  $sql .= ') VALUES ('.implode(',', $values).')';
1878 
1879  $resql = $this->db->query($sql);
1880  if (!$resql) {
1881  $this->error = $this->db->error();
1882  $this->db->rollback();
1883  return -1;
1884  }
1885  return 1;
1886  }
1887 
1888  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1895  public function setDraft($user)
1896  {
1897  // phpcs:enable
1898  global $conf, $langs;
1899 
1900  $error = 0;
1901 
1902  if ($this->statut == self::STATUS_DRAFT) {
1903  dol_syslog(get_class($this)."::setDraft already draft status", LOG_WARNING);
1904  return 0;
1905  }
1906 
1907  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1908  $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1909  $sql .= " WHERE rowid = ".((int) $this->id);
1910 
1911  if ($this->db->query($sql)) {
1912  if (!$error) {
1913  $this->oldcopy = clone $this;
1914  }
1915 
1916  if (!$error) {
1917  // Call trigger
1918  $result = $this->call_trigger('PROPOSAL_SUPPLIER_UNVALIDATE', $user);
1919  if ($result < 0) {
1920  $error++;
1921  }
1922  }
1923 
1924  if (!$error) {
1925  $this->statut = self::STATUS_DRAFT;
1926  $this->brouillon = 1;
1927  $this->db->commit();
1928  return 1;
1929  } else {
1930  $this->db->rollback();
1931  return -1;
1932  }
1933  } else {
1934  return -1;
1935  }
1936  }
1937 
1938 
1939  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1953  public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datec', $sortorder = 'DESC')
1954  {
1955  // phpcs:enable
1956  global $conf, $user;
1957 
1958  $ga = array();
1959 
1960  $sql = "SELECT s.rowid, s.nom as name, s.client,";
1961  $sql .= " p.rowid as supplier_proposalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
1962  $sql .= " p.datep as dp, p.fin_validite as datelimite";
1963  if (empty($user->rights->societe->client->voir) && !$socid) {
1964  $sql .= ", sc.fk_soc, sc.fk_user";
1965  }
1966  $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."supplier_proposal as p, ".MAIN_DB_PREFIX."c_propalst as c";
1967  if (empty($user->rights->societe->client->voir) && !$socid) {
1968  $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
1969  }
1970  $sql .= " WHERE p.entity IN (".getEntity('supplier_proposal').")";
1971  $sql .= " AND p.fk_soc = s.rowid";
1972  $sql .= " AND p.fk_statut = c.id";
1973  if (empty($user->rights->societe->client->voir) && !$socid) { //restriction
1974  $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
1975  }
1976  if ($socid) {
1977  $sql .= " AND s.rowid = ".((int) $socid);
1978  }
1979  if ($draft) {
1980  $sql .= " AND p.fk_statut = 0";
1981  }
1982  if ($notcurrentuser > 0) {
1983  $sql .= " AND p.fk_user_author <> ".$user->id;
1984  }
1985  $sql .= $this->db->order($sortfield, $sortorder);
1986  $sql .= $this->db->plimit($limit, $offset);
1987 
1988  $result = $this->db->query($sql);
1989  if ($result) {
1990  $num = $this->db->num_rows($result);
1991  if ($num) {
1992  $i = 0;
1993  while ($i < $num) {
1994  $obj = $this->db->fetch_object($result);
1995 
1996  if ($shortlist == 1) {
1997  $ga[$obj->supplier_proposalid] = $obj->ref;
1998  } elseif ($shortlist == 2) {
1999  $ga[$obj->supplier_proposalid] = $obj->ref.' ('.$obj->name.')';
2000  } else {
2001  $ga[$i]['id'] = $obj->supplier_proposalid;
2002  $ga[$i]['ref'] = $obj->ref;
2003  $ga[$i]['name'] = $obj->name;
2004  }
2005 
2006  $i++;
2007  }
2008  }
2009  return $ga;
2010  } else {
2011  dol_print_error($this->db);
2012  return -1;
2013  }
2014  }
2015 
2023  public function delete($user, $notrigger = 0)
2024  {
2025  global $conf, $langs;
2026  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2027 
2028  $error = 0;
2029 
2030  $this->db->begin();
2031 
2032  if (!$notrigger) {
2033  // Call trigger
2034  $result = $this->call_trigger('PROPOSAL_SUPPLIER_DELETE', $user);
2035  if ($result < 0) {
2036  $error++;
2037  }
2038  // End call triggers
2039  }
2040 
2041  if (!$error) {
2042  $main = MAIN_DB_PREFIX.'supplier_proposaldet';
2043  $ef = $main."_extrafields";
2044  $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_supplier_proposal = ".((int) $this->id).")";
2045  $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet WHERE fk_supplier_proposal = ".((int) $this->id);
2046  if ($this->db->query($sql)) {
2047  $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposal WHERE rowid = ".((int) $this->id);
2048  if ($this->db->query($sqlef) && $this->db->query($sql)) {
2049  // Delete linked object
2050  $res = $this->deleteObjectLinked();
2051  if ($res < 0) {
2052  $error++;
2053  }
2054 
2055  if (!$error) {
2056  // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2057  $this->deleteEcmFiles();
2058 
2059  // We remove directory
2060  $ref = dol_sanitizeFileName($this->ref);
2061  if ($conf->supplier_proposal->dir_output && !empty($this->ref)) {
2062  $dir = $conf->supplier_proposal->dir_output."/".$ref;
2063  $file = $dir."/".$ref.".pdf";
2064  if (file_exists($file)) {
2065  dol_delete_preview($this);
2066 
2067  if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2068  $this->error = 'ErrorFailToDeleteFile';
2069  $this->errors = array('ErrorFailToDeleteFile');
2070  $this->db->rollback();
2071  return 0;
2072  }
2073  }
2074  if (file_exists($dir)) {
2075  $res = @dol_delete_dir_recursive($dir);
2076  if (!$res) {
2077  $this->error = 'ErrorFailToDeleteDir';
2078  $this->errors = array('ErrorFailToDeleteDir');
2079  $this->db->rollback();
2080  return 0;
2081  }
2082  }
2083  }
2084  }
2085 
2086  // Removed extrafields
2087  if (!$error) {
2088  $result = $this->deleteExtraFields();
2089  if ($result < 0) {
2090  $error++;
2091  $errorflag = -4;
2092  dol_syslog(get_class($this)."::delete erreur ".$errorflag." ".$this->error, LOG_ERR);
2093  }
2094  }
2095 
2096  if (!$error) {
2097  dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
2098  $this->db->commit();
2099  return 1;
2100  } else {
2101  $this->error = $this->db->lasterror();
2102  $this->db->rollback();
2103  return 0;
2104  }
2105  } else {
2106  $this->error = $this->db->lasterror();
2107  $this->db->rollback();
2108  return -3;
2109  }
2110  } else {
2111  $this->error = $this->db->lasterror();
2112  $this->db->rollback();
2113  return -2;
2114  }
2115  } else {
2116  $this->db->rollback();
2117  return -1;
2118  }
2119  }
2120 
2127  public function info($id)
2128  {
2129  $sql = "SELECT c.rowid, ";
2130  $sql .= " c.datec, c.date_valid as datev, c.date_cloture as dateo,";
2131  $sql .= " c.fk_user_author, c.fk_user_valid, c.fk_user_cloture";
2132  $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as c";
2133  $sql .= " WHERE c.rowid = ".((int) $id);
2134 
2135  $result = $this->db->query($sql);
2136 
2137  if ($result) {
2138  if ($this->db->num_rows($result)) {
2139  $obj = $this->db->fetch_object($result);
2140 
2141  $this->id = $obj->rowid;
2142 
2143  $this->date_creation = $this->db->jdate($obj->datec);
2144  $this->date_validation = $this->db->jdate($obj->datev);
2145  $this->date_cloture = $this->db->jdate($obj->dateo);
2146 
2147  $cuser = new User($this->db);
2148  $cuser->fetch($obj->fk_user_author);
2149  $this->user_creation = $cuser;
2150 
2151  if ($obj->fk_user_valid) {
2152  $vuser = new User($this->db);
2153  $vuser->fetch($obj->fk_user_valid);
2154  $this->user_validation = $vuser;
2155  }
2156 
2157  if ($obj->fk_user_cloture) {
2158  $cluser = new User($this->db);
2159  $cluser->fetch($obj->fk_user_cloture);
2160  $this->user_cloture = $cluser;
2161  }
2162  }
2163  $this->db->free($result);
2164  } else {
2165  dol_print_error($this->db);
2166  }
2167  }
2168 
2169 
2176  public function getLibStatut($mode = 0)
2177  {
2178  return $this->LibStatut((isset($this->statut) ? $this->statut : $this->status), $mode);
2179  }
2180 
2181  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2189  public function LibStatut($status, $mode = 1)
2190  {
2191  // phpcs:enable
2192 
2193  // Init/load array of translation of status
2194  if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
2195  global $langs;
2196  $langs->load("supplier_proposal");
2197  $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraft");
2198  $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidated");
2199  $this->labelStatus[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSigned");
2200  $this->labelStatus[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSigned");
2201  $this->labelStatus[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosed");
2202  $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraftShort");
2203  $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidatedShort");
2204  $this->labelStatusShort[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSignedShort");
2205  $this->labelStatusShort[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSignedShort");
2206  $this->labelStatusShort[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosedShort");
2207  }
2208 
2209  $statusnew = '';
2210  if ($status == self::STATUS_DRAFT) {
2211  $statusnew = 'status0';
2212  } elseif ($status == self::STATUS_VALIDATED) {
2213  $statusnew = 'status1';
2214  } elseif ($status == self::STATUS_SIGNED) {
2215  $statusnew = 'status4';
2216  } elseif ($status == self::STATUS_NOTSIGNED) {
2217  $statusnew = 'status9';
2218  } elseif ($status == self::STATUS_CLOSE) {
2219  $statusnew = 'status6';
2220  }
2221 
2222  return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusnew, $mode);
2223  }
2224 
2225 
2226  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2234  public function load_board($user, $mode)
2235  {
2236  // phpcs:enable
2237  global $conf, $user, $langs;
2238 
2239  $now = dol_now();
2240 
2241  $this->nbtodo = $this->nbtodolate = 0;
2242  $clause = " WHERE";
2243 
2244  $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.date_cloture as datefin";
2245  $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2246  if (empty($user->rights->societe->client->voir) && !$user->socid) {
2247  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
2248  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2249  $clause = " AND";
2250  }
2251  $sql .= $clause." p.entity IN (".getEntity('supplier_proposal').")";
2252  if ($mode == 'opened') {
2253  $sql .= " AND p.fk_statut = 1";
2254  }
2255  if ($mode == 'signed') {
2256  $sql .= " AND p.fk_statut = 2";
2257  }
2258  if ($user->socid) {
2259  $sql .= " AND p.fk_soc = ".((int) $user->socid);
2260  }
2261 
2262  $resql = $this->db->query($sql);
2263  if ($resql) {
2264  $label = $labelShort = '';
2265  $status = '';
2266  if ($mode == 'opened') {
2267  $delay_warning = !empty($conf->supplier_proposal->cloture->warning_delay) ? $conf->supplier_proposal->cloture->warning_delay : 0;
2268  $status = self::STATUS_VALIDATED;
2269  $label = $langs->trans("SupplierProposalsToClose");
2270  $labelShort = $langs->trans("ToAcceptRefuse");
2271  }
2272  if ($mode == 'signed') {
2273  $delay_warning = !empty($conf->supplier_proposal->facturation->warning_delay) ? $conf->supplier_proposal->facturation->warning_delay : 0;
2274  $status = self::STATUS_SIGNED;
2275  $label = $langs->trans("SupplierProposalsToProcess"); // May be billed or ordered
2276  $labelShort = $langs->trans("ToClose");
2277  }
2278 
2279  $response = new WorkboardResponse();
2280  $response->warning_delay = $delay_warning / 60 / 60 / 24;
2281  $response->label = $label;
2282  $response->labelShort = $labelShort;
2283  $response->url = DOL_URL_ROOT.'/supplier_proposal/list.php?search_status='.$status;
2284  $response->img = img_object('', "propal");
2285 
2286  // This assignment in condition is not a bug. It allows walking the results.
2287  while ($obj = $this->db->fetch_object($resql)) {
2288  $response->nbtodo++;
2289  if ($mode == 'opened') {
2290  $datelimit = $this->db->jdate($obj->datefin);
2291  if ($datelimit < ($now - $delay_warning)) {
2292  $response->nbtodolate++;
2293  }
2294  }
2295  // TODO Definir regle des propales a facturer en retard
2296  // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
2297  }
2298  return $response;
2299  } else {
2300  $this->error = $this->db->lasterror();
2301  return -1;
2302  }
2303  }
2304 
2305 
2313  public function initAsSpecimen()
2314  {
2315  global $user, $langs, $conf;
2316 
2317  // Load array of products prodids
2318  $num_prods = 0;
2319  $prodids = array();
2320  $sql = "SELECT rowid";
2321  $sql .= " FROM ".MAIN_DB_PREFIX."product";
2322  $sql .= " WHERE entity IN (".getEntity('product').")";
2323  $sql .= $this->db->plimit(100);
2324 
2325  $resql = $this->db->query($sql);
2326  if ($resql) {
2327  $num_prods = $this->db->num_rows($resql);
2328  $i = 0;
2329  while ($i < $num_prods) {
2330  $i++;
2331  $row = $this->db->fetch_row($resql);
2332  $prodids[$i] = $row[0];
2333  }
2334  }
2335 
2336  // Initialise parametres
2337  $this->id = 0;
2338  $this->ref = 'SPECIMEN';
2339  $this->specimen = 1;
2340  $this->socid = 1;
2341  $this->date = time();
2342  $this->cond_reglement_id = 1;
2343  $this->cond_reglement_code = 'RECEP';
2344  $this->mode_reglement_id = 7;
2345  $this->mode_reglement_code = 'CHQ';
2346  $this->note_public = 'This is a comment (public)';
2347  $this->note_private = 'This is a comment (private)';
2348  // Lines
2349  $nbp = 5;
2350  $xnbp = 0;
2351  while ($xnbp < $nbp) {
2352  $line = new SupplierProposalLine($this->db);
2353  $line->desc = $langs->trans("Description")." ".$xnbp;
2354  $line->qty = 1;
2355  $line->subprice = 100;
2356  $line->tva_tx = 19.6;
2357  $line->localtax1_tx = 0;
2358  $line->localtax2_tx = 0;
2359  if ($xnbp == 2) {
2360  $line->total_ht = 50;
2361  $line->total_ttc = 59.8;
2362  $line->total_tva = 9.8;
2363  $line->remise_percent = 50;
2364  } else {
2365  $line->total_ht = 100;
2366  $line->total_ttc = 119.6;
2367  $line->total_tva = 19.6;
2368  $line->remise_percent = 00;
2369  }
2370 
2371  if ($num_prods > 0) {
2372  $prodid = mt_rand(1, $num_prods);
2373  $line->fk_product = $prodids[$prodid];
2374  }
2375 
2376  $this->lines[$xnbp] = $line;
2377 
2378  $this->total_ht += $line->total_ht;
2379  $this->total_tva += $line->total_tva;
2380  $this->total_ttc += $line->total_ttc;
2381 
2382  $xnbp++;
2383  }
2384  }
2385 
2386  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2392  public function load_state_board()
2393  {
2394  // phpcs:enable
2395  global $conf, $user;
2396 
2397  $this->nb = array();
2398  $clause = "WHERE";
2399 
2400  $sql = "SELECT count(p.rowid) as nb";
2401  $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2402  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
2403  if (empty($user->rights->societe->client->voir) && !$user->socid) {
2404  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2405  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2406  $clause = "AND";
2407  }
2408  $sql .= " ".$clause." p.entity IN (".getEntity('supplier_proposal').")";
2409 
2410  $resql = $this->db->query($sql);
2411  if ($resql) {
2412  // This assignment in condition is not a bug. It allows walking the results.
2413  while ($obj = $this->db->fetch_object($resql)) {
2414  $this->nb["supplier_proposals"] = $obj->nb;
2415  }
2416  $this->db->free($resql);
2417  return 1;
2418  } else {
2419  dol_print_error($this->db);
2420  $this->error = $this->db->lasterror();
2421  return -1;
2422  }
2423  }
2424 
2425 
2433  public function getNextNumRef($soc)
2434  {
2435  global $conf, $db, $langs;
2436  $langs->load("supplier_proposal");
2437 
2438  if (!empty($conf->global->SUPPLIER_PROPOSAL_ADDON)) {
2439  $mybool = false;
2440 
2441  $file = $conf->global->SUPPLIER_PROPOSAL_ADDON.".php";
2442  $classname = $conf->global->SUPPLIER_PROPOSAL_ADDON;
2443 
2444  // Include file with class
2445  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2446  foreach ($dirmodels as $reldir) {
2447  $dir = dol_buildpath($reldir."core/modules/supplier_proposal/");
2448 
2449  // Load file with numbering class (if found)
2450  $mybool |= @include_once $dir.$file;
2451  }
2452 
2453  if (!$mybool) {
2454  dol_print_error('', "Failed to include file ".$file);
2455  return '';
2456  }
2457 
2458  $obj = new $classname();
2459  $numref = "";
2460  $numref = $obj->getNextValue($soc, $this);
2461 
2462  if ($numref != "") {
2463  return $numref;
2464  } else {
2465  $this->error = $obj->error;
2466  return "";
2467  }
2468  } else {
2469  $langs->load("errors");
2470  print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("SupplierProposal"));
2471  return "";
2472  }
2473  }
2474 
2482  public function getTooltipContentArray($params)
2483  {
2484  global $conf, $langs, $menumanager;
2485 
2486  $langs->load('supplier_proposal');
2487 
2488  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
2489  return ['optimize' => $langs->trans("ShowSupplierProposal")];
2490  }
2491 
2492  $option = $params['option'] ?? '';
2493  $datas = [];
2494 
2495  $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("SupplierProposal").'</u>';
2496  if (isset($this->status)) {
2497  $datas['picto'] .= ' '.$this->getLibStatut(5);
2498  }
2499  if (!empty($this->ref)) {
2500  $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2501  }
2502  if (!empty($this->ref_fourn)) {
2503  $datas['ref_supplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_fourn;
2504  }
2505  if (!empty($this->total_ht)) {
2506  $datas['amount_ht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2507  }
2508  if (!empty($this->total_tva)) {
2509  $datas['amount_vat'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2510  }
2511  if (!empty($this->total_ttc)) {
2512  $datas['amount_ttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2513  }
2514 
2515  return $datas;
2516  }
2517 
2529  public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2530  {
2531  global $langs, $conf, $user, $hookmanager;
2532 
2533  if (!empty($conf->dol_no_mouse_hover)) {
2534  $notooltip = 1; // Force disable tooltips
2535  }
2536 
2537  $url = '';
2538  $result = '';
2539  $params = [
2540  'id' => $this->id,
2541  'objecttype' => $this->element,
2542  'option' => $option,
2543  ];
2544  $classfortooltip = 'classfortooltip';
2545  $dataparams = '';
2546  if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2547  $classfortooltip = 'classforajaxtooltip';
2548  $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
2549  $label = '';
2550  } else {
2551  $label = implode($this->getTooltipContentArray($params));
2552  }
2553 
2554  if ($option == '') {
2555  $url = DOL_URL_ROOT.'/supplier_proposal/card.php?id='.$this->id.$get_params;
2556  }
2557  if ($option == 'document') {
2558  $url = DOL_URL_ROOT.'/supplier_proposal/document.php?id='.$this->id.$get_params;
2559  }
2560 
2561  if ($option !== 'nolink') {
2562  // Add param to save lastsearch_values or not
2563  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2564  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2565  $add_save_lastsearch_values = 1;
2566  }
2567  if ($add_save_lastsearch_values) {
2568  $url .= '&save_lastsearch_values=1';
2569  }
2570  }
2571 
2572  $linkclose = '';
2573  if (empty($notooltip) && $user->hasRight('propal', 'lire')) {
2574  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
2575  $label = $langs->trans("ShowSupplierProposal");
2576  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
2577  }
2578  $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
2579  $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
2580  }
2581 
2582  $linkstart = '<a href="'.$url.'"';
2583  $linkstart .= $linkclose.'>';
2584  $linkend = '</a>';
2585 
2586  $result .= $linkstart;
2587  if ($withpicto) {
2588  $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
2589  }
2590  if ($withpicto != 2) {
2591  $result .= $this->ref;
2592  }
2593  $result .= $linkend;
2594 
2595  if ($addlinktonotes) {
2596  $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2597  if ($txttoshow) {
2598  $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2599  $result .= ' <span class="note inline-block">';
2600  $result .= '<a href="'.DOL_URL_ROOT.'/supplier_proposal/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2601  $result .= img_picto('', 'note');
2602  $result .= '</a>';
2603  //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
2604  //$result.='</a>';
2605  $result .= '</span>';
2606  }
2607  }
2608  global $action;
2609  $hookmanager->initHooks(array($this->element . 'dao'));
2610  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
2611  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2612  if ($reshook > 0) {
2613  $result = $hookmanager->resPrint;
2614  } else {
2615  $result .= $hookmanager->resPrint;
2616  }
2617  return $result;
2618  }
2619 
2625  public function getLinesArray()
2626  {
2627  // For other object, here we call fetch_lines. But fetch_lines does not exists on supplier proposal
2628 
2629  $sql = 'SELECT pt.rowid, pt.label as custom_label, pt.description, pt.fk_product, pt.fk_remise_except,';
2630  $sql .= ' pt.qty, pt.tva_tx, pt.vat_src_code, pt.remise_percent, pt.subprice, pt.info_bits,';
2631  $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,';
2632  $sql .= ' pt.product_type, pt.rang, pt.fk_parent_line,';
2633  $sql .= ' p.label as product_label, p.ref, p.fk_product_type, p.rowid as prodid,';
2634  $sql .= ' p.description as product_desc, pt.ref_fourn as ref_supplier,';
2635  $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';
2636  $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pt';
2637  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pt.fk_product=p.rowid';
2638  $sql .= ' WHERE pt.fk_supplier_proposal = '.((int) $this->id);
2639  $sql .= ' ORDER BY pt.rang ASC, pt.rowid';
2640 
2641  dol_syslog(get_class($this).'::getLinesArray', LOG_DEBUG);
2642  $resql = $this->db->query($sql);
2643  if ($resql) {
2644  $num = $this->db->num_rows($resql);
2645  $i = 0;
2646 
2647  while ($i < $num) {
2648  $obj = $this->db->fetch_object($resql);
2649 
2650  $this->lines[$i] = new SupplierProposalLine($this->db);
2651  $this->lines[$i]->id = $obj->rowid; // for backward compatibility
2652  $this->lines[$i]->rowid = $obj->rowid;
2653  $this->lines[$i]->label = $obj->custom_label;
2654  $this->lines[$i]->description = $obj->description;
2655  $this->lines[$i]->fk_product = $obj->fk_product;
2656  $this->lines[$i]->ref = $obj->ref;
2657  $this->lines[$i]->product_label = $obj->product_label;
2658  $this->lines[$i]->product_desc = $obj->product_desc;
2659  $this->lines[$i]->fk_product_type = $obj->fk_product_type; // deprecated
2660  $this->lines[$i]->product_type = $obj->product_type;
2661  $this->lines[$i]->qty = $obj->qty;
2662  $this->lines[$i]->subprice = $obj->subprice;
2663  $this->lines[$i]->fk_remise_except = $obj->fk_remise_except;
2664  $this->lines[$i]->remise_percent = $obj->remise_percent;
2665  $this->lines[$i]->tva_tx = $obj->tva_tx;
2666  $this->lines[$i]->vat_src_code = $obj->vat_src_code;
2667  $this->lines[$i]->info_bits = $obj->info_bits;
2668  $this->lines[$i]->total_ht = $obj->total_ht;
2669  $this->lines[$i]->total_tva = $obj->total_tva;
2670  $this->lines[$i]->total_ttc = $obj->total_ttc;
2671  $this->lines[$i]->fk_fournprice = $obj->fk_fournprice;
2672  $marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->lines[$i]->fk_fournprice, $obj->pa_ht);
2673  $this->lines[$i]->pa_ht = $marginInfos[0];
2674  $this->lines[$i]->marge_tx = $marginInfos[1];
2675  $this->lines[$i]->marque_tx = $marginInfos[2];
2676  $this->lines[$i]->fk_parent_line = $obj->fk_parent_line;
2677  $this->lines[$i]->special_code = $obj->special_code;
2678  $this->lines[$i]->rang = $obj->rang;
2679 
2680  $this->lines[$i]->ref_fourn = $obj->ref_supplier; // deprecated
2681  $this->lines[$i]->ref_supplier = $obj->ref_supplier;
2682 
2683  // Multicurrency
2684  $this->lines[$i]->fk_multicurrency = $obj->fk_multicurrency;
2685  $this->lines[$i]->multicurrency_code = $obj->multicurrency_code;
2686  $this->lines[$i]->multicurrency_subprice = $obj->multicurrency_subprice;
2687  $this->lines[$i]->multicurrency_total_ht = $obj->multicurrency_total_ht;
2688  $this->lines[$i]->multicurrency_total_tva = $obj->multicurrency_total_tva;
2689  $this->lines[$i]->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
2690  $this->lines[$i]->fk_unit = $obj->fk_unit;
2691 
2692  $i++;
2693  }
2694  $this->db->free($resql);
2695 
2696  return 1;
2697  } else {
2698  $this->error = $this->db->error();
2699  return -1;
2700  }
2701  }
2702 
2714  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2715  {
2716  global $conf, $langs;
2717 
2718  $langs->load("supplier_proposal");
2719  $outputlangs->load("products");
2720 
2721  if (!dol_strlen($modele)) {
2722  $modele = 'aurore';
2723 
2724  if ($this->model_pdf) {
2725  $modele = $this->model_pdf;
2726  } elseif (!empty($conf->global->SUPPLIER_PROPOSAL_ADDON_PDF)) {
2727  $modele = $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF;
2728  }
2729  }
2730 
2731  $modelpath = "core/modules/supplier_proposal/doc/";
2732 
2733  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2734  }
2735 
2736 
2745  public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2746  {
2747  $tables = array(
2748  'supplier_proposal'
2749  );
2750 
2751  return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2752  }
2753 
2762  public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
2763  {
2764  $tables = array(
2765  'supplier_proposaldet'
2766  );
2767 
2768  return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
2769  }
2770 
2771 
2779  public function getKanbanView($option = '', $arraydata = null)
2780  {
2781  global $langs;
2782 
2783  $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2784 
2785  $return = '<div class="box-flex-item box-flex-grow-zero">';
2786  $return .= '<div class="info-box info-box-sm">';
2787  $return .= '<span class="info-box-icon bg-infobox-action">';
2788  $return .= img_picto('', $this->picto);
2789  //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2790  $return .= '</span>';
2791  $return .= '<div class="info-box-content">';
2792  $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
2793  $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2794  if (property_exists($this, 'socid')) {
2795  $return .= '<span class="info-box-ref"> | '.$this->socid.'</span>';
2796  }
2797  if (property_exists($this, 'delivery_date')) {
2798  $return .= '<br><span class="opacitymedium">'.$langs->trans("DateEnd").'</span> : <span class="info-box-label">'.dol_print_date($this->delivery_date).'</span>';
2799  }
2800  if (property_exists($this, 'total_ttc')) {
2801  $return .='<br><span class="opacitymedium" >'.$langs->trans("AmountHT").' : </span><span class="info-box-label amount">'.price($this->total_ttc).'</span>';
2802  }
2803  if (method_exists($this, 'getLibStatut')) {
2804  $return .= '<br><div class="info-box-status margintoponly">'.$this->getLibStatut(3).'</div>';
2805  }
2806  $return .= '</div>';
2807  $return .= '</div>';
2808  $return .= '</div>';
2809  return $return;
2810  }
2811 }
2812 
2813 
2818 {
2822  public $db;
2823 
2827  public $error = '';
2828 
2832  public $element = 'supplier_proposaldet';
2833 
2837  public $table_element = 'supplier_proposaldet';
2838 
2839  public $oldline;
2840 
2844  public $id;
2845 
2849  public $fk_supplier_proposal;
2850 
2854  public $fk_parent_line;
2855 
2856  public $desc; // Description ligne
2857 
2861  public $fk_product; // Id produit predefini
2862 
2873  public $product_type = Product::TYPE_PRODUCT;
2874 
2875  public $qty;
2876  public $tva_tx;
2877  public $vat_src_code;
2878 
2879  public $subprice;
2880  public $remise_percent;
2881 
2885  public $fk_remise_except;
2886 
2887  public $rang = 0;
2888 
2892  public $fk_fournprice;
2893 
2894  public $pa_ht;
2895  public $marge_tx;
2896  public $marque_tx;
2897 
2898  public $special_code; // Tag for special lines (exlusive tags)
2899  // 1: frais de port
2900  // 2: ecotaxe
2901  // 3: option line (when qty = 0)
2902 
2903  public $info_bits = 0; // Liste d'options cumulables:
2904  // Bit 0: 0 si TVA normal - 1 si TVA NPR
2905  // Bit 1: 0 ligne normale - 1 si ligne de remise fixe
2906 
2907  public $total_ht; // Total HT de la ligne toute quantite et incluant la remise ligne
2908  public $total_tva; // Total TVA de la ligne toute quantite et incluant la remise ligne
2909  public $total_ttc; // Total TTC de la ligne toute quantite et incluant la remise ligne
2910 
2911  public $date_start;
2912  public $date_end;
2913 
2914  // From llx_product
2919  public $ref;
2920 
2925  public $product_ref;
2926 
2931  public $libelle;
2932 
2937  public $product_label;
2938 
2943  public $product_desc;
2944 
2945  public $localtax1_tx; // Local tax 1
2946  public $localtax2_tx; // Local tax 2
2947  public $localtax1_type; // Local tax 1 type
2948  public $localtax2_type; // Local tax 2 type
2949  public $total_localtax1; // Line total local tax 1
2950  public $total_localtax2; // Line total local tax 2
2951 
2952  public $skip_update_total; // Skip update price total for special lines
2953 
2954  public $ref_fourn;
2955  public $ref_supplier;
2956 
2957  // Multicurrency
2961  public $fk_multicurrency;
2962 
2963  public $multicurrency_code;
2964  public $multicurrency_subprice;
2965  public $multicurrency_total_ht;
2966  public $multicurrency_total_tva;
2967  public $multicurrency_total_ttc;
2968 
2974  public function __construct($db)
2975  {
2976  $this->db = $db;
2977  }
2978 
2985  public function fetch($rowid)
2986  {
2987  $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,';
2988  $sql .= ' pd.date_start, pd.date_end,';
2989  $sql .= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice,';
2990  $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,';
2991  $sql .= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
2992  $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
2993  $sql .= ' pd.product_type, pd.ref_fourn as ref_produit_fourn,';
2994  $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';
2995  $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pd';
2996  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
2997  $sql .= ' WHERE pd.rowid = '.((int) $rowid);
2998 
2999  $result = $this->db->query($sql);
3000  if ($result) {
3001  $objp = $this->db->fetch_object($result);
3002 
3003  $this->id = $objp->rowid;
3004  $this->fk_supplier_proposal = $objp->fk_supplier_proposal;
3005  $this->fk_parent_line = $objp->fk_parent_line;
3006  $this->label = $objp->custom_label;
3007  $this->desc = $objp->description;
3008  $this->qty = $objp->qty;
3009  $this->subprice = $objp->subprice;
3010  $this->tva_tx = $objp->tva_tx;
3011  $this->remise_percent = $objp->remise_percent;
3012  $this->fk_remise_except = $objp->fk_remise_except;
3013  $this->fk_product = $objp->fk_product;
3014  $this->info_bits = $objp->info_bits;
3015  $this->date_start = $this->db->jdate($objp->date_start);
3016  $this->date_end = $this->db->jdate($objp->date_end);
3017 
3018  $this->total_ht = $objp->total_ht;
3019  $this->total_tva = $objp->total_tva;
3020  $this->total_ttc = $objp->total_ttc;
3021 
3022  $this->fk_fournprice = $objp->fk_fournprice;
3023 
3024  $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
3025  $this->pa_ht = $marginInfos[0];
3026  $this->marge_tx = $marginInfos[1];
3027  $this->marque_tx = $marginInfos[2];
3028 
3029  $this->special_code = $objp->special_code;
3030  $this->product_type = $objp->product_type;
3031  $this->rang = $objp->rang;
3032 
3033  $this->ref = $objp->product_ref; // deprecated
3034  $this->product_ref = $objp->product_ref;
3035  $this->libelle = $objp->product_label; // deprecated
3036  $this->product_label = $objp->product_label;
3037  $this->product_desc = $objp->product_desc;
3038 
3039  $this->ref_fourn = $objp->ref_produit_fourn;
3040 
3041  // Multicurrency
3042  $this->fk_multicurrency = $objp->fk_multicurrency;
3043  $this->multicurrency_code = $objp->multicurrency_code;
3044  $this->multicurrency_subprice = $objp->multicurrency_subprice;
3045  $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3046  $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3047  $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3048  $this->fk_unit = $objp->fk_unit;
3049 
3050  $this->db->free($result);
3051  return 1;
3052  } else {
3053  dol_print_error($this->db);
3054  return -1;
3055  }
3056  }
3057 
3064  public function insert($notrigger = 0)
3065  {
3066  global $conf, $langs, $user;
3067 
3068  $error = 0;
3069 
3070  dol_syslog(get_class($this)."::insert rang=".$this->rang);
3071 
3072  // Clean parameters
3073  if (empty($this->tva_tx)) {
3074  $this->tva_tx = 0;
3075  }
3076  if (empty($this->vat_src_code)) {
3077  $this->vat_src_code = '';
3078  }
3079  if (empty($this->localtax1_tx)) {
3080  $this->localtax1_tx = 0;
3081  }
3082  if (empty($this->localtax2_tx)) {
3083  $this->localtax2_tx = 0;
3084  }
3085  if (empty($this->localtax1_type)) {
3086  $this->localtax1_type = 0;
3087  }
3088  if (empty($this->localtax2_type)) {
3089  $this->localtax2_type = 0;
3090  }
3091  if (empty($this->total_localtax1)) {
3092  $this->total_localtax1 = 0;
3093  }
3094  if (empty($this->total_localtax2)) {
3095  $this->total_localtax2 = 0;
3096  }
3097  if (empty($this->rang)) {
3098  $this->rang = 0;
3099  }
3100  if (empty($this->remise_percent)) {
3101  $this->remise_percent = 0;
3102  }
3103  if (empty($this->info_bits)) {
3104  $this->info_bits = 0;
3105  }
3106  if (empty($this->special_code)) {
3107  $this->special_code = 0;
3108  }
3109  if (empty($this->fk_parent_line)) {
3110  $this->fk_parent_line = 0;
3111  }
3112  if (empty($this->fk_fournprice)) {
3113  $this->fk_fournprice = 0;
3114  }
3115  if (empty($this->fk_unit)) {
3116  $this->fk_unit = 0;
3117  }
3118  if (empty($this->subprice)) {
3119  $this->subprice = 0;
3120  }
3121 
3122  if (empty($this->pa_ht)) {
3123  $this->pa_ht = 0;
3124  }
3125 
3126  // if buy price not defined, define buyprice as configured in margin admin
3127  if ($this->pa_ht == 0) {
3128  $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3129  if ($result < 0) {
3130  return $result;
3131  } else {
3132  $this->pa_ht = $result;
3133  }
3134  }
3135 
3136  // Check parameters
3137  if ($this->product_type < 0) {
3138  return -1;
3139  }
3140 
3141  $this->db->begin();
3142 
3143  // Insert line into database
3144  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'supplier_proposaldet';
3145  $sql .= ' (fk_supplier_proposal, fk_parent_line, label, description, fk_product, product_type,';
3146  $sql .= ' date_start, date_end,';
3147  $sql .= ' fk_remise_except, qty, tva_tx, vat_src_code, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3148  $sql .= ' subprice, remise_percent, ';
3149  $sql .= ' info_bits, ';
3150  $sql .= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
3151  $sql .= ' ref_fourn,';
3152  $sql .= ' fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc, fk_unit)';
3153  $sql .= " VALUES (".$this->fk_supplier_proposal.",";
3154  $sql .= " ".($this->fk_parent_line > 0 ? ((int) $this->fk_parent_line) : "null").",";
3155  $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
3156  $sql .= " '".$this->db->escape($this->desc)."',";
3157  $sql .= " ".($this->fk_product ? ((int) $this->fk_product) : "null").",";
3158  $sql .= " '".$this->db->escape($this->product_type)."',";
3159  $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3160  $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3161  $sql .= " ".($this->fk_remise_except ? ((int) $this->fk_remise_except) : "null").",";
3162  $sql .= " ".price2num($this->qty, 'MS').",";
3163  $sql .= " ".price2num($this->tva_tx).",";
3164  $sql .= " '".$this->db->escape($this->vat_src_code)."',";
3165  $sql .= " ".price2num($this->localtax1_tx).",";
3166  $sql .= " ".price2num($this->localtax2_tx).",";
3167  $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3168  $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3169  $sql .= " ".price2num($this->subprice, 'MU') .",";
3170  $sql .= " ".((float) $this->remise_percent).",";
3171  $sql .= " ".(isset($this->info_bits) ? ((int) $this->info_bits) : "null").",";
3172  $sql .= " ".price2num($this->total_ht, 'MT').",";
3173  $sql .= " ".price2num($this->total_tva, 'MT').",";
3174  $sql .= " ".price2num($this->total_localtax1, 'MT').",";
3175  $sql .= " ".price2num($this->total_localtax2, 'MT').",";
3176  $sql .= " ".price2num($this->total_ttc, 'MT').",";
3177  $sql .= " ".(!empty($this->fk_fournprice) ? ((int) $this->fk_fournprice) : "null").",";
3178  $sql .= " ".(isset($this->pa_ht) ? price2num($this->pa_ht, 'MU') : "null").",";
3179  $sql .= ' '.((int) $this->special_code).',';
3180  $sql .= ' '.((int) $this->rang).',';
3181  $sql .= " '".$this->db->escape($this->ref_fourn)."'";
3182  $sql .= ", ".($this->fk_multicurrency > 0 ? ((int) $this->fk_multicurrency) : 'null');
3183  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3184  $sql .= ", ".price2num($this->multicurrency_subprice, 'CU');
3185  $sql .= ", ".price2num($this->multicurrency_total_ht, 'CT');
3186  $sql .= ", ".price2num($this->multicurrency_total_tva, 'CT');
3187  $sql .= ", ".price2num($this->multicurrency_total_ttc, 'CT');
3188  $sql .= ", ".($this->fk_unit ? ((int) $this->fk_unit) : 'null');
3189  $sql .= ')';
3190 
3191  dol_syslog(get_class($this).'::insert', LOG_DEBUG);
3192  $resql = $this->db->query($sql);
3193  if ($resql) {
3194  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'supplier_proposaldet');
3195 
3196  if (!$error) {
3197  $result = $this->insertExtraFields();
3198  if ($result < 0) {
3199  $error++;
3200  }
3201  }
3202 
3203  if (!$error && !$notrigger) {
3204  // Call trigger
3205  $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_INSERT', $user);
3206  if ($result < 0) {
3207  $this->db->rollback();
3208  return -1;
3209  }
3210  // End call triggers
3211  }
3212 
3213  $this->db->commit();
3214  return 1;
3215  } else {
3216  $this->error = $this->db->error()." sql=".$sql;
3217  $this->db->rollback();
3218  return -1;
3219  }
3220  }
3221 
3227  public function delete()
3228  {
3229  global $conf, $langs, $user;
3230 
3231  $error = 0;
3232  $this->db->begin();
3233 
3234  $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet WHERE rowid = ".((int) $this->id);
3235  dol_syslog("SupplierProposalLine::delete", LOG_DEBUG);
3236  if ($this->db->query($sql)) {
3237  // Remove extrafields
3238  if (!$error) {
3239  $result = $this->deleteExtraFields();
3240  if ($result < 0) {
3241  $error++;
3242  dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3243  }
3244  }
3245 
3246  // Call trigger
3247  $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_DELETE', $user);
3248  if ($result < 0) {
3249  $this->db->rollback();
3250  return -1;
3251  }
3252  // End call triggers
3253 
3254  $this->db->commit();
3255 
3256  return 1;
3257  } else {
3258  $this->error = $this->db->error()." sql=".$sql;
3259  $this->db->rollback();
3260  return -1;
3261  }
3262  }
3263 
3270  public function update($notrigger = 0)
3271  {
3272  global $conf, $langs, $user;
3273 
3274  $error = 0;
3275 
3276  // Clean parameters
3277  if (empty($this->tva_tx)) {
3278  $this->tva_tx = 0;
3279  }
3280  if (empty($this->localtax1_tx)) {
3281  $this->localtax1_tx = 0;
3282  }
3283  if (empty($this->localtax2_tx)) {
3284  $this->localtax2_tx = 0;
3285  }
3286  if (empty($this->total_localtax1)) {
3287  $this->total_localtax1 = 0;
3288  }
3289  if (empty($this->total_localtax2)) {
3290  $this->total_localtax2 = 0;
3291  }
3292  if (empty($this->localtax1_type)) {
3293  $this->localtax1_type = 0;
3294  }
3295  if (empty($this->localtax2_type)) {
3296  $this->localtax2_type = 0;
3297  }
3298  if (empty($this->marque_tx)) {
3299  $this->marque_tx = 0;
3300  }
3301  if (empty($this->marge_tx)) {
3302  $this->marge_tx = 0;
3303  }
3304  if (empty($this->remise_percent)) {
3305  $this->remise_percent = 0;
3306  }
3307  if (empty($this->info_bits)) {
3308  $this->info_bits = 0;
3309  }
3310  if (empty($this->special_code)) {
3311  $this->special_code = 0;
3312  }
3313  if (empty($this->fk_parent_line)) {
3314  $this->fk_parent_line = 0;
3315  }
3316  if (empty($this->fk_fournprice)) {
3317  $this->fk_fournprice = 0;
3318  }
3319  if (empty($this->fk_unit)) {
3320  $this->fk_unit = 0;
3321  }
3322  if (empty($this->subprice)) {
3323  $this->subprice = 0;
3324  }
3325 
3326  if (empty($this->pa_ht)) {
3327  $this->pa_ht = 0;
3328  }
3329 
3330  // if buy price not defined, define buyprice as configured in margin admin
3331  if ($this->pa_ht == 0) {
3332  $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3333  if ($result < 0) {
3334  return $result;
3335  } else {
3336  $this->pa_ht = $result;
3337  }
3338  }
3339 
3340  $this->db->begin();
3341 
3342  // Mise a jour ligne en base
3343  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3344  $sql .= " description='".$this->db->escape($this->desc)."'";
3345  $sql .= " , label=".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
3346  $sql .= " , product_type=".((int) $this->product_type);
3347  $sql .= " , date_start=".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null");
3348  $sql .= " , date_end=".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null");
3349  $sql .= " , tva_tx='".price2num($this->tva_tx)."'";
3350  $sql .= " , localtax1_tx=".price2num($this->localtax1_tx);
3351  $sql .= " , localtax2_tx=".price2num($this->localtax2_tx);
3352  $sql .= " , localtax1_type='".$this->db->escape($this->localtax1_type)."'";
3353  $sql .= " , localtax2_type='".$this->db->escape($this->localtax2_type)."'";
3354  $sql .= " , qty='".price2num($this->qty)."'";
3355  $sql .= " , subprice=".price2num($this->subprice);
3356  $sql .= " , remise_percent=".price2num($this->remise_percent);
3357  $sql .= " , info_bits='".$this->db->escape($this->info_bits)."'";
3358  if (empty($this->skip_update_total)) {
3359  $sql .= " , total_ht=".price2num($this->total_ht);
3360  $sql .= " , total_tva=".price2num($this->total_tva);
3361  $sql .= " , total_ttc=".price2num($this->total_ttc);
3362  $sql .= " , total_localtax1=".price2num($this->total_localtax1);
3363  $sql .= " , total_localtax2=".price2num($this->total_localtax2);
3364  }
3365  $sql .= " , fk_product_fournisseur_price=".(!empty($this->fk_fournprice) ? "'".$this->db->escape($this->fk_fournprice)."'" : "null");
3366  $sql .= " , buy_price_ht=".price2num($this->pa_ht);
3367  if (strlen($this->special_code)) {
3368  $sql .= " , special_code=".((int) $this->special_code);
3369  }
3370  $sql .= " , fk_parent_line=".($this->fk_parent_line > 0 ? $this->fk_parent_line : "null");
3371  if (!empty($this->rang)) {
3372  $sql .= ", rang=".((int) $this->rang);
3373  }
3374  $sql .= " , ref_fourn=".(!empty($this->ref_fourn) ? "'".$this->db->escape($this->ref_fourn)."'" : "null");
3375  $sql .= " , fk_unit=".($this->fk_unit ? $this->fk_unit : 'null');
3376 
3377  // Multicurrency
3378  $sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice);
3379  $sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
3380  $sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
3381  $sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
3382 
3383  $sql .= " WHERE rowid = ".((int) $this->id);
3384 
3385  dol_syslog(get_class($this)."::update", LOG_DEBUG);
3386  $resql = $this->db->query($sql);
3387  if ($resql) {
3388  if (!$error) {
3389  $result = $this->insertExtraFields();
3390  if ($result < 0) {
3391  $error++;
3392  }
3393  }
3394 
3395  if (!$error && !$notrigger) {
3396  // Call trigger
3397  $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_MODIFY', $user);
3398  if ($result < 0) {
3399  $this->db->rollback();
3400  return -1;
3401  }
3402  // End call triggers
3403  }
3404 
3405  $this->db->commit();
3406  return 1;
3407  } else {
3408  $this->error = $this->db->error();
3409  $this->db->rollback();
3410  return -2;
3411  }
3412  }
3413 
3414  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3421  public function update_total()
3422  {
3423  // phpcs:enable
3424  $this->db->begin();
3425 
3426  // Mise a jour ligne en base
3427  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3428  $sql .= " total_ht=".price2num($this->total_ht, 'MT');
3429  $sql .= ",total_tva=".price2num($this->total_tva, 'MT');
3430  $sql .= ",total_ttc=".price2num($this->total_ttc, 'MT');
3431  $sql .= " WHERE rowid = ".((int) $this->id);
3432 
3433  dol_syslog("SupplierProposalLine::update_total", LOG_DEBUG);
3434 
3435  $resql = $this->db->query($sql);
3436  if ($resql) {
3437  $this->db->commit();
3438  return 1;
3439  } else {
3440  $this->error = $this->db->error();
3441  $this->db->rollback();
3442  return -2;
3443  }
3444  }
3445 }
$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.
setErrorsFromObject($object)
setErrorsFromObject
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 commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
static commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product 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.
static replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
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.
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)
getKanbanView($option='', $arraydata=null)
Return clicable link of object (with eventually picto)
getTooltipContentArray($params)
getTooltipContentArray
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:48
trait CommonIncoterm
Superclass for incoterm classes.
if(isModEnabled('facture') && $user->hasRight('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') && $user->hasRight('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)) $sql
Social contributions to pay.
Definition: index.php:746
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:1507
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:1356
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:62
dol_delete_preview($object)
Delete all preview files linked to object instance.
Definition: files.lib.php:1559
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_print_date($time, $format='', $tzoutput='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
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)
Function that returns whether VAT must be recoverable collected VAT (e.g.
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.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
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:926
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