dolibarr  18.0.0
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 
1472  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1473  $oldref = dol_sanitizeFileName($this->ref);
1474  $newref = dol_sanitizeFileName($num);
1475  $dirsource = $conf->supplier_proposal->dir_output.'/'.$oldref;
1476  $dirdest = $conf->supplier_proposal->dir_output.'/'.$newref;
1477  if (!$error && file_exists($dirsource)) {
1478  dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
1479  if (@rename($dirsource, $dirdest)) {
1480  dol_syslog("Rename ok");
1481  // Rename docs starting with $oldref with $newref
1482  $listoffiles = dol_dir_list($conf->supplier_proposal->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1483  foreach ($listoffiles as $fileentry) {
1484  $dirsource = $fileentry['name'];
1485  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1486  $dirsource = $fileentry['path'].'/'.$dirsource;
1487  $dirdest = $fileentry['path'].'/'.$dirdest;
1488  @rename($dirsource, $dirdest);
1489  }
1490  }
1491  }
1492  }
1493 
1494  $this->ref = $num;
1495  $this->brouillon = 0;
1496  $this->statut = 1;
1497  $this->user_valid_id = $user->id;
1498  $this->datev = $now;
1499 
1500  $this->db->commit();
1501  return 1;
1502  } else {
1503  $this->db->rollback();
1504  return -1;
1505  }
1506  } else {
1507  dol_syslog("You don't have permission to validate supplier proposal", LOG_WARNING);
1508  return -2;
1509  }
1510  }
1511 
1512  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1521  public function set_date_livraison($user, $delivery_date)
1522  {
1523  // phpcs:enable
1524  return $this->setDeliveryDate($user, $delivery_date);
1525  }
1526 
1534  public function setDeliveryDate($user, $delivery_date)
1535  {
1536  if (!empty($user->rights->supplier_proposal->creer)) {
1537  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1538  $sql .= " SET date_livraison = ".($delivery_date != '' ? "'".$this->db->idate($delivery_date)."'" : 'null');
1539  $sql .= " WHERE rowid = ".((int) $this->id);
1540 
1541  if ($this->db->query($sql)) {
1542  $this->date_livraison = $delivery_date;
1543  $this->delivery_date = $delivery_date;
1544  return 1;
1545  } else {
1546  $this->error = $this->db->error();
1547  dol_syslog(get_class($this)."::setDeliveryDate Erreur SQL");
1548  return -1;
1549  }
1550  }
1551  return 0;
1552  }
1553 
1554  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1562  public function set_remise_percent($user, $remise)
1563  {
1564  // phpcs:enable
1565  $remise = trim($remise) ?trim($remise) : 0;
1566 
1567  if (!empty($user->rights->supplier_proposal->creer)) {
1568  $remise = price2num($remise, 2);
1569 
1570  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal SET remise_percent = ".((float) $remise);
1571  $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1572 
1573  if ($this->db->query($sql)) {
1574  $this->remise_percent = ((float) $remise);
1575  $this->update_price(1);
1576  return 1;
1577  } else {
1578  $this->error = $this->db->error();
1579  return -1;
1580  }
1581  }
1582  return 0;
1583  }
1584 
1585 
1586  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1594  public function set_remise_absolue($user, $remise)
1595  {
1596  // phpcs:enable
1597  if (empty($remise)) {
1598  $remise = 0;
1599  }
1600 
1601  $remise = price2num($remise);
1602 
1603  if (!empty($user->rights->supplier_proposal->creer)) {
1604  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1605  $sql .= " SET remise_absolue = ".((float) $remise);
1606  $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1607 
1608  if ($this->db->query($sql)) {
1609  $this->remise_absolue = $remise;
1610  $this->update_price(1);
1611  return 1;
1612  } else {
1613  $this->error = $this->db->error();
1614  return -1;
1615  }
1616  }
1617  return 0;
1618  }
1619 
1620 
1621 
1631  public function reopen($user, $statut, $note = '', $notrigger = 0)
1632  {
1633  global $langs, $conf;
1634 
1635  $this->statut = $statut;
1636  $error = 0;
1637 
1638  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1639  $sql .= " SET fk_statut = ".((int) $this->statut).",";
1640  if (!empty($note)) {
1641  $sql .= " note_private = '".$this->db->escape($note)."',";
1642  }
1643  $sql .= " date_cloture=NULL, fk_user_cloture=NULL";
1644  $sql .= " WHERE rowid = ".((int) $this->id);
1645 
1646  $this->db->begin();
1647 
1648  dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
1649  $resql = $this->db->query($sql);
1650  if (!$resql) {
1651  $error++; $this->errors[] = "Error ".$this->db->lasterror();
1652  }
1653  if (!$error) {
1654  if (!$notrigger) {
1655  // Call trigger
1656  $result = $this->call_trigger('PROPOSAL_SUPPLIER_REOPEN', $user);
1657  if ($result < 0) {
1658  $error++;
1659  }
1660  // End call triggers
1661  }
1662  }
1663 
1664  // Commit or rollback
1665  if ($error) {
1666  if (!empty($this->errors)) {
1667  foreach ($this->errors as $errmsg) {
1668  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1669  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1670  }
1671  }
1672  $this->db->rollback();
1673  return -1 * $error;
1674  } else {
1675  $this->db->commit();
1676  return 1;
1677  }
1678  }
1679 
1680 
1689  public function cloture($user, $status, $note)
1690  {
1691  global $langs, $conf;
1692  $hidedetails = 0;
1693  $hidedesc = 0;
1694  $hideref = 0;
1695  $this->statut = $status;
1696  $error = 0;
1697  $now = dol_now();
1698 
1699  $this->db->begin();
1700 
1701  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1702  $sql .= " SET fk_statut = ".((int) $status).", note_private = '".$this->db->escape($note)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".$user->id;
1703  $sql .= " WHERE rowid = ".((int) $this->id);
1704 
1705  $resql = $this->db->query($sql);
1706  if ($resql) {
1707  $modelpdf = $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED ? $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED : (empty($this->model_pdf) ? '' : $this->model_pdf);
1708  $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_REFUSED';
1709 
1710  if ($status == 2) {
1711  $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_SIGNED';
1712  $modelpdf = $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL ? $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL : (empty($this->model_pdf) ? '' : $this->model_pdf);
1713 
1714  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
1715  $result = $this->updateOrCreatePriceFournisseur($user);
1716  }
1717  }
1718  if ($status == 4) {
1719  $triggerName = 'PROPOSAL_SUPPLIER_CLASSIFY_BILLED';
1720  }
1721 
1722  if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE)) {
1723  // Define output language
1724  $outputlangs = $langs;
1725  if (getDolGlobalInt('MAIN_MULTILANGS')) {
1726  $outputlangs = new Translate("", $conf);
1727  $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
1728  $outputlangs->setDefaultLang($newlang);
1729  }
1730  //$ret=$object->fetch($id); // Reload to get new records
1731  $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1732  }
1733 
1734  // Call trigger
1735  $result = $this->call_trigger($triggerName, $user);
1736  if ($result < 0) {
1737  $error++;
1738  }
1739  // End call triggers
1740 
1741  if (!$error) {
1742  $this->db->commit();
1743  return 1;
1744  } else {
1745  $this->db->rollback();
1746  return -1;
1747  }
1748  } else {
1749  $this->error = $this->db->lasterror();
1750  $this->errors[] = $this->db->lasterror();
1751  $this->db->rollback();
1752  return -1;
1753  }
1754  }
1755 
1762  public function updateOrCreatePriceFournisseur($user)
1763  {
1764  global $conf;
1765 
1766  dol_syslog(get_class($this)."::updateOrCreatePriceFournisseur", LOG_DEBUG);
1767  foreach ($this->lines as $product) {
1768  if ($product->subprice <= 0) {
1769  continue;
1770  }
1771  $productsupplier = new ProductFournisseur($this->db);
1772 
1773  $multicurrency_tx = 1;
1774  $fk_multicurrency = 0;
1775 
1776  if (empty($this->thirdparty)) {
1777  $this->fetch_thirdparty();
1778  }
1779 
1780  $ref_fourn = $product->ref_fourn;
1781  if (empty($ref_fourn)) {
1782  $ref_fourn = $product->ref_supplier;
1783  }
1784  if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1785  list($fk_multicurrency, $multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $product->multicurrency_code);
1786  }
1787  $productsupplier->id = $product->fk_product;
1788 
1789  $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, '', '', '');
1790  }
1791 
1792  return 1;
1793  }
1794 
1803  public function updatePriceFournisseur($idProductFournPrice, $product, $user)
1804  {
1805  $price = price2num($product->subprice * $product->qty, 'MU');
1806  $unitPrice = price2num($product->subprice, 'MU');
1807 
1808  $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);
1809 
1810  $resql = $this->db->query($sql);
1811  if (!$resql) {
1812  $this->error = $this->db->error();
1813  $this->db->rollback();
1814  return -1;
1815  }
1816  return 1;
1817  }
1818 
1826  public function createPriceFournisseur($product, $user)
1827  {
1828  global $conf;
1829 
1830  $price = price2num($product->subprice * $product->qty, 'MU');
1831  $qty = price2num($product->qty);
1832  $unitPrice = price2num($product->subprice, 'MU');
1833 
1834  $now = dol_now();
1835 
1836  $values = array(
1837  "'".$this->db->idate($now)."'",
1838  $product->fk_product,
1839  $this->thirdparty->id,
1840  "'".$product->ref_fourn."'",
1841  $price,
1842  $qty,
1843  $unitPrice,
1844  $product->tva_tx,
1845  $user->id
1846  );
1847  if (isModEnabled("multicurrency")) {
1848  if (!empty($product->multicurrency_code)) {
1849  include_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
1850  $multicurrency = new MultiCurrency($this->db); //need to fetch because empty fk_multicurrency and rate
1851  $multicurrency->fetch(0, $product->multicurrency_code);
1852  if (!empty($multicurrency->id)) {
1853  $values[] = $multicurrency->id;
1854  $values[] = "'".$product->multicurrency_code."'";
1855  $values[] = $product->multicurrency_subprice;
1856  $values[] = $product->multicurrency_total_ht;
1857  $values[] = $multicurrency->rate->rate;
1858  } else {
1859  for ($i = 0; $i < 5; $i++) {
1860  $values[] = 'NULL';
1861  }
1862  }
1863  }
1864  }
1865 
1866  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'product_fournisseur_price ';
1867  $sql .= '(datec, fk_product, fk_soc, ref_fourn, price, quantity, unitprice, tva_tx, fk_user';
1868  if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1869  $sql .= ',fk_multicurrency, multicurrency_code, multicurrency_unitprice, multicurrency_price, multicurrency_tx';
1870  }
1871  $sql .= ') VALUES ('.implode(',', $values).')';
1872 
1873  $resql = $this->db->query($sql);
1874  if (!$resql) {
1875  $this->error = $this->db->error();
1876  $this->db->rollback();
1877  return -1;
1878  }
1879  return 1;
1880  }
1881 
1882  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1889  public function setDraft($user)
1890  {
1891  // phpcs:enable
1892  global $conf, $langs;
1893 
1894  $error = 0;
1895 
1896  if ($this->statut == self::STATUS_DRAFT) {
1897  dol_syslog(get_class($this)."::setDraft already draft status", LOG_WARNING);
1898  return 0;
1899  }
1900 
1901  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1902  $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1903  $sql .= " WHERE rowid = ".((int) $this->id);
1904 
1905  if ($this->db->query($sql)) {
1906  if (!$error) {
1907  $this->oldcopy = clone $this;
1908  }
1909 
1910  if (!$error) {
1911  // Call trigger
1912  $result = $this->call_trigger('PROPOSAL_SUPPLIER_UNVALIDATE', $user);
1913  if ($result < 0) {
1914  $error++;
1915  }
1916  }
1917 
1918  if (!$error) {
1919  $this->statut = self::STATUS_DRAFT;
1920  $this->brouillon = 1;
1921  $this->db->commit();
1922  return 1;
1923  } else {
1924  $this->db->rollback();
1925  return -1;
1926  }
1927  } else {
1928  return -1;
1929  }
1930  }
1931 
1932 
1933  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1947  public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datec', $sortorder = 'DESC')
1948  {
1949  // phpcs:enable
1950  global $conf, $user;
1951 
1952  $ga = array();
1953 
1954  $sql = "SELECT s.rowid, s.nom as name, s.client,";
1955  $sql .= " p.rowid as supplier_proposalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
1956  $sql .= " p.datep as dp, p.fin_validite as datelimite";
1957  if (empty($user->rights->societe->client->voir) && !$socid) {
1958  $sql .= ", sc.fk_soc, sc.fk_user";
1959  }
1960  $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."supplier_proposal as p, ".MAIN_DB_PREFIX."c_propalst as c";
1961  if (empty($user->rights->societe->client->voir) && !$socid) {
1962  $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
1963  }
1964  $sql .= " WHERE p.entity IN (".getEntity('supplier_proposal').")";
1965  $sql .= " AND p.fk_soc = s.rowid";
1966  $sql .= " AND p.fk_statut = c.id";
1967  if (empty($user->rights->societe->client->voir) && !$socid) { //restriction
1968  $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
1969  }
1970  if ($socid) {
1971  $sql .= " AND s.rowid = ".((int) $socid);
1972  }
1973  if ($draft) {
1974  $sql .= " AND p.fk_statut = 0";
1975  }
1976  if ($notcurrentuser > 0) {
1977  $sql .= " AND p.fk_user_author <> ".$user->id;
1978  }
1979  $sql .= $this->db->order($sortfield, $sortorder);
1980  $sql .= $this->db->plimit($limit, $offset);
1981 
1982  $result = $this->db->query($sql);
1983  if ($result) {
1984  $num = $this->db->num_rows($result);
1985  if ($num) {
1986  $i = 0;
1987  while ($i < $num) {
1988  $obj = $this->db->fetch_object($result);
1989 
1990  if ($shortlist == 1) {
1991  $ga[$obj->supplier_proposalid] = $obj->ref;
1992  } elseif ($shortlist == 2) {
1993  $ga[$obj->supplier_proposalid] = $obj->ref.' ('.$obj->name.')';
1994  } else {
1995  $ga[$i]['id'] = $obj->supplier_proposalid;
1996  $ga[$i]['ref'] = $obj->ref;
1997  $ga[$i]['name'] = $obj->name;
1998  }
1999 
2000  $i++;
2001  }
2002  }
2003  return $ga;
2004  } else {
2005  dol_print_error($this->db);
2006  return -1;
2007  }
2008  }
2009 
2017  public function delete($user, $notrigger = 0)
2018  {
2019  global $conf, $langs;
2020  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2021 
2022  $error = 0;
2023 
2024  $this->db->begin();
2025 
2026  if (!$notrigger) {
2027  // Call trigger
2028  $result = $this->call_trigger('PROPOSAL_SUPPLIER_DELETE', $user);
2029  if ($result < 0) {
2030  $error++;
2031  }
2032  // End call triggers
2033  }
2034 
2035  if (!$error) {
2036  $main = MAIN_DB_PREFIX.'supplier_proposaldet';
2037  $ef = $main."_extrafields";
2038  $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_supplier_proposal = ".((int) $this->id).")";
2039  $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet WHERE fk_supplier_proposal = ".((int) $this->id);
2040  if ($this->db->query($sql)) {
2041  $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposal WHERE rowid = ".((int) $this->id);
2042  if ($this->db->query($sqlef) && $this->db->query($sql)) {
2043  // Delete linked object
2044  $res = $this->deleteObjectLinked();
2045  if ($res < 0) {
2046  $error++;
2047  }
2048 
2049  if (!$error) {
2050  // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2051  $this->deleteEcmFiles();
2052 
2053  // We remove directory
2054  $ref = dol_sanitizeFileName($this->ref);
2055  if ($conf->supplier_proposal->dir_output && !empty($this->ref)) {
2056  $dir = $conf->supplier_proposal->dir_output."/".$ref;
2057  $file = $dir."/".$ref.".pdf";
2058  if (file_exists($file)) {
2059  dol_delete_preview($this);
2060 
2061  if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2062  $this->error = 'ErrorFailToDeleteFile';
2063  $this->errors = array('ErrorFailToDeleteFile');
2064  $this->db->rollback();
2065  return 0;
2066  }
2067  }
2068  if (file_exists($dir)) {
2069  $res = @dol_delete_dir_recursive($dir);
2070  if (!$res) {
2071  $this->error = 'ErrorFailToDeleteDir';
2072  $this->errors = array('ErrorFailToDeleteDir');
2073  $this->db->rollback();
2074  return 0;
2075  }
2076  }
2077  }
2078  }
2079 
2080  // Removed extrafields
2081  if (!$error) {
2082  $result = $this->deleteExtraFields();
2083  if ($result < 0) {
2084  $error++;
2085  $errorflag = -4;
2086  dol_syslog(get_class($this)."::delete erreur ".$errorflag." ".$this->error, LOG_ERR);
2087  }
2088  }
2089 
2090  if (!$error) {
2091  dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
2092  $this->db->commit();
2093  return 1;
2094  } else {
2095  $this->error = $this->db->lasterror();
2096  $this->db->rollback();
2097  return 0;
2098  }
2099  } else {
2100  $this->error = $this->db->lasterror();
2101  $this->db->rollback();
2102  return -3;
2103  }
2104  } else {
2105  $this->error = $this->db->lasterror();
2106  $this->db->rollback();
2107  return -2;
2108  }
2109  } else {
2110  $this->db->rollback();
2111  return -1;
2112  }
2113  }
2114 
2121  public function info($id)
2122  {
2123  $sql = "SELECT c.rowid, ";
2124  $sql .= " c.datec, c.date_valid as datev, c.date_cloture as dateo,";
2125  $sql .= " c.fk_user_author, c.fk_user_valid, c.fk_user_cloture";
2126  $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as c";
2127  $sql .= " WHERE c.rowid = ".((int) $id);
2128 
2129  $result = $this->db->query($sql);
2130 
2131  if ($result) {
2132  if ($this->db->num_rows($result)) {
2133  $obj = $this->db->fetch_object($result);
2134 
2135  $this->id = $obj->rowid;
2136 
2137  $this->date_creation = $this->db->jdate($obj->datec);
2138  $this->date_validation = $this->db->jdate($obj->datev);
2139  $this->date_cloture = $this->db->jdate($obj->dateo);
2140 
2141  $cuser = new User($this->db);
2142  $cuser->fetch($obj->fk_user_author);
2143  $this->user_creation = $cuser;
2144 
2145  if ($obj->fk_user_valid) {
2146  $vuser = new User($this->db);
2147  $vuser->fetch($obj->fk_user_valid);
2148  $this->user_validation = $vuser;
2149  }
2150 
2151  if ($obj->fk_user_cloture) {
2152  $cluser = new User($this->db);
2153  $cluser->fetch($obj->fk_user_cloture);
2154  $this->user_cloture = $cluser;
2155  }
2156  }
2157  $this->db->free($result);
2158  } else {
2159  dol_print_error($this->db);
2160  }
2161  }
2162 
2163 
2170  public function getLibStatut($mode = 0)
2171  {
2172  return $this->LibStatut((isset($this->statut) ? $this->statut : $this->status), $mode);
2173  }
2174 
2175  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2183  public function LibStatut($status, $mode = 1)
2184  {
2185  // phpcs:enable
2186 
2187  // Init/load array of translation of status
2188  if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
2189  global $langs;
2190  $langs->load("supplier_proposal");
2191  $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraft");
2192  $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidated");
2193  $this->labelStatus[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSigned");
2194  $this->labelStatus[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSigned");
2195  $this->labelStatus[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosed");
2196  $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraftShort");
2197  $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidatedShort");
2198  $this->labelStatusShort[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSignedShort");
2199  $this->labelStatusShort[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSignedShort");
2200  $this->labelStatusShort[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosedShort");
2201  }
2202 
2203  $statusnew = '';
2204  if ($status == self::STATUS_DRAFT) {
2205  $statusnew = 'status0';
2206  } elseif ($status == self::STATUS_VALIDATED) {
2207  $statusnew = 'status1';
2208  } elseif ($status == self::STATUS_SIGNED) {
2209  $statusnew = 'status4';
2210  } elseif ($status == self::STATUS_NOTSIGNED) {
2211  $statusnew = 'status9';
2212  } elseif ($status == self::STATUS_CLOSE) {
2213  $statusnew = 'status6';
2214  }
2215 
2216  return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusnew, $mode);
2217  }
2218 
2219 
2220  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2228  public function load_board($user, $mode)
2229  {
2230  // phpcs:enable
2231  global $conf, $user, $langs;
2232 
2233  $now = dol_now();
2234 
2235  $this->nbtodo = $this->nbtodolate = 0;
2236  $clause = " WHERE";
2237 
2238  $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.date_cloture as datefin";
2239  $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2240  if (empty($user->rights->societe->client->voir) && !$user->socid) {
2241  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
2242  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2243  $clause = " AND";
2244  }
2245  $sql .= $clause." p.entity IN (".getEntity('supplier_proposal').")";
2246  if ($mode == 'opened') {
2247  $sql .= " AND p.fk_statut = 1";
2248  }
2249  if ($mode == 'signed') {
2250  $sql .= " AND p.fk_statut = 2";
2251  }
2252  if ($user->socid) {
2253  $sql .= " AND p.fk_soc = ".((int) $user->socid);
2254  }
2255 
2256  $resql = $this->db->query($sql);
2257  if ($resql) {
2258  $label = $labelShort = '';
2259  $status = '';
2260  if ($mode == 'opened') {
2261  $delay_warning = !empty($conf->supplier_proposal->cloture->warning_delay) ? $conf->supplier_proposal->cloture->warning_delay : 0;
2262  $status = self::STATUS_VALIDATED;
2263  $label = $langs->trans("SupplierProposalsToClose");
2264  $labelShort = $langs->trans("ToAcceptRefuse");
2265  }
2266  if ($mode == 'signed') {
2267  $delay_warning = !empty($conf->supplier_proposal->facturation->warning_delay) ? $conf->supplier_proposal->facturation->warning_delay : 0;
2268  $status = self::STATUS_SIGNED;
2269  $label = $langs->trans("SupplierProposalsToProcess"); // May be billed or ordered
2270  $labelShort = $langs->trans("ToClose");
2271  }
2272 
2273  $response = new WorkboardResponse();
2274  $response->warning_delay = $delay_warning / 60 / 60 / 24;
2275  $response->label = $label;
2276  $response->labelShort = $labelShort;
2277  $response->url = DOL_URL_ROOT.'/supplier_proposal/list.php?search_status='.$status;
2278  $response->img = img_object('', "propal");
2279 
2280  // This assignment in condition is not a bug. It allows walking the results.
2281  while ($obj = $this->db->fetch_object($resql)) {
2282  $response->nbtodo++;
2283  if ($mode == 'opened') {
2284  $datelimit = $this->db->jdate($obj->datefin);
2285  if ($datelimit < ($now - $delay_warning)) {
2286  $response->nbtodolate++;
2287  }
2288  }
2289  // TODO Definir regle des propales a facturer en retard
2290  // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
2291  }
2292  return $response;
2293  } else {
2294  $this->error = $this->db->lasterror();
2295  return -1;
2296  }
2297  }
2298 
2299 
2307  public function initAsSpecimen()
2308  {
2309  global $user, $langs, $conf;
2310 
2311  // Load array of products prodids
2312  $num_prods = 0;
2313  $prodids = array();
2314  $sql = "SELECT rowid";
2315  $sql .= " FROM ".MAIN_DB_PREFIX."product";
2316  $sql .= " WHERE entity IN (".getEntity('product').")";
2317  $sql .= $this->db->plimit(100);
2318 
2319  $resql = $this->db->query($sql);
2320  if ($resql) {
2321  $num_prods = $this->db->num_rows($resql);
2322  $i = 0;
2323  while ($i < $num_prods) {
2324  $i++;
2325  $row = $this->db->fetch_row($resql);
2326  $prodids[$i] = $row[0];
2327  }
2328  }
2329 
2330  // Initialise parametres
2331  $this->id = 0;
2332  $this->ref = 'SPECIMEN';
2333  $this->specimen = 1;
2334  $this->socid = 1;
2335  $this->date = time();
2336  $this->cond_reglement_id = 1;
2337  $this->cond_reglement_code = 'RECEP';
2338  $this->mode_reglement_id = 7;
2339  $this->mode_reglement_code = 'CHQ';
2340  $this->note_public = 'This is a comment (public)';
2341  $this->note_private = 'This is a comment (private)';
2342  // Lines
2343  $nbp = 5;
2344  $xnbp = 0;
2345  while ($xnbp < $nbp) {
2346  $line = new SupplierProposalLine($this->db);
2347  $line->desc = $langs->trans("Description")." ".$xnbp;
2348  $line->qty = 1;
2349  $line->subprice = 100;
2350  $line->tva_tx = 19.6;
2351  $line->localtax1_tx = 0;
2352  $line->localtax2_tx = 0;
2353  if ($xnbp == 2) {
2354  $line->total_ht = 50;
2355  $line->total_ttc = 59.8;
2356  $line->total_tva = 9.8;
2357  $line->remise_percent = 50;
2358  } else {
2359  $line->total_ht = 100;
2360  $line->total_ttc = 119.6;
2361  $line->total_tva = 19.6;
2362  $line->remise_percent = 00;
2363  }
2364 
2365  if ($num_prods > 0) {
2366  $prodid = mt_rand(1, $num_prods);
2367  $line->fk_product = $prodids[$prodid];
2368  }
2369 
2370  $this->lines[$xnbp] = $line;
2371 
2372  $this->total_ht += $line->total_ht;
2373  $this->total_tva += $line->total_tva;
2374  $this->total_ttc += $line->total_ttc;
2375 
2376  $xnbp++;
2377  }
2378  }
2379 
2380  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2386  public function load_state_board()
2387  {
2388  // phpcs:enable
2389  global $conf, $user;
2390 
2391  $this->nb = array();
2392  $clause = "WHERE";
2393 
2394  $sql = "SELECT count(p.rowid) as nb";
2395  $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2396  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
2397  if (empty($user->rights->societe->client->voir) && !$user->socid) {
2398  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2399  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2400  $clause = "AND";
2401  }
2402  $sql .= " ".$clause." p.entity IN (".getEntity('supplier_proposal').")";
2403 
2404  $resql = $this->db->query($sql);
2405  if ($resql) {
2406  // This assignment in condition is not a bug. It allows walking the results.
2407  while ($obj = $this->db->fetch_object($resql)) {
2408  $this->nb["supplier_proposals"] = $obj->nb;
2409  }
2410  $this->db->free($resql);
2411  return 1;
2412  } else {
2413  dol_print_error($this->db);
2414  $this->error = $this->db->lasterror();
2415  return -1;
2416  }
2417  }
2418 
2419 
2427  public function getNextNumRef($soc)
2428  {
2429  global $conf, $db, $langs;
2430  $langs->load("supplier_proposal");
2431 
2432  if (!empty($conf->global->SUPPLIER_PROPOSAL_ADDON)) {
2433  $mybool = false;
2434 
2435  $file = $conf->global->SUPPLIER_PROPOSAL_ADDON.".php";
2436  $classname = $conf->global->SUPPLIER_PROPOSAL_ADDON;
2437 
2438  // Include file with class
2439  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2440  foreach ($dirmodels as $reldir) {
2441  $dir = dol_buildpath($reldir."core/modules/supplier_proposal/");
2442 
2443  // Load file with numbering class (if found)
2444  $mybool |= @include_once $dir.$file;
2445  }
2446 
2447  if (!$mybool) {
2448  dol_print_error('', "Failed to include file ".$file);
2449  return '';
2450  }
2451 
2452  $obj = new $classname();
2453  $numref = "";
2454  $numref = $obj->getNextValue($soc, $this);
2455 
2456  if ($numref != "") {
2457  return $numref;
2458  } else {
2459  $this->error = $obj->error;
2460  return "";
2461  }
2462  } else {
2463  $langs->load("errors");
2464  print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("SupplierProposal"));
2465  return "";
2466  }
2467  }
2468 
2476  public function getTooltipContentArray($params)
2477  {
2478  global $conf, $langs, $menumanager;
2479 
2480  $langs->load('supplier_proposal');
2481 
2482  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
2483  return ['optimize' => $langs->trans("ShowSupplierProposal")];
2484  }
2485 
2486  $option = $params['option'] ?? '';
2487  $datas = [];
2488 
2489  $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("SupplierProposal").'</u>';
2490  if (isset($this->status)) {
2491  $datas['picto'] .= ' '.$this->getLibStatut(5);
2492  }
2493  if (!empty($this->ref)) {
2494  $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2495  }
2496  if (!empty($this->ref_fourn)) {
2497  $datas['ref_supplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_fourn;
2498  }
2499  if (!empty($this->total_ht)) {
2500  $datas['amount_ht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2501  }
2502  if (!empty($this->total_tva)) {
2503  $datas['amount_vat'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2504  }
2505  if (!empty($this->total_ttc)) {
2506  $datas['amount_ttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2507  }
2508 
2509  return $datas;
2510  }
2511 
2523  public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2524  {
2525  global $langs, $conf, $user, $hookmanager;
2526 
2527  if (!empty($conf->dol_no_mouse_hover)) {
2528  $notooltip = 1; // Force disable tooltips
2529  }
2530 
2531  $url = '';
2532  $result = '';
2533  $params = [
2534  'id' => $this->id,
2535  'objecttype' => $this->element,
2536  'option' => $option,
2537  ];
2538  $classfortooltip = 'classfortooltip';
2539  $dataparams = '';
2540  if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2541  $classfortooltip = 'classforajaxtooltip';
2542  $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
2543  $label = '';
2544  } else {
2545  $label = implode($this->getTooltipContentArray($params));
2546  }
2547 
2548  if ($option == '') {
2549  $url = DOL_URL_ROOT.'/supplier_proposal/card.php?id='.$this->id.$get_params;
2550  }
2551  if ($option == 'document') {
2552  $url = DOL_URL_ROOT.'/supplier_proposal/document.php?id='.$this->id.$get_params;
2553  }
2554 
2555  if ($option !== 'nolink') {
2556  // Add param to save lastsearch_values or not
2557  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2558  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2559  $add_save_lastsearch_values = 1;
2560  }
2561  if ($add_save_lastsearch_values) {
2562  $url .= '&save_lastsearch_values=1';
2563  }
2564  }
2565 
2566  $linkclose = '';
2567  if (empty($notooltip) && $user->hasRight('propal', 'lire')) {
2568  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
2569  $label = $langs->trans("ShowSupplierProposal");
2570  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
2571  }
2572  $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
2573  $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
2574  }
2575 
2576  $linkstart = '<a href="'.$url.'"';
2577  $linkstart .= $linkclose.'>';
2578  $linkend = '</a>';
2579 
2580  $result .= $linkstart;
2581  if ($withpicto) {
2582  $result .= img_object(($notooltip ? '' : $label), $this->picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : $dataparams.' class="'.(($withpicto != 2) ? 'paddingright ' : '').$classfortooltip.'"'), 0, 0, $notooltip ? 0 : 1);
2583  }
2584  if ($withpicto != 2) {
2585  $result .= $this->ref;
2586  }
2587  $result .= $linkend;
2588 
2589  if ($addlinktonotes) {
2590  $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2591  if ($txttoshow) {
2592  $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2593  $result .= ' <span class="note inline-block">';
2594  $result .= '<a href="'.DOL_URL_ROOT.'/supplier_proposal/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2595  $result .= img_picto('', 'note');
2596  $result .= '</a>';
2597  //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
2598  //$result.='</a>';
2599  $result .= '</span>';
2600  }
2601  }
2602  global $action;
2603  $hookmanager->initHooks(array($this->element . 'dao'));
2604  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
2605  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2606  if ($reshook > 0) {
2607  $result = $hookmanager->resPrint;
2608  } else {
2609  $result .= $hookmanager->resPrint;
2610  }
2611  return $result;
2612  }
2613 
2619  public function getLinesArray()
2620  {
2621  // For other object, here we call fetch_lines. But fetch_lines does not exists on supplier proposal
2622 
2623  $sql = 'SELECT pt.rowid, pt.label as custom_label, pt.description, pt.fk_product, pt.fk_remise_except,';
2624  $sql .= ' pt.qty, pt.tva_tx, pt.vat_src_code, pt.remise_percent, pt.subprice, pt.info_bits,';
2625  $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,';
2626  $sql .= ' pt.product_type, pt.rang, pt.fk_parent_line,';
2627  $sql .= ' p.label as product_label, p.ref, p.fk_product_type, p.rowid as prodid,';
2628  $sql .= ' p.description as product_desc, pt.ref_fourn as ref_supplier,';
2629  $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';
2630  $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pt';
2631  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pt.fk_product=p.rowid';
2632  $sql .= ' WHERE pt.fk_supplier_proposal = '.((int) $this->id);
2633  $sql .= ' ORDER BY pt.rang ASC, pt.rowid';
2634 
2635  dol_syslog(get_class($this).'::getLinesArray', LOG_DEBUG);
2636  $resql = $this->db->query($sql);
2637  if ($resql) {
2638  $num = $this->db->num_rows($resql);
2639  $i = 0;
2640 
2641  while ($i < $num) {
2642  $obj = $this->db->fetch_object($resql);
2643 
2644  $this->lines[$i] = new SupplierProposalLine($this->db);
2645  $this->lines[$i]->id = $obj->rowid; // for backward compatibility
2646  $this->lines[$i]->rowid = $obj->rowid;
2647  $this->lines[$i]->label = $obj->custom_label;
2648  $this->lines[$i]->description = $obj->description;
2649  $this->lines[$i]->fk_product = $obj->fk_product;
2650  $this->lines[$i]->ref = $obj->ref;
2651  $this->lines[$i]->product_label = $obj->product_label;
2652  $this->lines[$i]->product_desc = $obj->product_desc;
2653  $this->lines[$i]->fk_product_type = $obj->fk_product_type; // deprecated
2654  $this->lines[$i]->product_type = $obj->product_type;
2655  $this->lines[$i]->qty = $obj->qty;
2656  $this->lines[$i]->subprice = $obj->subprice;
2657  $this->lines[$i]->fk_remise_except = $obj->fk_remise_except;
2658  $this->lines[$i]->remise_percent = $obj->remise_percent;
2659  $this->lines[$i]->tva_tx = $obj->tva_tx;
2660  $this->lines[$i]->vat_src_code = $obj->vat_src_code;
2661  $this->lines[$i]->info_bits = $obj->info_bits;
2662  $this->lines[$i]->total_ht = $obj->total_ht;
2663  $this->lines[$i]->total_tva = $obj->total_tva;
2664  $this->lines[$i]->total_ttc = $obj->total_ttc;
2665  $this->lines[$i]->fk_fournprice = $obj->fk_fournprice;
2666  $marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->lines[$i]->fk_fournprice, $obj->pa_ht);
2667  $this->lines[$i]->pa_ht = $marginInfos[0];
2668  $this->lines[$i]->marge_tx = $marginInfos[1];
2669  $this->lines[$i]->marque_tx = $marginInfos[2];
2670  $this->lines[$i]->fk_parent_line = $obj->fk_parent_line;
2671  $this->lines[$i]->special_code = $obj->special_code;
2672  $this->lines[$i]->rang = $obj->rang;
2673 
2674  $this->lines[$i]->ref_fourn = $obj->ref_supplier; // deprecated
2675  $this->lines[$i]->ref_supplier = $obj->ref_supplier;
2676 
2677  // Multicurrency
2678  $this->lines[$i]->fk_multicurrency = $obj->fk_multicurrency;
2679  $this->lines[$i]->multicurrency_code = $obj->multicurrency_code;
2680  $this->lines[$i]->multicurrency_subprice = $obj->multicurrency_subprice;
2681  $this->lines[$i]->multicurrency_total_ht = $obj->multicurrency_total_ht;
2682  $this->lines[$i]->multicurrency_total_tva = $obj->multicurrency_total_tva;
2683  $this->lines[$i]->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
2684  $this->lines[$i]->fk_unit = $obj->fk_unit;
2685 
2686  $i++;
2687  }
2688  $this->db->free($resql);
2689 
2690  return 1;
2691  } else {
2692  $this->error = $this->db->error();
2693  return -1;
2694  }
2695  }
2696 
2708  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2709  {
2710  global $conf, $langs;
2711 
2712  $langs->load("supplier_proposal");
2713  $outputlangs->load("products");
2714 
2715  if (!dol_strlen($modele)) {
2716  $modele = 'aurore';
2717 
2718  if ($this->model_pdf) {
2719  $modele = $this->model_pdf;
2720  } elseif (!empty($conf->global->SUPPLIER_PROPOSAL_ADDON_PDF)) {
2721  $modele = $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF;
2722  }
2723  }
2724 
2725  $modelpath = "core/modules/supplier_proposal/doc/";
2726 
2727  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2728  }
2729 
2730 
2739  public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2740  {
2741  $tables = array(
2742  'supplier_proposal'
2743  );
2744 
2745  return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2746  }
2747 
2756  public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
2757  {
2758  $tables = array(
2759  'supplier_proposaldet'
2760  );
2761 
2762  return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
2763  }
2764 
2765 
2773  public function getKanbanView($option = '', $arraydata = null)
2774  {
2775  global $langs;
2776 
2777  $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2778 
2779  $return = '<div class="box-flex-item box-flex-grow-zero">';
2780  $return .= '<div class="info-box info-box-sm">';
2781  $return .= '<span class="info-box-icon bg-infobox-action">';
2782  $return .= img_picto('', $this->picto);
2783  //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2784  $return .= '</span>';
2785  $return .= '<div class="info-box-content">';
2786  $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
2787  $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2788  if (property_exists($this, 'socid')) {
2789  $return .= '<span class="info-box-ref"> | '.$this->socid.'</span>';
2790  }
2791  if (property_exists($this, 'delivery_date')) {
2792  $return .= '<br><span class="opacitymedium">'.$langs->trans("DateEnd").'</span> : <span class="info-box-label">'.dol_print_date($this->delivery_date).'</span>';
2793  }
2794  if (property_exists($this, 'total_ttc')) {
2795  $return .='<br><span class="opacitymedium" >'.$langs->trans("AmountHT").' : </span><span class="info-box-label amount">'.price($this->total_ttc).'</span>';
2796  }
2797  if (method_exists($this, 'getLibStatut')) {
2798  $return .= '<br><div class="info-box-status margintoponly">'.$this->getLibStatut(3).'</div>';
2799  }
2800  $return .= '</div>';
2801  $return .= '</div>';
2802  $return .= '</div>';
2803  return $return;
2804  }
2805 }
2806 
2807 
2812 {
2816  public $db;
2817 
2821  public $error = '';
2822 
2826  public $element = 'supplier_proposaldet';
2827 
2831  public $table_element = 'supplier_proposaldet';
2832 
2833  public $oldline;
2834 
2838  public $id;
2839 
2843  public $fk_supplier_proposal;
2844 
2848  public $fk_parent_line;
2849 
2850  public $desc; // Description ligne
2851 
2855  public $fk_product; // Id produit predefini
2856 
2867  public $product_type = Product::TYPE_PRODUCT;
2868 
2869  public $qty;
2870  public $tva_tx;
2871  public $vat_src_code;
2872 
2873  public $subprice;
2874  public $remise_percent;
2875 
2879  public $fk_remise_except;
2880 
2881  public $rang = 0;
2882 
2886  public $fk_fournprice;
2887 
2888  public $pa_ht;
2889  public $marge_tx;
2890  public $marque_tx;
2891 
2892  public $special_code; // Tag for special lines (exlusive tags)
2893  // 1: frais de port
2894  // 2: ecotaxe
2895  // 3: option line (when qty = 0)
2896 
2897  public $info_bits = 0; // Liste d'options cumulables:
2898  // Bit 0: 0 si TVA normal - 1 si TVA NPR
2899  // Bit 1: 0 ligne normale - 1 si ligne de remise fixe
2900 
2901  public $total_ht; // Total HT de la ligne toute quantite et incluant la remise ligne
2902  public $total_tva; // Total TVA de la ligne toute quantite et incluant la remise ligne
2903  public $total_ttc; // Total TTC de la ligne toute quantite et incluant la remise ligne
2904 
2905  public $date_start;
2906  public $date_end;
2907 
2908  // From llx_product
2913  public $ref;
2914 
2919  public $product_ref;
2920 
2925  public $libelle;
2926 
2931  public $product_label;
2932 
2937  public $product_desc;
2938 
2939  public $localtax1_tx; // Local tax 1
2940  public $localtax2_tx; // Local tax 2
2941  public $localtax1_type; // Local tax 1 type
2942  public $localtax2_type; // Local tax 2 type
2943  public $total_localtax1; // Line total local tax 1
2944  public $total_localtax2; // Line total local tax 2
2945 
2946  public $skip_update_total; // Skip update price total for special lines
2947 
2948  public $ref_fourn;
2949  public $ref_supplier;
2950 
2951  // Multicurrency
2955  public $fk_multicurrency;
2956 
2957  public $multicurrency_code;
2958  public $multicurrency_subprice;
2959  public $multicurrency_total_ht;
2960  public $multicurrency_total_tva;
2961  public $multicurrency_total_ttc;
2962 
2968  public function __construct($db)
2969  {
2970  $this->db = $db;
2971  }
2972 
2979  public function fetch($rowid)
2980  {
2981  $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,';
2982  $sql .= ' pd.date_start, pd.date_end,';
2983  $sql .= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice,';
2984  $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,';
2985  $sql .= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
2986  $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
2987  $sql .= ' pd.product_type, pd.ref_fourn as ref_produit_fourn,';
2988  $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';
2989  $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pd';
2990  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
2991  $sql .= ' WHERE pd.rowid = '.((int) $rowid);
2992 
2993  $result = $this->db->query($sql);
2994  if ($result) {
2995  $objp = $this->db->fetch_object($result);
2996 
2997  $this->id = $objp->rowid;
2998  $this->fk_supplier_proposal = $objp->fk_supplier_proposal;
2999  $this->fk_parent_line = $objp->fk_parent_line;
3000  $this->label = $objp->custom_label;
3001  $this->desc = $objp->description;
3002  $this->qty = $objp->qty;
3003  $this->subprice = $objp->subprice;
3004  $this->tva_tx = $objp->tva_tx;
3005  $this->remise_percent = $objp->remise_percent;
3006  $this->fk_remise_except = $objp->fk_remise_except;
3007  $this->fk_product = $objp->fk_product;
3008  $this->info_bits = $objp->info_bits;
3009  $this->date_start = $this->db->jdate($objp->date_start);
3010  $this->date_end = $this->db->jdate($objp->date_end);
3011 
3012  $this->total_ht = $objp->total_ht;
3013  $this->total_tva = $objp->total_tva;
3014  $this->total_ttc = $objp->total_ttc;
3015 
3016  $this->fk_fournprice = $objp->fk_fournprice;
3017 
3018  $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
3019  $this->pa_ht = $marginInfos[0];
3020  $this->marge_tx = $marginInfos[1];
3021  $this->marque_tx = $marginInfos[2];
3022 
3023  $this->special_code = $objp->special_code;
3024  $this->product_type = $objp->product_type;
3025  $this->rang = $objp->rang;
3026 
3027  $this->ref = $objp->product_ref; // deprecated
3028  $this->product_ref = $objp->product_ref;
3029  $this->libelle = $objp->product_label; // deprecated
3030  $this->product_label = $objp->product_label;
3031  $this->product_desc = $objp->product_desc;
3032 
3033  $this->ref_fourn = $objp->ref_produit_fourn;
3034 
3035  // Multicurrency
3036  $this->fk_multicurrency = $objp->fk_multicurrency;
3037  $this->multicurrency_code = $objp->multicurrency_code;
3038  $this->multicurrency_subprice = $objp->multicurrency_subprice;
3039  $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3040  $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3041  $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3042  $this->fk_unit = $objp->fk_unit;
3043 
3044  $this->db->free($result);
3045  return 1;
3046  } else {
3047  dol_print_error($this->db);
3048  return -1;
3049  }
3050  }
3051 
3058  public function insert($notrigger = 0)
3059  {
3060  global $conf, $langs, $user;
3061 
3062  $error = 0;
3063 
3064  dol_syslog(get_class($this)."::insert rang=".$this->rang);
3065 
3066  // Clean parameters
3067  if (empty($this->tva_tx)) {
3068  $this->tva_tx = 0;
3069  }
3070  if (empty($this->vat_src_code)) {
3071  $this->vat_src_code = '';
3072  }
3073  if (empty($this->localtax1_tx)) {
3074  $this->localtax1_tx = 0;
3075  }
3076  if (empty($this->localtax2_tx)) {
3077  $this->localtax2_tx = 0;
3078  }
3079  if (empty($this->localtax1_type)) {
3080  $this->localtax1_type = 0;
3081  }
3082  if (empty($this->localtax2_type)) {
3083  $this->localtax2_type = 0;
3084  }
3085  if (empty($this->total_localtax1)) {
3086  $this->total_localtax1 = 0;
3087  }
3088  if (empty($this->total_localtax2)) {
3089  $this->total_localtax2 = 0;
3090  }
3091  if (empty($this->rang)) {
3092  $this->rang = 0;
3093  }
3094  if (empty($this->remise_percent)) {
3095  $this->remise_percent = 0;
3096  }
3097  if (empty($this->info_bits)) {
3098  $this->info_bits = 0;
3099  }
3100  if (empty($this->special_code)) {
3101  $this->special_code = 0;
3102  }
3103  if (empty($this->fk_parent_line)) {
3104  $this->fk_parent_line = 0;
3105  }
3106  if (empty($this->fk_fournprice)) {
3107  $this->fk_fournprice = 0;
3108  }
3109  if (empty($this->fk_unit)) {
3110  $this->fk_unit = 0;
3111  }
3112  if (empty($this->subprice)) {
3113  $this->subprice = 0;
3114  }
3115 
3116  if (empty($this->pa_ht)) {
3117  $this->pa_ht = 0;
3118  }
3119 
3120  // if buy price not defined, define buyprice as configured in margin admin
3121  if ($this->pa_ht == 0) {
3122  $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3123  if ($result < 0) {
3124  return $result;
3125  } else {
3126  $this->pa_ht = $result;
3127  }
3128  }
3129 
3130  // Check parameters
3131  if ($this->product_type < 0) {
3132  return -1;
3133  }
3134 
3135  $this->db->begin();
3136 
3137  // Insert line into database
3138  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'supplier_proposaldet';
3139  $sql .= ' (fk_supplier_proposal, fk_parent_line, label, description, fk_product, product_type,';
3140  $sql .= ' date_start, date_end,';
3141  $sql .= ' fk_remise_except, qty, tva_tx, vat_src_code, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3142  $sql .= ' subprice, remise_percent, ';
3143  $sql .= ' info_bits, ';
3144  $sql .= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
3145  $sql .= ' ref_fourn,';
3146  $sql .= ' fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc, fk_unit)';
3147  $sql .= " VALUES (".$this->fk_supplier_proposal.",";
3148  $sql .= " ".($this->fk_parent_line > 0 ? ((int) $this->fk_parent_line) : "null").",";
3149  $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
3150  $sql .= " '".$this->db->escape($this->desc)."',";
3151  $sql .= " ".($this->fk_product ? ((int) $this->fk_product) : "null").",";
3152  $sql .= " '".$this->db->escape($this->product_type)."',";
3153  $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3154  $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3155  $sql .= " ".($this->fk_remise_except ? ((int) $this->fk_remise_except) : "null").",";
3156  $sql .= " ".price2num($this->qty, 'MS').",";
3157  $sql .= " ".price2num($this->tva_tx).",";
3158  $sql .= " '".$this->db->escape($this->vat_src_code)."',";
3159  $sql .= " ".price2num($this->localtax1_tx).",";
3160  $sql .= " ".price2num($this->localtax2_tx).",";
3161  $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3162  $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3163  $sql .= " ".price2num($this->subprice, 'MU') .",";
3164  $sql .= " ".((float) $this->remise_percent).",";
3165  $sql .= " ".(isset($this->info_bits) ? ((int) $this->info_bits) : "null").",";
3166  $sql .= " ".price2num($this->total_ht, 'MT').",";
3167  $sql .= " ".price2num($this->total_tva, 'MT').",";
3168  $sql .= " ".price2num($this->total_localtax1, 'MT').",";
3169  $sql .= " ".price2num($this->total_localtax2, 'MT').",";
3170  $sql .= " ".price2num($this->total_ttc, 'MT').",";
3171  $sql .= " ".(!empty($this->fk_fournprice) ? ((int) $this->fk_fournprice) : "null").",";
3172  $sql .= " ".(isset($this->pa_ht) ? price2num($this->pa_ht, 'MU') : "null").",";
3173  $sql .= ' '.((int) $this->special_code).',';
3174  $sql .= ' '.((int) $this->rang).',';
3175  $sql .= " '".$this->db->escape($this->ref_fourn)."'";
3176  $sql .= ", ".($this->fk_multicurrency > 0 ? ((int) $this->fk_multicurrency) : 'null');
3177  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3178  $sql .= ", ".price2num($this->multicurrency_subprice, 'CU');
3179  $sql .= ", ".price2num($this->multicurrency_total_ht, 'CT');
3180  $sql .= ", ".price2num($this->multicurrency_total_tva, 'CT');
3181  $sql .= ", ".price2num($this->multicurrency_total_ttc, 'CT');
3182  $sql .= ", ".($this->fk_unit ? ((int) $this->fk_unit) : 'null');
3183  $sql .= ')';
3184 
3185  dol_syslog(get_class($this).'::insert', LOG_DEBUG);
3186  $resql = $this->db->query($sql);
3187  if ($resql) {
3188  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'supplier_proposaldet');
3189 
3190  if (!$error) {
3191  $result = $this->insertExtraFields();
3192  if ($result < 0) {
3193  $error++;
3194  }
3195  }
3196 
3197  if (!$error && !$notrigger) {
3198  // Call trigger
3199  $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_INSERT', $user);
3200  if ($result < 0) {
3201  $this->db->rollback();
3202  return -1;
3203  }
3204  // End call triggers
3205  }
3206 
3207  $this->db->commit();
3208  return 1;
3209  } else {
3210  $this->error = $this->db->error()." sql=".$sql;
3211  $this->db->rollback();
3212  return -1;
3213  }
3214  }
3215 
3221  public function delete()
3222  {
3223  global $conf, $langs, $user;
3224 
3225  $error = 0;
3226  $this->db->begin();
3227 
3228  $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet WHERE rowid = ".((int) $this->id);
3229  dol_syslog("SupplierProposalLine::delete", LOG_DEBUG);
3230  if ($this->db->query($sql)) {
3231  // Remove extrafields
3232  if (!$error) {
3233  $result = $this->deleteExtraFields();
3234  if ($result < 0) {
3235  $error++;
3236  dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3237  }
3238  }
3239 
3240  // Call trigger
3241  $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_DELETE', $user);
3242  if ($result < 0) {
3243  $this->db->rollback();
3244  return -1;
3245  }
3246  // End call triggers
3247 
3248  $this->db->commit();
3249 
3250  return 1;
3251  } else {
3252  $this->error = $this->db->error()." sql=".$sql;
3253  $this->db->rollback();
3254  return -1;
3255  }
3256  }
3257 
3264  public function update($notrigger = 0)
3265  {
3266  global $conf, $langs, $user;
3267 
3268  $error = 0;
3269 
3270  // Clean parameters
3271  if (empty($this->tva_tx)) {
3272  $this->tva_tx = 0;
3273  }
3274  if (empty($this->localtax1_tx)) {
3275  $this->localtax1_tx = 0;
3276  }
3277  if (empty($this->localtax2_tx)) {
3278  $this->localtax2_tx = 0;
3279  }
3280  if (empty($this->total_localtax1)) {
3281  $this->total_localtax1 = 0;
3282  }
3283  if (empty($this->total_localtax2)) {
3284  $this->total_localtax2 = 0;
3285  }
3286  if (empty($this->localtax1_type)) {
3287  $this->localtax1_type = 0;
3288  }
3289  if (empty($this->localtax2_type)) {
3290  $this->localtax2_type = 0;
3291  }
3292  if (empty($this->marque_tx)) {
3293  $this->marque_tx = 0;
3294  }
3295  if (empty($this->marge_tx)) {
3296  $this->marge_tx = 0;
3297  }
3298  if (empty($this->remise_percent)) {
3299  $this->remise_percent = 0;
3300  }
3301  if (empty($this->info_bits)) {
3302  $this->info_bits = 0;
3303  }
3304  if (empty($this->special_code)) {
3305  $this->special_code = 0;
3306  }
3307  if (empty($this->fk_parent_line)) {
3308  $this->fk_parent_line = 0;
3309  }
3310  if (empty($this->fk_fournprice)) {
3311  $this->fk_fournprice = 0;
3312  }
3313  if (empty($this->fk_unit)) {
3314  $this->fk_unit = 0;
3315  }
3316  if (empty($this->subprice)) {
3317  $this->subprice = 0;
3318  }
3319 
3320  if (empty($this->pa_ht)) {
3321  $this->pa_ht = 0;
3322  }
3323 
3324  // if buy price not defined, define buyprice as configured in margin admin
3325  if ($this->pa_ht == 0) {
3326  $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3327  if ($result < 0) {
3328  return $result;
3329  } else {
3330  $this->pa_ht = $result;
3331  }
3332  }
3333 
3334  $this->db->begin();
3335 
3336  // Mise a jour ligne en base
3337  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3338  $sql .= " description='".$this->db->escape($this->desc)."'";
3339  $sql .= " , label=".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
3340  $sql .= " , product_type=".((int) $this->product_type);
3341  $sql .= " , date_start=".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null");
3342  $sql .= " , date_end=".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null");
3343  $sql .= " , tva_tx='".price2num($this->tva_tx)."'";
3344  $sql .= " , localtax1_tx=".price2num($this->localtax1_tx);
3345  $sql .= " , localtax2_tx=".price2num($this->localtax2_tx);
3346  $sql .= " , localtax1_type='".$this->db->escape($this->localtax1_type)."'";
3347  $sql .= " , localtax2_type='".$this->db->escape($this->localtax2_type)."'";
3348  $sql .= " , qty='".price2num($this->qty)."'";
3349  $sql .= " , subprice=".price2num($this->subprice);
3350  $sql .= " , remise_percent=".price2num($this->remise_percent);
3351  $sql .= " , info_bits='".$this->db->escape($this->info_bits)."'";
3352  if (empty($this->skip_update_total)) {
3353  $sql .= " , total_ht=".price2num($this->total_ht);
3354  $sql .= " , total_tva=".price2num($this->total_tva);
3355  $sql .= " , total_ttc=".price2num($this->total_ttc);
3356  $sql .= " , total_localtax1=".price2num($this->total_localtax1);
3357  $sql .= " , total_localtax2=".price2num($this->total_localtax2);
3358  }
3359  $sql .= " , fk_product_fournisseur_price=".(!empty($this->fk_fournprice) ? "'".$this->db->escape($this->fk_fournprice)."'" : "null");
3360  $sql .= " , buy_price_ht=".price2num($this->pa_ht);
3361  if (strlen($this->special_code)) {
3362  $sql .= " , special_code=".((int) $this->special_code);
3363  }
3364  $sql .= " , fk_parent_line=".($this->fk_parent_line > 0 ? $this->fk_parent_line : "null");
3365  if (!empty($this->rang)) {
3366  $sql .= ", rang=".((int) $this->rang);
3367  }
3368  $sql .= " , ref_fourn=".(!empty($this->ref_fourn) ? "'".$this->db->escape($this->ref_fourn)."'" : "null");
3369  $sql .= " , fk_unit=".($this->fk_unit ? $this->fk_unit : 'null');
3370 
3371  // Multicurrency
3372  $sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice);
3373  $sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
3374  $sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
3375  $sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
3376 
3377  $sql .= " WHERE rowid = ".((int) $this->id);
3378 
3379  dol_syslog(get_class($this)."::update", LOG_DEBUG);
3380  $resql = $this->db->query($sql);
3381  if ($resql) {
3382  if (!$error) {
3383  $result = $this->insertExtraFields();
3384  if ($result < 0) {
3385  $error++;
3386  }
3387  }
3388 
3389  if (!$error && !$notrigger) {
3390  // Call trigger
3391  $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_MODIFY', $user);
3392  if ($result < 0) {
3393  $this->db->rollback();
3394  return -1;
3395  }
3396  // End call triggers
3397  }
3398 
3399  $this->db->commit();
3400  return 1;
3401  } else {
3402  $this->error = $this->db->error();
3403  $this->db->rollback();
3404  return -2;
3405  }
3406  }
3407 
3408  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3415  public function update_total()
3416  {
3417  // phpcs:enable
3418  $this->db->begin();
3419 
3420  // Mise a jour ligne en base
3421  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3422  $sql .= " total_ht=".price2num($this->total_ht, 'MT');
3423  $sql .= ",total_tva=".price2num($this->total_tva, 'MT');
3424  $sql .= ",total_ttc=".price2num($this->total_ttc, 'MT');
3425  $sql .= " WHERE rowid = ".((int) $this->id);
3426 
3427  dol_syslog("SupplierProposalLine::update_total", LOG_DEBUG);
3428 
3429  $resql = $this->db->query($sql);
3430  if ($resql) {
3431  $this->db->commit();
3432  return 1;
3433  } else {
3434  $this->error = $this->db->error();
3435  $this->db->rollback();
3436  return -2;
3437  }
3438  }
3439 }
$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:1485
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:1334
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:1537
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:921
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