dolibarr 20.0.0
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' => '$conf->incoterm->enabled', 'visible' => -1, 'position' => 220),
347 'location_incoterms' => array('type' => 'varchar(255)', 'label' => 'IncotermLabel', 'enabled' => '$conf->incoterm->enabled', '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);
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);
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=".(isset($this->user_validation_id) ? $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;
2068 $this->user_validation_id = $user->id;
2069 $this->datev = $now;
2070 $this->date_validation = $now;
2071
2072 $this->db->commit();
2073 return 1;
2074 } else {
2075 $this->db->rollback();
2076 return -1;
2077 }
2078 }
2079
2080
2081 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2090 public function set_date($user, $date, $notrigger = 0)
2091 {
2092 // phpcs:enable
2093 if (empty($date)) {
2094 $this->error = 'ErrorBadParameter';
2095 dol_syslog(get_class($this)."::set_date ".$this->error, LOG_ERR);
2096 return -1;
2097 }
2098
2099 if ($user->hasRight('propal', 'creer')) {
2100 $error = 0;
2101
2102 $this->db->begin();
2103
2104 $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET datep = '".$this->db->idate($date)."'";
2105 $sql .= " WHERE rowid = ".((int) $this->id);
2106
2107 dol_syslog(__METHOD__, LOG_DEBUG);
2108 $resql = $this->db->query($sql);
2109 if (!$resql) {
2110 $this->errors[] = $this->db->error();
2111 $error++;
2112 }
2113
2114 if (!$error) {
2115 $this->oldcopy = clone $this;
2116 $this->date = $date;
2117 $this->datep = $date; // deprecated
2118 }
2119
2120 if (!$notrigger && empty($error)) {
2121 // Call trigger
2122 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2123 if ($result < 0) {
2124 $error++;
2125 }
2126 // End call triggers
2127 }
2128
2129 if (!$error) {
2130 $this->db->commit();
2131 return 1;
2132 } else {
2133 foreach ($this->errors as $errmsg) {
2134 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2135 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2136 }
2137 $this->db->rollback();
2138 return -1 * $error;
2139 }
2140 }
2141
2142 return -1;
2143 }
2144
2145 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2154 public function set_echeance($user, $date_end_validity, $notrigger = 0)
2155 {
2156 // phpcs:enable
2157 if ($user->hasRight('propal', 'creer')) {
2158 $error = 0;
2159
2160 $this->db->begin();
2161
2162 $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET fin_validite = ".($date_end_validity != '' ? "'".$this->db->idate($date_end_validity)."'" : 'null');
2163 $sql .= " WHERE rowid = ".((int) $this->id);
2164
2165 dol_syslog(__METHOD__, LOG_DEBUG);
2166
2167 $resql = $this->db->query($sql);
2168 if (!$resql) {
2169 $this->errors[] = $this->db->error();
2170 $error++;
2171 }
2172
2173
2174 if (!$error) {
2175 $this->oldcopy = clone $this;
2176 $this->fin_validite = $date_end_validity;
2177 }
2178
2179 if (!$notrigger && empty($error)) {
2180 // Call trigger
2181 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2182 if ($result < 0) {
2183 $error++;
2184 }
2185 // End call triggers
2186 }
2187
2188 if (!$error) {
2189 $this->db->commit();
2190 return 1;
2191 } else {
2192 foreach ($this->errors as $errmsg) {
2193 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2194 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2195 }
2196 $this->db->rollback();
2197 return -1 * $error;
2198 }
2199 }
2200
2201 return -1;
2202 }
2203
2204 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2214 public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2215 {
2216 // phpcs:enable
2217 return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2218 }
2219
2228 public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2229 {
2230 if ($user->hasRight('propal', 'creer')) {
2231 $error = 0;
2232
2233 $this->db->begin();
2234
2235 $sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
2236 $sql .= " SET date_livraison = ".($delivery_date != '' ? "'".$this->db->idate($delivery_date)."'" : 'null');
2237 $sql .= " WHERE rowid = ".((int) $this->id);
2238
2239 dol_syslog(__METHOD__, LOG_DEBUG);
2240 $resql = $this->db->query($sql);
2241 if (!$resql) {
2242 $this->errors[] = $this->db->error();
2243 $error++;
2244 }
2245
2246 if (!$error) {
2247 $this->oldcopy = clone $this;
2248 $this->delivery_date = $delivery_date;
2249 }
2250
2251 if (!$notrigger && empty($error)) {
2252 // Call trigger
2253 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2254 if ($result < 0) {
2255 $error++;
2256 }
2257 // End call triggers
2258 }
2259
2260 if (!$error) {
2261 $this->db->commit();
2262 return 1;
2263 } else {
2264 foreach ($this->errors as $errmsg) {
2265 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2266 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2267 }
2268 $this->db->rollback();
2269 return -1 * $error;
2270 }
2271 }
2272
2273 return -1;
2274 }
2275
2276 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2285 public function set_availability($user, $id, $notrigger = 0)
2286 {
2287 // phpcs:enable
2288 if ($user->hasRight('propal', 'creer') && $this->statut >= self::STATUS_DRAFT) {
2289 $error = 0;
2290
2291 $this->db->begin();
2292
2293 $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2294 $sql .= " SET fk_availability = ".((int) $id);
2295 $sql .= " WHERE rowid = ".((int) $this->id);
2296
2297 dol_syslog(__METHOD__.' availability('.$id.')', LOG_DEBUG);
2298 $resql = $this->db->query($sql);
2299 if (!$resql) {
2300 $this->errors[] = $this->db->error();
2301 $error++;
2302 }
2303
2304 if (!$error) {
2305 $this->oldcopy = clone $this;
2306 $this->fk_availability = $id;
2307 $this->availability_id = $id;
2308 }
2309
2310 if (!$notrigger && empty($error)) {
2311 // Call trigger
2312 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2313 if ($result < 0) {
2314 $error++;
2315 }
2316 // End call triggers
2317 }
2318
2319 if (!$error) {
2320 $this->db->commit();
2321 return 1;
2322 } else {
2323 foreach ($this->errors as $errmsg) {
2324 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2325 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2326 }
2327 $this->db->rollback();
2328 return -1 * $error;
2329 }
2330 } else {
2331 $error_str = 'Propal status do not meet requirement '.$this->statut;
2332 dol_syslog(__METHOD__.$error_str, LOG_ERR);
2333 $this->error = $error_str;
2334 $this->errors[] = $this->error;
2335 return -2;
2336 }
2337 }
2338
2339 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2348 public function set_demand_reason($user, $id, $notrigger = 0)
2349 {
2350 // phpcs:enable
2351 if ($user->hasRight('propal', 'creer') && $this->statut >= self::STATUS_DRAFT) {
2352 $error = 0;
2353
2354 $this->db->begin();
2355
2356 $sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
2357 $sql .= " SET fk_input_reason = ".((int) $id);
2358 $sql .= " WHERE rowid = ".((int) $this->id);
2359
2360 dol_syslog(__METHOD__, LOG_DEBUG);
2361 $resql = $this->db->query($sql);
2362 if (!$resql) {
2363 $this->errors[] = $this->db->error();
2364 $error++;
2365 }
2366
2367
2368 if (!$error) {
2369 $this->oldcopy = clone $this;
2370
2371 $this->demand_reason_id = $id;
2372 }
2373
2374
2375 if (!$notrigger && empty($error)) {
2376 // Call trigger
2377 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2378 if ($result < 0) {
2379 $error++;
2380 }
2381 // End call triggers
2382 }
2383
2384 if (!$error) {
2385 $this->db->commit();
2386 return 1;
2387 } else {
2388 foreach ($this->errors as $errmsg) {
2389 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2390 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2391 }
2392 $this->db->rollback();
2393 return -1 * $error;
2394 }
2395 } else {
2396 $error_str = 'Propal status do not meet requirement '.$this->statut;
2397 dol_syslog(__METHOD__.$error_str, LOG_ERR);
2398 $this->error = $error_str;
2399 $this->errors[] = $this->error;
2400 return -2;
2401 }
2402 }
2403
2404 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2413 public function set_ref_client($user, $ref_client, $notrigger = 0)
2414 {
2415 // phpcs:enable
2416 if ($user->hasRight('propal', 'creer')) {
2417 $error = 0;
2418
2419 $this->db->begin();
2420
2421 $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET ref_client = ".(empty($ref_client) ? 'NULL' : "'".$this->db->escape($ref_client)."'");
2422 $sql .= " WHERE rowid = ".((int) $this->id);
2423
2424 dol_syslog(__METHOD__.' $this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
2425 $resql = $this->db->query($sql);
2426 if (!$resql) {
2427 $this->errors[] = $this->db->error();
2428 $error++;
2429 }
2430
2431 if (!$error) {
2432 $this->oldcopy = clone $this;
2433 $this->ref_client = $ref_client;
2434 }
2435
2436 if (!$notrigger && empty($error)) {
2437 // Call trigger
2438 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2439 if ($result < 0) {
2440 $error++;
2441 }
2442 // End call triggers
2443 }
2444
2445 if (!$error) {
2446 $this->db->commit();
2447 return 1;
2448 } else {
2449 foreach ($this->errors as $errmsg) {
2450 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2451 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2452 }
2453 $this->db->rollback();
2454 return -1 * $error;
2455 }
2456 }
2457
2458 return -1;
2459 }
2460
2461
2471 public function reopen($user, $status, $note = '', $notrigger = 0)
2472 {
2473 $error = 0;
2474
2475 $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2476 $sql .= " SET fk_statut = ".((int) $status).",";
2477 if (!empty($note)) {
2478 $sql .= " note_private = '".$this->db->escape($note)."',";
2479 }
2480 $sql .= " date_cloture=NULL, fk_user_cloture=NULL";
2481 $sql .= " WHERE rowid = ".((int) $this->id);
2482
2483 $this->db->begin();
2484
2485 dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
2486 $resql = $this->db->query($sql);
2487 if (!$resql) {
2488 $error++;
2489 $this->errors[] = "Error ".$this->db->lasterror();
2490 }
2491 if (!$error) {
2492 if (!$notrigger) {
2493 // Call trigger
2494 $result = $this->call_trigger('PROPAL_REOPEN', $user);
2495 if ($result < 0) {
2496 $error++;
2497 }
2498 // End call triggers
2499 }
2500 }
2501
2502 // Commit or rollback
2503 if ($error) {
2504 if (!empty($this->errors)) {
2505 foreach ($this->errors as $errmsg) {
2506 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
2507 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2508 }
2509 }
2510 $this->db->rollback();
2511 return -1 * $error;
2512 } else {
2513 $this->statut = $status;
2514 $this->status = $status;
2515
2516 $this->db->commit();
2517 return 1;
2518 }
2519 }
2520
2530 public function closeProposal($user, $status, $note = '', $notrigger = 0)
2531 {
2532 global $langs,$conf;
2533
2534 $error = 0;
2535 $now = dol_now();
2536
2537 $this->db->begin();
2538
2539 $newprivatenote = dol_concatdesc($this->note_private, $note);
2540
2541 if (!getDolGlobalString('PROPALE_KEEP_OLD_SIGNATURE_INFO')) {
2542 $date_signature = $now;
2543 $fk_user_signature = $user->id;
2544 } else {
2545 $this->info($this->id);
2546 if (!isset($this->date_signature) || $this->date_signature == '') {
2547 $date_signature = $now;
2548 $fk_user_signature = $user->id;
2549 } else {
2550 $date_signature = $this->date_signature;
2551 $fk_user_signature = $this->user_signature->id;
2552 }
2553 }
2554
2555 $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2556 $sql .= " SET fk_statut = ".((int) $status).", note_private = '".$this->db->escape($newprivatenote)."'";
2557 if ($status == self::STATUS_SIGNED) {
2558 $sql .= ", date_signature='".$this->db->idate($now)."', fk_user_signature = ".($fk_user_signature);
2559 }
2560 $sql .= " WHERE rowid = ".((int) $this->id);
2561
2562 $resql = $this->db->query($sql);
2563 if ($resql) {
2564 // Status self::STATUS_REFUSED by default
2565 $modelpdf = getDolGlobalString('PROPALE_ADDON_PDF_ODT_CLOSED', $this->model_pdf);
2566 $trigger_name = 'PROPAL_CLOSE_REFUSED'; // used later in call_trigger()
2567
2568 if ($status == self::STATUS_SIGNED) { // Status self::STATUS_SIGNED
2569 $trigger_name = 'PROPAL_CLOSE_SIGNED'; // used later in call_trigger()
2570 $modelpdf = getDolGlobalString('PROPALE_ADDON_PDF_ODT_TOBILL') ? $conf->global->PROPALE_ADDON_PDF_ODT_TOBILL : $this->model_pdf;
2571
2572 // The connected company is classified as a client
2573 $soc = new Societe($this->db);
2574 $soc->id = $this->socid;
2575 $result = $soc->setAsCustomer();
2576
2577 if ($result < 0) {
2578 $this->error = $this->db->lasterror();
2579 $this->db->rollback();
2580 return -2;
2581 }
2582 }
2583
2584 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE') && !getDolGlobalInt('PROPAL_DISABLE_AUTOUPDATE_ON_CLOSE')) {
2585 // Define output language
2586 $outputlangs = $langs;
2587 if (getDolGlobalInt('MAIN_MULTILANGS')) {
2588 $outputlangs = new Translate("", $conf);
2589 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
2590 $outputlangs->setDefaultLang($newlang);
2591 }
2592
2593 // PDF
2594 $hidedetails = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 1 : 0);
2595 $hidedesc = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 1 : 0);
2596 $hideref = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 1 : 0);
2597
2598 //$ret=$object->fetch($id); // Reload to get new records
2599 $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
2600 }
2601
2602 if (!$error) {
2603 $this->oldcopy = clone $this;
2604 $this->statut = $status;
2605 $this->status = $status;
2606 $this->date_signature = $date_signature;
2607 $this->note_private = $newprivatenote;
2608 }
2609
2610 if (!$notrigger && empty($error)) {
2611 // Call trigger
2612 $result = $this->call_trigger($trigger_name, $user);
2613 if ($result < 0) {
2614 $error++;
2615 }
2616 // End call triggers
2617 }
2618
2619 if (!$error) {
2620 $this->db->commit();
2621 return 1;
2622 } else {
2623 $this->statut = $this->oldcopy->statut;
2624 $this->status = $this->oldcopy->statut;
2625 $this->date_signature = $this->oldcopy->date_signature;
2626 $this->note_private = $this->oldcopy->note_private;
2627
2628 $this->db->rollback();
2629 return -1;
2630 }
2631 } else {
2632 $this->error = $this->db->lasterror();
2633 $this->db->rollback();
2634 return -1;
2635 }
2636 }
2637
2646 public function classifyBilled(User $user, $notrigger = 0, $note = '')
2647 {
2648 global $conf, $langs;
2649
2650 $error = 0;
2651
2652 $now = dol_now();
2653 $num = 0;
2654
2655 $triggerName = 'PROPAL_CLASSIFY_BILLED';
2656
2657 $this->db->begin();
2658
2659 $newprivatenote = dol_concatdesc($this->note_private, $note);
2660
2661 $sql = 'UPDATE '.MAIN_DB_PREFIX.'propal SET fk_statut = '.self::STATUS_BILLED.", ";
2662 $sql .= " note_private = '".$this->db->escape($newprivatenote)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".((int) $user->id);
2663 $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.((int) self::STATUS_SIGNED);
2664
2665 dol_syslog(__METHOD__, LOG_DEBUG);
2666 $resql = $this->db->query($sql);
2667 if (!$resql) {
2668 $this->errors[] = $this->db->error();
2669 $error++;
2670 } else {
2671 $num = $this->db->affected_rows($resql);
2672 }
2673
2674 if (!$error) {
2675 $modelpdf = getDolGlobalString('PROPALE_ADDON_PDF_ODT_CLOSED', $this->model_pdf);
2676
2677 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
2678 // Define output language
2679 $outputlangs = $langs;
2680 if (getDolGlobalInt('MAIN_MULTILANGS')) {
2681 $outputlangs = new Translate("", $conf);
2682 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
2683 $outputlangs->setDefaultLang($newlang);
2684 }
2685
2686 // PDF
2687 $hidedetails = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 1 : 0);
2688 $hidedesc = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 1 : 0);
2689 $hideref = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 1 : 0);
2690
2691 //$ret=$object->fetch($id); // Reload to get new records
2692 $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
2693 }
2694
2695 $this->oldcopy = clone $this;
2696 $this->statut = self::STATUS_BILLED;
2697 $this->date_cloture = $now;
2698 $this->note_private = $newprivatenote;
2699 }
2700
2701 if (!$notrigger && empty($error)) {
2702 // Call trigger
2703 $result = $this->call_trigger($triggerName, $user);
2704 if ($result < 0) {
2705 $error++;
2706 }
2707 // End call triggers
2708 }
2709
2710 if (!$error) {
2711 $this->db->commit();
2712 return $num;
2713 } else {
2714 foreach ($this->errors as $errmsg) {
2715 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2716 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2717 }
2718 $this->db->rollback();
2719 return -1 * $error;
2720 }
2721 }
2722
2729 public function setCancel(User $user)
2730 {
2731 $error = 0;
2732
2733 $this->db->begin();
2734
2735 $sql = "UPDATE ". MAIN_DB_PREFIX . "propal";
2736 $sql .= " SET fk_statut = " . self::STATUS_CANCELED . ",";
2737 $sql .= " fk_user_modif = " . ((int) $user->id);
2738 $sql .= " WHERE rowid = " . ((int) $this->id);
2739
2740 dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
2741 if ($this->db->query($sql)) {
2742 if (!$error) {
2743 // Call trigger
2744 $result = $this->call_trigger('PROPAL_CANCEL', $user);
2745 if ($result < 0) {
2746 $error++;
2747 }
2748 // End call triggers
2749 }
2750
2751 if (!$error) {
2753 $this->db->commit();
2754 return 1;
2755 } else {
2756 foreach ($this->errors as $errmsg) {
2757 dol_syslog(get_class($this)."::cancel ".$errmsg, LOG_ERR);
2758 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2759 }
2760 $this->db->rollback();
2761 return -1;
2762 }
2763 } else {
2764 $this->error = $this->db->error();
2765 $this->db->rollback();
2766 return -1;
2767 }
2768 }
2769
2770 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2778 public function setDraft($user, $notrigger = 0)
2779 {
2780 // phpcs:enable
2781 $error = 0;
2782
2783 // Protection
2784 if ($this->statut <= self::STATUS_DRAFT) {
2785 return 0;
2786 }
2787
2788 dol_syslog(get_class($this)."::setDraft", LOG_DEBUG);
2789
2790 $this->db->begin();
2791
2792 $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2793 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
2794 $sql .= ", online_sign_ip = NULL , online_sign_name = NULL";
2795 $sql .= " WHERE rowid = ".((int) $this->id);
2796
2797 $resql = $this->db->query($sql);
2798 if (!$resql) {
2799 $this->errors[] = $this->db->error();
2800 $error++;
2801 }
2802
2803 if (!$error) {
2804 $this->oldcopy = clone $this;
2805 }
2806
2807 if (!$notrigger && empty($error)) {
2808 // Call trigger
2809 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2810 if ($result < 0) {
2811 $error++;
2812 }
2813 // End call triggers
2814 }
2815
2816 if (!$error) {
2817 $this->statut = self::STATUS_DRAFT;
2818 $this->status = self::STATUS_DRAFT;
2819
2820 $this->db->commit();
2821 return 1;
2822 } else {
2823 foreach ($this->errors as $errmsg) {
2824 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2825 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2826 }
2827 $this->db->rollback();
2828 return -1 * $error;
2829 }
2830 }
2831
2832
2833 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2847 public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datep', $sortorder = 'DESC')
2848 {
2849 // phpcs:enable
2850 global $user;
2851
2852 $ga = array();
2853
2854 $sql = "SELECT s.rowid, s.nom as name, s.client,";
2855 $sql .= " p.rowid as propalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
2856 $sql .= " p.datep as dp, p.fin_validite as datelimite";
2857 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."propal as p, ".MAIN_DB_PREFIX."c_propalst as c";
2858 $sql .= " WHERE p.entity IN (".getEntity('propal').")";
2859 $sql .= " AND p.fk_soc = s.rowid";
2860 $sql .= " AND p.fk_statut = c.id";
2861
2862 // If the internal user must only see his customers, force searching by him
2863 $search_sale = 0;
2864 if (!$user->hasRight('societe', 'client', 'voir')) {
2865 $search_sale = $user->id;
2866 }
2867 // Search on sale representative
2868 if ($search_sale && $search_sale != '-1') {
2869 if ($search_sale == -2) {
2870 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
2871 } elseif ($search_sale > 0) {
2872 $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).")";
2873 }
2874 }
2875 // Search on socid
2876 if ($socid) {
2877 $sql .= " AND p.fk_soc = ".((int) $socid);
2878 }
2879 if ($draft) {
2880 $sql .= " AND p.fk_statut = ".((int) self::STATUS_DRAFT);
2881 }
2882 if ($notcurrentuser > 0) {
2883 $sql .= " AND p.fk_user_author <> ".((int) $user->id);
2884 }
2885 $sql .= $this->db->order($sortfield, $sortorder);
2886 $sql .= $this->db->plimit($limit, $offset);
2887
2888 $result = $this->db->query($sql);
2889 if ($result) {
2890 $num = $this->db->num_rows($result);
2891 if ($num) {
2892 $i = 0;
2893 while ($i < $num) {
2894 $obj = $this->db->fetch_object($result);
2895
2896 if ($shortlist == 1) {
2897 $ga[$obj->propalid] = $obj->ref;
2898 } elseif ($shortlist == 2) {
2899 $ga[$obj->propalid] = $obj->ref.' ('.$obj->name.')';
2900 } else {
2901 $ga[$i]['id'] = $obj->propalid;
2902 $ga[$i]['ref'] = $obj->ref;
2903 $ga[$i]['name'] = $obj->name;
2904 }
2905
2906 $i++;
2907 }
2908 }
2909 return $ga;
2910 } else {
2911 dol_print_error($this->db);
2912 return -1;
2913 }
2914 }
2915
2921 public function getInvoiceArrayList()
2922 {
2923 return $this->InvoiceArrayList($this->id);
2924 }
2925
2926 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2933 public function InvoiceArrayList($id)
2934 {
2935 // phpcs:enable
2936 $ga = array();
2937 $linkedInvoices = array();
2938
2939 $this->fetchObjectLinked($id, $this->element);
2940 foreach ($this->linkedObjectsIds as $objecttype => $objectid) {
2941 // Nouveau système du common object renvoi des rowid et non un id linéaire de 1 à n
2942 // On parcourt donc une liste d'objets en tant qu'objet unique
2943 foreach ($objectid as $key => $object) {
2944 // Cas des factures liees directement
2945 if ($objecttype == 'facture') {
2946 $linkedInvoices[] = $object;
2947 } else {
2948 // Cas des factures liees par un autre object (ex: commande)
2949 $this->fetchObjectLinked($object, $objecttype);
2950 foreach ($this->linkedObjectsIds as $subobjecttype => $subobjectid) {
2951 foreach ($subobjectid as $subkey => $subobject) {
2952 if ($subobjecttype == 'facture') {
2953 $linkedInvoices[] = $subobject;
2954 }
2955 }
2956 }
2957 }
2958 }
2959 }
2960
2961 if (count($linkedInvoices) > 0) {
2962 $sql = "SELECT rowid as facid, ref, total_ht as total, datef as df, fk_user_author, fk_statut, paye";
2963 $sql .= " FROM ".MAIN_DB_PREFIX."facture";
2964 $sql .= " WHERE rowid IN (".$this->db->sanitize(implode(',', $linkedInvoices)).")";
2965
2966 dol_syslog(get_class($this)."::InvoiceArrayList", LOG_DEBUG);
2967 $resql = $this->db->query($sql);
2968
2969 if ($resql) {
2970 $tab_sqlobj = array();
2971 $nump = $this->db->num_rows($resql);
2972 for ($i = 0; $i < $nump; $i++) {
2973 $sqlobj = $this->db->fetch_object($resql);
2974 $tab_sqlobj[] = $sqlobj;
2975 }
2976 $this->db->free($resql);
2977
2978 $nump = count($tab_sqlobj);
2979
2980 if ($nump) {
2981 $i = 0;
2982 while ($i < $nump) {
2983 $obj = array_shift($tab_sqlobj);
2984
2985 $ga[$i] = $obj;
2986
2987 $i++;
2988 }
2989 }
2990 return $ga;
2991 } else {
2992 return -1;
2993 }
2994 } else {
2995 return $ga;
2996 }
2997 }
2998
3006 public function delete($user, $notrigger = 0)
3007 {
3008 global $conf;
3009 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3010
3011 $error = 0;
3012
3013 $this->db->begin();
3014
3015 if (!$notrigger) {
3016 // Call trigger
3017 $result = $this->call_trigger('PROPAL_DELETE', $user);
3018 if ($result < 0) {
3019 $error++;
3020 }
3021 // End call triggers
3022 }
3023
3024 // Delete extrafields of lines and lines
3025 if (!$error && !empty($this->table_element_line)) {
3026 $tabletodelete = $this->table_element_line;
3027 $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).")";
3028 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id);
3029 if (!$this->db->query($sqlef) || !$this->db->query($sql)) {
3030 $error++;
3031 $this->error = $this->db->lasterror();
3032 $this->errors[] = $this->error;
3033 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3034 }
3035 }
3036
3037 if (!$error) {
3038 // Delete linked object
3039 $res = $this->deleteObjectLinked();
3040 if ($res < 0) {
3041 $error++;
3042 }
3043 }
3044
3045 if (!$error) {
3046 // Delete linked contacts
3047 $res = $this->delete_linked_contact();
3048 if ($res < 0) {
3049 $error++;
3050 }
3051 }
3052
3053 // Removed extrafields of object
3054 if (!$error) {
3055 $result = $this->deleteExtraFields();
3056 if ($result < 0) {
3057 $error++;
3058 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3059 }
3060 }
3061
3062 // Delete main record
3063 if (!$error) {
3064 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element." WHERE rowid = ".((int) $this->id);
3065 $res = $this->db->query($sql);
3066 if (!$res) {
3067 $error++;
3068 $this->error = $this->db->lasterror();
3069 $this->errors[] = $this->error;
3070 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3071 }
3072 }
3073
3074 // Delete record into ECM index and physically
3075 if (!$error) {
3076 $res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
3077 $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
3078 if (!$res) {
3079 $error++;
3080 }
3081 }
3082
3083 if (!$error) {
3084 // We remove directory
3085 $ref = dol_sanitizeFileName($this->ref);
3086 if ($conf->propal->multidir_output[$this->entity] && !empty($this->ref)) {
3087 $dir = $conf->propal->multidir_output[$this->entity]."/".$ref;
3088 $file = $dir."/".$ref.".pdf";
3089 if (file_exists($file)) {
3090 dol_delete_preview($this);
3091
3092 if (!dol_delete_file($file, 0, 0, 0, $this)) {
3093 $this->error = 'ErrorFailToDeleteFile';
3094 $this->errors[] = $this->error;
3095 $this->db->rollback();
3096 return 0;
3097 }
3098 }
3099 if (file_exists($dir)) {
3100 $res = @dol_delete_dir_recursive($dir); // delete files physically + into ecm tables
3101 if (!$res) {
3102 $this->error = 'ErrorFailToDeleteDir';
3103 $this->errors[] = $this->error;
3104 $this->db->rollback();
3105 return 0;
3106 }
3107 }
3108 }
3109 }
3110
3111 if (!$error) {
3112 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
3113 $this->db->commit();
3114 return 1;
3115 } else {
3116 $this->db->rollback();
3117 return -1;
3118 }
3119 }
3120
3129 public function availability($availability_id, $notrigger = 0)
3130 {
3131 global $user;
3132
3133 if ($this->statut >= self::STATUS_DRAFT) {
3134 $error = 0;
3135
3136 $this->db->begin();
3137
3138 $sql = 'UPDATE '.MAIN_DB_PREFIX.'propal';
3139 $sql .= ' SET fk_availability = '.((int) $availability_id);
3140 $sql .= ' WHERE rowid='.((int) $this->id);
3141
3142 dol_syslog(__METHOD__.' availability('.$availability_id.')', LOG_DEBUG);
3143 $resql = $this->db->query($sql);
3144 if (!$resql) {
3145 $this->errors[] = $this->db->error();
3146 $error++;
3147 }
3148
3149 if (!$error) {
3150 $this->oldcopy = clone $this;
3151 $this->availability_id = $availability_id;
3152 }
3153
3154 if (!$notrigger && empty($error)) {
3155 // Call trigger
3156 $result = $this->call_trigger('PROPAL_MODIFY', $user);
3157 if ($result < 0) {
3158 $error++;
3159 }
3160 // End call triggers
3161 }
3162
3163 if (!$error) {
3164 $this->db->commit();
3165 return 1;
3166 } else {
3167 foreach ($this->errors as $errmsg) {
3168 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
3169 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3170 }
3171 $this->db->rollback();
3172 return -1 * $error;
3173 }
3174 } else {
3175 $error_str = 'Propal status do not meet requirement '.$this->statut;
3176 dol_syslog(__METHOD__.$error_str, LOG_ERR);
3177 $this->error = $error_str;
3178 $this->errors[] = $this->error;
3179 return -2;
3180 }
3181 }
3182
3183 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3192 public function demand_reason($demand_reason_id, $notrigger = 0)
3193 {
3194 // phpcs:enable
3195 global $user;
3196
3197 if ($this->status >= self::STATUS_DRAFT) {
3198 $error = 0;
3199
3200 $this->db->begin();
3201
3202 $sql = 'UPDATE '.MAIN_DB_PREFIX.'propal';
3203 $sql .= ' SET fk_input_reason = '.((int) $demand_reason_id);
3204 $sql .= ' WHERE rowid='.((int) $this->id);
3205
3206 dol_syslog(__METHOD__.' demand_reason('.$demand_reason_id.')', LOG_DEBUG);
3207 $resql = $this->db->query($sql);
3208 if (!$resql) {
3209 $this->errors[] = $this->db->error();
3210 $error++;
3211 }
3212
3213 if (!$error) {
3214 $this->oldcopy = clone $this;
3215 $this->demand_reason_id = $demand_reason_id;
3216 }
3217
3218 if (!$notrigger && empty($error)) {
3219 // Call trigger
3220 $result = $this->call_trigger('PROPAL_MODIFY', $user);
3221 if ($result < 0) {
3222 $error++;
3223 }
3224 // End call triggers
3225 }
3226
3227 if (!$error) {
3228 $this->db->commit();
3229 return 1;
3230 } else {
3231 foreach ($this->errors as $errmsg) {
3232 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
3233 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3234 }
3235 $this->db->rollback();
3236 return -1 * $error;
3237 }
3238 } else {
3239 $error_str = 'Propal status do not meet requirement '.$this->statut;
3240 dol_syslog(__METHOD__.$error_str, LOG_ERR);
3241 $this->error = $error_str;
3242 $this->errors[] = $this->error;
3243 return -2;
3244 }
3245 }
3246
3247
3254 public function info($id)
3255 {
3256 $sql = "SELECT c.rowid, ";
3257 $sql .= " c.datec, c.date_valid as datev, c.date_signature, c.date_cloture,";
3258 $sql .= " c.fk_user_author, c.fk_user_valid, c.fk_user_signature, c.fk_user_cloture";
3259 $sql .= " FROM ".MAIN_DB_PREFIX."propal as c";
3260 $sql .= " WHERE c.rowid = ".((int) $id);
3261
3262 $result = $this->db->query($sql);
3263
3264 if ($result) {
3265 if ($this->db->num_rows($result)) {
3266 $obj = $this->db->fetch_object($result);
3267
3268 $this->id = $obj->rowid;
3269
3270 $this->date_creation = $this->db->jdate($obj->datec);
3271 $this->date_validation = $this->db->jdate($obj->datev);
3272 $this->date_signature = $this->db->jdate($obj->date_signature);
3273 $this->date_cloture = $this->db->jdate($obj->date_cloture);
3274
3275 $this->user_creation_id = $obj->fk_user_author;
3276 $this->user_validation_id = $obj->fk_user_valid;
3277
3278 if ($obj->fk_user_signature) {
3279 $user_signature = new User($this->db);
3280 $user_signature->fetch($obj->fk_user_signature);
3281 $this->user_signature = $user_signature;
3282 }
3283
3284 $this->user_closing_id = $obj->fk_user_cloture;
3285 }
3286 $this->db->free($result);
3287 } else {
3288 dol_print_error($this->db);
3289 }
3290 }
3291
3292
3299 public function getLibStatut($mode = 0)
3300 {
3301 return $this->LibStatut($this->statut, $mode);
3302 }
3303
3304 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3312 public function LibStatut($status, $mode = 1)
3313 {
3314 // phpcs:enable
3315 global $hookmanager;
3316
3317 // Init/load array of translation of status
3318 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
3319 global $langs;
3320 $langs->load("propal");
3321 $this->labelStatus[-1] = $langs->transnoentitiesnoconv("PropalStatusCanceled");
3322 $this->labelStatus[0] = $langs->transnoentitiesnoconv("PropalStatusDraft");
3323 $this->labelStatus[1] = $langs->transnoentitiesnoconv("PropalStatusValidated");
3324 $this->labelStatus[2] = $langs->transnoentitiesnoconv("PropalStatusSigned");
3325 $this->labelStatus[3] = $langs->transnoentitiesnoconv("PropalStatusNotSigned");
3326 $this->labelStatus[4] = $langs->transnoentitiesnoconv("PropalStatusBilled");
3327 $this->labelStatusShort[-1] = $langs->transnoentitiesnoconv("PropalStatusCanceledShort");
3328 $this->labelStatusShort[0] = $langs->transnoentitiesnoconv("PropalStatusDraftShort");
3329 $this->labelStatusShort[1] = $langs->transnoentitiesnoconv("PropalStatusValidatedShort");
3330 $this->labelStatusShort[2] = $langs->transnoentitiesnoconv("PropalStatusSignedShort");
3331 $this->labelStatusShort[3] = $langs->transnoentitiesnoconv("PropalStatusNotSignedShort");
3332 $this->labelStatusShort[4] = $langs->transnoentitiesnoconv("PropalStatusBilledShort");
3333 }
3334
3335 $statusType = '';
3336 if ($status == self::STATUS_CANCELED) {
3337 $statusType = 'status9';
3338 } elseif ($status == self::STATUS_DRAFT) {
3339 $statusType = 'status0';
3340 } elseif ($status == self::STATUS_VALIDATED) {
3341 $statusType = 'status1';
3342 } elseif ($status == self::STATUS_SIGNED) {
3343 $statusType = 'status4';
3344 } elseif ($status == self::STATUS_NOTSIGNED) {
3345 $statusType = 'status9';
3346 } elseif ($status == self::STATUS_BILLED) {
3347 $statusType = 'status6';
3348 }
3349
3350 $parameters = array('status' => $status, 'mode' => $mode);
3351 $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
3352
3353 if ($reshook > 0) {
3354 return $hookmanager->resPrint;
3355 }
3356
3357 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
3358 }
3359
3360
3361 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3369 public function load_board($user, $mode)
3370 {
3371 // phpcs:enable
3372 global $conf, $langs;
3373
3374 $clause = " WHERE";
3375
3376 $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.fin_validite as datefin, p.total_ht";
3377 $sql .= " FROM ".MAIN_DB_PREFIX."propal as p";
3378 $sql .= $clause." p.entity IN (".getEntity('propal').")";
3379 if ($mode == 'opened') {
3380 $sql .= " AND p.fk_statut = ".self::STATUS_VALIDATED;
3381 }
3382 if ($mode == 'signed') {
3383 $sql .= " AND p.fk_statut = ".self::STATUS_SIGNED;
3384 }
3385 // If the internal user must only see his customers, force searching by him
3386 $search_sale = 0;
3387 if (!$user->hasRight('societe', 'client', 'voir')) {
3388 $search_sale = $user->id;
3389 }
3390 // Search on sale representative
3391 if ($search_sale && $search_sale != '-1') {
3392 if ($search_sale == -2) {
3393 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
3394 } elseif ($search_sale > 0) {
3395 $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).")";
3396 }
3397 }
3398
3399 $resql = $this->db->query($sql);
3400 if ($resql) {
3401 $langs->load("propal");
3402 $now = dol_now();
3403
3404 $delay_warning = 0;
3405 $status = 0;
3406 $label = $labelShort = '';
3407 if ($mode == 'opened') {
3408 $delay_warning = $conf->propal->cloture->warning_delay;
3409 $status = self::STATUS_VALIDATED;
3410 $label = $langs->transnoentitiesnoconv("PropalsToClose");
3411 $labelShort = $langs->transnoentitiesnoconv("ToAcceptRefuse");
3412 }
3413 if ($mode == 'signed') {
3414 $delay_warning = $conf->propal->facturation->warning_delay;
3415 $status = self::STATUS_SIGNED;
3416 $label = $langs->trans("PropalsToBill"); // We set here bill but may be billed or ordered
3417 $labelShort = $langs->trans("ToBill");
3418 }
3419
3420 $response = new WorkboardResponse();
3421 $response->warning_delay = $delay_warning / 60 / 60 / 24;
3422 $response->label = $label;
3423 $response->labelShort = $labelShort;
3424 $response->url = DOL_URL_ROOT.'/comm/propal/list.php?search_status='.$status.'&mainmenu=commercial&leftmenu=propals';
3425 $response->url_late = DOL_URL_ROOT.'/comm/propal/list.php?search_status='.$status.'&mainmenu=commercial&leftmenu=propals&sortfield=p.datep&sortorder=asc';
3426 $response->img = img_object('', "propal");
3427
3428 // This assignment in condition is not a bug. It allows walking the results.
3429 while ($obj = $this->db->fetch_object($resql)) {
3430 $response->nbtodo++;
3431 $response->total += $obj->total_ht;
3432
3433 if ($mode == 'opened') {
3434 $datelimit = $this->db->jdate($obj->datefin);
3435 if ($datelimit < ($now - $delay_warning)) {
3436 $response->nbtodolate++;
3437 }
3438 }
3439 // TODO Definir regle des propales a facturer en retard
3440 // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
3441 }
3442
3443 return $response;
3444 } else {
3445 $this->error = $this->db->error();
3446 return -1;
3447 }
3448 }
3449
3450
3458 public function initAsSpecimen()
3459 {
3460 global $conf, $langs;
3461
3462 // Load array of products prodids
3463 $num_prods = 0;
3464 $prodids = array();
3465 $sql = "SELECT rowid";
3466 $sql .= " FROM ".MAIN_DB_PREFIX."product";
3467 $sql .= " WHERE entity IN (".getEntity('product').")";
3468 $sql .= $this->db->plimit(100);
3469
3470 $resql = $this->db->query($sql);
3471 if ($resql) {
3472 $num_prods = $this->db->num_rows($resql);
3473 $i = 0;
3474 while ($i < $num_prods) {
3475 $i++;
3476 $row = $this->db->fetch_row($resql);
3477 $prodids[$i] = $row[0];
3478 }
3479 }
3480
3481 // Initialise parameters
3482 $this->id = 0;
3483 $this->ref = 'SPECIMEN';
3484 $this->ref_client = 'NEMICEPS';
3485 $this->specimen = 1;
3486 $this->socid = 1;
3487 $this->date = time();
3488 $this->fin_validite = $this->date + 3600 * 24 * 30;
3489 $this->cond_reglement_id = 1;
3490 $this->cond_reglement_code = 'RECEP';
3491 $this->mode_reglement_id = 7;
3492 $this->mode_reglement_code = 'CHQ';
3493 $this->availability_id = 1;
3494 $this->availability_code = 'AV_NOW';
3495 $this->demand_reason_id = 1;
3496 $this->demand_reason_code = 'SRC_00';
3497 $this->note_public = 'This is a comment (public)';
3498 $this->note_private = 'This is a comment (private)';
3499
3500 $this->multicurrency_tx = 1;
3501 $this->multicurrency_code = $conf->currency;
3502
3503 // Lines
3504 $nbp = 5;
3505 $xnbp = 0;
3506 while ($xnbp < $nbp) {
3507 $line = new PropaleLigne($this->db);
3508 $line->desc = $langs->trans("Description")." ".$xnbp;
3509 $line->qty = 1;
3510 $line->subprice = 100;
3511 $line->price = 100;
3512 $line->tva_tx = 20;
3513 $line->localtax1_tx = 0;
3514 $line->localtax2_tx = 0;
3515 if ($xnbp == 2) {
3516 $line->total_ht = 50;
3517 $line->total_ttc = 60;
3518 $line->total_tva = 10;
3519 $line->remise_percent = 50;
3520 } else {
3521 $line->total_ht = 100;
3522 $line->total_ttc = 120;
3523 $line->total_tva = 20;
3524 $line->remise_percent = 00;
3525 }
3526
3527 if ($num_prods > 0) {
3528 $prodid = mt_rand(1, $num_prods);
3529 $line->fk_product = $prodids[$prodid];
3530 $line->product_ref = 'SPECIMEN';
3531 }
3532
3533 $this->lines[$xnbp] = $line;
3534
3535 $this->total_ht += $line->total_ht;
3536 $this->total_tva += $line->total_tva;
3537 $this->total_ttc += $line->total_ttc;
3538
3539 $xnbp++;
3540 }
3541
3542 return 1;
3543 }
3544
3550 public function loadStateBoard()
3551 {
3552 global $user;
3553
3554 $this->nb = array();
3555 $clause = "WHERE";
3556
3557 $sql = "SELECT count(p.rowid) as nb";
3558 $sql .= " FROM ".MAIN_DB_PREFIX."propal as p";
3559 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
3560 $sql .= " ".$clause." p.entity IN (".getEntity('propal').")";
3561
3562 // If the internal user must only see his customers, force searching by him
3563 $search_sale = 0;
3564 if (!$user->hasRight('societe', 'client', 'voir')) {
3565 $search_sale = $user->id;
3566 }
3567 // Search on sale representative
3568 if ($search_sale && $search_sale != '-1') {
3569 if ($search_sale == -2) {
3570 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
3571 } elseif ($search_sale > 0) {
3572 $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).")";
3573 }
3574 }
3575
3576 $resql = $this->db->query($sql);
3577 if ($resql) {
3578 // This assignment in condition is not a bug. It allows walking the results.
3579 while ($obj = $this->db->fetch_object($resql)) {
3580 $this->nb["proposals"] = $obj->nb;
3581 }
3582 $this->db->free($resql);
3583 return 1;
3584 } else {
3585 dol_print_error($this->db);
3586 $this->error = $this->db->error();
3587 return -1;
3588 }
3589 }
3590
3591
3599 public function getNextNumRef($soc)
3600 {
3601 global $conf, $langs;
3602 $langs->load("propal");
3603
3604 $classname = getDolGlobalString('PROPALE_ADDON');
3605
3606 if (!empty($classname)) {
3607 $mybool = false;
3608
3609 $file = $classname.".php";
3610
3611 // Include file with class
3612 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
3613 foreach ($dirmodels as $reldir) {
3614 $dir = dol_buildpath($reldir."core/modules/propale/");
3615
3616 // Load file with numbering class (if found)
3617 $mybool = ((bool) @include_once $dir.$file) || $mybool;
3618 }
3619
3620 if (!$mybool) {
3621 dol_print_error(null, "Failed to include file ".$file);
3622 return '';
3623 }
3624
3625 $obj = new $classname();
3626 $numref = "";
3627 $numref = $obj->getNextValue($soc, $this);
3628
3629 if ($numref != "") {
3630 return $numref;
3631 } else {
3632 $this->error = $obj->error;
3633 //dol_print_error($db,"Propale::getNextNumRef ".$obj->error);
3634 return "";
3635 }
3636 } else {
3637 $langs->load("errors");
3638 print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("Proposal"));
3639 return "";
3640 }
3641 }
3642
3649 public function getTooltipContentArray($params)
3650 {
3651 global $conf, $langs, $user;
3652
3653 $langs->load('propal');
3654 $datas = [];
3655 $nofetch = !empty($params['nofetch']);
3656
3657 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3658 return ['optimize' => $langs->trans("Proposal")];
3659 }
3660 if ($user->hasRight('propal', 'lire')) {
3661 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("Proposal").'</u>';
3662 if (isset($this->status)) {
3663 $datas['status'] = ' '.$this->getLibStatut(5);
3664 }
3665 if (!empty($this->ref)) {
3666 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
3667 }
3668 if (!$nofetch) {
3669 $langs->load('companies');
3670 if (empty($this->thirdparty)) {
3671 $this->fetch_thirdparty();
3672 }
3673 $datas['customer'] = '<br><b>'.$langs->trans('Customer').':</b> '.$this->thirdparty->getNomUrl(1, '', 0, 1);
3674 }
3675 if (!empty($this->ref_customer)) {
3676 $datas['refcustomer'] = '<br><b>'.$langs->trans('RefCustomer').':</b> '.$this->ref_customer;
3677 }
3678 if (!$nofetch) {
3679 $langs->load('project');
3680 if (is_null($this->project) || (is_object($this->project) && $this->project->isEmpty())) {
3681 $res = $this->fetch_project();
3682 if ($res > 0 && $this->project instanceof Project) {
3683 $datas['project'] = '<br><b>'.$langs->trans('Project').':</b> '.$this->project->getNomUrl(1, '', 0, 1);
3684 }
3685 }
3686 }
3687 if (!empty($this->total_ht)) {
3688 $datas['amountht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3689 }
3690 if (!empty($this->total_tva)) {
3691 $datas['vat'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
3692 }
3693 if (!empty($this->total_ttc)) {
3694 $datas['amountttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3695 }
3696 if (!empty($this->date)) {
3697 $datas['date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
3698 }
3699 if (!empty($this->delivery_date)) {
3700 $datas['deliverydate'] = '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
3701 }
3702 }
3703
3704 return $datas;
3705 }
3706
3718 public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = -1)
3719 {
3720 global $langs, $conf, $user, $hookmanager;
3721
3722 if (!empty($conf->dol_no_mouse_hover)) {
3723 $notooltip = 1; // Force disable tooltips
3724 }
3725
3726 $result = '';
3727 $params = [
3728 'id' => $this->id,
3729 'objecttype' => $this->element,
3730 'option' => $option,
3731 'nofetch' => 1,
3732 ];
3733 $classfortooltip = 'classfortooltip';
3734 $dataparams = '';
3735 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
3736 $classfortooltip = 'classforajaxtooltip';
3737 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
3738 $label = '';
3739 } else {
3740 $label = implode($this->getTooltipContentArray($params));
3741 }
3742
3743 $url = '';
3744 if ($user->hasRight('propal', 'lire')) {
3745 if ($option == '') {
3746 $url = DOL_URL_ROOT.'/comm/propal/card.php?id='.$this->id.$get_params;
3747 } elseif ($option == 'compta') { // deprecated
3748 $url = DOL_URL_ROOT.'/comm/propal/card.php?id='.$this->id.$get_params;
3749 } elseif ($option == 'expedition') {
3750 $url = DOL_URL_ROOT.'/expedition/propal.php?id='.$this->id.$get_params;
3751 } elseif ($option == 'document') {
3752 $url = DOL_URL_ROOT.'/comm/propal/document.php?id='.$this->id.$get_params;
3753 }
3754
3755 if ($option != 'nolink') {
3756 // Add param to save lastsearch_values or not
3757 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
3758 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
3759 $add_save_lastsearch_values = 1;
3760 }
3761 if ($add_save_lastsearch_values) {
3762 $url .= '&save_lastsearch_values=1';
3763 }
3764 }
3765 }
3766
3767 $linkclose = '';
3768 if (empty($notooltip) && $user->hasRight('propal', 'lire')) {
3769 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3770 $label = $langs->trans("Proposal");
3771 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
3772 }
3773 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
3774 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
3775 }
3776
3777 $linkstart = '<a href="'.$url.'"';
3778 $linkstart .= $linkclose.'>';
3779 $linkend = '</a>';
3780
3781 $result .= $linkstart;
3782 if ($withpicto) {
3783 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
3784 }
3785 if ($withpicto != 2) {
3786 $result .= $this->ref;
3787 }
3788 $result .= $linkend;
3789
3790 if ($addlinktonotes >= 0) {
3791 $txttoshow = '';
3792
3793 if ($addlinktonotes == 0) {
3794 if (!empty($this->note_private) || !empty($this->note_public)) {
3795 $txttoshow = $langs->trans('ViewPrivateNote');
3796 }
3797 } elseif ($addlinktonotes == 1) {
3798 if (!empty($this->note_private)) {
3799 $txttoshow .= ($user->socid > 0 ? '' : dol_string_nohtmltag($this->note_private, 1));
3800 }
3801 } elseif ($addlinktonotes == 2) {
3802 if (!empty($this->note_public)) {
3803 $txttoshow .= dol_string_nohtmltag($this->note_public, 1);
3804 }
3805 } elseif ($addlinktonotes == 3) {
3806 if ($user->socid > 0) {
3807 if (!empty($this->note_public)) {
3808 $txttoshow .= dol_string_nohtmltag($this->note_public, 1);
3809 }
3810 } else {
3811 if (!empty($this->note_public)) {
3812 $txttoshow .= dol_string_nohtmltag($this->note_public, 1);
3813 }
3814 if (!empty($this->note_private)) {
3815 if (!empty($txttoshow)) {
3816 $txttoshow .= '<br><br>';
3817 }
3818 $txttoshow .= dol_string_nohtmltag($this->note_private, 1);
3819 }
3820 }
3821 }
3822
3823 if ($txttoshow) {
3824 $result .= ' <span class="note inline-block">';
3825 $result .= '<a href="'.DOL_URL_ROOT.'/comm/propal/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($txttoshow).'">';
3826 $result .= img_picto('', 'note');
3827 $result .= '</a>';
3828 $result .= '</span>';
3829 }
3830 }
3831
3832 global $action;
3833 $hookmanager->initHooks(array($this->element . 'dao'));
3834 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
3835 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3836 if ($reshook > 0) {
3837 $result = $hookmanager->resPrint;
3838 } else {
3839 $result .= $hookmanager->resPrint;
3840 }
3841 return $result;
3842 }
3843
3850 public function getLinesArray($sqlforgedfilters = '')
3851 {
3852 return $this->fetch_lines(0, 0, $sqlforgedfilters);
3853 }
3854
3866 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3867 {
3868 global $conf, $langs;
3869
3870 $langs->load("propale");
3871 $outputlangs->load("products");
3872
3873 if (!dol_strlen($modele)) {
3874 $modele = 'azur';
3875
3876 if ($this->model_pdf) {
3877 $modele = $this->model_pdf;
3878 } elseif (getDolGlobalString('PROPALE_ADDON_PDF')) {
3879 $modele = getDolGlobalString('PROPALE_ADDON_PDF');
3880 }
3881 }
3882
3883 $modelpath = "core/modules/propale/doc/";
3884
3885 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3886 }
3887
3896 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3897 {
3898 $tables = array(
3899 'propal'
3900 );
3901
3902 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3903 }
3904
3913 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3914 {
3915 $tables = array(
3916 'propaldet'
3917 );
3918
3919 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3920 }
3921
3929 public function getKanbanView($option = '', $arraydata = null)
3930 {
3931 global $langs;
3932
3933 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3934
3935 $return = '<div class="box-flex-item box-flex-grow-zero">';
3936 $return .= '<div class="info-box info-box-sm">';
3937 $return .= '<div class="info-box-icon bg-infobox-action">';
3938 $return .= img_picto('', $this->picto);
3939 $return .= '</div>';
3940 $return .= '<div class="info-box-content">';
3941 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
3942 if ($selected >= 0) {
3943 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3944 }
3945 if (!empty($arraydata['projectlink'])) {
3946 $return .= '<span class="info-box-ref"> | '.$arraydata['projectlink'].'</span>';
3947 }
3948 $return .= '<br>';
3949 if (property_exists($this, 'thirdparty') && is_object($this->thirdparty)) {
3950 $return .= '<div class="info-box-ref tdoverflowmax150">'.$this->thirdparty->getNomUrl(1).'</div>';
3951 }
3952 if (property_exists($this, 'total_ht')) {
3953 $return .= '<span class="info-box-label amount" title="'.$langs->trans("AmountHT").'">'.price($this->total_ht).'</span>';
3954 }
3955 if (!empty($arraydata['authorlink'])) {
3956 $return .= ' &nbsp; <span class="info-box-label">'.$arraydata['authorlink'].'</span>';
3957 }
3958 if (method_exists($this, 'getLibStatut')) {
3959 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
3960 }
3961 $return .= '</div>';
3962 $return .= '</div>';
3963 $return .= '</div>';
3964 return $return;
3965 }
3966}
3967
3972{
3976 public $element = 'propaldet';
3977
3981 public $table_element = 'propaldet';
3982
3986 public $parent_element = 'propal';
3987
3991 public $fk_parent_attribute = 'fk_propal';
3992
3993 public $oldline;
3994
3995 // From llx_propaldet
3996 public $fk_propal;
3997 public $fk_parent_line;
3998 public $desc; // Description ligne
3999 public $fk_product; // Id produit predefini
4010 public $product_type = Product::TYPE_PRODUCT;
4011
4012 public $qty;
4013
4014 public $tva_tx;
4015 public $vat_src_code;
4016
4021 public $subprice;
4022 public $remise_percent;
4023 public $fk_remise_except;
4024
4025 public $rang = 0;
4026
4027 public $fk_fournprice;
4028 public $pa_ht;
4029 public $marge_tx;
4030 public $marque_tx;
4031
4038 public $special_code; // Tag for special lines (exclusive tags)
4039
4040 public $info_bits = 0; // Some other info:
4041 // Bit 0: 0 si TVA normal - 1 if TVA NPR
4042 // Bit 1: 0 ligne normal - 1 if line with fixed discount
4043
4044 public $total_ht; // Total HT de la ligne toute quantite et incluant la remise ligne
4045 public $total_tva; // Total TVA de la ligne toute quantite et incluant la remise ligne
4046 public $total_ttc; // Total TTC de la ligne toute quantite et incluant la remise ligne
4047
4052 public $remise;
4057 public $price;
4058
4059 // From llx_product
4064 public $ref;
4069 public $product_ref;
4074 public $libelle;
4079 public $label;
4084 public $product_label;
4089 public $product_desc;
4090
4095 public $product_tobatch;
4096
4101 public $product_barcode;
4102
4103 public $localtax1_tx; // Local tax 1
4104 public $localtax2_tx; // Local tax 2
4105 public $localtax1_type; // Local tax 1 type
4106 public $localtax2_type; // Local tax 2 type
4107 public $total_localtax1; // Line total local tax 1
4108 public $total_localtax2; // Line total local tax 2
4109
4110 public $date_start;
4111 public $date_end;
4112
4113 public $skip_update_total; // Skip update price total for special lines
4114
4115 // Multicurrency
4116 public $fk_multicurrency;
4117 public $multicurrency_code;
4118 public $multicurrency_subprice;
4119 public $multicurrency_total_ht;
4120 public $multicurrency_total_tva;
4121 public $multicurrency_total_ttc;
4122
4123
4129 public function __construct($db)
4130 {
4131 $this->db = $db;
4132 }
4133
4140 public function fetch($rowid)
4141 {
4142 $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,';
4143 $sql .= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice,';
4144 $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,';
4145 $sql .= ' pd.fk_unit,';
4146 $sql .= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
4147 $sql .= ' pd.fk_multicurrency, pd.multicurrency_code, pd.multicurrency_subprice, pd.multicurrency_total_ht, pd.multicurrency_total_tva, pd.multicurrency_total_ttc,';
4148 $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
4149 $sql .= ' pd.date_start, pd.date_end, pd.product_type';
4150 $sql .= ' FROM '.MAIN_DB_PREFIX.'propaldet as pd';
4151 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
4152 $sql .= ' WHERE pd.rowid = '.((int) $rowid);
4153
4154 $result = $this->db->query($sql);
4155 if ($result) {
4156 $objp = $this->db->fetch_object($result);
4157
4158 if ($objp) {
4159 $this->id = $objp->rowid;
4160 $this->rowid = $objp->rowid; // deprecated
4161 $this->fk_propal = $objp->fk_propal;
4162 $this->fk_parent_line = $objp->fk_parent_line;
4163 $this->label = $objp->custom_label;
4164 $this->desc = $objp->description;
4165 $this->qty = $objp->qty;
4166 $this->price = $objp->price; // deprecated
4167 $this->subprice = $objp->subprice;
4168 $this->vat_src_code = $objp->vat_src_code;
4169 $this->tva_tx = $objp->tva_tx;
4170 $this->remise = $objp->remise; // deprecated
4171 $this->remise_percent = $objp->remise_percent;
4172 $this->fk_remise_except = $objp->fk_remise_except;
4173 $this->fk_product = $objp->fk_product;
4174 $this->info_bits = $objp->info_bits;
4175
4176 $this->total_ht = $objp->total_ht;
4177 $this->total_tva = $objp->total_tva;
4178 $this->total_ttc = $objp->total_ttc;
4179
4180 $this->fk_fournprice = $objp->fk_fournprice;
4181
4182 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
4183 $this->pa_ht = $marginInfos[0];
4184 $this->marge_tx = $marginInfos[1];
4185 $this->marque_tx = $marginInfos[2];
4186
4187 $this->special_code = $objp->special_code;
4188 $this->product_type = $objp->product_type;
4189 $this->rang = $objp->rang;
4190
4191 $this->ref = $objp->product_ref; // deprecated
4192 $this->product_ref = $objp->product_ref;
4193 $this->libelle = $objp->product_label; // deprecated
4194 $this->product_label = $objp->product_label;
4195 $this->product_desc = $objp->product_desc;
4196 $this->fk_unit = $objp->fk_unit;
4197
4198 $this->date_start = $this->db->jdate($objp->date_start);
4199 $this->date_end = $this->db->jdate($objp->date_end);
4200
4201 // Multicurrency
4202 $this->fk_multicurrency = $objp->fk_multicurrency;
4203 $this->multicurrency_code = $objp->multicurrency_code;
4204 $this->multicurrency_subprice = $objp->multicurrency_subprice;
4205 $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
4206 $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
4207 $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
4208
4209 $this->fetch_optionals();
4210
4211 $this->db->free($result);
4212
4213 return 1;
4214 } else {
4215 return 0;
4216 }
4217 } else {
4218 return -1;
4219 }
4220 }
4221
4228 public function insert($notrigger = 0)
4229 {
4230 global $conf, $user;
4231
4232 $error = 0;
4233
4234 dol_syslog(get_class($this)."::insert rang=".$this->rang);
4235
4236 $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'.
4237
4238 // Clean parameters
4239 if (empty($this->tva_tx)) {
4240 $this->tva_tx = 0;
4241 }
4242 if (empty($this->localtax1_tx)) {
4243 $this->localtax1_tx = 0;
4244 }
4245 if (empty($this->localtax2_tx)) {
4246 $this->localtax2_tx = 0;
4247 }
4248 if (empty($this->localtax1_type)) {
4249 $this->localtax1_type = 0;
4250 }
4251 if (empty($this->localtax2_type)) {
4252 $this->localtax2_type = 0;
4253 }
4254 if (empty($this->total_localtax1)) {
4255 $this->total_localtax1 = 0;
4256 }
4257 if (empty($this->total_localtax2)) {
4258 $this->total_localtax2 = 0;
4259 }
4260 if (empty($this->rang)) {
4261 $this->rang = 0;
4262 }
4263 if (empty($this->remise_percent) || !is_numeric($this->remise_percent)) {
4264 $this->remise_percent = 0;
4265 }
4266 if (empty($this->info_bits)) {
4267 $this->info_bits = 0;
4268 }
4269 if (empty($this->special_code)) {
4270 $this->special_code = 0;
4271 }
4272 if (empty($this->fk_parent_line)) {
4273 $this->fk_parent_line = 0;
4274 }
4275 if (empty($this->fk_fournprice)) {
4276 $this->fk_fournprice = 0;
4277 }
4278 if (!is_numeric($this->qty)) {
4279 $this->qty = 0;
4280 }
4281 if (empty($this->pa_ht)) {
4282 $this->pa_ht = 0;
4283 }
4284 if (empty($this->multicurrency_subprice)) {
4285 $this->multicurrency_subprice = 0;
4286 }
4287 if (empty($this->multicurrency_total_ht)) {
4288 $this->multicurrency_total_ht = 0;
4289 }
4290 if (empty($this->multicurrency_total_tva)) {
4291 $this->multicurrency_total_tva = 0;
4292 }
4293 if (empty($this->multicurrency_total_ttc)) {
4294 $this->multicurrency_total_ttc = 0;
4295 }
4296
4297 // if buy price not defined, define buyprice as configured in margin admin
4298 if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
4299 if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0) {
4300 return $result;
4301 } else {
4302 $this->pa_ht = $result;
4303 }
4304 }
4305
4306 // Check parameters
4307 if ($this->product_type < 0) {
4308 return -1;
4309 }
4310
4311 $this->db->begin();
4312
4313 // Insert line into database
4314 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'propaldet';
4315 $sql .= ' (fk_propal, fk_parent_line, label, description, fk_product, product_type,';
4316 $sql .= ' fk_remise_except, qty, vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
4317 $sql .= ' subprice, remise_percent, ';
4318 $sql .= ' info_bits, ';
4319 $sql .= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
4320 $sql .= ' fk_unit,';
4321 $sql .= ' date_start, date_end';
4322 $sql .= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc)';
4323 $sql .= " VALUES (".$this->fk_propal.",";
4324 $sql .= " ".($this->fk_parent_line > 0 ? "'".$this->db->escape($this->fk_parent_line)."'" : "null").",";
4325 $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
4326 $sql .= " '".$this->db->escape($this->desc)."',";
4327 $sql .= " ".($this->fk_product ? "'".$this->db->escape($this->fk_product)."'" : "null").",";
4328 $sql .= " '".$this->db->escape($this->product_type)."',";
4329 $sql .= " ".($this->fk_remise_except ? "'".$this->db->escape($this->fk_remise_except)."'" : "null").",";
4330 $sql .= " ".price2num($this->qty, 'MS').",";
4331 $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
4332 $sql .= " ".price2num($this->tva_tx).",";
4333 $sql .= " ".price2num($this->localtax1_tx).",";
4334 $sql .= " ".price2num($this->localtax2_tx).",";
4335 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
4336 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
4337 $sql .= " ".(price2num($this->subprice) !== '' ? price2num($this->subprice, 'MU') : "null").",";
4338 $sql .= " ".price2num($this->remise_percent, 3).",";
4339 $sql .= " ".(isset($this->info_bits) ? ((int) $this->info_bits) : "null").",";
4340 $sql .= " ".price2num($this->total_ht, 'MT').",";
4341 $sql .= " ".price2num($this->total_tva, 'MT').",";
4342 $sql .= " ".price2num($this->total_localtax1, 'MT').",";
4343 $sql .= " ".price2num($this->total_localtax2, 'MT').",";
4344 $sql .= " ".price2num($this->total_ttc, 'MT').",";
4345 $sql .= " ".(!empty($this->fk_fournprice) ? "'".$this->db->escape($this->fk_fournprice)."'" : "null").",";
4346 $sql .= " ".(isset($this->pa_ht) ? "'".price2num($this->pa_ht)."'" : "null").",";
4347 $sql .= ' '.((int) $this->special_code).',';
4348 $sql .= ' '.((int) $this->rang).',';
4349 $sql .= ' '.(empty($this->fk_unit) ? 'NULL' : ((int) $this->fk_unit)).',';
4350 $sql .= " ".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null").',';
4351 $sql .= " ".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
4352 $sql .= ", ".($this->fk_multicurrency > 0 ? ((int) $this->fk_multicurrency) : 'null');
4353 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
4354 $sql .= ", ".price2num($this->multicurrency_subprice, 'CU');
4355 $sql .= ", ".price2num($this->multicurrency_total_ht, 'CT');
4356 $sql .= ", ".price2num($this->multicurrency_total_tva, 'CT');
4357 $sql .= ", ".price2num($this->multicurrency_total_ttc, 'CT');
4358 $sql .= ')';
4359
4360 dol_syslog(get_class($this).'::insert', LOG_DEBUG);
4361 $resql = $this->db->query($sql);
4362 if ($resql) {
4363 $this->rowid = $this->db->last_insert_id(MAIN_DB_PREFIX.'propaldet');
4364
4365 if (!$error) {
4366 $this->id = $this->rowid;
4367 $result = $this->insertExtraFields();
4368 if ($result < 0) {
4369 $error++;
4370 }
4371 }
4372
4373 if (!$error && !$notrigger) {
4374 // Call trigger
4375 $result = $this->call_trigger('LINEPROPAL_INSERT', $user);
4376 if ($result < 0) {
4377 $this->db->rollback();
4378 return -1;
4379 }
4380 // End call triggers
4381 }
4382
4383 $this->db->commit();
4384 return 1;
4385 } else {
4386 $this->error = $this->db->error()." sql=".$sql;
4387 $this->db->rollback();
4388 return -1;
4389 }
4390 }
4391
4399 public function delete(User $user, $notrigger = 0)
4400 {
4401 global $conf;
4402
4403 $error = 0;
4404 $this->db->begin();
4405
4406 if (!$notrigger) {
4407 // Call trigger
4408 $result = $this->call_trigger('LINEPROPAL_DELETE', $user);
4409 if ($result < 0) {
4410 $error++;
4411 }
4412 }
4413 // End call triggers
4414
4415 if (!$error) {
4416 $sql = "DELETE FROM " . MAIN_DB_PREFIX . "propaldet WHERE rowid = " . ((int) $this->rowid);
4417 dol_syslog("PropaleLigne::delete", LOG_DEBUG);
4418 if ($this->db->query($sql)) {
4419 // Remove extrafields
4420 if (!$error) {
4421 $this->id = $this->rowid;
4422 $result = $this->deleteExtraFields();
4423 if ($result < 0) {
4424 $error++;
4425 dol_syslog(get_class($this) . "::delete error -4 " . $this->error, LOG_ERR);
4426 }
4427 }
4428 } else {
4429 $this->error = $this->db->error() . " sql=" . $sql;
4430 $error++;
4431 }
4432 }
4433
4434 if ($error) {
4435 $this->db->rollback();
4436 return -1;
4437 } else {
4438 $this->db->commit();
4439 return 1;
4440 }
4441 }
4442
4449 public function update($notrigger = 0)
4450 {
4451 global $conf, $user;
4452
4453 $error = 0;
4454
4455 $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'.
4456
4457 if (empty($this->id) && !empty($this->rowid)) {
4458 $this->id = $this->rowid;
4459 }
4460
4461 // Clean parameters
4462 if (empty($this->tva_tx)) {
4463 $this->tva_tx = 0;
4464 }
4465 if (empty($this->localtax1_tx)) {
4466 $this->localtax1_tx = 0;
4467 }
4468 if (empty($this->localtax2_tx)) {
4469 $this->localtax2_tx = 0;
4470 }
4471 if (empty($this->total_localtax1)) {
4472 $this->total_localtax1 = 0;
4473 }
4474 if (empty($this->total_localtax2)) {
4475 $this->total_localtax2 = 0;
4476 }
4477 if (empty($this->localtax1_type)) {
4478 $this->localtax1_type = 0;
4479 }
4480 if (empty($this->localtax2_type)) {
4481 $this->localtax2_type = 0;
4482 }
4483 if (empty($this->marque_tx)) {
4484 $this->marque_tx = 0;
4485 }
4486 if (empty($this->marge_tx)) {
4487 $this->marge_tx = 0;
4488 }
4489 if (empty($this->price)) {
4490 $this->price = 0; // TODO A virer
4491 }
4492 if (empty($this->remise_percent)) {
4493 $this->remise_percent = 0;
4494 }
4495 if (empty($this->info_bits)) {
4496 $this->info_bits = 0;
4497 }
4498 if (empty($this->special_code)) {
4499 $this->special_code = 0;
4500 }
4501 if (empty($this->fk_parent_line)) {
4502 $this->fk_parent_line = 0;
4503 }
4504 if (empty($this->fk_fournprice)) {
4505 $this->fk_fournprice = 0;
4506 }
4507 if (empty($this->subprice)) {
4508 $this->subprice = 0;
4509 }
4510 if (empty($this->pa_ht)) {
4511 $this->pa_ht = 0;
4512 }
4513
4514 // if buy price not defined, define buyprice as configured in margin admin
4515 if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
4516 if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0) {
4517 return $result;
4518 } else {
4519 $this->pa_ht = $result;
4520 }
4521 }
4522
4523 $this->db->begin();
4524
4525 // Mise a jour ligne en base
4526 $sql = "UPDATE ".MAIN_DB_PREFIX."propaldet SET";
4527 $sql .= " description='".$this->db->escape($this->desc)."'";
4528 $sql .= ", label=".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
4529 $sql .= ", product_type=".$this->product_type;
4530 $sql .= ", vat_src_code = '".(empty($this->vat_src_code) ? '' : $this->vat_src_code)."'";
4531 $sql .= ", tva_tx='".price2num($this->tva_tx)."'";
4532 $sql .= ", localtax1_tx=".price2num($this->localtax1_tx);
4533 $sql .= ", localtax2_tx=".price2num($this->localtax2_tx);
4534 $sql .= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4535 $sql .= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4536 $sql .= ", qty='".price2num($this->qty)."'";
4537 $sql .= ", subprice=".price2num($this->subprice);
4538 $sql .= ", remise_percent=".price2num($this->remise_percent);
4539 $sql .= ", price=".(float) price2num($this->price); // TODO A virer
4540 $sql .= ", remise=".(float) price2num($this->remise); // TODO A virer
4541 $sql .= ", info_bits='".$this->db->escape($this->info_bits)."'";
4542 if (empty($this->skip_update_total)) {
4543 $sql .= ", total_ht=".price2num($this->total_ht);
4544 $sql .= ", total_tva=".price2num($this->total_tva);
4545 $sql .= ", total_ttc=".price2num($this->total_ttc);
4546 $sql .= ", total_localtax1=".price2num($this->total_localtax1);
4547 $sql .= ", total_localtax2=".price2num($this->total_localtax2);
4548 }
4549 $sql .= ", fk_product_fournisseur_price=".(!empty($this->fk_fournprice) ? "'".$this->db->escape($this->fk_fournprice)."'" : "null");
4550 $sql .= ", buy_price_ht=".price2num($this->pa_ht);
4551 $sql .= ", special_code=".((int) $this->special_code);
4552 $sql .= ", fk_parent_line=".($this->fk_parent_line > 0 ? (int) $this->fk_parent_line : "null");
4553 if (!empty($this->rang)) {
4554 $sql .= ", rang=".((int) $this->rang);
4555 }
4556 $sql .= ", date_start=".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null");
4557 $sql .= ", date_end=".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
4558 $sql .= ", fk_unit=".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4559
4560 // Multicurrency
4561 $sql .= ", multicurrency_subprice=".price2num($this->multicurrency_subprice);
4562 $sql .= ", multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
4563 $sql .= ", multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
4564 $sql .= ", multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
4565
4566 $sql .= " WHERE rowid = ".((int) $this->id);
4567
4568 dol_syslog(get_class($this)."::update", LOG_DEBUG);
4569 $resql = $this->db->query($sql);
4570 if ($resql) {
4571 if (!$error) {
4572 $result = $this->insertExtraFields();
4573 if ($result < 0) {
4574 $error++;
4575 }
4576 }
4577
4578 if (!$error && !$notrigger) {
4579 // Call trigger
4580 $result = $this->call_trigger('LINEPROPAL_MODIFY', $user);
4581 if ($result < 0) {
4582 $this->db->rollback();
4583 return -1;
4584 }
4585 // End call triggers
4586 }
4587
4588 $this->db->commit();
4589 return 1;
4590 } else {
4591 $this->error = $this->db->error();
4592 $this->db->rollback();
4593 return -2;
4594 }
4595 }
4596
4597 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4604 public function update_total()
4605 {
4606 // phpcs:enable
4607 $this->db->begin();
4608
4609 // Mise a jour ligne en base
4610 $sql = "UPDATE ".MAIN_DB_PREFIX."propaldet SET";
4611 $sql .= " total_ht=".price2num($this->total_ht, 'MT');
4612 $sql .= ",total_tva=".price2num($this->total_tva, 'MT');
4613 $sql .= ",total_ttc=".price2num($this->total_ttc, 'MT');
4614 $sql .= " WHERE rowid = ".((int) $this->rowid);
4615
4616 dol_syslog("PropaleLigne::update_total", LOG_DEBUG);
4617
4618 $resql = $this->db->query($sql);
4619 if ($resql) {
4620 $this->db->commit();
4621 return 1;
4622 } else {
4623 $this->error = $this->db->error();
4624 $this->db->rollback();
4625 return -2;
4626 }
4627 }
4628}
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:636
$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:1991
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:1991