dolibarr  20.0.0-beta
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-2024 Frédéric France <frederic.france@free.fr>
17  * Copyright (C) 2020 Tobias Sekan <tobias.sekan@startmail.com>
18  * Copyright (C) 2022 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
19  * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
20  *
21  * This program is free software; you can redistribute it and/or modify
22  * it under the terms of the GNU General Public License as published by
23  * the Free Software Foundation; either version 3 of the License, or
24  * (at your option) any later version.
25  *
26  * This program is distributed in the hope that it will be useful,
27  * but WITHOUT ANY WARRANTY; without even the implied warranty of
28  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29  * GNU General Public License for more details.
30  *
31  * You should have received a copy of the GNU General Public License
32  * along with this program. If not, see <https://www.gnu.org/licenses/>.
33  */
34 
40 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
41 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
42 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
43 require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
44 require_once DOL_DOCUMENT_ROOT.'/margin/lib/margins.lib.php';
45 require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
46 require_once DOL_DOCUMENT_ROOT.'/core/class/commonincoterm.class.php';
47 
52 {
53  use CommonIncoterm;
54 
58  public $element = 'supplier_proposal';
59 
63  public $table_element = 'supplier_proposal';
64 
68  public $table_element_line = 'supplier_proposaldet';
69 
73  public $fk_element = 'fk_supplier_proposal';
74 
78  public $picto = 'supplier_proposal';
79 
84  public $restrictiononfksoc = 1;
85 
89  protected $table_ref_field = 'ref';
90 
91  public $socid; // Id client
92 
97  public $author;
98 
99  public $ref_fourn; //Reference saisie lors de l'ajout d'une ligne à la demande
100  public $ref_supplier; //Reference saisie lors de l'ajout d'une ligne à la demande
101 
105  public $statut; // 0 (draft), 1 (validated), 2 (signed), 3 (not signed), 4 (processed/billed)
106 
110  public $date;
111 
115  public $delivery_date;
116 
121  public $datec;
122 
126  public $date_creation;
127 
132  public $datev;
133 
137  public $date_validation;
138 
139 
140  public $user_author_id;
141 
146  public $price;
147 
152  public $tva;
153 
158  public $total;
159 
160  public $cond_reglement_code;
161  public $cond_reglement_doc; // label doc
162 
163  public $mode_reglement_code;
168  public $mode_reglement;
169 
170  public $extraparams = array();
171  public $lines = array();
172  public $line;
173 
174  public $labelStatus = array();
175  public $labelStatusShort = array();
176 
177  public $nbtodo;
178  public $nbtodolate;
179 
180  // Multicurrency
184  public $fk_multicurrency;
185 
186  public $multicurrency_code;
187  public $multicurrency_tx;
188  public $multicurrency_total_ht;
189  public $multicurrency_total_tva;
190  public $multicurrency_total_ttc;
191 
195  const STATUS_DRAFT = 0;
196 
200  const STATUS_VALIDATED = 1;
201 
205  const STATUS_SIGNED = 2;
206 
210  const STATUS_NOTSIGNED = 3;
211 
215  const STATUS_CLOSE = 4;
216 
217 
218 
226  public function __construct($db, $socid = 0, $supplier_proposalid = 0)
227  {
228  global $conf, $langs;
229 
230  $this->db = $db;
231 
232  $this->ismultientitymanaged = 1;
233  $this->socid = $socid;
234  $this->id = $supplier_proposalid;
235  }
236 
237 
238  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
249  public function add_product($idproduct, $qty, $remise_percent = 0)
250  {
251  // phpcs:enable
252  global $conf, $mysoc;
253 
254  if (!$qty) {
255  $qty = 1;
256  }
257 
258  dol_syslog(get_class($this)."::add_product $idproduct, $qty, $remise_percent");
259  if ($idproduct > 0) {
260  $prod = new Product($this->db);
261  $prod->fetch($idproduct);
262 
263  $productdesc = $prod->description;
264 
265  $tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
266  $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
267  if (empty($tva_tx)) {
268  $tva_npr = 0;
269  }
270  $localtax1_tx = get_localtax($tva_tx, 1, $mysoc, $this->thirdparty, $tva_npr);
271  $localtax2_tx = get_localtax($tva_tx, 2, $mysoc, $this->thirdparty, $tva_npr);
272 
273  // multiprix
274  if ($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level) {
275  $price = $prod->multiprices[$this->thirdparty->price_level];
276  } else {
277  $price = $prod->price;
278  }
279 
280  $line = new SupplierProposalLine($this->db);
281 
282  $line->fk_product = $idproduct;
283  $line->desc = $productdesc;
284  $line->qty = $qty;
285  $line->subprice = $price;
286  $line->remise_percent = $remise_percent;
287  $line->tva_tx = $tva_tx;
288 
289  $this->lines[] = $line;
290  return 1;
291  }
292  return -1;
293  }
294 
295  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
302  public function insert_discount($idremise)
303  {
304  // phpcs:enable
305  global $langs;
306 
307  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
308  include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
309 
310  $this->db->begin();
311 
312  $remise = new DiscountAbsolute($this->db);
313  $result = $remise->fetch($idremise);
314 
315  if ($result > 0) {
316  if ($remise->fk_facture) { // Protection against multiple submission
317  $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
318  $this->db->rollback();
319  return -5;
320  }
321 
322  $supplier_proposalligne = new SupplierProposalLine($this->db);
323  $supplier_proposalligne->fk_supplier_proposal = $this->id;
324  $supplier_proposalligne->fk_remise_except = $remise->id;
325  $supplier_proposalligne->desc = $remise->description; // Description ligne
326  $supplier_proposalligne->tva_tx = $remise->tva_tx;
327  $supplier_proposalligne->subprice = -$remise->amount_ht;
328  $supplier_proposalligne->fk_product = 0; // Id produit predefini
329  $supplier_proposalligne->qty = 1;
330  $supplier_proposalligne->remise_percent = 0;
331  $supplier_proposalligne->rang = -1;
332  $supplier_proposalligne->info_bits = 2;
333 
334  $supplier_proposalligne->total_ht = -$remise->amount_ht;
335  $supplier_proposalligne->total_tva = -$remise->amount_tva;
336  $supplier_proposalligne->total_ttc = -$remise->amount_ttc;
337 
338  $result = $supplier_proposalligne->insert();
339  if ($result > 0) {
340  $result = $this->update_price(1);
341  if ($result > 0) {
342  $this->db->commit();
343  return 1;
344  } else {
345  $this->db->rollback();
346  return -1;
347  }
348  } else {
349  $this->error = $supplier_proposalligne->error;
350  $this->db->rollback();
351  return -2;
352  }
353  } else {
354  $this->db->rollback();
355  return -2;
356  }
357  }
358 
396  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 = [], $ref_supplier = '', $fk_unit = 0, $origin = '', $origin_id = 0, $pu_ht_devise = 0, $date_start = 0, $date_end = 0)
397  {
398  global $mysoc, $conf, $langs;
399 
400  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");
401  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
402 
403  // Clean parameters
404  if (empty($remise_percent)) {
405  $remise_percent = 0;
406  }
407  if (empty($qty)) {
408  $qty = 0;
409  }
410  if (empty($info_bits)) {
411  $info_bits = 0;
412  }
413  if (empty($rang)) {
414  $rang = 0;
415  }
416  if (empty($fk_parent_line) || $fk_parent_line < 0) {
417  $fk_parent_line = 0;
418  }
419  if (empty($pu_ht)) {
420  $pu_ht = 0;
421  }
422 
423  $remise_percent = price2num($remise_percent);
424  $qty = price2num($qty);
425  $pu_ht = price2num($pu_ht);
426  $pu_ttc = price2num($pu_ttc);
427  if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
428  $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
429  }
430  $txlocaltax1 = price2num($txlocaltax1);
431  $txlocaltax2 = price2num($txlocaltax2);
432  $pa_ht = price2num($pa_ht);
433  if ($price_base_type == 'HT') {
434  $pu = $pu_ht;
435  } else {
436  $pu = $pu_ttc;
437  }
438 
439  // Check parameters
440  if ($type < 0) {
441  return -1;
442  }
443 
444  if ($this->statut == self::STATUS_DRAFT) {
445  $this->db->begin();
446 
447  if ($fk_product > 0) {
448  if (getDolGlobalString('SUPPLIER_PROPOSAL_WITH_PREDEFINED_PRICES_ONLY')) {
449  // Check quantity is enough
450  dol_syslog(get_class($this)."::addline we check supplier prices fk_product=".$fk_product." fk_fournprice=".$fk_fournprice." qty=".$qty." ref_supplier=".$ref_supplier);
451  $productsupplier = new ProductFournisseur($this->db);
452  if ($productsupplier->fetch($fk_product) > 0) {
453  $product_type = $productsupplier->type;
454  $label = $productsupplier->label;
455  $fk_prod_fourn_price = $fk_fournprice;
456 
457  // We use 'none' instead of $ref_supplier, because fourn_ref may not exists anymore. So we will take the first supplier price ok.
458  // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
459  // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
460  $result = $productsupplier->get_buyprice($fk_prod_fourn_price, $qty, $fk_product, 'none', $this->socid); // Search on couple $fk_prod_fourn_price/$qty first, then on triplet $qty/$fk_product/$ref_supplier/$this->socid
461  if ($result > 0) {
462  $pu = $productsupplier->fourn_pu; // Unit price supplier price set by get_buyprice
463  $ref_supplier = $productsupplier->ref_supplier; // Ref supplier price set by get_buyprice
464  // is remise percent not keyed but present for the product we add it
465  if ($remise_percent == 0 && $productsupplier->remise_percent != 0) {
466  $remise_percent = $productsupplier->remise_percent;
467  }
468  }
469  if ($result == 0) { // If result == 0, we failed to found the supplier reference price
470  $langs->load("errors");
471  $this->error = "Ref ".$productsupplier->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
472  $this->db->rollback();
473  dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
474  //$pu = $productsupplier->fourn_pu; // We do not overwrite unit price
475  //$ref = $productsupplier_fourn; // We do not overwrite ref supplier price
476  return -1;
477  }
478  if ($result == -1) {
479  $langs->load("errors");
480  $this->error = "Ref ".$productsupplier->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
481  $this->db->rollback();
482  dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
483  return -1;
484  }
485  if ($result < -1) {
486  $this->error = $productsupplier->error;
487  $this->errors = $productsupplier->errors;
488  $this->db->rollback();
489  dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
490  return -1;
491  }
492  } else {
493  $this->error = $productsupplier->error;
494  $this->errors = $productsupplier->errors;
495  $this->db->rollback();
496  return -1;
497  }
498  }
499  } else {
500  $product_type = $type;
501  }
502 
503  // Calcul du total TTC et de la TVA pour la ligne a partir de
504  // qty, pu, remise_percent et txtva
505  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
506  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
507 
508  $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
509 
510  // Clean vat code
511  $reg = array();
512  $vat_src_code = '';
513  if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
514  $vat_src_code = $reg[1];
515  $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
516  }
517 
518  if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
519  $pu = 0;
520  }
521 
522  $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);
523  $total_ht = $tabprice[0];
524  $total_tva = $tabprice[1];
525  $total_ttc = $tabprice[2];
526  $total_localtax1 = $tabprice[9];
527  $total_localtax2 = $tabprice[10];
528  $pu = $pu_ht = $tabprice[3];
529 
530  // MultiCurrency
531  $multicurrency_total_ht = $tabprice[16];
532  $multicurrency_total_tva = $tabprice[17];
533  $multicurrency_total_ttc = $tabprice[18];
534  $pu_ht_devise = $tabprice[19];
535 
536  // Rang to use
537  $ranktouse = $rang;
538  if ($ranktouse == -1) {
539  $rangmax = $this->line_max($fk_parent_line);
540  $ranktouse = $rangmax + 1;
541  }
542 
543  // TODO A virer
544  // Anciens indicateurs: $price, $remise (a ne plus utiliser)
545  $price = $pu;
546  $remise = 0;
547  if ($remise_percent > 0) {
548  $remise = round(($pu * (float) $remise_percent / 100), 2);
549  $price = $pu - $remise;
550  }
551 
552  // Insert line
553  $this->line = new SupplierProposalLine($this->db);
554 
555  $this->line->fk_supplier_proposal = $this->id;
556  $this->line->label = $label;
557  $this->line->desc = $desc;
558  $this->line->qty = $qty;
559 
560  $this->line->vat_src_code = $vat_src_code;
561  $this->line->tva_tx = $txtva;
562  $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
563  $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
564  $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
565  $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
566  $this->line->fk_product = $fk_product;
567  $this->line->remise_percent = $remise_percent;
568  $this->line->subprice = $pu_ht;
569  $this->line->rang = $ranktouse;
570  $this->line->info_bits = $info_bits;
571  $this->line->total_ht = $total_ht;
572  $this->line->total_tva = $total_tva;
573  $this->line->total_localtax1 = $total_localtax1;
574  $this->line->total_localtax2 = $total_localtax2;
575  $this->line->total_ttc = $total_ttc;
576  $this->line->product_type = $type;
577  $this->line->special_code = $special_code;
578  $this->line->fk_parent_line = $fk_parent_line;
579  $this->line->fk_unit = $fk_unit;
580  $this->line->origin = $origin;
581  $this->line->origin_id = $origin_id;
582  $this->line->ref_fourn = $this->db->escape($ref_supplier);
583  $this->line->date_start = $date_start;
584  $this->line->date_end = $date_end;
585 
586  // infos merge
587  if (!empty($fk_product) && $fk_product > 0 && empty($fk_fournprice) && empty($pa_ht)) {
588  // When fk_fournprice is 0, we take the lowest buying price
589  include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
590  $productFournisseur = new ProductFournisseur($this->db);
591  $productFournisseur->find_min_price_product_fournisseur($fk_product);
592  $this->line->fk_fournprice = $productFournisseur->product_fourn_price_id;
593  } else {
594  $this->line->fk_fournprice = ($fk_fournprice > 0 ? $fk_fournprice : 0); // If fk_fournprice is -1, we will not use fk_fournprice
595  }
596  $this->line->pa_ht = $pa_ht;
597  //var_dump($this->line->fk_fournprice);exit;
598 
599  // Multicurrency
600  $this->line->fk_multicurrency = $this->fk_multicurrency;
601  $this->line->multicurrency_code = $this->multicurrency_code;
602  $this->line->multicurrency_subprice = $pu_ht_devise;
603  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
604  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
605  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
606 
607  // Mise en option de la ligne
608  if (empty($qty) && empty($special_code)) {
609  $this->line->special_code = 3;
610  }
611 
612  if (is_array($array_options) && count($array_options) > 0) {
613  $this->line->array_options = $array_options;
614  }
615 
616  $result = $this->line->insert();
617  if ($result > 0) {
618  // Reorder if child line
619  if (!empty($fk_parent_line)) {
620  $this->line_order(true, 'DESC');
621  } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) { // Update all rank of all other lines
622  $linecount = count($this->lines);
623  for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
624  $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
625  }
626  }
627 
628  // Mise a jour information denormalisees au niveau de la propale meme
629  $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.
630  if ($result > 0) {
631  $this->db->commit();
632  return $this->line->id;
633  } else {
634  $this->db->rollback();
635  return -1;
636  }
637  } else {
638  $this->error = $this->line->error;
639  $this->errors = $this->line->errors;
640  $this->db->rollback();
641  return -2;
642  }
643  } else {
644  $this->error = 'BadStatusOfObjectToAddLine';
645  return -5;
646  }
647  }
648 
649 
676  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 = [], $ref_supplier = '', $fk_unit = 0, $pu_ht_devise = 0)
677  {
678  global $conf, $user, $langs, $mysoc;
679 
680  dol_syslog(get_class($this)."::updateLine $rowid, $pu, $qty, $remise_percent, $txtva, $desc, $price_base_type, $info_bits");
681  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
682 
683  // Clean parameters
684  $remise_percent = price2num($remise_percent);
685  $qty = price2num($qty);
686  $pu = price2num($pu);
687  if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
688  $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
689  }
690  $txlocaltax1 = price2num($txlocaltax1);
691  $txlocaltax2 = price2num($txlocaltax2);
692  $pa_ht = price2num($pa_ht);
693  if (empty($qty) && empty($special_code)) {
694  $special_code = 3; // Set option tag
695  }
696  if (!empty($qty) && $special_code == 3) {
697  $special_code = 0; // Remove option tag
698  }
699 
700  if ($this->statut == 0) {
701  $this->db->begin();
702 
703  // Calcul du total TTC et de la TVA pour la ligne a partir de
704  // qty, pu, remise_percent et txtva
705  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
706  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
707 
708  $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
709 
710  // Clean vat code
711  $reg = array();
712  $vat_src_code = '';
713  if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
714  $vat_src_code = $reg[1];
715  $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
716  }
717 
718  if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
719  $pu = 0;
720  }
721 
722  $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);
723  $total_ht = $tabprice[0];
724  $total_tva = $tabprice[1];
725  $total_ttc = $tabprice[2];
726  $total_localtax1 = $tabprice[9];
727  $total_localtax2 = $tabprice[10];
728  $pu_ht = $tabprice[3];
729  $pu_tva = $tabprice[4];
730  $pu_ttc = $tabprice[5];
731 
732  // MultiCurrency
733  $multicurrency_total_ht = $tabprice[16];
734  $multicurrency_total_tva = $tabprice[17];
735  $multicurrency_total_ttc = $tabprice[18];
736  $pu_ht_devise = $tabprice[19];
737 
738  $pu = $pu_ht;
739  if ($price_base_type == 'TTC') {
740  $pu = $pu_ttc;
741  }
742 
743  //Fetch current line from the database and then clone the object and set it in $oldline property
744  $line = new SupplierProposalLine($this->db);
745  $line->fetch($rowid);
746  $line->fetch_optionals();
747 
748  $fk_product = $line->fk_product;
749 
750  // Stock previous line records
751  $staticline = clone $line;
752 
753  $line->oldline = $staticline;
754  $this->line = $line;
755  $this->line->context = $this->context;
756 
757  // Reorder if fk_parent_line change
758  if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
759  $rangmax = $this->line_max($fk_parent_line);
760  $this->line->rang = $rangmax + 1;
761  }
762 
763  $this->line->id = $rowid;
764  $this->line->label = $label;
765  $this->line->desc = $desc;
766  $this->line->qty = $qty;
767  $this->line->product_type = $type;
768 
769  $this->line->vat_src_code = $vat_src_code;
770  $this->line->tva_tx = $txtva;
771  $this->line->localtax1_tx = $txlocaltax1;
772  $this->line->localtax2_tx = $txlocaltax2;
773  $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
774  $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
775  $this->line->remise_percent = $remise_percent;
776  $this->line->subprice = $pu;
777  $this->line->info_bits = $info_bits;
778  $this->line->total_ht = $total_ht;
779  $this->line->total_tva = $total_tva;
780  $this->line->total_localtax1 = $total_localtax1;
781  $this->line->total_localtax2 = $total_localtax2;
782  $this->line->total_ttc = $total_ttc;
783  $this->line->special_code = $special_code;
784  $this->line->fk_parent_line = $fk_parent_line;
785  $this->line->skip_update_total = $skip_update_total;
786  $this->line->ref_fourn = $ref_supplier;
787  $this->line->fk_unit = $fk_unit;
788 
789  // infos merge
790  if (!empty($fk_product) && $fk_product > 0 && empty($fk_fournprice) && empty($pa_ht)) {
791  // by external module, take lowest buying price
792  include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
793  $productFournisseur = new ProductFournisseur($this->db);
794  $productFournisseur->find_min_price_product_fournisseur($fk_product);
795  $this->line->fk_fournprice = $productFournisseur->product_fourn_price_id;
796  } else {
797  $this->line->fk_fournprice = $fk_fournprice;
798  }
799  $this->line->pa_ht = $pa_ht;
800 
801  if (is_array($array_options) && count($array_options) > 0) {
802  // We replace values in this->line->array_options only for entries defined into $array_options
803  foreach ($array_options as $key => $value) {
804  $this->line->array_options[$key] = $array_options[$key];
805  }
806  }
807 
808  // Multicurrency
809  $this->line->multicurrency_subprice = $pu_ht_devise;
810  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
811  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
812  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
813 
814  $result = $this->line->update();
815  if ($result > 0) {
816  // Reorder if child line
817  if (!empty($fk_parent_line)) {
818  $this->line_order(true, 'DESC');
819  }
820 
821  $this->update_price(1);
822 
823  $this->db->commit();
824  return $result;
825  } else {
826  $this->error = $this->db->error();
827  $this->db->rollback();
828  return -1;
829  }
830  } else {
831  dol_syslog(get_class($this)."::updateline Erreur -2 SupplierProposal en mode incompatible pour cette action");
832  return -2;
833  }
834  }
835 
836 
843  public function deleteLine($lineid)
844  {
845  global $user;
846 
847  if ($this->statut == 0) {
848  $line = new SupplierProposalLine($this->db);
849 
850  // For triggers
851  $line->fetch($lineid);
852 
853  if ($line->delete($user) > 0) {
854  $this->update_price(1);
855 
856  return 1;
857  } else {
858  return -1;
859  }
860  } else {
861  return -2;
862  }
863  }
864 
865 
874  public function create($user, $notrigger = 0)
875  {
876  global $langs, $conf, $mysoc, $hookmanager;
877  $error = 0;
878 
879  $now = dol_now();
880 
881  dol_syslog(get_class($this)."::create");
882 
883  // Check parameters
884  $result = $this->fetch_thirdparty();
885  if ($result < 0) {
886  $this->error = "Failed to fetch company";
887  dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
888  return -3;
889  }
890  if (!empty($this->ref)) { // We check that ref is not already used
891  $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
892  if ($result > 0) {
893  $this->error = 'ErrorRefAlreadyExists';
894  dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
895  $this->db->rollback();
896  return -1;
897  }
898  }
899 
900  // Set tmp vars
901  $delivery_date = $this->delivery_date;
902 
903  // Multicurrency
904  if (!empty($this->multicurrency_code)) {
905  list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $now);
906  }
907  if (empty($this->fk_multicurrency)) {
908  $this->multicurrency_code = $conf->currency;
909  $this->fk_multicurrency = 0;
910  $this->multicurrency_tx = 1;
911  }
912 
913  $this->db->begin();
914 
915  // Insert into database
916  $sql = "INSERT INTO ".MAIN_DB_PREFIX."supplier_proposal (";
917  $sql .= "fk_soc";
918  $sql .= ", price";
919  $sql .= ", total_tva";
920  $sql .= ", total_ttc";
921  $sql .= ", datec";
922  $sql .= ", ref";
923  $sql .= ", fk_user_author";
924  $sql .= ", note_private";
925  $sql .= ", note_public";
926  $sql .= ", model_pdf";
927  $sql .= ", fk_cond_reglement";
928  $sql .= ", fk_mode_reglement";
929  $sql .= ", fk_account";
930  $sql .= ", date_livraison";
931  $sql .= ", fk_shipping_method";
932  $sql .= ", fk_projet";
933  $sql .= ", entity";
934  $sql .= ", fk_multicurrency";
935  $sql .= ", multicurrency_code";
936  $sql .= ", multicurrency_tx";
937  $sql .= ") ";
938  $sql .= " VALUES (";
939  $sql .= ((int) $this->socid);
940  $sql .= ", 0";
941  $sql .= ", 0";
942  $sql .= ", 0";
943  $sql .= ", '".$this->db->idate($now)."'";
944  $sql .= ", '(PROV)'";
945  $sql .= ", ".($user->id > 0 ? ((int) $user->id) : "null");
946  $sql .= ", '".$this->db->escape($this->note_private)."'";
947  $sql .= ", '".$this->db->escape($this->note_public)."'";
948  $sql .= ", '".$this->db->escape($this->model_pdf)."'";
949  $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : 'NULL');
950  $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : 'NULL');
951  $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
952  $sql .= ", ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : "null");
953  $sql .= ", ".($this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : 'NULL');
954  $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
955  $sql .= ", ".((int) $conf->entity);
956  $sql .= ", ".((int) $this->fk_multicurrency);
957  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
958  $sql .= ", ".((float) $this->multicurrency_tx);
959  $sql .= ")";
960 
961  dol_syslog(get_class($this)."::create", LOG_DEBUG);
962  $resql = $this->db->query($sql);
963  if ($resql) {
964  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."supplier_proposal");
965 
966  if ($this->id) {
967  $this->ref = '(PROV'.$this->id.')';
968  $sql = 'UPDATE '.MAIN_DB_PREFIX."supplier_proposal SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
969 
970  dol_syslog(get_class($this)."::create", LOG_DEBUG);
971  $resql = $this->db->query($sql);
972  if (!$resql) {
973  $error++;
974  }
975 
976  if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
977  $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
978  }
979 
980  // Add object linked
981  if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
982  foreach ($this->linked_objects as $origin => $tmp_origin_id) {
983  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, ...))
984  foreach ($tmp_origin_id as $origin_id) {
985  $ret = $this->add_object_linked($origin, $origin_id);
986  if (!$ret) {
987  dol_print_error($this->db);
988  $error++;
989  }
990  }
991  }
992  }
993  }
994 
995  /*
996  * Insertion du detail des produits dans la base
997  */
998  if (!$error) {
999  $fk_parent_line = 0;
1000  $num = count($this->lines);
1001 
1002  for ($i = 0; $i < $num; $i++) {
1003  // Reset fk_parent_line for no child products and special product
1004  if (($this->lines[$i]->product_type != 9 && empty($this->lines[$i]->fk_parent_line)) || $this->lines[$i]->product_type == 9) {
1005  $fk_parent_line = 0;
1006  }
1007 
1008  $result = $this->addline(
1009  $this->lines[$i]->desc,
1010  $this->lines[$i]->subprice,
1011  $this->lines[$i]->qty,
1012  $this->lines[$i]->tva_tx,
1013  $this->lines[$i]->localtax1_tx,
1014  $this->lines[$i]->localtax2_tx,
1015  $this->lines[$i]->fk_product,
1016  $this->lines[$i]->remise_percent,
1017  'HT',
1018  0,
1019  0,
1020  $this->lines[$i]->product_type,
1021  $this->lines[$i]->rang,
1022  $this->lines[$i]->special_code,
1023  $fk_parent_line,
1024  $this->lines[$i]->fk_fournprice,
1025  $this->lines[$i]->pa_ht,
1026  empty($this->lines[$i]->label) ? '' : $this->lines[$i]->label, // deprecated
1027  $this->lines[$i]->array_options,
1028  $this->lines[$i]->ref_fourn,
1029  $this->lines[$i]->fk_unit,
1030  'supplier_proposal',
1031  $this->lines[$i]->rowid
1032  );
1033 
1034  if ($result < 0) {
1035  $error++;
1036  $this->error = $this->db->error;
1037  dol_print_error($this->db);
1038  break;
1039  }
1040  // Defined the new fk_parent_line
1041  if ($result > 0 && $this->lines[$i]->product_type == 9) {
1042  $fk_parent_line = $result;
1043  }
1044  }
1045  }
1046 
1047  if (!$error) {
1048  // Mise a jour infos denormalisees
1049  $resql = $this->update_price(1);
1050  if ($resql) {
1051  $action = 'update';
1052 
1053  // Actions on extra fields
1054  if (!$error) {
1055  $result = $this->insertExtraFields();
1056  if ($result < 0) {
1057  $error++;
1058  }
1059  }
1060 
1061  if (!$error && !$notrigger) {
1062  // Call trigger
1063  $result = $this->call_trigger('PROPOSAL_SUPPLIER_CREATE', $user);
1064  if ($result < 0) {
1065  $error++;
1066  }
1067  // End call triggers
1068  }
1069  } else {
1070  $this->error = $this->db->lasterror();
1071  $error++;
1072  }
1073  }
1074  } else {
1075  $this->error = $this->db->lasterror();
1076  $error++;
1077  }
1078 
1079  if (!$error) {
1080  $this->db->commit();
1081  dol_syslog(get_class($this)."::create done id=".$this->id);
1082  return $this->id;
1083  } else {
1084  $this->db->rollback();
1085  return -2;
1086  }
1087  } else {
1088  $this->error = $this->db->lasterror();
1089  $this->db->rollback();
1090  return -1;
1091  }
1092  }
1093 
1101  public function createFromClone(User $user, $fromid = 0)
1102  {
1103  global $conf, $hookmanager;
1104 
1105  $error = 0;
1106  $now = dol_now();
1107 
1108  $this->db->begin();
1109 
1110  // get extrafields so they will be clone
1111  foreach ($this->lines as $line) {
1112  $line->fetch_optionals();
1113  }
1114 
1115  // Load source object
1116  $objFrom = clone $this;
1117 
1118  $objsoc = new Societe($this->db);
1119 
1120  // Change socid if needed
1121  if (!empty($fromid) && $fromid != $this->socid) {
1122  if ($objsoc->fetch($fromid) > 0) {
1123  $this->socid = $objsoc->id;
1124  $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1125  $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1126  unset($this->fk_project);
1127  }
1128 
1129  // TODO Change product price if multi-prices
1130  } else {
1131  $objsoc->fetch($this->socid);
1132  }
1133 
1134  $this->id = 0;
1135  $this->statut = 0;
1136 
1137  if (!getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') || !is_readable(DOL_DOCUMENT_ROOT."/core/modules/supplier_proposal/" . getDolGlobalString('SUPPLIER_PROPOSAL_ADDON').".php")) {
1138  $this->error = 'ErrorSetupNotComplete';
1139  return -1;
1140  }
1141 
1142  // Clear fields
1143  $this->user_author_id = $user->id;
1144  $this->user_validation_id = 0;
1145  $this->date = $now;
1146 
1147  // Set ref
1148  require_once DOL_DOCUMENT_ROOT."/core/modules/supplier_proposal/" . getDolGlobalString('SUPPLIER_PROPOSAL_ADDON').'.php';
1149  $obj = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
1150  $modSupplierProposal = new $obj();
1151  $this->ref = $modSupplierProposal->getNextValue($objsoc, $this);
1152 
1153  // Create clone
1154  $this->context['createfromclone'] = 'createfromclone';
1155  $result = $this->create($user);
1156  if ($result < 0) {
1157  $error++;
1158  }
1159 
1160  if (!$error) {
1161  // Hook of thirdparty module
1162  if (is_object($hookmanager)) {
1163  $parameters = array('objFrom' => $objFrom);
1164  $action = '';
1165  $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1166  if ($reshook < 0) {
1167  $this->setErrorsFromObject($hookmanager);
1168  $error++;
1169  }
1170  }
1171  }
1172 
1173  unset($this->context['createfromclone']);
1174 
1175  // End
1176  if (!$error) {
1177  $this->db->commit();
1178  return $this->id;
1179  } else {
1180  $this->db->rollback();
1181  return -1;
1182  }
1183  }
1184 
1192  public function fetch($rowid, $ref = '')
1193  {
1194  global $conf;
1195 
1196  $sql = "SELECT p.rowid, p.entity, p.ref, p.fk_soc as socid";
1197  $sql .= ", p.total_ttc, p.total_tva, p.localtax1, p.localtax2, p.total_ht";
1198  $sql .= ", p.datec";
1199  $sql .= ", p.date_valid as datev";
1200  $sql .= ", p.date_livraison as delivery_date";
1201  $sql .= ", p.model_pdf, p.extraparams";
1202  $sql .= ", p.note_private, p.note_public";
1203  $sql .= ", p.fk_projet as fk_project, p.fk_statut";
1204  $sql .= ", p.fk_user_author, p.fk_user_valid, p.fk_user_cloture";
1205  $sql .= ", p.fk_cond_reglement";
1206  $sql .= ", p.fk_mode_reglement";
1207  $sql .= ', p.fk_account';
1208  $sql .= ", p.fk_shipping_method";
1209  $sql .= ", p.fk_multicurrency, p.multicurrency_code, p.multicurrency_tx, p.multicurrency_total_ht, p.multicurrency_total_tva, p.multicurrency_total_ttc";
1210  $sql .= ", c.label as statut_label";
1211  $sql .= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc";
1212  $sql .= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement";
1213  $sql .= " FROM ".MAIN_DB_PREFIX."c_propalst as c, ".MAIN_DB_PREFIX."supplier_proposal as p";
1214  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id';
1215  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON p.fk_cond_reglement = cr.rowid';
1216  $sql .= " WHERE p.fk_statut = c.id";
1217  $sql .= " AND p.entity IN (".getEntity('supplier_proposal').")";
1218  if ($ref) {
1219  $sql .= " AND p.ref = '".$this->db->escape($ref)."'";
1220  } else {
1221  $sql .= " AND p.rowid = ".((int) $rowid);
1222  }
1223 
1224  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1225  $resql = $this->db->query($sql);
1226  if ($resql) {
1227  if ($this->db->num_rows($resql)) {
1228  $obj = $this->db->fetch_object($resql);
1229 
1230  $this->id = $obj->rowid;
1231  $this->entity = $obj->entity;
1232 
1233  $this->ref = $obj->ref;
1234  $this->total_ht = $obj->total_ht;
1235  $this->total_tva = $obj->total_tva;
1236  $this->total_localtax1 = $obj->localtax1;
1237  $this->total_localtax2 = $obj->localtax2;
1238  $this->total_ttc = $obj->total_ttc;
1239  $this->socid = $obj->socid;
1240  $this->fk_project = $obj->fk_project;
1241  $this->model_pdf = $obj->model_pdf;
1242  $this->note = $obj->note_private; // TODO deprecated
1243  $this->note_private = $obj->note_private;
1244  $this->note_public = $obj->note_public;
1245  $this->statut = (int) $obj->fk_statut;
1246  $this->status = (int) $obj->fk_statut;
1247  $this->datec = $this->db->jdate($obj->datec); // TODO deprecated
1248  $this->datev = $this->db->jdate($obj->datev); // TODO deprecated
1249  $this->date_creation = $this->db->jdate($obj->datec); // Creation date
1250  $this->date = $this->date_creation;
1251  $this->date_validation = $this->db->jdate($obj->datev); // Validation date
1252  $this->delivery_date = $this->db->jdate($obj->delivery_date);
1253  $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1254 
1255  $this->mode_reglement_id = $obj->fk_mode_reglement;
1256  $this->mode_reglement_code = $obj->mode_reglement_code;
1257  $this->mode_reglement = $obj->mode_reglement;
1258  $this->fk_account = ($obj->fk_account > 0) ? $obj->fk_account : null;
1259  $this->cond_reglement_id = $obj->fk_cond_reglement;
1260  $this->cond_reglement_code = $obj->cond_reglement_code;
1261  $this->cond_reglement = $obj->cond_reglement;
1262  $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1263 
1264  $this->extraparams = (array) json_decode($obj->extraparams, true);
1265 
1266  $this->user_author_id = $obj->fk_user_author;
1267  $this->user_validation_id = $obj->fk_user_valid;
1268  $this->user_closing_id = $obj->fk_user_cloture;
1269 
1270  // Multicurrency
1271  $this->fk_multicurrency = $obj->fk_multicurrency;
1272  $this->multicurrency_code = $obj->multicurrency_code;
1273  $this->multicurrency_tx = $obj->multicurrency_tx;
1274  $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1275  $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1276  $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1277 
1278  // Retrieve all extrafield
1279  // fetch optionals attributes and labels
1280  $this->fetch_optionals();
1281 
1282  $this->db->free($resql);
1283 
1284  $this->lines = array();
1285 
1286  // Lines of supplier proposals
1287  $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,";
1288  $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,";
1289  $sql .= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label,';
1290  $sql .= ' d.ref_fourn as ref_produit_fourn,';
1291  $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';
1292  $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposaldet as d";
1293  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON d.fk_product = p.rowid";
1294  $sql .= " WHERE d.fk_supplier_proposal = ".((int) $this->id);
1295  $sql .= " ORDER by d.rang";
1296 
1297  $result = $this->db->query($sql);
1298  if ($result) {
1299  $num = $this->db->num_rows($result);
1300  $i = 0;
1301 
1302  while ($i < $num) {
1303  $objp = $this->db->fetch_object($result);
1304 
1305  $line = new SupplierProposalLine($this->db);
1306 
1307  $line->rowid = $objp->rowid; // deprecated
1308  $line->id = $objp->rowid;
1309  $line->fk_supplier_proposal = $objp->fk_supplier_proposal;
1310  $line->fk_parent_line = $objp->fk_parent_line;
1311  $line->product_type = $objp->product_type;
1312  $line->label = $objp->custom_label;
1313  $line->desc = $objp->description; // Description ligne
1314  $line->qty = $objp->qty;
1315  $line->tva_tx = $objp->tva_tx;
1316  $line->localtax1_tx = $objp->localtax1_tx;
1317  $line->localtax2_tx = $objp->localtax2_tx;
1318  $line->subprice = $objp->subprice;
1319  $line->fk_remise_except = $objp->fk_remise_except;
1320  $line->remise_percent = $objp->remise_percent;
1321 
1322  $line->info_bits = $objp->info_bits;
1323  $line->total_ht = $objp->total_ht;
1324  $line->total_tva = $objp->total_tva;
1325  $line->total_localtax1 = $objp->total_localtax1;
1326  $line->total_localtax2 = $objp->total_localtax2;
1327  $line->total_ttc = $objp->total_ttc;
1328  $line->fk_fournprice = $objp->fk_fournprice;
1329  $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1330  $line->pa_ht = $marginInfos[0];
1331  $line->marge_tx = $marginInfos[1];
1332  $line->marque_tx = $marginInfos[2];
1333  $line->special_code = $objp->special_code;
1334  $line->rang = $objp->rang;
1335 
1336  $line->fk_product = $objp->fk_product;
1337 
1338  $line->ref = $objp->product_ref; // deprecated
1339  $line->product_ref = $objp->product_ref;
1340  $line->libelle = $objp->product_label; // deprecated
1341  $line->product_label = $objp->product_label;
1342  $line->product_desc = $objp->product_desc; // Description produit
1343  $line->fk_product_type = $objp->fk_product_type;
1344 
1345  $line->ref_fourn = $objp->ref_produit_fourn;
1346 
1347  // Multicurrency
1348  $line->fk_multicurrency = $objp->fk_multicurrency;
1349  $line->multicurrency_code = $objp->multicurrency_code;
1350  $line->multicurrency_subprice = $objp->multicurrency_subprice;
1351  $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
1352  $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
1353  $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
1354  $line->fk_unit = $objp->fk_unit;
1355 
1356  $this->lines[$i] = $line;
1357 
1358  $i++;
1359  }
1360  $this->db->free($result);
1361  } else {
1362  $this->error = $this->db->error();
1363  return -1;
1364  }
1365 
1366  // Retrieve all extrafield
1367  // fetch optionals attributes and labels
1368  $this->fetch_optionals();
1369 
1370  return 1;
1371  }
1372 
1373  $this->error = "Record Not Found";
1374  return 0;
1375  } else {
1376  $this->error = $this->db->error();
1377  return -1;
1378  }
1379  }
1380 
1388  public function valid($user, $notrigger = 0)
1389  {
1390  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1391 
1392  global $conf, $langs;
1393 
1394  $error = 0;
1395  $now = dol_now();
1396 
1397  if ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'creer'))
1398  || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'validate_advance'))) {
1399  $this->db->begin();
1400 
1401  // Numbering module definition
1402  $soc = new Societe($this->db);
1403  $result = $soc->fetch($this->socid);
1404 
1405  if ($result < 0) {
1406  return -1;
1407  }
1408 
1409  // Define new ref
1410  if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1411  $num = $this->getNextNumRef($soc);
1412  } else {
1413  $num = $this->ref;
1414  }
1415  $this->newref = dol_sanitizeFileName($num);
1416 
1417  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1418  $sql .= " SET ref = '".$this->db->escape($num)."',";
1419  $sql .= " fk_statut = 1, date_valid='".$this->db->idate($now)."', fk_user_valid=".((int) $user->id);
1420  $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1421 
1422  dol_syslog(get_class($this)."::valid", LOG_DEBUG);
1423  $resql = $this->db->query($sql);
1424  if (!$resql) {
1425  dol_print_error($this->db);
1426  $error++;
1427  }
1428 
1429  // Trigger calls
1430  if (!$error && !$notrigger) {
1431  // Call trigger
1432  $result = $this->call_trigger('PROPOSAL_SUPPLIER_VALIDATE', $user);
1433  if ($result < 0) {
1434  $error++;
1435  }
1436  // End call triggers
1437  }
1438 
1439  if (!$error) {
1440  $this->oldref = $this->ref;
1441 
1442  // Rename directory if dir was a temporary ref
1443  if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1444  // Now we rename also files into index
1445  $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)."'";
1446  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1447  $resql = $this->db->query($sql);
1448  if (!$resql) {
1449  $error++;
1450  $this->error = $this->db->lasterror();
1451  }
1452  $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'supplier_proposal/".$this->db->escape($this->newref)."'";
1453  $sql .= " WHERE filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1454  $resql = $this->db->query($sql);
1455  if (!$resql) {
1456  $error++;
1457  $this->error = $this->db->lasterror();
1458  }
1459 
1460  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1461  $oldref = dol_sanitizeFileName($this->ref);
1462  $newref = dol_sanitizeFileName($num);
1463  $dirsource = $conf->supplier_proposal->dir_output.'/'.$oldref;
1464  $dirdest = $conf->supplier_proposal->dir_output.'/'.$newref;
1465  if (!$error && file_exists($dirsource)) {
1466  dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
1467  if (@rename($dirsource, $dirdest)) {
1468  dol_syslog("Rename ok");
1469  // Rename docs starting with $oldref with $newref
1470  $listoffiles = dol_dir_list($conf->supplier_proposal->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1471  foreach ($listoffiles as $fileentry) {
1472  $dirsource = $fileentry['name'];
1473  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1474  $dirsource = $fileentry['path'].'/'.$dirsource;
1475  $dirdest = $fileentry['path'].'/'.$dirdest;
1476  @rename($dirsource, $dirdest);
1477  }
1478  }
1479  }
1480  }
1481 
1482  $this->ref = $num;
1483  $this->statut = self::STATUS_VALIDATED;
1484  $this->status = self::STATUS_VALIDATED;
1485  $this->user_validation_id = $user->id;
1486  $this->datev = $now;
1487  $this->date_validation = $now;
1488 
1489  $this->db->commit();
1490  return 1;
1491  } else {
1492  $this->db->rollback();
1493  return -1;
1494  }
1495  } else {
1496  dol_syslog("You don't have permission to validate supplier proposal", LOG_WARNING);
1497  return -2;
1498  }
1499  }
1500 
1501  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1510  public function set_date_livraison($user, $delivery_date)
1511  {
1512  // phpcs:enable
1513  return $this->setDeliveryDate($user, $delivery_date);
1514  }
1515 
1523  public function setDeliveryDate($user, $delivery_date)
1524  {
1525  if ($user->hasRight('supplier_proposal', 'creer')) {
1526  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1527  $sql .= " SET date_livraison = ".($delivery_date != '' ? "'".$this->db->idate($delivery_date)."'" : 'null');
1528  $sql .= " WHERE rowid = ".((int) $this->id);
1529 
1530  if ($this->db->query($sql)) {
1531  $this->delivery_date = $delivery_date;
1532  return 1;
1533  } else {
1534  $this->error = $this->db->error();
1535  dol_syslog(get_class($this)."::setDeliveryDate Erreur SQL");
1536  return -1;
1537  }
1538  }
1539  return 0;
1540  }
1541 
1542  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1550  /*
1551  public function set_remise_percent($user, $remise)
1552  {
1553  // phpcs:enable
1554  $remise = trim($remise) ?trim($remise) : 0;
1555 
1556  if ($user->hasRight('supplier_proposal', 'creer')) {
1557  $remise = price2num($remise, 2);
1558 
1559  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal SET remise_percent = ".((float) $remise);
1560  $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1561 
1562  if ($this->db->query($sql)) {
1563  $this->remise_percent = ((float) $remise);
1564  $this->update_price(1);
1565  return 1;
1566  } else {
1567  $this->error = $this->db->error();
1568  return -1;
1569  }
1570  }
1571  return 0;
1572  }
1573  */
1574 
1575  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1583  /*
1584  public function set_remise_absolue($user, $remise)
1585  {
1586  // phpcs:enable
1587  if (empty($remise)) {
1588  $remise = 0;
1589  }
1590 
1591  $remise = price2num($remise);
1592 
1593  if ($user->hasRight('supplier_proposal', 'creer')) {
1594  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1595  $sql .= " SET remise_absolue = ".((float) $remise);
1596  $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1597 
1598  if ($this->db->query($sql)) {
1599  $this->remise_absolue = $remise;
1600  $this->update_price(1);
1601  return 1;
1602  } else {
1603  $this->error = $this->db->error();
1604  return -1;
1605  }
1606  }
1607  return 0;
1608  }
1609  */
1610 
1611 
1621  public function reopen($user, $statut, $note = '', $notrigger = 0)
1622  {
1623  global $langs, $conf;
1624 
1625  $this->statut = $statut;
1626  $error = 0;
1627 
1628  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1629  $sql .= " SET fk_statut = ".((int) $this->statut).",";
1630  if (!empty($note)) {
1631  $sql .= " note_private = '".$this->db->escape($note)."',";
1632  }
1633  $sql .= " date_cloture = NULL, fk_user_cloture = NULL";
1634  $sql .= " WHERE rowid = ".((int) $this->id);
1635 
1636  $this->db->begin();
1637 
1638  dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
1639  $resql = $this->db->query($sql);
1640  if (!$resql) {
1641  $error++;
1642  $this->errors[] = "Error ".$this->db->lasterror();
1643  }
1644  if (!$error) {
1645  if (!$notrigger) {
1646  // Call trigger
1647  $result = $this->call_trigger('PROPOSAL_SUPPLIER_REOPEN', $user);
1648  if ($result < 0) {
1649  $error++;
1650  }
1651  // End call triggers
1652  }
1653  }
1654 
1655  // Commit or rollback
1656  if ($error) {
1657  if (!empty($this->errors)) {
1658  foreach ($this->errors as $errmsg) {
1659  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1660  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1661  }
1662  }
1663  $this->db->rollback();
1664  return -1 * $error;
1665  } else {
1666  $this->db->commit();
1667  return 1;
1668  }
1669  }
1670 
1671 
1680  public function cloture($user, $status, $note)
1681  {
1682  global $langs, $conf;
1683  $hidedetails = 0;
1684  $hidedesc = 0;
1685  $hideref = 0;
1686  $this->statut = $status;
1687  $error = 0;
1688  $now = dol_now();
1689 
1690  $this->db->begin();
1691 
1692  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1693  $sql .= " SET fk_statut = ".((int) $status).", note_private = '".$this->db->escape($note)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".$user->id;
1694  $sql .= " WHERE rowid = ".((int) $this->id);
1695 
1696  $resql = $this->db->query($sql);
1697  if ($resql) {
1698  $modelpdf = $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED ? $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED : (empty($this->model_pdf) ? '' : $this->model_pdf);
1699  $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_REFUSED';
1700 
1701  if ($status == 2) {
1702  $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_SIGNED';
1703  $modelpdf = $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL ? $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL : (empty($this->model_pdf) ? '' : $this->model_pdf);
1704 
1705  if (getDolGlobalString('SUPPLIER_PROPOSAL_UPDATE_PRICE_ON_SUPPlIER_PROPOSAL')) { // TODO This option was not tested correctly. Error if product ref does not exists
1706  $result = $this->updateOrCreatePriceFournisseur($user);
1707  }
1708  }
1709  if ($status == 4) {
1710  $triggerName = 'PROPOSAL_SUPPLIER_CLASSIFY_BILLED';
1711  }
1712 
1713  if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1714  // Define output language
1715  $outputlangs = $langs;
1716  if (getDolGlobalInt('MAIN_MULTILANGS')) {
1717  $outputlangs = new Translate("", $conf);
1718  $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
1719  $outputlangs->setDefaultLang($newlang);
1720  }
1721  //$ret=$object->fetch($id); // Reload to get new records
1722  $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1723  }
1724 
1725  // Call trigger
1726  $result = $this->call_trigger($triggerName, $user);
1727  if ($result < 0) {
1728  $error++;
1729  }
1730  // End call triggers
1731 
1732  if (!$error) {
1733  $this->db->commit();
1734  return 1;
1735  } else {
1736  $this->db->rollback();
1737  return -1;
1738  }
1739  } else {
1740  $this->error = $this->db->lasterror();
1741  $this->errors[] = $this->db->lasterror();
1742  $this->db->rollback();
1743  return -1;
1744  }
1745  }
1746 
1753  public function updateOrCreatePriceFournisseur($user)
1754  {
1755  global $conf;
1756 
1757  dol_syslog(get_class($this)."::updateOrCreatePriceFournisseur", LOG_DEBUG);
1758  foreach ($this->lines as $product) {
1759  if ($product->subprice <= 0) {
1760  continue;
1761  }
1762  $productsupplier = new ProductFournisseur($this->db);
1763 
1764  $multicurrency_tx = 1;
1765  $fk_multicurrency = 0;
1766 
1767  if (empty($this->thirdparty)) {
1768  $this->fetch_thirdparty();
1769  }
1770 
1771  $ref_fourn = $product->ref_fourn;
1772  if (empty($ref_fourn)) {
1773  $ref_fourn = $product->ref_supplier;
1774  }
1775  if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1776  list($fk_multicurrency, $multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $product->multicurrency_code);
1777  }
1778  $productsupplier->id = $product->fk_product;
1779 
1780  $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, '', '', '');
1781  }
1782 
1783  return 1;
1784  }
1785 
1794  public function updatePriceFournisseur($idProductFournPrice, $product, $user)
1795  {
1796  $price = price2num($product->subprice * $product->qty, 'MU');
1797  $unitPrice = price2num($product->subprice, 'MU');
1798 
1799  $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);
1800 
1801  $resql = $this->db->query($sql);
1802  if (!$resql) {
1803  $this->error = $this->db->error();
1804  $this->db->rollback();
1805  return -1;
1806  }
1807  return 1;
1808  }
1809 
1817  public function createPriceFournisseur($product, $user)
1818  {
1819  global $conf;
1820 
1821  $price = price2num($product->subprice * $product->qty, 'MU');
1822  $qty = price2num($product->qty);
1823  $unitPrice = price2num($product->subprice, 'MU');
1824 
1825  $now = dol_now();
1826 
1827  $values = array(
1828  "'".$this->db->idate($now)."'",
1829  $product->fk_product,
1830  $this->thirdparty->id,
1831  "'".$product->ref_fourn."'",
1832  $price,
1833  $qty,
1834  $unitPrice,
1835  $product->tva_tx,
1836  $user->id
1837  );
1838  if (isModEnabled("multicurrency")) {
1839  if (!empty($product->multicurrency_code)) {
1840  include_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
1841  $multicurrency = new MultiCurrency($this->db); //need to fetch because empty fk_multicurrency and rate
1842  $multicurrency->fetch(0, $product->multicurrency_code);
1843  if (!empty($multicurrency->id)) {
1844  $values[] = $multicurrency->id;
1845  $values[] = "'".$product->multicurrency_code."'";
1846  $values[] = $product->multicurrency_subprice;
1847  $values[] = $product->multicurrency_total_ht;
1848  $values[] = $multicurrency->rate->rate;
1849  } else {
1850  for ($i = 0; $i < 5; $i++) {
1851  $values[] = 'NULL';
1852  }
1853  }
1854  }
1855  }
1856 
1857  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'product_fournisseur_price ';
1858  $sql .= '(datec, fk_product, fk_soc, ref_fourn, price, quantity, unitprice, tva_tx, fk_user';
1859  if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1860  $sql .= ',fk_multicurrency, multicurrency_code, multicurrency_unitprice, multicurrency_price, multicurrency_tx';
1861  }
1862  $sql .= ') VALUES ('.implode(',', $values).')';
1863 
1864  $resql = $this->db->query($sql);
1865  if (!$resql) {
1866  $this->error = $this->db->error();
1867  $this->db->rollback();
1868  return -1;
1869  }
1870  return 1;
1871  }
1872 
1873  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1880  public function setDraft($user)
1881  {
1882  // phpcs:enable
1883  global $conf, $langs;
1884 
1885  $error = 0;
1886 
1887  if ($this->statut == self::STATUS_DRAFT) {
1888  dol_syslog(get_class($this)."::setDraft already draft status", LOG_WARNING);
1889  return 0;
1890  }
1891 
1892  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1893  $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1894  $sql .= " WHERE rowid = ".((int) $this->id);
1895 
1896  if ($this->db->query($sql)) {
1897  if (!$error) {
1898  $this->oldcopy = clone $this;
1899  }
1900 
1901  if (!$error) {
1902  // Call trigger
1903  $result = $this->call_trigger('PROPOSAL_SUPPLIER_UNVALIDATE', $user);
1904  if ($result < 0) {
1905  $error++;
1906  }
1907  }
1908 
1909  if (!$error) {
1910  $this->status = self::STATUS_DRAFT;
1911  $this->statut = self::STATUS_DRAFT; // deprecated
1912  $this->db->commit();
1913  return 1;
1914  } else {
1915  $this->db->rollback();
1916  return -1;
1917  }
1918  } else {
1919  return -1;
1920  }
1921  }
1922 
1923 
1924  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1938  public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datec', $sortorder = 'DESC')
1939  {
1940  // phpcs:enable
1941  global $user;
1942 
1943  $ga = array();
1944 
1945  $search_sale = 0;
1946  if (!$user->hasRight('societe', 'client', 'voir')) {
1947  $search_sale = $user->id;
1948  }
1949 
1950  $sql = "SELECT s.rowid, s.nom as name, s.client,";
1951  $sql .= " p.rowid as supplier_proposalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
1952  $sql .= " p.datep as dp, p.fin_validite as datelimite";
1953  $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."supplier_proposal as p, ".MAIN_DB_PREFIX."c_propalst as c";
1954  $sql .= " WHERE p.entity IN (".getEntity('supplier_proposal').")";
1955  $sql .= " AND p.fk_soc = s.rowid";
1956  $sql .= " AND p.fk_statut = c.id";
1957  if ($socid) {
1958  $sql .= " AND s.rowid = ".((int) $socid);
1959  }
1960  if ($draft) {
1961  $sql .= " AND p.fk_statut = 0";
1962  }
1963  if ($notcurrentuser > 0) {
1964  $sql .= " AND p.fk_user_author <> ".((int) $user->id);
1965  }
1966  // Search on sale representative
1967  if ($search_sale && $search_sale != '-1') {
1968  if ($search_sale == -2) {
1969  $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
1970  } elseif ($search_sale > 0) {
1971  $sql .= " AND EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc AND sc.fk_user = ".((int) $search_sale).")";
1972  }
1973  }
1974  $sql .= $this->db->order($sortfield, $sortorder);
1975  $sql .= $this->db->plimit($limit, $offset);
1976 
1977  $result = $this->db->query($sql);
1978  if ($result) {
1979  $num = $this->db->num_rows($result);
1980  if ($num) {
1981  $i = 0;
1982  while ($i < $num) {
1983  $obj = $this->db->fetch_object($result);
1984 
1985  if ($shortlist == 1) {
1986  $ga[$obj->supplier_proposalid] = $obj->ref;
1987  } elseif ($shortlist == 2) {
1988  $ga[$obj->supplier_proposalid] = $obj->ref.' ('.$obj->name.')';
1989  } else {
1990  $ga[$i]['id'] = $obj->supplier_proposalid;
1991  $ga[$i]['ref'] = $obj->ref;
1992  $ga[$i]['name'] = $obj->name;
1993  }
1994 
1995  $i++;
1996  }
1997  }
1998  return $ga;
1999  } else {
2000  dol_print_error($this->db);
2001  return -1;
2002  }
2003  }
2004 
2012  public function delete($user, $notrigger = 0)
2013  {
2014  global $conf, $langs;
2015  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2016 
2017  $error = 0;
2018 
2019  $this->db->begin();
2020 
2021  if (!$notrigger) {
2022  // Call trigger
2023  $result = $this->call_trigger('PROPOSAL_SUPPLIER_DELETE', $user);
2024  if ($result < 0) {
2025  $error++;
2026  }
2027  // End call triggers
2028  }
2029 
2030  if (!$error) {
2031  $main = MAIN_DB_PREFIX.'supplier_proposaldet';
2032  $ef = $main."_extrafields";
2033  $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_supplier_proposal = ".((int) $this->id).")";
2034  $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet WHERE fk_supplier_proposal = ".((int) $this->id);
2035  if ($this->db->query($sql)) {
2036  $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposal WHERE rowid = ".((int) $this->id);
2037  if ($this->db->query($sqlef) && $this->db->query($sql)) {
2038  // Delete linked object
2039  $res = $this->deleteObjectLinked();
2040  if ($res < 0) {
2041  $error++;
2042  }
2043 
2044  if (!$error) {
2045  // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2046  $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
2047  $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
2048 
2049  // We remove directory
2050  $ref = dol_sanitizeFileName($this->ref);
2051  if ($conf->supplier_proposal->dir_output && !empty($this->ref)) {
2052  $dir = $conf->supplier_proposal->dir_output."/".$ref;
2053  $file = $dir."/".$ref.".pdf";
2054  if (file_exists($file)) {
2055  dol_delete_preview($this);
2056 
2057  if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2058  $this->error = 'ErrorFailToDeleteFile';
2059  $this->errors = array('ErrorFailToDeleteFile');
2060  $this->db->rollback();
2061  return 0;
2062  }
2063  }
2064  if (file_exists($dir)) {
2065  $res = @dol_delete_dir_recursive($dir);
2066  if (!$res) {
2067  $this->error = 'ErrorFailToDeleteDir';
2068  $this->errors = array('ErrorFailToDeleteDir');
2069  $this->db->rollback();
2070  return 0;
2071  }
2072  }
2073  }
2074  }
2075 
2076  // Removed extrafields
2077  if (!$error) {
2078  $result = $this->deleteExtraFields();
2079  if ($result < 0) {
2080  $error++;
2081  $errorflag = -4;
2082  dol_syslog(get_class($this)."::delete erreur ".$errorflag." ".$this->error, LOG_ERR);
2083  }
2084  }
2085 
2086  if (!$error) {
2087  dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
2088  $this->db->commit();
2089  return 1;
2090  } else {
2091  $this->error = $this->db->lasterror();
2092  $this->db->rollback();
2093  return 0;
2094  }
2095  } else {
2096  $this->error = $this->db->lasterror();
2097  $this->db->rollback();
2098  return -3;
2099  }
2100  } else {
2101  $this->error = $this->db->lasterror();
2102  $this->db->rollback();
2103  return -2;
2104  }
2105  } else {
2106  $this->db->rollback();
2107  return -1;
2108  }
2109  }
2110 
2117  public function info($id)
2118  {
2119  $sql = "SELECT c.rowid, ";
2120  $sql .= " c.datec as date_creation, c.date_valid as date_validation, c.date_cloture as date_closure,";
2121  $sql .= " c.fk_user_author, c.fk_user_valid, c.fk_user_cloture";
2122  $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as c";
2123  $sql .= " WHERE c.rowid = ".((int) $id);
2124 
2125  $result = $this->db->query($sql);
2126 
2127  if ($result) {
2128  if ($this->db->num_rows($result)) {
2129  $obj = $this->db->fetch_object($result);
2130 
2131  $this->id = $obj->rowid;
2132 
2133  $this->date_creation = $this->db->jdate($obj->date_creation);
2134  $this->date_validation = $this->db->jdate($obj->date_validation);
2135  $this->date_cloture = $this->db->jdate($obj->date_closure);
2136 
2137  $this->user_creation_id = $obj->fk_user_author;
2138  $this->user_validation_id = $obj->fk_user_valid;
2139  $this->user_closing_id = $obj->fk_user_cloture;
2140  }
2141  $this->db->free($result);
2142  } else {
2143  dol_print_error($this->db);
2144  }
2145  }
2146 
2147 
2154  public function getLibStatut($mode = 0)
2155  {
2156  return $this->LibStatut((isset($this->statut) ? $this->statut : $this->status), $mode);
2157  }
2158 
2159  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2167  public function LibStatut($status, $mode = 1)
2168  {
2169  // phpcs:enable
2170 
2171  // Init/load array of translation of status
2172  if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
2173  global $langs;
2174  $langs->load("supplier_proposal");
2175  $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraft");
2176  $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidated");
2177  $this->labelStatus[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSigned");
2178  $this->labelStatus[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSigned");
2179  $this->labelStatus[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosed");
2180  $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraftShort");
2181  $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidatedShort");
2182  $this->labelStatusShort[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSignedShort");
2183  $this->labelStatusShort[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSignedShort");
2184  $this->labelStatusShort[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosedShort");
2185  }
2186 
2187  $statusnew = '';
2188  if ($status == self::STATUS_DRAFT) {
2189  $statusnew = 'status0';
2190  } elseif ($status == self::STATUS_VALIDATED) {
2191  $statusnew = 'status1';
2192  } elseif ($status == self::STATUS_SIGNED) {
2193  $statusnew = 'status4';
2194  } elseif ($status == self::STATUS_NOTSIGNED) {
2195  $statusnew = 'status9';
2196  } elseif ($status == self::STATUS_CLOSE) {
2197  $statusnew = 'status6';
2198  }
2199 
2200  return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusnew, $mode);
2201  }
2202 
2203 
2204  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2212  public function load_board($user, $mode)
2213  {
2214  // phpcs:enable
2215  global $conf, $user, $langs;
2216 
2217  $now = dol_now();
2218 
2219  $clause = " WHERE";
2220 
2221  $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.date_cloture as datefin";
2222  $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2223  if (!$user->hasRight('societe', 'client', 'voir')) {
2224  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
2225  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2226  $clause = " AND";
2227  }
2228  $sql .= $clause." p.entity IN (".getEntity('supplier_proposal').")";
2229  if ($mode == 'opened') {
2230  $sql .= " AND p.fk_statut = 1";
2231  }
2232  if ($mode == 'signed') {
2233  $sql .= " AND p.fk_statut = 2";
2234  }
2235  if ($user->socid) {
2236  $sql .= " AND p.fk_soc = ".((int) $user->socid);
2237  }
2238 
2239  $resql = $this->db->query($sql);
2240  if ($resql) {
2241  $label = $labelShort = '';
2242  $status = '';
2243  if ($mode == 'opened') {
2244  $delay_warning = !empty($conf->supplier_proposal->cloture->warning_delay) ? $conf->supplier_proposal->cloture->warning_delay : 0;
2245  $status = self::STATUS_VALIDATED;
2246  $label = $langs->trans("SupplierProposalsToClose");
2247  $labelShort = $langs->trans("ToAcceptRefuse");
2248  }
2249  if ($mode == 'signed') {
2250  $delay_warning = !empty($conf->supplier_proposal->facturation->warning_delay) ? $conf->supplier_proposal->facturation->warning_delay : 0;
2251  $status = self::STATUS_SIGNED;
2252  $label = $langs->trans("SupplierProposalsToProcess"); // May be billed or ordered
2253  $labelShort = $langs->trans("ToClose");
2254  }
2255 
2256  $response = new WorkboardResponse();
2257  $response->warning_delay = $delay_warning / 60 / 60 / 24;
2258  $response->label = $label;
2259  $response->labelShort = $labelShort;
2260  $response->url = DOL_URL_ROOT.'/supplier_proposal/list.php?search_status='.$status;
2261  $response->img = img_object('', "propal");
2262 
2263  // This assignment in condition is not a bug. It allows walking the results.
2264  while ($obj = $this->db->fetch_object($resql)) {
2265  $response->nbtodo++;
2266  if ($mode == 'opened') {
2267  $datelimit = $this->db->jdate($obj->datefin);
2268  if ($datelimit < ($now - $delay_warning)) {
2269  $response->nbtodolate++;
2270  }
2271  }
2272  // TODO Definir regle des propales a facturer en retard
2273  // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
2274  }
2275  return $response;
2276  } else {
2277  $this->error = $this->db->lasterror();
2278  return -1;
2279  }
2280  }
2281 
2282 
2290  public function initAsSpecimen()
2291  {
2292  global $user, $langs, $conf;
2293 
2294  // Load array of products prodids
2295  $num_prods = 0;
2296  $prodids = array();
2297  $sql = "SELECT rowid";
2298  $sql .= " FROM ".MAIN_DB_PREFIX."product";
2299  $sql .= " WHERE entity IN (".getEntity('product').")";
2300  $sql .= $this->db->plimit(100);
2301 
2302  $resql = $this->db->query($sql);
2303  if ($resql) {
2304  $num_prods = $this->db->num_rows($resql);
2305  $i = 0;
2306  while ($i < $num_prods) {
2307  $i++;
2308  $row = $this->db->fetch_row($resql);
2309  $prodids[$i] = $row[0];
2310  }
2311  }
2312 
2313  // Initialise parameters
2314  $this->id = 0;
2315  $this->ref = 'SPECIMEN';
2316  $this->specimen = 1;
2317  $this->socid = 1;
2318  $this->date = time();
2319  $this->cond_reglement_id = 1;
2320  $this->cond_reglement_code = 'RECEP';
2321  $this->mode_reglement_id = 7;
2322  $this->mode_reglement_code = 'CHQ';
2323  $this->note_public = 'This is a comment (public)';
2324  $this->note_private = 'This is a comment (private)';
2325  // Lines
2326  $nbp = 5;
2327  $xnbp = 0;
2328  while ($xnbp < $nbp) {
2329  $line = new SupplierProposalLine($this->db);
2330  $line->desc = $langs->trans("Description")." ".$xnbp;
2331  $line->qty = 1;
2332  $line->subprice = 100;
2333  $line->tva_tx = 19.6;
2334  $line->localtax1_tx = 0;
2335  $line->localtax2_tx = 0;
2336  if ($xnbp == 2) {
2337  $line->total_ht = 50;
2338  $line->total_ttc = 59.8;
2339  $line->total_tva = 9.8;
2340  $line->remise_percent = 50;
2341  } else {
2342  $line->total_ht = 100;
2343  $line->total_ttc = 119.6;
2344  $line->total_tva = 19.6;
2345  $line->remise_percent = 00;
2346  }
2347 
2348  if ($num_prods > 0) {
2349  $prodid = mt_rand(1, $num_prods);
2350  $line->fk_product = $prodids[$prodid];
2351  }
2352 
2353  $this->lines[$xnbp] = $line;
2354 
2355  $this->total_ht += $line->total_ht;
2356  $this->total_tva += $line->total_tva;
2357  $this->total_ttc += $line->total_ttc;
2358 
2359  $xnbp++;
2360  }
2361 
2362  return 1;
2363  }
2364 
2370  public function loadStateBoard()
2371  {
2372  global $conf, $user;
2373 
2374  $this->nb = array();
2375  $clause = "WHERE";
2376 
2377  $sql = "SELECT count(p.rowid) as nb";
2378  $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2379  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
2380  if (!$user->hasRight('societe', 'client', 'voir')) {
2381  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2382  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2383  $clause = "AND";
2384  }
2385  $sql .= " ".$clause." p.entity IN (".getEntity('supplier_proposal').")";
2386 
2387  $resql = $this->db->query($sql);
2388  if ($resql) {
2389  // This assignment in condition is not a bug. It allows walking the results.
2390  while ($obj = $this->db->fetch_object($resql)) {
2391  $this->nb["supplier_proposals"] = $obj->nb;
2392  }
2393  $this->db->free($resql);
2394  return 1;
2395  } else {
2396  dol_print_error($this->db);
2397  $this->error = $this->db->lasterror();
2398  return -1;
2399  }
2400  }
2401 
2402 
2410  public function getNextNumRef($soc)
2411  {
2412  global $conf, $db, $langs;
2413  $langs->load("supplier_proposal");
2414 
2415  if (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON')) {
2416  $mybool = false;
2417 
2418  $file = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') . ".php";
2419  $classname = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
2420 
2421  // Include file with class
2422  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2423  foreach ($dirmodels as $reldir) {
2424  $dir = dol_buildpath($reldir."core/modules/supplier_proposal/");
2425 
2426  // Load file with numbering class (if found)
2427  $mybool = ((bool) @include_once $dir.$file) || $mybool;
2428  }
2429 
2430  if (!$mybool) {
2431  dol_print_error(null, "Failed to include file ".$file);
2432  return '';
2433  }
2434 
2435  $obj = new $classname();
2436  $numref = "";
2437  $numref = $obj->getNextValue($soc, $this);
2438 
2439  if ($numref != "") {
2440  return $numref;
2441  } else {
2442  $this->error = $obj->error;
2443  return "";
2444  }
2445  } else {
2446  $langs->load("errors");
2447  print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("SupplierProposal"));
2448  return "";
2449  }
2450  }
2451 
2459  public function getTooltipContentArray($params)
2460  {
2461  global $conf, $langs, $menumanager;
2462 
2463  $langs->load('supplier_proposal');
2464 
2465  if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2466  return ['optimize' => $langs->trans("ShowSupplierProposal")];
2467  }
2468 
2469  $option = $params['option'] ?? '';
2470  $datas = [];
2471 
2472  $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("SupplierProposal").'</u>';
2473  if (isset($this->status)) {
2474  $datas['picto'] .= ' '.$this->getLibStatut(5);
2475  }
2476  if (!empty($this->ref)) {
2477  $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2478  }
2479  if (!empty($this->ref_fourn)) {
2480  $datas['ref_supplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_fourn;
2481  }
2482  if (!empty($this->total_ht)) {
2483  $datas['amount_ht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2484  }
2485  if (!empty($this->total_tva)) {
2486  $datas['amount_vat'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2487  }
2488  if (!empty($this->total_ttc)) {
2489  $datas['amount_ttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2490  }
2491 
2492  return $datas;
2493  }
2494 
2506  public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2507  {
2508  global $langs, $conf, $user, $hookmanager;
2509 
2510  if (!empty($conf->dol_no_mouse_hover)) {
2511  $notooltip = 1; // Force disable tooltips
2512  }
2513 
2514  $url = '';
2515  $result = '';
2516  $params = [
2517  'id' => $this->id,
2518  'objecttype' => $this->element,
2519  'option' => $option,
2520  ];
2521  $classfortooltip = 'classfortooltip';
2522  $dataparams = '';
2523  if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2524  $classfortooltip = 'classforajaxtooltip';
2525  $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
2526  $label = '';
2527  } else {
2528  $label = implode($this->getTooltipContentArray($params));
2529  }
2530 
2531  if ($option == '') {
2532  $url = DOL_URL_ROOT.'/supplier_proposal/card.php?id='.$this->id.$get_params;
2533  }
2534  if ($option == 'document') {
2535  $url = DOL_URL_ROOT.'/supplier_proposal/document.php?id='.$this->id.$get_params;
2536  }
2537 
2538  if ($option !== 'nolink') {
2539  // Add param to save lastsearch_values or not
2540  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2541  if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2542  $add_save_lastsearch_values = 1;
2543  }
2544  if ($add_save_lastsearch_values) {
2545  $url .= '&save_lastsearch_values=1';
2546  }
2547  }
2548 
2549  $linkclose = '';
2550  if (empty($notooltip) && $user->hasRight('propal', 'lire')) {
2551  if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2552  $label = $langs->trans("ShowSupplierProposal");
2553  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
2554  }
2555  $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
2556  $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
2557  }
2558 
2559  $linkstart = '<a href="'.$url.'"';
2560  $linkstart .= $linkclose.'>';
2561  $linkend = '</a>';
2562 
2563  $result .= $linkstart;
2564  if ($withpicto) {
2565  $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
2566  }
2567  if ($withpicto != 2) {
2568  $result .= $this->ref;
2569  }
2570  $result .= $linkend;
2571 
2572  if ($addlinktonotes) {
2573  $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2574  if ($txttoshow) {
2575  $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2576  $result .= ' <span class="note inline-block">';
2577  $result .= '<a href="'.DOL_URL_ROOT.'/supplier_proposal/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2578  $result .= img_picto('', 'note');
2579  $result .= '</a>';
2580  //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
2581  //$result.='</a>';
2582  $result .= '</span>';
2583  }
2584  }
2585  global $action;
2586  $hookmanager->initHooks(array($this->element . 'dao'));
2587  $parameters = array('id' => $this->id, 'getnomurl' => &$result);
2588  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2589  if ($reshook > 0) {
2590  $result = $hookmanager->resPrint;
2591  } else {
2592  $result .= $hookmanager->resPrint;
2593  }
2594  return $result;
2595  }
2596 
2602  public function getLinesArray()
2603  {
2604  // For other object, here we call fetch_lines. But fetch_lines does not exists on supplier proposal
2605 
2606  $sql = 'SELECT pt.rowid, pt.label as custom_label, pt.description, pt.fk_product, pt.fk_remise_except,';
2607  $sql .= ' pt.qty, pt.tva_tx, pt.vat_src_code, pt.remise_percent, pt.subprice, pt.info_bits,';
2608  $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,';
2609  $sql .= ' pt.product_type, pt.rang, pt.fk_parent_line,';
2610  $sql .= ' p.label as product_label, p.ref, p.fk_product_type, p.rowid as prodid,';
2611  $sql .= ' p.description as product_desc, pt.ref_fourn as ref_supplier,';
2612  $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';
2613  $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pt';
2614  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pt.fk_product=p.rowid';
2615  $sql .= ' WHERE pt.fk_supplier_proposal = '.((int) $this->id);
2616  $sql .= ' ORDER BY pt.rang ASC, pt.rowid';
2617 
2618  dol_syslog(get_class($this).'::getLinesArray', LOG_DEBUG);
2619  $resql = $this->db->query($sql);
2620  if ($resql) {
2621  $num = $this->db->num_rows($resql);
2622  $i = 0;
2623 
2624  while ($i < $num) {
2625  $obj = $this->db->fetch_object($resql);
2626 
2627  $this->lines[$i] = new SupplierProposalLine($this->db);
2628  $this->lines[$i]->id = $obj->rowid; // for backward compatibility
2629  $this->lines[$i]->rowid = $obj->rowid;
2630  $this->lines[$i]->label = $obj->custom_label;
2631  $this->lines[$i]->description = $obj->description;
2632  $this->lines[$i]->fk_product = $obj->fk_product;
2633  $this->lines[$i]->ref = $obj->ref;
2634  $this->lines[$i]->product_label = $obj->product_label;
2635  $this->lines[$i]->product_desc = $obj->product_desc;
2636  $this->lines[$i]->fk_product_type = $obj->fk_product_type; // deprecated
2637  $this->lines[$i]->product_type = $obj->product_type;
2638  $this->lines[$i]->qty = $obj->qty;
2639  $this->lines[$i]->subprice = $obj->subprice;
2640  $this->lines[$i]->fk_remise_except = $obj->fk_remise_except;
2641  $this->lines[$i]->remise_percent = $obj->remise_percent;
2642  $this->lines[$i]->tva_tx = $obj->tva_tx;
2643  $this->lines[$i]->vat_src_code = $obj->vat_src_code;
2644  $this->lines[$i]->info_bits = $obj->info_bits;
2645  $this->lines[$i]->total_ht = $obj->total_ht;
2646  $this->lines[$i]->total_tva = $obj->total_tva;
2647  $this->lines[$i]->total_ttc = $obj->total_ttc;
2648  $this->lines[$i]->fk_fournprice = $obj->fk_fournprice;
2649  $marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->lines[$i]->fk_fournprice, $obj->pa_ht);
2650  $this->lines[$i]->pa_ht = $marginInfos[0];
2651  $this->lines[$i]->marge_tx = $marginInfos[1];
2652  $this->lines[$i]->marque_tx = $marginInfos[2];
2653  $this->lines[$i]->fk_parent_line = $obj->fk_parent_line;
2654  $this->lines[$i]->special_code = $obj->special_code;
2655  $this->lines[$i]->rang = $obj->rang;
2656 
2657  $this->lines[$i]->ref_fourn = $obj->ref_supplier; // deprecated
2658  $this->lines[$i]->ref_supplier = $obj->ref_supplier;
2659 
2660  // Multicurrency
2661  $this->lines[$i]->fk_multicurrency = $obj->fk_multicurrency;
2662  $this->lines[$i]->multicurrency_code = $obj->multicurrency_code;
2663  $this->lines[$i]->multicurrency_subprice = $obj->multicurrency_subprice;
2664  $this->lines[$i]->multicurrency_total_ht = $obj->multicurrency_total_ht;
2665  $this->lines[$i]->multicurrency_total_tva = $obj->multicurrency_total_tva;
2666  $this->lines[$i]->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
2667  $this->lines[$i]->fk_unit = $obj->fk_unit;
2668 
2669  $i++;
2670  }
2671  $this->db->free($resql);
2672 
2673  return 1;
2674  } else {
2675  $this->error = $this->db->error();
2676  return -1;
2677  }
2678  }
2679 
2691  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2692  {
2693  global $conf, $langs;
2694 
2695  $langs->load("supplier_proposal");
2696  $outputlangs->load("products");
2697 
2698  if (!dol_strlen($modele)) {
2699  $modele = 'aurore';
2700 
2701  if ($this->model_pdf) {
2702  $modele = $this->model_pdf;
2703  } elseif (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF')) {
2704  $modele = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF');
2705  }
2706  }
2707 
2708  $modelpath = "core/modules/supplier_proposal/doc/";
2709 
2710  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2711  }
2712 
2713 
2722  public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2723  {
2724  $tables = array(
2725  'supplier_proposal'
2726  );
2727 
2728  return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2729  }
2730 
2739  public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
2740  {
2741  $tables = array(
2742  'supplier_proposaldet'
2743  );
2744 
2745  return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
2746  }
2747 
2748 
2756  public function getKanbanView($option = '', $arraydata = null)
2757  {
2758  global $langs;
2759 
2760  $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2761 
2762  $return = '<div class="box-flex-item box-flex-grow-zero">';
2763  $return .= '<div class="info-box info-box-sm">';
2764  $return .= '<span class="info-box-icon bg-infobox-action">';
2765  $return .= img_picto('', $this->picto);
2766  //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2767  $return .= '</span>';
2768  $return .= '<div class="info-box-content">';
2769  $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
2770  if ($selected >= 0) {
2771  $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2772  }
2773  if (property_exists($this, 'socid')) {
2774  $return .= '<span class="info-box-ref"> | '.$this->socid.'</span>';
2775  }
2776  if (property_exists($this, 'delivery_date')) {
2777  $return .= '<br><span class="opacitymedium">'.$langs->trans("DateEnd").'</span> : <span class="info-box-label">'.dol_print_date($this->delivery_date).'</span>';
2778  }
2779  if (property_exists($this, 'total_ttc')) {
2780  $return .= '<br><span class="opacitymedium" >'.$langs->trans("AmountHT").' : </span><span class="info-box-label amount">'.price($this->total_ttc).'</span>';
2781  }
2782  if (method_exists($this, 'getLibStatut')) {
2783  $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
2784  }
2785  $return .= '</div>';
2786  $return .= '</div>';
2787  $return .= '</div>';
2788  return $return;
2789  }
2790 }
2791 
2792 
2797 {
2801  public $db;
2802 
2806  public $error = '';
2807 
2811  public $element = 'supplier_proposaldet';
2812 
2816  public $table_element = 'supplier_proposaldet';
2817 
2818  public $oldline;
2819 
2823  public $id;
2824 
2828  public $fk_supplier_proposal;
2829 
2833  public $fk_parent_line;
2834 
2835  public $desc; // Description ligne
2836 
2840  public $fk_product; // Id produit predefini
2841 
2852  public $product_type = Product::TYPE_PRODUCT;
2853 
2854  public $qty;
2855  public $tva_tx;
2856  public $vat_src_code;
2857 
2858  public $subprice;
2859  public $remise_percent;
2860 
2864  public $fk_remise_except;
2865 
2866  public $rang = 0;
2867 
2871  public $fk_fournprice;
2872 
2873  public $pa_ht;
2874  public $marge_tx;
2875  public $marque_tx;
2876 
2880  public $special_code; // Tag for special lines (exclusive tags)
2881  // 1: frais de port
2882  // 2: ecotaxe
2883  // 3: option line (when qty = 0)
2884 
2885  public $info_bits = 0; // Liste d'options cumulables:
2886  // Bit 0: 0 si TVA normal - 1 if TVA NPR
2887  // Bit 1: 0 ligne normal - 1 if fixed reduction
2888 
2889  public $total_ht; // Total HT de la ligne toute quantite et incluant la remise ligne
2890  public $total_tva; // Total TVA de la ligne toute quantite et incluant la remise ligne
2891  public $total_ttc; // Total TTC de la ligne toute quantite et incluant la remise ligne
2892 
2893  public $date_start;
2894  public $date_end;
2895 
2896  // From llx_product
2901  public $ref;
2902 
2907  public $product_ref;
2908 
2913  public $libelle;
2914 
2919  public $product_label;
2920 
2925  public $label;
2926 
2931  public $product_desc;
2932 
2933  public $localtax1_tx; // Local tax 1
2934  public $localtax2_tx; // Local tax 2
2935  public $localtax1_type; // Local tax 1 type
2936  public $localtax2_type; // Local tax 2 type
2937  public $total_localtax1; // Line total local tax 1
2938  public $total_localtax2; // Line total local tax 2
2939 
2940  public $skip_update_total; // Skip update price total for special lines
2941 
2942  public $ref_fourn;
2943  public $ref_supplier;
2944 
2945  // Multicurrency
2949  public $fk_multicurrency;
2950 
2951  public $multicurrency_code;
2952  public $multicurrency_subprice;
2953  public $multicurrency_total_ht;
2954  public $multicurrency_total_tva;
2955  public $multicurrency_total_ttc;
2956 
2962  public function __construct($db)
2963  {
2964  $this->db = $db;
2965  }
2966 
2973  public function fetch($rowid)
2974  {
2975  $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,';
2976  $sql .= ' pd.date_start, pd.date_end,';
2977  $sql .= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice,';
2978  $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,';
2979  $sql .= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
2980  $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
2981  $sql .= ' pd.product_type, pd.ref_fourn as ref_produit_fourn,';
2982  $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';
2983  $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pd';
2984  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
2985  $sql .= ' WHERE pd.rowid = '.((int) $rowid);
2986 
2987  $result = $this->db->query($sql);
2988  if ($result) {
2989  $objp = $this->db->fetch_object($result);
2990 
2991  $this->id = $objp->rowid;
2992  $this->fk_supplier_proposal = $objp->fk_supplier_proposal;
2993  $this->fk_parent_line = $objp->fk_parent_line;
2994  $this->label = $objp->custom_label;
2995  $this->desc = $objp->description;
2996  $this->qty = $objp->qty;
2997  $this->subprice = $objp->subprice;
2998  $this->tva_tx = $objp->tva_tx;
2999  $this->remise_percent = $objp->remise_percent;
3000  $this->fk_remise_except = $objp->fk_remise_except;
3001  $this->fk_product = $objp->fk_product;
3002  $this->info_bits = $objp->info_bits;
3003  $this->date_start = $this->db->jdate($objp->date_start);
3004  $this->date_end = $this->db->jdate($objp->date_end);
3005 
3006  $this->total_ht = $objp->total_ht;
3007  $this->total_tva = $objp->total_tva;
3008  $this->total_ttc = $objp->total_ttc;
3009 
3010  $this->fk_fournprice = $objp->fk_fournprice;
3011 
3012  $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
3013  $this->pa_ht = $marginInfos[0];
3014  $this->marge_tx = $marginInfos[1];
3015  $this->marque_tx = $marginInfos[2];
3016 
3017  $this->special_code = $objp->special_code;
3018  $this->product_type = $objp->product_type;
3019  $this->rang = $objp->rang;
3020 
3021  $this->ref = $objp->product_ref; // deprecated
3022  $this->product_ref = $objp->product_ref;
3023  $this->libelle = $objp->product_label; // deprecated
3024  $this->product_label = $objp->product_label;
3025  $this->product_desc = $objp->product_desc;
3026 
3027  $this->ref_fourn = $objp->ref_produit_fourn;
3028 
3029  // Multicurrency
3030  $this->fk_multicurrency = $objp->fk_multicurrency;
3031  $this->multicurrency_code = $objp->multicurrency_code;
3032  $this->multicurrency_subprice = $objp->multicurrency_subprice;
3033  $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3034  $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3035  $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3036  $this->fk_unit = $objp->fk_unit;
3037 
3038  $this->db->free($result);
3039  return 1;
3040  } else {
3041  dol_print_error($this->db);
3042  return -1;
3043  }
3044  }
3045 
3052  public function insert($notrigger = 0)
3053  {
3054  global $conf, $langs, $user;
3055 
3056  $error = 0;
3057 
3058  dol_syslog(get_class($this)."::insert rang=".$this->rang);
3059 
3060  // Clean parameters
3061  if (empty($this->tva_tx)) {
3062  $this->tva_tx = 0;
3063  }
3064  if (empty($this->vat_src_code)) {
3065  $this->vat_src_code = '';
3066  }
3067  if (empty($this->localtax1_tx)) {
3068  $this->localtax1_tx = 0;
3069  }
3070  if (empty($this->localtax2_tx)) {
3071  $this->localtax2_tx = 0;
3072  }
3073  if (empty($this->localtax1_type)) {
3074  $this->localtax1_type = 0;
3075  }
3076  if (empty($this->localtax2_type)) {
3077  $this->localtax2_type = 0;
3078  }
3079  if (empty($this->total_localtax1)) {
3080  $this->total_localtax1 = 0;
3081  }
3082  if (empty($this->total_localtax2)) {
3083  $this->total_localtax2 = 0;
3084  }
3085  if (empty($this->rang)) {
3086  $this->rang = 0;
3087  }
3088  if (empty($this->remise_percent)) {
3089  $this->remise_percent = 0;
3090  }
3091  if (empty($this->info_bits)) {
3092  $this->info_bits = 0;
3093  }
3094  if (empty($this->special_code)) {
3095  $this->special_code = 0;
3096  }
3097  if (empty($this->fk_parent_line)) {
3098  $this->fk_parent_line = 0;
3099  }
3100  if (empty($this->fk_fournprice)) {
3101  $this->fk_fournprice = 0;
3102  }
3103  if (empty($this->fk_unit)) {
3104  $this->fk_unit = 0;
3105  }
3106  if (empty($this->subprice)) {
3107  $this->subprice = 0;
3108  }
3109 
3110  if (empty($this->pa_ht)) {
3111  $this->pa_ht = 0;
3112  }
3113 
3114  // if buy price not defined, define buyprice as configured in margin admin
3115  if ($this->pa_ht == 0) {
3116  $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3117  if ($result < 0) {
3118  return $result;
3119  } else {
3120  $this->pa_ht = $result;
3121  }
3122  }
3123 
3124  // Check parameters
3125  if ($this->product_type < 0) {
3126  return -1;
3127  }
3128 
3129  $this->db->begin();
3130 
3131  // Insert line into database
3132  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'supplier_proposaldet';
3133  $sql .= ' (fk_supplier_proposal, fk_parent_line, label, description, fk_product, product_type,';
3134  $sql .= ' date_start, date_end,';
3135  $sql .= ' fk_remise_except, qty, tva_tx, vat_src_code, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3136  $sql .= ' subprice, remise_percent, ';
3137  $sql .= ' info_bits, ';
3138  $sql .= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
3139  $sql .= ' ref_fourn,';
3140  $sql .= ' fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc, fk_unit)';
3141  $sql .= " VALUES (".$this->fk_supplier_proposal.",";
3142  $sql .= " ".($this->fk_parent_line > 0 ? ((int) $this->fk_parent_line) : "null").",";
3143  $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
3144  $sql .= " '".$this->db->escape($this->desc)."',";
3145  $sql .= " ".($this->fk_product ? ((int) $this->fk_product) : "null").",";
3146  $sql .= " '".$this->db->escape($this->product_type)."',";
3147  $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3148  $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3149  $sql .= " ".($this->fk_remise_except ? ((int) $this->fk_remise_except) : "null").",";
3150  $sql .= " ".price2num($this->qty, 'MS').",";
3151  $sql .= " ".price2num($this->tva_tx).",";
3152  $sql .= " '".$this->db->escape($this->vat_src_code)."',";
3153  $sql .= " ".price2num($this->localtax1_tx).",";
3154  $sql .= " ".price2num($this->localtax2_tx).",";
3155  $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3156  $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3157  $sql .= " ".price2num($this->subprice, 'MU') .",";
3158  $sql .= " ".((float) $this->remise_percent).",";
3159  $sql .= " ".(isset($this->info_bits) ? ((int) $this->info_bits) : "null").",";
3160  $sql .= " ".price2num($this->total_ht, 'MT').",";
3161  $sql .= " ".price2num($this->total_tva, 'MT').",";
3162  $sql .= " ".price2num($this->total_localtax1, 'MT').",";
3163  $sql .= " ".price2num($this->total_localtax2, 'MT').",";
3164  $sql .= " ".price2num($this->total_ttc, 'MT').",";
3165  $sql .= " ".(!empty($this->fk_fournprice) ? ((int) $this->fk_fournprice) : "null").",";
3166  $sql .= " ".(isset($this->pa_ht) ? price2num($this->pa_ht, 'MU') : "null").",";
3167  $sql .= ' '.((int) $this->special_code).',';
3168  $sql .= ' '.((int) $this->rang).',';
3169  $sql .= " '".$this->db->escape($this->ref_fourn)."'";
3170  $sql .= ", ".($this->fk_multicurrency > 0 ? ((int) $this->fk_multicurrency) : 'null');
3171  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3172  $sql .= ", ".price2num($this->multicurrency_subprice, 'CU');
3173  $sql .= ", ".price2num($this->multicurrency_total_ht, 'CT');
3174  $sql .= ", ".price2num($this->multicurrency_total_tva, 'CT');
3175  $sql .= ", ".price2num($this->multicurrency_total_ttc, 'CT');
3176  $sql .= ", ".($this->fk_unit ? ((int) $this->fk_unit) : 'null');
3177  $sql .= ')';
3178 
3179  dol_syslog(get_class($this).'::insert', LOG_DEBUG);
3180  $resql = $this->db->query($sql);
3181  if ($resql) {
3182  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'supplier_proposaldet');
3183 
3184  if (!$error) {
3185  $result = $this->insertExtraFields();
3186  if ($result < 0) {
3187  $error++;
3188  }
3189  }
3190 
3191  if (!$error && !$notrigger) {
3192  // Call trigger
3193  $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_INSERT', $user);
3194  if ($result < 0) {
3195  $this->db->rollback();
3196  return -1;
3197  }
3198  // End call triggers
3199  }
3200 
3201  $this->db->commit();
3202  return 1;
3203  } else {
3204  $this->error = $this->db->error()." sql=".$sql;
3205  $this->db->rollback();
3206  return -1;
3207  }
3208  }
3209 
3216  public function delete($user)
3217  {
3218  $error = 0;
3219 
3220  $this->db->begin();
3221 
3222  $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet";
3223  $sql .= " WHERE rowid = ".((int) $this->id);
3224 
3225  if ($this->db->query($sql)) {
3226  // Remove extrafields
3227  if (!$error) {
3228  $result = $this->deleteExtraFields();
3229  if ($result < 0) {
3230  $error++;
3231  dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3232  }
3233  }
3234 
3235  // Call trigger
3236  $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_DELETE', $user);
3237  if ($result < 0) {
3238  $this->db->rollback();
3239  return -1;
3240  }
3241  // End call triggers
3242 
3243  $this->db->commit();
3244 
3245  return 1;
3246  } else {
3247  $this->error = $this->db->error()." sql=".$sql;
3248  $this->db->rollback();
3249  return -1;
3250  }
3251  }
3252 
3259  public function update($notrigger = 0)
3260  {
3261  global $conf, $langs, $user;
3262 
3263  $error = 0;
3264 
3265  // Clean parameters
3266  if (empty($this->tva_tx)) {
3267  $this->tva_tx = 0;
3268  }
3269  if (empty($this->localtax1_tx)) {
3270  $this->localtax1_tx = 0;
3271  }
3272  if (empty($this->localtax2_tx)) {
3273  $this->localtax2_tx = 0;
3274  }
3275  if (empty($this->total_localtax1)) {
3276  $this->total_localtax1 = 0;
3277  }
3278  if (empty($this->total_localtax2)) {
3279  $this->total_localtax2 = 0;
3280  }
3281  if (empty($this->localtax1_type)) {
3282  $this->localtax1_type = 0;
3283  }
3284  if (empty($this->localtax2_type)) {
3285  $this->localtax2_type = 0;
3286  }
3287  if (empty($this->marque_tx)) {
3288  $this->marque_tx = 0;
3289  }
3290  if (empty($this->marge_tx)) {
3291  $this->marge_tx = 0;
3292  }
3293  if (empty($this->remise_percent)) {
3294  $this->remise_percent = 0;
3295  }
3296  if (empty($this->info_bits)) {
3297  $this->info_bits = 0;
3298  }
3299  if (empty($this->special_code)) {
3300  $this->special_code = 0;
3301  }
3302  if (empty($this->fk_parent_line)) {
3303  $this->fk_parent_line = 0;
3304  }
3305  if (empty($this->fk_fournprice)) {
3306  $this->fk_fournprice = 0;
3307  }
3308  if (empty($this->fk_unit)) {
3309  $this->fk_unit = 0;
3310  }
3311  if (empty($this->subprice)) {
3312  $this->subprice = 0;
3313  }
3314 
3315  if (empty($this->pa_ht)) {
3316  $this->pa_ht = 0;
3317  }
3318 
3319  // if buy price not defined, define buyprice as configured in margin admin
3320  if ($this->pa_ht == 0) {
3321  $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3322  if ($result < 0) {
3323  return $result;
3324  } else {
3325  $this->pa_ht = $result;
3326  }
3327  }
3328 
3329  $this->db->begin();
3330 
3331  // Mise a jour ligne en base
3332  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3333  $sql .= " description='".$this->db->escape($this->desc)."'";
3334  $sql .= " , label=".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
3335  $sql .= " , product_type=".((int) $this->product_type);
3336  $sql .= " , date_start=".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null");
3337  $sql .= " , date_end=".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null");
3338  $sql .= " , tva_tx='".price2num($this->tva_tx)."'";
3339  $sql .= " , localtax1_tx=".price2num($this->localtax1_tx);
3340  $sql .= " , localtax2_tx=".price2num($this->localtax2_tx);
3341  $sql .= " , localtax1_type='".$this->db->escape($this->localtax1_type)."'";
3342  $sql .= " , localtax2_type='".$this->db->escape($this->localtax2_type)."'";
3343  $sql .= " , qty='".price2num($this->qty)."'";
3344  $sql .= " , subprice=".price2num($this->subprice);
3345  $sql .= " , remise_percent=".price2num($this->remise_percent);
3346  $sql .= " , info_bits='".$this->db->escape($this->info_bits)."'";
3347  if (empty($this->skip_update_total)) {
3348  $sql .= " , total_ht=".price2num($this->total_ht);
3349  $sql .= " , total_tva=".price2num($this->total_tva);
3350  $sql .= " , total_ttc=".price2num($this->total_ttc);
3351  $sql .= " , total_localtax1=".price2num($this->total_localtax1);
3352  $sql .= " , total_localtax2=".price2num($this->total_localtax2);
3353  }
3354  $sql .= " , fk_product_fournisseur_price=".(!empty($this->fk_fournprice) ? "'".$this->db->escape($this->fk_fournprice)."'" : "null");
3355  $sql .= " , buy_price_ht=".price2num($this->pa_ht);
3356  $sql .= " , special_code=".((int) $this->special_code);
3357  $sql .= " , fk_parent_line=".($this->fk_parent_line > 0 ? $this->fk_parent_line : "null");
3358  if (!empty($this->rang)) {
3359  $sql .= ", rang=".((int) $this->rang);
3360  }
3361  $sql .= " , ref_fourn=".(!empty($this->ref_fourn) ? "'".$this->db->escape($this->ref_fourn)."'" : "null");
3362  $sql .= " , fk_unit=".($this->fk_unit ? $this->fk_unit : 'null');
3363 
3364  // Multicurrency
3365  $sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice);
3366  $sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
3367  $sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
3368  $sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
3369 
3370  $sql .= " WHERE rowid = ".((int) $this->id);
3371 
3372  dol_syslog(get_class($this)."::update", LOG_DEBUG);
3373  $resql = $this->db->query($sql);
3374  if ($resql) {
3375  if (!$error) {
3376  $result = $this->insertExtraFields();
3377  if ($result < 0) {
3378  $error++;
3379  }
3380  }
3381 
3382  if (!$error && !$notrigger) {
3383  // Call trigger
3384  $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_MODIFY', $user);
3385  if ($result < 0) {
3386  $this->db->rollback();
3387  return -1;
3388  }
3389  // End call triggers
3390  }
3391 
3392  $this->db->commit();
3393  return 1;
3394  } else {
3395  $this->error = $this->db->error();
3396  $this->db->rollback();
3397  return -2;
3398  }
3399  }
3400 
3401  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3408  public function update_total()
3409  {
3410  // phpcs:enable
3411  $this->db->begin();
3412 
3413  // Mise a jour ligne en base
3414  $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3415  $sql .= " total_ht=".price2num($this->total_ht, 'MT');
3416  $sql .= ",total_tva=".price2num($this->total_tva, 'MT');
3417  $sql .= ",total_ttc=".price2num($this->total_ttc, 'MT');
3418  $sql .= " WHERE rowid = ".((int) $this->id);
3419 
3420  dol_syslog("SupplierProposalLine::update_total", LOG_DEBUG);
3421 
3422  $resql = $this->db->query($sql);
3423  if ($resql) {
3424  $this->db->commit();
3425  return 1;
3426  } else {
3427  $this->error = $this->db->error();
3428  $this->db->rollback();
3429  return -2;
3430  }
3431  }
3432 }
print $langs trans("AuditedSecurityEvents").'</strong >< span class="opacitymedium"></span >< br > status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition: security.php:607
$object ref
Definition: info.php:79
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.
update_price($exclspec=0, $roundingadjust='auto', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
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.
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid=0, $f_user=null, $notrigger=0)
Delete all links between an object $this.
setErrorsFromObject($object)
setErrorsFromObject
static isExistingObject($element, $id, $ref='', $ref_ext='')
Check if an object id or ref exists If you don't need or want to instantiate the object and just need...
updateRangOfLine($rowid, $rang)
Update position of line (rang)
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)
Update ProductFournisseur.
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=[], $ref_supplier='', $fk_unit=0, $pu_ht_devise=0)
Update a proposal line.
cloture($user, $status, $note)
Close the askprice.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template module.
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=[], $ref_supplier='', $fk_unit=0, $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 parameters sont deja cense e...
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.
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.
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.
__construct($db, $socid=0, $supplier_proposalid=0)
Constructor.
LibStatut($status, $mode=1)
Return label of a status (draft, validated, ...)
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.
reopen($user, $statut, $note='', $notrigger=0)
Set an overall discount on the proposal.
loadStateBoard()
Load indicator this->nb of global stats widget.
deleteLine($lineid)
Delete detail line.
const STATUS_VALIDATED
Validated status.
createPriceFournisseur($product, $user)
Create ProductFournisseur.
getLibStatut($mode=0)
Return label of status of proposal (draft, validated, ...)
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 Constructor.
Class to manage translations.
Class to manage Dolibarr users.
Definition: user.class.php:50
trait CommonIncoterm
Superclass for incoterm classes.
if(isModEnabled('invoice') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&!getDolGlobalString('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') && $user->hasRight('tax', 'charges', 'lire')) if(isModEnabled('invoice') &&isModEnabled('order') && $user->hasRight("commande", "lire") &&!getDolGlobalString('WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER')) $sql
Social contributions to pay.
Definition: index.php:745
print *****$script_file(".$version.") pid c cd cd cd description as p label as s rowid
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
Definition: files.lib.php:1609
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:1458
dol_dir_list($utf8_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:63
dol_delete_preview($object)
Delete all preview files linked to object instance.
Definition: files.lib.php:1661
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
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_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
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.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
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...
get_localtax($vatrate, $local, $thirdparty_buyer=null, $thirdparty_seller=null, $vatnpr=0)
Return localtax rate for a particular vat, when selling a product with vat $vatrate,...
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($pv_ht, $remise_percent, $tva_tx, $localtax1_tx, $localtax2_tx, $fk_pa, $pa_ht)
Return an array with margins information of a line.
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:88