dolibarr 21.0.0-alpha
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-2022 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-2024 Frédéric France <frederic.france@free.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 * Copyright (C) 2023 William Mead <william.mead@manchenumerique.fr>
22 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
23 *
24 * This program is free software; you can redistribute it and/or modify
25 * it under the terms of the GNU General Public License as published by
26 * the Free Software Foundation; either version 3 of the License, or
27 * (at your option) any later version.
28 *
29 * This program is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 * GNU General Public License for more details.
33 *
34 * You should have received a copy of the GNU General Public License
35 * along with this program. If not, see <https://www.gnu.org/licenses/>.
36 */
37
44require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
45require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propaleligne.class.php';
46require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
47require_once DOL_DOCUMENT_ROOT.'/margin/lib/margins.lib.php';
48require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
49require_once DOL_DOCUMENT_ROOT.'/core/class/commonincoterm.class.php';
50
54class Propal extends CommonObject
55{
56 use CommonIncoterm;
57
61 public $code = "";
62
66 public $element = 'propal';
67
71 public $table_element = 'propal';
72
76 public $table_element_line = 'propaldet';
77
81 public $fk_element = 'fk_propal';
82
86 public $picto = 'propal';
87
92 public $restrictiononfksoc = 1;
93
97 protected $table_ref_field = 'ref';
98
103 public $socid;
104
109 public $contactid;
110
117 public $ref_client;
118
123 public $ref_customer;
124
128 public $oldcopy;
129
136 public $statut;
137
143 public $status;
144
150 public $datec;
151
157 public $datev;
158
162 public $date_validation;
163
167 public $date_signature;
168
172 public $user_signature;
173
177 public $date;
178
184 public $datep;
185
189 public $delivery_date; // Date expected of shipment (date starting shipment, not the reception that occurs some days after)
190
191
195 public $fin_validite;
196
200 public $user_author_id;
201
205 public $author;
206
212 public $price;
213
219 public $tva;
225 public $total;
226
230 public $cond_reglement_code;
231
235 public $cond_reglement;
236
240 public $cond_reglement_doc;
241
245 public $mode_reglement_code;
246
250 public $mode_reglement;
251
257 public $deposit_percent;
258
263 public $fk_address;
264
269 public $address_type;
270
275 public $address;
276
280 public $availability_id;
281
287 public $fk_availability;
288
292 public $availability_code;
293
297 public $availability;
298
302 public $duree_validite;
303
307 public $demand_reason_id;
308
312 public $demand_reason_code;
313
317 public $demand_reason;
318
322 public $warehouse_id;
323
327 public $lines = array();
328
332 public $line;
333
334 public $labelStatus = array();
335 public $labelStatusShort = array();
336
337
362 // BEGIN MODULEBUILDER PROPERTIES
366 public $fields = array(
367 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10),
368 'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 15, 'index' => 1),
369 'ref' => array('type' => 'varchar(30)', 'label' => 'Ref', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'showoncombobox' => 1, 'position' => 20),
370 'ref_client' => array('type' => 'varchar(255)', 'label' => 'RefCustomer', 'enabled' => 1, 'visible' => -1, 'position' => 22),
371 'ref_ext' => array('type' => 'varchar(255)', 'label' => 'RefExt', 'enabled' => 1, 'visible' => 0, 'position' => 40),
372 'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'enabled' => 'isModEnabled("societe")', 'visible' => -1, 'position' => 23),
373 'fk_projet' => array('type' => 'integer:Project:projet/class/project.class.php:1:(fk_statut:=:1)', 'label' => 'Fk projet', 'enabled' => "isModEnabled('project')", 'visible' => -1, 'position' => 24),
374 'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 25),
375 'datec' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -1, 'position' => 55),
376 'datep' => array('type' => 'date', 'label' => 'Date', 'enabled' => 1, 'visible' => -1, 'position' => 60),
377 'fin_validite' => array('type' => 'datetime', 'label' => 'DateEnd', 'enabled' => 1, 'visible' => -1, 'position' => 65),
378 'date_valid' => array('type' => 'datetime', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -1, 'position' => 70),
379 'date_cloture' => array('type' => 'datetime', 'label' => 'DateClosing', 'enabled' => 1, 'visible' => -1, 'position' => 75),
380 'fk_user_author' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'Fk user author', 'enabled' => 1, 'visible' => -1, 'position' => 80),
381 'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'position' => 85),
382 'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'enabled' => 1, 'visible' => -1, 'position' => 90),
383 'fk_user_cloture' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'Fk user cloture', 'enabled' => 1, 'visible' => -1, 'position' => 95),
384 'price' => array('type' => 'double', 'label' => 'Price', 'enabled' => 1, 'visible' => -1, 'position' => 105),
385 'total_ht' => array('type' => 'double(24,8)', 'label' => 'TotalHT', 'enabled' => 1, 'visible' => -1, 'position' => 125, 'isameasure' => 1),
386 'total_tva' => array('type' => 'double(24,8)', 'label' => 'VAT', 'enabled' => 1, 'visible' => -1, 'position' => 130, 'isameasure' => 1),
387 'localtax1' => array('type' => 'double(24,8)', 'label' => 'LocalTax1', 'enabled' => 1, 'visible' => -1, 'position' => 135, 'isameasure' => 1),
388 'localtax2' => array('type' => 'double(24,8)', 'label' => 'LocalTax2', 'enabled' => 1, 'visible' => -1, 'position' => 140, 'isameasure' => 1),
389 'total_ttc' => array('type' => 'double(24,8)', 'label' => 'TotalTTC', 'enabled' => 1, 'visible' => -1, 'position' => 145, 'isameasure' => 1),
390 'fk_account' => array('type' => 'integer', 'label' => 'BankAccount', 'enabled' => 'isModEnabled("bank")', 'visible' => -1, 'position' => 150),
391 'fk_currency' => array('type' => 'varchar(3)', 'label' => 'Currency', 'enabled' => 1, 'visible' => -1, 'position' => 155),
392 'fk_cond_reglement' => array('type' => 'integer', 'label' => 'PaymentTerm', 'enabled' => 1, 'visible' => -1, 'position' => 160),
393 'deposit_percent' => array('type' => 'varchar(63)', 'label' => 'DepositPercent', 'enabled' => 1, 'visible' => -1, 'position' => 161),
394 'fk_mode_reglement' => array('type' => 'integer', 'label' => 'PaymentMode', 'enabled' => 1, 'visible' => -1, 'position' => 165),
395 'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 170),
396 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 175),
397 'model_pdf' => array('type' => 'varchar(255)', 'label' => 'PDFTemplate', 'enabled' => 1, 'visible' => 0, 'position' => 180),
398 'date_livraison' => array('type' => 'date', 'label' => 'DateDeliveryPlanned', 'enabled' => 1, 'visible' => -1, 'position' => 185),
399 'fk_shipping_method' => array('type' => 'integer', 'label' => 'ShippingMethod', 'enabled' => 1, 'visible' => -1, 'position' => 190),
400 'fk_warehouse' => array('type' => 'integer:Entrepot:product/stock/class/entrepot.class.php', 'label' => 'Fk warehouse', 'enabled' => 'isModEnabled("stock")', 'visible' => -1, 'position' => 191),
401 'fk_availability' => array('type' => 'integer', 'label' => 'Availability', 'enabled' => 1, 'visible' => -1, 'position' => 195),
402 'fk_delivery_address' => array('type' => 'integer', 'label' => 'DeliveryAddress', 'enabled' => 1, 'visible' => 0, 'position' => 200), // deprecated
403 'fk_input_reason' => array('type' => 'integer', 'label' => 'InputReason', 'enabled' => 1, 'visible' => -1, 'position' => 205),
404 'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => -1, 'position' => 215),
405 'fk_incoterms' => array('type' => 'integer', 'label' => 'IncotermCode', 'enabled' => '$conf->incoterm->enabled', 'visible' => -1, 'position' => 220),
406 'location_incoterms' => array('type' => 'varchar(255)', 'label' => 'IncotermLabel', 'enabled' => '$conf->incoterm->enabled', 'visible' => -1, 'position' => 225),
407 'fk_multicurrency' => array('type' => 'integer', 'label' => 'MulticurrencyID', 'enabled' => 1, 'visible' => -1, 'position' => 230),
408 'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'MulticurrencyCurrency', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 235),
409 'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyRate', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 240, 'isameasure' => 1),
410 'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountHT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 245, 'isameasure' => 1),
411 'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountVAT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 250, 'isameasure' => 1),
412 'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountTTC', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 255, 'isameasure' => 1),
413 'last_main_doc' => array('type' => 'varchar(255)', 'label' => 'LastMainDoc', 'enabled' => 1, 'visible' => -1, 'position' => 260),
414 'fk_statut' => array('type' => 'smallint(6)', 'label' => 'Status', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 500),
415 'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'position' => 900),
416 );
417 // END MODULEBUILDER PROPERTIES
418
422 const STATUS_CANCELED = -1;
426 const STATUS_DRAFT = 0;
434 const STATUS_SIGNED = 2;
442 const STATUS_BILLED = 4; // Todo rename into STATUS_CLOSE ?
443
444
452 public function __construct($db, $socid = 0, $propalid = 0)
453 {
454 $this->db = $db;
455
456 $this->ismultientitymanaged = 1;
457 $this->socid = $socid;
458 $this->id = $propalid;
459
460 $this->duree_validite = getDolGlobalInt('PROPALE_VALIDITY_DURATION', 0);
461 }
462
463
464 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
476 public function add_product($idproduct, $qty, $remise_percent = 0)
477 {
478 // phpcs:enable
479 global $conf, $mysoc;
480
481 if (!$qty) {
482 $qty = 1;
483 }
484
485 dol_syslog(get_class($this)."::add_product $idproduct, $qty, $remise_percent");
486 if ($idproduct > 0) {
487 $prod = new Product($this->db);
488 $prod->fetch($idproduct);
489
490 $productdesc = $prod->description;
491
492 $tva_tx = (string) get_default_tva($mysoc, $this->thirdparty, $prod->id);
493 $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
494 if (empty($tva_tx)) {
495 $tva_npr = 0;
496 }
497 $vat_src_code = ''; // May be defined into tva_tx
498
499 $localtax1_tx = get_localtax($tva_tx, 1, $mysoc, $this->thirdparty, $tva_npr);
500 $localtax2_tx = get_localtax($tva_tx, 2, $mysoc, $this->thirdparty, $tva_npr);
501
502 // multiprices
503 if ($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level) {
504 $price = $prod->multiprices[$this->thirdparty->price_level];
505 } else {
506 $price = $prod->price;
507 }
508
509 $line = new PropaleLigne($this->db);
510
511 $line->fk_product = $idproduct;
512 $line->desc = $productdesc;
513 $line->qty = $qty;
514 $line->subprice = $price;
515 $line->remise_percent = $remise_percent;
516 $line->vat_src_code = $vat_src_code;
517 $line->tva_tx = $tva_tx;
518 $line->fk_unit = $prod->fk_unit;
519 if ($tva_npr) {
520 $line->info_bits = 1;
521 }
522
523 $this->lines[] = $line;
524 }
525
526 return 1;
527 }
528
529 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
536 public function insert_discount($idremise)
537 {
538 // phpcs:enable
539 global $langs;
540
541 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
542 include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
543
544 $this->db->begin();
545
546 $remise = new DiscountAbsolute($this->db);
547 $result = $remise->fetch($idremise);
548
549 if ($result > 0) {
550 if ($remise->fk_facture) { // Protection against multiple submission
551 $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
552 $this->db->rollback();
553 return -5;
554 }
555
556 $line = new PropaleLigne($this->db);
557
558 $line->context = $this->context;
559
560 $line->fk_propal = $this->id;
561 $line->fk_remise_except = $remise->id;
562 $line->desc = $remise->description; // Description ligne
563 $line->vat_src_code = $remise->vat_src_code;
564 $line->tva_tx = $remise->tva_tx;
565 $line->subprice = -(float) $remise->amount_ht;
566 $line->fk_product = 0; // Id produit predefined
567 $line->qty = 1;
568 $line->remise_percent = 0;
569 $line->rang = -1;
570 $line->info_bits = 2;
571
572 // TODO deprecated
573 $line->price = -(float) $remise->amount_ht;
574
575 $line->total_ht = -(float) $remise->amount_ht;
576 $line->total_tva = -(float) $remise->amount_tva;
577 $line->total_ttc = -(float) $remise->amount_ttc;
578
579 $result = $line->insert();
580 if ($result > 0) {
581 $result = $this->update_price(1);
582 if ($result > 0) {
583 $this->db->commit();
584 return 1;
585 } else {
586 $this->db->rollback();
587 return -1;
588 }
589 } else {
590 $this->error = $line->error;
591 $this->errors = $line->errors;
592 $this->db->rollback();
593 return -2;
594 }
595 } else {
596 $this->db->rollback();
597 return -2;
598 }
599 }
600
638 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 = array(), $fk_unit = null, $origin = '', $origin_id = 0, $pu_ht_devise = 0, $fk_remise_except = 0, $noupdateafterinsertline = 0)
639 {
640 global $mysoc, $conf, $langs;
641
642 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);
643
644 if ($this->statut == self::STATUS_DRAFT) {
645 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
646
647 // Clean parameters
648 if (empty($remise_percent)) {
649 $remise_percent = 0;
650 }
651 if (empty($qty)) {
652 $qty = 0;
653 }
654 if (empty($info_bits)) {
655 $info_bits = 0;
656 }
657 if (empty($rang)) {
658 $rang = 0;
659 }
660 if (empty($fk_parent_line) || $fk_parent_line < 0) {
661 $fk_parent_line = 0;
662 }
663
664 $remise_percent = price2num($remise_percent);
665 $qty = (float) price2num($qty);
666 $pu_ht = price2num($pu_ht);
667 $pu_ht_devise = price2num($pu_ht_devise);
668 $pu_ttc = price2num($pu_ttc);
669 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
670 $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
671 }
672 $txlocaltax1 = price2num($txlocaltax1);
673 $txlocaltax2 = price2num($txlocaltax2);
674 $pa_ht = price2num($pa_ht);
675 if ($price_base_type == 'HT') {
676 $pu = $pu_ht;
677 } else {
678 $pu = $pu_ttc;
679 }
680
681 // Check parameters
682 if ($type < 0) {
683 return -1;
684 }
685
686 if ($date_start && $date_end && $date_start > $date_end) {
687 $langs->load("errors");
688 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
689 return -1;
690 }
691
692 $this->db->begin();
693
694 $product_type = $type;
695 if (!empty($fk_product) && $fk_product > 0) {
696 $product = new Product($this->db);
697 $result = $product->fetch($fk_product);
698 $product_type = $product->type;
699
700 if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_PROPOSAL') && $product_type == 0 && $product->stock_reel < $qty) {
701 $langs->load("errors");
702 $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnProposal', $product->ref);
703 $this->db->rollback();
704 return -3;
705 }
706 }
707
708 // Calcul du total TTC et de la TVA pour la ligne a partir de
709 // qty, pu, remise_percent et txtva
710 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
711 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
712
713 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
714
715 // Clean vat code
716 $reg = array();
717 $vat_src_code = '';
718 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
719 $vat_src_code = $reg[1];
720 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
721 }
722
723 $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);
724
725 $total_ht = $tabprice[0];
726 $total_tva = $tabprice[1];
727 $total_ttc = $tabprice[2];
728 $total_localtax1 = $tabprice[9];
729 $total_localtax2 = $tabprice[10];
730 $pu_ht = $tabprice[3];
731 $pu_tva = $tabprice[4];
732 $pu_ttc = $tabprice[5];
733
734 // MultiCurrency
735 $multicurrency_total_ht = $tabprice[16];
736 $multicurrency_total_tva = $tabprice[17];
737 $multicurrency_total_ttc = $tabprice[18];
738 $pu_ht_devise = $tabprice[19];
739
740 // Rang to use
741 $ranktouse = $rang;
742 if ($ranktouse == -1) {
743 $rangmax = $this->line_max($fk_parent_line);
744 $ranktouse = $rangmax + 1;
745 }
746
747 // TODO A virer
748 // Anciens indicateurs: $price, $remise (a ne plus utiliser)
749 $price = $pu;
750 $remise = 0;
751 if ((float) $remise_percent > 0) {
752 $remise = round(((float) $pu * (float) $remise_percent / 100), 2);
753 $price = (float) $pu - $remise;
754 }
755
756 // Insert line
757 $this->line = new PropaleLigne($this->db);
758
759 $this->line->context = $this->context;
760
761 $this->line->fk_propal = $this->id;
762 $this->line->label = $label;
763 $this->line->desc = $desc;
764 $this->line->qty = $qty;
765
766 $this->line->vat_src_code = $vat_src_code;
767 $this->line->tva_tx = $txtva;
768 $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
769 $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
770 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
771 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
772 $this->line->fk_product = $fk_product;
773 $this->line->product_type = $type;
774 $this->line->fk_remise_except = $fk_remise_except;
775 $this->line->remise_percent = $remise_percent;
776 $this->line->subprice = $pu_ht;
777 $this->line->rang = $ranktouse;
778 $this->line->info_bits = $info_bits;
779 $this->line->total_ht = $total_ht;
780 $this->line->total_tva = $total_tva;
781 $this->line->total_localtax1 = $total_localtax1;
782 $this->line->total_localtax2 = $total_localtax2;
783 $this->line->total_ttc = $total_ttc;
784 $this->line->special_code = $special_code;
785 $this->line->fk_parent_line = $fk_parent_line;
786 $this->line->fk_unit = $fk_unit;
787
788 $this->line->date_start = $date_start;
789 $this->line->date_end = $date_end;
790
791 $this->line->fk_fournprice = $fk_fournprice;
792 $this->line->pa_ht = $pa_ht;
793
794 $this->line->origin_id = $origin_id;
795 $this->line->origin = $origin;
796
797 // Multicurrency
798 $this->line->fk_multicurrency = $this->fk_multicurrency;
799 $this->line->multicurrency_code = $this->multicurrency_code;
800 $this->line->multicurrency_subprice = $pu_ht_devise;
801 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
802 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
803 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
804
805 // Mise en option de la ligne
806 if (empty($qty) && empty($special_code)) {
807 $this->line->special_code = 3;
808 }
809
810 // TODO deprecated
811 $this->line->price = $price;
812
813 if (is_array($array_options) && count($array_options) > 0) {
814 $this->line->array_options = $array_options;
815 }
816
817 $result = $this->line->insert();
818 if ($result > 0) {
819 // Reorder if child line
820 if (!empty($fk_parent_line)) {
821 $this->line_order(true, 'DESC');
822 } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) { // Update all rank of all other lines
823 $linecount = count($this->lines);
824 for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
825 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
826 }
827 }
828
829 // Mise a jour information denormalisees au niveau de la propale meme
830 if (empty($noupdateafterinsertline)) {
831 $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.
832 }
833
834 if ($result > 0) {
835 $this->db->commit();
836 return $this->line->id;
837 } else {
838 $this->error = $this->db->error();
839 $this->db->rollback();
840 return -1;
841 }
842 } else {
843 $this->error = $this->line->error;
844 $this->errors = $this->line->errors;
845 $this->db->rollback();
846 return -2;
847 }
848 } else {
849 dol_syslog(get_class($this)."::addline status of proposal must be Draft to allow use of ->addline()", LOG_ERR);
850 return -3;
851 }
852 }
853
854
884 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 = array(), $fk_unit = null, $pu_ht_devise = 0, $notrigger = 0, $rang = 0)
885 {
886 global $mysoc, $langs;
887
888 dol_syslog(get_class($this)."::updateLine rowid=$rowid, pu=$pu, qty=$qty, remise_percent=$remise_percent,
889 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");
890 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
891
892 // Clean parameters
893 $remise_percent = price2num($remise_percent);
894 $qty = (float) price2num($qty);
895 $pu = price2num($pu);
896 $pu_ht_devise = price2num($pu_ht_devise);
897 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
898 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
899 }
900 $txlocaltax1 = price2num($txlocaltax1);
901 $txlocaltax2 = price2num($txlocaltax2);
902 $pa_ht = price2num($pa_ht);
903 if (empty($qty) && empty($special_code)) {
904 $special_code = 3; // Set option tag
905 }
906 if (!empty($qty) && $special_code == 3) {
907 $special_code = 0; // Remove option tag
908 }
909 if (empty($type)) {
910 $type = 0;
911 }
912
913 if ($date_start && $date_end && $date_start > $date_end) {
914 $langs->load("errors");
915 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
916 return -1;
917 }
918
919 if ($this->status == self::STATUS_DRAFT) {
920 $this->db->begin();
921
922 // Calcul du total TTC et de la TVA pour la ligne a partir de
923 // qty, pu, remise_percent et txtva
924 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
925 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
926
927 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
928
929 // Clean vat code
930 $reg = array();
931 $vat_src_code = '';
932 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
933 $vat_src_code = $reg[1];
934 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
935 }
936
937 // TODO Implement if (getDolGlobalInt('MAIN_UNIT_PRICE_WITH_TAX_IS_FOR_ALL_TAXES')) ?
938
939 $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);
940 $total_ht = $tabprice[0];
941 $total_tva = $tabprice[1];
942 $total_ttc = $tabprice[2];
943 $total_localtax1 = $tabprice[9];
944 $total_localtax2 = $tabprice[10];
945 $pu_ht = $tabprice[3];
946 $pu_tva = $tabprice[4];
947 $pu_ttc = $tabprice[5];
948
949 // MultiCurrency
950 $multicurrency_total_ht = $tabprice[16];
951 $multicurrency_total_tva = $tabprice[17];
952 $multicurrency_total_ttc = $tabprice[18];
953 $pu_ht_devise = $tabprice[19];
954
955 // Anciens indicateurs: $price, $remise (a ne plus utiliser)
956 $price = $pu;
957 $remise = 0;
958 if ((float) $remise_percent > 0) {
959 $remise = round(((float) $pu * (float) $remise_percent / 100), 2);
960 $price = (float) $pu - $remise;
961 }
962
963 // Fetch current line from the database and then clone the object and set it in $oldline property
964 $line = new PropaleLigne($this->db);
965 $line->fetch($rowid);
966
967 $staticline = clone $line;
968
969 $line->oldline = $staticline;
970 $this->line = $line;
971 $this->line->context = $this->context;
972 $this->line->rang = $rang;
973
974 // Reorder if fk_parent_line change
975 if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
976 $rangmax = $this->line_max($fk_parent_line);
977 $this->line->rang = $rangmax + 1;
978 }
979
980 $this->line->id = $rowid;
981 $this->line->label = $label;
982 $this->line->desc = $desc;
983 $this->line->qty = $qty;
984 $this->line->product_type = $type;
985 $this->line->vat_src_code = $vat_src_code;
986 $this->line->tva_tx = $txtva;
987 $this->line->localtax1_tx = $txlocaltax1;
988 $this->line->localtax2_tx = $txlocaltax2;
989 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
990 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
991 $this->line->remise_percent = $remise_percent;
992 $this->line->subprice = $pu_ht;
993 $this->line->info_bits = $info_bits;
994
995 $this->line->total_ht = $total_ht;
996 $this->line->total_tva = $total_tva;
997 $this->line->total_localtax1 = $total_localtax1;
998 $this->line->total_localtax2 = $total_localtax2;
999 $this->line->total_ttc = $total_ttc;
1000 $this->line->special_code = $special_code;
1001 $this->line->fk_parent_line = $fk_parent_line;
1002 $this->line->skip_update_total = $skip_update_total;
1003 $this->line->fk_unit = $fk_unit;
1004
1005 $this->line->fk_fournprice = $fk_fournprice;
1006 $this->line->pa_ht = $pa_ht;
1007
1008 $this->line->date_start = $date_start;
1009 $this->line->date_end = $date_end;
1010
1011 if (is_array($array_options) && count($array_options) > 0) {
1012 // We replace values in this->line->array_options only for entries defined into $array_options
1013 foreach ($array_options as $key => $value) {
1014 $this->line->array_options[$key] = $array_options[$key];
1015 }
1016 }
1017
1018 // Multicurrency
1019 $this->line->multicurrency_subprice = $pu_ht_devise;
1020 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
1021 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
1022 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
1023
1024 $result = $this->line->update($notrigger);
1025 if ($result > 0) {
1026 // Reorder if child line
1027 if (!empty($fk_parent_line)) {
1028 $this->line_order(true, 'DESC');
1029 }
1030
1031 $this->update_price(1, 'auto');
1032
1033 // $this is Propal
1034 // $this->fk_propal = $this->id;
1035 // $this->rowid = $rowid;
1036
1037 $this->db->commit();
1038 return $result;
1039 } else {
1040 $this->error = $this->line->error;
1041 $this->errors = $this->line->errors;
1042 $this->db->rollback();
1043 return -1;
1044 }
1045 } else {
1046 dol_syslog(get_class($this)."::updateline Erreur -2 Propal en mode incompatible pour cette action");
1047 return -2;
1048 }
1049 }
1050
1051
1059 public function deleteLine($lineid, $id = 0)
1060 {
1061 global $user;
1062
1063 if ($this->status == self::STATUS_DRAFT) {
1064 $this->db->begin();
1065
1066 $line = new PropaleLigne($this->db);
1067
1068 $line->context = $this->context;
1069
1070 // Load data
1071 $line->fetch($lineid);
1072
1073 if ($id > 0 && $line->fk_propal != $id) {
1074 $this->error = 'ErrorLineIDDoesNotMatchWithObjectID';
1075 return -1;
1076 }
1077
1078 // Memorize previous line for triggers
1079 $staticline = clone $line;
1080 $line->oldline = $staticline;
1081
1082 if ($line->delete($user) > 0) {
1083 $this->update_price(1);
1084
1085 $this->db->commit();
1086 return 1;
1087 } else {
1088 $this->error = $line->error;
1089 $this->errors = $line->errors;
1090 $this->db->rollback();
1091 return -1;
1092 }
1093 } else {
1094 $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
1095 return -2;
1096 }
1097 }
1098
1099
1108 public function create($user, $notrigger = 0)
1109 {
1110 global $conf, $hookmanager, $mysoc;
1111 $error = 0;
1112
1113 $now = dol_now();
1114
1115 // Clean parameters
1116 if (empty($this->date)) {
1117 $this->date = $this->datep;
1118 }
1119 $this->fin_validite = $this->date + ($this->duree_validite * 24 * 3600);
1120 if (empty($this->availability_id)) {
1121 $this->availability_id = 0;
1122 }
1123 if (empty($this->demand_reason_id)) {
1124 $this->demand_reason_id = 0;
1125 }
1126
1127 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1128 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1129 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date);
1130 } else {
1131 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1132 }
1133 if (empty($this->fk_multicurrency)) {
1134 $this->multicurrency_code = $conf->currency;
1135 $this->fk_multicurrency = 0;
1136 $this->multicurrency_tx = 1;
1137 }
1138
1139 // Set tmp vars
1140 $delivery_date = $this->delivery_date;
1141
1142 dol_syslog(get_class($this)."::create");
1143
1144 // Check parameters
1145 $result = $this->fetch_thirdparty();
1146 if ($result < 0) {
1147 $this->error = "Failed to fetch company";
1148 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
1149 return -3;
1150 }
1151
1152 // Check parameters
1153 if (!empty($this->ref)) { // We check that ref is not already used
1154 $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
1155 if ($result > 0) {
1156 $this->error = 'ErrorRefAlreadyExists';
1157 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
1158 $this->db->rollback();
1159 return -1;
1160 }
1161 }
1162
1163 if (empty($this->date)) {
1164 $this->error = "Date of proposal is required";
1165 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
1166 return -4;
1167 }
1168
1169
1170 $this->db->begin();
1171
1172 // Insert into database
1173 $sql = "INSERT INTO ".MAIN_DB_PREFIX."propal (";
1174 $sql .= "fk_soc";
1175 $sql .= ", price";
1176 $sql .= ", total_tva";
1177 $sql .= ", total_ttc";
1178 $sql .= ", datep";
1179 $sql .= ", datec";
1180 $sql .= ", ref";
1181 $sql .= ", fk_user_author";
1182 $sql .= ", note_private";
1183 $sql .= ", note_public";
1184 $sql .= ", model_pdf";
1185 $sql .= ", fin_validite";
1186 $sql .= ", fk_cond_reglement";
1187 $sql .= ", deposit_percent";
1188 $sql .= ", fk_mode_reglement";
1189 $sql .= ", fk_account";
1190 $sql .= ", ref_client";
1191 $sql .= ", ref_ext";
1192 $sql .= ", date_livraison";
1193 $sql .= ", fk_shipping_method";
1194 $sql .= ", fk_warehouse";
1195 $sql .= ", fk_availability";
1196 $sql .= ", fk_input_reason";
1197 $sql .= ", fk_projet";
1198 $sql .= ", fk_incoterms";
1199 $sql .= ", location_incoterms";
1200 $sql .= ", entity";
1201 $sql .= ", fk_multicurrency";
1202 $sql .= ", multicurrency_code";
1203 $sql .= ", multicurrency_tx";
1204 $sql .= ") ";
1205 $sql .= " VALUES (";
1206 $sql .= $this->socid;
1207 $sql .= ", 0";
1208 $sql .= ", 0";
1209 $sql .= ", 0";
1210 $sql .= ", '".$this->db->idate($this->date)."'";
1211 $sql .= ", '".$this->db->idate($now)."'";
1212 $sql .= ", '(PROV)'";
1213 $sql .= ", ".($user->id > 0 ? ((int) $user->id) : "NULL");
1214 $sql .= ", '".$this->db->escape($this->note_private)."'";
1215 $sql .= ", '".$this->db->escape($this->note_public)."'";
1216 $sql .= ", '".$this->db->escape($this->model_pdf)."'";
1217 $sql .= ", ".($this->fin_validite != '' ? "'".$this->db->idate($this->fin_validite)."'" : "NULL");
1218 $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : 'NULL');
1219 $sql .= ", ".(!empty($this->deposit_percent) ? "'".$this->db->escape($this->deposit_percent)."'" : 'NULL');
1220 $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : 'NULL');
1221 $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
1222 $sql .= ", '".$this->db->escape($this->ref_client)."'";
1223 $sql .= ", '".$this->db->escape($this->ref_ext)."'";
1224 $sql .= ", ".(!isDolTms($delivery_date) ? "NULL" : "'".$this->db->idate($delivery_date)."'");
1225 $sql .= ", ".($this->shipping_method_id > 0 ? $this->shipping_method_id : 'NULL');
1226 $sql .= ", ".($this->warehouse_id > 0 ? $this->warehouse_id : 'NULL');
1227 $sql .= ", ".$this->availability_id;
1228 $sql .= ", ".$this->demand_reason_id;
1229 $sql .= ", ".($this->fk_project ? $this->fk_project : "null");
1230 $sql .= ", ".(int) $this->fk_incoterms;
1231 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
1232 $sql .= ", ".setEntity($this);
1233 $sql .= ", ".(int) $this->fk_multicurrency;
1234 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1235 $sql .= ", ".(float) $this->multicurrency_tx;
1236 $sql .= ")";
1237
1238 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1239 $resql = $this->db->query($sql);
1240 if ($resql) {
1241 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."propal");
1242
1243 if ($this->id) {
1244 $this->ref = '(PROV'.$this->id.')';
1245 $sql = 'UPDATE '.MAIN_DB_PREFIX."propal SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
1246
1247 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1248 $resql = $this->db->query($sql);
1249 if (!$resql) {
1250 $error++;
1251 }
1252
1253 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1254 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1255 }
1256
1257 // Add object linked
1258 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1259 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1260 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, ...))
1261 foreach ($tmp_origin_id as $origin_id) {
1262 $ret = $this->add_object_linked($origin, $origin_id);
1263 if (!$ret) {
1264 $this->error = $this->db->lasterror();
1265 $error++;
1266 }
1267 }
1268 } else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1269 $origin_id = $tmp_origin_id;
1270 $ret = $this->add_object_linked($origin, $origin_id);
1271 if (!$ret) {
1272 $this->error = $this->db->lasterror();
1273 $error++;
1274 }
1275 }
1276 }
1277 }
1278
1279 /*
1280 * Insertion du detail des produits dans la base
1281 * Insert products detail in database
1282 */
1283 if (!$error) {
1284 $fk_parent_line = 0;
1285 $num = count($this->lines);
1286
1287 for ($i = 0; $i < $num; $i++) {
1288 if (!is_object($this->lines[$i])) { // If this->lines is not array of objects, coming from REST API
1289 // Convert into object this->lines[$i].
1290 $line = (object) $this->lines[$i];
1291 } else {
1292 $line = $this->lines[$i];
1293 }
1294 // Reset fk_parent_line for line that are not child lines or special product
1295 if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
1296 $fk_parent_line = 0;
1297 }
1298 // Complete vat rate with code
1299 $vatrate = $line->tva_tx;
1300 if ($line->vat_src_code && !preg_match('/\‍(.*\‍)/', $vatrate)) {
1301 $vatrate .= ' ('.$line->vat_src_code.')';
1302 }
1303
1304 if (getDolGlobalString('MAIN_CREATEFROM_KEEP_LINE_ORIGIN_INFORMATION')) {
1305 $originid = $line->origin_id;
1306 $origintype = $line->origin;
1307 } else {
1308 $originid = $line->id;
1309 $origintype = $this->element;
1310 }
1311
1312 $result = $this->addline(
1313 $line->desc,
1314 $line->subprice,
1315 $line->qty,
1316 $vatrate,
1317 $line->localtax1_tx,
1318 $line->localtax2_tx,
1319 $line->fk_product,
1320 $line->remise_percent,
1321 'HT',
1322 0,
1323 $line->info_bits,
1324 $line->product_type,
1325 $line->rang,
1326 $line->special_code,
1327 $fk_parent_line,
1328 $line->fk_fournprice,
1329 $line->pa_ht,
1330 $line->label,
1331 $line->date_start,
1332 $line->date_end,
1333 $line->array_options,
1334 $line->fk_unit,
1335 $origintype,
1336 $originid,
1337 0,
1338 0,
1339 1
1340 );
1341
1342 if ($result < 0) {
1343 $error++;
1344 $this->error = $this->db->error;
1345 dol_print_error($this->db);
1346 break;
1347 }
1348
1349 // Set the id on created row
1350 $line->id = $result;
1351
1352 // Defined the new fk_parent_line
1353 if ($result > 0 && $line->product_type == 9) {
1354 $fk_parent_line = $result;
1355 }
1356 }
1357 }
1358
1359 // Set delivery address
1360 /*if (! $error && $this->fk_delivery_address)
1361 {
1362 $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
1363 $sql.= " SET fk_delivery_address = ".((int) $this->fk_delivery_address);
1364 $sql.= " WHERE ref = '".$this->db->escape($this->ref)."'";
1365 $sql.= " AND entity = ".setEntity($this);
1366
1367 $result=$this->db->query($sql);
1368 }*/
1369
1370 if (!$error) {
1371 // Mise a jour infos denormalisees
1372 $resql = $this->update_price(1, 'auto', 0, $mysoc);
1373 if ($resql) {
1374 $action = 'update';
1375
1376 // Actions on extra fields
1377 if (!$error) {
1378 $result = $this->insertExtraFields();
1379 if ($result < 0) {
1380 $error++;
1381 }
1382 }
1383
1384 if (!$error && !$notrigger) {
1385 // Call trigger
1386 $result = $this->call_trigger('PROPAL_CREATE', $user);
1387 if ($result < 0) {
1388 $error++;
1389 }
1390 // End call triggers
1391 }
1392 } else {
1393 $this->error = $this->db->lasterror();
1394 $error++;
1395 }
1396 }
1397 } else {
1398 $this->error = $this->db->lasterror();
1399 $error++;
1400 }
1401
1402 if (!$error) {
1403 $this->db->commit();
1404 dol_syslog(get_class($this)."::create done id=".$this->id);
1405 return $this->id;
1406 } else {
1407 $this->db->rollback();
1408 return -2;
1409 }
1410 } else {
1411 $this->error = $this->db->lasterror();
1412 $this->db->rollback();
1413 return -1;
1414 }
1415 }
1416
1427 public function createFromClone(User $user, $socid = 0, $forceentity = null, $update_prices = false, $update_desc = false)
1428 {
1429 global $conf, $hookmanager, $mysoc;
1430
1431 dol_include_once('/projet/class/project.class.php');
1432
1433 $error = 0;
1434 $now = dol_now();
1435
1436 dol_syslog(__METHOD__, LOG_DEBUG);
1437
1438 $object = new self($this->db);
1439
1440 $this->db->begin();
1441
1442 // Load source object
1443 $object->fetch($this->id);
1444
1445 $objsoc = new Societe($this->db);
1446
1447 // Change socid if needed
1448 if (!empty($socid) && $socid != $object->socid) {
1449 if ($objsoc->fetch($socid) > 0) {
1450 $object->socid = $objsoc->id;
1451 $object->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1452 $object->deposit_percent = (!empty($objsoc->deposit_percent) ? $objsoc->deposit_percent : null);
1453 $object->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1454 $object->fk_delivery_address = 0;
1455
1456 /*if (isModEnabled('project'))
1457 {
1458 $project = new Project($db);
1459 if ($this->fk_project > 0 && $project->fetch($this->fk_project)) {
1460 if ($project->socid <= 0) $clonedObj->fk_project = $this->fk_project;
1461 else $clonedObj->fk_project = '';
1462 } else {
1463 $clonedObj->fk_project = '';
1464 }
1465 }*/
1466 $object->fk_project = 0; // A cloned proposal is set by default to no project.
1467 }
1468
1469 // reset ref_client
1470 if (!getDolGlobalString('MAIN_KEEP_REF_CUSTOMER_ON_CLONING')) {
1471 $object->ref_client = '';
1472 $object->ref_customer = '';
1473 }
1474
1475 // TODO Change product price if multi-prices
1476 } else {
1477 $objsoc->fetch($object->socid);
1478 }
1479
1480 // update prices
1481 if ($update_prices === true || $update_desc === true) {
1482 if ($objsoc->id > 0 && !empty($object->lines)) {
1483 if ($update_prices === true && getDolGlobalString('PRODUIT_CUSTOMER_PRICES')) {
1484 // If price per customer
1485 require_once DOL_DOCUMENT_ROOT . '/product/class/productcustomerprice.class.php';
1486 }
1487
1488 foreach ($object->lines as $line) {
1489 $line->id = 0;
1490
1491 if ($line->fk_product > 0) {
1492 $prod = new Product($this->db);
1493 $res = $prod->fetch($line->fk_product);
1494 if ($res > 0) {
1495 if ($update_prices === true) {
1496 $pu_ht = $prod->price;
1497 $tva_tx = (string) get_default_tva($mysoc, $objsoc, $prod->id);
1498 $remise_percent = $objsoc->remise_percent;
1499
1500 if (getDolGlobalString('PRODUIT_MULTIPRICES') && $objsoc->price_level > 0) {
1501 $pu_ht = $prod->multiprices[$objsoc->price_level];
1502 if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) { // using this option is a bug. kept for backward compatibility
1503 if (isset($prod->multiprices_tva_tx[$objsoc->price_level])) {
1504 $tva_tx = (string) $prod->multiprices_tva_tx[$objsoc->price_level];
1505 }
1506 }
1507 } elseif (getDolGlobalString('PRODUIT_CUSTOMER_PRICES')) {
1508 $prodcustprice = new ProductCustomerPrice($this->db);
1509 $filter = array('t.fk_product' => $prod->id, 't.fk_soc' => $objsoc->id);
1510 $result = $prodcustprice->fetchAll('', '', 0, 0, $filter);
1511 if ($result) {
1512 // If there is some prices specific to the customer
1513 if (count($prodcustprice->lines) > 0) {
1514 $pu_ht = price($prodcustprice->lines[0]->price);
1515 $tva_tx = ($prodcustprice->lines[0]->default_vat_code ? $prodcustprice->lines[0]->tva_tx.' ('.$prodcustprice->lines[0]->default_vat_code.' )' : $prodcustprice->lines[0]->tva_tx);
1516 if ($prodcustprice->lines[0]->default_vat_code && !preg_match('/\‍(.*\‍)/', $tva_tx)) {
1517 $tva_tx .= ' ('.$prodcustprice->lines[0]->default_vat_code.')';
1518 }
1519 }
1520 }
1521 }
1522
1523 $line->subprice = $pu_ht;
1524 $line->tva_tx = $tva_tx;
1525 $line->remise_percent = $remise_percent;
1526 }
1527 if ($update_desc === true) {
1528 $line->desc = $prod->description;
1529 }
1530 }
1531 }
1532 }
1533 }
1534 }
1535
1536 $object->id = 0;
1537 $object->ref = '';
1538 $object->entity = (!empty($forceentity) ? $forceentity : $object->entity);
1539 $object->statut = self::STATUS_DRAFT;
1540
1541 // Clear fields
1542 $object->user_creation_id = $user->id;
1543 $object->user_validation_id = 0;
1544 $object->date = $now;
1545 $object->datep = $now; // deprecated
1546 $object->fin_validite = $object->date + ($object->duree_validite * 24 * 3600);
1547 if (!getDolGlobalString('MAIN_KEEP_REF_CUSTOMER_ON_CLONING')) {
1548 $object->ref_client = '';
1549 $object->ref_customer = '';
1550 }
1551 if (getDolGlobalInt('MAIN_DONT_KEEP_NOTE_ON_CLONING') == 1) {
1552 $object->note_private = '';
1553 $object->note_public = '';
1554 }
1555 // Create clone
1556 $object->context['createfromclone'] = 'createfromclone';
1557 $result = $object->create($user);
1558 if ($result < 0) {
1559 $this->error = $object->error;
1560 $this->errors = array_merge($this->errors, $object->errors);
1561 $error++;
1562 }
1563
1564 if (!$error && !getDolGlobalInt('MAIN_IGNORE_CONTACTS_ON_CLONING')) {
1565 // copy internal contacts
1566 if ($object->copy_linked_contact($this, 'internal') < 0) {
1567 $error++;
1568 }
1569 }
1570
1571 if (!$error) {
1572 // copy external contacts if same company
1573 if ($this->socid == $object->socid) {
1574 if ($object->copy_linked_contact($this, 'external') < 0) {
1575 $error++;
1576 }
1577 }
1578 }
1579
1580 if (!$error) {
1581 // Hook of thirdparty module
1582 if (is_object($hookmanager)) {
1583 $parameters = array('objFrom' => $this, 'clonedObj' => $object);
1584 $action = '';
1585 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
1586 if ($reshook < 0) {
1587 $this->setErrorsFromObject($hookmanager);
1588 $error++;
1589 }
1590 }
1591 }
1592
1593 unset($object->context['createfromclone']);
1594
1595 // End
1596 if (!$error) {
1597 $this->db->commit();
1598 return $object->id;
1599 } else {
1600 $this->db->rollback();
1601 return -1;
1602 }
1603 }
1604
1614 public function fetch($rowid, $ref = '', $ref_ext = '', $forceentity = 0)
1615 {
1616 $sql = "SELECT p.rowid, p.ref, p.entity, p.fk_soc";
1617 $sql .= ", p.total_ttc, p.total_tva, p.localtax1, p.localtax2, p.total_ht";
1618 $sql .= ", p.datec";
1619 $sql .= ", p.date_signature as dates";
1620 $sql .= ", p.date_valid as datev";
1621 $sql .= ", p.datep as dp";
1622 $sql .= ", p.fin_validite as dfv";
1623 $sql .= ", p.date_livraison as delivery_date";
1624 $sql .= ", p.model_pdf, p.last_main_doc, p.ref_client, ref_ext, p.extraparams";
1625 $sql .= ", p.note_private, p.note_public";
1626 $sql .= ", p.fk_projet as fk_project, p.fk_statut";
1627 $sql .= ", p.fk_user_author, p.fk_user_valid, p.fk_user_cloture";
1628 $sql .= ", p.fk_delivery_address";
1629 $sql .= ", p.fk_availability";
1630 $sql .= ", p.fk_input_reason";
1631 $sql .= ", p.fk_cond_reglement";
1632 $sql .= ", p.fk_mode_reglement";
1633 $sql .= ', p.fk_account';
1634 $sql .= ", p.fk_shipping_method";
1635 $sql .= ", p.fk_warehouse";
1636 $sql .= ", p.fk_incoterms, p.location_incoterms";
1637 $sql .= ", p.fk_multicurrency, p.multicurrency_code, p.multicurrency_tx, p.multicurrency_total_ht, p.multicurrency_total_tva, p.multicurrency_total_ttc";
1638 $sql .= ", p.tms as date_modification";
1639 $sql .= ", i.libelle as label_incoterms";
1640 $sql .= ", c.label as statut_label";
1641 $sql .= ", ca.code as availability_code, ca.label as availability";
1642 $sql .= ", dr.code as demand_reason_code, dr.label as demand_reason";
1643 $sql .= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc, p.deposit_percent";
1644 $sql .= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement";
1645 $sql .= " FROM ".MAIN_DB_PREFIX."propal as p";
1646 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_propalst as c ON p.fk_statut = c.id';
1647 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id AND cp.entity IN ('.getEntity('c_paiement').')';
1648 $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').')';
1649 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_availability as ca ON p.fk_availability = ca.rowid';
1650 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_input_reason as dr ON p.fk_input_reason = dr.rowid';
1651 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON p.fk_incoterms = i.rowid';
1652
1653 if (!empty($ref)) {
1654 if (!empty($forceentity)) {
1655 $sql .= " WHERE p.entity = ".(int) $forceentity; // Check only the current entity because we may have the same reference in several entities
1656 } else {
1657 $sql .= " WHERE p.entity IN (".getEntity('propal').")";
1658 }
1659 $sql .= " AND p.ref='".$this->db->escape($ref)."'";
1660 } else {
1661 // Don't use entity if you use rowid
1662 $sql .= " WHERE p.rowid = ".((int) $rowid);
1663 }
1664
1665 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1666 $resql = $this->db->query($sql);
1667 if ($resql) {
1668 if ($this->db->num_rows($resql)) {
1669 $obj = $this->db->fetch_object($resql);
1670
1671 $this->id = $obj->rowid;
1672 $this->entity = $obj->entity;
1673
1674 $this->ref = $obj->ref;
1675 $this->ref_client = $obj->ref_client;
1676 $this->ref_customer = $obj->ref_client;
1677 $this->ref_ext = $obj->ref_ext;
1678
1679 $this->total = $obj->total_ttc; // TODO deprecated
1680 $this->total_ttc = $obj->total_ttc;
1681 $this->total_ht = $obj->total_ht;
1682 $this->total_tva = $obj->total_tva;
1683 $this->total_localtax1 = $obj->localtax1;
1684 $this->total_localtax2 = $obj->localtax2;
1685
1686 $this->socid = $obj->fk_soc;
1687 $this->thirdparty = null; // Clear if another value was already set by fetch_thirdparty
1688
1689 $this->fk_project = $obj->fk_project;
1690 $this->project = null; // Clear if another value was already set by fetchProject
1691
1692 $this->model_pdf = $obj->model_pdf;
1693 $this->last_main_doc = $obj->last_main_doc;
1694 $this->note = $obj->note_private; // TODO deprecated
1695 $this->note_private = $obj->note_private;
1696 $this->note_public = $obj->note_public;
1697
1698 $this->status = (int) $obj->fk_statut;
1699 $this->statut = $this->status; // deprecated
1700
1701 $this->datec = $this->db->jdate($obj->datec); // TODO deprecated
1702 $this->datev = $this->db->jdate($obj->datev); // TODO deprecated
1703 $this->date_creation = $this->db->jdate($obj->datec); //Creation date
1704 $this->date_validation = $this->db->jdate($obj->datev); //Validation date
1705 $this->date_modification = $this->db->jdate($obj->date_modification); // tms
1706 $this->date_signature = $this->db->jdate($obj->dates); // Signature date
1707 $this->date = $this->db->jdate($obj->dp); // Proposal date
1708 $this->datep = $this->db->jdate($obj->dp); // deprecated
1709 $this->fin_validite = $this->db->jdate($obj->dfv);
1710 $this->delivery_date = $this->db->jdate($obj->delivery_date);
1711 $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1712 $this->warehouse_id = ($obj->fk_warehouse > 0) ? $obj->fk_warehouse : null;
1713 $this->availability_id = $obj->fk_availability;
1714 $this->availability_code = $obj->availability_code;
1715 $this->availability = $obj->availability;
1716 $this->demand_reason_id = $obj->fk_input_reason;
1717 $this->demand_reason_code = $obj->demand_reason_code;
1718 $this->demand_reason = $obj->demand_reason;
1719 $this->fk_address = $obj->fk_delivery_address;
1720
1721 $this->mode_reglement_id = $obj->fk_mode_reglement;
1722 $this->mode_reglement_code = $obj->mode_reglement_code;
1723 $this->mode_reglement = $obj->mode_reglement;
1724 $this->fk_account = ($obj->fk_account > 0) ? $obj->fk_account : null;
1725 $this->cond_reglement_id = $obj->fk_cond_reglement;
1726 $this->cond_reglement_code = $obj->cond_reglement_code;
1727 $this->cond_reglement = $obj->cond_reglement;
1728 $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1729 $this->deposit_percent = $obj->deposit_percent;
1730
1731 $this->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
1732
1733 $this->user_author_id = $obj->fk_user_author;
1734 $this->user_validation_id = $obj->fk_user_valid;
1735 $this->user_closing_id = $obj->fk_user_cloture;
1736
1737 //Incoterms
1738 $this->fk_incoterms = $obj->fk_incoterms;
1739 $this->location_incoterms = $obj->location_incoterms;
1740 $this->label_incoterms = $obj->label_incoterms;
1741
1742 // Multicurrency
1743 $this->fk_multicurrency = $obj->fk_multicurrency;
1744 $this->multicurrency_code = $obj->multicurrency_code;
1745 $this->multicurrency_tx = $obj->multicurrency_tx;
1746 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1747 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1748 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1749
1750 // Retrieve all extrafield
1751 // fetch optionals attributes and labels
1752 $this->fetch_optionals();
1753
1754 $this->db->free($resql);
1755
1756 $this->lines = array();
1757
1758 // Lines
1759 $result = $this->fetch_lines();
1760 if ($result < 0) {
1761 return -3;
1762 }
1763
1764 return 1;
1765 }
1766
1767 $this->error = "Record Not Found";
1768 return 0;
1769 } else {
1770 $this->error = $this->db->lasterror();
1771 return -1;
1772 }
1773 }
1774
1782 public function update(User $user, $notrigger = 0)
1783 {
1784 global $conf;
1785
1786 $error = 0;
1787
1788 // Clean parameters
1789 if (isset($this->ref)) {
1790 $this->ref = trim($this->ref);
1791 }
1792 if (isset($this->ref_client)) {
1793 $this->ref_client = trim($this->ref_client);
1794 }
1795 if (isset($this->note) || isset($this->note_private)) {
1796 $this->note_private = (isset($this->note_private) ? trim($this->note_private) : trim($this->note));
1797 }
1798 if (isset($this->note_public)) {
1799 $this->note_public = trim($this->note_public);
1800 }
1801 if (isset($this->model_pdf)) {
1802 $this->model_pdf = trim($this->model_pdf);
1803 }
1804 if (isset($this->import_key)) {
1805 $this->import_key = trim($this->import_key);
1806 }
1807 if (!empty($this->duree_validite) && is_numeric($this->duree_validite)) {
1808 $this->fin_validite = $this->date + ($this->duree_validite * 24 * 3600);
1809 }
1810
1811 // Check parameters
1812 // Put here code to add control on parameters values
1813
1814 // Update request
1815 $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET";
1816 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1817 $sql .= " ref_client=".(isset($this->ref_client) ? "'".$this->db->escape($this->ref_client)."'" : "null").",";
1818 $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1819 $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
1820 $sql .= " datep=".(strval($this->date) != '' ? "'".$this->db->idate($this->date)."'" : 'null').",";
1821 if (!empty($this->fin_validite)) {
1822 $sql .= " fin_validite=".(strval($this->fin_validite) != '' ? "'".$this->db->idate($this->fin_validite)."'" : 'null').",";
1823 }
1824 $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
1825 $sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
1826 $sql .= " localtax1=".(isset($this->total_localtax1) ? $this->total_localtax1 : "null").",";
1827 $sql .= " localtax2=".(isset($this->total_localtax2) ? $this->total_localtax2 : "null").",";
1828 $sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
1829 $sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
1830 $sql .= " fk_statut=".(isset($this->status) ? $this->status : "null").",";
1831 $sql .= " fk_user_author=".(isset($this->user_author_id) ? $this->user_author_id : "null").",";
1832 $sql .= " fk_user_valid=".(isset($this->user_validation_id) ? $this->user_validation_id : "null").",";
1833 $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
1834 $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
1835 $sql .= " deposit_percent=".(!empty($this->deposit_percent) ? "'".$this->db->escape($this->deposit_percent)."'" : "null").",";
1836 $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
1837 $sql .= " fk_input_reason=".(isset($this->demand_reason_id) ? $this->demand_reason_id : "null").",";
1838 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1839 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1840 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1841 $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
1842 $sql .= " WHERE rowid=".((int) $this->id);
1843
1844 $this->db->begin();
1845
1846 dol_syslog(get_class($this)."::update", LOG_DEBUG);
1847 $resql = $this->db->query($sql);
1848 if (!$resql) {
1849 $error++;
1850 $this->errors[] = "Error ".$this->db->lasterror();
1851 }
1852
1853 if (!$error) {
1854 $result = $this->insertExtraFields();
1855 if ($result < 0) {
1856 $error++;
1857 }
1858 }
1859
1860 if (!$error && !$notrigger) {
1861 // Call trigger
1862 $result = $this->call_trigger('PROPAL_MODIFY', $user);
1863 if ($result < 0) {
1864 $error++;
1865 }
1866 // End call triggers
1867 }
1868
1869 // Commit or rollback
1870 if ($error) {
1871 foreach ($this->errors as $errmsg) {
1872 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1873 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1874 }
1875 $this->db->rollback();
1876 return -1 * $error;
1877 } else {
1878 $this->db->commit();
1879 return 1;
1880 }
1881 }
1882
1883
1884 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1893 public function fetch_lines($only_product = 0, $loadalsotranslation = 0, $sqlforgedfilters = '')
1894 {
1895 // phpcs:enable
1896 $this->lines = array();
1897
1898 $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,';
1899 $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,';
1900 $sql .= ' d.fk_unit,';
1901 $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,';
1902 $sql .= ' p.weight, p.weight_units, p.volume, p.volume_units,';
1903 $sql .= ' d.date_start, d.date_end,';
1904 $sql .= ' d.fk_multicurrency, d.multicurrency_code, d.multicurrency_subprice, d.multicurrency_total_ht, d.multicurrency_total_tva, d.multicurrency_total_ttc';
1905 $sql .= ' FROM '.MAIN_DB_PREFIX.'propaldet as d';
1906 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (d.fk_product = p.rowid)';
1907 $sql .= ' WHERE d.fk_propal = '.((int) $this->id);
1908 if ($only_product) {
1909 $sql .= ' AND p.fk_product_type = 0';
1910 }
1911 if ($sqlforgedfilters) {
1912 $sql .= $sqlforgedfilters;
1913 }
1914 $sql .= ' ORDER by d.rang';
1915
1916 dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
1917 $result = $this->db->query($sql);
1918 if ($result) {
1919 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
1920
1921 $num = $this->db->num_rows($result);
1922
1923 $i = 0;
1924 while ($i < $num) {
1925 $objp = $this->db->fetch_object($result);
1926
1927 $line = new PropaleLigne($this->db);
1928
1929 $line->rowid = $objp->rowid; //Deprecated
1930 $line->id = $objp->rowid;
1931 $line->fk_propal = $objp->fk_propal;
1932 $line->fk_parent_line = $objp->fk_parent_line;
1933 $line->product_type = $objp->product_type;
1934 $line->label = $objp->custom_label;
1935 $line->desc = $objp->description; // Description ligne
1936 $line->description = $objp->description; // Description ligne
1937 $line->qty = $objp->qty;
1938 $line->vat_src_code = $objp->vat_src_code;
1939 $line->tva_tx = $objp->tva_tx;
1940 $line->localtax1_tx = $objp->localtax1_tx;
1941 $line->localtax2_tx = $objp->localtax2_tx;
1942 $line->localtax1_type = $objp->localtax1_type;
1943 $line->localtax2_type = $objp->localtax2_type;
1944 $line->subprice = $objp->subprice;
1945 $line->fk_remise_except = $objp->fk_remise_except;
1946 $line->remise_percent = $objp->remise_percent;
1947 $line->price = $objp->price; // TODO deprecated
1948
1949 $line->info_bits = $objp->info_bits;
1950 $line->total_ht = $objp->total_ht;
1951 $line->total_tva = $objp->total_tva;
1952 $line->total_localtax1 = $objp->total_localtax1;
1953 $line->total_localtax2 = $objp->total_localtax2;
1954 $line->total_ttc = $objp->total_ttc;
1955 $line->fk_fournprice = $objp->fk_fournprice;
1956 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1957 $line->pa_ht = $marginInfos[0];
1958 $line->marge_tx = $marginInfos[1];
1959 $line->marque_tx = $marginInfos[2];
1960 $line->special_code = $objp->special_code;
1961 $line->rang = $objp->rang;
1962
1963 $line->fk_product = $objp->fk_product;
1964
1965 $line->ref = $objp->product_ref; // deprecated
1966 $line->libelle = $objp->product_label; // deprecated
1967
1968 $line->product_ref = $objp->product_ref;
1969 $line->product_label = $objp->product_label;
1970 $line->product_desc = $objp->product_desc; // Description produit
1971 $line->product_tobatch = $objp->product_tobatch;
1972 $line->product_barcode = $objp->product_barcode;
1973
1974 $line->fk_product_type = $objp->fk_product_type; // deprecated
1975 $line->fk_unit = $objp->fk_unit;
1976 $line->weight = $objp->weight;
1977 $line->weight_units = $objp->weight_units;
1978 $line->volume = $objp->volume;
1979 $line->volume_units = $objp->volume_units;
1980
1981 $line->date_start = $this->db->jdate($objp->date_start);
1982 $line->date_end = $this->db->jdate($objp->date_end);
1983
1984 // Multicurrency
1985 $line->fk_multicurrency = $objp->fk_multicurrency;
1986 $line->multicurrency_code = $objp->multicurrency_code;
1987 $line->multicurrency_subprice = $objp->multicurrency_subprice;
1988 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
1989 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
1990 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
1991
1992 $line->fetch_optionals();
1993
1994 // multilangs
1995 if (getDolGlobalInt('MAIN_MULTILANGS') && !empty($objp->fk_product) && !empty($loadalsotranslation)) {
1996 $tmpproduct = new Product($this->db);
1997 $tmpproduct->fetch($objp->fk_product);
1998 $tmpproduct->getMultiLangs();
1999
2000 $line->multilangs = $tmpproduct->multilangs;
2001 }
2002
2003 $this->lines[$i] = $line;
2004
2005 $i++;
2006 }
2007
2008 $this->db->free($result);
2009
2010 return $num;
2011 } else {
2012 $this->error = $this->db->lasterror();
2013 return -3;
2014 }
2015 }
2016
2024 public function valid($user, $notrigger = 0)
2025 {
2026 global $conf;
2027
2028 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2029
2030 $error = 0;
2031
2032 // Protection
2033 if ($this->status == self::STATUS_VALIDATED) {
2034 dol_syslog(get_class($this)."::valid action abandoned: already validated", LOG_WARNING);
2035 return 0;
2036 }
2037
2038 if (!((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('propal', 'creer'))
2039 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('propal', 'propal_advance', 'validate')))) {
2040 $this->error = 'ErrorPermissionDenied';
2041 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
2042 return -1;
2043 }
2044
2045 $now = dol_now();
2046
2047 $this->db->begin();
2048
2049 // Numbering module definition
2050 $soc = new Societe($this->db);
2051 $soc->fetch($this->socid);
2052
2053 // Define new ref
2054 if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
2055 $num = $this->getNextNumRef($soc);
2056 } else {
2057 $num = $this->ref;
2058 }
2059 $this->newref = dol_sanitizeFileName($num);
2060
2061 $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2062 $sql .= " SET ref = '".$this->db->escape($num)."',";
2063 $sql .= " fk_statut = ".self::STATUS_VALIDATED.", date_valid='".$this->db->idate($now)."', fk_user_valid=".((int) $user->id);
2064 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = ".self::STATUS_DRAFT;
2065
2066 dol_syslog(get_class($this)."::valid", LOG_DEBUG);
2067 $resql = $this->db->query($sql);
2068 if (!$resql) {
2069 dol_print_error($this->db);
2070 $error++;
2071 }
2072
2073 // Trigger calls
2074 if (!$error && !$notrigger) {
2075 // Call trigger
2076 $result = $this->call_trigger('PROPAL_VALIDATE', $user);
2077 if ($result < 0) {
2078 $error++;
2079 }
2080 // End call triggers
2081 }
2082
2083 if (!$error) {
2084 $this->oldref = $this->ref;
2085
2086 // Rename directory if dir was a temporary ref
2087 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
2088 // Now we rename also files into index
2089 $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)."'";
2090 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'propale/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
2091 $resql = $this->db->query($sql);
2092 if (!$resql) {
2093 $error++;
2094 $this->error = $this->db->lasterror();
2095 }
2096 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'propale/".$this->db->escape($this->newref)."'";
2097 $sql .= " WHERE filepath = 'propale/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
2098 $resql = $this->db->query($sql);
2099 if (!$resql) {
2100 $error++;
2101 $this->error = $this->db->lasterror();
2102 }
2103
2104 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
2105 $oldref = dol_sanitizeFileName($this->ref);
2106 $newref = dol_sanitizeFileName($num);
2107 $dirsource = $conf->propal->multidir_output[$this->entity].'/'.$oldref;
2108 $dirdest = $conf->propal->multidir_output[$this->entity].'/'.$newref;
2109 if (!$error && file_exists($dirsource)) {
2110 dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
2111 if (@rename($dirsource, $dirdest)) {
2112 dol_syslog("Rename ok");
2113 // Rename docs starting with $oldref with $newref
2114 $listoffiles = dol_dir_list($dirdest, 'files', 1, '^'.preg_quote($oldref, '/'));
2115 foreach ($listoffiles as $fileentry) {
2116 $dirsource = $fileentry['name'];
2117 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
2118 $dirsource = $fileentry['path'].'/'.$dirsource;
2119 $dirdest = $fileentry['path'].'/'.$dirdest;
2120 @rename($dirsource, $dirdest);
2121 }
2122 }
2123 }
2124 }
2125
2126 $this->ref = $num;
2127 $this->statut = self::STATUS_VALIDATED;
2129 $this->user_validation_id = $user->id;
2130 $this->datev = $now;
2131 $this->date_validation = $now;
2132
2133 $this->db->commit();
2134 return 1;
2135 } else {
2136 $this->db->rollback();
2137 return -1;
2138 }
2139 }
2140
2141
2142 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2151 public function set_date($user, $date, $notrigger = 0)
2152 {
2153 // phpcs:enable
2154 if (empty($date)) {
2155 $this->error = 'ErrorBadParameter';
2156 dol_syslog(get_class($this)."::set_date ".$this->error, LOG_ERR);
2157 return -1;
2158 }
2159
2160 if ($user->hasRight('propal', 'creer')) {
2161 $error = 0;
2162
2163 $this->db->begin();
2164
2165 $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET datep = '".$this->db->idate($date)."'";
2166 $sql .= " WHERE rowid = ".((int) $this->id);
2167
2168 dol_syslog(__METHOD__, LOG_DEBUG);
2169 $resql = $this->db->query($sql);
2170 if (!$resql) {
2171 $this->errors[] = $this->db->error();
2172 $error++;
2173 }
2174
2175 if (!$error) {
2176 $this->oldcopy = clone $this;
2177 $this->date = $date;
2178 $this->datep = $date; // deprecated
2179 }
2180
2181 if (!$notrigger && empty($error)) {
2182 // Call trigger
2183 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2184 if ($result < 0) {
2185 $error++;
2186 }
2187 // End call triggers
2188 }
2189
2190 if (!$error) {
2191 $this->db->commit();
2192 return 1;
2193 } else {
2194 foreach ($this->errors as $errmsg) {
2195 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2196 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2197 }
2198 $this->db->rollback();
2199 return -1 * $error;
2200 }
2201 }
2202
2203 return -1;
2204 }
2205
2206 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2215 public function set_echeance($user, $date_end_validity, $notrigger = 0)
2216 {
2217 // phpcs:enable
2218 if ($user->hasRight('propal', 'creer')) {
2219 $error = 0;
2220
2221 $this->db->begin();
2222
2223 $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET fin_validite = ".($date_end_validity != '' ? "'".$this->db->idate($date_end_validity)."'" : 'null');
2224 $sql .= " WHERE rowid = ".((int) $this->id);
2225
2226 dol_syslog(__METHOD__, LOG_DEBUG);
2227
2228 $resql = $this->db->query($sql);
2229 if (!$resql) {
2230 $this->errors[] = $this->db->error();
2231 $error++;
2232 }
2233
2234
2235 if (!$error) {
2236 $this->oldcopy = clone $this;
2237 $this->fin_validite = $date_end_validity;
2238 }
2239
2240 if (!$notrigger && empty($error)) {
2241 // Call trigger
2242 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2243 if ($result < 0) {
2244 $error++;
2245 }
2246 // End call triggers
2247 }
2248
2249 if (!$error) {
2250 $this->db->commit();
2251 return 1;
2252 } else {
2253 foreach ($this->errors as $errmsg) {
2254 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2255 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2256 }
2257 $this->db->rollback();
2258 return -1 * $error;
2259 }
2260 }
2261
2262 return -1;
2263 }
2264
2265 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2275 public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2276 {
2277 // phpcs:enable
2278 return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2279 }
2280
2289 public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2290 {
2291 if ($user->hasRight('propal', 'creer')) {
2292 $error = 0;
2293
2294 $this->db->begin();
2295
2296 $sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
2297 $sql .= " SET date_livraison = ".(isDolTms($delivery_date) ? "'".$this->db->idate($delivery_date)."'" : 'null');
2298 $sql .= " WHERE rowid = ".((int) $this->id);
2299
2300 dol_syslog(__METHOD__, LOG_DEBUG);
2301 $resql = $this->db->query($sql);
2302 if (!$resql) {
2303 $this->errors[] = $this->db->error();
2304 $error++;
2305 }
2306
2307 if (!$error) {
2308 $this->oldcopy = clone $this;
2309 $this->delivery_date = $delivery_date;
2310 }
2311
2312 if (!$notrigger && empty($error)) {
2313 // Call trigger
2314 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2315 if ($result < 0) {
2316 $error++;
2317 }
2318 // End call triggers
2319 }
2320
2321 if (!$error) {
2322 $this->db->commit();
2323 return 1;
2324 } else {
2325 foreach ($this->errors as $errmsg) {
2326 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2327 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2328 }
2329 $this->db->rollback();
2330 return -1 * $error;
2331 }
2332 }
2333
2334 return -1;
2335 }
2336
2337 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2346 public function set_availability($user, $id, $notrigger = 0)
2347 {
2348 // phpcs:enable
2349 if ($user->hasRight('propal', 'creer') && $this->status >= self::STATUS_DRAFT) {
2350 $error = 0;
2351
2352 $this->db->begin();
2353
2354 $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2355 $sql .= " SET fk_availability = ".((int) $id);
2356 $sql .= " WHERE rowid = ".((int) $this->id);
2357
2358 dol_syslog(__METHOD__.' availability('.$id.')', LOG_DEBUG);
2359 $resql = $this->db->query($sql);
2360 if (!$resql) {
2361 $this->errors[] = $this->db->error();
2362 $error++;
2363 }
2364
2365 if (!$error) {
2366 $this->oldcopy = clone $this;
2367 $this->fk_availability = $id;
2368 $this->availability_id = $id;
2369 }
2370
2371 if (!$notrigger && empty($error)) {
2372 // Call trigger
2373 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2374 if ($result < 0) {
2375 $error++;
2376 }
2377 // End call triggers
2378 }
2379
2380 if (!$error) {
2381 $this->db->commit();
2382 return 1;
2383 } else {
2384 foreach ($this->errors as $errmsg) {
2385 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2386 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2387 }
2388 $this->db->rollback();
2389 return -1 * $error;
2390 }
2391 } else {
2392 $error_str = 'Propal status do not meet requirement '.$this->status;
2393 dol_syslog(__METHOD__.$error_str, LOG_ERR);
2394 $this->error = $error_str;
2395 $this->errors[] = $this->error;
2396 return -2;
2397 }
2398 }
2399
2400 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2409 public function set_demand_reason($user, $id, $notrigger = 0)
2410 {
2411 // phpcs:enable
2412 if ($user->hasRight('propal', 'creer') && $this->status >= self::STATUS_DRAFT) {
2413 $error = 0;
2414
2415 $this->db->begin();
2416
2417 $sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
2418 $sql .= " SET fk_input_reason = ".((int) $id);
2419 $sql .= " WHERE rowid = ".((int) $this->id);
2420
2421 dol_syslog(__METHOD__, LOG_DEBUG);
2422 $resql = $this->db->query($sql);
2423 if (!$resql) {
2424 $this->errors[] = $this->db->error();
2425 $error++;
2426 }
2427
2428
2429 if (!$error) {
2430 $this->oldcopy = clone $this;
2431
2432 $this->demand_reason_id = $id;
2433 }
2434
2435
2436 if (!$notrigger && empty($error)) {
2437 // Call trigger
2438 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2439 if ($result < 0) {
2440 $error++;
2441 }
2442 // End call triggers
2443 }
2444
2445 if (!$error) {
2446 $this->db->commit();
2447 return 1;
2448 } else {
2449 foreach ($this->errors as $errmsg) {
2450 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2451 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2452 }
2453 $this->db->rollback();
2454 return -1 * $error;
2455 }
2456 } else {
2457 $error_str = 'Propal status do not meet requirement '.$this->status;
2458 dol_syslog(__METHOD__.$error_str, LOG_ERR);
2459 $this->error = $error_str;
2460 $this->errors[] = $this->error;
2461 return -2;
2462 }
2463 }
2464
2465 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2474 public function set_ref_client($user, $ref_client, $notrigger = 0)
2475 {
2476 // phpcs:enable
2477 if ($user->hasRight('propal', 'creer')) {
2478 $error = 0;
2479
2480 $this->db->begin();
2481
2482 $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET ref_client = ".(empty($ref_client) ? 'NULL' : "'".$this->db->escape($ref_client)."'");
2483 $sql .= " WHERE rowid = ".((int) $this->id);
2484
2485 dol_syslog(__METHOD__.' $this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
2486 $resql = $this->db->query($sql);
2487 if (!$resql) {
2488 $this->errors[] = $this->db->error();
2489 $error++;
2490 }
2491
2492 if (!$error) {
2493 $this->oldcopy = clone $this;
2494 $this->ref_client = $ref_client;
2495 }
2496
2497 if (!$notrigger && empty($error)) {
2498 // Call trigger
2499 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2500 if ($result < 0) {
2501 $error++;
2502 }
2503 // End call triggers
2504 }
2505
2506 if (!$error) {
2507 $this->db->commit();
2508 return 1;
2509 } else {
2510 foreach ($this->errors as $errmsg) {
2511 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2512 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2513 }
2514 $this->db->rollback();
2515 return -1 * $error;
2516 }
2517 }
2518
2519 return -1;
2520 }
2521
2522
2532 public function reopen($user, $status, $note = '', $notrigger = 0)
2533 {
2534 $error = 0;
2535
2536 $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2537 $sql .= " SET fk_statut = ".((int) $status).",";
2538 if (!empty($note)) {
2539 $sql .= " note_private = '".$this->db->escape($note)."',";
2540 }
2541 $sql .= " date_cloture=NULL, fk_user_cloture=NULL";
2542 $sql .= " WHERE rowid = ".((int) $this->id);
2543
2544 $this->db->begin();
2545
2546 dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
2547 $resql = $this->db->query($sql);
2548 if (!$resql) {
2549 $error++;
2550 $this->errors[] = "Error ".$this->db->lasterror();
2551 }
2552 if (!$error) {
2553 if (!$notrigger) {
2554 // Call trigger
2555 $result = $this->call_trigger('PROPAL_REOPEN', $user);
2556 if ($result < 0) {
2557 $error++;
2558 }
2559 // End call triggers
2560 }
2561 }
2562
2563 // Commit or rollback
2564 if ($error) {
2565 if (!empty($this->errors)) {
2566 foreach ($this->errors as $errmsg) {
2567 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
2568 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2569 }
2570 }
2571 $this->db->rollback();
2572 return -1 * $error;
2573 } else {
2574 $this->statut = $status;
2575 $this->status = $status;
2576
2577 $this->db->commit();
2578 return 1;
2579 }
2580 }
2581
2592 public function closeProposal($user, $status, $note_private = '', $notrigger = 0, $note_public = '')
2593 {
2594 global $langs,$conf;
2595
2596 $error = 0;
2597 $now = dol_now();
2598
2599 $this->db->begin();
2600
2601 $newprivatenote = dol_concatdesc($this->note_private, $note_private);
2602 $newpublicnote = dol_concatdesc($this->note_public, $note_public);
2603
2604 if (!getDolGlobalString('PROPALE_KEEP_OLD_SIGNATURE_INFO')) {
2605 $date_signature = $now;
2606 $fk_user_signature = $user->id;
2607 } else {
2608 $this->info($this->id);
2609 if (!isset($this->date_signature) || $this->date_signature == '') {
2610 $date_signature = $now;
2611 $fk_user_signature = $user->id;
2612 } else {
2613 $date_signature = $this->date_signature;
2614 $fk_user_signature = $this->user_signature->id;
2615 }
2616 }
2617
2618 $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2619 $sql .= " SET fk_statut = ".((int) $status).", note_private = '".$this->db->escape($newprivatenote)."', note_public = '".$this->db->escape($newpublicnote)."'";
2620 if ($status == self::STATUS_SIGNED) {
2621 $sql .= ", date_signature='".$this->db->idate($now)."', fk_user_signature = ".($fk_user_signature);
2622 }
2623 $sql .= " WHERE rowid = ".((int) $this->id);
2624
2625 $resql = $this->db->query($sql);
2626 if ($resql) {
2627 // Status self::STATUS_REFUSED by default
2628 $modelpdf = getDolGlobalString('PROPALE_ADDON_PDF_ODT_CLOSED', $this->model_pdf);
2629 $trigger_name = 'PROPAL_CLOSE_REFUSED'; // used later in call_trigger()
2630
2631 if ($status == self::STATUS_SIGNED) { // Status self::STATUS_SIGNED
2632 $trigger_name = 'PROPAL_CLOSE_SIGNED'; // used later in call_trigger()
2633 $modelpdf = getDolGlobalString('PROPALE_ADDON_PDF_ODT_TOBILL') ? $conf->global->PROPALE_ADDON_PDF_ODT_TOBILL : $this->model_pdf;
2634
2635 // The connected company is classified as a client
2636 $soc = new Societe($this->db);
2637 $soc->id = $this->socid;
2638 $result = $soc->setAsCustomer();
2639
2640 if ($result < 0) {
2641 $this->error = $this->db->lasterror();
2642 $this->db->rollback();
2643 return -2;
2644 }
2645 }
2646
2647 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE') && !getDolGlobalInt('PROPAL_DISABLE_AUTOUPDATE_ON_CLOSE')) {
2648 // Define output language
2649 $outputlangs = $langs;
2650 if (getDolGlobalInt('MAIN_MULTILANGS')) {
2651 $outputlangs = new Translate("", $conf);
2652 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
2653 $outputlangs->setDefaultLang($newlang);
2654 }
2655
2656 // PDF
2657 $hidedetails = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 1 : 0);
2658 $hidedesc = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 1 : 0);
2659 $hideref = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 1 : 0);
2660
2661 //$ret=$object->fetch($id); // Reload to get new records
2662 $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
2663 }
2664
2665 if (!$error) {
2666 $this->oldcopy = clone $this;
2667 $this->statut = $status;
2668 $this->status = $status;
2669 $this->date_signature = $date_signature;
2670 $this->note_private = $newprivatenote;
2671 }
2672
2673 if (!$notrigger && empty($error)) {
2674 // Call trigger
2675 $result = $this->call_trigger($trigger_name, $user);
2676 if ($result < 0) {
2677 $error++;
2678 }
2679 // End call triggers
2680 }
2681
2682 if (!$error) {
2683 $this->db->commit();
2684 return 1;
2685 } else {
2686 $this->statut = $this->oldcopy->status;
2687 $this->status = $this->oldcopy->status;
2688 $this->date_signature = $this->oldcopy->date_signature;
2689 $this->note_private = $this->oldcopy->note_private;
2690
2691 $this->db->rollback();
2692 return -1;
2693 }
2694 } else {
2695 $this->error = $this->db->lasterror();
2696 $this->db->rollback();
2697 return -1;
2698 }
2699 }
2700
2709 public function classifyBilled(User $user, $notrigger = 0, $note = '')
2710 {
2711 global $conf, $langs;
2712
2713 $error = 0;
2714
2715 $now = dol_now();
2716 $num = 0;
2717
2718 $triggerName = 'PROPAL_CLASSIFY_BILLED';
2719
2720 $this->db->begin();
2721
2722 $newprivatenote = dol_concatdesc($this->note_private, $note);
2723
2724 $sql = 'UPDATE '.MAIN_DB_PREFIX.'propal SET fk_statut = '.self::STATUS_BILLED.", ";
2725 $sql .= " note_private = '".$this->db->escape($newprivatenote)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".((int) $user->id);
2726 $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.((int) self::STATUS_SIGNED);
2727
2728 dol_syslog(__METHOD__, LOG_DEBUG);
2729 $resql = $this->db->query($sql);
2730 if (!$resql) {
2731 $this->errors[] = $this->db->error();
2732 $error++;
2733 } else {
2734 $num = $this->db->affected_rows($resql);
2735 }
2736
2737 if (!$error) {
2738 $modelpdf = getDolGlobalString('PROPALE_ADDON_PDF_ODT_CLOSED', $this->model_pdf);
2739
2740 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
2741 // Define output language
2742 $outputlangs = $langs;
2743 if (getDolGlobalInt('MAIN_MULTILANGS')) {
2744 $outputlangs = new Translate("", $conf);
2745 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
2746 $outputlangs->setDefaultLang($newlang);
2747 }
2748
2749 // PDF
2750 $hidedetails = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 1 : 0);
2751 $hidedesc = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 1 : 0);
2752 $hideref = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 1 : 0);
2753
2754 //$ret=$object->fetch($id); // Reload to get new records
2755 $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
2756 }
2757
2758 $this->oldcopy = clone $this;
2759 $this->statut = self::STATUS_BILLED;
2760 $this->status = self::STATUS_BILLED;
2761 $this->date_cloture = $now;
2762 $this->note_private = $newprivatenote;
2763 }
2764
2765 if (!$notrigger && empty($error)) {
2766 // Call trigger
2767 $result = $this->call_trigger($triggerName, $user);
2768 if ($result < 0) {
2769 $error++;
2770 }
2771 // End call triggers
2772 }
2773
2774 if (!$error) {
2775 $this->db->commit();
2776 return $num;
2777 } else {
2778 foreach ($this->errors as $errmsg) {
2779 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2780 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2781 }
2782 $this->db->rollback();
2783 return -1 * $error;
2784 }
2785 }
2786
2793 public function setCancel(User $user)
2794 {
2795 $error = 0;
2796
2797 $this->db->begin();
2798
2799 $sql = "UPDATE ". MAIN_DB_PREFIX . "propal";
2800 $sql .= " SET fk_statut = " . self::STATUS_CANCELED . ",";
2801 $sql .= " fk_user_modif = " . ((int) $user->id);
2802 $sql .= " WHERE rowid = " . ((int) $this->id);
2803
2804 dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
2805 if ($this->db->query($sql)) {
2806 if (!$error) {
2807 // Call trigger
2808 $result = $this->call_trigger('PROPAL_CANCEL', $user);
2809 if ($result < 0) {
2810 $error++;
2811 }
2812 // End call triggers
2813 }
2814
2815 if (!$error) {
2816 $this->statut = self::STATUS_CANCELED;
2818 $this->db->commit();
2819 return 1;
2820 } else {
2821 foreach ($this->errors as $errmsg) {
2822 dol_syslog(get_class($this)."::cancel ".$errmsg, LOG_ERR);
2823 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2824 }
2825 $this->db->rollback();
2826 return -1;
2827 }
2828 } else {
2829 $this->error = $this->db->error();
2830 $this->db->rollback();
2831 return -1;
2832 }
2833 }
2834
2835 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2843 public function setDraft($user, $notrigger = 0)
2844 {
2845 // phpcs:enable
2846 $error = 0;
2847
2848 // Protection
2849 if ($this->status <= self::STATUS_DRAFT) {
2850 return 0;
2851 }
2852
2853 dol_syslog(get_class($this)."::setDraft", LOG_DEBUG);
2854
2855 $this->db->begin();
2856
2857 $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2858 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
2859 $sql .= ", online_sign_ip = NULL , online_sign_name = NULL";
2860 $sql .= " WHERE rowid = ".((int) $this->id);
2861
2862 $resql = $this->db->query($sql);
2863 if (!$resql) {
2864 $this->errors[] = $this->db->error();
2865 $error++;
2866 }
2867
2868 if (!$error) {
2869 $this->oldcopy = clone $this;
2870 }
2871
2872 if (!$notrigger && empty($error)) {
2873 // Call trigger
2874 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2875 if ($result < 0) {
2876 $error++;
2877 }
2878 // End call triggers
2879 }
2880
2881 if (!$error) {
2882 $this->statut = self::STATUS_DRAFT;
2883 $this->status = self::STATUS_DRAFT;
2884
2885 $this->db->commit();
2886 return 1;
2887 } else {
2888 foreach ($this->errors as $errmsg) {
2889 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2890 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2891 }
2892 $this->db->rollback();
2893 return -1 * $error;
2894 }
2895 }
2896
2897
2898 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2912 public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datep', $sortorder = 'DESC')
2913 {
2914 // phpcs:enable
2915 global $user;
2916
2917 $ga = array();
2918
2919 $sql = "SELECT s.rowid, s.nom as name, s.client,";
2920 $sql .= " p.rowid as propalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
2921 $sql .= " p.datep as dp, p.fin_validite as datelimite";
2922 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."propal as p, ".MAIN_DB_PREFIX."c_propalst as c";
2923 $sql .= " WHERE p.entity IN (".getEntity('propal').")";
2924 $sql .= " AND p.fk_soc = s.rowid";
2925 $sql .= " AND p.fk_statut = c.id";
2926
2927 // If the internal user must only see his customers, force searching by him
2928 $search_sale = 0;
2929 if (!$user->hasRight('societe', 'client', 'voir')) {
2930 $search_sale = $user->id;
2931 }
2932 // Search on sale representative
2933 if ($search_sale && $search_sale != '-1') {
2934 if ($search_sale == -2) {
2935 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
2936 } elseif ($search_sale > 0) {
2937 $sql .= " AND EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc AND sc.fk_user = ".((int) $search_sale).")";
2938 }
2939 }
2940 // Search on socid
2941 if ($socid) {
2942 $sql .= " AND p.fk_soc = ".((int) $socid);
2943 }
2944 if ($draft) {
2945 $sql .= " AND p.fk_statut = ".((int) self::STATUS_DRAFT);
2946 }
2947 if ($notcurrentuser > 0) {
2948 $sql .= " AND p.fk_user_author <> ".((int) $user->id);
2949 }
2950 $sql .= $this->db->order($sortfield, $sortorder);
2951 $sql .= $this->db->plimit($limit, $offset);
2952
2953 $result = $this->db->query($sql);
2954 if ($result) {
2955 $num = $this->db->num_rows($result);
2956 if ($num) {
2957 $i = 0;
2958 while ($i < $num) {
2959 $obj = $this->db->fetch_object($result);
2960
2961 if ($shortlist == 1) {
2962 $ga[$obj->propalid] = $obj->ref;
2963 } elseif ($shortlist == 2) {
2964 $ga[$obj->propalid] = $obj->ref.' ('.$obj->name.')';
2965 } else {
2966 $ga[$i]['id'] = $obj->propalid;
2967 $ga[$i]['ref'] = $obj->ref;
2968 $ga[$i]['name'] = $obj->name;
2969 }
2970
2971 $i++;
2972 }
2973 }
2974 return $ga;
2975 } else {
2976 dol_print_error($this->db);
2977 return -1;
2978 }
2979 }
2980
2986 public function getInvoiceArrayList()
2987 {
2988 return $this->InvoiceArrayList($this->id);
2989 }
2990
2991 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2998 public function InvoiceArrayList($id)
2999 {
3000 // phpcs:enable
3001 $ga = array();
3002 $linkedInvoices = array();
3003
3004 $this->fetchObjectLinked($id, $this->element);
3005 foreach ($this->linkedObjectsIds as $objecttype => $objectid) {
3006 // Nouveau système du common object renvoi des rowid et non un id linéaire de 1 à n
3007 // On parcourt donc une liste d'objets en tant qu'objet unique
3008 foreach ($objectid as $key => $object) {
3009 // Cas des factures liees directement
3010 if ($objecttype == 'facture') {
3011 $linkedInvoices[] = $object;
3012 } else {
3013 // Cas des factures liees par un autre object (ex: commande)
3014 $this->fetchObjectLinked($object, $objecttype);
3015 foreach ($this->linkedObjectsIds as $subobjecttype => $subobjectid) {
3016 foreach ($subobjectid as $subkey => $subobject) {
3017 if ($subobjecttype == 'facture') {
3018 $linkedInvoices[] = $subobject;
3019 }
3020 }
3021 }
3022 }
3023 }
3024 }
3025
3026 if (count($linkedInvoices) > 0) {
3027 $sql = "SELECT rowid as facid, ref, total_ht as total, datef as df, fk_user_author, fk_statut, paye";
3028 $sql .= " FROM ".MAIN_DB_PREFIX."facture";
3029 $sql .= " WHERE rowid IN (".$this->db->sanitize(implode(',', $linkedInvoices)).")";
3030
3031 dol_syslog(get_class($this)."::InvoiceArrayList", LOG_DEBUG);
3032 $resql = $this->db->query($sql);
3033
3034 if ($resql) {
3035 $tab_sqlobj = array();
3036 $nump = $this->db->num_rows($resql);
3037 for ($i = 0; $i < $nump; $i++) {
3038 $sqlobj = $this->db->fetch_object($resql);
3039 $tab_sqlobj[] = $sqlobj;
3040 }
3041 $this->db->free($resql);
3042
3043 $nump = count($tab_sqlobj);
3044
3045 if ($nump) {
3046 $i = 0;
3047 while ($i < $nump) {
3048 $obj = array_shift($tab_sqlobj);
3049
3050 $ga[$i] = $obj;
3051
3052 $i++;
3053 }
3054 }
3055 return $ga;
3056 } else {
3057 return -1;
3058 }
3059 } else {
3060 return $ga;
3061 }
3062 }
3063
3071 public function delete($user, $notrigger = 0)
3072 {
3073 global $conf;
3074 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3075
3076 $error = 0;
3077
3078 $this->db->begin();
3079
3080 if (!$notrigger) {
3081 // Call trigger
3082 $result = $this->call_trigger('PROPAL_DELETE', $user);
3083 if ($result < 0) {
3084 $error++;
3085 }
3086 // End call triggers
3087 }
3088
3089 // Delete extrafields of lines and lines
3090 if (!$error && !empty($this->table_element_line)) {
3091 $tabletodelete = $this->table_element_line;
3092 $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).")";
3093 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id);
3094 if (!$this->db->query($sqlef) || !$this->db->query($sql)) {
3095 $error++;
3096 $this->error = $this->db->lasterror();
3097 $this->errors[] = $this->error;
3098 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3099 }
3100 }
3101
3102 if (!$error) {
3103 // Delete linked object
3104 $res = $this->deleteObjectLinked();
3105 if ($res < 0) {
3106 $error++;
3107 }
3108 }
3109
3110 if (!$error) {
3111 // Delete linked contacts
3112 $res = $this->delete_linked_contact();
3113 if ($res < 0) {
3114 $error++;
3115 }
3116 }
3117
3118 // Removed extrafields of object
3119 if (!$error) {
3120 $result = $this->deleteExtraFields();
3121 if ($result < 0) {
3122 $error++;
3123 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3124 }
3125 }
3126
3127 // Delete main record
3128 if (!$error) {
3129 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element." WHERE rowid = ".((int) $this->id);
3130 $res = $this->db->query($sql);
3131 if (!$res) {
3132 $error++;
3133 $this->error = $this->db->lasterror();
3134 $this->errors[] = $this->error;
3135 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3136 }
3137 }
3138
3139 // Delete record into ECM index and physically
3140 if (!$error) {
3141 $res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
3142 $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
3143 if (!$res) {
3144 $error++;
3145 }
3146 }
3147
3148 if (!$error) {
3149 // We remove directory
3150 $ref = dol_sanitizeFileName($this->ref);
3151 if ($conf->propal->multidir_output[$this->entity] && !empty($this->ref)) {
3152 $dir = $conf->propal->multidir_output[$this->entity]."/".$ref;
3153 $file = $dir."/".$ref.".pdf";
3154 if (file_exists($file)) {
3155 dol_delete_preview($this);
3156
3157 if (!dol_delete_file($file, 0, 0, 0, $this)) {
3158 $this->error = 'ErrorFailToDeleteFile';
3159 $this->errors[] = $this->error;
3160 $this->db->rollback();
3161 return 0;
3162 }
3163 }
3164 if (file_exists($dir)) {
3165 $res = @dol_delete_dir_recursive($dir); // delete files physically + into ecm tables
3166 if (!$res) {
3167 $this->error = 'ErrorFailToDeleteDir';
3168 $this->errors[] = $this->error;
3169 $this->db->rollback();
3170 return 0;
3171 }
3172 }
3173 }
3174 }
3175
3176 if (!$error) {
3177 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
3178 $this->db->commit();
3179 return 1;
3180 } else {
3181 $this->db->rollback();
3182 return -1;
3183 }
3184 }
3185
3194 public function availability($availability_id, $notrigger = 0)
3195 {
3196 global $user;
3197
3198 if ($this->status >= self::STATUS_DRAFT) {
3199 $error = 0;
3200
3201 $this->db->begin();
3202
3203 $sql = 'UPDATE '.MAIN_DB_PREFIX.'propal';
3204 $sql .= ' SET fk_availability = '.((int) $availability_id);
3205 $sql .= ' WHERE rowid='.((int) $this->id);
3206
3207 dol_syslog(__METHOD__.' availability('.$availability_id.')', LOG_DEBUG);
3208 $resql = $this->db->query($sql);
3209 if (!$resql) {
3210 $this->errors[] = $this->db->error();
3211 $error++;
3212 }
3213
3214 if (!$error) {
3215 $this->oldcopy = clone $this;
3216 $this->availability_id = $availability_id;
3217 }
3218
3219 if (!$notrigger && empty($error)) {
3220 // Call trigger
3221 $result = $this->call_trigger('PROPAL_MODIFY', $user);
3222 if ($result < 0) {
3223 $error++;
3224 }
3225 // End call triggers
3226 }
3227
3228 if (!$error) {
3229 $this->db->commit();
3230 return 1;
3231 } else {
3232 foreach ($this->errors as $errmsg) {
3233 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
3234 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3235 }
3236 $this->db->rollback();
3237 return -1 * $error;
3238 }
3239 } else {
3240 $error_str = 'Propal status do not meet requirement '.$this->status;
3241 dol_syslog(__METHOD__.$error_str, LOG_ERR);
3242 $this->error = $error_str;
3243 $this->errors[] = $this->error;
3244 return -2;
3245 }
3246 }
3247
3248 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3257 public function demand_reason($demand_reason_id, $notrigger = 0)
3258 {
3259 // phpcs:enable
3260 global $user;
3261
3262 if ($this->status >= self::STATUS_DRAFT) {
3263 $error = 0;
3264
3265 $this->db->begin();
3266
3267 $sql = 'UPDATE '.MAIN_DB_PREFIX.'propal';
3268 $sql .= ' SET fk_input_reason = '.((int) $demand_reason_id);
3269 $sql .= ' WHERE rowid='.((int) $this->id);
3270
3271 dol_syslog(__METHOD__.' demand_reason('.$demand_reason_id.')', LOG_DEBUG);
3272 $resql = $this->db->query($sql);
3273 if (!$resql) {
3274 $this->errors[] = $this->db->error();
3275 $error++;
3276 }
3277
3278 if (!$error) {
3279 $this->oldcopy = clone $this;
3280 $this->demand_reason_id = $demand_reason_id;
3281 }
3282
3283 if (!$notrigger && empty($error)) {
3284 // Call trigger
3285 $result = $this->call_trigger('PROPAL_MODIFY', $user);
3286 if ($result < 0) {
3287 $error++;
3288 }
3289 // End call triggers
3290 }
3291
3292 if (!$error) {
3293 $this->db->commit();
3294 return 1;
3295 } else {
3296 foreach ($this->errors as $errmsg) {
3297 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
3298 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3299 }
3300 $this->db->rollback();
3301 return -1 * $error;
3302 }
3303 } else {
3304 $error_str = 'Propal status do not meet requirement '.$this->status;
3305 dol_syslog(__METHOD__.$error_str, LOG_ERR);
3306 $this->error = $error_str;
3307 $this->errors[] = $this->error;
3308 return -2;
3309 }
3310 }
3311
3312
3319 public function info($id)
3320 {
3321 $sql = "SELECT c.rowid, ";
3322 $sql .= " c.datec, c.date_valid as datev, c.date_signature, c.date_cloture,";
3323 $sql .= " c.fk_user_author, c.fk_user_valid, c.fk_user_signature, c.fk_user_cloture";
3324 $sql .= " FROM ".MAIN_DB_PREFIX."propal as c";
3325 $sql .= " WHERE c.rowid = ".((int) $id);
3326
3327 $result = $this->db->query($sql);
3328
3329 if ($result) {
3330 if ($this->db->num_rows($result)) {
3331 $obj = $this->db->fetch_object($result);
3332
3333 $this->id = $obj->rowid;
3334
3335 $this->date_creation = $this->db->jdate($obj->datec);
3336 $this->date_validation = $this->db->jdate($obj->datev);
3337 $this->date_signature = $this->db->jdate($obj->date_signature);
3338 $this->date_cloture = $this->db->jdate($obj->date_cloture);
3339
3340 $this->user_creation_id = $obj->fk_user_author;
3341 $this->user_validation_id = $obj->fk_user_valid;
3342
3343 if ($obj->fk_user_signature) {
3344 $user_signature = new User($this->db);
3345 $user_signature->fetch($obj->fk_user_signature);
3346 $this->user_signature = $user_signature;
3347 }
3348
3349 $this->user_closing_id = $obj->fk_user_cloture;
3350 }
3351 $this->db->free($result);
3352 } else {
3353 dol_print_error($this->db);
3354 }
3355 }
3356
3357
3364 public function getLibStatut($mode = 0)
3365 {
3366 return $this->LibStatut($this->status, $mode);
3367 }
3368
3369 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3377 public function LibStatut($status, $mode = 1)
3378 {
3379 // phpcs:enable
3380 global $hookmanager;
3381
3382 // Init/load array of translation of status
3383 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
3384 global $langs;
3385 $langs->load("propal");
3386 $this->labelStatus[-1] = $langs->transnoentitiesnoconv("PropalStatusCanceled");
3387 $this->labelStatus[0] = $langs->transnoentitiesnoconv("PropalStatusDraft");
3388 $this->labelStatus[1] = $langs->transnoentitiesnoconv("PropalStatusValidated");
3389 $this->labelStatus[2] = $langs->transnoentitiesnoconv("PropalStatusSigned");
3390 $this->labelStatus[3] = $langs->transnoentitiesnoconv("PropalStatusNotSigned");
3391 $this->labelStatus[4] = $langs->transnoentitiesnoconv("PropalStatusBilled");
3392 $this->labelStatusShort[-1] = $langs->transnoentitiesnoconv("PropalStatusCanceledShort");
3393 $this->labelStatusShort[0] = $langs->transnoentitiesnoconv("PropalStatusDraftShort");
3394 $this->labelStatusShort[1] = $langs->transnoentitiesnoconv("PropalStatusValidatedShort");
3395 $this->labelStatusShort[2] = $langs->transnoentitiesnoconv("PropalStatusSignedShort");
3396 $this->labelStatusShort[3] = $langs->transnoentitiesnoconv("PropalStatusNotSignedShort");
3397 $this->labelStatusShort[4] = $langs->transnoentitiesnoconv("PropalStatusBilledShort");
3398 }
3399
3400 $statusType = '';
3401 if ($status == self::STATUS_CANCELED) {
3402 $statusType = 'status9';
3403 } elseif ($status == self::STATUS_DRAFT) {
3404 $statusType = 'status0';
3405 } elseif ($status == self::STATUS_VALIDATED) {
3406 $statusType = 'status1';
3407 } elseif ($status == self::STATUS_SIGNED) {
3408 $statusType = 'status4';
3409 } elseif ($status == self::STATUS_NOTSIGNED) {
3410 $statusType = 'status9';
3411 } elseif ($status == self::STATUS_BILLED) {
3412 $statusType = 'status6';
3413 }
3414
3415 $parameters = array('status' => $status, 'mode' => $mode);
3416 $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
3417
3418 if ($reshook > 0) {
3419 return $hookmanager->resPrint;
3420 }
3421
3422 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
3423 }
3424
3425
3426 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3434 public function load_board($user, $mode)
3435 {
3436 // phpcs:enable
3437 global $conf, $langs;
3438
3439 $clause = " WHERE";
3440
3441 $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.fin_validite as datefin, p.total_ht";
3442 $sql .= " FROM ".MAIN_DB_PREFIX."propal as p";
3443 $sql .= $clause." p.entity IN (".getEntity('propal').")";
3444 if ($mode == 'opened') {
3445 $sql .= " AND p.fk_statut = ".self::STATUS_VALIDATED;
3446 }
3447 if ($mode == 'signed') {
3448 $sql .= " AND p.fk_statut = ".self::STATUS_SIGNED;
3449 }
3450 // If the internal user must only see his customers, force searching by him
3451 $search_sale = 0;
3452 if (!$user->hasRight('societe', 'client', 'voir')) {
3453 $search_sale = $user->id;
3454 }
3455 // Search on sale representative
3456 if ($search_sale && $search_sale != '-1') {
3457 if ($search_sale == -2) {
3458 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
3459 } elseif ($search_sale > 0) {
3460 $sql .= " AND EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc AND sc.fk_user = ".((int) $search_sale).")";
3461 }
3462 }
3463
3464 $resql = $this->db->query($sql);
3465 if ($resql) {
3466 $langs->load("propal");
3467 $now = dol_now();
3468
3469 $delay_warning = 0;
3470 $status = 0;
3471 $label = $labelShort = '';
3472 if ($mode == 'opened') {
3473 $delay_warning = $conf->propal->cloture->warning_delay;
3474 $status = self::STATUS_VALIDATED;
3475 $label = $langs->transnoentitiesnoconv("PropalsToClose");
3476 $labelShort = $langs->transnoentitiesnoconv("ToAcceptRefuse");
3477 }
3478 if ($mode == 'signed') {
3479 $delay_warning = $conf->propal->facturation->warning_delay;
3480 $status = self::STATUS_SIGNED;
3481 $label = $langs->trans("PropalsToBill"); // We set here bill but may be billed or ordered
3482 $labelShort = $langs->trans("ToBill");
3483 }
3484
3485 $response = new WorkboardResponse();
3486 $response->warning_delay = $delay_warning / 60 / 60 / 24;
3487 $response->label = $label;
3488 $response->labelShort = $labelShort;
3489 $response->url = DOL_URL_ROOT.'/comm/propal/list.php?search_status='.$status.'&mainmenu=commercial&leftmenu=propals';
3490 $response->url_late = DOL_URL_ROOT.'/comm/propal/list.php?search_option=late&mainmenu=commercial&leftmenu=propals&sortfield=p.datep&sortorder=asc';
3491 $response->img = img_object('', "propal");
3492
3493 // This assignment in condition is not a bug. It allows walking the results.
3494 while ($obj = $this->db->fetch_object($resql)) {
3495 $response->nbtodo++;
3496 $response->total += $obj->total_ht;
3497
3498 if ($mode == 'opened') {
3499 $datelimit = $this->db->jdate($obj->datefin);
3500 if ($datelimit < ($now - $delay_warning)) {
3501 $response->nbtodolate++;
3502 }
3503 }
3504 // TODO Definir regle des propales a facturer en retard
3505 // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
3506 }
3507
3508 return $response;
3509 } else {
3510 $this->error = $this->db->error();
3511 return -1;
3512 }
3513 }
3514
3515
3523 public function initAsSpecimen()
3524 {
3525 global $conf, $langs;
3526
3527 // Load array of products prodids
3528 $num_prods = 0;
3529 $prodids = array();
3530 $sql = "SELECT rowid";
3531 $sql .= " FROM ".MAIN_DB_PREFIX."product";
3532 $sql .= " WHERE entity IN (".getEntity('product').")";
3533 $sql .= $this->db->plimit(100);
3534
3535 $resql = $this->db->query($sql);
3536 if ($resql) {
3537 $num_prods = $this->db->num_rows($resql);
3538 $i = 0;
3539 while ($i < $num_prods) {
3540 $i++;
3541 $row = $this->db->fetch_row($resql);
3542 $prodids[$i] = $row[0];
3543 }
3544 }
3545
3546 // Initialise parameters
3547 $this->id = 0;
3548 $this->ref = 'SPECIMEN';
3549 $this->ref_client = 'NEMICEPS';
3550 $this->specimen = 1;
3551 $this->socid = 1;
3552 $this->date = time();
3553 $this->fin_validite = $this->date + 3600 * 24 * 30;
3554 $this->cond_reglement_id = 1;
3555 $this->cond_reglement_code = 'RECEP';
3556 $this->mode_reglement_id = 7;
3557 $this->mode_reglement_code = 'CHQ';
3558 $this->availability_id = 1;
3559 $this->availability_code = 'AV_NOW';
3560 $this->demand_reason_id = 1;
3561 $this->demand_reason_code = 'SRC_00';
3562 $this->note_public = 'This is a comment (public)';
3563 $this->note_private = 'This is a comment (private)';
3564
3565 $this->multicurrency_tx = 1;
3566 $this->multicurrency_code = $conf->currency;
3567
3568 // Lines
3569 $nbp = 5;
3570 $xnbp = 0;
3571 while ($xnbp < $nbp) {
3572 $line = new PropaleLigne($this->db);
3573 $line->desc = $langs->trans("Description")." ".$xnbp;
3574 $line->qty = 1;
3575 $line->subprice = 100;
3576 $line->price = 100;
3577 $line->tva_tx = 20;
3578 $line->localtax1_tx = 0;
3579 $line->localtax2_tx = 0;
3580 if ($xnbp == 2) {
3581 $line->total_ht = 50;
3582 $line->total_ttc = 60;
3583 $line->total_tva = 10;
3584 $line->remise_percent = 50;
3585 } else {
3586 $line->total_ht = 100;
3587 $line->total_ttc = 120;
3588 $line->total_tva = 20;
3589 $line->remise_percent = 00;
3590 }
3591
3592 if ($num_prods > 0) {
3593 $prodid = mt_rand(1, $num_prods);
3594 $line->fk_product = $prodids[$prodid];
3595 $line->product_ref = 'SPECIMEN';
3596 }
3597
3598 $this->lines[$xnbp] = $line;
3599
3600 $this->total_ht += $line->total_ht;
3601 $this->total_tva += $line->total_tva;
3602 $this->total_ttc += $line->total_ttc;
3603
3604 $xnbp++;
3605 }
3606
3607 return 1;
3608 }
3609
3615 public function loadStateBoard()
3616 {
3617 global $user;
3618
3619 $this->nb = array();
3620 $clause = "WHERE";
3621
3622 $sql = "SELECT count(p.rowid) as nb";
3623 $sql .= " FROM ".MAIN_DB_PREFIX."propal as p";
3624 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
3625 $sql .= " ".$clause." p.entity IN (".getEntity('propal').")";
3626
3627 // If the internal user must only see his customers, force searching by him
3628 $search_sale = 0;
3629 if (!$user->hasRight('societe', 'client', 'voir')) {
3630 $search_sale = $user->id;
3631 }
3632 // Search on sale representative
3633 if ($search_sale && $search_sale != '-1') {
3634 if ($search_sale == -2) {
3635 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
3636 } elseif ($search_sale > 0) {
3637 $sql .= " AND EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc AND sc.fk_user = ".((int) $search_sale).")";
3638 }
3639 }
3640
3641 $resql = $this->db->query($sql);
3642 if ($resql) {
3643 // This assignment in condition is not a bug. It allows walking the results.
3644 while ($obj = $this->db->fetch_object($resql)) {
3645 $this->nb["proposals"] = $obj->nb;
3646 }
3647 $this->db->free($resql);
3648 return 1;
3649 } else {
3650 dol_print_error($this->db);
3651 $this->error = $this->db->error();
3652 return -1;
3653 }
3654 }
3655
3656
3664 public function getNextNumRef($soc)
3665 {
3666 global $conf, $langs;
3667 $langs->load("propal");
3668
3669 $classname = getDolGlobalString('PROPALE_ADDON');
3670
3671 if (!empty($classname)) {
3672 $mybool = false;
3673
3674 $file = $classname.".php";
3675
3676 // Include file with class
3677 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
3678 foreach ($dirmodels as $reldir) {
3679 $dir = dol_buildpath($reldir."core/modules/propale/");
3680
3681 // Load file with numbering class (if found)
3682 $mybool = ((bool) @include_once $dir.$file) || $mybool;
3683 }
3684
3685 if (!$mybool) {
3686 dol_print_error(null, "Failed to include file ".$file);
3687 return '';
3688 }
3689
3690 $obj = new $classname();
3691 '@phan-var-force ModeleNumRefPropales $obj';
3692
3693 $numref = $obj->getNextValue($soc, $this);
3694
3695 if ($numref != "") {
3696 return $numref;
3697 } else {
3698 $this->error = $obj->error;
3699 //dol_print_error($db,"Propale::getNextNumRef ".$obj->error);
3700 return "";
3701 }
3702 } else {
3703 $langs->load("errors");
3704 print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("Proposal"));
3705 return "";
3706 }
3707 }
3708
3715 public function getTooltipContentArray($params)
3716 {
3717 global $conf, $langs, $user;
3718
3719 $langs->load('propal');
3720 $datas = [];
3721 $nofetch = !empty($params['nofetch']);
3722
3723 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3724 return ['optimize' => $langs->trans("Proposal")];
3725 }
3726 if ($user->hasRight('propal', 'lire')) {
3727 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("Proposal").'</u>';
3728 if (isset($this->status)) {
3729 $datas['status'] = ' '.$this->getLibStatut(5);
3730 }
3731 if (!empty($this->ref)) {
3732 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
3733 }
3734 if (!$nofetch) {
3735 $langs->load('companies');
3736 if (empty($this->thirdparty)) {
3737 $this->fetch_thirdparty();
3738 }
3739 $datas['customer'] = '<br><b>'.$langs->trans('Customer').':</b> '.$this->thirdparty->getNomUrl(1, '', 0, 1);
3740 }
3741 if (!empty($this->ref_customer)) {
3742 $datas['refcustomer'] = '<br><b>'.$langs->trans('RefCustomer').':</b> '.$this->ref_customer;
3743 }
3744 if (!$nofetch) {
3745 $langs->load('project');
3746 if (is_null($this->project) || (is_object($this->project) && $this->project->isEmpty())) {
3747 $res = $this->fetchProject();
3748 if ($res > 0 && $this->project instanceof Project) {
3749 $datas['project'] = '<br><b>'.$langs->trans('Project').':</b> '.$this->project->getNomUrl(1, '', 0, 1);
3750 }
3751 }
3752 }
3753 if (!empty($this->total_ht)) {
3754 $datas['amountht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3755 }
3756 if (!empty($this->total_tva)) {
3757 $datas['vat'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
3758 }
3759 if (!empty($this->total_ttc)) {
3760 $datas['amountttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3761 }
3762 if (!empty($this->date)) {
3763 $datas['date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
3764 }
3765 if (!empty($this->delivery_date)) {
3766 $datas['deliverydate'] = '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
3767 }
3768 }
3769
3770 return $datas;
3771 }
3772
3784 public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = -1)
3785 {
3786 global $langs, $conf, $user, $hookmanager;
3787
3788 if (!empty($conf->dol_no_mouse_hover)) {
3789 $notooltip = 1; // Force disable tooltips
3790 }
3791
3792 $result = '';
3793 $params = [
3794 'id' => $this->id,
3795 'objecttype' => $this->element,
3796 'option' => $option,
3797 'nofetch' => 1,
3798 ];
3799 $classfortooltip = 'classfortooltip';
3800 $dataparams = '';
3801 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
3802 $classfortooltip = 'classforajaxtooltip';
3803 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
3804 $label = '';
3805 } else {
3806 $label = implode($this->getTooltipContentArray($params));
3807 }
3808
3809 $url = '';
3810 if ($user->hasRight('propal', 'lire')) {
3811 if ($option == '') {
3812 $url = DOL_URL_ROOT.'/comm/propal/card.php?id='.$this->id.$get_params;
3813 } elseif ($option == 'compta') { // deprecated
3814 $url = DOL_URL_ROOT.'/comm/propal/card.php?id='.$this->id.$get_params;
3815 } elseif ($option == 'expedition') {
3816 $url = DOL_URL_ROOT.'/expedition/propal.php?id='.$this->id.$get_params;
3817 } elseif ($option == 'document') {
3818 $url = DOL_URL_ROOT.'/comm/propal/document.php?id='.$this->id.$get_params;
3819 }
3820
3821 if ($option != 'nolink') {
3822 // Add param to save lastsearch_values or not
3823 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
3824 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
3825 $add_save_lastsearch_values = 1;
3826 }
3827 if ($add_save_lastsearch_values) {
3828 $url .= '&save_lastsearch_values=1';
3829 }
3830 }
3831 }
3832
3833 $linkclose = '';
3834 if (empty($notooltip) && $user->hasRight('propal', 'lire')) {
3835 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3836 $label = $langs->trans("Proposal");
3837 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
3838 }
3839 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
3840 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
3841 }
3842
3843 $linkstart = '<a href="'.$url.'"';
3844 $linkstart .= $linkclose.'>';
3845 $linkend = '</a>';
3846
3847 $result .= $linkstart;
3848 if ($withpicto) {
3849 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
3850 }
3851 if ($withpicto != 2) {
3852 $result .= $this->ref;
3853 }
3854 $result .= $linkend;
3855
3856 if ($addlinktonotes >= 0) {
3857 $txttoshow = '';
3858
3859 if ($addlinktonotes == 0) {
3860 if (!empty($this->note_private) || !empty($this->note_public)) {
3861 $txttoshow = $langs->trans('ViewPrivateNote');
3862 }
3863 } elseif ($addlinktonotes == 1) {
3864 if (!empty($this->note_private)) {
3865 $txttoshow .= ($user->socid > 0 ? '' : dol_string_nohtmltag($this->note_private, 1));
3866 }
3867 } elseif ($addlinktonotes == 2) {
3868 if (!empty($this->note_public)) {
3869 $txttoshow .= dol_string_nohtmltag($this->note_public, 1);
3870 }
3871 } elseif ($addlinktonotes == 3) {
3872 if ($user->socid > 0) {
3873 if (!empty($this->note_public)) {
3874 $txttoshow .= dol_string_nohtmltag($this->note_public, 1);
3875 }
3876 } else {
3877 if (!empty($this->note_public)) {
3878 $txttoshow .= dol_string_nohtmltag($this->note_public, 1);
3879 }
3880 if (!empty($this->note_private)) {
3881 if (!empty($txttoshow)) {
3882 $txttoshow .= '<br><br>';
3883 }
3884 $txttoshow .= dol_string_nohtmltag($this->note_private, 1);
3885 }
3886 }
3887 }
3888
3889 if ($txttoshow) {
3890 $result .= ' <span class="note inline-block">';
3891 $result .= '<a href="'.DOL_URL_ROOT.'/comm/propal/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($txttoshow).'">';
3892 $result .= img_picto('', 'note');
3893 $result .= '</a>';
3894 $result .= '</span>';
3895 }
3896 }
3897
3898 global $action;
3899 $hookmanager->initHooks(array($this->element . 'dao'));
3900 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
3901 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3902 if ($reshook > 0) {
3903 $result = $hookmanager->resPrint;
3904 } else {
3905 $result .= $hookmanager->resPrint;
3906 }
3907 return $result;
3908 }
3909
3916 public function getLinesArray($sqlforgedfilters = '')
3917 {
3918 return $this->fetch_lines(0, 0, $sqlforgedfilters);
3919 }
3920
3932 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3933 {
3934 global $conf, $langs;
3935
3936 $langs->load("propale");
3937 $outputlangs->load("products");
3938
3939 if (!dol_strlen($modele)) {
3940 $modele = 'azur';
3941
3942 if ($this->model_pdf) {
3943 $modele = $this->model_pdf;
3944 } elseif (getDolGlobalString('PROPALE_ADDON_PDF')) {
3945 $modele = getDolGlobalString('PROPALE_ADDON_PDF');
3946 }
3947 }
3948
3949 $modelpath = "core/modules/propale/doc/";
3950
3951 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3952 }
3953
3962 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3963 {
3964 $tables = array(
3965 'propal'
3966 );
3967
3968 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3969 }
3970
3979 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3980 {
3981 $tables = array(
3982 'propaldet'
3983 );
3984
3985 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3986 }
3987
3995 public function getKanbanView($option = '', $arraydata = null)
3996 {
3997 global $langs;
3998
3999 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
4000
4001 $return = '<div class="box-flex-item box-flex-grow-zero">';
4002 $return .= '<div class="info-box info-box-sm">';
4003 $return .= '<div class="info-box-icon bg-infobox-action">';
4004 $return .= img_picto('', $this->picto);
4005 $return .= '</div>';
4006 $return .= '<div class="info-box-content">';
4007 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
4008 if ($selected >= 0) {
4009 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
4010 }
4011 if (!empty($arraydata['projectlink'])) {
4012 $return .= '<span class="info-box-ref"> | '.$arraydata['projectlink'].'</span>';
4013 }
4014 $return .= '<br>';
4015 if (property_exists($this, 'thirdparty') && is_object($this->thirdparty)) {
4016 $return .= '<div class="info-box-ref tdoverflowmax150">'.$this->thirdparty->getNomUrl(1).'</div>';
4017 }
4018 if (property_exists($this, 'total_ht')) {
4019 $return .= '<span class="info-box-label amount" title="'.$langs->trans("AmountHT").'">'.price($this->total_ht).'</span>';
4020 }
4021 if (!empty($arraydata['authorlink'])) {
4022 $return .= ' &nbsp; <span class="info-box-label">'.$arraydata['authorlink'].'</span>';
4023 }
4024 if (method_exists($this, 'getLibStatut')) {
4025 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
4026 }
4027 $return .= '</div>';
4028 $return .= '</div>';
4029 $return .= '</div>';
4030 return $return;
4031 }
4032}
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:58
$object ref
Definition info.php:79
Parent class of all other business classes (invoices, contracts, proposals, orders,...
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
Save a new position (field rang) for details lines.
deleteEcmFiles($mode=0)
Delete related files of object in database.
update_price($exclspec=0, $roundingadjust='auto', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
add_object_linked($origin=null, $origin_id=null, $f_user=null, $notrigger=0)
Add an object link into llx_element_element.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
fetchProject()
Load the project with id $this->fk_project into this->project.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty.
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid=0, $f_user=null, $notrigger=0)
Delete all links between an object $this.
setErrorsFromObject($object)
setErrorsFromObject
static isExistingObject($element, $id, $ref='', $ref_ext='')
Check if an object id or ref exists If you don't need or want to instantiate the object and just need...
updateRangOfLine($rowid, $rang)
Update position of line (rang)
deleteExtraFields()
Delete all extra fields values for the current object.
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).
static commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
static commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product id with another one.
line_max($fk_parent_line=0)
Get max value used for position of line (rang)
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
delete_linked_contact($source='', $code='')
Delete all links between an object $this and all its contacts in llx_element_contact.
call_trigger($triggerName, $user)
Call trigger based on this instance.
Class to manage absolute discounts.
Class to manage Dolibarr database access.
static getIdFromCode($dbs, $code)
Get id of currency from code.
static getIdAndTxFromCode($dbs, $code, $date_document='')
Get id and rate of currency from code.
File of class to manage predefined price products or services by customer.
Class to manage products or services.
Class to manage projects.
Class to manage proposals.
getTooltipContentArray($params)
getTooltipContentArray
const STATUS_DRAFT
Draft status.
set_date($user, $date, $notrigger=0)
Define proposal date.
const STATUS_SIGNED
Signed quote.
getNomUrl($withpicto=0, $option='', $get_params='', $notooltip=0, $save_lastsearch_value=-1, $addlinktonotes=-1)
Return clickable link of object (with eventually picto)
getKanbanView($option='', $arraydata=null)
Return clickable link of object (with eventually picto)
InvoiceArrayList($id)
Returns an array with id and ref of related invoices.
availability($availability_id, $notrigger=0)
Change the delivery time.
const STATUS_NOTSIGNED
Not signed quote.
$table_ref_field
{}
setDeliveryDate($user, $delivery_date, $notrigger=0)
Set delivery date.
classifyBilled(User $user, $notrigger=0, $note='')
Classify the proposal to status Billed.
getLinesArray($sqlforgedfilters='')
Retrieve an array of proposal lines.
fetch($rowid, $ref='', $ref_ext='', $forceentity=0)
Load a proposal from database.
set_availability($user, $id, $notrigger=0)
Set delivery.
fetch_lines($only_product=0, $loadalsotranslation=0, $sqlforgedfilters='')
Load array lines.
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=array(), $fk_unit=null, $pu_ht_devise=0, $notrigger=0, $rang=0)
Update a proposal line.
update(User $user, $notrigger=0)
Update database.
closeProposal($user, $status, $note_private='', $notrigger=0, $note_public='')
Close/set the commercial proposal to status signed or refused (fill also date signature)
demand_reason($demand_reason_id, $notrigger=0)
Change source demand.
info($id)
Object Proposal Information.
const STATUS_BILLED
Billed or processed quote.
static replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
getLibStatut($mode=0)
Return label of status of proposal (draft, validated, ...)
const STATUS_CANCELED
Canceled status.
insert_discount($idremise)
Add a discount line into an proposal (as a proposal line) using an existing absolute discount (Consum...
getNextNumRef($soc)
Returns the reference to the following non used Proposal used depending on the active numbering modul...
LibStatut($status, $mode=1)
Return label of a status (draft, validated, ...)
valid($user, $notrigger=0)
Set status to validated.
static replaceProduct(DoliDB $db, $origin_id, $dest_id)
Function used to replace a product id with another one.
set_ref_client($user, $ref_client, $notrigger=0)
Set customer reference number.
setDraft($user, $notrigger=0)
Set draft status.
set_echeance($user, $date_end_validity, $notrigger=0)
Define end validity date.
set_demand_reason($user, $id, $notrigger=0)
Set source of demand.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template module.
create($user, $notrigger=0)
Create commercial proposal into database this->ref can be set or empty.
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.
add_product($idproduct, $qty, $remise_percent=0)
Add line into array ->lines $this->thirdparty should be loaded.
initAsSpecimen()
Initialise an instance with random values.
reopen($user, $status, $note='', $notrigger=0)
Reopen the commercial proposal.
deleteLine($lineid, $id=0)
Delete detail line.
__construct($db, $socid=0, $propalid=0)
Constructor.
load_board($user, $mode)
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
setCancel(User $user)
Cancel the proposal.
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=array(), $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...
getInvoiceArrayList()
Returns an array with the numbers of related invoices.
set_date_livraison($user, $delivery_date, $notrigger=0)
Set delivery date.
const STATUS_VALIDATED
Validated status.
loadStateBoard()
Load the indicators this->nb for the state board.
createFromClone(User $user, $socid=0, $forceentity=null, $update_prices=false, $update_desc=false)
Load an object from its id and create a new one in database.
Class to manage commercial proposal lines.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage translations.
Class to manage Dolibarr users.
print $langs trans("Ref").' m titre as m m statut as status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition index.php:162
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)
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.
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:63
dol_delete_preview($object)
Delete all preview files linked to object instance.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
isDolTms($timestamp)
isDolTms check if a timestamp is valid.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that returns whether VAT must be recoverable collected VAT (e.g.: VAT NPR in France)
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...
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that return vat rate of a product line (according to seller, buyer and product vat rate) VAT...
get_localtax($vatrate, $local, $thirdparty_buyer=null, $thirdparty_seller=null, $vatnpr=0)
Return localtax rate for a particular vat, when selling a product with vat $vatrate,...
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
getMarginInfos($pv_ht, $remise_percent, $tva_tx, $localtax1_tx, $localtax2_tx, $fk_pa, $pa_ht)
Return an array with margins information of a line.
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller='', $localtaxes_array=[], $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition price.lib.php:88