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