dolibarr  16.0.1
propal.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-2017 Philippe Grand <philippe.grand@atoo-net.com>
11  * Copyright (C) 2012-2014 Christophe Battarel <christophe.battarel@altairis.fr>
12  * Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
13  * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
14  * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
15  * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
16  * Copyright (C) 2018-2021 Frédéric France <frederic.france@netlogic.fr>
17  * Copyright (C) 2018 Ferran Marcet <fmarcet@2byte.es>
18  * Copyright (C) 2022 ATM Consulting <contact@atm-consulting.fr>
19  * Copyright (C) 2022 OpenDSI <support@open-dsi.fr>
20  * Copyright (C) 2022 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
21  *
22  * This program is free software; you can redistribute it and/or modify
23  * it under the terms of the GNU General Public License as published by
24  * the Free Software Foundation; either version 3 of the License, or
25  * (at your option) any later version.
26  *
27  * This program is distributed in the hope that it will be useful,
28  * but WITHOUT ANY WARRANTY; without even the implied warranty of
29  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30  * GNU General Public License for more details.
31  *
32  * You should have received a copy of the GNU General Public License
33  * along with this program. If not, see <https://www.gnu.org/licenses/>.
34  */
35 
41 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
42 require_once DOL_DOCUMENT_ROOT."/core/class/commonobjectline.class.php";
43 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
44 require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
45 require_once DOL_DOCUMENT_ROOT.'/margin/lib/margins.lib.php';
46 require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
47 require_once DOL_DOCUMENT_ROOT.'/core/class/commonincoterm.class.php';
48 
52 class Propal extends CommonObject
53 {
54  use CommonIncoterm;
55 
59  public $element = 'propal';
60 
64  public $table_element = 'propal';
65 
69  public $table_element_line = 'propaldet';
70 
74  public $fk_element = 'fk_propal';
75 
79  public $picto = 'propal';
80 
85  public $ismultientitymanaged = 1;
86 
91  public $restrictiononfksoc = 1;
92 
96  protected $table_ref_field = 'ref';
97 
102  public $socid;
103 
108  public $contactid;
109  public $author;
110 
115  public $ref_client;
116 
122  public $statut;
123 
129  public $status;
130 
135  public $datec;
136 
140  public $date_creation;
141 
146  public $datev;
147 
151  public $date_validation;
152 
156  public $date_signature;
157 
161  public $user_signature;
162 
166  public $date;
167 
172  public $datep;
173 
178  public $date_livraison; // deprecated; Use delivery_date instead.
179 
183  public $delivery_date; // Date expected of shipment (date starting shipment, not the reception that occurs some days after)
184 
185 
186  public $fin_validite;
187 
188  public $user_author_id;
189  public $user_valid_id;
190  public $user_close_id;
191 
196  public $price;
201  public $tva;
206  public $total;
207 
208  public $cond_reglement_code;
209  public $deposit_percent;
210  public $mode_reglement_code;
211  public $remise_percent;
212 
216  public $remise;
221 
226  public $fk_address;
227 
228  public $address_type;
229  public $address;
230 
231  public $availability_id;
232  public $availability_code;
233 
234  public $duree_validite;
235 
236  public $demand_reason_id;
237  public $demand_reason_code;
238 
239  public $warehouse_id;
240 
241  public $extraparams = array();
242 
246  public $lines = array();
247  public $line;
248 
249  public $labelStatus = array();
250  public $labelStatusShort = array();
251 
252  // Multicurrency
256  public $fk_multicurrency;
257 
258  public $multicurrency_code;
259  public $multicurrency_tx;
260  public $multicurrency_total_ht;
261  public $multicurrency_total_tva;
262  public $multicurrency_total_ttc;
263 
264 
289  // BEGIN MODULEBUILDER PROPERTIES
293  public $fields = array(
294  'rowid' =>array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>10),
295  'entity' =>array('type'=>'integer', 'label'=>'Entity', 'default'=>1, 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>15, 'index'=>1),
296  'ref' =>array('type'=>'varchar(30)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'showoncombobox'=>1, 'position'=>20),
297  'ref_client' =>array('type'=>'varchar(255)', 'label'=>'RefCustomer', 'enabled'=>1, 'visible'=>-1, 'position'=>22),
298  'ref_ext' =>array('type'=>'varchar(255)', 'label'=>'RefExt', 'enabled'=>1, 'visible'=>0, 'position'=>40),
299  'fk_soc' =>array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'enabled'=>'$conf->societe->enabled', 'visible'=>-1, 'position'=>23),
300  'fk_projet' =>array('type'=>'integer:Project:projet/class/project.class.php:1:fk_statut=1', 'label'=>'Fk projet', 'enabled'=>'$conf->project->enabled', 'visible'=>-1, 'position'=>24),
301  'tms' =>array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>25),
302  'datec' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-1, 'position'=>55),
303  'datep' =>array('type'=>'date', 'label'=>'Date', 'enabled'=>1, 'visible'=>-1, 'position'=>60),
304  'fin_validite' =>array('type'=>'datetime', 'label'=>'DateEnd', 'enabled'=>1, 'visible'=>-1, 'position'=>65),
305  'date_valid' =>array('type'=>'datetime', 'label'=>'DateValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>70),
306  'date_cloture' =>array('type'=>'datetime', 'label'=>'DateClosing', 'enabled'=>1, 'visible'=>-1, 'position'=>75),
307  'fk_user_author' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'Fk user author', 'enabled'=>1, 'visible'=>-1, 'position'=>80),
308  'fk_user_modif' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserModif', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'position'=>85),
309  'fk_user_valid' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>90),
310  'fk_user_cloture' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'Fk user cloture', 'enabled'=>1, 'visible'=>-1, 'position'=>95),
311  'price' =>array('type'=>'double', 'label'=>'Price', 'enabled'=>1, 'visible'=>-1, 'position'=>105),
312  'remise_percent' =>array('type'=>'double', 'label'=>'RelativeDiscount', 'enabled'=>1, 'visible'=>-1, 'position'=>110),
313  //'remise_absolue' =>array('type'=>'double', 'label'=>'CustomerRelativeDiscount', 'enabled'=>1, 'visible'=>-1, 'position'=>115),
314  //'remise' =>array('type'=>'double', 'label'=>'Remise', 'enabled'=>1, 'visible'=>-1, 'position'=>120),
315  'total_ht' =>array('type'=>'double(24,8)', 'label'=>'TotalHT', 'enabled'=>1, 'visible'=>-1, 'position'=>125, 'isameasure'=>1),
316  'total_tva' =>array('type'=>'double(24,8)', 'label'=>'VAT', 'enabled'=>1, 'visible'=>-1, 'position'=>130, 'isameasure'=>1),
317  'localtax1' =>array('type'=>'double(24,8)', 'label'=>'LocalTax1', 'enabled'=>1, 'visible'=>-1, 'position'=>135, 'isameasure'=>1),
318  'localtax2' =>array('type'=>'double(24,8)', 'label'=>'LocalTax2', 'enabled'=>1, 'visible'=>-1, 'position'=>140, 'isameasure'=>1),
319  'total_ttc' =>array('type'=>'double(24,8)', 'label'=>'TotalTTC', 'enabled'=>1, 'visible'=>-1, 'position'=>145, 'isameasure'=>1),
320  'fk_account' =>array('type'=>'integer', 'label'=>'BankAccount', 'enabled'=>'$conf->banque->enabled', 'visible'=>-1, 'position'=>150),
321  'fk_currency' =>array('type'=>'varchar(3)', 'label'=>'Currency', 'enabled'=>1, 'visible'=>-1, 'position'=>155),
322  'fk_cond_reglement' =>array('type'=>'integer', 'label'=>'PaymentTerm', 'enabled'=>1, 'visible'=>-1, 'position'=>160),
323  'deposit_percent' =>array('type'=>'varchar(63)', 'label'=>'DepositPercent', 'enabled'=>1, 'visible'=>-1, 'position'=>161),
324  'fk_mode_reglement' =>array('type'=>'integer', 'label'=>'PaymentMode', 'enabled'=>1, 'visible'=>-1, 'position'=>165),
325  'note_private' =>array('type'=>'text', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>0, 'position'=>170),
326  'note_public' =>array('type'=>'text', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>0, 'position'=>175),
327  'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'PDFTemplate', 'enabled'=>1, 'visible'=>0, 'position'=>180),
328  'date_livraison' =>array('type'=>'date', 'label'=>'DateDeliveryPlanned', 'enabled'=>1, 'visible'=>-1, 'position'=>185),
329  'fk_shipping_method' =>array('type'=>'integer', 'label'=>'ShippingMethod', 'enabled'=>1, 'visible'=>-1, 'position'=>190),
330  'fk_warehouse' =>array('type'=>'integer:Entrepot:product/stock/class/entrepot.class.php', 'label'=>'Fk warehouse', 'enabled'=>'$conf->stock->enabled', 'visible'=>-1, 'position'=>191),
331  'fk_availability' =>array('type'=>'integer', 'label'=>'Availability', 'enabled'=>1, 'visible'=>-1, 'position'=>195),
332  'fk_delivery_address' =>array('type'=>'integer', 'label'=>'DeliveryAddress', 'enabled'=>1, 'visible'=>0, 'position'=>200), // deprecated
333  'fk_input_reason' =>array('type'=>'integer', 'label'=>'InputReason', 'enabled'=>1, 'visible'=>-1, 'position'=>205),
334  'extraparams' =>array('type'=>'varchar(255)', 'label'=>'Extraparams', 'enabled'=>1, 'visible'=>-1, 'position'=>215),
335  'fk_incoterms' =>array('type'=>'integer', 'label'=>'IncotermCode', 'enabled'=>'$conf->incoterm->enabled', 'visible'=>-1, 'position'=>220),
336  'location_incoterms' =>array('type'=>'varchar(255)', 'label'=>'IncotermLabel', 'enabled'=>'$conf->incoterm->enabled', 'visible'=>-1, 'position'=>225),
337  'fk_multicurrency' =>array('type'=>'integer', 'label'=>'MulticurrencyID', 'enabled'=>1, 'visible'=>-1, 'position'=>230),
338  'multicurrency_code' =>array('type'=>'varchar(255)', 'label'=>'MulticurrencyCurrency', 'enabled'=>'$conf->multicurrency->enabled', 'visible'=>-1, 'position'=>235),
339  'multicurrency_tx' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyRate', 'enabled'=>'$conf->multicurrency->enabled', 'visible'=>-1, 'position'=>240, 'isameasure'=>1),
340  'multicurrency_total_ht' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountHT', 'enabled'=>'$conf->multicurrency->enabled', 'visible'=>-1, 'position'=>245, 'isameasure'=>1),
341  'multicurrency_total_tva' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountVAT', 'enabled'=>'$conf->multicurrency->enabled', 'visible'=>-1, 'position'=>250, 'isameasure'=>1),
342  'multicurrency_total_ttc' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountTTC', 'enabled'=>'$conf->multicurrency->enabled', 'visible'=>-1, 'position'=>255, 'isameasure'=>1),
343  'last_main_doc' =>array('type'=>'varchar(255)', 'label'=>'LastMainDoc', 'enabled'=>1, 'visible'=>-1, 'position'=>260),
344  'fk_statut' =>array('type'=>'smallint(6)', 'label'=>'Status', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>500),
345  'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>900),
346  );
347  // END MODULEBUILDER PROPERTIES
348 
352  const STATUS_DRAFT = 0;
356  const STATUS_VALIDATED = 1;
360  const STATUS_SIGNED = 2;
364  const STATUS_NOTSIGNED = 3;
368  const STATUS_BILLED = 4; // Todo rename into STATUS_CLOSE ?
369 
370 
378  public function __construct($db, $socid = 0, $propalid = 0)
379  {
380  global $conf, $langs;
381 
382  $this->db = $db;
383 
384  $this->socid = $socid;
385  $this->id = $propalid;
386 
387  $this->duree_validite = getDolGlobalInt('PROPALE_VALIDITY_DURATION', 0);
388  }
389 
390 
391  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
403  public function add_product($idproduct, $qty, $remise_percent = 0)
404  {
405  // phpcs:enable
406  global $conf, $mysoc;
407 
408  if (!$qty) {
409  $qty = 1;
410  }
411 
412  dol_syslog(get_class($this)."::add_product $idproduct, $qty, $remise_percent");
413  if ($idproduct > 0) {
414  $prod = new Product($this->db);
415  $prod->fetch($idproduct);
416 
417  $productdesc = $prod->description;
418 
419  $tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
420  $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
421  if (empty($tva_tx)) {
422  $tva_npr = 0;
423  }
424  $vat_src_code = ''; // May be defined into tva_tx
425 
426  $localtax1_tx = get_localtax($tva_tx, 1, $mysoc, $this->thirdparty, $tva_npr);
427  $localtax2_tx = get_localtax($tva_tx, 2, $mysoc, $this->thirdparty, $tva_npr);
428 
429  // multiprices
430  if ($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level) {
431  $price = $prod->multiprices[$this->thirdparty->price_level];
432  } else {
433  $price = $prod->price;
434  }
435 
436  $line = new PropaleLigne($this->db);
437 
438  $line->fk_product = $idproduct;
439  $line->desc = $productdesc;
440  $line->qty = $qty;
441  $line->subprice = $price;
442  $line->remise_percent = $remise_percent;
443  $line->vat_src_code = $vat_src_code;
444  $line->tva_tx = $tva_tx;
445  $line->fk_unit = $prod->fk_unit;
446  if ($tva_npr) {
447  $line->info_bits = 1;
448  }
449 
450  $this->lines[] = $line;
451  }
452  }
453 
454  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
461  public function insert_discount($idremise)
462  {
463  // phpcs:enable
464  global $langs;
465 
466  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
467  include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
468 
469  $this->db->begin();
470 
471  $remise = new DiscountAbsolute($this->db);
472  $result = $remise->fetch($idremise);
473 
474  if ($result > 0) {
475  if ($remise->fk_facture) { // Protection against multiple submission
476  $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
477  $this->db->rollback();
478  return -5;
479  }
480 
481  $line = new PropaleLigne($this->db);
482 
483  $this->line->context = $this->context;
484 
485  $line->fk_propal = $this->id;
486  $line->fk_remise_except = $remise->id;
487  $line->desc = $remise->description; // Description ligne
488  $line->vat_src_code = $remise->vat_src_code;
489  $line->tva_tx = $remise->tva_tx;
490  $line->subprice = -$remise->amount_ht;
491  $line->fk_product = 0; // Id produit predefined
492  $line->qty = 1;
493  $line->remise_percent = 0;
494  $line->rang = -1;
495  $line->info_bits = 2;
496 
497  // TODO deprecated
498  $line->price = -$remise->amount_ht;
499 
500  $line->total_ht = -$remise->amount_ht;
501  $line->total_tva = -$remise->amount_tva;
502  $line->total_ttc = -$remise->amount_ttc;
503 
504  $result = $line->insert();
505  if ($result > 0) {
506  $result = $this->update_price(1);
507  if ($result > 0) {
508  $this->db->commit();
509  return 1;
510  } else {
511  $this->db->rollback();
512  return -1;
513  }
514  } else {
515  $this->error = $line->error;
516  $this->errors = $line->errors;
517  $this->db->rollback();
518  return -2;
519  }
520  } else {
521  $this->db->rollback();
522  return -2;
523  }
524  }
525 
563  public function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1 = 0.0, $txlocaltax2 = 0.0, $fk_product = 0, $remise_percent = 0.0, $price_base_type = 'HT', $pu_ttc = 0.0, $info_bits = 0, $type = 0, $rang = -1, $special_code = 0, $fk_parent_line = 0, $fk_fournprice = 0, $pa_ht = 0, $label = '', $date_start = '', $date_end = '', $array_options = 0, $fk_unit = null, $origin = '', $origin_id = 0, $pu_ht_devise = 0, $fk_remise_except = 0, $noupdateafterinsertline = 0)
564  {
565  global $mysoc, $conf, $langs;
566 
567  dol_syslog(get_class($this)."::addline propalid=$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, fk_remise_except=".$fk_remise_except);
568 
569  if ($this->statut == self::STATUS_DRAFT) {
570  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
571 
572  // Clean parameters
573  if (empty($remise_percent)) {
574  $remise_percent = 0;
575  }
576  if (empty($qty)) {
577  $qty = 0;
578  }
579  if (empty($info_bits)) {
580  $info_bits = 0;
581  }
582  if (empty($rang)) {
583  $rang = 0;
584  }
585  if (empty($fk_parent_line) || $fk_parent_line < 0) {
586  $fk_parent_line = 0;
587  }
588 
589  $remise_percent = price2num($remise_percent);
590  $qty = price2num($qty);
591  $pu_ht = price2num($pu_ht);
592  $pu_ht_devise = price2num($pu_ht_devise);
593  $pu_ttc = price2num($pu_ttc);
594  if (!preg_match('/\((.*)\)/', $txtva)) {
595  $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
596  }
597  $txlocaltax1 = price2num($txlocaltax1);
598  $txlocaltax2 = price2num($txlocaltax2);
599  $pa_ht = price2num($pa_ht);
600  if ($price_base_type == 'HT') {
601  $pu = $pu_ht;
602  } else {
603  $pu = $pu_ttc;
604  }
605 
606  // Check parameters
607  if ($type < 0) {
608  return -1;
609  }
610 
611  if ($date_start && $date_end && $date_start > $date_end) {
612  $langs->load("errors");
613  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
614  return -1;
615  }
616 
617  $this->db->begin();
618 
619  $product_type = $type;
620  if (!empty($fk_product) && $fk_product > 0) {
621  $product = new Product($this->db);
622  $result = $product->fetch($fk_product);
623  $product_type = $product->type;
624 
625  if (!empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_PROPOSAL) && $product_type == 0 && $product->stock_reel < $qty) {
626  $langs->load("errors");
627  $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnProposal', $product->ref);
628  $this->db->rollback();
629  return -3;
630  }
631  }
632 
633  // Calcul du total TTC et de la TVA pour la ligne a partir de
634  // qty, pu, remise_percent et txtva
635  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
636  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
637 
638  $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
639 
640  // Clean vat code
641  $reg = array();
642  $vat_src_code = '';
643  $reg = array();
644  if (preg_match('/\((.*)\)/', $txtva, $reg)) {
645  $vat_src_code = $reg[1];
646  $txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
647  }
648 
649  $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $product_type, $mysoc, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
650 
651  $total_ht = $tabprice[0];
652  $total_tva = $tabprice[1];
653  $total_ttc = $tabprice[2];
654  $total_localtax1 = $tabprice[9];
655  $total_localtax2 = $tabprice[10];
656  $pu_ht = $tabprice[3];
657  $pu_tva = $tabprice[4];
658  $pu_ttc = $tabprice[5];
659 
660  // MultiCurrency
661  $multicurrency_total_ht = $tabprice[16];
662  $multicurrency_total_tva = $tabprice[17];
663  $multicurrency_total_ttc = $tabprice[18];
664  $pu_ht_devise = $tabprice[19];
665 
666  // Rang to use
667  $ranktouse = $rang;
668  if ($ranktouse == -1) {
669  $rangmax = $this->line_max($fk_parent_line);
670  $ranktouse = $rangmax + 1;
671  }
672 
673  // TODO A virer
674  // Anciens indicateurs: $price, $remise (a ne plus utiliser)
675  $price = $pu;
676  $remise = 0;
677  if ($remise_percent > 0) {
678  $remise = round(($pu * $remise_percent / 100), 2);
679  $price = $pu - $remise;
680  }
681 
682  // Insert line
683  $this->line = new PropaleLigne($this->db);
684 
685  $this->line->context = $this->context;
686 
687  $this->line->fk_propal = $this->id;
688  $this->line->label = $label;
689  $this->line->desc = $desc;
690  $this->line->qty = $qty;
691 
692  $this->line->vat_src_code = $vat_src_code;
693  $this->line->tva_tx = $txtva;
694  $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
695  $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
696  $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
697  $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
698  $this->line->fk_product = $fk_product;
699  $this->line->product_type = $type;
700  $this->line->fk_remise_except = $fk_remise_except;
701  $this->line->remise_percent = $remise_percent;
702  $this->line->subprice = $pu_ht;
703  $this->line->rang = $ranktouse;
704  $this->line->info_bits = $info_bits;
705  $this->line->total_ht = $total_ht;
706  $this->line->total_tva = $total_tva;
707  $this->line->total_localtax1 = $total_localtax1;
708  $this->line->total_localtax2 = $total_localtax2;
709  $this->line->total_ttc = $total_ttc;
710  $this->line->special_code = $special_code;
711  $this->line->fk_parent_line = $fk_parent_line;
712  $this->line->fk_unit = $fk_unit;
713 
714  $this->line->date_start = $date_start;
715  $this->line->date_end = $date_end;
716 
717  $this->line->fk_fournprice = $fk_fournprice;
718  $this->line->pa_ht = $pa_ht;
719 
720  $this->line->origin_id = $origin_id;
721  $this->line->origin = $origin;
722 
723  // Multicurrency
724  $this->line->fk_multicurrency = $this->fk_multicurrency;
725  $this->line->multicurrency_code = $this->multicurrency_code;
726  $this->line->multicurrency_subprice = $pu_ht_devise;
727  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
728  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
729  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
730 
731  // Mise en option de la ligne
732  if (empty($qty) && empty($special_code)) {
733  $this->line->special_code = 3;
734  }
735 
736  // TODO deprecated
737  $this->line->price = $price;
738 
739  if (is_array($array_options) && count($array_options) > 0) {
740  $this->line->array_options = $array_options;
741  }
742 
743  $result = $this->line->insert();
744  if ($result > 0) {
745  // Reorder if child line
746  if (!empty($fk_parent_line)) {
747  $this->line_order(true, 'DESC');
748  } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) { // Update all rank of all other lines
749  $linecount = count($this->lines);
750  for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
751  $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
752  }
753  }
754 
755  // Mise a jour informations denormalisees au niveau de la propale meme
756  if (empty($noupdateafterinsertline)) {
757  $result = $this->update_price(1, 'auto', 0, $mysoc); // This method is designed to add line from user input so total calculation must be done using 'auto' mode.
758  }
759 
760  if ($result > 0) {
761  $this->db->commit();
762  return $this->line->id;
763  } else {
764  $this->error = $this->db->error();
765  $this->db->rollback();
766  return -1;
767  }
768  } else {
769  $this->error = $this->line->error;
770  $this->errors = $this->line->errors;
771  $this->db->rollback();
772  return -2;
773  }
774  } else {
775  dol_syslog(get_class($this)."::addline status of proposal must be Draft to allow use of ->addline()", LOG_ERR);
776  return -3;
777  }
778  }
779 
780 
810  public function updateline($rowid, $pu, $qty, $remise_percent, $txtva, $txlocaltax1 = 0.0, $txlocaltax2 = 0.0, $desc = '', $price_base_type = 'HT', $info_bits = 0, $special_code = 0, $fk_parent_line = 0, $skip_update_total = 0, $fk_fournprice = 0, $pa_ht = 0, $label = '', $type = 0, $date_start = '', $date_end = '', $array_options = 0, $fk_unit = null, $pu_ht_devise = 0, $notrigger = 0, $rang = 0)
811  {
812  global $mysoc, $langs;
813 
814  dol_syslog(get_class($this)."::updateLine rowid=$rowid, pu=$pu, qty=$qty, remise_percent=$remise_percent,
815  txtva=$txtva, desc=$desc, price_base_type=$price_base_type, info_bits=$info_bits, special_code=$special_code, fk_parent_line=$fk_parent_line, pa_ht=$pa_ht, type=$type, date_start=$date_start, date_end=$date_end");
816  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
817 
818  // Clean parameters
819  $remise_percent = price2num($remise_percent);
820  $qty = price2num($qty);
821  $pu = price2num($pu);
822  $pu_ht_devise = price2num($pu_ht_devise);
823  if (!preg_match('/\((.*)\)/', $txtva)) {
824  $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
825  }
826  $txlocaltax1 = price2num($txlocaltax1);
827  $txlocaltax2 = price2num($txlocaltax2);
828  $pa_ht = price2num($pa_ht);
829  if (empty($qty) && empty($special_code)) {
830  $special_code = 3; // Set option tag
831  }
832  if (!empty($qty) && $special_code == 3) {
833  $special_code = 0; // Remove option tag
834  }
835  if (empty($type)) {
836  $type = 0;
837  }
838 
839  if ($date_start && $date_end && $date_start > $date_end) {
840  $langs->load("errors");
841  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
842  return -1;
843  }
844 
845  if ($this->statut == self::STATUS_DRAFT) {
846  $this->db->begin();
847 
848  // Calcul du total TTC et de la TVA pour la ligne a partir de
849  // qty, pu, remise_percent et txtva
850  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
851  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
852 
853  $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
854 
855  // Clean vat code
856  $reg = array();
857  $vat_src_code = '';
858  if (preg_match('/\((.*)\)/', $txtva, $reg)) {
859  $vat_src_code = $reg[1];
860  $txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
861  }
862 
863  $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $mysoc, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
864  $total_ht = $tabprice[0];
865  $total_tva = $tabprice[1];
866  $total_ttc = $tabprice[2];
867  $total_localtax1 = $tabprice[9];
868  $total_localtax2 = $tabprice[10];
869  $pu_ht = $tabprice[3];
870  $pu_tva = $tabprice[4];
871  $pu_ttc = $tabprice[5];
872 
873  // MultiCurrency
874  $multicurrency_total_ht = $tabprice[16];
875  $multicurrency_total_tva = $tabprice[17];
876  $multicurrency_total_ttc = $tabprice[18];
877  $pu_ht_devise = $tabprice[19];
878 
879  // Anciens indicateurs: $price, $remise (a ne plus utiliser)
880  $price = $pu;
881  $remise = 0;
882  if ($remise_percent > 0) {
883  $remise = round(($pu * $remise_percent / 100), 2);
884  $price = $pu - $remise;
885  }
886 
887  //Fetch current line from the database and then clone the object and set it in $oldline property
888  $line = new PropaleLigne($this->db);
889  $line->fetch($rowid);
890 
891  $staticline = clone $line;
892 
893  $line->oldline = $staticline;
894  $this->line = $line;
895  $this->line->context = $this->context;
896  $this->line->rang = $rang;
897 
898  // Reorder if fk_parent_line change
899  if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
900  $rangmax = $this->line_max($fk_parent_line);
901  $this->line->rang = $rangmax + 1;
902  }
903 
904  $this->line->id = $rowid;
905  $this->line->label = $label;
906  $this->line->desc = $desc;
907  $this->line->qty = $qty;
908  $this->line->product_type = $type;
909  $this->line->vat_src_code = $vat_src_code;
910  $this->line->tva_tx = $txtva;
911  $this->line->localtax1_tx = $txlocaltax1;
912  $this->line->localtax2_tx = $txlocaltax2;
913  $this->line->localtax1_type = $localtaxes_type[0];
914  $this->line->localtax2_type = $localtaxes_type[2];
915  $this->line->remise_percent = $remise_percent;
916  $this->line->subprice = $pu_ht;
917  $this->line->info_bits = $info_bits;
918 
919  $this->line->total_ht = $total_ht;
920  $this->line->total_tva = $total_tva;
921  $this->line->total_localtax1 = $total_localtax1;
922  $this->line->total_localtax2 = $total_localtax2;
923  $this->line->total_ttc = $total_ttc;
924  $this->line->special_code = $special_code;
925  $this->line->fk_parent_line = $fk_parent_line;
926  $this->line->skip_update_total = $skip_update_total;
927  $this->line->fk_unit = $fk_unit;
928 
929  $this->line->fk_fournprice = $fk_fournprice;
930  $this->line->pa_ht = $pa_ht;
931 
932  $this->line->date_start = $date_start;
933  $this->line->date_end = $date_end;
934 
935  // TODO deprecated
936  $this->line->price = $price;
937 
938  if (is_array($array_options) && count($array_options) > 0) {
939  // We replace values in this->line->array_options only for entries defined into $array_options
940  foreach ($array_options as $key => $value) {
941  $this->line->array_options[$key] = $array_options[$key];
942  }
943  }
944 
945  // Multicurrency
946  $this->line->multicurrency_subprice = $pu_ht_devise;
947  $this->line->multicurrency_total_ht = $multicurrency_total_ht;
948  $this->line->multicurrency_total_tva = $multicurrency_total_tva;
949  $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
950 
951  $result = $this->line->update($notrigger);
952  if ($result > 0) {
953  // Reorder if child line
954  if (!empty($fk_parent_line)) {
955  $this->line_order(true, 'DESC');
956  }
957 
958  $this->update_price(1);
959 
960  $this->fk_propal = $this->id;
961  $this->rowid = $rowid;
962 
963  $this->db->commit();
964  return $result;
965  } else {
966  $this->error = $this->line->error;
967  $this->errors = $this->line->errors;
968  $this->db->rollback();
969  return -1;
970  }
971  } else {
972  dol_syslog(get_class($this)."::updateline Erreur -2 Propal en mode incompatible pour cette action");
973  return -2;
974  }
975  }
976 
977 
984  public function deleteline($lineid)
985  {
986  global $user;
987 
988  if ($this->statut == self::STATUS_DRAFT) {
989  $this->db->begin();
990 
991  $line = new PropaleLigne($this->db);
992 
993  // For triggers
994  $line->fetch($lineid);
995 
996  if ($line->delete($user) > 0) {
997  $this->update_price(1);
998 
999  $this->db->commit();
1000  return 1;
1001  } else {
1002  $this->error = $line->error;
1003  $this->errors = $line->errors;
1004  $this->db->rollback();
1005  return -1;
1006  }
1007  } else {
1008  $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
1009  return -2;
1010  }
1011  }
1012 
1013 
1022  public function create($user, $notrigger = 0)
1023  {
1024  global $conf, $hookmanager, $mysoc;
1025  $error = 0;
1026 
1027  $now = dol_now();
1028 
1029  // Clean parameters
1030  if (empty($this->date)) {
1031  $this->date = $this->datep;
1032  }
1033  $this->fin_validite = $this->date + ($this->duree_validite * 24 * 3600);
1034  if (empty($this->availability_id)) {
1035  $this->availability_id = 0;
1036  }
1037  if (empty($this->demand_reason_id)) {
1038  $this->demand_reason_id = 0;
1039  }
1040 
1041  // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1042  if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1043  list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date);
1044  } else {
1045  $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1046  }
1047  if (empty($this->fk_multicurrency)) {
1048  $this->multicurrency_code = $conf->currency;
1049  $this->fk_multicurrency = 0;
1050  $this->multicurrency_tx = 1;
1051  }
1052 
1053  // Set tmp vars
1054  $delivery_date = empty($this->delivery_date) ? $this->date_livraison : $this->delivery_date;
1055 
1056  dol_syslog(get_class($this)."::create");
1057 
1058  // Check parameters
1059  $result = $this->fetch_thirdparty();
1060  if ($result < 0) {
1061  $this->error = "Failed to fetch company";
1062  dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
1063  return -3;
1064  }
1065 
1066  // Check parameters
1067  if (!empty($this->ref)) { // We check that ref is not already used
1068  $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
1069  if ($result > 0) {
1070  $this->error = 'ErrorRefAlreadyExists';
1071  dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
1072  $this->db->rollback();
1073  return -1;
1074  }
1075  }
1076 
1077  if (empty($this->date)) {
1078  $this->error = "Date of proposal is required";
1079  dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
1080  return -4;
1081  }
1082 
1083 
1084  $this->db->begin();
1085 
1086  // Insert into database
1087  $sql = "INSERT INTO ".MAIN_DB_PREFIX."propal (";
1088  $sql .= "fk_soc";
1089  $sql .= ", price";
1090  $sql .= ", remise";
1091  $sql .= ", remise_percent";
1092  $sql .= ", remise_absolue";
1093  $sql .= ", total_tva";
1094  $sql .= ", total_ttc";
1095  $sql .= ", datep";
1096  $sql .= ", datec";
1097  $sql .= ", ref";
1098  $sql .= ", fk_user_author";
1099  $sql .= ", note_private";
1100  $sql .= ", note_public";
1101  $sql .= ", model_pdf";
1102  $sql .= ", fin_validite";
1103  $sql .= ", fk_cond_reglement";
1104  $sql .= ", deposit_percent";
1105  $sql .= ", fk_mode_reglement";
1106  $sql .= ", fk_account";
1107  $sql .= ", ref_client";
1108  $sql .= ", date_livraison";
1109  $sql .= ", fk_shipping_method";
1110  $sql .= ", fk_warehouse";
1111  $sql .= ", fk_availability";
1112  $sql .= ", fk_input_reason";
1113  $sql .= ", fk_projet";
1114  $sql .= ", fk_incoterms";
1115  $sql .= ", location_incoterms";
1116  $sql .= ", entity";
1117  $sql .= ", fk_multicurrency";
1118  $sql .= ", multicurrency_code";
1119  $sql .= ", multicurrency_tx";
1120  $sql .= ") ";
1121  $sql .= " VALUES (";
1122  $sql .= $this->socid;
1123  $sql .= ", 0";
1124  $sql .= ", ".((float) $this->remise); // deprecated
1125  $sql .= ", ".($this->remise_percent ? ((float) $this->remise_percent) : 'NULL');
1126  $sql .= ", ".($this->remise_absolue ? ((float) $this->remise_absolue) : 'NULL'); // deprecated
1127  $sql .= ", 0";
1128  $sql .= ", 0";
1129  $sql .= ", '".$this->db->idate($this->date)."'";
1130  $sql .= ", '".$this->db->idate($now)."'";
1131  $sql .= ", '(PROV)'";
1132  $sql .= ", ".($user->id > 0 ? ((int) $user->id) : "NULL");
1133  $sql .= ", '".$this->db->escape($this->note_private)."'";
1134  $sql .= ", '".$this->db->escape($this->note_public)."'";
1135  $sql .= ", '".$this->db->escape($this->model_pdf)."'";
1136  $sql .= ", ".($this->fin_validite != '' ? "'".$this->db->idate($this->fin_validite)."'" : "NULL");
1137  $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : 'NULL');
1138  $sql .= ", ".(! empty($this->deposit_percent) ? "'".$this->db->escape($this->deposit_percent)."'" : 'NULL');
1139  $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : 'NULL');
1140  $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
1141  $sql .= ", '".$this->db->escape($this->ref_client)."'";
1142  $sql .= ", ".(empty($delivery_date) ? "NULL" : "'".$this->db->idate($delivery_date)."'");
1143  $sql .= ", ".($this->shipping_method_id > 0 ? $this->shipping_method_id : 'NULL');
1144  $sql .= ", ".($this->warehouse_id > 0 ? $this->warehouse_id : 'NULL');
1145  $sql .= ", ".$this->availability_id;
1146  $sql .= ", ".$this->demand_reason_id;
1147  $sql .= ", ".($this->fk_project ? $this->fk_project : "null");
1148  $sql .= ", ".(int) $this->fk_incoterms;
1149  $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
1150  $sql .= ", ".setEntity($this);
1151  $sql .= ", ".(int) $this->fk_multicurrency;
1152  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1153  $sql .= ", ".(double) $this->multicurrency_tx;
1154  $sql .= ")";
1155 
1156  dol_syslog(get_class($this)."::create", LOG_DEBUG);
1157  $resql = $this->db->query($sql);
1158  if ($resql) {
1159  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."propal");
1160 
1161  if ($this->id) {
1162  $this->ref = '(PROV'.$this->id.')';
1163  $sql = 'UPDATE '.MAIN_DB_PREFIX."propal SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
1164 
1165  dol_syslog(get_class($this)."::create", LOG_DEBUG);
1166  $resql = $this->db->query($sql);
1167  if (!$resql) {
1168  $error++;
1169  }
1170 
1171  if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1172  $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1173  }
1174 
1175  // Add object linked
1176  if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1177  foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1178  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, ...))
1179  foreach ($tmp_origin_id as $origin_id) {
1180  $ret = $this->add_object_linked($origin, $origin_id);
1181  if (!$ret) {
1182  $this->error = $this->db->lasterror();
1183  $error++;
1184  }
1185  }
1186  } else // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1187  {
1188  $origin_id = $tmp_origin_id;
1189  $ret = $this->add_object_linked($origin, $origin_id);
1190  if (!$ret) {
1191  $this->error = $this->db->lasterror();
1192  $error++;
1193  }
1194  }
1195  }
1196  }
1197 
1198  /*
1199  * Insertion du detail des produits dans la base
1200  * Insert products detail in database
1201  */
1202  if (!$error) {
1203  $fk_parent_line = 0;
1204  $num = count($this->lines);
1205 
1206  for ($i = 0; $i < $num; $i++) {
1207  if (!is_object($this->lines[$i])) { // If this->lines is not array of objects, coming from REST API
1208  // Convert into object this->lines[$i].
1209  $line = (object) $this->lines[$i];
1210  } else {
1211  $line = $this->lines[$i];
1212  }
1213  // Reset fk_parent_line for line that are not child lines or special product
1214  if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
1215  $fk_parent_line = 0;
1216  }
1217  // Complete vat rate with code
1218  $vatrate = $line->tva_tx;
1219  if ($line->vat_src_code && !preg_match('/\(.*\)/', $vatrate)) {
1220  $vatrate .= ' ('.$line->vat_src_code.')';
1221  }
1222 
1223  if (!empty($conf->global->MAIN_CREATEFROM_KEEP_LINE_ORIGIN_INFORMATION)) {
1224  $originid = $line->origin_id;
1225  $origintype = $line->origin;
1226  } else {
1227  $originid = $line->id;
1228  $origintype = $this->element;
1229  }
1230 
1231  $result = $this->addline(
1232  $line->desc,
1233  $line->subprice,
1234  $line->qty,
1235  $vatrate,
1236  $line->localtax1_tx,
1237  $line->localtax2_tx,
1238  $line->fk_product,
1239  $line->remise_percent,
1240  'HT',
1241  0,
1242  $line->info_bits,
1243  $line->product_type,
1244  $line->rang,
1245  $line->special_code,
1246  $fk_parent_line,
1247  $line->fk_fournprice,
1248  $line->pa_ht,
1249  $line->label,
1250  $line->date_start,
1251  $line->date_end,
1252  $line->array_options,
1253  $line->fk_unit,
1254  $origintype,
1255  $originid,
1256  0,
1257  0,
1258  1
1259  );
1260 
1261  if ($result < 0) {
1262  $error++;
1263  $this->error = $this->db->error;
1264  dol_print_error($this->db);
1265  break;
1266  }
1267  // Defined the new fk_parent_line
1268  if ($result > 0 && $line->product_type == 9) {
1269  $fk_parent_line = $result;
1270  }
1271  }
1272  }
1273 
1274  // Set delivery address
1275  /*if (! $error && $this->fk_delivery_address)
1276  {
1277  $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
1278  $sql.= " SET fk_delivery_address = ".((int) $this->fk_delivery_address);
1279  $sql.= " WHERE ref = '".$this->db->escape($this->ref)."'";
1280  $sql.= " AND entity = ".setEntity($this);
1281 
1282  $result=$this->db->query($sql);
1283  }*/
1284 
1285  if (!$error) {
1286  // Mise a jour infos denormalisees
1287  $resql = $this->update_price(1, 'auto', 0, $mysoc);
1288  if ($resql) {
1289  $action = 'update';
1290 
1291  // Actions on extra fields
1292  if (!$error) {
1293  $result = $this->insertExtraFields();
1294  if ($result < 0) {
1295  $error++;
1296  }
1297  }
1298 
1299  if (!$error && !$notrigger) {
1300  // Call trigger
1301  $result = $this->call_trigger('PROPAL_CREATE', $user);
1302  if ($result < 0) {
1303  $error++;
1304  }
1305  // End call triggers
1306  }
1307  } else {
1308  $this->error = $this->db->lasterror();
1309  $error++;
1310  }
1311  }
1312  } else {
1313  $this->error = $this->db->lasterror();
1314  $error++;
1315  }
1316 
1317  if (!$error) {
1318  $this->db->commit();
1319  dol_syslog(get_class($this)."::create done id=".$this->id);
1320  return $this->id;
1321  } else {
1322  $this->db->rollback();
1323  return -2;
1324  }
1325  } else {
1326  $this->error = $this->db->lasterror();
1327  $this->db->rollback();
1328  return -1;
1329  }
1330  }
1331 
1341  public function createFromClone(User $user, $socid = 0, $forceentity = null, $update_prices = false)
1342  {
1343  global $conf, $hookmanager, $mysoc;
1344 
1345  dol_include_once('/projet/class/project.class.php');
1346 
1347  $error = 0;
1348  $now = dol_now();
1349 
1350  dol_syslog(__METHOD__, LOG_DEBUG);
1351 
1352  $object = new self($this->db);
1353 
1354  $this->db->begin();
1355 
1356  // Load source object
1357  $object->fetch($this->id);
1358 
1359  $objsoc = new Societe($this->db);
1360 
1361  // Change socid if needed
1362  if (!empty($socid) && $socid != $object->socid) {
1363  if ($objsoc->fetch($socid) > 0) {
1364  $object->socid = $objsoc->id;
1365  $object->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1366  $object->deposit_percent = (!empty($objsoc->deposit_percent) ? $objsoc->deposit_percent : null);
1367  $object->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1368  $object->fk_delivery_address = '';
1369 
1370  /*if (!empty($conf->project->enabled))
1371  {
1372  $project = new Project($db);
1373  if ($this->fk_project > 0 && $project->fetch($this->fk_project)) {
1374  if ($project->socid <= 0) $clonedObj->fk_project = $this->fk_project;
1375  else $clonedObj->fk_project = '';
1376  } else {
1377  $clonedObj->fk_project = '';
1378  }
1379  }*/
1380  $object->fk_project = ''; // A cloned proposal is set by default to no project.
1381  }
1382 
1383  // reset ref_client
1384  $object->ref_client = '';
1385 
1386  // TODO Change product price if multi-prices
1387  } else {
1388  $objsoc->fetch($object->socid);
1389  }
1390 
1391  // update prices
1392  if ($update_prices === true) {
1393  if ($objsoc->id > 0 && !empty($object->lines)) {
1394  if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) {
1395  // If price per customer
1396  require_once DOL_DOCUMENT_ROOT . '/product/class/productcustomerprice.class.php';
1397  }
1398 
1399  foreach ($object->lines as $line) {
1400  if ($line->fk_product > 0) {
1401  $prod = new Product($this->db);
1402  $res = $prod->fetch($line->fk_product);
1403  if ($res > 0) {
1404  $pu_ht = $prod->price;
1405  $tva_tx = get_default_tva($mysoc, $objsoc, $prod->id);
1406  $remise_percent = $objsoc->remise_percent;
1407 
1408  if (!empty($conf->global->PRODUIT_MULTIPRICES) && $objsoc->price_level > 0) {
1409  $pu_ht = $prod->multiprices[$objsoc->price_level];
1410  if (!empty($conf->global->PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL)) { // using this option is a bug. kept for backward compatibility
1411  if (isset($prod->multiprices_tva_tx[$objsoc->price_level])) {
1412  $tva_tx = $prod->multiprices_tva_tx[$objsoc->price_level];
1413  }
1414  }
1415  } elseif (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) {
1416  $prodcustprice = new Productcustomerprice($this->db);
1417  $filter = array('t.fk_product' => $prod->id, 't.fk_soc' => $objsoc->id);
1418  $result = $prodcustprice->fetch_all('', '', 0, 0, $filter);
1419  if ($result) {
1420  // If there is some prices specific to the customer
1421  if (count($prodcustprice->lines) > 0) {
1422  $pu_ht = price($prodcustprice->lines[0]->price);
1423  $tva_tx = ($prodcustprice->lines[0]->default_vat_code ? $prodcustprice->lines[0]->tva_tx.' ('.$prodcustprice->lines[0]->default_vat_code.' )' : $prodcustprice->lines[0]->tva_tx);
1424  if ($prodcustprice->lines[0]->default_vat_code && !preg_match('/\(.*\)/', $tva_tx)) {
1425  $tva_tx .= ' ('.$prodcustprice->lines[0]->default_vat_code.')';
1426  }
1427  }
1428  }
1429  }
1430 
1431  $line->subprice = $pu_ht;
1432  $line->tva_tx = $tva_tx;
1433  $line->remise_percent = $remise_percent;
1434  }
1435  }
1436  }
1437  }
1438  }
1439 
1440  $object->id = 0;
1441  $object->ref = '';
1442  $object->entity = (!empty($forceentity) ? $forceentity : $object->entity);
1443  $object->statut = self::STATUS_DRAFT;
1444 
1445  // Clear fields
1446  $object->user_author = $user->id;
1447  $object->user_valid = 0;
1448  $object->date = $now;
1449  $object->datep = $now; // deprecated
1450  $object->fin_validite = $object->date + ($object->duree_validite * 24 * 3600);
1451  if (empty($conf->global->MAIN_KEEP_REF_CUSTOMER_ON_CLONING)) {
1452  $object->ref_client = '';
1453  }
1454  if ($conf->global->MAIN_DONT_KEEP_NOTE_ON_CLONING == 1) {
1455  $object->note_private = '';
1456  $object->note_public = '';
1457  }
1458  // Create clone
1459  $object->context['createfromclone'] = 'createfromclone';
1460  $result = $object->create($user);
1461  if ($result < 0) {
1462  $this->error = $object->error;
1463  $this->errors = array_merge($this->errors, $object->errors);
1464  $error++;
1465  }
1466 
1467  if (!$error) {
1468  // copy internal contacts
1469  if ($object->copy_linked_contact($this, 'internal') < 0) {
1470  $error++;
1471  }
1472  }
1473 
1474  if (!$error) {
1475  // copy external contacts if same company
1476  if ($this->socid == $object->socid) {
1477  if ($object->copy_linked_contact($this, 'external') < 0) {
1478  $error++;
1479  }
1480  }
1481  }
1482 
1483  if (!$error) {
1484  // Hook of thirdparty module
1485  if (is_object($hookmanager)) {
1486  $parameters = array('objFrom'=>$this, 'clonedObj'=>$object);
1487  $action = '';
1488  $reshook = $hookmanager->executeHooks('createFrom', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
1489  if ($reshook < 0) {
1490  $error++;
1491  }
1492  }
1493  }
1494 
1495  unset($object->context['createfromclone']);
1496 
1497  // End
1498  if (!$error) {
1499  $this->db->commit();
1500  return $object->id;
1501  } else {
1502  $this->db->rollback();
1503  return -1;
1504  }
1505  }
1506 
1516  public function fetch($rowid, $ref = '', $ref_ext = '', $forceentity = 0)
1517  {
1518  $sql = "SELECT p.rowid, p.ref, p.entity, p.remise, p.remise_percent, p.remise_absolue, p.fk_soc";
1519  $sql .= ", p.total_ttc, p.total_tva, p.localtax1, p.localtax2, p.total_ht";
1520  $sql .= ", p.datec";
1521  $sql .= ", p.date_signature as dates";
1522  $sql .= ", p.date_valid as datev";
1523  $sql .= ", p.datep as dp";
1524  $sql .= ", p.fin_validite as dfv";
1525  $sql .= ", p.date_livraison as delivery_date";
1526  $sql .= ", p.model_pdf, p.last_main_doc, p.ref_client, p.extraparams";
1527  $sql .= ", p.note_private, p.note_public";
1528  $sql .= ", p.fk_projet as fk_project, p.fk_statut";
1529  $sql .= ", p.fk_user_author, p.fk_user_valid, p.fk_user_cloture";
1530  $sql .= ", p.fk_delivery_address";
1531  $sql .= ", p.fk_availability";
1532  $sql .= ", p.fk_input_reason";
1533  $sql .= ", p.fk_cond_reglement";
1534  $sql .= ", p.fk_mode_reglement";
1535  $sql .= ', p.fk_account';
1536  $sql .= ", p.fk_shipping_method";
1537  $sql .= ", p.fk_warehouse";
1538  $sql .= ", p.fk_incoterms, p.location_incoterms";
1539  $sql .= ", p.fk_multicurrency, p.multicurrency_code, p.multicurrency_tx, p.multicurrency_total_ht, p.multicurrency_total_tva, p.multicurrency_total_ttc";
1540  $sql .= ", p.tms as date_modification";
1541  $sql .= ", i.libelle as label_incoterms";
1542  $sql .= ", c.label as statut_label";
1543  $sql .= ", ca.code as availability_code, ca.label as availability";
1544  $sql .= ", dr.code as demand_reason_code, dr.label as demand_reason";
1545  $sql .= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc, p.deposit_percent";
1546  $sql .= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement";
1547  $sql .= " FROM ".MAIN_DB_PREFIX."propal as p";
1548  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_propalst as c ON p.fk_statut = c.id';
1549  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id AND cp.entity IN ('.getEntity('c_paiement').')';
1550  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON p.fk_cond_reglement = cr.rowid AND cr.entity IN ('.getEntity('c_payment_term').')';
1551  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_availability as ca ON p.fk_availability = ca.rowid';
1552  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_input_reason as dr ON p.fk_input_reason = dr.rowid';
1553  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON p.fk_incoterms = i.rowid';
1554 
1555  if (!empty($ref)) {
1556  if (!empty($forceentity)) {
1557  $sql .= " WHERE p.entity = ".(int) $forceentity; // Check only the current entity because we may have the same reference in several entities
1558  } else {
1559  $sql .= " WHERE p.entity IN (".getEntity('propal').")";
1560  }
1561  $sql .= " AND p.ref='".$this->db->escape($ref)."'";
1562  } else {
1563  // Dont't use entity if you use rowid
1564  $sql .= " WHERE p.rowid = ".((int) $rowid);
1565  }
1566 
1567  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1568  $resql = $this->db->query($sql);
1569  if ($resql) {
1570  if ($this->db->num_rows($resql)) {
1571  $obj = $this->db->fetch_object($resql);
1572 
1573  $this->id = $obj->rowid;
1574  $this->entity = $obj->entity;
1575 
1576  $this->ref = $obj->ref;
1577  $this->ref_client = $obj->ref_client;
1578  $this->remise = $obj->remise;
1579  $this->remise_percent = $obj->remise_percent;
1580  $this->remise_absolue = $obj->remise_absolue;
1581  $this->total = $obj->total_ttc; // TODO deprecated
1582  $this->total_ttc = $obj->total_ttc;
1583  $this->total_ht = $obj->total_ht;
1584  $this->total_tva = $obj->total_tva;
1585  $this->total_localtax1 = $obj->localtax1;
1586  $this->total_localtax2 = $obj->localtax2;
1587 
1588  $this->socid = $obj->fk_soc;
1589  $this->thirdparty = null; // Clear if another value was already set by fetch_thirdparty
1590 
1591  $this->fk_project = $obj->fk_project;
1592  $this->project = null; // Clear if another value was already set by fetch_projet
1593 
1594  $this->model_pdf = $obj->model_pdf;
1595  $this->modelpdf = $obj->model_pdf; // deprecated
1596  $this->last_main_doc = $obj->last_main_doc;
1597  $this->note = $obj->note_private; // TODO deprecated
1598  $this->note_private = $obj->note_private;
1599  $this->note_public = $obj->note_public;
1600 
1601  $this->status = (int) $obj->fk_statut;
1602  $this->statut = $this->status; // deprecated
1603  $this->statut_libelle = $obj->statut_label;
1604 
1605  $this->datec = $this->db->jdate($obj->datec); // TODO deprecated
1606  $this->datev = $this->db->jdate($obj->datev); // TODO deprecated
1607  $this->date_creation = $this->db->jdate($obj->datec); //Creation date
1608  $this->date_validation = $this->db->jdate($obj->datev); //Validation date
1609  $this->date_modification = $this->db->jdate($obj->date_modification); // tms
1610  $this->date_signature = $this->db->jdate($obj->dates); // Signature date
1611  $this->date = $this->db->jdate($obj->dp); // Proposal date
1612  $this->datep = $this->db->jdate($obj->dp); // deprecated
1613  $this->fin_validite = $this->db->jdate($obj->dfv);
1614  $this->date_livraison = $this->db->jdate($obj->delivery_date); // deprecated
1615  $this->delivery_date = $this->db->jdate($obj->delivery_date);
1616  $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1617  $this->warehouse_id = ($obj->fk_warehouse > 0) ? $obj->fk_warehouse : null;
1618  $this->availability_id = $obj->fk_availability;
1619  $this->availability_code = $obj->availability_code;
1620  $this->availability = $obj->availability;
1621  $this->demand_reason_id = $obj->fk_input_reason;
1622  $this->demand_reason_code = $obj->demand_reason_code;
1623  $this->demand_reason = $obj->demand_reason;
1624  $this->fk_address = $obj->fk_delivery_address;
1625 
1626  $this->mode_reglement_id = $obj->fk_mode_reglement;
1627  $this->mode_reglement_code = $obj->mode_reglement_code;
1628  $this->mode_reglement = $obj->mode_reglement;
1629  $this->fk_account = ($obj->fk_account > 0) ? $obj->fk_account : null;
1630  $this->cond_reglement_id = $obj->fk_cond_reglement;
1631  $this->cond_reglement_code = $obj->cond_reglement_code;
1632  $this->cond_reglement = $obj->cond_reglement;
1633  $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1634  $this->deposit_percent = $obj->deposit_percent;
1635 
1636  $this->extraparams = (array) json_decode($obj->extraparams, true);
1637 
1638  $this->user_author_id = $obj->fk_user_author;
1639  $this->user_valid_id = $obj->fk_user_valid;
1640  $this->user_close_id = $obj->fk_user_cloture;
1641 
1642  //Incoterms
1643  $this->fk_incoterms = $obj->fk_incoterms;
1644  $this->location_incoterms = $obj->location_incoterms;
1645  $this->label_incoterms = $obj->label_incoterms;
1646 
1647  // Multicurrency
1648  $this->fk_multicurrency = $obj->fk_multicurrency;
1649  $this->multicurrency_code = $obj->multicurrency_code;
1650  $this->multicurrency_tx = $obj->multicurrency_tx;
1651  $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1652  $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1653  $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1654 
1655  if ($obj->fk_statut == self::STATUS_DRAFT) {
1656  $this->brouillon = 1;
1657  }
1658 
1659  // Retrieve all extrafield
1660  // fetch optionals attributes and labels
1661  $this->fetch_optionals();
1662 
1663  $this->db->free($resql);
1664 
1665  $this->lines = array();
1666 
1667  // Lines
1668  $result = $this->fetch_lines();
1669  if ($result < 0) {
1670  return -3;
1671  }
1672 
1673  return 1;
1674  }
1675 
1676  $this->error = "Record Not Found";
1677  return 0;
1678  } else {
1679  $this->error = $this->db->lasterror();
1680  return -1;
1681  }
1682  }
1683 
1691  public function update(User $user, $notrigger = 0)
1692  {
1693  global $conf;
1694 
1695  $error = 0;
1696 
1697  // Clean parameters
1698  if (isset($this->ref)) {
1699  $this->ref = trim($this->ref);
1700  }
1701  if (isset($this->ref_client)) {
1702  $this->ref_client = trim($this->ref_client);
1703  }
1704  if (isset($this->note) || isset($this->note_private)) {
1705  $this->note_private = (isset($this->note_private) ? trim($this->note_private) : trim($this->note));
1706  }
1707  if (isset($this->note_public)) {
1708  $this->note_public = trim($this->note_public);
1709  }
1710  if (isset($this->model_pdf)) {
1711  $this->model_pdf = trim($this->model_pdf);
1712  }
1713  if (isset($this->import_key)) {
1714  $this->import_key = trim($this->import_key);
1715  }
1716  if (!empty($this->duree_validite) && is_numeric($this->duree_validite)) {
1717  $this->fin_validite = $this->date + ($this->duree_validite * 24 * 3600);
1718  }
1719 
1720  // Check parameters
1721  // Put here code to add control on parameters values
1722 
1723  // Update request
1724  $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET";
1725  $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1726  $sql .= " ref_client=".(isset($this->ref_client) ? "'".$this->db->escape($this->ref_client)."'" : "null").",";
1727  $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1728  $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
1729  $sql .= " datep=".(strval($this->date) != '' ? "'".$this->db->idate($this->date)."'" : 'null').",";
1730  if (!empty($this->fin_validite)) {
1731  $sql .= " fin_validite=".(strval($this->fin_validite) != '' ? "'".$this->db->idate($this->fin_validite)."'" : 'null').",";
1732  }
1733  $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
1734  $sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
1735  $sql .= " localtax1=".(isset($this->total_localtax1) ? $this->total_localtax1 : "null").",";
1736  $sql .= " localtax2=".(isset($this->total_localtax2) ? $this->total_localtax2 : "null").",";
1737  $sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
1738  $sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
1739  $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
1740  $sql .= " fk_user_author=".(isset($this->user_author_id) ? $this->user_author_id : "null").",";
1741  $sql .= " fk_user_valid=".(isset($this->user_valid) ? $this->user_valid : "null").",";
1742  $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
1743  $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
1744  $sql .= " deposit_percent=".(! empty($this->deposit_percent) ? "'".$this->db->escape($this->deposit_percent)."'" : "null").",";
1745  $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
1746  $sql .= " fk_input_reason=".(isset($this->demand_reason_id) ? $this->demand_reason_id : "null").",";
1747  $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1748  $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1749  $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1750  $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null")."";
1751  $sql .= " WHERE rowid=".((int) $this->id);
1752 
1753  $this->db->begin();
1754 
1755  dol_syslog(get_class($this)."::update", LOG_DEBUG);
1756  $resql = $this->db->query($sql);
1757  if (!$resql) {
1758  $error++;
1759  $this->errors[] = "Error ".$this->db->lasterror();
1760  }
1761 
1762  if (!$error) {
1763  $result = $this->insertExtraFields();
1764  if ($result < 0) {
1765  $error++;
1766  }
1767  }
1768 
1769  if (!$error && !$notrigger) {
1770  // Call trigger
1771  $result = $this->call_trigger('PROPAL_MODIFY', $user);
1772  if ($result < 0) {
1773  $error++;
1774  }
1775  // End call triggers
1776  }
1777 
1778  // Commit or rollback
1779  if ($error) {
1780  foreach ($this->errors as $errmsg) {
1781  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1782  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1783  }
1784  $this->db->rollback();
1785  return -1 * $error;
1786  } else {
1787  $this->db->commit();
1788  return 1;
1789  }
1790  }
1791 
1792 
1793  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1803  public function fetch_lines($only_product = 0, $loadalsotranslation = 0, $filters = '')
1804  {
1805  // phpcs:enable
1806  global $langs, $conf;
1807 
1808  $this->lines = array();
1809 
1810  $sql = 'SELECT d.rowid, d.fk_propal, d.fk_parent_line, d.label as custom_label, d.description, d.price, d.vat_src_code, d.tva_tx, d.localtax1_tx, d.localtax2_tx, d.localtax1_type, d.localtax2_type, d.qty, d.fk_remise_except, d.remise_percent, d.subprice, d.fk_product,';
1811  $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,';
1812  $sql .= ' d.fk_unit,';
1813  $sql .= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label, p.tobatch as product_tobatch, p.barcode as product_barcode,';
1814  $sql .= ' p.weight, p.weight_units, p.volume, p.volume_units,';
1815  $sql .= ' d.date_start, d.date_end,';
1816  $sql .= ' d.fk_multicurrency, d.multicurrency_code, d.multicurrency_subprice, d.multicurrency_total_ht, d.multicurrency_total_tva, d.multicurrency_total_ttc';
1817  $sql .= ' FROM '.MAIN_DB_PREFIX.'propaldet as d';
1818  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (d.fk_product = p.rowid)';
1819  $sql .= ' WHERE d.fk_propal = '.((int) $this->id);
1820  if ($only_product) {
1821  $sql .= ' AND p.fk_product_type = 0';
1822  }
1823  if ($filters) {
1824  $sql .= $filters;
1825  }
1826  $sql .= ' ORDER by d.rang';
1827 
1828  dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
1829  $result = $this->db->query($sql);
1830  if ($result) {
1831  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
1832 
1833  $num = $this->db->num_rows($result);
1834 
1835  $i = 0;
1836  while ($i < $num) {
1837  $objp = $this->db->fetch_object($result);
1838 
1839  $line = new PropaleLigne($this->db);
1840 
1841  $line->rowid = $objp->rowid; //Deprecated
1842  $line->id = $objp->rowid;
1843  $line->fk_propal = $objp->fk_propal;
1844  $line->fk_parent_line = $objp->fk_parent_line;
1845  $line->product_type = $objp->product_type;
1846  $line->label = $objp->custom_label;
1847  $line->desc = $objp->description; // Description ligne
1848  $line->description = $objp->description; // Description ligne
1849  $line->qty = $objp->qty;
1850  $line->vat_src_code = $objp->vat_src_code;
1851  $line->tva_tx = $objp->tva_tx;
1852  $line->localtax1_tx = $objp->localtax1_tx;
1853  $line->localtax2_tx = $objp->localtax2_tx;
1854  $line->localtax1_type = $objp->localtax1_type;
1855  $line->localtax2_type = $objp->localtax2_type;
1856  $line->subprice = $objp->subprice;
1857  $line->fk_remise_except = $objp->fk_remise_except;
1858  $line->remise_percent = $objp->remise_percent;
1859  $line->price = $objp->price; // TODO deprecated
1860 
1861  $line->info_bits = $objp->info_bits;
1862  $line->total_ht = $objp->total_ht;
1863  $line->total_tva = $objp->total_tva;
1864  $line->total_localtax1 = $objp->total_localtax1;
1865  $line->total_localtax2 = $objp->total_localtax2;
1866  $line->total_ttc = $objp->total_ttc;
1867  $line->fk_fournprice = $objp->fk_fournprice;
1868  $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1869  $line->pa_ht = $marginInfos[0];
1870  $line->marge_tx = $marginInfos[1];
1871  $line->marque_tx = $marginInfos[2];
1872  $line->special_code = $objp->special_code;
1873  $line->rang = $objp->rang;
1874 
1875  $line->fk_product = $objp->fk_product;
1876 
1877  $line->ref = $objp->product_ref; // deprecated
1878  $line->libelle = $objp->product_label; // deprecated
1879 
1880  $line->product_ref = $objp->product_ref;
1881  $line->product_label = $objp->product_label;
1882  $line->product_desc = $objp->product_desc; // Description produit
1883  $line->product_tobatch = $objp->product_tobatch;
1884  $line->product_barcode = $objp->product_barcode;
1885 
1886  $line->fk_product_type = $objp->fk_product_type; // deprecated
1887  $line->fk_unit = $objp->fk_unit;
1888  $line->weight = $objp->weight;
1889  $line->weight_units = $objp->weight_units;
1890  $line->volume = $objp->volume;
1891  $line->volume_units = $objp->volume_units;
1892 
1893  $line->date_start = $this->db->jdate($objp->date_start);
1894  $line->date_end = $this->db->jdate($objp->date_end);
1895 
1896  // Multicurrency
1897  $line->fk_multicurrency = $objp->fk_multicurrency;
1898  $line->multicurrency_code = $objp->multicurrency_code;
1899  $line->multicurrency_subprice = $objp->multicurrency_subprice;
1900  $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
1901  $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
1902  $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
1903 
1904  $line->fetch_optionals();
1905 
1906  // multilangs
1907  if (!empty($conf->global->MAIN_MULTILANGS) && !empty($objp->fk_product) && !empty($loadalsotranslation)) {
1908  $tmpproduct = new Product($this->db);
1909  $tmpproduct->fetch($objp->fk_product);
1910  $tmpproduct->getMultiLangs();
1911 
1912  $line->multilangs = $tmpproduct->multilangs;
1913  }
1914 
1915  $this->lines[$i] = $line;
1916 
1917  $i++;
1918  }
1919 
1920  $this->db->free($result);
1921 
1922  return $num;
1923  } else {
1924  $this->error = $this->db->lasterror();
1925  return -3;
1926  }
1927  }
1928 
1936  public function valid($user, $notrigger = 0)
1937  {
1938  global $conf;
1939 
1940  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1941 
1942  $error = 0;
1943 
1944  // Protection
1945  if ($this->statut == self::STATUS_VALIDATED) {
1946  dol_syslog(get_class($this)."::valid action abandonned: already validated", LOG_WARNING);
1947  return 0;
1948  }
1949 
1950  if (!((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->propal->creer))
1951  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->propal->propal_advance->validate)))) {
1952  $this->error = 'ErrorPermissionDenied';
1953  dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
1954  return -1;
1955  }
1956 
1957  $now = dol_now();
1958 
1959  $this->db->begin();
1960 
1961  // Numbering module definition
1962  $soc = new Societe($this->db);
1963  $soc->fetch($this->socid);
1964 
1965  // Define new ref
1966  if (!$error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
1967  $num = $this->getNextNumRef($soc);
1968  } else {
1969  $num = $this->ref;
1970  }
1971  $this->newref = dol_sanitizeFileName($num);
1972 
1973  $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
1974  $sql .= " SET ref = '".$this->db->escape($num)."',";
1975  $sql .= " fk_statut = ".self::STATUS_VALIDATED.", date_valid='".$this->db->idate($now)."', fk_user_valid=".((int) $user->id);
1976  $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = ".self::STATUS_DRAFT;
1977 
1978  dol_syslog(get_class($this)."::valid", LOG_DEBUG);
1979  $resql = $this->db->query($sql);
1980  if (!$resql) {
1981  dol_print_error($this->db);
1982  $error++;
1983  }
1984 
1985  // Trigger calls
1986  if (!$error && !$notrigger) {
1987  // Call trigger
1988  $result = $this->call_trigger('PROPAL_VALIDATE', $user);
1989  if ($result < 0) {
1990  $error++;
1991  }
1992  // End call triggers
1993  }
1994 
1995  if (!$error) {
1996  $this->oldref = $this->ref;
1997 
1998  // Rename directory if dir was a temporary ref
1999  if (preg_match('/^[\(]?PROV/i', $this->ref)) {
2000  // Now we rename also files into index
2001  $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'propale/".$this->db->escape($this->newref)."'";
2002  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'propale/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
2003  $resql = $this->db->query($sql);
2004  if (!$resql) {
2005  $error++;
2006  $this->error = $this->db->lasterror();
2007  }
2008 
2009  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
2010  $oldref = dol_sanitizeFileName($this->ref);
2011  $newref = dol_sanitizeFileName($num);
2012  $dirsource = $conf->propal->multidir_output[$this->entity].'/'.$oldref;
2013  $dirdest = $conf->propal->multidir_output[$this->entity].'/'.$newref;
2014  if (!$error && file_exists($dirsource)) {
2015  dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
2016  if (@rename($dirsource, $dirdest)) {
2017  dol_syslog("Rename ok");
2018  // Rename docs starting with $oldref with $newref
2019  $listoffiles = dol_dir_list($dirdest, 'files', 1, '^'.preg_quote($oldref, '/'));
2020  foreach ($listoffiles as $fileentry) {
2021  $dirsource = $fileentry['name'];
2022  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
2023  $dirsource = $fileentry['path'].'/'.$dirsource;
2024  $dirdest = $fileentry['path'].'/'.$dirdest;
2025  @rename($dirsource, $dirdest);
2026  }
2027  }
2028  }
2029  }
2030 
2031  $this->ref = $num;
2032  $this->brouillon = 0;
2033  $this->statut = self::STATUS_VALIDATED;
2034  $this->user_valid_id = $user->id;
2035  $this->datev = $now;
2036 
2037  $this->db->commit();
2038  return 1;
2039  } else {
2040  $this->db->rollback();
2041  return -1;
2042  }
2043  }
2044 
2045 
2046  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2055  public function set_date($user, $date, $notrigger = 0)
2056  {
2057  // phpcs:enable
2058  if (empty($date)) {
2059  $this->error = 'ErrorBadParameter';
2060  dol_syslog(get_class($this)."::set_date ".$this->error, LOG_ERR);
2061  return -1;
2062  }
2063 
2064  if (!empty($user->rights->propal->creer)) {
2065  $error = 0;
2066 
2067  $this->db->begin();
2068 
2069  $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET datep = '".$this->db->idate($date)."'";
2070  $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = ".self::STATUS_DRAFT;
2071 
2072  dol_syslog(__METHOD__, LOG_DEBUG);
2073  $resql = $this->db->query($sql);
2074  if (!$resql) {
2075  $this->errors[] = $this->db->error();
2076  $error++;
2077  }
2078 
2079  if (!$error) {
2080  $this->oldcopy = clone $this;
2081  $this->date = $date;
2082  $this->datep = $date; // deprecated
2083  }
2084 
2085  if (!$notrigger && empty($error)) {
2086  // Call trigger
2087  $result = $this->call_trigger('PROPAL_MODIFY', $user);
2088  if ($result < 0) {
2089  $error++;
2090  }
2091  // End call triggers
2092  }
2093 
2094  if (!$error) {
2095  $this->db->commit();
2096  return 1;
2097  } else {
2098  foreach ($this->errors as $errmsg) {
2099  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2100  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2101  }
2102  $this->db->rollback();
2103  return -1 * $error;
2104  }
2105  }
2106  }
2107 
2108  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2117  public function set_echeance($user, $date_fin_validite, $notrigger = 0)
2118  {
2119  // phpcs:enable
2120  if (!empty($user->rights->propal->creer)) {
2121  $error = 0;
2122 
2123  $this->db->begin();
2124 
2125  $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET fin_validite = ".($date_fin_validite != '' ? "'".$this->db->idate($date_fin_validite)."'" : 'null');
2126  $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = ".self::STATUS_DRAFT;
2127 
2128  dol_syslog(__METHOD__, LOG_DEBUG);
2129  $resql = $this->db->query($sql);
2130  if (!$resql) {
2131  $this->errors[] = $this->db->error();
2132  $error++;
2133  }
2134 
2135 
2136  if (!$error) {
2137  $this->oldcopy = clone $this;
2138  $this->fin_validite = $date_fin_validite;
2139  }
2140 
2141  if (!$notrigger && empty($error)) {
2142  // Call trigger
2143  $result = $this->call_trigger('PROPAL_MODIFY', $user);
2144  if ($result < 0) {
2145  $error++;
2146  }
2147  // End call triggers
2148  }
2149 
2150  if (!$error) {
2151  $this->db->commit();
2152  return 1;
2153  } else {
2154  foreach ($this->errors as $errmsg) {
2155  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2156  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2157  }
2158  $this->db->rollback();
2159  return -1 * $error;
2160  }
2161  }
2162  }
2163 
2164  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2174  public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2175  {
2176  // phpcs:enable
2177  return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2178  }
2179 
2188  public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2189  {
2190  if (!empty($user->rights->propal->creer)) {
2191  $error = 0;
2192 
2193  $this->db->begin();
2194 
2195  $sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
2196  $sql .= " SET date_livraison = ".($delivery_date != '' ? "'".$this->db->idate($delivery_date)."'" : 'null');
2197  $sql .= " WHERE rowid = ".((int) $this->id);
2198 
2199  dol_syslog(__METHOD__, LOG_DEBUG);
2200  $resql = $this->db->query($sql);
2201  if (!$resql) {
2202  $this->errors[] = $this->db->error();
2203  $error++;
2204  }
2205 
2206  if (!$error) {
2207  $this->oldcopy = clone $this;
2208  $this->date_livraison = $delivery_date;
2209  $this->delivery_date = $delivery_date;
2210  }
2211 
2212  if (!$notrigger && empty($error)) {
2213  // Call trigger
2214  $result = $this->call_trigger('PROPAL_MODIFY', $user);
2215  if ($result < 0) {
2216  $error++;
2217  }
2218  // End call triggers
2219  }
2220 
2221  if (!$error) {
2222  $this->db->commit();
2223  return 1;
2224  } else {
2225  foreach ($this->errors as $errmsg) {
2226  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2227  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2228  }
2229  $this->db->rollback();
2230  return -1 * $error;
2231  }
2232  }
2233  }
2234 
2235  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2244  public function set_availability($user, $id, $notrigger = 0)
2245  {
2246  // phpcs:enable
2247  if (!empty($user->rights->propal->creer) && $this->statut >= self::STATUS_DRAFT) {
2248  $error = 0;
2249 
2250  $this->db->begin();
2251 
2252  $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2253  $sql .= " SET fk_availability = ".((int) $id);
2254  $sql .= " WHERE rowid = ".((int) $this->id);
2255 
2256  dol_syslog(__METHOD__.' availability('.$id.')', LOG_DEBUG);
2257  $resql = $this->db->query($sql);
2258  if (!$resql) {
2259  $this->errors[] = $this->db->error();
2260  $error++;
2261  }
2262 
2263  if (!$error) {
2264  $this->oldcopy = clone $this;
2265  $this->fk_availability = $id;
2266  $this->availability_id = $id;
2267  }
2268 
2269  if (!$notrigger && empty($error)) {
2270  // Call trigger
2271  $result = $this->call_trigger('PROPAL_MODIFY', $user);
2272  if ($result < 0) {
2273  $error++;
2274  }
2275  // End call triggers
2276  }
2277 
2278  if (!$error) {
2279  $this->db->commit();
2280  return 1;
2281  } else {
2282  foreach ($this->errors as $errmsg) {
2283  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2284  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2285  }
2286  $this->db->rollback();
2287  return -1 * $error;
2288  }
2289  } else {
2290  $error_str = 'Propal status do not meet requirement '.$this->statut;
2291  dol_syslog(__METHOD__.$error_str, LOG_ERR);
2292  $this->error = $error_str;
2293  $this->errors[] = $this->error;
2294  return -2;
2295  }
2296  }
2297 
2298  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2307  public function set_demand_reason($user, $id, $notrigger = 0)
2308  {
2309  // phpcs:enable
2310  if (!empty($user->rights->propal->creer) && $this->statut >= self::STATUS_DRAFT) {
2311  $error = 0;
2312 
2313  $this->db->begin();
2314 
2315  $sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
2316  $sql .= " SET fk_input_reason = ".((int) $id);
2317  $sql .= " WHERE rowid = ".((int) $this->id);
2318 
2319  dol_syslog(__METHOD__, LOG_DEBUG);
2320  $resql = $this->db->query($sql);
2321  if (!$resql) {
2322  $this->errors[] = $this->db->error();
2323  $error++;
2324  }
2325 
2326 
2327  if (!$error) {
2328  $this->oldcopy = clone $this;
2329  $this->fk_input_reason = $id;
2330  $this->demand_reason_id = $id;
2331  }
2332 
2333 
2334  if (!$notrigger && empty($error)) {
2335  // Call trigger
2336  $result = $this->call_trigger('PROPAL_MODIFY', $user);
2337  if ($result < 0) {
2338  $error++;
2339  }
2340  // End call triggers
2341  }
2342 
2343  if (!$error) {
2344  $this->db->commit();
2345  return 1;
2346  } else {
2347  foreach ($this->errors as $errmsg) {
2348  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2349  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2350  }
2351  $this->db->rollback();
2352  return -1 * $error;
2353  }
2354  } else {
2355  $error_str = 'Propal status do not meet requirement '.$this->statut;
2356  dol_syslog(__METHOD__.$error_str, LOG_ERR);
2357  $this->error = $error_str;
2358  $this->errors[] = $this->error;
2359  return -2;
2360  }
2361  }
2362 
2363  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2372  public function set_ref_client($user, $ref_client, $notrigger = 0)
2373  {
2374  // phpcs:enable
2375  if (!empty($user->rights->propal->creer)) {
2376  $error = 0;
2377 
2378  $this->db->begin();
2379 
2380  $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET ref_client = ".(empty($ref_client) ? 'NULL' : "'".$this->db->escape($ref_client)."'");
2381  $sql .= " WHERE rowid = ".((int) $this->id);
2382 
2383  dol_syslog(__METHOD__.' $this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
2384  $resql = $this->db->query($sql);
2385  if (!$resql) {
2386  $this->errors[] = $this->db->error();
2387  $error++;
2388  }
2389 
2390  if (!$error) {
2391  $this->oldcopy = clone $this;
2392  $this->ref_client = $ref_client;
2393  }
2394 
2395  if (!$notrigger && empty($error)) {
2396  // Call trigger
2397  $result = $this->call_trigger('PROPAL_MODIFY', $user);
2398  if ($result < 0) {
2399  $error++;
2400  }
2401  // End call triggers
2402  }
2403 
2404  if (!$error) {
2405  $this->db->commit();
2406  return 1;
2407  } else {
2408  foreach ($this->errors as $errmsg) {
2409  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2410  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2411  }
2412  $this->db->rollback();
2413  return -1 * $error;
2414  }
2415  } else {
2416  return -1;
2417  }
2418  }
2419 
2420  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2429  public function set_remise_percent($user, $remise, $notrigger = 0)
2430  {
2431  // phpcs:enable
2432  $remise = trim($remise) ?trim($remise) : 0;
2433 
2434  if (!empty($user->rights->propal->creer)) {
2435  $remise = price2num($remise, 2);
2436 
2437  $error = 0;
2438 
2439  $this->db->begin();
2440 
2441  $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET remise_percent = ".((float) $remise);
2442  $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = ".self::STATUS_DRAFT;
2443 
2444  dol_syslog(__METHOD__, LOG_DEBUG);
2445  $resql = $this->db->query($sql);
2446  if (!$resql) {
2447  $this->errors[] = $this->db->error();
2448  $error++;
2449  }
2450 
2451  if (!$error) {
2452  $this->oldcopy = clone $this;
2453  $this->remise_percent = $remise;
2454  $this->update_price(1);
2455  }
2456 
2457  if (!$notrigger && empty($error)) {
2458  // Call trigger
2459  $result = $this->call_trigger('PROPAL_MODIFY', $user);
2460  if ($result < 0) {
2461  $error++;
2462  }
2463  // End call triggers
2464  }
2465 
2466  if (!$error) {
2467  $this->db->commit();
2468  return 1;
2469  } else {
2470  foreach ($this->errors as $errmsg) {
2471  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2472  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2473  }
2474  $this->db->rollback();
2475  return -1 * $error;
2476  }
2477  }
2478  }
2479 
2480 
2481  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2490  public function set_remise_absolue($user, $remise, $notrigger = 0)
2491  {
2492  // phpcs:enable
2493  if (empty($remise)) {
2494  $remise = 0;
2495  }
2497 
2498  if (!empty($user->rights->propal->creer)) {
2499  $error = 0;
2500 
2501  $this->db->begin();
2502 
2503  $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2504  $sql .= " SET remise_absolue = ".((float) $remise);
2505  $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = ".self::STATUS_DRAFT;
2506 
2507  dol_syslog(__METHOD__, LOG_DEBUG);
2508  $resql = $this->db->query($sql);
2509  if (!$resql) {
2510  $this->errors[] = $this->db->error();
2511  $error++;
2512  }
2513 
2514  if (!$error) {
2515  $this->oldcopy = clone $this;
2516  $this->update_price(1);
2517  }
2518 
2519  if (!$notrigger && empty($error)) {
2520  // Call trigger
2521  $result = $this->call_trigger('PROPAL_MODIFY', $user);
2522  if ($result < 0) {
2523  $error++;
2524  }
2525  // End call triggers
2526  }
2527 
2528  if (!$error) {
2529  $this->db->commit();
2530  return 1;
2531  } else {
2532  foreach ($this->errors as $errmsg) {
2533  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2534  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2535  }
2536  $this->db->rollback();
2537  return -1 * $error;
2538  }
2539  }
2540  }
2541 
2542 
2543 
2553  public function reopen($user, $status, $note = '', $notrigger = 0)
2554  {
2555  $error = 0;
2556 
2557  $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2558  $sql .= " SET fk_statut = ".((int) $status).",";
2559  if (!empty($note)) {
2560  $sql .= " note_private = '".$this->db->escape($note)."',";
2561  }
2562  $sql .= " date_cloture=NULL, fk_user_cloture=NULL";
2563  $sql .= " WHERE rowid = ".((int) $this->id);
2564 
2565  $this->db->begin();
2566 
2567  dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
2568  $resql = $this->db->query($sql);
2569  if (!$resql) {
2570  $error++;
2571  $this->errors[] = "Error ".$this->db->lasterror();
2572  }
2573  if (!$error) {
2574  if (!$notrigger) {
2575  // Call trigger
2576  $result = $this->call_trigger('PROPAL_REOPEN', $user);
2577  if ($result < 0) {
2578  $error++;
2579  }
2580  // End call triggers
2581  }
2582  }
2583 
2584  // Commit or rollback
2585  if ($error) {
2586  if (!empty($this->errors)) {
2587  foreach ($this->errors as $errmsg) {
2588  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
2589  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2590  }
2591  }
2592  $this->db->rollback();
2593  return -1 * $error;
2594  } else {
2595  $this->statut = $status;
2596  $this->status = $status;
2597 
2598  $this->db->commit();
2599  return 1;
2600  }
2601  }
2602 
2612  public function closeProposal($user, $status, $note = '', $notrigger = 0)
2613  {
2614  global $langs,$conf;
2615 
2616  $error = 0;
2617  $now = dol_now();
2618 
2619  $this->db->begin();
2620 
2621  $newprivatenote = dol_concatdesc($this->note_private, $note);
2622 
2623  $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2624  $sql .= " SET fk_statut = ".((int) $status).", note_private = '".$this->db->escape($newprivatenote)."', date_signature='".$this->db->idate($now)."', fk_user_signature=".$user->id;
2625  $sql .= " WHERE rowid = ".((int) $this->id);
2626 
2627  $resql = $this->db->query($sql);
2628  if ($resql) {
2629  // Status self::STATUS_REFUSED by default
2630  $modelpdf = !empty($conf->global->PROPALE_ADDON_PDF_ODT_CLOSED) ? $conf->global->PROPALE_ADDON_PDF_ODT_CLOSED : $this->model_pdf;
2631  $trigger_name = 'PROPAL_CLOSE_REFUSED';
2632 
2633  if ($status == self::STATUS_SIGNED) { // Status self::STATUS_SIGNED
2634  $trigger_name = 'PROPAL_CLOSE_SIGNED';
2635  $modelpdf = !empty($conf->global->PROPALE_ADDON_PDF_ODT_TOBILL) ? $conf->global->PROPALE_ADDON_PDF_ODT_TOBILL : $this->model_pdf;
2636 
2637  // The connected company is classified as a client
2638  $soc=new Societe($this->db);
2639  $soc->id = $this->socid;
2640  $result = $soc->set_as_client();
2641 
2642  if ($result < 0) {
2643  $this->error=$this->db->lasterror();
2644  $this->db->rollback();
2645  return -2;
2646  }
2647  }
2648 
2649  if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE)) {
2650  // Define output language
2651  $outputlangs = $langs;
2652  if (!empty($conf->global->MAIN_MULTILANGS)) {
2653  $outputlangs = new Translate("", $conf);
2654  $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
2655  $outputlangs->setDefaultLang($newlang);
2656  }
2657 
2658  // PDF
2659  $hidedetails = (!empty($conf->global->MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS) ? 1 : 0);
2660  $hidedesc = (!empty($conf->global->MAIN_GENERATE_DOCUMENTS_HIDE_DESC) ? 1 : 0);
2661  $hideref = (!empty($conf->global->MAIN_GENERATE_DOCUMENTS_HIDE_REF) ? 1 : 0);
2662 
2663  //$ret=$object->fetch($id); // Reload to get new records
2664  $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
2665  }
2666 
2667  if (!$error) {
2668  $this->oldcopy= clone $this;
2669  $this->statut = $status;
2670  $this->status = $status;
2671  $this->date_signature = $now;
2672  $this->note_private = $newprivatenote;
2673  }
2674 
2675  if (!$notrigger && empty($error)) {
2676  // Call trigger
2677  $result=$this->call_trigger($trigger_name, $user);
2678  if ($result < 0) {
2679  $error++;
2680  }
2681  // End call triggers
2682  }
2683 
2684  if (!$error ) {
2685  $this->db->commit();
2686  return 1;
2687  } else {
2688  $this->statut = $this->oldcopy->statut;
2689  $this->status = $this->oldcopy->statut;
2690  $this->date_signature = $this->oldcopy->date_signature;
2691  $this->note_private = $this->oldcopy->note_private;
2692 
2693  $this->db->rollback();
2694  return -1;
2695  }
2696  } else {
2697  $this->error = $this->db->lasterror();
2698  $this->db->rollback();
2699  return -1;
2700  }
2701  }
2702 
2711  public function classifyBilled(User $user, $notrigger = 0, $note = '')
2712  {
2713  global $conf, $langs;
2714 
2715  $error = 0;
2716 
2717  $now = dol_now();
2718  $num = 0;
2719 
2720  $triggerName = 'PROPAL_CLASSIFY_BILLED';
2721 
2722  $this->db->begin();
2723 
2724  $newprivatenote = dol_concatdesc($this->note_private, $note);
2725 
2726  $sql = 'UPDATE '.MAIN_DB_PREFIX.'propal SET fk_statut = '.self::STATUS_BILLED.", ";
2727  $sql .= " note_private = '".$this->db->escape($newprivatenote)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".((int) $user->id);
2728  $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.((int) self::STATUS_SIGNED);
2729 
2730  dol_syslog(__METHOD__, LOG_DEBUG);
2731  $resql = $this->db->query($sql);
2732  if (!$resql) {
2733  $this->errors[] = $this->db->error();
2734  $error++;
2735  } else {
2736  $num = $this->db->affected_rows($resql);
2737  }
2738 
2739  if (!$error) {
2740  $modelpdf = $conf->global->PROPALE_ADDON_PDF_ODT_CLOSED ? $conf->global->PROPALE_ADDON_PDF_ODT_CLOSED : $this->model_pdf;
2741 
2742  if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE)) {
2743  // Define output language
2744  $outputlangs = $langs;
2745  if (!empty($conf->global->MAIN_MULTILANGS)) {
2746  $outputlangs = new Translate("", $conf);
2747  $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
2748  $outputlangs->setDefaultLang($newlang);
2749  }
2750 
2751  // PDF
2752  $hidedetails = (!empty($conf->global->MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS) ? 1 : 0);
2753  $hidedesc = (!empty($conf->global->MAIN_GENERATE_DOCUMENTS_HIDE_DESC) ? 1 : 0);
2754  $hideref = (!empty($conf->global->MAIN_GENERATE_DOCUMENTS_HIDE_REF) ? 1 : 0);
2755 
2756  //$ret=$object->fetch($id); // Reload to get new records
2757  $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
2758  }
2759 
2760  $this->oldcopy = clone $this;
2761  $this->statut = self::STATUS_BILLED;
2762  $this->date_cloture = $now;
2763  $this->note_private = $newprivatenote;
2764  }
2765 
2766  if (!$notrigger && empty($error)) {
2767  // Call trigger
2768  $result = $this->call_trigger($triggerName, $user);
2769  if ($result < 0) {
2770  $error++;
2771  }
2772  // End call triggers
2773  }
2774 
2775  if (!$error) {
2776  $this->db->commit();
2777  return $num;
2778  } else {
2779  foreach ($this->errors as $errmsg) {
2780  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2781  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2782  }
2783  $this->db->rollback();
2784  return -1 * $error;
2785  }
2786  }
2787 
2788  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2796  public function setDraft($user, $notrigger = 0)
2797  {
2798  // phpcs:enable
2799  $error = 0;
2800 
2801  // Protection
2802  if ($this->statut <= self::STATUS_DRAFT) {
2803  return 0;
2804  }
2805 
2806  dol_syslog(get_class($this)."::setDraft", LOG_DEBUG);
2807 
2808  $this->db->begin();
2809 
2810  $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2811  $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
2812  $sql .= " WHERE rowid = ".((int) $this->id);
2813 
2814  $resql = $this->db->query($sql);
2815  if (!$resql) {
2816  $this->errors[] = $this->db->error();
2817  $error++;
2818  }
2819 
2820  if (!$error) {
2821  $this->oldcopy = clone $this;
2822  }
2823 
2824  if (!$notrigger && empty($error)) {
2825  // Call trigger
2826  $result = $this->call_trigger('PROPAL_MODIFY', $user);
2827  if ($result < 0) {
2828  $error++;
2829  }
2830  // End call triggers
2831  }
2832 
2833  if (!$error) {
2834  $this->statut = self::STATUS_DRAFT;
2835  $this->brouillon = 1;
2836 
2837  $this->db->commit();
2838  return 1;
2839  } else {
2840  foreach ($this->errors as $errmsg) {
2841  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2842  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2843  }
2844  $this->db->rollback();
2845  return -1 * $error;
2846  }
2847  }
2848 
2849 
2850  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2864  public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datep', $sortorder = 'DESC')
2865  {
2866  // phpcs:enable
2867  global $user;
2868 
2869  $ga = array();
2870 
2871  $sql = "SELECT s.rowid, s.nom as name, s.client,";
2872  $sql .= " p.rowid as propalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
2873  $sql .= " p.datep as dp, p.fin_validite as datelimite";
2874  if (empty($user->rights->societe->client->voir) && !$socid) {
2875  $sql .= ", sc.fk_soc, sc.fk_user";
2876  }
2877  $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."propal as p, ".MAIN_DB_PREFIX."c_propalst as c";
2878  if (empty($user->rights->societe->client->voir) && !$socid) {
2879  $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
2880  }
2881  $sql .= " WHERE p.entity IN (".getEntity('propal').")";
2882  $sql .= " AND p.fk_soc = s.rowid";
2883  $sql .= " AND p.fk_statut = c.id";
2884  if (empty($user->rights->societe->client->voir) && !$socid) { //restriction
2885  $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
2886  }
2887  if ($socid) {
2888  $sql .= " AND s.rowid = ".((int) $socid);
2889  }
2890  if ($draft) {
2891  $sql .= " AND p.fk_statut = ".self::STATUS_DRAFT;
2892  }
2893  if ($notcurrentuser > 0) {
2894  $sql .= " AND p.fk_user_author <> ".((int) $user->id);
2895  }
2896  $sql .= $this->db->order($sortfield, $sortorder);
2897  $sql .= $this->db->plimit($limit, $offset);
2898 
2899  $result = $this->db->query($sql);
2900  if ($result) {
2901  $num = $this->db->num_rows($result);
2902  if ($num) {
2903  $i = 0;
2904  while ($i < $num) {
2905  $obj = $this->db->fetch_object($result);
2906 
2907  if ($shortlist == 1) {
2908  $ga[$obj->propalid] = $obj->ref;
2909  } elseif ($shortlist == 2) {
2910  $ga[$obj->propalid] = $obj->ref.' ('.$obj->name.')';
2911  } else {
2912  $ga[$i]['id'] = $obj->propalid;
2913  $ga[$i]['ref'] = $obj->ref;
2914  $ga[$i]['name'] = $obj->name;
2915  }
2916 
2917  $i++;
2918  }
2919  }
2920  return $ga;
2921  } else {
2922  dol_print_error($this->db);
2923  return -1;
2924  }
2925  }
2926 
2932  public function getInvoiceArrayList()
2933  {
2934  return $this->InvoiceArrayList($this->id);
2935  }
2936 
2937  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2944  public function InvoiceArrayList($id)
2945  {
2946  // phpcs:enable
2947  $ga = array();
2948  $linkedInvoices = array();
2949 
2950  $this->fetchObjectLinked($id, $this->element);
2951  foreach ($this->linkedObjectsIds as $objecttype => $objectid) {
2952  // Nouveau système du comon object renvoi des rowid et non un id linéaire de 1 à n
2953  // On parcourt donc une liste d'objets en tant qu'objet unique
2954  foreach ($objectid as $key => $object) {
2955  // Cas des factures liees directement
2956  if ($objecttype == 'facture') {
2957  $linkedInvoices[] = $object;
2958  } else {
2959  // Cas des factures liees par un autre objet (ex: commande)
2960  $this->fetchObjectLinked($object, $objecttype);
2961  foreach ($this->linkedObjectsIds as $subobjecttype => $subobjectid) {
2962  foreach ($subobjectid as $subkey => $subobject) {
2963  if ($subobjecttype == 'facture') {
2964  $linkedInvoices[] = $subobject;
2965  }
2966  }
2967  }
2968  }
2969  }
2970  }
2971 
2972  if (count($linkedInvoices) > 0) {
2973  $sql = "SELECT rowid as facid, ref, total_ht as total, datef as df, fk_user_author, fk_statut, paye";
2974  $sql .= " FROM ".MAIN_DB_PREFIX."facture";
2975  $sql .= " WHERE rowid IN (".$this->db->sanitize(implode(',', $linkedInvoices)).")";
2976 
2977  dol_syslog(get_class($this)."::InvoiceArrayList", LOG_DEBUG);
2978  $resql = $this->db->query($sql);
2979 
2980  if ($resql) {
2981  $tab_sqlobj = array();
2982  $nump = $this->db->num_rows($resql);
2983  for ($i = 0; $i < $nump; $i++) {
2984  $sqlobj = $this->db->fetch_object($resql);
2985  $tab_sqlobj[] = $sqlobj;
2986  }
2987  $this->db->free($resql);
2988 
2989  $nump = count($tab_sqlobj);
2990 
2991  if ($nump) {
2992  $i = 0;
2993  while ($i < $nump) {
2994  $obj = array_shift($tab_sqlobj);
2995 
2996  $ga[$i] = $obj;
2997 
2998  $i++;
2999  }
3000  }
3001  return $ga;
3002  } else {
3003  return -1;
3004  }
3005  } else {
3006  return $ga;
3007  }
3008  }
3009 
3017  public function delete($user, $notrigger = 0)
3018  {
3019  global $conf;
3020  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3021 
3022  $error = 0;
3023 
3024  $this->db->begin();
3025 
3026  if (!$notrigger) {
3027  // Call trigger
3028  $result = $this->call_trigger('PROPAL_DELETE', $user);
3029  if ($result < 0) {
3030  $error++;
3031  }
3032  // End call triggers
3033  }
3034 
3035  // Delete extrafields of lines and lines
3036  if (!$error && !empty($this->table_element_line)) {
3037  $tabletodelete = $this->table_element_line;
3038  $sqlef = "DELETE FROM ".MAIN_DB_PREFIX.$tabletodelete."_extrafields WHERE fk_object IN (SELECT rowid FROM ".MAIN_DB_PREFIX.$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id).")";
3039  $sql = "DELETE FROM ".MAIN_DB_PREFIX.$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id);
3040  if (!$this->db->query($sqlef) || !$this->db->query($sql)) {
3041  $error++;
3042  $this->error = $this->db->lasterror();
3043  $this->errors[] = $this->error;
3044  dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3045  }
3046  }
3047 
3048  if (!$error) {
3049  // Delete linked object
3050  $res = $this->deleteObjectLinked();
3051  if ($res < 0) {
3052  $error++;
3053  }
3054  }
3055 
3056  if (!$error) {
3057  // Delete linked contacts
3058  $res = $this->delete_linked_contact();
3059  if ($res < 0) {
3060  $error++;
3061  }
3062  }
3063 
3064  // Removed extrafields of object
3065  if (!$error) {
3066  $result = $this->deleteExtraFields();
3067  if ($result < 0) {
3068  $error++;
3069  dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3070  }
3071  }
3072 
3073  // Delete main record
3074  if (!$error) {
3075  $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element." WHERE rowid = ".((int) $this->id);
3076  $res = $this->db->query($sql);
3077  if (!$res) {
3078  $error++;
3079  $this->error = $this->db->lasterror();
3080  $this->errors[] = $this->error;
3081  dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3082  }
3083  }
3084 
3085  // Delete record into ECM index and physically
3086  if (!$error) {
3087  $res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
3088  if (!$res) {
3089  $error++;
3090  }
3091  }
3092 
3093  if (!$error) {
3094  // We remove directory
3095  $ref = dol_sanitizeFileName($this->ref);
3096  if ($conf->propal->multidir_output[$this->entity] && !empty($this->ref)) {
3097  $dir = $conf->propal->multidir_output[$this->entity]."/".$ref;
3098  $file = $dir."/".$ref.".pdf";
3099  if (file_exists($file)) {
3100  dol_delete_preview($this);
3101 
3102  if (!dol_delete_file($file, 0, 0, 0, $this)) {
3103  $this->error = 'ErrorFailToDeleteFile';
3104  $this->errors[] = $this->error;
3105  $this->db->rollback();
3106  return 0;
3107  }
3108  }
3109  if (file_exists($dir)) {
3110  $res = @dol_delete_dir_recursive($dir);
3111  if (!$res) {
3112  $this->error = 'ErrorFailToDeleteDir';
3113  $this->errors[] = $this->error;
3114  $this->db->rollback();
3115  return 0;
3116  }
3117  }
3118  }
3119  }
3120 
3121  if (!$error) {
3122  dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
3123  $this->db->commit();
3124  return 1;
3125  } else {
3126  $this->db->rollback();
3127  return -1;
3128  }
3129  }
3130 
3139  public function availability($availability_id, $notrigger = 0)
3140  {
3141  global $user;
3142 
3143  if ($this->statut >= self::STATUS_DRAFT) {
3144  $error = 0;
3145 
3146  $this->db->begin();
3147 
3148  $sql = 'UPDATE '.MAIN_DB_PREFIX.'propal';
3149  $sql .= ' SET fk_availability = '.((int) $availability_id);
3150  $sql .= ' WHERE rowid='.((int) $this->id);
3151 
3152  dol_syslog(__METHOD__.' availability('.$availability_id.')', LOG_DEBUG);
3153  $resql = $this->db->query($sql);
3154  if (!$resql) {
3155  $this->errors[] = $this->db->error();
3156  $error++;
3157  }
3158 
3159  if (!$error) {
3160  $this->oldcopy = clone $this;
3161  $this->availability_id = $availability_id;
3162  }
3163 
3164  if (!$notrigger && empty($error)) {
3165  // Call trigger
3166  $result = $this->call_trigger('PROPAL_MODIFY', $user);
3167  if ($result < 0) {
3168  $error++;
3169  }
3170  // End call triggers
3171  }
3172 
3173  if (!$error) {
3174  $this->db->commit();
3175  return 1;
3176  } else {
3177  foreach ($this->errors as $errmsg) {
3178  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
3179  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3180  }
3181  $this->db->rollback();
3182  return -1 * $error;
3183  }
3184  } else {
3185  $error_str = 'Propal status do not meet requirement '.$this->statut;
3186  dol_syslog(__METHOD__.$error_str, LOG_ERR);
3187  $this->error = $error_str;
3188  $this->errors[] = $this->error;
3189  return -2;
3190  }
3191  }
3192 
3193  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3202  public function demand_reason($demand_reason_id, $notrigger = 0)
3203  {
3204  // phpcs:enable
3205  global $user;
3206 
3207  if ($this->statut >= self::STATUS_DRAFT) {
3208  $error = 0;
3209 
3210  $this->db->begin();
3211 
3212  $sql = 'UPDATE '.MAIN_DB_PREFIX.'propal';
3213  $sql .= ' SET fk_input_reason = '.((int) $demand_reason_id);
3214  $sql .= ' WHERE rowid='.((int) $this->id);
3215 
3216  dol_syslog(__METHOD__.' demand_reason('.$demand_reason_id.')', LOG_DEBUG);
3217  $resql = $this->db->query($sql);
3218  if (!$resql) {
3219  $this->errors[] = $this->db->error();
3220  $error++;
3221  }
3222 
3223  if (!$error) {
3224  $this->oldcopy = clone $this;
3225  $this->demand_reason_id = $demand_reason_id;
3226  }
3227 
3228  if (!$notrigger && empty($error)) {
3229  // Call trigger
3230  $result = $this->call_trigger('PROPAL_MODIFY', $user);
3231  if ($result < 0) {
3232  $error++;
3233  }
3234  // End call triggers
3235  }
3236 
3237  if (!$error) {
3238  $this->db->commit();
3239  return 1;
3240  } else {
3241  foreach ($this->errors as $errmsg) {
3242  dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
3243  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3244  }
3245  $this->db->rollback();
3246  return -1 * $error;
3247  }
3248  } else {
3249  $error_str = 'Propal status do not meet requirement '.$this->statut;
3250  dol_syslog(__METHOD__.$error_str, LOG_ERR);
3251  $this->error = $error_str;
3252  $this->errors[] = $this->error;
3253  return -2;
3254  }
3255  }
3256 
3257 
3264  public function info($id)
3265  {
3266  $sql = "SELECT c.rowid, ";
3267  $sql .= " c.datec, c.date_valid as datev, c.date_signature, c.date_cloture,";
3268  $sql .= " c.fk_user_author, c.fk_user_valid, c.fk_user_signature, c.fk_user_cloture";
3269  $sql .= " FROM ".MAIN_DB_PREFIX."propal as c";
3270  $sql .= " WHERE c.rowid = ".((int) $id);
3271 
3272  $result = $this->db->query($sql);
3273 
3274  if ($result) {
3275  if ($this->db->num_rows($result)) {
3276  $obj = $this->db->fetch_object($result);
3277 
3278  $this->id = $obj->rowid;
3279 
3280  $this->date_creation = $this->db->jdate($obj->datec);
3281  $this->date_validation = $this->db->jdate($obj->datev);
3282  $this->date_signature = $this->db->jdate($obj->date_signature);
3283  $this->date_cloture = $this->db->jdate($obj->date_cloture);
3284 
3285  $cuser = new User($this->db);
3286  $cuser->fetch($obj->fk_user_author);
3287  $this->user_creation = $cuser;
3288 
3289  if ($obj->fk_user_valid) {
3290  $vuser = new User($this->db);
3291  $vuser->fetch($obj->fk_user_valid);
3292  $this->user_validation = $vuser;
3293  }
3294 
3295  if ($obj->fk_user_signature) {
3296  $user_signature = new User($this->db);
3297  $user_signature->fetch($obj->fk_user_signature);
3298  $this->user_signature = $user_signature;
3299  }
3300 
3301  if ($obj->fk_user_cloture) {
3302  $cluser = new User($this->db);
3303  $cluser->fetch($obj->fk_user_cloture);
3304  $this->user_cloture = $cluser;
3305  }
3306  }
3307  $this->db->free($result);
3308  } else {
3309  dol_print_error($this->db);
3310  }
3311  }
3312 
3313 
3320  public function getLibStatut($mode = 0)
3321  {
3322  return $this->LibStatut($this->statut, $mode);
3323  }
3324 
3325  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3333  public function LibStatut($status, $mode = 1)
3334  {
3335  // phpcs:enable
3336  global $conf;
3337 
3338  // Init/load array of translation of status
3339  if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
3340  global $langs;
3341  $langs->load("propal");
3342  $this->labelStatus[0] = $langs->transnoentitiesnoconv("PropalStatusDraft");
3343  $this->labelStatus[1] = $langs->transnoentitiesnoconv("PropalStatusValidated");
3344  $this->labelStatus[2] = $langs->transnoentitiesnoconv("PropalStatusSigned");
3345  $this->labelStatus[3] = $langs->transnoentitiesnoconv("PropalStatusNotSigned");
3346  $this->labelStatus[4] = $langs->transnoentitiesnoconv("PropalStatusBilled");
3347  $this->labelStatusShort[0] = $langs->transnoentitiesnoconv("PropalStatusDraftShort");
3348  $this->labelStatusShort[1] = $langs->transnoentitiesnoconv("PropalStatusValidatedShort");
3349  $this->labelStatusShort[2] = $langs->transnoentitiesnoconv("PropalStatusSignedShort");
3350  $this->labelStatusShort[3] = $langs->transnoentitiesnoconv("PropalStatusNotSignedShort");
3351  $this->labelStatusShort[4] = $langs->transnoentitiesnoconv("PropalStatusBilledShort");
3352  }
3353 
3354  $statusType = '';
3355  if ($status == self::STATUS_DRAFT) {
3356  $statusType = 'status0';
3357  } elseif ($status == self::STATUS_VALIDATED) {
3358  $statusType = 'status1';
3359  } elseif ($status == self::STATUS_SIGNED) {
3360  $statusType = 'status4';
3361  } elseif ($status == self::STATUS_NOTSIGNED) {
3362  $statusType = 'status9';
3363  } elseif ($status == self::STATUS_BILLED) {
3364  $statusType = 'status6';
3365  }
3366 
3367  return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
3368  }
3369 
3370 
3371  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3379  public function load_board($user, $mode)
3380  {
3381  // phpcs:enable
3382  global $conf, $langs;
3383 
3384  $clause = " WHERE";
3385 
3386  $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.fin_validite as datefin, p.total_ht";
3387  $sql .= " FROM ".MAIN_DB_PREFIX."propal as p";
3388  if (empty($user->rights->societe->client->voir) && !$user->socid) {
3389  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
3390  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3391  $clause = " AND";
3392  }
3393  $sql .= $clause." p.entity IN (".getEntity('propal').")";
3394  if ($mode == 'opened') {
3395  $sql .= " AND p.fk_statut = ".self::STATUS_VALIDATED;
3396  }
3397  if ($mode == 'signed') {
3398  $sql .= " AND p.fk_statut = ".self::STATUS_SIGNED;
3399  }
3400  if ($user->socid) {
3401  $sql .= " AND p.fk_soc = ".((int) $user->socid);
3402  }
3403 
3404  $resql = $this->db->query($sql);
3405  if ($resql) {
3406  $langs->load("propal");
3407  $now = dol_now();
3408 
3409  $delay_warning = 0;
3410  $status = 0;
3411  $label = $labelShort = '';
3412  if ($mode == 'opened') {
3413  $delay_warning = $conf->propal->cloture->warning_delay;
3414  $status = self::STATUS_VALIDATED;
3415  $label = $langs->transnoentitiesnoconv("PropalsToClose");
3416  $labelShort = $langs->transnoentitiesnoconv("ToAcceptRefuse");
3417  }
3418  if ($mode == 'signed') {
3419  $delay_warning = $conf->propal->facturation->warning_delay;
3420  $status = self::STATUS_SIGNED;
3421  $label = $langs->trans("PropalsToBill"); // We set here bill but may be billed or ordered
3422  $labelShort = $langs->trans("ToBill");
3423  }
3424 
3425  $response = new WorkboardResponse();
3426  $response->warning_delay = $delay_warning / 60 / 60 / 24;
3427  $response->label = $label;
3428  $response->labelShort = $labelShort;
3429  $response->url = DOL_URL_ROOT.'/comm/propal/list.php?search_status='.$status.'&mainmenu=commercial&leftmenu=propals';
3430  $response->url_late = DOL_URL_ROOT.'/comm/propal/list.php?search_status='.$status.'&mainmenu=commercial&leftmenu=propals&sortfield=p.datep&sortorder=asc';
3431  $response->img = img_object('', "propal");
3432 
3433  // This assignment in condition is not a bug. It allows walking the results.
3434  while ($obj = $this->db->fetch_object($resql)) {
3435  $response->nbtodo++;
3436  $response->total += $obj->total_ht;
3437 
3438  if ($mode == 'opened') {
3439  $datelimit = $this->db->jdate($obj->datefin);
3440  if ($datelimit < ($now - $delay_warning)) {
3441  $response->nbtodolate++;
3442  }
3443  }
3444  // TODO Definir regle des propales a facturer en retard
3445  // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
3446  }
3447 
3448  return $response;
3449  } else {
3450  $this->error = $this->db->error();
3451  return -1;
3452  }
3453  }
3454 
3455 
3463  public function initAsSpecimen()
3464  {
3465  global $conf, $langs;
3466 
3467  // Load array of products prodids
3468  $num_prods = 0;
3469  $prodids = array();
3470  $sql = "SELECT rowid";
3471  $sql .= " FROM ".MAIN_DB_PREFIX."product";
3472  $sql .= " WHERE entity IN (".getEntity('product').")";
3473  $sql .= $this->db->plimit(100);
3474 
3475  $resql = $this->db->query($sql);
3476  if ($resql) {
3477  $num_prods = $this->db->num_rows($resql);
3478  $i = 0;
3479  while ($i < $num_prods) {
3480  $i++;
3481  $row = $this->db->fetch_row($resql);
3482  $prodids[$i] = $row[0];
3483  }
3484  }
3485 
3486  // Initialise parametres
3487  $this->id = 0;
3488  $this->ref = 'SPECIMEN';
3489  $this->ref_client = 'NEMICEPS';
3490  $this->specimen = 1;
3491  $this->socid = 1;
3492  $this->date = time();
3493  $this->fin_validite = $this->date + 3600 * 24 * 30;
3494  $this->cond_reglement_id = 1;
3495  $this->cond_reglement_code = 'RECEP';
3496  $this->mode_reglement_id = 7;
3497  $this->mode_reglement_code = 'CHQ';
3498  $this->availability_id = 1;
3499  $this->availability_code = 'AV_NOW';
3500  $this->demand_reason_id = 1;
3501  $this->demand_reason_code = 'SRC_00';
3502  $this->note_public = 'This is a comment (public)';
3503  $this->note_private = 'This is a comment (private)';
3504 
3505  $this->multicurrency_tx = 1;
3506  $this->multicurrency_code = $conf->currency;
3507 
3508  // Lines
3509  $nbp = 5;
3510  $xnbp = 0;
3511  while ($xnbp < $nbp) {
3512  $line = new PropaleLigne($this->db);
3513  $line->desc = $langs->trans("Description")." ".$xnbp;
3514  $line->qty = 1;
3515  $line->subprice = 100;
3516  $line->price = 100;
3517  $line->tva_tx = 20;
3518  $line->localtax1_tx = 0;
3519  $line->localtax2_tx = 0;
3520  if ($xnbp == 2) {
3521  $line->total_ht = 50;
3522  $line->total_ttc = 60;
3523  $line->total_tva = 10;
3524  $line->remise_percent = 50;
3525  } else {
3526  $line->total_ht = 100;
3527  $line->total_ttc = 120;
3528  $line->total_tva = 20;
3529  $line->remise_percent = 00;
3530  }
3531 
3532  if ($num_prods > 0) {
3533  $prodid = mt_rand(1, $num_prods);
3534  $line->fk_product = $prodids[$prodid];
3535  $line->product_ref = 'SPECIMEN';
3536  }
3537 
3538  $this->lines[$xnbp] = $line;
3539 
3540  $this->total_ht += $line->total_ht;
3541  $this->total_tva += $line->total_tva;
3542  $this->total_ttc += $line->total_ttc;
3543 
3544  $xnbp++;
3545  }
3546  }
3547 
3548  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3554  public function load_state_board()
3555  {
3556  // phpcs:enable
3557  global $user;
3558 
3559  $this->nb = array();
3560  $clause = "WHERE";
3561 
3562  $sql = "SELECT count(p.rowid) as nb";
3563  $sql .= " FROM ".MAIN_DB_PREFIX."propal as p";
3564  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
3565  if (empty($user->rights->societe->client->voir) && !$user->socid) {
3566  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3567  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3568  $clause = "AND";
3569  }
3570  $sql .= " ".$clause." p.entity IN (".getEntity('propal').")";
3571 
3572  $resql = $this->db->query($sql);
3573  if ($resql) {
3574  // This assignment in condition is not a bug. It allows walking the results.
3575  while ($obj = $this->db->fetch_object($resql)) {
3576  $this->nb["proposals"] = $obj->nb;
3577  }
3578  $this->db->free($resql);
3579  return 1;
3580  } else {
3581  dol_print_error($this->db);
3582  $this->error = $this->db->error();
3583  return -1;
3584  }
3585  }
3586 
3587 
3595  public function getNextNumRef($soc)
3596  {
3597  global $conf, $langs;
3598  $langs->load("propal");
3599 
3600  $classname = $conf->global->PROPALE_ADDON;
3601 
3602  if (!empty($classname)) {
3603  $mybool = false;
3604 
3605  $file = $classname.".php";
3606 
3607  // Include file with class
3608  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
3609  foreach ($dirmodels as $reldir) {
3610  $dir = dol_buildpath($reldir."core/modules/propale/");
3611 
3612  // Load file with numbering class (if found)
3613  $mybool |= @include_once $dir.$file;
3614  }
3615 
3616  if (!$mybool) {
3617  dol_print_error('', "Failed to include file ".$file);
3618  return '';
3619  }
3620 
3621  $obj = new $classname();
3622  $numref = "";
3623  $numref = $obj->getNextValue($soc, $this);
3624 
3625  if ($numref != "") {
3626  return $numref;
3627  } else {
3628  $this->error = $obj->error;
3629  //dol_print_error($db,"Propale::getNextNumRef ".$obj->error);
3630  return "";
3631  }
3632  } else {
3633  $langs->load("errors");
3634  print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("Proposal"));
3635  return "";
3636  }
3637  }
3638 
3650  public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = -1)
3651  {
3652  global $langs, $conf, $user, $hookmanager;
3653 
3654  if (!empty($conf->dol_no_mouse_hover)) {
3655  $notooltip = 1; // Force disable tooltips
3656  }
3657 
3658  $result = '';
3659  $label = '';
3660  $url = '';
3661 
3662  if ($user->rights->propal->lire) {
3663  $label = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("Proposal").'</u>';
3664  if (isset($this->statut)) {
3665  $label .= ' '.$this->getLibStatut(5);
3666  }
3667  if (!empty($this->ref)) {
3668  $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
3669  }
3670  if (!empty($this->ref_client)) {
3671  $label .= '<br><b>'.$langs->trans('RefCustomer').':</b> '.$this->ref_client;
3672  }
3673  if (!empty($this->total_ht)) {
3674  $label .= '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3675  }
3676  if (!empty($this->total_tva)) {
3677  $label .= '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
3678  }
3679  if (!empty($this->total_ttc)) {
3680  $label .= '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3681  }
3682  if (!empty($this->date)) {
3683  $label .= '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
3684  }
3685  if (!empty($this->delivery_date)) {
3686  $label .= '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
3687  }
3688 
3689  if ($option == '') {
3690  $url = DOL_URL_ROOT.'/comm/propal/card.php?id='.$this->id.$get_params;
3691  } elseif ($option == 'compta') { // deprecated
3692  $url = DOL_URL_ROOT.'/comm/propal/card.php?id='.$this->id.$get_params;
3693  } elseif ($option == 'expedition') {
3694  $url = DOL_URL_ROOT.'/expedition/propal.php?id='.$this->id.$get_params;
3695  } elseif ($option == 'document') {
3696  $url = DOL_URL_ROOT.'/comm/propal/document.php?id='.$this->id.$get_params;
3697  }
3698 
3699  if ($option != 'nolink') {
3700  // Add param to save lastsearch_values or not
3701  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
3702  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
3703  $add_save_lastsearch_values = 1;
3704  }
3705  if ($add_save_lastsearch_values) {
3706  $url .= '&save_lastsearch_values=1';
3707  }
3708  }
3709  }
3710 
3711  $linkclose = '';
3712  if (empty($notooltip) && $user->rights->propal->lire) {
3713  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
3714  $label = $langs->trans("Proposal");
3715  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
3716  }
3717  $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
3718  $linkclose .= ' class="classfortooltip"';
3719  }
3720 
3721  $linkstart = '<a href="'.$url.'"';
3722  $linkstart .= $linkclose.'>';
3723  $linkend = '</a>';
3724 
3725  $result .= $linkstart;
3726  if ($withpicto) {
3727  $result .= img_object(($notooltip ? '' : $label), $this->picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
3728  }
3729  if ($withpicto != 2) {
3730  $result .= $this->ref;
3731  }
3732  $result .= $linkend;
3733 
3734  if ($addlinktonotes >= 0) {
3735  $txttoshow = '';
3736 
3737  if ($addlinktonotes == 0) {
3738  if (!empty($this->note_private) || !empty($this->note_public)) {
3739  $txttoshow = $langs->trans('ViewPrivateNote');
3740  }
3741  } elseif ($addlinktonotes == 1) {
3742  if (!empty($this->note_private)) {
3743  $txttoshow .= ($user->socid > 0 ? '' : dol_string_nohtmltag($this->note_private, 1));
3744  }
3745  } elseif ($addlinktonotes == 2) {
3746  if (!empty($this->note_public)) {
3747  $txttoshow .= dol_string_nohtmltag($this->note_public, 1);
3748  }
3749  } elseif ($addlinktonotes == 3) {
3750  if ($user->socid > 0) {
3751  if (!empty($this->note_public)) {
3752  $txttoshow .= dol_string_nohtmltag($this->note_public, 1);
3753  }
3754  } else {
3755  if (!empty($this->note_public)) {
3756  $txttoshow .= dol_string_nohtmltag($this->note_public, 1);
3757  }
3758  if (!empty($this->note_private)) {
3759  if (!empty($txttoshow)) {
3760  $txttoshow .= '<br><br>';
3761  }
3762  $txttoshow .= dol_string_nohtmltag($this->note_private, 1);
3763  }
3764  }
3765  }
3766 
3767  if ($txttoshow) {
3768  $result .= ' <span class="note inline-block">';
3769  $result .= '<a href="'.DOL_URL_ROOT.'/comm/propal/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($txttoshow).'">';
3770  $result .= img_picto('', 'note');
3771  $result .= '</a>';
3772  $result .= '</span>';
3773  }
3774  }
3775 
3776  global $action;
3777  $hookmanager->initHooks(array($this->element . 'dao'));
3778  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
3779  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3780  if ($reshook > 0) {
3781  $result = $hookmanager->resPrint;
3782  } else {
3783  $result .= $hookmanager->resPrint;
3784  }
3785  return $result;
3786  }
3787 
3794  public function getLinesArray($filters = '')
3795  {
3796  return $this->fetch_lines(0, 0, $filters);
3797  }
3798 
3810  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3811  {
3812  global $conf, $langs;
3813 
3814  $langs->load("propale");
3815  $outputlangs->load("products");
3816 
3817  if (!dol_strlen($modele)) {
3818  $modele = 'azur';
3819 
3820  if ($this->model_pdf) {
3821  $modele = $this->model_pdf;
3822  } elseif (!empty($conf->global->PROPALE_ADDON_PDF)) {
3823  $modele = $conf->global->PROPALE_ADDON_PDF;
3824  }
3825  }
3826 
3827  $modelpath = "core/modules/propale/doc/";
3828 
3829  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3830  }
3831 
3840  public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
3841  {
3842  $tables = array(
3843  'propal'
3844  );
3845 
3846  return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
3847  }
3848 
3857  public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3858  {
3859  $tables = array(
3860  'propaldet'
3861  );
3862 
3863  return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3864  }
3865 }
3866 
3871 {
3875  public $element = 'propaldet';
3876 
3880  public $table_element = 'propaldet';
3881 
3882  public $oldline;
3883 
3884  // From llx_propaldet
3885  public $fk_propal;
3886  public $fk_parent_line;
3887  public $desc; // Description ligne
3888  public $fk_product; // Id produit predefini
3899  public $product_type = Product::TYPE_PRODUCT;
3900 
3901  public $qty;
3902 
3903  public $tva_tx;
3904  public $vat_src_code;
3905 
3906  public $subprice;
3907  public $remise_percent;
3908  public $fk_remise_except;
3909 
3910  public $rang = 0;
3911 
3912  public $fk_fournprice;
3913  public $pa_ht;
3914  public $marge_tx;
3915  public $marque_tx;
3916 
3917  public $special_code; // Tag for special lines (exlusive tags)
3918  // 1: frais de port
3919  // 2: ecotaxe
3920  // 3: option line (when qty = 0)
3921 
3922  public $info_bits = 0; // Some other info:
3923  // Bit 0: 0 si TVA normal - 1 si TVA NPR
3924  // Bit 1: 0 ligne normale - 1 si ligne de remise fixe
3925 
3926  public $total_ht; // Total HT de la ligne toute quantite et incluant la remise ligne
3927  public $total_tva; // Total TVA de la ligne toute quantite et incluant la remise ligne
3928  public $total_ttc; // Total TTC de la ligne toute quantite et incluant la remise ligne
3929 
3934  public $remise;
3939  public $price;
3940 
3941  // From llx_product
3946  public $ref;
3951  public $product_ref;
3956  public $libelle;
3961  public $label;
3966  public $product_label;
3971  public $product_desc;
3972 
3977  public $product_tobatch;
3978 
3983  public $product_barcode;
3984 
3985  public $localtax1_tx; // Local tax 1
3986  public $localtax2_tx; // Local tax 2
3987  public $localtax1_type; // Local tax 1 type
3988  public $localtax2_type; // Local tax 2 type
3989  public $total_localtax1; // Line total local tax 1
3990  public $total_localtax2; // Line total local tax 2
3991 
3992  public $date_start;
3993  public $date_end;
3994 
3995  public $skip_update_total; // Skip update price total for special lines
3996 
3997  // Multicurrency
3998  public $fk_multicurrency;
3999  public $multicurrency_code;
4000  public $multicurrency_subprice;
4001  public $multicurrency_total_ht;
4002  public $multicurrency_total_tva;
4003  public $multicurrency_total_ttc;
4004 
4005 
4011  public function __construct($db)
4012  {
4013  $this->db = $db;
4014  }
4015 
4022  public function fetch($rowid)
4023  {
4024  $sql = 'SELECT pd.rowid, pd.fk_propal, pd.fk_parent_line, pd.fk_product, pd.label as custom_label, pd.description, pd.price, pd.qty, pd.vat_src_code, pd.tva_tx,';
4025  $sql .= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice,';
4026  $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,';
4027  $sql .= ' pd.fk_unit,';
4028  $sql .= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
4029  $sql .= ' pd.fk_multicurrency, pd.multicurrency_code, pd.multicurrency_subprice, pd.multicurrency_total_ht, pd.multicurrency_total_tva, pd.multicurrency_total_ttc,';
4030  $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
4031  $sql .= ' pd.date_start, pd.date_end, pd.product_type';
4032  $sql .= ' FROM '.MAIN_DB_PREFIX.'propaldet as pd';
4033  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
4034  $sql .= ' WHERE pd.rowid = '.((int) $rowid);
4035 
4036  $result = $this->db->query($sql);
4037  if ($result) {
4038  $objp = $this->db->fetch_object($result);
4039 
4040  if ($objp) {
4041  $this->id = $objp->rowid;
4042  $this->rowid = $objp->rowid; // deprecated
4043  $this->fk_propal = $objp->fk_propal;
4044  $this->fk_parent_line = $objp->fk_parent_line;
4045  $this->label = $objp->custom_label;
4046  $this->desc = $objp->description;
4047  $this->qty = $objp->qty;
4048  $this->price = $objp->price; // deprecated
4049  $this->subprice = $objp->subprice;
4050  $this->vat_src_code = $objp->vat_src_code;
4051  $this->tva_tx = $objp->tva_tx;
4052  $this->remise = $objp->remise; // deprecated
4053  $this->remise_percent = $objp->remise_percent;
4054  $this->fk_remise_except = $objp->fk_remise_except;
4055  $this->fk_product = $objp->fk_product;
4056  $this->info_bits = $objp->info_bits;
4057 
4058  $this->total_ht = $objp->total_ht;
4059  $this->total_tva = $objp->total_tva;
4060  $this->total_ttc = $objp->total_ttc;
4061 
4062  $this->fk_fournprice = $objp->fk_fournprice;
4063 
4064  $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
4065  $this->pa_ht = $marginInfos[0];
4066  $this->marge_tx = $marginInfos[1];
4067  $this->marque_tx = $marginInfos[2];
4068 
4069  $this->special_code = $objp->special_code;
4070  $this->product_type = $objp->product_type;
4071  $this->rang = $objp->rang;
4072 
4073  $this->ref = $objp->product_ref; // deprecated
4074  $this->product_ref = $objp->product_ref;
4075  $this->libelle = $objp->product_label; // deprecated
4076  $this->product_label = $objp->product_label;
4077  $this->product_desc = $objp->product_desc;
4078  $this->fk_unit = $objp->fk_unit;
4079 
4080  $this->date_start = $this->db->jdate($objp->date_start);
4081  $this->date_end = $this->db->jdate($objp->date_end);
4082 
4083  // Multicurrency
4084  $this->fk_multicurrency = $objp->fk_multicurrency;
4085  $this->multicurrency_code = $objp->multicurrency_code;
4086  $this->multicurrency_subprice = $objp->multicurrency_subprice;
4087  $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
4088  $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
4089  $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
4090 
4091  $this->fetch_optionals();
4092 
4093  $this->db->free($result);
4094 
4095  return 1;
4096  } else {
4097  return 0;
4098  }
4099  } else {
4100  return -1;
4101  }
4102  }
4103 
4110  public function insert($notrigger = 0)
4111  {
4112  global $conf, $user;
4113 
4114  $error = 0;
4115 
4116  dol_syslog(get_class($this)."::insert rang=".$this->rang);
4117 
4118  $pa_ht_isemptystring = (empty($this->pa_ht) && $this->pa_ht == ''); // If true, we can use a default value. If this->pa_ht = '0', we must use '0'.
4119 
4120  // Clean parameters
4121  if (empty($this->tva_tx)) {
4122  $this->tva_tx = 0;
4123  }
4124  if (empty($this->localtax1_tx)) {
4125  $this->localtax1_tx = 0;
4126  }
4127  if (empty($this->localtax2_tx)) {
4128  $this->localtax2_tx = 0;
4129  }
4130  if (empty($this->localtax1_type)) {
4131  $this->localtax1_type = 0;
4132  }
4133  if (empty($this->localtax2_type)) {
4134  $this->localtax2_type = 0;
4135  }
4136  if (empty($this->total_localtax1)) {
4137  $this->total_localtax1 = 0;
4138  }
4139  if (empty($this->total_localtax2)) {
4140  $this->total_localtax2 = 0;
4141  }
4142  if (empty($this->rang)) {
4143  $this->rang = 0;
4144  }
4145  if (empty($this->remise_percent) || !is_numeric($this->remise_percent)) {
4146  $this->remise_percent = 0;
4147  }
4148  if (empty($this->info_bits)) {
4149  $this->info_bits = 0;
4150  }
4151  if (empty($this->special_code)) {
4152  $this->special_code = 0;
4153  }
4154  if (empty($this->fk_parent_line)) {
4155  $this->fk_parent_line = 0;
4156  }
4157  if (empty($this->fk_fournprice)) {
4158  $this->fk_fournprice = 0;
4159  }
4160  if (!is_numeric($this->qty)) {
4161  $this->qty = 0;
4162  }
4163  if (empty($this->pa_ht)) {
4164  $this->pa_ht = 0;
4165  }
4166  if (empty($this->multicurrency_subprice)) {
4167  $this->multicurrency_subprice = 0;
4168  }
4169  if (empty($this->multicurrency_total_ht)) {
4170  $this->multicurrency_total_ht = 0;
4171  }
4172  if (empty($this->multicurrency_total_tva)) {
4173  $this->multicurrency_total_tva = 0;
4174  }
4175  if (empty($this->multicurrency_total_ttc)) {
4176  $this->multicurrency_total_ttc = 0;
4177  }
4178 
4179  // if buy price not defined, define buyprice as configured in margin admin
4180  if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
4181  if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0) {
4182  return $result;
4183  } else {
4184  $this->pa_ht = $result;
4185  }
4186  }
4187 
4188  // Check parameters
4189  if ($this->product_type < 0) {
4190  return -1;
4191  }
4192 
4193  $this->db->begin();
4194 
4195  // Insert line into database
4196  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'propaldet';
4197  $sql .= ' (fk_propal, fk_parent_line, label, description, fk_product, product_type,';
4198  $sql .= ' fk_remise_except, qty, vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
4199  $sql .= ' subprice, remise_percent, ';
4200  $sql .= ' info_bits, ';
4201  $sql .= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
4202  $sql .= ' fk_unit,';
4203  $sql .= ' date_start, date_end';
4204  $sql .= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc)';
4205  $sql .= " VALUES (".$this->fk_propal.",";
4206  $sql .= " ".($this->fk_parent_line > 0 ? "'".$this->db->escape($this->fk_parent_line)."'" : "null").",";
4207  $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
4208  $sql .= " '".$this->db->escape($this->desc)."',";
4209  $sql .= " ".($this->fk_product ? "'".$this->db->escape($this->fk_product)."'" : "null").",";
4210  $sql .= " '".$this->db->escape($this->product_type)."',";
4211  $sql .= " ".($this->fk_remise_except ? "'".$this->db->escape($this->fk_remise_except)."'" : "null").",";
4212  $sql .= " ".price2num($this->qty, 'MS').",";
4213  $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
4214  $sql .= " ".price2num($this->tva_tx).",";
4215  $sql .= " ".price2num($this->localtax1_tx).",";
4216  $sql .= " ".price2num($this->localtax2_tx).",";
4217  $sql .= " '".$this->db->escape($this->localtax1_type)."',";
4218  $sql .= " '".$this->db->escape($this->localtax2_type)."',";
4219  $sql .= " ".(price2num($this->subprice) !== '' ? price2num($this->subprice, 'MU') : "null").",";
4220  $sql .= " ".price2num($this->remise_percent, 3).",";
4221  $sql .= " ".(isset($this->info_bits) ? ((int) $this->info_bits) : "null").",";
4222  $sql .= " ".price2num($this->total_ht, 'MT').",";
4223  $sql .= " ".price2num($this->total_tva, 'MT').",";
4224  $sql .= " ".price2num($this->total_localtax1, 'MT').",";
4225  $sql .= " ".price2num($this->total_localtax2, 'MT').",";
4226  $sql .= " ".price2num($this->total_ttc, 'MT').",";
4227  $sql .= " ".(!empty($this->fk_fournprice) ? "'".$this->db->escape($this->fk_fournprice)."'" : "null").",";
4228  $sql .= " ".(isset($this->pa_ht) ? "'".price2num($this->pa_ht)."'" : "null").",";
4229  $sql .= ' '.((int) $this->special_code).',';
4230  $sql .= ' '.((int) $this->rang).',';
4231  $sql .= ' '.(empty($this->fk_unit) ? 'NULL' : ((int) $this->fk_unit)).',';
4232  $sql .= " ".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null").',';
4233  $sql .= " ".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
4234  $sql .= ", ".($this->fk_multicurrency > 0 ? ((int) $this->fk_multicurrency) : 'null');
4235  $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
4236  $sql .= ", ".price2num($this->multicurrency_subprice, 'CU');
4237  $sql .= ", ".price2num($this->multicurrency_total_ht, 'CT');
4238  $sql .= ", ".price2num($this->multicurrency_total_tva, 'CT');
4239  $sql .= ", ".price2num($this->multicurrency_total_ttc, 'CT');
4240  $sql .= ')';
4241 
4242  dol_syslog(get_class($this).'::insert', LOG_DEBUG);
4243  $resql = $this->db->query($sql);
4244  if ($resql) {
4245  $this->rowid = $this->db->last_insert_id(MAIN_DB_PREFIX.'propaldet');
4246 
4247  if (!$error) {
4248  $this->id = $this->rowid;
4249  $result = $this->insertExtraFields();
4250  if ($result < 0) {
4251  $error++;
4252  }
4253  }
4254 
4255  if (!$error && !$notrigger) {
4256  // Call trigger
4257  $result = $this->call_trigger('LINEPROPAL_INSERT', $user);
4258  if ($result < 0) {
4259  $this->db->rollback();
4260  return -1;
4261  }
4262  // End call triggers
4263  }
4264 
4265  $this->db->commit();
4266  return 1;
4267  } else {
4268  $this->error = $this->db->error()." sql=".$sql;
4269  $this->db->rollback();
4270  return -1;
4271  }
4272  }
4273 
4281  public function delete(User $user, $notrigger = 0)
4282  {
4283  global $conf;
4284 
4285  $error = 0;
4286  $this->db->begin();
4287 
4288  if (!$notrigger) {
4289  // Call trigger
4290  $result = $this->call_trigger('LINEPROPAL_DELETE', $user);
4291  if ($result < 0) {
4292  $error++;
4293  }
4294  }
4295  // End call triggers
4296 
4297  if (!$error) {
4298  $sql = "DELETE FROM " . MAIN_DB_PREFIX . "propaldet WHERE rowid = " . ((int) $this->rowid);
4299  dol_syslog("PropaleLigne::delete", LOG_DEBUG);
4300  if ($this->db->query($sql)) {
4301  // Remove extrafields
4302  if (!$error) {
4303  $this->id = $this->rowid;
4304  $result = $this->deleteExtraFields();
4305  if ($result < 0) {
4306  $error++;
4307  dol_syslog(get_class($this) . "::delete error -4 " . $this->error, LOG_ERR);
4308  }
4309  }
4310  } else {
4311  $this->error = $this->db->error() . " sql=" . $sql;
4312  $error++;
4313  }
4314  }
4315 
4316  if ($error) {
4317  $this->db->rollback();
4318  return -1;
4319  } else {
4320  $this->db->commit();
4321  return 1;
4322  }
4323  }
4324 
4331  public function update($notrigger = 0)
4332  {
4333  global $conf, $user;
4334 
4335  $error = 0;
4336 
4337  $pa_ht_isemptystring = (empty($this->pa_ht) && $this->pa_ht == ''); // If true, we can use a default value. If this->pa_ht = '0', we must use '0'.
4338 
4339  if (empty($this->id) && !empty($this->rowid)) {
4340  $this->id = $this->rowid;
4341  }
4342 
4343  // Clean parameters
4344  if (empty($this->tva_tx)) {
4345  $this->tva_tx = 0;
4346  }
4347  if (empty($this->localtax1_tx)) {
4348  $this->localtax1_tx = 0;
4349  }
4350  if (empty($this->localtax2_tx)) {
4351  $this->localtax2_tx = 0;
4352  }
4353  if (empty($this->total_localtax1)) {
4354  $this->total_localtax1 = 0;
4355  }
4356  if (empty($this->total_localtax2)) {
4357  $this->total_localtax2 = 0;
4358  }
4359  if (empty($this->localtax1_type)) {
4360  $this->localtax1_type = 0;
4361  }
4362  if (empty($this->localtax2_type)) {
4363  $this->localtax2_type = 0;
4364  }
4365  if (empty($this->marque_tx)) {
4366  $this->marque_tx = 0;
4367  }
4368  if (empty($this->marge_tx)) {
4369  $this->marge_tx = 0;
4370  }
4371  if (empty($this->price)) {
4372  $this->price = 0; // TODO A virer
4373  }
4374  if (empty($this->remise_percent)) {
4375  $this->remise_percent = 0;
4376  }
4377  if (empty($this->info_bits)) {
4378  $this->info_bits = 0;
4379  }
4380  if (empty($this->special_code)) {
4381  $this->special_code = 0;
4382  }
4383  if (empty($this->fk_parent_line)) {
4384  $this->fk_parent_line = 0;
4385  }
4386  if (empty($this->fk_fournprice)) {
4387  $this->fk_fournprice = 0;
4388  }
4389  if (empty($this->subprice)) {
4390  $this->subprice = 0;
4391  }
4392  if (empty($this->pa_ht)) {
4393  $this->pa_ht = 0;
4394  }
4395 
4396  // if buy price not defined, define buyprice as configured in margin admin
4397  if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
4398  if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0) {
4399  return $result;
4400  } else {
4401  $this->pa_ht = $result;
4402  }
4403  }
4404 
4405  $this->db->begin();
4406 
4407  // Mise a jour ligne en base
4408  $sql = "UPDATE ".MAIN_DB_PREFIX."propaldet SET";
4409  $sql .= " description='".$this->db->escape($this->desc)."'";
4410  $sql .= ", label=".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
4411  $sql .= ", product_type=".$this->product_type;
4412  $sql .= ", vat_src_code = '".(empty($this->vat_src_code) ? '' : $this->vat_src_code)."'";
4413  $sql .= ", tva_tx='".price2num($this->tva_tx)."'";
4414  $sql .= ", localtax1_tx=".price2num($this->localtax1_tx);
4415  $sql .= ", localtax2_tx=".price2num($this->localtax2_tx);
4416  $sql .= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4417  $sql .= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4418  $sql .= ", qty='".price2num($this->qty)."'";
4419  $sql .= ", subprice=".price2num($this->subprice)."";
4420  $sql .= ", remise_percent=".price2num($this->remise_percent)."";
4421  $sql .= ", price=".price2num($this->price).""; // TODO A virer
4422  $sql .= ", remise=".price2num($this->remise).""; // TODO A virer
4423  $sql .= ", info_bits='".$this->db->escape($this->info_bits)."'";
4424  if (empty($this->skip_update_total)) {
4425  $sql .= ", total_ht=".price2num($this->total_ht)."";
4426  $sql .= ", total_tva=".price2num($this->total_tva)."";
4427  $sql .= ", total_ttc=".price2num($this->total_ttc)."";
4428  $sql .= ", total_localtax1=".price2num($this->total_localtax1)."";
4429  $sql .= ", total_localtax2=".price2num($this->total_localtax2)."";
4430  }
4431  $sql .= ", fk_product_fournisseur_price=".(!empty($this->fk_fournprice) ? "'".$this->db->escape($this->fk_fournprice)."'" : "null");
4432  $sql .= ", buy_price_ht=".price2num($this->pa_ht);
4433  if (strlen($this->special_code)) {
4434  $sql .= ", special_code=".$this->special_code;
4435  }
4436  $sql .= ", fk_parent_line=".($this->fk_parent_line > 0 ? $this->fk_parent_line : "null");
4437  if (!empty($this->rang)) {
4438  $sql .= ", rang=".((int) $this->rang);
4439  }
4440  $sql .= ", date_start=".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null");
4441  $sql .= ", date_end=".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
4442  $sql .= ", fk_unit=".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4443 
4444  // Multicurrency
4445  $sql .= ", multicurrency_subprice=".price2num($this->multicurrency_subprice)."";
4446  $sql .= ", multicurrency_total_ht=".price2num($this->multicurrency_total_ht)."";
4447  $sql .= ", multicurrency_total_tva=".price2num($this->multicurrency_total_tva)."";
4448  $sql .= ", multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc)."";
4449 
4450  $sql .= " WHERE rowid = ".((int) $this->id);
4451 
4452  dol_syslog(get_class($this)."::update", LOG_DEBUG);
4453  $resql = $this->db->query($sql);
4454  if ($resql) {
4455  if (!$error) {
4456  $result = $this->insertExtraFields();
4457  if ($result < 0) {
4458  $error++;
4459  }
4460  }
4461 
4462  if (!$error && !$notrigger) {
4463  // Call trigger
4464  $result = $this->call_trigger('LINEPROPAL_MODIFY', $user);
4465  if ($result < 0) {
4466  $this->db->rollback();
4467  return -1;
4468  }
4469  // End call triggers
4470  }
4471 
4472  $this->db->commit();
4473  return 1;
4474  } else {
4475  $this->error = $this->db->error();
4476  $this->db->rollback();
4477  return -2;
4478  }
4479  }
4480 
4481  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4488  public function update_total()
4489  {
4490  // phpcs:enable
4491  $this->db->begin();
4492 
4493  // Mise a jour ligne en base
4494  $sql = "UPDATE ".MAIN_DB_PREFIX."propaldet SET";
4495  $sql .= " total_ht=".price2num($this->total_ht, 'MT')."";
4496  $sql .= ",total_tva=".price2num($this->total_tva, 'MT')."";
4497  $sql .= ",total_ttc=".price2num($this->total_ttc, 'MT')."";
4498  $sql .= " WHERE rowid = ".((int) $this->rowid);
4499 
4500  dol_syslog("PropaleLigne::update_total", LOG_DEBUG);
4501 
4502  $resql = $this->db->query($sql);
4503  if ($resql) {
4504  $this->db->commit();
4505  return 1;
4506  } else {
4507  $this->error = $this->db->error();
4508  $this->db->rollback();
4509  return -2;
4510  }
4511  }
4512 }
if(!function_exists('dol_getprefix')) dol_include_once($relpath, $classname= '')
Make an include_once using default root and alternate root if it fails.
deleteline($lineid)
Delete detail line.
File of class to manage predefined price products or services by customer.
GETPOST($paramname, $check= 'alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
createFromClone(User $user, $socid=0, $forceentity=null, $update_prices=false)
Load an object from its id and create a new one in database.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto= 'UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
set_date_livraison($user, $delivery_date, $notrigger=0)
Set delivery date.
getInvoiceArrayList()
Returns an array with the numbers of related invoices.
set_ref_client($user, $ref_client, $notrigger=0)
Set customer reference number.
fetch($rowid)
Retrieve the propal line object.
getMarginInfos($pvht, $remise_percent, $tva_tx, $localtax1_tx, $localtax2_tx, $fk_pa, $paht)
Return an array with margins information of a line.
update($notrigger=0)
Update propal line object into DB.
setDraft($user, $notrigger=0)
Set draft status.
InvoiceArrayList($id)
Returns an array with id and ref of related invoices.
$conf db
API class for accounts.
Definition: inc.php:41
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template module.
updateRangOfLine($rowid, $rang)
Update position of line (rang)
closeProposal($user, $status, $note= '', $notrigger=0)
Close/set the commercial proposal to status signed or refused (fill also date signature) ...
reopen($user, $status, $note= '', $notrigger=0)
Reopen the commercial proposal.
Class to manage products or services.
dol_now($mode= 'auto')
Return date for now.
line_order($renum=false, $rowidorder= 'ASC', $fk_parent_line=true)
Save a new position (field rang) for details lines.
deleteObjectLinked($sourceid=null, $sourcetype= '', $targetid=null, $targettype= '', $rowid= '', $f_user=null, $notrigger=0)
Delete all links between an object $this.
dol_delete_preview($object)
Delete all preview files linked to object instance.
Definition: files.lib.php:1434
static commonReplaceProduct(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product id with another one.
delete_linked_contact($source= '', $code= '')
Delete all links between an object $this and all its contacts.
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller= '', $localtaxes_array= '', $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code= '')
Calculate totals (net, vat, ...) of a line.
Definition: price.lib.php:86
Class to manage Dolibarr users.
Definition: user.class.php:44
Class to manage Dolibarr database access.
set_echeance($user, $date_fin_validite, $notrigger=0)
Define end validity date.
set_availability($user, $id, $notrigger=0)
Set delivery.
liste_array($shortlist=0, $draft=0, $notcurrentuser=0, $socid=0, $limit=0, $offset=0, $sortfield= 'p.datep', $sortorder= 'DESC')
Return list of proposal (eventually filtered on user) into an array.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
const TYPE_PRODUCT
Regular product.
get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Fonction qui renvoie si tva doit etre tva percue recuperable.
insert($notrigger=0)
Insert object line propal in database.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this-&gt;socid or $this-&gt;fk_soc, into this-&gt;thirdparty.
update(User $user, $notrigger=0)
Update database.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags= '', $escapeonlyhtmltags=0)
Returns text escaped for inclusion in HTML alt or title tags, or into values of HTML input fields...
dol_concatdesc($text1, $text2, $forxml=false, $invert=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
get_localtax($vatrate, $local, $thirdparty_buyer="", $thirdparty_seller="", $vatnpr=0)
Return localtax rate for a particular vat, when selling a product with vat $vatrate, from a $thirdparty_buyer to a $thirdparty_seller Note: This function applies same rules than get_default_tva.
valid($user, $notrigger=0)
Set status to validated.
add_product($idproduct, $qty, $remise_percent=0)
Add line into array -&gt;lines $this-&gt;thirdparty should be loaded.
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...
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
insertExtraFields($trigger= '', $userused=null)
Add/Update all extra fields values for the current object.
create($user, $notrigger=0)
Create commercial proposal into database this-&gt;ref can be set or empty.
static getIdAndTxFromCode($dbs, $code, $date_document= '')
Get id and rate of currency from code.
set_demand_reason($user, $id, $notrigger=0)
Set source of demand.
const STATUS_NOTSIGNED
Not signed quote.
$table_ref_field
{}
Class to manage third parties objects (customers, suppliers, prospects...)
addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1=0.0, $txlocaltax2=0.0, $fk_product=0, $remise_percent=0.0, $price_base_type= 'HT', $pu_ttc=0.0, $info_bits=0, $type=0, $rang=-1, $special_code=0, $fk_parent_line=0, $fk_fournprice=0, $pa_ht=0, $label= '', $date_start= '', $date_end= '', $array_options=0, $fk_unit=null, $origin= '', $origin_id=0, $pu_ht_devise=0, $fk_remise_except=0, $noupdateafterinsertline=0)
Add a proposal line into database (linked to product/service or not) The parameters are already suppo...
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
line_max($fk_parent_line=0)
Get max value used for position of line (rang)
dol_strlen($string, $stringencoding= 'UTF-8')
Make a strlen call.
const STATUS_BILLED
Billed or processed quote.
price2num($amount, $rounding= '', $option=0)
Function that return a number with universal decimal format (decimal separator is &#39;...
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:1382
const STATUS_SIGNED
Signed quote.
deleteEcmFiles($mode=0)
Delete related files of object in database.
static commonReplaceThirdparty(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
LibStatut($status, $mode=1)
Return label of a status (draft, validated, ...)
img_picto($titlealt, $picto, $moreatt= '', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt= '', $morecss= '', $marginleftonlyshort=2)
Show picto whatever it&#39;s its name (generic function)
set_remise_percent($user, $remise, $notrigger=0)
Set an overall discount on the proposal.
load_state_board()
Charge indicateurs this-&gt;nb de tableau de bord.
demand_reason($demand_reason_id, $notrigger=0)
Change source demand.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename= '', $restricttologhandler= '', $logcontext=null)
Write log message into outputs.
static replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
fetchObjectLinked($sourceid=null, $sourcetype= '', $targetid=null, $targettype= '', $clause= 'OR', $alsosametype=1, $orderby= 'sourcetype', $loadalsoobjects=1)
Fetch array of objects linked to current object (object of enabled modules only). ...
Class to manage commercial proposal lines.
img_object($titlealt, $picto, $moreatt= '', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
deleteExtraFields()
Delete all extra fields values for the current object.
set_remise_absolue($user, $remise, $notrigger=0)
Set an absolute overall discount on the proposal.
Class to manage translations.
dol_sanitizeFileName($str, $newstr= '_', $unaccent=1)
Clean a string to use it as a file name.
dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0)
Scan a directory and return a list of files/directories.
Definition: files.lib.php:60
set_date($user, $date, $notrigger=0)
Define proposal date.
static getIdFromCode($dbs, $code)
Get id of currency from code.
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this-&gt;array_options This method is in most cases call...
defineBuyPrice($unitPrice=0.0, $discountPercent=0.0, $fk_product=0)
Get buy price to use for margin calculation.
if(isModEnabled('facture')&&!empty($user->rights->facture->lire)) if((isModEnabled('fournisseur')&&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)&&$user->rights->fournisseur->facture->lire)||(isModEnabled('supplier_invoice')&&$user->rights->supplier_invoice->lire)) if(isModEnabled('don')&&!empty($user->rights->don->lire)) if(isModEnabled('tax')&&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture')&&isModEnabled('commande')&&$user->rights->commande->lire &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $resql
Social contributions to pay.
Definition: index.php:742
const STATUS_DRAFT
Draft status.
__construct($db, $socid=0, $propalid=0)
Constructor.
setDeliveryDate($user, $delivery_date, $notrigger=0)
Set delivery date.
trait CommonIncoterm
Superclass for incoterm classes.
div float
Buy price without taxes.
Definition: style.css.php:809
dol_print_date($time, $format= '', $tzoutput= 'auto', $outputlangs= '', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
call_trigger($triggerName, $user)
Call trigger based on this instance.
fetch_lines($only_product=0, $loadalsotranslation=0, $filters= '')
Load array lines.
add_object_linked($origin=null, $origin_id=null, $f_user=null, $notrigger=0)
Add objects linked in llx_element_element.
static replaceProduct(DoliDB $db, $origin_id, $dest_id)
Function used to replace a product id with another one.
__construct($db)
Class line Contructor.
$object ref
Definition: info.php:77
dol_print_error($db= '', $error= '', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
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...
Class to manage absolute discounts.
fetch($rowid, $ref= '', $ref_ext= '', $forceentity=0)
Load a proposal from database.
dolGetStatus($statusLabel= '', $statusLabelShort= '', $html= '', $statusType= 'status0', $displayMode=0, $url= '', $params=array())
Output the badge of a status.
classifyBilled(User $user, $notrigger=0, $note= '')
Classify the proposal to status Billed.
const STATUS_VALIDATED
Validated status.
getNextNumRef($soc)
Returns the reference to the following non used Proposal used depending on the active numbering modul...
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
initAsSpecimen()
Initialise an instance with random values.
insert_discount($idremise)
Adding line of fixed discount in the proposal in DB.
getLibStatut($mode=0)
Return label of status of proposal (draft, validated, ...)
update_price($exclspec=0, $roundingadjust= 'none', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines)...
update_total()
Update DB line fields total_xxx Used by migration.
availability($availability_id, $notrigger=0)
Change the delivery time.
info($id)
Object Proposal Information.
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:1230
Parent class of all other business classes (invoices, contracts, proposals, orders, ...)
load_board($user, $mode)
Load indicators for dashboard (this-&gt;nbtodo and this-&gt;nbtodolate)
updateline($rowid, $pu, $qty, $remise_percent, $txtva, $txlocaltax1=0.0, $txlocaltax2=0.0, $desc= '', $price_base_type= 'HT', $info_bits=0, $special_code=0, $fk_parent_line=0, $skip_update_total=0, $fk_fournprice=0, $pa_ht=0, $label= '', $type=0, $date_start= '', $date_end= '', $array_options=0, $fk_unit=null, $pu_ht_devise=0, $notrigger=0, $rang=0)
Update a proposal line.
getLinesArray($filters= '')
Retrieve an array of proposal lines.
Class to manage proposals.
getNomUrl($withpicto=0, $option= '', $get_params= '', $notooltip=0, $save_lastsearch_value=-1, $addlinktonotes=-1)
Return clicable link of object (with eventually picto)
print *****$script_file(".$version.") pid c cd cd cd description as p label as s rowid