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