dolibarr 20.0.5
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
43require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
44require_once DOL_DOCUMENT_ROOT."/core/class/commonobjectline.class.php";
45require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
46require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.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{
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 public $author;
111
118 public $ref_client;
119
124 public $ref_customer;
125
129 public $oldcopy;
130
137 public $statut;
138
144 public $status;
145
150 public $datec;
151
155 public $date_creation;
156
161 public $datev;
162
166 public $date_validation;
167
171 public $date_signature;
172
176 public $user_signature;
177
181 public $date;
182
187 public $datep;
188
192 public $delivery_date; // Date expected of shipment (date starting shipment, not the reception that occurs some days after)
193
194
195 public $fin_validite;
196
197 public $user_author_id;
198
203 public $price;
208 public $tva;
213 public $total;
214
215 public $cond_reglement_code; // code
216 public $cond_reglement; // label
217 public $cond_reglement_doc; // label doc
218
219 public $mode_reglement_code; // code
220 public $mode_reglement; // label
221
222 public $deposit_percent;
223
228 public $fk_address;
229
230 public $address_type;
231 public $address;
232
236 public $availability_id;
237
243 public $fk_availability;
244
248 public $availability_code;
249
253 public $availability;
254
255 public $duree_validite;
256
257 public $demand_reason_id; // id
258 public $demand_reason_code; // code
259 public $demand_reason; // label
260
261 public $warehouse_id;
262
263 public $extraparams = array();
264
268 public $lines = array();
269
273 public $line;
274
275 public $labelStatus = array();
276 public $labelStatusShort = array();
277
278
303 // BEGIN MODULEBUILDER PROPERTIES
307 public $fields = array(
308 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10),
309 'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 15, 'index' => 1),
310 'ref' => array('type' => 'varchar(30)', 'label' => 'Ref', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'showoncombobox' => 1, 'position' => 20),
311 'ref_client' => array('type' => 'varchar(255)', 'label' => 'RefCustomer', 'enabled' => 1, 'visible' => -1, 'position' => 22),
312 'ref_ext' => array('type' => 'varchar(255)', 'label' => 'RefExt', 'enabled' => 1, 'visible' => 0, 'position' => 40),
313 'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'enabled' => 'isModEnabled("societe")', 'visible' => -1, 'position' => 23),
314 '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),
315 'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 25),
316 'datec' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -1, 'position' => 55),
317 'datep' => array('type' => 'date', 'label' => 'Date', 'enabled' => 1, 'visible' => -1, 'position' => 60),
318 'fin_validite' => array('type' => 'datetime', 'label' => 'DateEnd', 'enabled' => 1, 'visible' => -1, 'position' => 65),
319 'date_valid' => array('type' => 'datetime', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -1, 'position' => 70),
320 'date_cloture' => array('type' => 'datetime', 'label' => 'DateClosing', 'enabled' => 1, 'visible' => -1, 'position' => 75),
321 'fk_user_author' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'Fk user author', 'enabled' => 1, 'visible' => -1, 'position' => 80),
322 'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'position' => 85),
323 'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'enabled' => 1, 'visible' => -1, 'position' => 90),
324 'fk_user_cloture' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'Fk user cloture', 'enabled' => 1, 'visible' => -1, 'position' => 95),
325 'price' => array('type' => 'double', 'label' => 'Price', 'enabled' => 1, 'visible' => -1, 'position' => 105),
326 'total_ht' => array('type' => 'double(24,8)', 'label' => 'TotalHT', 'enabled' => 1, 'visible' => -1, 'position' => 125, 'isameasure' => 1),
327 'total_tva' => array('type' => 'double(24,8)', 'label' => 'VAT', 'enabled' => 1, 'visible' => -1, 'position' => 130, 'isameasure' => 1),
328 'localtax1' => array('type' => 'double(24,8)', 'label' => 'LocalTax1', 'enabled' => 1, 'visible' => -1, 'position' => 135, 'isameasure' => 1),
329 'localtax2' => array('type' => 'double(24,8)', 'label' => 'LocalTax2', 'enabled' => 1, 'visible' => -1, 'position' => 140, 'isameasure' => 1),
330 'total_ttc' => array('type' => 'double(24,8)', 'label' => 'TotalTTC', 'enabled' => 1, 'visible' => -1, 'position' => 145, 'isameasure' => 1),
331 'fk_account' => array('type' => 'integer', 'label' => 'BankAccount', 'enabled' => 'isModEnabled("bank")', 'visible' => -1, 'position' => 150),
332 'fk_currency' => array('type' => 'varchar(3)', 'label' => 'Currency', 'enabled' => 1, 'visible' => -1, 'position' => 155),
333 'fk_cond_reglement' => array('type' => 'integer', 'label' => 'PaymentTerm', 'enabled' => 1, 'visible' => -1, 'position' => 160),
334 'deposit_percent' => array('type' => 'varchar(63)', 'label' => 'DepositPercent', 'enabled' => 1, 'visible' => -1, 'position' => 161),
335 'fk_mode_reglement' => array('type' => 'integer', 'label' => 'PaymentMode', 'enabled' => 1, 'visible' => -1, 'position' => 165),
336 'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 170),
337 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 175),
338 'model_pdf' => array('type' => 'varchar(255)', 'label' => 'PDFTemplate', 'enabled' => 1, 'visible' => 0, 'position' => 180),
339 'date_livraison' => array('type' => 'date', 'label' => 'DateDeliveryPlanned', 'enabled' => 1, 'visible' => -1, 'position' => 185),
340 'fk_shipping_method' => array('type' => 'integer', 'label' => 'ShippingMethod', 'enabled' => 1, 'visible' => -1, 'position' => 190),
341 'fk_warehouse' => array('type' => 'integer:Entrepot:product/stock/class/entrepot.class.php', 'label' => 'Fk warehouse', 'enabled' => 'isModEnabled("stock")', 'visible' => -1, 'position' => 191),
342 'fk_availability' => array('type' => 'integer', 'label' => 'Availability', 'enabled' => 1, 'visible' => -1, 'position' => 195),
343 'fk_delivery_address' => array('type' => 'integer', 'label' => 'DeliveryAddress', 'enabled' => 1, 'visible' => 0, 'position' => 200), // deprecated
344 'fk_input_reason' => array('type' => 'integer', 'label' => 'InputReason', 'enabled' => 1, 'visible' => -1, 'position' => 205),
345 'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => -1, 'position' => 215),
346 'fk_incoterms' => array('type' => 'integer', 'label' => 'IncotermCode', 'enabled' => 'isModEnabled("incoterm")', 'visible' => -1, 'position' => 220),
347 'location_incoterms' => array('type' => 'varchar(255)', 'label' => 'IncotermLabel', 'enabled' => 'isModEnabled("incoterm")', 'visible' => -1, 'position' => 225),
348 'fk_multicurrency' => array('type' => 'integer', 'label' => 'MulticurrencyID', 'enabled' => 1, 'visible' => -1, 'position' => 230),
349 'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'MulticurrencyCurrency', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 235),
350 'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyRate', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 240, 'isameasure' => 1),
351 'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountHT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 245, 'isameasure' => 1),
352 'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountVAT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 250, 'isameasure' => 1),
353 'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountTTC', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 255, 'isameasure' => 1),
354 'last_main_doc' => array('type' => 'varchar(255)', 'label' => 'LastMainDoc', 'enabled' => 1, 'visible' => -1, 'position' => 260),
355 'fk_statut' => array('type' => 'smallint(6)', 'label' => 'Status', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 500),
356 'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'position' => 900),
357 );
358 // END MODULEBUILDER PROPERTIES
359
363 const STATUS_CANCELED = -1;
367 const STATUS_DRAFT = 0;
375 const STATUS_SIGNED = 2;
383 const STATUS_BILLED = 4; // Todo rename into STATUS_CLOSE ?
384
385
393 public function __construct($db, $socid = 0, $propalid = 0)
394 {
395 $this->db = $db;
396
397 $this->ismultientitymanaged = 1;
398 $this->socid = $socid;
399 $this->id = $propalid;
400
401 $this->duree_validite = getDolGlobalInt('PROPALE_VALIDITY_DURATION', 0);
402 }
403
404
405 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
417 public function add_product($idproduct, $qty, $remise_percent = 0)
418 {
419 // phpcs:enable
420 global $conf, $mysoc;
421
422 if (!$qty) {
423 $qty = 1;
424 }
425
426 dol_syslog(get_class($this)."::add_product $idproduct, $qty, $remise_percent");
427 if ($idproduct > 0) {
428 $prod = new Product($this->db);
429 $prod->fetch($idproduct);
430
431 $productdesc = $prod->description;
432
433 $tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
434 $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
435 if (empty($tva_tx)) {
436 $tva_npr = 0;
437 }
438 $vat_src_code = ''; // May be defined into tva_tx
439
440 $localtax1_tx = get_localtax($tva_tx, 1, $mysoc, $this->thirdparty, $tva_npr);
441 $localtax2_tx = get_localtax($tva_tx, 2, $mysoc, $this->thirdparty, $tva_npr);
442
443 // multiprices
444 if ($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level) {
445 $price = $prod->multiprices[$this->thirdparty->price_level];
446 } else {
447 $price = $prod->price;
448 }
449
450 $line = new PropaleLigne($this->db);
451
452 $line->fk_product = $idproduct;
453 $line->desc = $productdesc;
454 $line->qty = $qty;
455 $line->subprice = $price;
456 $line->remise_percent = $remise_percent;
457 $line->vat_src_code = $vat_src_code;
458 $line->tva_tx = $tva_tx;
459 $line->fk_unit = $prod->fk_unit;
460 if ($tva_npr) {
461 $line->info_bits = 1;
462 }
463
464 $this->lines[] = $line;
465 }
466
467 return 1;
468 }
469
470 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
477 public function insert_discount($idremise)
478 {
479 // phpcs:enable
480 global $langs;
481
482 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
483 include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
484
485 $this->db->begin();
486
487 $remise = new DiscountAbsolute($this->db);
488 $result = $remise->fetch($idremise);
489
490 if ($result > 0) {
491 if ($remise->fk_facture) { // Protection against multiple submission
492 $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
493 $this->db->rollback();
494 return -5;
495 }
496
497 $line = new PropaleLigne($this->db);
498
499 $line->context = $this->context;
500
501 $line->fk_propal = $this->id;
502 $line->fk_remise_except = $remise->id;
503 $line->desc = $remise->description; // Description ligne
504 $line->vat_src_code = $remise->vat_src_code;
505 $line->tva_tx = $remise->tva_tx;
506 $line->subprice = -$remise->amount_ht;
507 $line->fk_product = 0; // Id produit predefined
508 $line->qty = 1;
509 $line->remise_percent = 0;
510 $line->rang = -1;
511 $line->info_bits = 2;
512
513 // TODO deprecated
514 $line->price = -$remise->amount_ht;
515
516 $line->total_ht = -$remise->amount_ht;
517 $line->total_tva = -$remise->amount_tva;
518 $line->total_ttc = -$remise->amount_ttc;
519
520 $result = $line->insert();
521 if ($result > 0) {
522 $result = $this->update_price(1);
523 if ($result > 0) {
524 $this->db->commit();
525 return 1;
526 } else {
527 $this->db->rollback();
528 return -1;
529 }
530 } else {
531 $this->error = $line->error;
532 $this->errors = $line->errors;
533 $this->db->rollback();
534 return -2;
535 }
536 } else {
537 $this->db->rollback();
538 return -2;
539 }
540 }
541
579 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)
580 {
581 global $mysoc, $conf, $langs;
582
583 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);
584
585 if ($this->statut == self::STATUS_DRAFT) {
586 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
587
588 // Clean parameters
589 if (empty($remise_percent)) {
590 $remise_percent = 0;
591 }
592 if (empty($qty)) {
593 $qty = 0;
594 }
595 if (empty($info_bits)) {
596 $info_bits = 0;
597 }
598 if (empty($rang)) {
599 $rang = 0;
600 }
601 if (empty($fk_parent_line) || $fk_parent_line < 0) {
602 $fk_parent_line = 0;
603 }
604
605 $remise_percent = price2num($remise_percent);
606 $qty = (float) price2num($qty);
607 $pu_ht = price2num($pu_ht);
608 $pu_ht_devise = price2num($pu_ht_devise);
609 $pu_ttc = price2num($pu_ttc);
610 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
611 $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
612 }
613 $txlocaltax1 = price2num($txlocaltax1);
614 $txlocaltax2 = price2num($txlocaltax2);
615 $pa_ht = price2num($pa_ht); // do not convert to float here, it breaks the functioning of $pa_ht_isemptystring
616 if ($price_base_type == 'HT') {
617 $pu = $pu_ht;
618 } else {
619 $pu = $pu_ttc;
620 }
621
622 // Check parameters
623 if ($type < 0) {
624 return -1;
625 }
626
627 if ($date_start && $date_end && $date_start > $date_end) {
628 $langs->load("errors");
629 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
630 return -1;
631 }
632
633 $this->db->begin();
634
635 $product_type = $type;
636 if (!empty($fk_product) && $fk_product > 0) {
637 $product = new Product($this->db);
638 $result = $product->fetch($fk_product);
639 $product_type = $product->type;
640
641 if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_PROPOSAL') && $product_type == 0 && $product->stock_reel < $qty) {
642 $langs->load("errors");
643 $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnProposal', $product->ref);
644 $this->db->rollback();
645 return -3;
646 }
647 }
648
649 // Calcul du total TTC et de la TVA pour la ligne a partir de
650 // qty, pu, remise_percent et txtva
651 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
652 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
653
654 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
655
656 // Clean vat code
657 $reg = array();
658 $vat_src_code = '';
659 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
660 $vat_src_code = $reg[1];
661 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
662 }
663
664 $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);
665
666 $total_ht = $tabprice[0];
667 $total_tva = $tabprice[1];
668 $total_ttc = $tabprice[2];
669 $total_localtax1 = $tabprice[9];
670 $total_localtax2 = $tabprice[10];
671 $pu_ht = $tabprice[3];
672 $pu_tva = $tabprice[4];
673 $pu_ttc = $tabprice[5];
674
675 // MultiCurrency
676 $multicurrency_total_ht = $tabprice[16];
677 $multicurrency_total_tva = $tabprice[17];
678 $multicurrency_total_ttc = $tabprice[18];
679 $pu_ht_devise = $tabprice[19];
680
681 // Rang to use
682 $ranktouse = $rang;
683 if ($ranktouse == -1) {
684 $rangmax = $this->line_max($fk_parent_line);
685 $ranktouse = $rangmax + 1;
686 }
687
688 // TODO A virer
689 // Anciens indicateurs: $price, $remise (a ne plus utiliser)
690 $price = $pu;
691 $remise = 0;
692 if ((float) $remise_percent > 0) {
693 $remise = round(((float) $pu * (float) $remise_percent / 100), 2);
694 $price = (float) $pu - $remise;
695 }
696
697 // Insert line
698 $this->line = new PropaleLigne($this->db);
699
700 $this->line->context = $this->context;
701
702 $this->line->fk_propal = $this->id;
703 $this->line->label = $label;
704 $this->line->desc = $desc;
705 $this->line->qty = $qty;
706
707 $this->line->vat_src_code = $vat_src_code;
708 $this->line->tva_tx = $txtva;
709 $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
710 $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
711 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
712 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
713 $this->line->fk_product = $fk_product;
714 $this->line->product_type = $type;
715 $this->line->fk_remise_except = $fk_remise_except;
716 $this->line->remise_percent = $remise_percent;
717 $this->line->subprice = $pu_ht;
718 $this->line->rang = $ranktouse;
719 $this->line->info_bits = $info_bits;
720 $this->line->total_ht = $total_ht;
721 $this->line->total_tva = $total_tva;
722 $this->line->total_localtax1 = $total_localtax1;
723 $this->line->total_localtax2 = $total_localtax2;
724 $this->line->total_ttc = $total_ttc;
725 $this->line->special_code = $special_code;
726 $this->line->fk_parent_line = $fk_parent_line;
727 $this->line->fk_unit = $fk_unit;
728
729 $this->line->date_start = $date_start;
730 $this->line->date_end = $date_end;
731
732 $this->line->fk_fournprice = $fk_fournprice;
733 $this->line->pa_ht = $pa_ht;
734
735 $this->line->origin_id = $origin_id;
736 $this->line->origin = $origin;
737
738 // Multicurrency
739 $this->line->fk_multicurrency = $this->fk_multicurrency;
740 $this->line->multicurrency_code = $this->multicurrency_code;
741 $this->line->multicurrency_subprice = $pu_ht_devise;
742 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
743 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
744 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
745
746 // Mise en option de la ligne
747 if (empty($qty) && empty($special_code)) {
748 $this->line->special_code = 3;
749 }
750
751 // TODO deprecated
752 $this->line->price = $price;
753
754 if (is_array($array_options) && count($array_options) > 0) {
755 $this->line->array_options = $array_options;
756 }
757
758 $result = $this->line->insert();
759 if ($result > 0) {
760 // Reorder if child line
761 if (!empty($fk_parent_line)) {
762 $this->line_order(true, 'DESC');
763 } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) { // Update all rank of all other lines
764 $linecount = count($this->lines);
765 for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
766 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
767 }
768 }
769
770 // Mise a jour information denormalisees au niveau de la propale meme
771 if (empty($noupdateafterinsertline)) {
772 $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.
773 }
774
775 if ($result > 0) {
776 $this->db->commit();
777 return $this->line->id;
778 } else {
779 $this->error = $this->db->error();
780 $this->db->rollback();
781 return -1;
782 }
783 } else {
784 $this->error = $this->line->error;
785 $this->errors = $this->line->errors;
786 $this->db->rollback();
787 return -2;
788 }
789 } else {
790 dol_syslog(get_class($this)."::addline status of proposal must be Draft to allow use of ->addline()", LOG_ERR);
791 return -3;
792 }
793 }
794
795
825 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)
826 {
827 global $mysoc, $langs;
828
829 dol_syslog(get_class($this)."::updateLine rowid=$rowid, pu=$pu, qty=$qty, remise_percent=$remise_percent,
830 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");
831 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
832
833 // Clean parameters
834 $remise_percent = price2num($remise_percent);
835 $qty = (float) price2num($qty);
836 $pu = price2num($pu);
837 $pu_ht_devise = price2num($pu_ht_devise);
838 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
839 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
840 }
841 $txlocaltax1 = price2num($txlocaltax1);
842 $txlocaltax2 = price2num($txlocaltax2);
843 $pa_ht = price2num($pa_ht); // do not convert to float here, it breaks the functioning of $pa_ht_isemptystring
844 if (empty($qty) && empty($special_code)) {
845 $special_code = 3; // Set option tag
846 }
847 if (!empty($qty) && $special_code == 3) {
848 $special_code = 0; // Remove option tag
849 }
850 if (empty($type)) {
851 $type = 0;
852 }
853
854 if ($date_start && $date_end && $date_start > $date_end) {
855 $langs->load("errors");
856 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
857 return -1;
858 }
859
860 if ($this->status == self::STATUS_DRAFT) {
861 $this->db->begin();
862
863 // Calcul du total TTC et de la TVA pour la ligne a partir de
864 // qty, pu, remise_percent et txtva
865 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
866 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
867
868 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
869
870 // Clean vat code
871 $reg = array();
872 $vat_src_code = '';
873 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
874 $vat_src_code = $reg[1];
875 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
876 }
877
878 // TODO Implement if (getDolGlobalInt('MAIN_UNIT_PRICE_WITH_TAX_IS_FOR_ALL_TAXES')) ?
879
880 $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);
881 $total_ht = $tabprice[0];
882 $total_tva = $tabprice[1];
883 $total_ttc = $tabprice[2];
884 $total_localtax1 = $tabprice[9];
885 $total_localtax2 = $tabprice[10];
886 $pu_ht = $tabprice[3];
887 $pu_tva = $tabprice[4];
888 $pu_ttc = $tabprice[5];
889
890 // MultiCurrency
891 $multicurrency_total_ht = $tabprice[16];
892 $multicurrency_total_tva = $tabprice[17];
893 $multicurrency_total_ttc = $tabprice[18];
894 $pu_ht_devise = $tabprice[19];
895
896 // Anciens indicateurs: $price, $remise (a ne plus utiliser)
897 $price = $pu;
898 $remise = 0;
899 if ((float) $remise_percent > 0) {
900 $remise = round(((float) $pu * (float) $remise_percent / 100), 2);
901 $price = (float) $pu - $remise;
902 }
903
904 //Fetch current line from the database and then clone the object and set it in $oldline property
905 $line = new PropaleLigne($this->db);
906 $line->fetch($rowid);
907
908 $staticline = clone $line;
909
910 $line->oldline = $staticline;
911 $this->line = $line;
912 $this->line->context = $this->context;
913 $this->line->rang = $rang;
914
915 // Reorder if fk_parent_line change
916 if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
917 $rangmax = $this->line_max($fk_parent_line);
918 $this->line->rang = $rangmax + 1;
919 }
920
921 $this->line->id = $rowid;
922 $this->line->label = $label;
923 $this->line->desc = $desc;
924 $this->line->qty = $qty;
925 $this->line->product_type = $type;
926 $this->line->vat_src_code = $vat_src_code;
927 $this->line->tva_tx = $txtva;
928 $this->line->localtax1_tx = $txlocaltax1;
929 $this->line->localtax2_tx = $txlocaltax2;
930 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
931 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
932 $this->line->remise_percent = $remise_percent;
933 $this->line->subprice = $pu_ht;
934 $this->line->info_bits = $info_bits;
935
936 $this->line->total_ht = $total_ht;
937 $this->line->total_tva = $total_tva;
938 $this->line->total_localtax1 = $total_localtax1;
939 $this->line->total_localtax2 = $total_localtax2;
940 $this->line->total_ttc = $total_ttc;
941 $this->line->special_code = $special_code;
942 $this->line->fk_parent_line = $fk_parent_line;
943 $this->line->skip_update_total = $skip_update_total;
944 $this->line->fk_unit = $fk_unit;
945
946 $this->line->fk_fournprice = $fk_fournprice;
947 $this->line->pa_ht = $pa_ht;
948
949 $this->line->date_start = $date_start;
950 $this->line->date_end = $date_end;
951
952 if (is_array($array_options) && count($array_options) > 0) {
953 // We replace values in this->line->array_options only for entries defined into $array_options
954 foreach ($array_options as $key => $value) {
955 $this->line->array_options[$key] = $array_options[$key];
956 }
957 }
958
959 // Multicurrency
960 $this->line->multicurrency_subprice = $pu_ht_devise;
961 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
962 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
963 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
964
965 $result = $this->line->update($notrigger);
966 if ($result > 0) {
967 // Reorder if child line
968 if (!empty($fk_parent_line)) {
969 $this->line_order(true, 'DESC');
970 }
971
972 $this->update_price(1, 'auto');
973
974 // $this is Propal
975 // $this->fk_propal = $this->id;
976 // $this->rowid = $rowid;
977
978 $this->db->commit();
979 return $result;
980 } else {
981 $this->error = $this->line->error;
982 $this->errors = $this->line->errors;
983 $this->db->rollback();
984 return -1;
985 }
986 } else {
987 dol_syslog(get_class($this)."::updateline Erreur -2 Propal en mode incompatible pour cette action");
988 return -2;
989 }
990 }
991
992
1000 public function deleteLine($lineid, $id = 0)
1001 {
1002 global $user;
1003
1004 if ($this->statut == self::STATUS_DRAFT) {
1005 $this->db->begin();
1006
1007 $line = new PropaleLigne($this->db);
1008
1009 $line->context = $this->context;
1010
1011 // Load data
1012 $line->fetch($lineid);
1013
1014 if ($id > 0 && $line->fk_propal != $id) {
1015 $this->error = 'ErrorLineIDDoesNotMatchWithObjectID';
1016 return -1;
1017 }
1018
1019 // Memorize previous line for triggers
1020 $staticline = clone $line;
1021 $line->oldline = $staticline;
1022
1023 if ($line->delete($user) > 0) {
1024 $this->update_price(1);
1025
1026 $this->db->commit();
1027 return 1;
1028 } else {
1029 $this->error = $line->error;
1030 $this->errors = $line->errors;
1031 $this->db->rollback();
1032 return -1;
1033 }
1034 } else {
1035 $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
1036 return -2;
1037 }
1038 }
1039
1040
1049 public function create($user, $notrigger = 0)
1050 {
1051 global $conf, $hookmanager, $mysoc;
1052 $error = 0;
1053
1054 $now = dol_now();
1055
1056 // Clean parameters
1057 if (empty($this->date)) {
1058 $this->date = $this->datep;
1059 }
1060 $this->fin_validite = $this->date + ($this->duree_validite * 24 * 3600);
1061 if (empty($this->availability_id)) {
1062 $this->availability_id = 0;
1063 }
1064 if (empty($this->demand_reason_id)) {
1065 $this->demand_reason_id = 0;
1066 }
1067
1068 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1069 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1070 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date);
1071 } else {
1072 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1073 }
1074 if (empty($this->fk_multicurrency)) {
1075 $this->multicurrency_code = $conf->currency;
1076 $this->fk_multicurrency = 0;
1077 $this->multicurrency_tx = 1;
1078 }
1079
1080 // Set tmp vars
1081 $delivery_date = $this->delivery_date;
1082
1083 dol_syslog(get_class($this)."::create");
1084
1085 // Check parameters
1086 $result = $this->fetch_thirdparty();
1087 if ($result < 0) {
1088 $this->error = "Failed to fetch company";
1089 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
1090 return -3;
1091 }
1092
1093 // Check parameters
1094 if (!empty($this->ref)) { // We check that ref is not already used
1095 $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
1096 if ($result > 0) {
1097 $this->error = 'ErrorRefAlreadyExists';
1098 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
1099 $this->db->rollback();
1100 return -1;
1101 }
1102 }
1103
1104 if (empty($this->date)) {
1105 $this->error = "Date of proposal is required";
1106 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
1107 return -4;
1108 }
1109
1110
1111 $this->db->begin();
1112
1113 // Insert into database
1114 $sql = "INSERT INTO ".MAIN_DB_PREFIX."propal (";
1115 $sql .= "fk_soc";
1116 $sql .= ", price";
1117 $sql .= ", total_tva";
1118 $sql .= ", total_ttc";
1119 $sql .= ", datep";
1120 $sql .= ", datec";
1121 $sql .= ", ref";
1122 $sql .= ", fk_user_author";
1123 $sql .= ", note_private";
1124 $sql .= ", note_public";
1125 $sql .= ", model_pdf";
1126 $sql .= ", fin_validite";
1127 $sql .= ", fk_cond_reglement";
1128 $sql .= ", deposit_percent";
1129 $sql .= ", fk_mode_reglement";
1130 $sql .= ", fk_account";
1131 $sql .= ", ref_client";
1132 $sql .= ", ref_ext";
1133 $sql .= ", date_livraison";
1134 $sql .= ", fk_shipping_method";
1135 $sql .= ", fk_warehouse";
1136 $sql .= ", fk_availability";
1137 $sql .= ", fk_input_reason";
1138 $sql .= ", fk_projet";
1139 $sql .= ", fk_incoterms";
1140 $sql .= ", location_incoterms";
1141 $sql .= ", entity";
1142 $sql .= ", fk_multicurrency";
1143 $sql .= ", multicurrency_code";
1144 $sql .= ", multicurrency_tx";
1145 $sql .= ") ";
1146 $sql .= " VALUES (";
1147 $sql .= $this->socid;
1148 $sql .= ", 0";
1149 $sql .= ", 0";
1150 $sql .= ", 0";
1151 $sql .= ", '".$this->db->idate($this->date)."'";
1152 $sql .= ", '".$this->db->idate($now)."'";
1153 $sql .= ", '(PROV)'";
1154 $sql .= ", ".($user->id > 0 ? ((int) $user->id) : "NULL");
1155 $sql .= ", '".$this->db->escape($this->note_private)."'";
1156 $sql .= ", '".$this->db->escape($this->note_public)."'";
1157 $sql .= ", '".$this->db->escape($this->model_pdf)."'";
1158 $sql .= ", ".($this->fin_validite != '' ? "'".$this->db->idate($this->fin_validite)."'" : "NULL");
1159 $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : 'NULL');
1160 $sql .= ", ".(!empty($this->deposit_percent) ? "'".$this->db->escape($this->deposit_percent)."'" : 'NULL');
1161 $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : 'NULL');
1162 $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
1163 $sql .= ", '".$this->db->escape($this->ref_client)."'";
1164 $sql .= ", '".$this->db->escape($this->ref_ext)."'";
1165 $sql .= ", ".(empty($delivery_date) ? "NULL" : "'".$this->db->idate($delivery_date)."'");
1166 $sql .= ", ".($this->shipping_method_id > 0 ? $this->shipping_method_id : 'NULL');
1167 $sql .= ", ".($this->warehouse_id > 0 ? $this->warehouse_id : 'NULL');
1168 $sql .= ", ".$this->availability_id;
1169 $sql .= ", ".$this->demand_reason_id;
1170 $sql .= ", ".($this->fk_project ? $this->fk_project : "null");
1171 $sql .= ", ".(int) $this->fk_incoterms;
1172 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
1173 $sql .= ", ".setEntity($this);
1174 $sql .= ", ".(int) $this->fk_multicurrency;
1175 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1176 $sql .= ", ".(float) $this->multicurrency_tx;
1177 $sql .= ")";
1178
1179 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1180 $resql = $this->db->query($sql);
1181 if ($resql) {
1182 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."propal");
1183
1184 if ($this->id) {
1185 $this->ref = '(PROV'.$this->id.')';
1186 $sql = 'UPDATE '.MAIN_DB_PREFIX."propal SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
1187
1188 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1189 $resql = $this->db->query($sql);
1190 if (!$resql) {
1191 $error++;
1192 }
1193
1194 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1195 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1196 }
1197
1198 // Add object linked
1199 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1200 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1201 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, ...))
1202 foreach ($tmp_origin_id as $origin_id) {
1203 $ret = $this->add_object_linked($origin, $origin_id);
1204 if (!$ret) {
1205 $this->error = $this->db->lasterror();
1206 $error++;
1207 }
1208 }
1209 } else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1210 $origin_id = $tmp_origin_id;
1211 $ret = $this->add_object_linked($origin, $origin_id);
1212 if (!$ret) {
1213 $this->error = $this->db->lasterror();
1214 $error++;
1215 }
1216 }
1217 }
1218 }
1219
1220 /*
1221 * Insertion du detail des produits dans la base
1222 * Insert products detail in database
1223 */
1224 if (!$error) {
1225 $fk_parent_line = 0;
1226 $num = count($this->lines);
1227
1228 for ($i = 0; $i < $num; $i++) {
1229 if (!is_object($this->lines[$i])) { // If this->lines is not array of objects, coming from REST API
1230 // Convert into object this->lines[$i].
1231 $line = (object) $this->lines[$i];
1232 } else {
1233 $line = $this->lines[$i];
1234 }
1235 // Reset fk_parent_line for line that are not child lines or special product
1236 if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
1237 $fk_parent_line = 0;
1238 }
1239 // Complete vat rate with code
1240 $vatrate = $line->tva_tx;
1241 if ($line->vat_src_code && !preg_match('/\‍(.*\‍)/', $vatrate)) {
1242 $vatrate .= ' ('.$line->vat_src_code.')';
1243 }
1244
1245 if (getDolGlobalString('MAIN_CREATEFROM_KEEP_LINE_ORIGIN_INFORMATION')) {
1246 $originid = $line->origin_id;
1247 $origintype = $line->origin;
1248 } else {
1249 $originid = $line->id;
1250 $origintype = $this->element;
1251 }
1252
1253 $result = $this->addline(
1254 $line->desc,
1255 $line->subprice,
1256 $line->qty,
1257 $vatrate,
1258 $line->localtax1_tx,
1259 $line->localtax2_tx,
1260 $line->fk_product,
1261 $line->remise_percent,
1262 'HT',
1263 0,
1264 $line->info_bits,
1265 $line->product_type,
1266 $line->rang,
1267 $line->special_code,
1268 $fk_parent_line,
1269 $line->fk_fournprice,
1270 $line->pa_ht,
1271 $line->label,
1272 $line->date_start,
1273 $line->date_end,
1274 $line->array_options,
1275 $line->fk_unit,
1276 $origintype,
1277 $originid,
1278 0,
1279 0,
1280 1
1281 );
1282
1283 if ($result < 0) {
1284 $error++;
1285 $this->error = $this->db->error;
1286 dol_print_error($this->db);
1287 break;
1288 }
1289
1290 // Set the id on created row
1291 $line->id = $result;
1292
1293 // Defined the new fk_parent_line
1294 if ($result > 0 && $line->product_type == 9) {
1295 $fk_parent_line = $result;
1296 }
1297 }
1298 }
1299
1300 // Set delivery address
1301 /*if (! $error && $this->fk_delivery_address)
1302 {
1303 $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
1304 $sql.= " SET fk_delivery_address = ".((int) $this->fk_delivery_address);
1305 $sql.= " WHERE ref = '".$this->db->escape($this->ref)."'";
1306 $sql.= " AND entity = ".setEntity($this);
1307
1308 $result=$this->db->query($sql);
1309 }*/
1310
1311 if (!$error) {
1312 // Mise a jour infos denormalisees
1313 $resql = $this->update_price(1, 'auto', 0, $mysoc);
1314 if ($resql) {
1315 $action = 'update';
1316
1317 // Actions on extra fields
1318 if (!$error) {
1319 $result = $this->insertExtraFields();
1320 if ($result < 0) {
1321 $error++;
1322 }
1323 }
1324
1325 if (!$error && !$notrigger) {
1326 // Call trigger
1327 $result = $this->call_trigger('PROPAL_CREATE', $user);
1328 if ($result < 0) {
1329 $error++;
1330 }
1331 // End call triggers
1332 }
1333 } else {
1334 $this->error = $this->db->lasterror();
1335 $error++;
1336 }
1337 }
1338 } else {
1339 $this->error = $this->db->lasterror();
1340 $error++;
1341 }
1342
1343 if (!$error) {
1344 $this->db->commit();
1345 dol_syslog(get_class($this)."::create done id=".$this->id);
1346 return $this->id;
1347 } else {
1348 $this->db->rollback();
1349 return -2;
1350 }
1351 } else {
1352 $this->error = $this->db->lasterror();
1353 $this->db->rollback();
1354 return -1;
1355 }
1356 }
1357
1368 public function createFromClone(User $user, $socid = 0, $forceentity = null, $update_prices = false, $update_desc = false)
1369 {
1370 global $conf, $hookmanager, $mysoc;
1371
1372 dol_include_once('/projet/class/project.class.php');
1373
1374 $error = 0;
1375 $now = dol_now();
1376
1377 dol_syslog(__METHOD__, LOG_DEBUG);
1378
1379 $object = new self($this->db);
1380
1381 $this->db->begin();
1382
1383 // Load source object
1384 $object->fetch($this->id);
1385
1386 $objsoc = new Societe($this->db);
1387
1388 // Change socid if needed
1389 if (!empty($socid) && $socid != $object->socid) {
1390 if ($objsoc->fetch($socid) > 0) {
1391 $object->socid = $objsoc->id;
1392 $object->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1393 $object->deposit_percent = (!empty($objsoc->deposit_percent) ? $objsoc->deposit_percent : null);
1394 $object->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1395 $object->fk_delivery_address = 0;
1396
1397 /*if (isModEnabled('project'))
1398 {
1399 $project = new Project($db);
1400 if ($this->fk_project > 0 && $project->fetch($this->fk_project)) {
1401 if ($project->socid <= 0) $clonedObj->fk_project = $this->fk_project;
1402 else $clonedObj->fk_project = '';
1403 } else {
1404 $clonedObj->fk_project = '';
1405 }
1406 }*/
1407 $object->fk_project = 0; // A cloned proposal is set by default to no project.
1408 }
1409
1410 // reset ref_client
1411 if (!getDolGlobalString('MAIN_KEEP_REF_CUSTOMER_ON_CLONING')) {
1412 $object->ref_client = '';
1413 }
1414
1415 // TODO Change product price if multi-prices
1416 } else {
1417 $objsoc->fetch($object->socid);
1418 }
1419
1420 // update prices
1421 if ($update_prices === true || $update_desc === true) {
1422 if ($objsoc->id > 0 && !empty($object->lines)) {
1423 if ($update_prices === true && getDolGlobalString('PRODUIT_CUSTOMER_PRICES')) {
1424 // If price per customer
1425 require_once DOL_DOCUMENT_ROOT . '/product/class/productcustomerprice.class.php';
1426 }
1427
1428 foreach ($object->lines as $line) {
1429 $line->id = 0;
1430
1431 if ($line->fk_product > 0) {
1432 $prod = new Product($this->db);
1433 $res = $prod->fetch($line->fk_product);
1434 if ($res > 0) {
1435 if ($update_prices === true) {
1436 $pu_ht = $prod->price;
1437 $tva_tx = get_default_tva($mysoc, $objsoc, $prod->id);
1438 $remise_percent = $objsoc->remise_percent;
1439
1440 if (getDolGlobalString('PRODUIT_MULTIPRICES') && $objsoc->price_level > 0) {
1441 $pu_ht = $prod->multiprices[$objsoc->price_level];
1442 if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) { // using this option is a bug. kept for backward compatibility
1443 if (isset($prod->multiprices_tva_tx[$objsoc->price_level])) {
1444 $tva_tx = $prod->multiprices_tva_tx[$objsoc->price_level];
1445 }
1446 }
1447 } elseif (getDolGlobalString('PRODUIT_CUSTOMER_PRICES')) {
1448 $prodcustprice = new ProductCustomerPrice($this->db);
1449 $filter = array('t.fk_product' => $prod->id, 't.fk_soc' => $objsoc->id);
1450 $result = $prodcustprice->fetchAll('', '', 0, 0, $filter);
1451 if ($result) {
1452 // If there is some prices specific to the customer
1453 if (count($prodcustprice->lines) > 0) {
1454 $pu_ht = price($prodcustprice->lines[0]->price);
1455 $tva_tx = ($prodcustprice->lines[0]->default_vat_code ? $prodcustprice->lines[0]->tva_tx.' ('.$prodcustprice->lines[0]->default_vat_code.' )' : $prodcustprice->lines[0]->tva_tx);
1456 if ($prodcustprice->lines[0]->default_vat_code && !preg_match('/\‍(.*\‍)/', $tva_tx)) {
1457 $tva_tx .= ' ('.$prodcustprice->lines[0]->default_vat_code.')';
1458 }
1459 }
1460 }
1461 }
1462
1463 $line->subprice = $pu_ht;
1464 $line->tva_tx = $tva_tx;
1465 $line->remise_percent = $remise_percent;
1466 }
1467 if ($update_desc === true) {
1468 $line->desc = $prod->description;
1469 }
1470 }
1471 }
1472 }
1473 }
1474 }
1475
1476 $object->id = 0;
1477 $object->ref = '';
1478 $object->entity = (!empty($forceentity) ? $forceentity : $object->entity);
1479 $object->statut = self::STATUS_DRAFT;
1480
1481 // Clear fields
1482 $object->user_creation_id = $user->id;
1483 $object->user_validation_id = 0;
1484 $object->date = $now;
1485 $object->datep = $now; // deprecated
1486 $object->fin_validite = $object->date + ($object->duree_validite * 24 * 3600);
1487 if (!getDolGlobalString('MAIN_KEEP_REF_CUSTOMER_ON_CLONING')) {
1488 $object->ref_client = '';
1489 }
1490 if (getDolGlobalInt('MAIN_DONT_KEEP_NOTE_ON_CLONING') == 1) {
1491 $object->note_private = '';
1492 $object->note_public = '';
1493 }
1494 // Create clone
1495 $object->context['createfromclone'] = 'createfromclone';
1496 $result = $object->create($user);
1497 if ($result < 0) {
1498 $this->error = $object->error;
1499 $this->errors = array_merge($this->errors, $object->errors);
1500 $error++;
1501 }
1502
1503 if (!$error && !getDolGlobalInt('MAIN_IGNORE_CONTACTS_ON_CLONING')) {
1504 // copy internal contacts
1505 if ($object->copy_linked_contact($this, 'internal') < 0) {
1506 $error++;
1507 }
1508 }
1509
1510 if (!$error) {
1511 // copy external contacts if same company
1512 if ($this->socid == $object->socid) {
1513 if ($object->copy_linked_contact($this, 'external') < 0) {
1514 $error++;
1515 }
1516 }
1517 }
1518
1519 if (!$error) {
1520 // Hook of thirdparty module
1521 if (is_object($hookmanager)) {
1522 $parameters = array('objFrom' => $this, 'clonedObj' => $object);
1523 $action = '';
1524 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
1525 if ($reshook < 0) {
1526 $this->setErrorsFromObject($hookmanager);
1527 $error++;
1528 }
1529 }
1530 }
1531
1532 unset($object->context['createfromclone']);
1533
1534 // End
1535 if (!$error) {
1536 $this->db->commit();
1537 return $object->id;
1538 } else {
1539 $this->db->rollback();
1540 return -1;
1541 }
1542 }
1543
1553 public function fetch($rowid, $ref = '', $ref_ext = '', $forceentity = 0)
1554 {
1555 $sql = "SELECT p.rowid, p.ref, p.entity, p.fk_soc";
1556 $sql .= ", p.total_ttc, p.total_tva, p.localtax1, p.localtax2, p.total_ht";
1557 $sql .= ", p.datec";
1558 $sql .= ", p.date_signature as dates";
1559 $sql .= ", p.date_valid as datev";
1560 $sql .= ", p.datep as dp";
1561 $sql .= ", p.fin_validite as dfv";
1562 $sql .= ", p.date_livraison as delivery_date";
1563 $sql .= ", p.model_pdf, p.last_main_doc, p.ref_client, ref_ext, p.extraparams";
1564 $sql .= ", p.note_private, p.note_public";
1565 $sql .= ", p.fk_projet as fk_project, p.fk_statut";
1566 $sql .= ", p.fk_user_author, p.fk_user_valid, p.fk_user_cloture";
1567 $sql .= ", p.fk_delivery_address";
1568 $sql .= ", p.fk_availability";
1569 $sql .= ", p.fk_input_reason";
1570 $sql .= ", p.fk_cond_reglement";
1571 $sql .= ", p.fk_mode_reglement";
1572 $sql .= ', p.fk_account';
1573 $sql .= ", p.fk_shipping_method";
1574 $sql .= ", p.fk_warehouse";
1575 $sql .= ", p.fk_incoterms, p.location_incoterms";
1576 $sql .= ", p.fk_multicurrency, p.multicurrency_code, p.multicurrency_tx, p.multicurrency_total_ht, p.multicurrency_total_tva, p.multicurrency_total_ttc";
1577 $sql .= ", p.tms as date_modification";
1578 $sql .= ", i.libelle as label_incoterms";
1579 $sql .= ", c.label as statut_label";
1580 $sql .= ", ca.code as availability_code, ca.label as availability";
1581 $sql .= ", dr.code as demand_reason_code, dr.label as demand_reason";
1582 $sql .= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc, p.deposit_percent";
1583 $sql .= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement";
1584 $sql .= " FROM ".MAIN_DB_PREFIX."propal as p";
1585 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_propalst as c ON p.fk_statut = c.id';
1586 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id AND cp.entity IN ('.getEntity('c_paiement').')';
1587 $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').')';
1588 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_availability as ca ON p.fk_availability = ca.rowid';
1589 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_input_reason as dr ON p.fk_input_reason = dr.rowid';
1590 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON p.fk_incoterms = i.rowid';
1591
1592 if (!empty($ref)) {
1593 if (!empty($forceentity)) {
1594 $sql .= " WHERE p.entity = ".(int) $forceentity; // Check only the current entity because we may have the same reference in several entities
1595 } else {
1596 $sql .= " WHERE p.entity IN (".getEntity('propal').")";
1597 }
1598 $sql .= " AND p.ref='".$this->db->escape($ref)."'";
1599 } else {
1600 // Don't use entity if you use rowid
1601 $sql .= " WHERE p.rowid = ".((int) $rowid);
1602 }
1603
1604 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1605 $resql = $this->db->query($sql);
1606 if ($resql) {
1607 if ($this->db->num_rows($resql)) {
1608 $obj = $this->db->fetch_object($resql);
1609
1610 $this->id = $obj->rowid;
1611 $this->entity = $obj->entity;
1612
1613 $this->ref = $obj->ref;
1614 $this->ref_client = $obj->ref_client;
1615 $this->ref_customer = $obj->ref_client;
1616 $this->ref_ext = $obj->ref_ext;
1617
1618 $this->total = $obj->total_ttc; // TODO deprecated
1619 $this->total_ttc = $obj->total_ttc;
1620 $this->total_ht = $obj->total_ht;
1621 $this->total_tva = $obj->total_tva;
1622 $this->total_localtax1 = $obj->localtax1;
1623 $this->total_localtax2 = $obj->localtax2;
1624
1625 $this->socid = $obj->fk_soc;
1626 $this->thirdparty = null; // Clear if another value was already set by fetch_thirdparty
1627
1628 $this->fk_project = $obj->fk_project;
1629 $this->project = null; // Clear if another value was already set by fetch_projet
1630
1631 $this->model_pdf = $obj->model_pdf;
1632 $this->last_main_doc = $obj->last_main_doc;
1633 $this->note = $obj->note_private; // TODO deprecated
1634 $this->note_private = $obj->note_private;
1635 $this->note_public = $obj->note_public;
1636
1637 $this->status = (int) $obj->fk_statut;
1638 $this->statut = $this->status; // deprecated
1639
1640 $this->datec = $this->db->jdate($obj->datec); // TODO deprecated
1641 $this->datev = $this->db->jdate($obj->datev); // TODO deprecated
1642 $this->date_creation = $this->db->jdate($obj->datec); //Creation date
1643 $this->date_validation = $this->db->jdate($obj->datev); //Validation date
1644 $this->date_modification = $this->db->jdate($obj->date_modification); // tms
1645 $this->date_signature = $this->db->jdate($obj->dates); // Signature date
1646 $this->date = $this->db->jdate($obj->dp); // Proposal date
1647 $this->datep = $this->db->jdate($obj->dp); // deprecated
1648 $this->fin_validite = $this->db->jdate($obj->dfv);
1649 $this->delivery_date = $this->db->jdate($obj->delivery_date);
1650 $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1651 $this->warehouse_id = ($obj->fk_warehouse > 0) ? $obj->fk_warehouse : null;
1652 $this->availability_id = $obj->fk_availability;
1653 $this->availability_code = $obj->availability_code;
1654 $this->availability = $obj->availability;
1655 $this->demand_reason_id = $obj->fk_input_reason;
1656 $this->demand_reason_code = $obj->demand_reason_code;
1657 $this->demand_reason = $obj->demand_reason;
1658 $this->fk_address = $obj->fk_delivery_address;
1659
1660 $this->mode_reglement_id = $obj->fk_mode_reglement;
1661 $this->mode_reglement_code = $obj->mode_reglement_code;
1662 $this->mode_reglement = $obj->mode_reglement;
1663 $this->fk_account = ($obj->fk_account > 0) ? $obj->fk_account : null;
1664 $this->cond_reglement_id = $obj->fk_cond_reglement;
1665 $this->cond_reglement_code = $obj->cond_reglement_code;
1666 $this->cond_reglement = $obj->cond_reglement;
1667 $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1668 $this->deposit_percent = $obj->deposit_percent;
1669
1670 $this->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
1671
1672 $this->user_author_id = $obj->fk_user_author;
1673 $this->user_validation_id = $obj->fk_user_valid;
1674 $this->user_closing_id = $obj->fk_user_cloture;
1675
1676 //Incoterms
1677 $this->fk_incoterms = $obj->fk_incoterms;
1678 $this->location_incoterms = $obj->location_incoterms;
1679 $this->label_incoterms = $obj->label_incoterms;
1680
1681 // Multicurrency
1682 $this->fk_multicurrency = $obj->fk_multicurrency;
1683 $this->multicurrency_code = $obj->multicurrency_code;
1684 $this->multicurrency_tx = $obj->multicurrency_tx;
1685 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1686 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1687 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1688
1689 // Retrieve all extrafield
1690 // fetch optionals attributes and labels
1691 $this->fetch_optionals();
1692
1693 $this->db->free($resql);
1694
1695 $this->lines = array();
1696
1697 // Lines
1698 $result = $this->fetch_lines();
1699 if ($result < 0) {
1700 return -3;
1701 }
1702
1703 return 1;
1704 }
1705
1706 $this->error = "Record Not Found";
1707 return 0;
1708 } else {
1709 $this->error = $this->db->lasterror();
1710 return -1;
1711 }
1712 }
1713
1721 public function update(User $user, $notrigger = 0)
1722 {
1723 global $conf;
1724
1725 $error = 0;
1726
1727 // Clean parameters
1728 if (isset($this->ref)) {
1729 $this->ref = trim($this->ref);
1730 }
1731 if (isset($this->ref_client)) {
1732 $this->ref_client = trim($this->ref_client);
1733 }
1734 if (isset($this->note) || isset($this->note_private)) {
1735 $this->note_private = (isset($this->note_private) ? trim($this->note_private) : trim($this->note));
1736 }
1737 if (isset($this->note_public)) {
1738 $this->note_public = trim($this->note_public);
1739 }
1740 if (isset($this->model_pdf)) {
1741 $this->model_pdf = trim($this->model_pdf);
1742 }
1743 if (isset($this->import_key)) {
1744 $this->import_key = trim($this->import_key);
1745 }
1746 if (!empty($this->duree_validite) && is_numeric($this->duree_validite)) {
1747 $this->fin_validite = $this->date + ($this->duree_validite * 24 * 3600);
1748 }
1749
1750 // Check parameters
1751 // Put here code to add control on parameters values
1752
1753 // Update request
1754 $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET";
1755 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1756 $sql .= " ref_client=".(isset($this->ref_client) ? "'".$this->db->escape($this->ref_client)."'" : "null").",";
1757 $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1758 $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
1759 $sql .= " datep=".(strval($this->date) != '' ? "'".$this->db->idate($this->date)."'" : 'null').",";
1760 if (!empty($this->fin_validite)) {
1761 $sql .= " fin_validite=".(strval($this->fin_validite) != '' ? "'".$this->db->idate($this->fin_validite)."'" : 'null').",";
1762 }
1763 $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
1764 $sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
1765 $sql .= " localtax1=".(isset($this->total_localtax1) ? $this->total_localtax1 : "null").",";
1766 $sql .= " localtax2=".(isset($this->total_localtax2) ? $this->total_localtax2 : "null").",";
1767 $sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
1768 $sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
1769 $sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
1770 $sql .= " fk_user_author=".(isset($this->user_author_id) ? $this->user_author_id : "null").",";
1771 $sql .= " fk_user_valid = ".(!empty($this->user_validation_id) ? (int) $this->user_validation_id : "null").",";
1772 $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
1773 $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
1774 $sql .= " deposit_percent=".(!empty($this->deposit_percent) ? "'".$this->db->escape($this->deposit_percent)."'" : "null").",";
1775 $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
1776 $sql .= " fk_input_reason=".(isset($this->demand_reason_id) ? $this->demand_reason_id : "null").",";
1777 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1778 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1779 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1780 $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
1781 $sql .= " WHERE rowid=".((int) $this->id);
1782
1783 $this->db->begin();
1784
1785 dol_syslog(get_class($this)."::update", LOG_DEBUG);
1786 $resql = $this->db->query($sql);
1787 if (!$resql) {
1788 $error++;
1789 $this->errors[] = "Error ".$this->db->lasterror();
1790 }
1791
1792 if (!$error) {
1793 $result = $this->insertExtraFields();
1794 if ($result < 0) {
1795 $error++;
1796 }
1797 }
1798
1799 if (!$error && !$notrigger) {
1800 // Call trigger
1801 $result = $this->call_trigger('PROPAL_MODIFY', $user);
1802 if ($result < 0) {
1803 $error++;
1804 }
1805 // End call triggers
1806 }
1807
1808 // Commit or rollback
1809 if ($error) {
1810 foreach ($this->errors as $errmsg) {
1811 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1812 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1813 }
1814 $this->db->rollback();
1815 return -1 * $error;
1816 } else {
1817 $this->db->commit();
1818 return 1;
1819 }
1820 }
1821
1822
1823 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1832 public function fetch_lines($only_product = 0, $loadalsotranslation = 0, $sqlforgedfilters = '')
1833 {
1834 // phpcs:enable
1835 $this->lines = array();
1836
1837 $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,';
1838 $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,';
1839 $sql .= ' d.fk_unit,';
1840 $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,';
1841 $sql .= ' p.weight, p.weight_units, p.volume, p.volume_units,';
1842 $sql .= ' d.date_start, d.date_end,';
1843 $sql .= ' d.fk_multicurrency, d.multicurrency_code, d.multicurrency_subprice, d.multicurrency_total_ht, d.multicurrency_total_tva, d.multicurrency_total_ttc';
1844 $sql .= ' FROM '.MAIN_DB_PREFIX.'propaldet as d';
1845 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (d.fk_product = p.rowid)';
1846 $sql .= ' WHERE d.fk_propal = '.((int) $this->id);
1847 if ($only_product) {
1848 $sql .= ' AND p.fk_product_type = 0';
1849 }
1850 if ($sqlforgedfilters) {
1851 $sql .= $sqlforgedfilters;
1852 }
1853 $sql .= ' ORDER by d.rang';
1854
1855 dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
1856 $result = $this->db->query($sql);
1857 if ($result) {
1858 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
1859
1860 $num = $this->db->num_rows($result);
1861
1862 $i = 0;
1863 while ($i < $num) {
1864 $objp = $this->db->fetch_object($result);
1865
1866 $line = new PropaleLigne($this->db);
1867
1868 $line->rowid = $objp->rowid; //Deprecated
1869 $line->id = $objp->rowid;
1870 $line->fk_propal = $objp->fk_propal;
1871 $line->fk_parent_line = $objp->fk_parent_line;
1872 $line->product_type = $objp->product_type;
1873 $line->label = $objp->custom_label;
1874 $line->desc = $objp->description; // Description ligne
1875 $line->description = $objp->description; // Description ligne
1876 $line->qty = $objp->qty;
1877 $line->vat_src_code = $objp->vat_src_code;
1878 $line->tva_tx = $objp->tva_tx;
1879 $line->localtax1_tx = $objp->localtax1_tx;
1880 $line->localtax2_tx = $objp->localtax2_tx;
1881 $line->localtax1_type = $objp->localtax1_type;
1882 $line->localtax2_type = $objp->localtax2_type;
1883 $line->subprice = $objp->subprice;
1884 $line->fk_remise_except = $objp->fk_remise_except;
1885 $line->remise_percent = $objp->remise_percent;
1886 $line->price = $objp->price; // TODO deprecated
1887
1888 $line->info_bits = $objp->info_bits;
1889 $line->total_ht = $objp->total_ht;
1890 $line->total_tva = $objp->total_tva;
1891 $line->total_localtax1 = $objp->total_localtax1;
1892 $line->total_localtax2 = $objp->total_localtax2;
1893 $line->total_ttc = $objp->total_ttc;
1894 $line->fk_fournprice = $objp->fk_fournprice;
1895 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1896 $line->pa_ht = $marginInfos[0];
1897 $line->marge_tx = $marginInfos[1];
1898 $line->marque_tx = $marginInfos[2];
1899 $line->special_code = $objp->special_code;
1900 $line->rang = $objp->rang;
1901
1902 $line->fk_product = $objp->fk_product;
1903
1904 $line->ref = $objp->product_ref; // deprecated
1905 $line->libelle = $objp->product_label; // deprecated
1906
1907 $line->product_ref = $objp->product_ref;
1908 $line->product_label = $objp->product_label;
1909 $line->product_desc = $objp->product_desc; // Description produit
1910 $line->product_tobatch = $objp->product_tobatch;
1911 $line->product_barcode = $objp->product_barcode;
1912
1913 $line->fk_product_type = $objp->fk_product_type; // deprecated
1914 $line->fk_unit = $objp->fk_unit;
1915 $line->weight = $objp->weight;
1916 $line->weight_units = $objp->weight_units;
1917 $line->volume = $objp->volume;
1918 $line->volume_units = $objp->volume_units;
1919
1920 $line->date_start = $this->db->jdate($objp->date_start);
1921 $line->date_end = $this->db->jdate($objp->date_end);
1922
1923 // Multicurrency
1924 $line->fk_multicurrency = $objp->fk_multicurrency;
1925 $line->multicurrency_code = $objp->multicurrency_code;
1926 $line->multicurrency_subprice = $objp->multicurrency_subprice;
1927 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
1928 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
1929 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
1930
1931 $line->fetch_optionals();
1932
1933 // multilangs
1934 if (getDolGlobalInt('MAIN_MULTILANGS') && !empty($objp->fk_product) && !empty($loadalsotranslation)) {
1935 $tmpproduct = new Product($this->db);
1936 $tmpproduct->fetch($objp->fk_product);
1937 $tmpproduct->getMultiLangs();
1938
1939 $line->multilangs = $tmpproduct->multilangs;
1940 }
1941
1942 $this->lines[$i] = $line;
1943
1944 $i++;
1945 }
1946
1947 $this->db->free($result);
1948
1949 return $num;
1950 } else {
1951 $this->error = $this->db->lasterror();
1952 return -3;
1953 }
1954 }
1955
1963 public function valid($user, $notrigger = 0)
1964 {
1965 global $conf;
1966
1967 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1968
1969 $error = 0;
1970
1971 // Protection
1972 if ($this->statut == self::STATUS_VALIDATED) {
1973 dol_syslog(get_class($this)."::valid action abandoned: already validated", LOG_WARNING);
1974 return 0;
1975 }
1976
1977 if (!((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('propal', 'creer'))
1978 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('propal', 'propal_advance', 'validate')))) {
1979 $this->error = 'ErrorPermissionDenied';
1980 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
1981 return -1;
1982 }
1983
1984 $now = dol_now();
1985
1986 $this->db->begin();
1987
1988 // Numbering module definition
1989 $soc = new Societe($this->db);
1990 $soc->fetch($this->socid);
1991
1992 // Define new ref
1993 if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
1994 $num = $this->getNextNumRef($soc);
1995 } else {
1996 $num = $this->ref;
1997 }
1998 $this->newref = dol_sanitizeFileName($num);
1999
2000 $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2001 $sql .= " SET ref = '".$this->db->escape($num)."',";
2002 $sql .= " fk_statut = ".self::STATUS_VALIDATED.", date_valid='".$this->db->idate($now)."', fk_user_valid=".((int) $user->id);
2003 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = ".self::STATUS_DRAFT;
2004
2005 dol_syslog(get_class($this)."::valid", LOG_DEBUG);
2006 $resql = $this->db->query($sql);
2007 if (!$resql) {
2008 dol_print_error($this->db);
2009 $error++;
2010 }
2011
2012 // Trigger calls
2013 if (!$error && !$notrigger) {
2014 // Call trigger
2015 $result = $this->call_trigger('PROPAL_VALIDATE', $user);
2016 if ($result < 0) {
2017 $error++;
2018 }
2019 // End call triggers
2020 }
2021
2022 if (!$error) {
2023 $this->oldref = $this->ref;
2024
2025 // Rename directory if dir was a temporary ref
2026 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
2027 // Now we rename also files into index
2028 $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)."'";
2029 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'propale/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
2030 $resql = $this->db->query($sql);
2031 if (!$resql) {
2032 $error++;
2033 $this->error = $this->db->lasterror();
2034 }
2035 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'propale/".$this->db->escape($this->newref)."'";
2036 $sql .= " WHERE filepath = 'propale/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
2037 $resql = $this->db->query($sql);
2038 if (!$resql) {
2039 $error++;
2040 $this->error = $this->db->lasterror();
2041 }
2042
2043 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
2044 $oldref = dol_sanitizeFileName($this->ref);
2045 $newref = dol_sanitizeFileName($num);
2046 $dirsource = $conf->propal->multidir_output[$this->entity].'/'.$oldref;
2047 $dirdest = $conf->propal->multidir_output[$this->entity].'/'.$newref;
2048 if (!$error && file_exists($dirsource)) {
2049 dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
2050 if (@rename($dirsource, $dirdest)) {
2051 dol_syslog("Rename ok");
2052 // Rename docs starting with $oldref with $newref
2053 $listoffiles = dol_dir_list($dirdest, 'files', 1, '^'.preg_quote($oldref, '/'));
2054 foreach ($listoffiles as $fileentry) {
2055 $dirsource = $fileentry['name'];
2056 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
2057 $dirsource = $fileentry['path'].'/'.$dirsource;
2058 $dirdest = $fileentry['path'].'/'.$dirdest;
2059 @rename($dirsource, $dirdest);
2060 }
2061 }
2062 }
2063 }
2064
2065 $this->ref = $num;
2067 $this->user_validation_id = $user->id;
2068 $this->datev = $now;
2069 $this->date_validation = $now;
2070
2071 $this->db->commit();
2072 return 1;
2073 } else {
2074 $this->db->rollback();
2075 return -1;
2076 }
2077 }
2078
2079
2080 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2089 public function set_date($user, $date, $notrigger = 0)
2090 {
2091 // phpcs:enable
2092 if (empty($date)) {
2093 $this->error = 'ErrorBadParameter';
2094 dol_syslog(get_class($this)."::set_date ".$this->error, LOG_ERR);
2095 return -1;
2096 }
2097
2098 if ($user->hasRight('propal', 'creer')) {
2099 $error = 0;
2100
2101 $this->db->begin();
2102
2103 $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET datep = '".$this->db->idate($date)."'";
2104 $sql .= " WHERE rowid = ".((int) $this->id);
2105
2106 dol_syslog(__METHOD__, LOG_DEBUG);
2107 $resql = $this->db->query($sql);
2108 if (!$resql) {
2109 $this->errors[] = $this->db->error();
2110 $error++;
2111 }
2112
2113 if (!$error) {
2114 $this->oldcopy = clone $this;
2115 $this->date = $date;
2116 $this->datep = $date; // deprecated
2117 }
2118
2119 if (!$notrigger && empty($error)) {
2120 // Call trigger
2121 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2122 if ($result < 0) {
2123 $error++;
2124 }
2125 // End call triggers
2126 }
2127
2128 if (!$error) {
2129 $this->db->commit();
2130 return 1;
2131 } else {
2132 foreach ($this->errors as $errmsg) {
2133 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2134 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2135 }
2136 $this->db->rollback();
2137 return -1 * $error;
2138 }
2139 }
2140
2141 return -1;
2142 }
2143
2144 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2153 public function set_echeance($user, $date_end_validity, $notrigger = 0)
2154 {
2155 // phpcs:enable
2156 if ($user->hasRight('propal', 'creer')) {
2157 $error = 0;
2158
2159 $this->db->begin();
2160
2161 $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET fin_validite = ".($date_end_validity != '' ? "'".$this->db->idate($date_end_validity)."'" : 'null');
2162 $sql .= " WHERE rowid = ".((int) $this->id);
2163
2164 dol_syslog(__METHOD__, LOG_DEBUG);
2165
2166 $resql = $this->db->query($sql);
2167 if (!$resql) {
2168 $this->errors[] = $this->db->error();
2169 $error++;
2170 }
2171
2172
2173 if (!$error) {
2174 $this->oldcopy = clone $this;
2175 $this->fin_validite = $date_end_validity;
2176 }
2177
2178 if (!$notrigger && empty($error)) {
2179 // Call trigger
2180 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2181 if ($result < 0) {
2182 $error++;
2183 }
2184 // End call triggers
2185 }
2186
2187 if (!$error) {
2188 $this->db->commit();
2189 return 1;
2190 } else {
2191 foreach ($this->errors as $errmsg) {
2192 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2193 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2194 }
2195 $this->db->rollback();
2196 return -1 * $error;
2197 }
2198 }
2199
2200 return -1;
2201 }
2202
2203 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2213 public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2214 {
2215 // phpcs:enable
2216 return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2217 }
2218
2227 public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2228 {
2229 if ($user->hasRight('propal', 'creer')) {
2230 $error = 0;
2231
2232 $this->db->begin();
2233
2234 $sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
2235 $sql .= " SET date_livraison = ".($delivery_date != '' ? "'".$this->db->idate($delivery_date)."'" : 'null');
2236 $sql .= " WHERE rowid = ".((int) $this->id);
2237
2238 dol_syslog(__METHOD__, LOG_DEBUG);
2239 $resql = $this->db->query($sql);
2240 if (!$resql) {
2241 $this->errors[] = $this->db->error();
2242 $error++;
2243 }
2244
2245 if (!$error) {
2246 $this->oldcopy = clone $this;
2247 $this->delivery_date = $delivery_date;
2248 }
2249
2250 if (!$notrigger && empty($error)) {
2251 // Call trigger
2252 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2253 if ($result < 0) {
2254 $error++;
2255 }
2256 // End call triggers
2257 }
2258
2259 if (!$error) {
2260 $this->db->commit();
2261 return 1;
2262 } else {
2263 foreach ($this->errors as $errmsg) {
2264 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2265 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2266 }
2267 $this->db->rollback();
2268 return -1 * $error;
2269 }
2270 }
2271
2272 return -1;
2273 }
2274
2275 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2284 public function set_availability($user, $id, $notrigger = 0)
2285 {
2286 // phpcs:enable
2287 if ($user->hasRight('propal', 'creer') && $this->statut >= self::STATUS_DRAFT) {
2288 $error = 0;
2289
2290 $this->db->begin();
2291
2292 $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2293 $sql .= " SET fk_availability = ".((int) $id);
2294 $sql .= " WHERE rowid = ".((int) $this->id);
2295
2296 dol_syslog(__METHOD__.' availability('.$id.')', LOG_DEBUG);
2297 $resql = $this->db->query($sql);
2298 if (!$resql) {
2299 $this->errors[] = $this->db->error();
2300 $error++;
2301 }
2302
2303 if (!$error) {
2304 $this->oldcopy = clone $this;
2305 $this->fk_availability = $id;
2306 $this->availability_id = $id;
2307 }
2308
2309 if (!$notrigger && empty($error)) {
2310 // Call trigger
2311 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2312 if ($result < 0) {
2313 $error++;
2314 }
2315 // End call triggers
2316 }
2317
2318 if (!$error) {
2319 $this->db->commit();
2320 return 1;
2321 } else {
2322 foreach ($this->errors as $errmsg) {
2323 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2324 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2325 }
2326 $this->db->rollback();
2327 return -1 * $error;
2328 }
2329 } else {
2330 $error_str = 'Propal status do not meet requirement '.$this->statut;
2331 dol_syslog(__METHOD__.$error_str, LOG_ERR);
2332 $this->error = $error_str;
2333 $this->errors[] = $this->error;
2334 return -2;
2335 }
2336 }
2337
2338 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2347 public function set_demand_reason($user, $id, $notrigger = 0)
2348 {
2349 // phpcs:enable
2350 if ($user->hasRight('propal', 'creer') && $this->statut >= self::STATUS_DRAFT) {
2351 $error = 0;
2352
2353 $this->db->begin();
2354
2355 $sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
2356 $sql .= " SET fk_input_reason = ".((int) $id);
2357 $sql .= " WHERE rowid = ".((int) $this->id);
2358
2359 dol_syslog(__METHOD__, LOG_DEBUG);
2360 $resql = $this->db->query($sql);
2361 if (!$resql) {
2362 $this->errors[] = $this->db->error();
2363 $error++;
2364 }
2365
2366
2367 if (!$error) {
2368 $this->oldcopy = clone $this;
2369
2370 $this->demand_reason_id = $id;
2371 }
2372
2373
2374 if (!$notrigger && empty($error)) {
2375 // Call trigger
2376 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2377 if ($result < 0) {
2378 $error++;
2379 }
2380 // End call triggers
2381 }
2382
2383 if (!$error) {
2384 $this->db->commit();
2385 return 1;
2386 } else {
2387 foreach ($this->errors as $errmsg) {
2388 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2389 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2390 }
2391 $this->db->rollback();
2392 return -1 * $error;
2393 }
2394 } else {
2395 $error_str = 'Propal status do not meet requirement '.$this->statut;
2396 dol_syslog(__METHOD__.$error_str, LOG_ERR);
2397 $this->error = $error_str;
2398 $this->errors[] = $this->error;
2399 return -2;
2400 }
2401 }
2402
2403 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2412 public function set_ref_client($user, $ref_client, $notrigger = 0)
2413 {
2414 // phpcs:enable
2415 if ($user->hasRight('propal', 'creer')) {
2416 $error = 0;
2417
2418 $this->db->begin();
2419
2420 $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET ref_client = ".(empty($ref_client) ? 'NULL' : "'".$this->db->escape($ref_client)."'");
2421 $sql .= " WHERE rowid = ".((int) $this->id);
2422
2423 dol_syslog(__METHOD__.' $this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
2424 $resql = $this->db->query($sql);
2425 if (!$resql) {
2426 $this->errors[] = $this->db->error();
2427 $error++;
2428 }
2429
2430 if (!$error) {
2431 $this->oldcopy = clone $this;
2432 $this->ref_client = $ref_client;
2433 }
2434
2435 if (!$notrigger && empty($error)) {
2436 // Call trigger
2437 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2438 if ($result < 0) {
2439 $error++;
2440 }
2441 // End call triggers
2442 }
2443
2444 if (!$error) {
2445 $this->db->commit();
2446 return 1;
2447 } else {
2448 foreach ($this->errors as $errmsg) {
2449 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2450 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2451 }
2452 $this->db->rollback();
2453 return -1 * $error;
2454 }
2455 }
2456
2457 return -1;
2458 }
2459
2460
2470 public function reopen($user, $status, $note = '', $notrigger = 0)
2471 {
2472 $error = 0;
2473
2474 $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2475 $sql .= " SET fk_statut = ".((int) $status).",";
2476 if (!empty($note)) {
2477 $sql .= " note_private = '".$this->db->escape($note)."',";
2478 }
2479 $sql .= " date_cloture=NULL, fk_user_cloture=NULL";
2480 $sql .= " WHERE rowid = ".((int) $this->id);
2481
2482 $this->db->begin();
2483
2484 dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
2485 $resql = $this->db->query($sql);
2486 if (!$resql) {
2487 $error++;
2488 $this->errors[] = "Error ".$this->db->lasterror();
2489 }
2490 if (!$error) {
2491 if (!$notrigger) {
2492 // Call trigger
2493 $result = $this->call_trigger('PROPAL_REOPEN', $user);
2494 if ($result < 0) {
2495 $error++;
2496 }
2497 // End call triggers
2498 }
2499 }
2500
2501 // Commit or rollback
2502 if ($error) {
2503 if (!empty($this->errors)) {
2504 foreach ($this->errors as $errmsg) {
2505 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
2506 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2507 }
2508 }
2509 $this->db->rollback();
2510 return -1 * $error;
2511 } else {
2512 $this->statut = $status;
2513 $this->status = $status;
2514
2515 $this->db->commit();
2516 return 1;
2517 }
2518 }
2519
2529 public function closeProposal($user, $status, $note = '', $notrigger = 0)
2530 {
2531 global $langs,$conf;
2532
2533 $error = 0;
2534 $now = dol_now();
2535
2536 $this->db->begin();
2537
2538 $newprivatenote = dol_concatdesc($this->note_private, $note);
2539
2540 if (!getDolGlobalString('PROPALE_KEEP_OLD_SIGNATURE_INFO')) {
2541 $date_signature = $now;
2542 $fk_user_signature = $user->id;
2543 } else {
2544 $this->info($this->id);
2545 if (!isset($this->date_signature) || $this->date_signature == '') {
2546 $date_signature = $now;
2547 $fk_user_signature = $user->id;
2548 } else {
2549 $date_signature = $this->date_signature;
2550 $fk_user_signature = $this->user_signature->id;
2551 }
2552 }
2553
2554 $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2555 $sql .= " SET fk_statut = ".((int) $status).", note_private = '".$this->db->escape($newprivatenote)."'";
2556 if ($status == self::STATUS_SIGNED) {
2557 $sql .= ", date_signature='".$this->db->idate($now)."', fk_user_signature = ".($fk_user_signature);
2558 }
2559 $sql .= " WHERE rowid = ".((int) $this->id);
2560
2561 $resql = $this->db->query($sql);
2562 if ($resql) {
2563 // Status self::STATUS_REFUSED by default
2564 $modelpdf = getDolGlobalString('PROPALE_ADDON_PDF_ODT_CLOSED', $this->model_pdf);
2565 $trigger_name = 'PROPAL_CLOSE_REFUSED'; // used later in call_trigger()
2566
2567 if ($status == self::STATUS_SIGNED) { // Status self::STATUS_SIGNED
2568 $trigger_name = 'PROPAL_CLOSE_SIGNED'; // used later in call_trigger()
2569 $modelpdf = getDolGlobalString('PROPALE_ADDON_PDF_ODT_TOBILL') ? $conf->global->PROPALE_ADDON_PDF_ODT_TOBILL : $this->model_pdf;
2570
2571 // The connected company is classified as a client
2572 $soc = new Societe($this->db);
2573 $soc->id = $this->socid;
2574 $result = $soc->setAsCustomer();
2575
2576 if ($result < 0) {
2577 $this->error = $this->db->lasterror();
2578 $this->db->rollback();
2579 return -2;
2580 }
2581 }
2582
2583 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE') && !getDolGlobalInt('PROPAL_DISABLE_AUTOUPDATE_ON_CLOSE')) {
2584 // Define output language
2585 $outputlangs = $langs;
2586 if (getDolGlobalInt('MAIN_MULTILANGS')) {
2587 $outputlangs = new Translate("", $conf);
2588 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
2589 $outputlangs->setDefaultLang($newlang);
2590 }
2591
2592 // PDF
2593 $hidedetails = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 1 : 0);
2594 $hidedesc = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 1 : 0);
2595 $hideref = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 1 : 0);
2596
2597 //$ret=$object->fetch($id); // Reload to get new records
2598 $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
2599 }
2600
2601 if (!$error) {
2602 $this->oldcopy = clone $this;
2603 $this->statut = $status;
2604 $this->status = $status;
2605 $this->date_signature = $date_signature;
2606 $this->note_private = $newprivatenote;
2607 }
2608
2609 if (!$notrigger && empty($error)) {
2610 // Call trigger
2611 $result = $this->call_trigger($trigger_name, $user);
2612 if ($result < 0) {
2613 $error++;
2614 }
2615 // End call triggers
2616 }
2617
2618 if (!$error) {
2619 $this->db->commit();
2620 return 1;
2621 } else {
2622 $this->statut = $this->oldcopy->statut;
2623 $this->status = $this->oldcopy->statut;
2624 $this->date_signature = $this->oldcopy->date_signature;
2625 $this->note_private = $this->oldcopy->note_private;
2626
2627 $this->db->rollback();
2628 return -1;
2629 }
2630 } else {
2631 $this->error = $this->db->lasterror();
2632 $this->db->rollback();
2633 return -1;
2634 }
2635 }
2636
2645 public function classifyBilled(User $user, $notrigger = 0, $note = '')
2646 {
2647 global $conf, $langs;
2648
2649 $error = 0;
2650
2651 $now = dol_now();
2652 $num = 0;
2653
2654 $triggerName = 'PROPAL_CLASSIFY_BILLED';
2655
2656 $this->db->begin();
2657
2658 $newprivatenote = dol_concatdesc($this->note_private, $note);
2659
2660 $sql = 'UPDATE '.MAIN_DB_PREFIX.'propal SET fk_statut = '.self::STATUS_BILLED.", ";
2661 $sql .= " note_private = '".$this->db->escape($newprivatenote)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".((int) $user->id);
2662 $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.((int) self::STATUS_SIGNED);
2663
2664 dol_syslog(__METHOD__, LOG_DEBUG);
2665 $resql = $this->db->query($sql);
2666 if (!$resql) {
2667 $this->errors[] = $this->db->error();
2668 $error++;
2669 } else {
2670 $num = $this->db->affected_rows($resql);
2671 }
2672
2673 if (!$error) {
2674 $modelpdf = getDolGlobalString('PROPALE_ADDON_PDF_ODT_CLOSED', $this->model_pdf);
2675
2676 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
2677 // Define output language
2678 $outputlangs = $langs;
2679 if (getDolGlobalInt('MAIN_MULTILANGS')) {
2680 $outputlangs = new Translate("", $conf);
2681 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
2682 $outputlangs->setDefaultLang($newlang);
2683 }
2684
2685 // PDF
2686 $hidedetails = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 1 : 0);
2687 $hidedesc = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 1 : 0);
2688 $hideref = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 1 : 0);
2689
2690 //$ret=$object->fetch($id); // Reload to get new records
2691 $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
2692 }
2693
2694 $this->oldcopy = clone $this;
2695 $this->statut = self::STATUS_BILLED;
2696 $this->date_cloture = $now;
2697 $this->note_private = $newprivatenote;
2698 }
2699
2700 if (!$notrigger && empty($error)) {
2701 // Call trigger
2702 $result = $this->call_trigger($triggerName, $user);
2703 if ($result < 0) {
2704 $error++;
2705 }
2706 // End call triggers
2707 }
2708
2709 if (!$error) {
2710 $this->db->commit();
2711 return $num;
2712 } else {
2713 foreach ($this->errors as $errmsg) {
2714 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2715 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2716 }
2717 $this->db->rollback();
2718 return -1 * $error;
2719 }
2720 }
2721
2728 public function setCancel(User $user)
2729 {
2730 $error = 0;
2731
2732 $this->db->begin();
2733
2734 $sql = "UPDATE ". MAIN_DB_PREFIX . "propal";
2735 $sql .= " SET fk_statut = " . self::STATUS_CANCELED . ",";
2736 $sql .= " fk_user_modif = " . ((int) $user->id);
2737 $sql .= " WHERE rowid = " . ((int) $this->id);
2738
2739 dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
2740 if ($this->db->query($sql)) {
2741 if (!$error) {
2742 // Call trigger
2743 $result = $this->call_trigger('PROPAL_CANCEL', $user);
2744 if ($result < 0) {
2745 $error++;
2746 }
2747 // End call triggers
2748 }
2749
2750 if (!$error) {
2752 $this->db->commit();
2753 return 1;
2754 } else {
2755 foreach ($this->errors as $errmsg) {
2756 dol_syslog(get_class($this)."::cancel ".$errmsg, LOG_ERR);
2757 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2758 }
2759 $this->db->rollback();
2760 return -1;
2761 }
2762 } else {
2763 $this->error = $this->db->error();
2764 $this->db->rollback();
2765 return -1;
2766 }
2767 }
2768
2769 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2777 public function setDraft($user, $notrigger = 0)
2778 {
2779 // phpcs:enable
2780 $error = 0;
2781
2782 // Protection
2783 if ($this->statut <= self::STATUS_DRAFT) {
2784 return 0;
2785 }
2786
2787 dol_syslog(get_class($this)."::setDraft", LOG_DEBUG);
2788
2789 $this->db->begin();
2790
2791 $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2792 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
2793 $sql .= ", online_sign_ip = NULL , online_sign_name = NULL";
2794 $sql .= " WHERE rowid = ".((int) $this->id);
2795
2796 $resql = $this->db->query($sql);
2797 if (!$resql) {
2798 $this->errors[] = $this->db->error();
2799 $error++;
2800 }
2801
2802 if (!$error) {
2803 $this->oldcopy = clone $this;
2804 }
2805
2806 if (!$notrigger && empty($error)) {
2807 // Call trigger
2808 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2809 if ($result < 0) {
2810 $error++;
2811 }
2812 // End call triggers
2813 }
2814
2815 if (!$error) {
2816 $this->statut = self::STATUS_DRAFT;
2817 $this->status = self::STATUS_DRAFT;
2818
2819 $this->db->commit();
2820 return 1;
2821 } else {
2822 foreach ($this->errors as $errmsg) {
2823 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2824 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2825 }
2826 $this->db->rollback();
2827 return -1 * $error;
2828 }
2829 }
2830
2831
2832 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2846 public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datep', $sortorder = 'DESC')
2847 {
2848 // phpcs:enable
2849 global $user;
2850
2851 $ga = array();
2852
2853 $sql = "SELECT s.rowid, s.nom as name, s.client,";
2854 $sql .= " p.rowid as propalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
2855 $sql .= " p.datep as dp, p.fin_validite as datelimite";
2856 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."propal as p, ".MAIN_DB_PREFIX."c_propalst as c";
2857 $sql .= " WHERE p.entity IN (".getEntity('propal').")";
2858 $sql .= " AND p.fk_soc = s.rowid";
2859 $sql .= " AND p.fk_statut = c.id";
2860
2861 // If the internal user must only see his customers, force searching by him
2862 $search_sale = 0;
2863 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2864 $search_sale = $user->id;
2865 }
2866 // Search on sale representative
2867 if ($search_sale && $search_sale != '-1') {
2868 if ($search_sale == -2) {
2869 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
2870 } elseif ($search_sale > 0) {
2871 $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).")";
2872 }
2873 }
2874 // Search on socid
2875 if ($socid) {
2876 $sql .= " AND p.fk_soc = ".((int) $socid);
2877 }
2878 if ($draft) {
2879 $sql .= " AND p.fk_statut = ".((int) self::STATUS_DRAFT);
2880 }
2881 if ($notcurrentuser > 0) {
2882 $sql .= " AND p.fk_user_author <> ".((int) $user->id);
2883 }
2884 $sql .= $this->db->order($sortfield, $sortorder);
2885 $sql .= $this->db->plimit($limit, $offset);
2886
2887 $result = $this->db->query($sql);
2888 if ($result) {
2889 $num = $this->db->num_rows($result);
2890 if ($num) {
2891 $i = 0;
2892 while ($i < $num) {
2893 $obj = $this->db->fetch_object($result);
2894
2895 if ($shortlist == 1) {
2896 $ga[$obj->propalid] = $obj->ref;
2897 } elseif ($shortlist == 2) {
2898 $ga[$obj->propalid] = $obj->ref.' ('.$obj->name.')';
2899 } else {
2900 $ga[$i]['id'] = $obj->propalid;
2901 $ga[$i]['ref'] = $obj->ref;
2902 $ga[$i]['name'] = $obj->name;
2903 }
2904
2905 $i++;
2906 }
2907 }
2908 return $ga;
2909 } else {
2910 dol_print_error($this->db);
2911 return -1;
2912 }
2913 }
2914
2920 public function getInvoiceArrayList()
2921 {
2922 return $this->InvoiceArrayList($this->id);
2923 }
2924
2925 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2932 public function InvoiceArrayList($id)
2933 {
2934 // phpcs:enable
2935 $ga = array();
2936 $linkedInvoices = array();
2937
2938 $this->fetchObjectLinked($id, $this->element);
2939 foreach ($this->linkedObjectsIds as $objecttype => $objectid) {
2940 // Nouveau système du common object renvoi des rowid et non un id linéaire de 1 à n
2941 // On parcourt donc une liste d'objets en tant qu'objet unique
2942 foreach ($objectid as $key => $object) {
2943 // Cas des factures liees directement
2944 if ($objecttype == 'facture') {
2945 $linkedInvoices[] = $object;
2946 } else {
2947 // Cas des factures liees par un autre object (ex: commande)
2948 $this->fetchObjectLinked($object, $objecttype);
2949 foreach ($this->linkedObjectsIds as $subobjecttype => $subobjectid) {
2950 foreach ($subobjectid as $subkey => $subobject) {
2951 if ($subobjecttype == 'facture') {
2952 $linkedInvoices[] = $subobject;
2953 }
2954 }
2955 }
2956 }
2957 }
2958 }
2959
2960 if (count($linkedInvoices) > 0) {
2961 $sql = "SELECT rowid as facid, ref, total_ht as total, datef as df, fk_user_author, fk_statut, paye";
2962 $sql .= " FROM ".MAIN_DB_PREFIX."facture";
2963 $sql .= " WHERE rowid IN (".$this->db->sanitize(implode(',', $linkedInvoices)).")";
2964
2965 dol_syslog(get_class($this)."::InvoiceArrayList", LOG_DEBUG);
2966 $resql = $this->db->query($sql);
2967
2968 if ($resql) {
2969 $tab_sqlobj = array();
2970 $nump = $this->db->num_rows($resql);
2971 for ($i = 0; $i < $nump; $i++) {
2972 $sqlobj = $this->db->fetch_object($resql);
2973 $tab_sqlobj[] = $sqlobj;
2974 }
2975 $this->db->free($resql);
2976
2977 $nump = count($tab_sqlobj);
2978
2979 if ($nump) {
2980 $i = 0;
2981 while ($i < $nump) {
2982 $obj = array_shift($tab_sqlobj);
2983
2984 $ga[$i] = $obj;
2985
2986 $i++;
2987 }
2988 }
2989 return $ga;
2990 } else {
2991 return -1;
2992 }
2993 } else {
2994 return $ga;
2995 }
2996 }
2997
3005 public function delete($user, $notrigger = 0)
3006 {
3007 global $conf;
3008 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3009
3010 $error = 0;
3011
3012 $this->db->begin();
3013
3014 if (!$notrigger) {
3015 // Call trigger
3016 $result = $this->call_trigger('PROPAL_DELETE', $user);
3017 if ($result < 0) {
3018 $error++;
3019 }
3020 // End call triggers
3021 }
3022
3023 // Delete extrafields of lines and lines
3024 if (!$error && !empty($this->table_element_line)) {
3025 $tabletodelete = $this->table_element_line;
3026 $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).")";
3027 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id);
3028 if (!$this->db->query($sqlef) || !$this->db->query($sql)) {
3029 $error++;
3030 $this->error = $this->db->lasterror();
3031 $this->errors[] = $this->error;
3032 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3033 }
3034 }
3035
3036 if (!$error) {
3037 // Delete linked object
3038 $res = $this->deleteObjectLinked();
3039 if ($res < 0) {
3040 $error++;
3041 }
3042 }
3043
3044 if (!$error) {
3045 // Delete linked contacts
3046 $res = $this->delete_linked_contact();
3047 if ($res < 0) {
3048 $error++;
3049 }
3050 }
3051
3052 // Removed extrafields of object
3053 if (!$error) {
3054 $result = $this->deleteExtraFields();
3055 if ($result < 0) {
3056 $error++;
3057 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3058 }
3059 }
3060
3061 // Delete main record
3062 if (!$error) {
3063 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element." WHERE rowid = ".((int) $this->id);
3064 $res = $this->db->query($sql);
3065 if (!$res) {
3066 $error++;
3067 $this->error = $this->db->lasterror();
3068 $this->errors[] = $this->error;
3069 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3070 }
3071 }
3072
3073 // Delete record into ECM index and physically
3074 if (!$error) {
3075 $res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
3076 $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
3077 if (!$res) {
3078 $error++;
3079 }
3080 }
3081
3082 if (!$error) {
3083 // We remove directory
3084 $ref = dol_sanitizeFileName($this->ref);
3085 if ($conf->propal->multidir_output[$this->entity] && !empty($this->ref)) {
3086 $dir = $conf->propal->multidir_output[$this->entity]."/".$ref;
3087 $file = $dir."/".$ref.".pdf";
3088 if (file_exists($file)) {
3089 dol_delete_preview($this);
3090
3091 if (!dol_delete_file($file, 0, 0, 0, $this)) {
3092 $this->error = 'ErrorFailToDeleteFile';
3093 $this->errors[] = $this->error;
3094 $this->db->rollback();
3095 return 0;
3096 }
3097 }
3098 if (file_exists($dir)) {
3099 $res = @dol_delete_dir_recursive($dir); // delete files physically + into ecm tables
3100 if (!$res) {
3101 $this->error = 'ErrorFailToDeleteDir';
3102 $this->errors[] = $this->error;
3103 $this->db->rollback();
3104 return 0;
3105 }
3106 }
3107 }
3108 }
3109
3110 if (!$error) {
3111 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
3112 $this->db->commit();
3113 return 1;
3114 } else {
3115 $this->db->rollback();
3116 return -1;
3117 }
3118 }
3119
3128 public function availability($availability_id, $notrigger = 0)
3129 {
3130 global $user;
3131
3132 if ($this->statut >= self::STATUS_DRAFT) {
3133 $error = 0;
3134
3135 $this->db->begin();
3136
3137 $sql = 'UPDATE '.MAIN_DB_PREFIX.'propal';
3138 $sql .= ' SET fk_availability = '.((int) $availability_id);
3139 $sql .= ' WHERE rowid='.((int) $this->id);
3140
3141 dol_syslog(__METHOD__.' availability('.$availability_id.')', LOG_DEBUG);
3142 $resql = $this->db->query($sql);
3143 if (!$resql) {
3144 $this->errors[] = $this->db->error();
3145 $error++;
3146 }
3147
3148 if (!$error) {
3149 $this->oldcopy = clone $this;
3150 $this->availability_id = $availability_id;
3151 }
3152
3153 if (!$notrigger && empty($error)) {
3154 // Call trigger
3155 $result = $this->call_trigger('PROPAL_MODIFY', $user);
3156 if ($result < 0) {
3157 $error++;
3158 }
3159 // End call triggers
3160 }
3161
3162 if (!$error) {
3163 $this->db->commit();
3164 return 1;
3165 } else {
3166 foreach ($this->errors as $errmsg) {
3167 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
3168 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3169 }
3170 $this->db->rollback();
3171 return -1 * $error;
3172 }
3173 } else {
3174 $error_str = 'Propal status do not meet requirement '.$this->statut;
3175 dol_syslog(__METHOD__.$error_str, LOG_ERR);
3176 $this->error = $error_str;
3177 $this->errors[] = $this->error;
3178 return -2;
3179 }
3180 }
3181
3182 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3191 public function demand_reason($demand_reason_id, $notrigger = 0)
3192 {
3193 // phpcs:enable
3194 global $user;
3195
3196 if ($this->status >= self::STATUS_DRAFT) {
3197 $error = 0;
3198
3199 $this->db->begin();
3200
3201 $sql = 'UPDATE '.MAIN_DB_PREFIX.'propal';
3202 $sql .= ' SET fk_input_reason = '.((int) $demand_reason_id);
3203 $sql .= ' WHERE rowid='.((int) $this->id);
3204
3205 dol_syslog(__METHOD__.' demand_reason('.$demand_reason_id.')', LOG_DEBUG);
3206 $resql = $this->db->query($sql);
3207 if (!$resql) {
3208 $this->errors[] = $this->db->error();
3209 $error++;
3210 }
3211
3212 if (!$error) {
3213 $this->oldcopy = clone $this;
3214 $this->demand_reason_id = $demand_reason_id;
3215 }
3216
3217 if (!$notrigger && empty($error)) {
3218 // Call trigger
3219 $result = $this->call_trigger('PROPAL_MODIFY', $user);
3220 if ($result < 0) {
3221 $error++;
3222 }
3223 // End call triggers
3224 }
3225
3226 if (!$error) {
3227 $this->db->commit();
3228 return 1;
3229 } else {
3230 foreach ($this->errors as $errmsg) {
3231 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
3232 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3233 }
3234 $this->db->rollback();
3235 return -1 * $error;
3236 }
3237 } else {
3238 $error_str = 'Propal status do not meet requirement '.$this->statut;
3239 dol_syslog(__METHOD__.$error_str, LOG_ERR);
3240 $this->error = $error_str;
3241 $this->errors[] = $this->error;
3242 return -2;
3243 }
3244 }
3245
3246
3253 public function info($id)
3254 {
3255 $sql = "SELECT c.rowid, ";
3256 $sql .= " c.datec, c.date_valid as datev, c.date_signature, c.date_cloture,";
3257 $sql .= " c.fk_user_author, c.fk_user_valid, c.fk_user_signature, c.fk_user_cloture";
3258 $sql .= " FROM ".MAIN_DB_PREFIX."propal as c";
3259 $sql .= " WHERE c.rowid = ".((int) $id);
3260
3261 $result = $this->db->query($sql);
3262
3263 if ($result) {
3264 if ($this->db->num_rows($result)) {
3265 $obj = $this->db->fetch_object($result);
3266
3267 $this->id = $obj->rowid;
3268
3269 $this->date_creation = $this->db->jdate($obj->datec);
3270 $this->date_validation = $this->db->jdate($obj->datev);
3271 $this->date_signature = $this->db->jdate($obj->date_signature);
3272 $this->date_cloture = $this->db->jdate($obj->date_cloture);
3273
3274 $this->user_creation_id = $obj->fk_user_author;
3275 $this->user_validation_id = $obj->fk_user_valid;
3276
3277 if ($obj->fk_user_signature) {
3278 $user_signature = new User($this->db);
3279 $user_signature->fetch($obj->fk_user_signature);
3280 $this->user_signature = $user_signature;
3281 }
3282
3283 $this->user_closing_id = $obj->fk_user_cloture;
3284 }
3285 $this->db->free($result);
3286 } else {
3287 dol_print_error($this->db);
3288 }
3289 }
3290
3291
3298 public function getLibStatut($mode = 0)
3299 {
3300 return $this->LibStatut($this->statut, $mode);
3301 }
3302
3303 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3311 public function LibStatut($status, $mode = 1)
3312 {
3313 // phpcs:enable
3314 global $hookmanager;
3315
3316 // Init/load array of translation of status
3317 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
3318 global $langs;
3319 $langs->load("propal");
3320 $this->labelStatus[-1] = $langs->transnoentitiesnoconv("PropalStatusCanceled");
3321 $this->labelStatus[0] = $langs->transnoentitiesnoconv("PropalStatusDraft");
3322 $this->labelStatus[1] = $langs->transnoentitiesnoconv("PropalStatusValidated");
3323 $this->labelStatus[2] = $langs->transnoentitiesnoconv("PropalStatusSigned");
3324 $this->labelStatus[3] = $langs->transnoentitiesnoconv("PropalStatusNotSigned");
3325 $this->labelStatus[4] = $langs->transnoentitiesnoconv("PropalStatusBilled");
3326 $this->labelStatusShort[-1] = $langs->transnoentitiesnoconv("PropalStatusCanceledShort");
3327 $this->labelStatusShort[0] = $langs->transnoentitiesnoconv("PropalStatusDraftShort");
3328 $this->labelStatusShort[1] = $langs->transnoentitiesnoconv("PropalStatusValidatedShort");
3329 $this->labelStatusShort[2] = $langs->transnoentitiesnoconv("PropalStatusSignedShort");
3330 $this->labelStatusShort[3] = $langs->transnoentitiesnoconv("PropalStatusNotSignedShort");
3331 $this->labelStatusShort[4] = $langs->transnoentitiesnoconv("PropalStatusBilledShort");
3332 }
3333
3334 $statusType = '';
3335 if ($status == self::STATUS_CANCELED) {
3336 $statusType = 'status9';
3337 } elseif ($status == self::STATUS_DRAFT) {
3338 $statusType = 'status0';
3339 } elseif ($status == self::STATUS_VALIDATED) {
3340 $statusType = 'status1';
3341 } elseif ($status == self::STATUS_SIGNED) {
3342 $statusType = 'status4';
3343 } elseif ($status == self::STATUS_NOTSIGNED) {
3344 $statusType = 'status9';
3345 } elseif ($status == self::STATUS_BILLED) {
3346 $statusType = 'status6';
3347 }
3348
3349 $parameters = array('status' => $status, 'mode' => $mode);
3350 $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
3351
3352 if ($reshook > 0) {
3353 return $hookmanager->resPrint;
3354 }
3355
3356 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
3357 }
3358
3359
3360 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3368 public function load_board($user, $mode)
3369 {
3370 // phpcs:enable
3371 global $conf, $langs;
3372
3373 $clause = " WHERE";
3374
3375 $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.fin_validite as datefin, p.total_ht";
3376 $sql .= " FROM ".MAIN_DB_PREFIX."propal as p";
3377 $sql .= $clause." p.entity IN (".getEntity('propal').")";
3378 if ($mode == 'opened') {
3379 $sql .= " AND p.fk_statut = ".self::STATUS_VALIDATED;
3380 }
3381 if ($mode == 'signed') {
3382 $sql .= " AND p.fk_statut = ".self::STATUS_SIGNED;
3383 }
3384 // If the internal user must only see his customers, force searching by him
3385 $search_sale = 0;
3386 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
3387 $search_sale = $user->id;
3388 }
3389 // Search on sale representative
3390 if ($search_sale && $search_sale != '-1') {
3391 if ($search_sale == -2) {
3392 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
3393 } elseif ($search_sale > 0) {
3394 $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).")";
3395 }
3396 }
3397
3398 $resql = $this->db->query($sql);
3399 if ($resql) {
3400 $langs->load("propal");
3401 $now = dol_now();
3402
3403 $delay_warning = 0;
3404 $status = 0;
3405 $label = $labelShort = '';
3406 if ($mode == 'opened') {
3407 $delay_warning = $conf->propal->cloture->warning_delay;
3408 $status = self::STATUS_VALIDATED;
3409 $label = $langs->transnoentitiesnoconv("PropalsToClose");
3410 $labelShort = $langs->transnoentitiesnoconv("ToAcceptRefuse");
3411 }
3412 if ($mode == 'signed') {
3413 $delay_warning = $conf->propal->facturation->warning_delay;
3414 $status = self::STATUS_SIGNED;
3415 $label = $langs->trans("PropalsToBill"); // We set here bill but may be billed or ordered
3416 $labelShort = $langs->trans("ToBill");
3417 }
3418
3419 $response = new WorkboardResponse();
3420 $response->warning_delay = $delay_warning / 60 / 60 / 24;
3421 $response->label = $label;
3422 $response->labelShort = $labelShort;
3423 $response->url = DOL_URL_ROOT.'/comm/propal/list.php?search_status='.$status.'&mainmenu=commercial&leftmenu=propals';
3424 $response->url_late = DOL_URL_ROOT.'/comm/propal/list.php?search_option=late&mainmenu=commercial&leftmenu=propals&sortfield=p.datep&sortorder=asc';
3425 $response->img = img_object('', "propal");
3426
3427 // This assignment in condition is not a bug. It allows walking the results.
3428 while ($obj = $this->db->fetch_object($resql)) {
3429 $response->nbtodo++;
3430 $response->total += $obj->total_ht;
3431
3432 if ($mode == 'opened') {
3433 $datelimit = $this->db->jdate($obj->datefin);
3434 if ($datelimit < ($now - $delay_warning)) {
3435 $response->nbtodolate++;
3436 }
3437 }
3438 // TODO Definir regle des propales a facturer en retard
3439 // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
3440 }
3441
3442 return $response;
3443 } else {
3444 $this->error = $this->db->error();
3445 return -1;
3446 }
3447 }
3448
3449
3457 public function initAsSpecimen()
3458 {
3459 global $conf, $langs;
3460
3461 // Load array of products prodids
3462 $num_prods = 0;
3463 $prodids = array();
3464 $sql = "SELECT rowid";
3465 $sql .= " FROM ".MAIN_DB_PREFIX."product";
3466 $sql .= " WHERE entity IN (".getEntity('product').")";
3467 $sql .= $this->db->plimit(100);
3468
3469 $resql = $this->db->query($sql);
3470 if ($resql) {
3471 $num_prods = $this->db->num_rows($resql);
3472 $i = 0;
3473 while ($i < $num_prods) {
3474 $i++;
3475 $row = $this->db->fetch_row($resql);
3476 $prodids[$i] = $row[0];
3477 }
3478 }
3479
3480 // Initialise parameters
3481 $this->id = 0;
3482 $this->ref = 'SPECIMEN';
3483 $this->ref_client = 'NEMICEPS';
3484 $this->specimen = 1;
3485 $this->socid = 1;
3486 $this->date = time();
3487 $this->fin_validite = $this->date + 3600 * 24 * 30;
3488 $this->cond_reglement_id = 1;
3489 $this->cond_reglement_code = 'RECEP';
3490 $this->mode_reglement_id = 7;
3491 $this->mode_reglement_code = 'CHQ';
3492 $this->availability_id = 1;
3493 $this->availability_code = 'AV_NOW';
3494 $this->demand_reason_id = 1;
3495 $this->demand_reason_code = 'SRC_00';
3496 $this->note_public = 'This is a comment (public)';
3497 $this->note_private = 'This is a comment (private)';
3498
3499 $this->multicurrency_tx = 1;
3500 $this->multicurrency_code = $conf->currency;
3501
3502 // Lines
3503 $nbp = 5;
3504 $xnbp = 0;
3505 while ($xnbp < $nbp) {
3506 $line = new PropaleLigne($this->db);
3507 $line->desc = $langs->trans("Description")." ".$xnbp;
3508 $line->qty = 1;
3509 $line->subprice = 100;
3510 $line->price = 100;
3511 $line->tva_tx = 20;
3512 $line->localtax1_tx = 0;
3513 $line->localtax2_tx = 0;
3514 if ($xnbp == 2) {
3515 $line->total_ht = 50;
3516 $line->total_ttc = 60;
3517 $line->total_tva = 10;
3518 $line->remise_percent = 50;
3519 } else {
3520 $line->total_ht = 100;
3521 $line->total_ttc = 120;
3522 $line->total_tva = 20;
3523 $line->remise_percent = 00;
3524 }
3525
3526 if ($num_prods > 0) {
3527 $prodid = mt_rand(1, $num_prods);
3528 $line->fk_product = $prodids[$prodid];
3529 $line->product_ref = 'SPECIMEN';
3530 }
3531
3532 $this->lines[$xnbp] = $line;
3533
3534 $this->total_ht += $line->total_ht;
3535 $this->total_tva += $line->total_tva;
3536 $this->total_ttc += $line->total_ttc;
3537
3538 $xnbp++;
3539 }
3540
3541 return 1;
3542 }
3543
3549 public function loadStateBoard()
3550 {
3551 global $user;
3552
3553 $this->nb = array();
3554 $clause = "WHERE";
3555
3556 $sql = "SELECT count(p.rowid) as nb";
3557 $sql .= " FROM ".MAIN_DB_PREFIX."propal as p";
3558 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
3559 $sql .= " ".$clause." p.entity IN (".getEntity('propal').")";
3560
3561 // If the internal user must only see his customers, force searching by him
3562 $search_sale = 0;
3563 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
3564 $search_sale = $user->id;
3565 }
3566 // Search on sale representative
3567 if ($search_sale && $search_sale != '-1') {
3568 if ($search_sale == -2) {
3569 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
3570 } elseif ($search_sale > 0) {
3571 $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).")";
3572 }
3573 }
3574
3575 $resql = $this->db->query($sql);
3576 if ($resql) {
3577 // This assignment in condition is not a bug. It allows walking the results.
3578 while ($obj = $this->db->fetch_object($resql)) {
3579 $this->nb["proposals"] = $obj->nb;
3580 }
3581 $this->db->free($resql);
3582 return 1;
3583 } else {
3584 dol_print_error($this->db);
3585 $this->error = $this->db->error();
3586 return -1;
3587 }
3588 }
3589
3590
3598 public function getNextNumRef($soc)
3599 {
3600 global $conf, $langs;
3601 $langs->load("propal");
3602
3603 $classname = getDolGlobalString('PROPALE_ADDON');
3604
3605 if (!empty($classname)) {
3606 $mybool = false;
3607
3608 $file = $classname.".php";
3609
3610 // Include file with class
3611 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
3612 foreach ($dirmodels as $reldir) {
3613 $dir = dol_buildpath($reldir."core/modules/propale/");
3614
3615 // Load file with numbering class (if found)
3616 $mybool = ((bool) @include_once $dir.$file) || $mybool;
3617 }
3618
3619 if (!$mybool) {
3620 dol_print_error(null, "Failed to include file ".$file);
3621 return '';
3622 }
3623
3624 $obj = new $classname();
3625 $numref = "";
3626 $numref = $obj->getNextValue($soc, $this);
3627
3628 if ($numref != "") {
3629 return $numref;
3630 } else {
3631 $this->error = $obj->error;
3632 //dol_print_error($db,"Propale::getNextNumRef ".$obj->error);
3633 return "";
3634 }
3635 } else {
3636 $langs->load("errors");
3637 print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("Proposal"));
3638 return "";
3639 }
3640 }
3641
3648 public function getTooltipContentArray($params)
3649 {
3650 global $conf, $langs, $user;
3651
3652 $langs->load('propal');
3653 $datas = [];
3654 $nofetch = !empty($params['nofetch']);
3655
3656 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3657 return ['optimize' => $langs->trans("Proposal")];
3658 }
3659 if ($user->hasRight('propal', 'lire')) {
3660 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("Proposal").'</u>';
3661 if (isset($this->status)) {
3662 $datas['status'] = ' '.$this->getLibStatut(5);
3663 }
3664 if (!empty($this->ref)) {
3665 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
3666 }
3667 if (!$nofetch) {
3668 $langs->load('companies');
3669 if (empty($this->thirdparty)) {
3670 $this->fetch_thirdparty();
3671 }
3672 $datas['customer'] = '<br><b>'.$langs->trans('Customer').':</b> '.$this->thirdparty->getNomUrl(1, '', 0, 1);
3673 }
3674 if (!empty($this->ref_customer)) {
3675 $datas['refcustomer'] = '<br><b>'.$langs->trans('RefCustomer').':</b> '.$this->ref_customer;
3676 }
3677 if (!$nofetch) {
3678 $langs->load('project');
3679 if (is_null($this->project) || (is_object($this->project) && $this->project->isEmpty())) {
3680 $res = $this->fetch_project();
3681 if ($res > 0 && $this->project instanceof Project) {
3682 $datas['project'] = '<br><b>'.$langs->trans('Project').':</b> '.$this->project->getNomUrl(1, '', 0, 1);
3683 }
3684 }
3685 }
3686 if (!empty($this->total_ht)) {
3687 $datas['amountht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3688 }
3689 if (!empty($this->total_tva)) {
3690 $datas['vat'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
3691 }
3692 if (!empty($this->total_ttc)) {
3693 $datas['amountttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3694 }
3695 if (!empty($this->date)) {
3696 $datas['date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
3697 }
3698 if (!empty($this->delivery_date)) {
3699 $datas['deliverydate'] = '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
3700 }
3701 }
3702
3703 return $datas;
3704 }
3705
3717 public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = -1)
3718 {
3719 global $langs, $conf, $user, $hookmanager;
3720
3721 if (!empty($conf->dol_no_mouse_hover)) {
3722 $notooltip = 1; // Force disable tooltips
3723 }
3724
3725 $result = '';
3726 $params = [
3727 'id' => $this->id,
3728 'objecttype' => $this->element,
3729 'option' => $option,
3730 'nofetch' => 1,
3731 ];
3732 $classfortooltip = 'classfortooltip';
3733 $dataparams = '';
3734 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
3735 $classfortooltip = 'classforajaxtooltip';
3736 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
3737 $label = '';
3738 } else {
3739 $label = implode($this->getTooltipContentArray($params));
3740 }
3741
3742 $url = '';
3743 if ($user->hasRight('propal', 'lire')) {
3744 if ($option == '') {
3745 $url = DOL_URL_ROOT.'/comm/propal/card.php?id='.$this->id.$get_params;
3746 } elseif ($option == 'compta') { // deprecated
3747 $url = DOL_URL_ROOT.'/comm/propal/card.php?id='.$this->id.$get_params;
3748 } elseif ($option == 'expedition') {
3749 $url = DOL_URL_ROOT.'/expedition/propal.php?id='.$this->id.$get_params;
3750 } elseif ($option == 'document') {
3751 $url = DOL_URL_ROOT.'/comm/propal/document.php?id='.$this->id.$get_params;
3752 }
3753
3754 if ($option != 'nolink') {
3755 // Add param to save lastsearch_values or not
3756 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
3757 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
3758 $add_save_lastsearch_values = 1;
3759 }
3760 if ($add_save_lastsearch_values) {
3761 $url .= '&save_lastsearch_values=1';
3762 }
3763 }
3764 }
3765
3766 $linkclose = '';
3767 if (empty($notooltip) && $user->hasRight('propal', 'lire')) {
3768 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3769 $label = $langs->trans("Proposal");
3770 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
3771 }
3772 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
3773 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
3774 }
3775
3776 $linkstart = '<a href="'.$url.'"';
3777 $linkstart .= $linkclose.'>';
3778 $linkend = '</a>';
3779
3780 $result .= $linkstart;
3781 if ($withpicto) {
3782 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
3783 }
3784 if ($withpicto != 2) {
3785 $result .= $this->ref;
3786 }
3787 $result .= $linkend;
3788
3789 if ($addlinktonotes >= 0) {
3790 $txttoshow = '';
3791
3792 if ($addlinktonotes == 0) {
3793 if (!empty($this->note_private) || !empty($this->note_public)) {
3794 $txttoshow = $langs->trans('ViewPrivateNote');
3795 }
3796 } elseif ($addlinktonotes == 1) {
3797 if (!empty($this->note_private)) {
3798 $txttoshow .= ($user->socid > 0 ? '' : dol_string_nohtmltag($this->note_private, 1));
3799 }
3800 } elseif ($addlinktonotes == 2) {
3801 if (!empty($this->note_public)) {
3802 $txttoshow .= dol_string_nohtmltag($this->note_public, 1);
3803 }
3804 } elseif ($addlinktonotes == 3) {
3805 if ($user->socid > 0) {
3806 if (!empty($this->note_public)) {
3807 $txttoshow .= dol_string_nohtmltag($this->note_public, 1);
3808 }
3809 } else {
3810 if (!empty($this->note_public)) {
3811 $txttoshow .= dol_string_nohtmltag($this->note_public, 1);
3812 }
3813 if (!empty($this->note_private)) {
3814 if (!empty($txttoshow)) {
3815 $txttoshow .= '<br><br>';
3816 }
3817 $txttoshow .= dol_string_nohtmltag($this->note_private, 1);
3818 }
3819 }
3820 }
3821
3822 if ($txttoshow) {
3823 $result .= ' <span class="note inline-block">';
3824 $result .= '<a href="'.DOL_URL_ROOT.'/comm/propal/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($txttoshow).'">';
3825 $result .= img_picto('', 'note');
3826 $result .= '</a>';
3827 $result .= '</span>';
3828 }
3829 }
3830
3831 global $action;
3832 $hookmanager->initHooks(array($this->element . 'dao'));
3833 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
3834 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3835 if ($reshook > 0) {
3836 $result = $hookmanager->resPrint;
3837 } else {
3838 $result .= $hookmanager->resPrint;
3839 }
3840 return $result;
3841 }
3842
3849 public function getLinesArray($sqlforgedfilters = '')
3850 {
3851 return $this->fetch_lines(0, 0, $sqlforgedfilters);
3852 }
3853
3865 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3866 {
3867 global $conf, $langs;
3868
3869 $langs->load("propale");
3870 $outputlangs->load("products");
3871
3872 if (!dol_strlen($modele)) {
3873 $modele = 'azur';
3874
3875 if ($this->model_pdf) {
3876 $modele = $this->model_pdf;
3877 } elseif (getDolGlobalString('PROPALE_ADDON_PDF')) {
3878 $modele = getDolGlobalString('PROPALE_ADDON_PDF');
3879 }
3880 }
3881
3882 $modelpath = "core/modules/propale/doc/";
3883
3884 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3885 }
3886
3895 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3896 {
3897 $tables = array(
3898 'propal'
3899 );
3900
3901 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3902 }
3903
3912 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3913 {
3914 $tables = array(
3915 'propaldet'
3916 );
3917
3918 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3919 }
3920
3928 public function getKanbanView($option = '', $arraydata = null)
3929 {
3930 global $langs;
3931
3932 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3933
3934 $return = '<div class="box-flex-item box-flex-grow-zero">';
3935 $return .= '<div class="info-box info-box-sm">';
3936 $return .= '<div class="info-box-icon bg-infobox-action">';
3937 $return .= img_picto('', $this->picto);
3938 $return .= '</div>';
3939 $return .= '<div class="info-box-content">';
3940 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
3941 if ($selected >= 0) {
3942 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3943 }
3944 if (!empty($arraydata['projectlink'])) {
3945 $return .= '<span class="info-box-ref"> | '.$arraydata['projectlink'].'</span>';
3946 }
3947 $return .= '<br>';
3948 if (property_exists($this, 'thirdparty') && is_object($this->thirdparty)) {
3949 $return .= '<div class="info-box-ref tdoverflowmax150">'.$this->thirdparty->getNomUrl(1).'</div>';
3950 }
3951 if (property_exists($this, 'total_ht')) {
3952 $return .= '<span class="info-box-label amount" title="'.$langs->trans("AmountHT").'">'.price($this->total_ht).'</span>';
3953 }
3954 if (!empty($arraydata['authorlink'])) {
3955 $return .= ' &nbsp; <span class="info-box-label">'.$arraydata['authorlink'].'</span>';
3956 }
3957 if (method_exists($this, 'getLibStatut')) {
3958 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
3959 }
3960 $return .= '</div>';
3961 $return .= '</div>';
3962 $return .= '</div>';
3963 return $return;
3964 }
3965}
3966
3971{
3975 public $element = 'propaldet';
3976
3980 public $table_element = 'propaldet';
3981
3985 public $parent_element = 'propal';
3986
3990 public $fk_parent_attribute = 'fk_propal';
3991
3992 public $oldline;
3993
3994 // From llx_propaldet
3995 public $fk_propal;
3996 public $fk_parent_line;
3997 public $desc; // Description ligne
3998 public $fk_product; // Id produit predefini
4009 public $product_type = Product::TYPE_PRODUCT;
4010
4011 public $qty;
4012
4013 public $tva_tx;
4014 public $vat_src_code;
4015
4020 public $subprice;
4021 public $remise_percent;
4022 public $fk_remise_except;
4023
4024 public $rang = 0;
4025
4026 public $fk_fournprice;
4027
4032 public $pa_ht;
4033 public $marge_tx;
4034 public $marque_tx;
4035
4042 public $special_code; // Tag for special lines (exclusive tags)
4043
4044 public $info_bits = 0; // Some other info:
4045 // Bit 0: 0 si TVA normal - 1 if TVA NPR
4046 // Bit 1: 0 ligne normal - 1 if line with fixed discount
4047
4048 public $total_ht; // Total HT de la ligne toute quantite et incluant la remise ligne
4049 public $total_tva; // Total TVA de la ligne toute quantite et incluant la remise ligne
4050 public $total_ttc; // Total TTC de la ligne toute quantite et incluant la remise ligne
4051
4056 public $remise;
4061 public $price;
4062
4063 // From llx_product
4068 public $ref;
4073 public $product_ref;
4078 public $libelle;
4083 public $label;
4088 public $product_label;
4093 public $product_desc;
4094
4099 public $product_tobatch;
4100
4105 public $product_barcode;
4106
4107 public $localtax1_tx; // Local tax 1
4108 public $localtax2_tx; // Local tax 2
4109 public $localtax1_type; // Local tax 1 type
4110 public $localtax2_type; // Local tax 2 type
4111 public $total_localtax1; // Line total local tax 1
4112 public $total_localtax2; // Line total local tax 2
4113
4114 public $date_start;
4115 public $date_end;
4116
4117 public $skip_update_total; // Skip update price total for special lines
4118
4119 // Multicurrency
4120 public $fk_multicurrency;
4121 public $multicurrency_code;
4122 public $multicurrency_subprice;
4123 public $multicurrency_total_ht;
4124 public $multicurrency_total_tva;
4125 public $multicurrency_total_ttc;
4126
4127
4133 public function __construct($db)
4134 {
4135 $this->db = $db;
4136 }
4137
4144 public function fetch($rowid)
4145 {
4146 $sql = 'SELECT pd.rowid, pd.fk_propal, pd.fk_parent_line, pd.fk_product, pd.label as custom_label, pd.description, pd.price, pd.qty, pd.vat_src_code, pd.tva_tx,';
4147 $sql .= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice,';
4148 $sql .= ' pd.info_bits, pd.total_ht, pd.total_tva, pd.total_ttc, pd.fk_product_fournisseur_price as fk_fournprice, pd.buy_price_ht as pa_ht, pd.special_code, pd.rang,';
4149 $sql .= ' pd.fk_unit,';
4150 $sql .= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
4151 $sql .= ' pd.fk_multicurrency, pd.multicurrency_code, pd.multicurrency_subprice, pd.multicurrency_total_ht, pd.multicurrency_total_tva, pd.multicurrency_total_ttc,';
4152 $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
4153 $sql .= ' pd.date_start, pd.date_end, pd.product_type';
4154 $sql .= ' FROM '.MAIN_DB_PREFIX.'propaldet as pd';
4155 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
4156 $sql .= ' WHERE pd.rowid = '.((int) $rowid);
4157
4158 $result = $this->db->query($sql);
4159 if ($result) {
4160 $objp = $this->db->fetch_object($result);
4161
4162 if ($objp) {
4163 $this->id = $objp->rowid;
4164 $this->rowid = $objp->rowid; // deprecated
4165 $this->fk_propal = $objp->fk_propal;
4166 $this->fk_parent_line = $objp->fk_parent_line;
4167 $this->label = $objp->custom_label;
4168 $this->desc = $objp->description;
4169 $this->qty = $objp->qty;
4170 $this->price = $objp->price; // deprecated
4171 $this->subprice = $objp->subprice;
4172 $this->vat_src_code = $objp->vat_src_code;
4173 $this->tva_tx = $objp->tva_tx;
4174 $this->remise = $objp->remise; // deprecated
4175 $this->remise_percent = $objp->remise_percent;
4176 $this->fk_remise_except = $objp->fk_remise_except;
4177 $this->fk_product = $objp->fk_product;
4178 $this->info_bits = $objp->info_bits;
4179
4180 $this->total_ht = $objp->total_ht;
4181 $this->total_tva = $objp->total_tva;
4182 $this->total_ttc = $objp->total_ttc;
4183
4184 $this->fk_fournprice = $objp->fk_fournprice;
4185
4186 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
4187 $this->pa_ht = $marginInfos[0];
4188 $this->marge_tx = $marginInfos[1];
4189 $this->marque_tx = $marginInfos[2];
4190
4191 $this->special_code = $objp->special_code;
4192 $this->product_type = $objp->product_type;
4193 $this->rang = $objp->rang;
4194
4195 $this->ref = $objp->product_ref; // deprecated
4196 $this->product_ref = $objp->product_ref;
4197 $this->libelle = $objp->product_label; // deprecated
4198 $this->product_label = $objp->product_label;
4199 $this->product_desc = $objp->product_desc;
4200 $this->fk_unit = $objp->fk_unit;
4201
4202 $this->date_start = $this->db->jdate($objp->date_start);
4203 $this->date_end = $this->db->jdate($objp->date_end);
4204
4205 // Multicurrency
4206 $this->fk_multicurrency = $objp->fk_multicurrency;
4207 $this->multicurrency_code = $objp->multicurrency_code;
4208 $this->multicurrency_subprice = $objp->multicurrency_subprice;
4209 $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
4210 $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
4211 $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
4212
4213 $this->fetch_optionals();
4214
4215 $this->db->free($result);
4216
4217 return 1;
4218 } else {
4219 return 0;
4220 }
4221 } else {
4222 return -1;
4223 }
4224 }
4225
4232 public function insert($notrigger = 0)
4233 {
4234 global $conf, $user;
4235
4236 $error = 0;
4237
4238 dol_syslog(get_class($this)."::insert rang=".$this->rang);
4239
4240 $pa_ht_isemptystring = (empty($this->pa_ht) && $this->pa_ht == ''); // If true, we can use a default value. If this->pa_ht = '0', we must use '0'.
4241 $this->pa_ht = (float) $this->pa_ht; // convert to float after check if empty value
4242
4243 // Clean parameters
4244 if (empty($this->tva_tx)) {
4245 $this->tva_tx = 0;
4246 }
4247 if (empty($this->localtax1_tx)) {
4248 $this->localtax1_tx = 0;
4249 }
4250 if (empty($this->localtax2_tx)) {
4251 $this->localtax2_tx = 0;
4252 }
4253 if (empty($this->localtax1_type)) {
4254 $this->localtax1_type = 0;
4255 }
4256 if (empty($this->localtax2_type)) {
4257 $this->localtax2_type = 0;
4258 }
4259 if (empty($this->total_localtax1)) {
4260 $this->total_localtax1 = 0;
4261 }
4262 if (empty($this->total_localtax2)) {
4263 $this->total_localtax2 = 0;
4264 }
4265 if (empty($this->rang)) {
4266 $this->rang = 0;
4267 }
4268 if (empty($this->remise_percent) || !is_numeric($this->remise_percent)) {
4269 $this->remise_percent = 0;
4270 }
4271 if (empty($this->info_bits)) {
4272 $this->info_bits = 0;
4273 }
4274 if (empty($this->special_code)) {
4275 $this->special_code = 0;
4276 }
4277 if (empty($this->fk_parent_line)) {
4278 $this->fk_parent_line = 0;
4279 }
4280 if (empty($this->fk_fournprice)) {
4281 $this->fk_fournprice = 0;
4282 }
4283 if (!is_numeric($this->qty)) {
4284 $this->qty = 0;
4285 }
4286 if (empty($this->multicurrency_subprice)) {
4287 $this->multicurrency_subprice = 0;
4288 }
4289 if (empty($this->multicurrency_total_ht)) {
4290 $this->multicurrency_total_ht = 0;
4291 }
4292 if (empty($this->multicurrency_total_tva)) {
4293 $this->multicurrency_total_tva = 0;
4294 }
4295 if (empty($this->multicurrency_total_ttc)) {
4296 $this->multicurrency_total_ttc = 0;
4297 }
4298
4299 // if buy price not defined, define buyprice as configured in margin admin
4300 if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
4301 if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0) {
4302 return $result;
4303 } else {
4304 $this->pa_ht = $result;
4305 }
4306 }
4307
4308 // Check parameters
4309 if ($this->product_type < 0) {
4310 return -1;
4311 }
4312
4313 $this->db->begin();
4314
4315 // Insert line into database
4316 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'propaldet';
4317 $sql .= ' (fk_propal, fk_parent_line, label, description, fk_product, product_type,';
4318 $sql .= ' fk_remise_except, qty, vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
4319 $sql .= ' subprice, remise_percent, ';
4320 $sql .= ' info_bits, ';
4321 $sql .= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
4322 $sql .= ' fk_unit,';
4323 $sql .= ' date_start, date_end';
4324 $sql .= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc)';
4325 $sql .= " VALUES (".$this->fk_propal.",";
4326 $sql .= " ".($this->fk_parent_line > 0 ? "'".$this->db->escape($this->fk_parent_line)."'" : "null").",";
4327 $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
4328 $sql .= " '".$this->db->escape($this->desc)."',";
4329 $sql .= " ".($this->fk_product ? "'".$this->db->escape($this->fk_product)."'" : "null").",";
4330 $sql .= " '".$this->db->escape($this->product_type)."',";
4331 $sql .= " ".($this->fk_remise_except ? "'".$this->db->escape($this->fk_remise_except)."'" : "null").",";
4332 $sql .= " ".price2num($this->qty, 'MS').",";
4333 $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
4334 $sql .= " ".price2num($this->tva_tx).",";
4335 $sql .= " ".price2num($this->localtax1_tx).",";
4336 $sql .= " ".price2num($this->localtax2_tx).",";
4337 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
4338 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
4339 $sql .= " ".(price2num($this->subprice) !== '' ? price2num($this->subprice, 'MU') : "null").",";
4340 $sql .= " ".price2num($this->remise_percent, 3).",";
4341 $sql .= " ".(isset($this->info_bits) ? ((int) $this->info_bits) : "null").",";
4342 $sql .= " ".price2num($this->total_ht, 'MT').",";
4343 $sql .= " ".price2num($this->total_tva, 'MT').",";
4344 $sql .= " ".price2num($this->total_localtax1, 'MT').",";
4345 $sql .= " ".price2num($this->total_localtax2, 'MT').",";
4346 $sql .= " ".price2num($this->total_ttc, 'MT').",";
4347 $sql .= " ".(!empty($this->fk_fournprice) ? "'".$this->db->escape($this->fk_fournprice)."'" : "null").",";
4348 $sql .= " ".(isset($this->pa_ht) ? "'".price2num($this->pa_ht)."'" : "null").",";
4349 $sql .= ' '.((int) $this->special_code).',';
4350 $sql .= ' '.((int) $this->rang).',';
4351 $sql .= ' '.(empty($this->fk_unit) ? 'NULL' : ((int) $this->fk_unit)).',';
4352 $sql .= " ".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null").',';
4353 $sql .= " ".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
4354 $sql .= ", ".($this->fk_multicurrency > 0 ? ((int) $this->fk_multicurrency) : 'null');
4355 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
4356 $sql .= ", ".price2num($this->multicurrency_subprice, 'CU');
4357 $sql .= ", ".price2num($this->multicurrency_total_ht, 'CT');
4358 $sql .= ", ".price2num($this->multicurrency_total_tva, 'CT');
4359 $sql .= ", ".price2num($this->multicurrency_total_ttc, 'CT');
4360 $sql .= ')';
4361
4362 dol_syslog(get_class($this).'::insert', LOG_DEBUG);
4363 $resql = $this->db->query($sql);
4364 if ($resql) {
4365 $this->rowid = $this->db->last_insert_id(MAIN_DB_PREFIX.'propaldet');
4366
4367 if (!$error) {
4368 $this->id = $this->rowid;
4369 $result = $this->insertExtraFields();
4370 if ($result < 0) {
4371 $error++;
4372 }
4373 }
4374
4375 if (!$error && !$notrigger) {
4376 // Call trigger
4377 $result = $this->call_trigger('LINEPROPAL_INSERT', $user);
4378 if ($result < 0) {
4379 $this->db->rollback();
4380 return -1;
4381 }
4382 // End call triggers
4383 }
4384
4385 if (!$error) {
4386 $this->db->commit();
4387 return 1;
4388 }
4389
4390 foreach ($this->errors as $errmsg) {
4391 dol_syslog(get_class($this)."::insert ".$errmsg, LOG_ERR);
4392 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4393 }
4394 $this->db->rollback();
4395 return -1 * $error;
4396 } else {
4397 $this->error = $this->db->error()." sql=".$sql;
4398 $this->db->rollback();
4399 return -1;
4400 }
4401 }
4402
4410 public function delete(User $user, $notrigger = 0)
4411 {
4412 global $conf;
4413
4414 $error = 0;
4415 $this->db->begin();
4416
4417 if (!$notrigger) {
4418 // Call trigger
4419 $result = $this->call_trigger('LINEPROPAL_DELETE', $user);
4420 if ($result < 0) {
4421 $error++;
4422 }
4423 }
4424 // End call triggers
4425
4426 if (!$error) {
4427 $sql = "DELETE FROM " . MAIN_DB_PREFIX . "propaldet WHERE rowid = " . ((int) $this->rowid);
4428 dol_syslog("PropaleLigne::delete", LOG_DEBUG);
4429 if ($this->db->query($sql)) {
4430 // Remove extrafields
4431 if (!$error) {
4432 $this->id = $this->rowid;
4433 $result = $this->deleteExtraFields();
4434 if ($result < 0) {
4435 $error++;
4436 dol_syslog(get_class($this) . "::delete error -4 " . $this->error, LOG_ERR);
4437 }
4438 }
4439 } else {
4440 $this->error = $this->db->error() . " sql=" . $sql;
4441 $error++;
4442 }
4443 }
4444
4445 if ($error) {
4446 $this->db->rollback();
4447 return -1;
4448 } else {
4449 $this->db->commit();
4450 return 1;
4451 }
4452 }
4453
4460 public function update($notrigger = 0)
4461 {
4462 global $conf, $user;
4463
4464 $error = 0;
4465
4466 $pa_ht_isemptystring = (empty($this->pa_ht) && $this->pa_ht == ''); // If true, we can use a default value. If this->pa_ht = '0', we must use '0'.
4467 $this->pa_ht = (float) $this->pa_ht; // convert to float after check if empty value
4468
4469 if (empty($this->id) && !empty($this->rowid)) {
4470 $this->id = $this->rowid;
4471 }
4472
4473 // Clean parameters
4474 if (empty($this->tva_tx)) {
4475 $this->tva_tx = 0;
4476 }
4477 if (empty($this->localtax1_tx)) {
4478 $this->localtax1_tx = 0;
4479 }
4480 if (empty($this->localtax2_tx)) {
4481 $this->localtax2_tx = 0;
4482 }
4483 if (empty($this->total_localtax1)) {
4484 $this->total_localtax1 = 0;
4485 }
4486 if (empty($this->total_localtax2)) {
4487 $this->total_localtax2 = 0;
4488 }
4489 if (empty($this->localtax1_type)) {
4490 $this->localtax1_type = 0;
4491 }
4492 if (empty($this->localtax2_type)) {
4493 $this->localtax2_type = 0;
4494 }
4495 if (empty($this->marque_tx)) {
4496 $this->marque_tx = 0;
4497 }
4498 if (empty($this->marge_tx)) {
4499 $this->marge_tx = 0;
4500 }
4501 if (empty($this->price)) {
4502 $this->price = 0; // TODO A virer
4503 }
4504 if (empty($this->remise_percent)) {
4505 $this->remise_percent = 0;
4506 }
4507 if (empty($this->info_bits)) {
4508 $this->info_bits = 0;
4509 }
4510 if (empty($this->special_code)) {
4511 $this->special_code = 0;
4512 }
4513 if (empty($this->fk_parent_line)) {
4514 $this->fk_parent_line = 0;
4515 }
4516 if (empty($this->fk_fournprice)) {
4517 $this->fk_fournprice = 0;
4518 }
4519 if (empty($this->subprice)) {
4520 $this->subprice = 0;
4521 }
4522
4523 // if buy price not defined, define buyprice as configured in margin admin
4524 if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
4525 if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0) {
4526 return $result;
4527 } else {
4528 $this->pa_ht = $result;
4529 }
4530 }
4531
4532 $this->db->begin();
4533
4534 // Mise a jour ligne en base
4535 $sql = "UPDATE ".MAIN_DB_PREFIX."propaldet SET";
4536 $sql .= " description='".$this->db->escape($this->desc)."'";
4537 $sql .= ", label=".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
4538 $sql .= ", product_type=".$this->product_type;
4539 $sql .= ", vat_src_code = '".(empty($this->vat_src_code) ? '' : $this->vat_src_code)."'";
4540 $sql .= ", tva_tx='".price2num($this->tva_tx)."'";
4541 $sql .= ", localtax1_tx=".price2num($this->localtax1_tx);
4542 $sql .= ", localtax2_tx=".price2num($this->localtax2_tx);
4543 $sql .= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4544 $sql .= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4545 $sql .= ", qty='".price2num($this->qty)."'";
4546 $sql .= ", subprice=".price2num($this->subprice);
4547 $sql .= ", remise_percent=".price2num($this->remise_percent);
4548 $sql .= ", price=".(float) price2num($this->price); // TODO A virer
4549 $sql .= ", remise=".(float) price2num($this->remise); // TODO A virer
4550 $sql .= ", info_bits='".$this->db->escape($this->info_bits)."'";
4551 if (empty($this->skip_update_total)) {
4552 $sql .= ", total_ht=".price2num($this->total_ht);
4553 $sql .= ", total_tva=".price2num($this->total_tva);
4554 $sql .= ", total_ttc=".price2num($this->total_ttc);
4555 $sql .= ", total_localtax1=".price2num($this->total_localtax1);
4556 $sql .= ", total_localtax2=".price2num($this->total_localtax2);
4557 }
4558 $sql .= ", fk_product_fournisseur_price=".(!empty($this->fk_fournprice) ? "'".$this->db->escape($this->fk_fournprice)."'" : "null");
4559 $sql .= ", buy_price_ht=".price2num($this->pa_ht);
4560 $sql .= ", special_code=".((int) $this->special_code);
4561 $sql .= ", fk_parent_line=".($this->fk_parent_line > 0 ? (int) $this->fk_parent_line : "null");
4562 if (!empty($this->rang)) {
4563 $sql .= ", rang=".((int) $this->rang);
4564 }
4565 $sql .= ", date_start=".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null");
4566 $sql .= ", date_end=".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
4567 $sql .= ", fk_unit=".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4568
4569 // Multicurrency
4570 $sql .= ", multicurrency_subprice=".price2num($this->multicurrency_subprice);
4571 $sql .= ", multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
4572 $sql .= ", multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
4573 $sql .= ", multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
4574
4575 $sql .= " WHERE rowid = ".((int) $this->id);
4576
4577 dol_syslog(get_class($this)."::update", LOG_DEBUG);
4578 $resql = $this->db->query($sql);
4579 if ($resql) {
4580 if (!$error) {
4581 $result = $this->insertExtraFields();
4582 if ($result < 0) {
4583 $error++;
4584 }
4585 }
4586
4587 if (!$error && !$notrigger) {
4588 // Call trigger
4589 $result = $this->call_trigger('LINEPROPAL_MODIFY', $user);
4590 if ($result < 0) {
4591 $this->db->rollback();
4592 return -1;
4593 }
4594 // End call triggers
4595 }
4596
4597 if (!$error) {
4598 $this->db->commit();
4599 return 1;
4600 }
4601
4602 foreach ($this->errors as $errmsg) {
4603 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
4604 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4605 }
4606 $this->db->rollback();
4607 return -1 * $error;
4608 } else {
4609 $this->error = $this->db->error();
4610 $this->db->rollback();
4611 return -2;
4612 }
4613 }
4614
4615 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4622 public function update_total()
4623 {
4624 // phpcs:enable
4625 $this->db->begin();
4626
4627 // Mise a jour ligne en base
4628 $sql = "UPDATE ".MAIN_DB_PREFIX."propaldet SET";
4629 $sql .= " total_ht=".price2num($this->total_ht, 'MT');
4630 $sql .= ",total_tva=".price2num($this->total_tva, 'MT');
4631 $sql .= ",total_ttc=".price2num($this->total_ttc, 'MT');
4632 $sql .= " WHERE rowid = ".((int) $this->rowid);
4633
4634 dol_syslog("PropaleLigne::update_total", LOG_DEBUG);
4635
4636 $resql = $this->db->query($sql);
4637 if ($resql) {
4638 $this->db->commit();
4639 return 1;
4640 } else {
4641 $this->error = $this->db->error();
4642 $this->db->rollback();
4643 return -2;
4644 }
4645 }
4646}
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:637
$object ref
Definition info.php:79
Parent class of all other business classes (invoices, contracts, proposals, orders,...
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
Save a new position (field rang) for details lines.
deleteEcmFiles($mode=0)
Delete related files of object in database.
update_price($exclspec=0, $roundingadjust='auto', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
add_object_linked($origin=null, $origin_id=null, $f_user=null, $notrigger=0)
Add an object link into llx_element_element.
defineBuyPrice($unitPrice=0.0, $discountPercent=0.0, $fk_product=0)
Get buy price to use for margin calculation.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty.
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid=0, $f_user=null, $notrigger=0)
Delete all links between an object $this.
setErrorsFromObject($object)
setErrorsFromObject
static isExistingObject($element, $id, $ref='', $ref_ext='')
Check if an object id or ref exists If you don't need or want to instantiate the object and just need...
updateRangOfLine($rowid, $rang)
Update position of line (rang)
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.
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
Class to manage absolute discounts.
Class to manage Dolibarr database access.
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.
const TYPE_PRODUCT
Regular product.
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 clicable link of object (with eventually picto)
getKanbanView($option='', $arraydata=null)
Return clicable 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.
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)
Adding line of fixed discount in the proposal in DB.
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.
closeProposal($user, $status, $note='', $notrigger=0)
Close/set the commercial proposal to status signed or refused (fill also date signature)
reopen($user, $status, $note='', $notrigger=0)
Reopen the commercial proposal.
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.
__construct($db)
Class line Constructor.
update($notrigger=0)
Update propal line object into DB.
fetch($rowid)
Retrieve the propal line object.
insert($notrigger=0)
Insert object line propal in database.
update_total()
Update DB line fields total_xxx Used by migration.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage translations.
Class to manage Dolibarr users.
trait CommonIncoterm
Superclass for incoterm classes.
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 '.
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 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
publicphonebutton2 phonegreen basiclayout basiclayout TotalHT VATCode TotalVAT TotalLT1 TotalLT2 TotalTTC TotalHT clearboth nowraponall TAKEPOS_SHOW_SUBPRICE right right right takeposterminal SELECT e e e e e statut
Definition invoice.php:2043
publicphonebutton2 phonegreen basiclayout basiclayout TotalHT VATCode TotalVAT TotalLT1 TotalLT2 TotalTTC TotalHT clearboth nowraponall TAKEPOS_SHOW_SUBPRICE right right right takeposterminal SELECT e rowid
Definition invoice.php:2043