dolibarr 21.0.0-alpha
propal.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2002-2004 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2004 Eric Seigne <eric.seigne@ryxeo.com>
4 * Copyright (C) 2004-2011 Laurent Destailleur <eldy@users.sourceforge.net>
5 * Copyright (C) 2005 Marc Barilley <marc@ocebo.com>
6 * Copyright (C) 2005-2013 Regis Houssin <regis.houssin@inodbox.com>
7 * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
8 * Copyright (C) 2008 Raphael Bertrand <raphael.bertrand@resultic.fr>
9 * Copyright (C) 2010-2020 Juanjo Menent <jmenent@2byte.es>
10 * Copyright (C) 2010-2022 Philippe Grand <philippe.grand@atoo-net.com>
11 * Copyright (C) 2012-2014 Christophe Battarel <christophe.battarel@altairis.fr>
12 * Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
13 * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
14 * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
15 * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
16 * Copyright (C) 2018-2024 Frédéric France <frederic.france@free.fr>
17 * Copyright (C) 2018 Ferran Marcet <fmarcet@2byte.es>
18 * Copyright (C) 2022 ATM Consulting <contact@atm-consulting.fr>
19 * Copyright (C) 2022 OpenDSI <support@open-dsi.fr>
20 * Copyright (C) 2022 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
21 * Copyright (C) 2023 William Mead <william.mead@manchenumerique.fr>
22 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
23 *
24 * This program is free software; you can redistribute it and/or modify
25 * it under the terms of the GNU General Public License as published by
26 * the Free Software Foundation; either version 3 of the License, or
27 * (at your option) any later version.
28 *
29 * This program is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 * GNU General Public License for more details.
33 *
34 * You should have received a copy of the GNU General Public License
35 * along with this program. If not, see <https://www.gnu.org/licenses/>.
36 */
37
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
156 public $datev;
157
161 public $date_validation;
162
166 public $date_signature;
167
171 public $user_signature;
172
176 public $date;
177
182 public $datep;
183
187 public $delivery_date; // Date expected of shipment (date starting shipment, not the reception that occurs some days after)
188
189
193 public $fin_validite;
194
198 public $user_author_id;
199
204 public $price;
205
210 public $tva;
215 public $total;
216
217 public $cond_reglement_code; // code
218 public $cond_reglement; // label
219 public $cond_reglement_doc; // label doc
220
221 public $mode_reglement_code; // code
222 public $mode_reglement; // label
223
224 public $deposit_percent;
225
230 public $fk_address;
231
232 public $address_type;
233 public $address;
234
238 public $availability_id;
239
245 public $fk_availability;
246
250 public $availability_code;
251
255 public $availability;
256
257 public $duree_validite;
258
262 public $demand_reason_id;
263
267 public $demand_reason_code;
268
272 public $demand_reason;
273
277 public $warehouse_id;
278
282 public $lines = array();
283
287 public $line;
288
289 public $labelStatus = array();
290 public $labelStatusShort = array();
291
292
317 // BEGIN MODULEBUILDER PROPERTIES
321 public $fields = array(
322 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10),
323 'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 15, 'index' => 1),
324 'ref' => array('type' => 'varchar(30)', 'label' => 'Ref', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'showoncombobox' => 1, 'position' => 20),
325 'ref_client' => array('type' => 'varchar(255)', 'label' => 'RefCustomer', 'enabled' => 1, 'visible' => -1, 'position' => 22),
326 'ref_ext' => array('type' => 'varchar(255)', 'label' => 'RefExt', 'enabled' => 1, 'visible' => 0, 'position' => 40),
327 'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'enabled' => 'isModEnabled("societe")', 'visible' => -1, 'position' => 23),
328 '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),
329 'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 25),
330 'datec' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -1, 'position' => 55),
331 'datep' => array('type' => 'date', 'label' => 'Date', 'enabled' => 1, 'visible' => -1, 'position' => 60),
332 'fin_validite' => array('type' => 'datetime', 'label' => 'DateEnd', 'enabled' => 1, 'visible' => -1, 'position' => 65),
333 'date_valid' => array('type' => 'datetime', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -1, 'position' => 70),
334 'date_cloture' => array('type' => 'datetime', 'label' => 'DateClosing', 'enabled' => 1, 'visible' => -1, 'position' => 75),
335 'fk_user_author' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'Fk user author', 'enabled' => 1, 'visible' => -1, 'position' => 80),
336 'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'position' => 85),
337 'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'enabled' => 1, 'visible' => -1, 'position' => 90),
338 'fk_user_cloture' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'Fk user cloture', 'enabled' => 1, 'visible' => -1, 'position' => 95),
339 'price' => array('type' => 'double', 'label' => 'Price', 'enabled' => 1, 'visible' => -1, 'position' => 105),
340 'total_ht' => array('type' => 'double(24,8)', 'label' => 'TotalHT', 'enabled' => 1, 'visible' => -1, 'position' => 125, 'isameasure' => 1),
341 'total_tva' => array('type' => 'double(24,8)', 'label' => 'VAT', 'enabled' => 1, 'visible' => -1, 'position' => 130, 'isameasure' => 1),
342 'localtax1' => array('type' => 'double(24,8)', 'label' => 'LocalTax1', 'enabled' => 1, 'visible' => -1, 'position' => 135, 'isameasure' => 1),
343 'localtax2' => array('type' => 'double(24,8)', 'label' => 'LocalTax2', 'enabled' => 1, 'visible' => -1, 'position' => 140, 'isameasure' => 1),
344 'total_ttc' => array('type' => 'double(24,8)', 'label' => 'TotalTTC', 'enabled' => 1, 'visible' => -1, 'position' => 145, 'isameasure' => 1),
345 'fk_account' => array('type' => 'integer', 'label' => 'BankAccount', 'enabled' => 'isModEnabled("bank")', 'visible' => -1, 'position' => 150),
346 'fk_currency' => array('type' => 'varchar(3)', 'label' => 'Currency', 'enabled' => 1, 'visible' => -1, 'position' => 155),
347 'fk_cond_reglement' => array('type' => 'integer', 'label' => 'PaymentTerm', 'enabled' => 1, 'visible' => -1, 'position' => 160),
348 'deposit_percent' => array('type' => 'varchar(63)', 'label' => 'DepositPercent', 'enabled' => 1, 'visible' => -1, 'position' => 161),
349 'fk_mode_reglement' => array('type' => 'integer', 'label' => 'PaymentMode', 'enabled' => 1, 'visible' => -1, 'position' => 165),
350 'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 170),
351 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 175),
352 'model_pdf' => array('type' => 'varchar(255)', 'label' => 'PDFTemplate', 'enabled' => 1, 'visible' => 0, 'position' => 180),
353 'date_livraison' => array('type' => 'date', 'label' => 'DateDeliveryPlanned', 'enabled' => 1, 'visible' => -1, 'position' => 185),
354 'fk_shipping_method' => array('type' => 'integer', 'label' => 'ShippingMethod', 'enabled' => 1, 'visible' => -1, 'position' => 190),
355 'fk_warehouse' => array('type' => 'integer:Entrepot:product/stock/class/entrepot.class.php', 'label' => 'Fk warehouse', 'enabled' => 'isModEnabled("stock")', 'visible' => -1, 'position' => 191),
356 'fk_availability' => array('type' => 'integer', 'label' => 'Availability', 'enabled' => 1, 'visible' => -1, 'position' => 195),
357 'fk_delivery_address' => array('type' => 'integer', 'label' => 'DeliveryAddress', 'enabled' => 1, 'visible' => 0, 'position' => 200), // deprecated
358 'fk_input_reason' => array('type' => 'integer', 'label' => 'InputReason', 'enabled' => 1, 'visible' => -1, 'position' => 205),
359 'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => -1, 'position' => 215),
360 'fk_incoterms' => array('type' => 'integer', 'label' => 'IncotermCode', 'enabled' => '$conf->incoterm->enabled', 'visible' => -1, 'position' => 220),
361 'location_incoterms' => array('type' => 'varchar(255)', 'label' => 'IncotermLabel', 'enabled' => '$conf->incoterm->enabled', 'visible' => -1, 'position' => 225),
362 'fk_multicurrency' => array('type' => 'integer', 'label' => 'MulticurrencyID', 'enabled' => 1, 'visible' => -1, 'position' => 230),
363 'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'MulticurrencyCurrency', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 235),
364 'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyRate', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 240, 'isameasure' => 1),
365 'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountHT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 245, 'isameasure' => 1),
366 'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountVAT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 250, 'isameasure' => 1),
367 'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountTTC', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 255, 'isameasure' => 1),
368 'last_main_doc' => array('type' => 'varchar(255)', 'label' => 'LastMainDoc', 'enabled' => 1, 'visible' => -1, 'position' => 260),
369 'fk_statut' => array('type' => 'smallint(6)', 'label' => 'Status', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 500),
370 'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'position' => 900),
371 );
372 // END MODULEBUILDER PROPERTIES
373
377 const STATUS_CANCELED = -1;
381 const STATUS_DRAFT = 0;
389 const STATUS_SIGNED = 2;
397 const STATUS_BILLED = 4; // Todo rename into STATUS_CLOSE ?
398
399
407 public function __construct($db, $socid = 0, $propalid = 0)
408 {
409 $this->db = $db;
410
411 $this->ismultientitymanaged = 1;
412 $this->socid = $socid;
413 $this->id = $propalid;
414
415 $this->duree_validite = getDolGlobalInt('PROPALE_VALIDITY_DURATION', 0);
416 }
417
418
419 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
431 public function add_product($idproduct, $qty, $remise_percent = 0)
432 {
433 // phpcs:enable
434 global $conf, $mysoc;
435
436 if (!$qty) {
437 $qty = 1;
438 }
439
440 dol_syslog(get_class($this)."::add_product $idproduct, $qty, $remise_percent");
441 if ($idproduct > 0) {
442 $prod = new Product($this->db);
443 $prod->fetch($idproduct);
444
445 $productdesc = $prod->description;
446
447 $tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
448 $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
449 if (empty($tva_tx)) {
450 $tva_npr = 0;
451 }
452 $vat_src_code = ''; // May be defined into tva_tx
453
454 $localtax1_tx = get_localtax($tva_tx, 1, $mysoc, $this->thirdparty, $tva_npr);
455 $localtax2_tx = get_localtax($tva_tx, 2, $mysoc, $this->thirdparty, $tva_npr);
456
457 // multiprices
458 if ($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level) {
459 $price = $prod->multiprices[$this->thirdparty->price_level];
460 } else {
461 $price = $prod->price;
462 }
463
464 $line = new PropaleLigne($this->db);
465
466 $line->fk_product = $idproduct;
467 $line->desc = $productdesc;
468 $line->qty = $qty;
469 $line->subprice = $price;
470 $line->remise_percent = $remise_percent;
471 $line->vat_src_code = $vat_src_code;
472 $line->tva_tx = $tva_tx;
473 $line->fk_unit = $prod->fk_unit;
474 if ($tva_npr) {
475 $line->info_bits = 1;
476 }
477
478 $this->lines[] = $line;
479 }
480
481 return 1;
482 }
483
484 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
491 public function insert_discount($idremise)
492 {
493 // phpcs:enable
494 global $langs;
495
496 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
497 include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
498
499 $this->db->begin();
500
501 $remise = new DiscountAbsolute($this->db);
502 $result = $remise->fetch($idremise);
503
504 if ($result > 0) {
505 if ($remise->fk_facture) { // Protection against multiple submission
506 $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
507 $this->db->rollback();
508 return -5;
509 }
510
511 $line = new PropaleLigne($this->db);
512
513 $line->context = $this->context;
514
515 $line->fk_propal = $this->id;
516 $line->fk_remise_except = $remise->id;
517 $line->desc = $remise->description; // Description ligne
518 $line->vat_src_code = $remise->vat_src_code;
519 $line->tva_tx = $remise->tva_tx;
520 $line->subprice = -$remise->amount_ht;
521 $line->fk_product = 0; // Id produit predefined
522 $line->qty = 1;
523 $line->remise_percent = 0;
524 $line->rang = -1;
525 $line->info_bits = 2;
526
527 // TODO deprecated
528 $line->price = -$remise->amount_ht;
529
530 $line->total_ht = -$remise->amount_ht;
531 $line->total_tva = -$remise->amount_tva;
532 $line->total_ttc = -$remise->amount_ttc;
533
534 $result = $line->insert();
535 if ($result > 0) {
536 $result = $this->update_price(1);
537 if ($result > 0) {
538 $this->db->commit();
539 return 1;
540 } else {
541 $this->db->rollback();
542 return -1;
543 }
544 } else {
545 $this->error = $line->error;
546 $this->errors = $line->errors;
547 $this->db->rollback();
548 return -2;
549 }
550 } else {
551 $this->db->rollback();
552 return -2;
553 }
554 }
555
593 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)
594 {
595 global $mysoc, $conf, $langs;
596
597 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);
598
599 if ($this->statut == self::STATUS_DRAFT) {
600 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
601
602 // Clean parameters
603 if (empty($remise_percent)) {
604 $remise_percent = 0;
605 }
606 if (empty($qty)) {
607 $qty = 0;
608 }
609 if (empty($info_bits)) {
610 $info_bits = 0;
611 }
612 if (empty($rang)) {
613 $rang = 0;
614 }
615 if (empty($fk_parent_line) || $fk_parent_line < 0) {
616 $fk_parent_line = 0;
617 }
618
619 $remise_percent = price2num($remise_percent);
620 $qty = (float) price2num($qty);
621 $pu_ht = price2num($pu_ht);
622 $pu_ht_devise = price2num($pu_ht_devise);
623 $pu_ttc = price2num($pu_ttc);
624 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
625 $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
626 }
627 $txlocaltax1 = price2num($txlocaltax1);
628 $txlocaltax2 = price2num($txlocaltax2);
629 $pa_ht = price2num($pa_ht);
630 if ($price_base_type == 'HT') {
631 $pu = $pu_ht;
632 } else {
633 $pu = $pu_ttc;
634 }
635
636 // Check parameters
637 if ($type < 0) {
638 return -1;
639 }
640
641 if ($date_start && $date_end && $date_start > $date_end) {
642 $langs->load("errors");
643 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
644 return -1;
645 }
646
647 $this->db->begin();
648
649 $product_type = $type;
650 if (!empty($fk_product) && $fk_product > 0) {
651 $product = new Product($this->db);
652 $result = $product->fetch($fk_product);
653 $product_type = $product->type;
654
655 if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_PROPOSAL') && $product_type == 0 && $product->stock_reel < $qty) {
656 $langs->load("errors");
657 $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnProposal', $product->ref);
658 $this->db->rollback();
659 return -3;
660 }
661 }
662
663 // Calcul du total TTC et de la TVA pour la ligne a partir de
664 // qty, pu, remise_percent et txtva
665 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
666 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
667
668 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
669
670 // Clean vat code
671 $reg = array();
672 $vat_src_code = '';
673 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
674 $vat_src_code = $reg[1];
675 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
676 }
677
678 $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);
679
680 $total_ht = $tabprice[0];
681 $total_tva = $tabprice[1];
682 $total_ttc = $tabprice[2];
683 $total_localtax1 = $tabprice[9];
684 $total_localtax2 = $tabprice[10];
685 $pu_ht = $tabprice[3];
686 $pu_tva = $tabprice[4];
687 $pu_ttc = $tabprice[5];
688
689 // MultiCurrency
690 $multicurrency_total_ht = $tabprice[16];
691 $multicurrency_total_tva = $tabprice[17];
692 $multicurrency_total_ttc = $tabprice[18];
693 $pu_ht_devise = $tabprice[19];
694
695 // Rang to use
696 $ranktouse = $rang;
697 if ($ranktouse == -1) {
698 $rangmax = $this->line_max($fk_parent_line);
699 $ranktouse = $rangmax + 1;
700 }
701
702 // TODO A virer
703 // Anciens indicateurs: $price, $remise (a ne plus utiliser)
704 $price = $pu;
705 $remise = 0;
706 if ((float) $remise_percent > 0) {
707 $remise = round(((float) $pu * (float) $remise_percent / 100), 2);
708 $price = (float) $pu - $remise;
709 }
710
711 // Insert line
712 $this->line = new PropaleLigne($this->db);
713
714 $this->line->context = $this->context;
715
716 $this->line->fk_propal = $this->id;
717 $this->line->label = $label;
718 $this->line->desc = $desc;
719 $this->line->qty = $qty;
720
721 $this->line->vat_src_code = $vat_src_code;
722 $this->line->tva_tx = $txtva;
723 $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
724 $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
725 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
726 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
727 $this->line->fk_product = $fk_product;
728 $this->line->product_type = $type;
729 $this->line->fk_remise_except = $fk_remise_except;
730 $this->line->remise_percent = $remise_percent;
731 $this->line->subprice = $pu_ht;
732 $this->line->rang = $ranktouse;
733 $this->line->info_bits = $info_bits;
734 $this->line->total_ht = $total_ht;
735 $this->line->total_tva = $total_tva;
736 $this->line->total_localtax1 = $total_localtax1;
737 $this->line->total_localtax2 = $total_localtax2;
738 $this->line->total_ttc = $total_ttc;
739 $this->line->special_code = $special_code;
740 $this->line->fk_parent_line = $fk_parent_line;
741 $this->line->fk_unit = $fk_unit;
742
743 $this->line->date_start = $date_start;
744 $this->line->date_end = $date_end;
745
746 $this->line->fk_fournprice = $fk_fournprice;
747 $this->line->pa_ht = $pa_ht;
748
749 $this->line->origin_id = $origin_id;
750 $this->line->origin = $origin;
751
752 // Multicurrency
753 $this->line->fk_multicurrency = $this->fk_multicurrency;
754 $this->line->multicurrency_code = $this->multicurrency_code;
755 $this->line->multicurrency_subprice = $pu_ht_devise;
756 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
757 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
758 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
759
760 // Mise en option de la ligne
761 if (empty($qty) && empty($special_code)) {
762 $this->line->special_code = 3;
763 }
764
765 // TODO deprecated
766 $this->line->price = $price;
767
768 if (is_array($array_options) && count($array_options) > 0) {
769 $this->line->array_options = $array_options;
770 }
771
772 $result = $this->line->insert();
773 if ($result > 0) {
774 // Reorder if child line
775 if (!empty($fk_parent_line)) {
776 $this->line_order(true, 'DESC');
777 } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) { // Update all rank of all other lines
778 $linecount = count($this->lines);
779 for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
780 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
781 }
782 }
783
784 // Mise a jour information denormalisees au niveau de la propale meme
785 if (empty($noupdateafterinsertline)) {
786 $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.
787 }
788
789 if ($result > 0) {
790 $this->db->commit();
791 return $this->line->id;
792 } else {
793 $this->error = $this->db->error();
794 $this->db->rollback();
795 return -1;
796 }
797 } else {
798 $this->error = $this->line->error;
799 $this->errors = $this->line->errors;
800 $this->db->rollback();
801 return -2;
802 }
803 } else {
804 dol_syslog(get_class($this)."::addline status of proposal must be Draft to allow use of ->addline()", LOG_ERR);
805 return -3;
806 }
807 }
808
809
839 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)
840 {
841 global $mysoc, $langs;
842
843 dol_syslog(get_class($this)."::updateLine rowid=$rowid, pu=$pu, qty=$qty, remise_percent=$remise_percent,
844 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");
845 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
846
847 // Clean parameters
848 $remise_percent = price2num($remise_percent);
849 $qty = (float) price2num($qty);
850 $pu = price2num($pu);
851 $pu_ht_devise = price2num($pu_ht_devise);
852 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
853 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
854 }
855 $txlocaltax1 = price2num($txlocaltax1);
856 $txlocaltax2 = price2num($txlocaltax2);
857 $pa_ht = price2num($pa_ht);
858 if (empty($qty) && empty($special_code)) {
859 $special_code = 3; // Set option tag
860 }
861 if (!empty($qty) && $special_code == 3) {
862 $special_code = 0; // Remove option tag
863 }
864 if (empty($type)) {
865 $type = 0;
866 }
867
868 if ($date_start && $date_end && $date_start > $date_end) {
869 $langs->load("errors");
870 $this->error = $langs->trans('ErrorStartDateGreaterEnd');
871 return -1;
872 }
873
874 if ($this->status == self::STATUS_DRAFT) {
875 $this->db->begin();
876
877 // Calcul du total TTC et de la TVA pour la ligne a partir de
878 // qty, pu, remise_percent et txtva
879 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
880 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
881
882 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
883
884 // Clean vat code
885 $reg = array();
886 $vat_src_code = '';
887 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
888 $vat_src_code = $reg[1];
889 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
890 }
891
892 // TODO Implement if (getDolGlobalInt('MAIN_UNIT_PRICE_WITH_TAX_IS_FOR_ALL_TAXES')) ?
893
894 $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);
895 $total_ht = $tabprice[0];
896 $total_tva = $tabprice[1];
897 $total_ttc = $tabprice[2];
898 $total_localtax1 = $tabprice[9];
899 $total_localtax2 = $tabprice[10];
900 $pu_ht = $tabprice[3];
901 $pu_tva = $tabprice[4];
902 $pu_ttc = $tabprice[5];
903
904 // MultiCurrency
905 $multicurrency_total_ht = $tabprice[16];
906 $multicurrency_total_tva = $tabprice[17];
907 $multicurrency_total_ttc = $tabprice[18];
908 $pu_ht_devise = $tabprice[19];
909
910 // Anciens indicateurs: $price, $remise (a ne plus utiliser)
911 $price = $pu;
912 $remise = 0;
913 if ((float) $remise_percent > 0) {
914 $remise = round(((float) $pu * (float) $remise_percent / 100), 2);
915 $price = (float) $pu - $remise;
916 }
917
918 //Fetch current line from the database and then clone the object and set it in $oldline property
919 $line = new PropaleLigne($this->db);
920 $line->fetch($rowid);
921
922 $staticline = clone $line;
923
924 $line->oldline = $staticline;
925 $this->line = $line;
926 $this->line->context = $this->context;
927 $this->line->rang = $rang;
928
929 // Reorder if fk_parent_line change
930 if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
931 $rangmax = $this->line_max($fk_parent_line);
932 $this->line->rang = $rangmax + 1;
933 }
934
935 $this->line->id = $rowid;
936 $this->line->label = $label;
937 $this->line->desc = $desc;
938 $this->line->qty = $qty;
939 $this->line->product_type = $type;
940 $this->line->vat_src_code = $vat_src_code;
941 $this->line->tva_tx = $txtva;
942 $this->line->localtax1_tx = $txlocaltax1;
943 $this->line->localtax2_tx = $txlocaltax2;
944 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
945 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
946 $this->line->remise_percent = $remise_percent;
947 $this->line->subprice = $pu_ht;
948 $this->line->info_bits = $info_bits;
949
950 $this->line->total_ht = $total_ht;
951 $this->line->total_tva = $total_tva;
952 $this->line->total_localtax1 = $total_localtax1;
953 $this->line->total_localtax2 = $total_localtax2;
954 $this->line->total_ttc = $total_ttc;
955 $this->line->special_code = $special_code;
956 $this->line->fk_parent_line = $fk_parent_line;
957 $this->line->skip_update_total = $skip_update_total;
958 $this->line->fk_unit = $fk_unit;
959
960 $this->line->fk_fournprice = $fk_fournprice;
961 $this->line->pa_ht = $pa_ht;
962
963 $this->line->date_start = $date_start;
964 $this->line->date_end = $date_end;
965
966 if (is_array($array_options) && count($array_options) > 0) {
967 // We replace values in this->line->array_options only for entries defined into $array_options
968 foreach ($array_options as $key => $value) {
969 $this->line->array_options[$key] = $array_options[$key];
970 }
971 }
972
973 // Multicurrency
974 $this->line->multicurrency_subprice = $pu_ht_devise;
975 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
976 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
977 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
978
979 $result = $this->line->update($notrigger);
980 if ($result > 0) {
981 // Reorder if child line
982 if (!empty($fk_parent_line)) {
983 $this->line_order(true, 'DESC');
984 }
985
986 $this->update_price(1, 'auto');
987
988 // $this is Propal
989 // $this->fk_propal = $this->id;
990 // $this->rowid = $rowid;
991
992 $this->db->commit();
993 return $result;
994 } else {
995 $this->error = $this->line->error;
996 $this->errors = $this->line->errors;
997 $this->db->rollback();
998 return -1;
999 }
1000 } else {
1001 dol_syslog(get_class($this)."::updateline Erreur -2 Propal en mode incompatible pour cette action");
1002 return -2;
1003 }
1004 }
1005
1006
1014 public function deleteLine($lineid, $id = 0)
1015 {
1016 global $user;
1017
1018 if ($this->status == self::STATUS_DRAFT) {
1019 $this->db->begin();
1020
1021 $line = new PropaleLigne($this->db);
1022
1023 $line->context = $this->context;
1024
1025 // Load data
1026 $line->fetch($lineid);
1027
1028 if ($id > 0 && $line->fk_propal != $id) {
1029 $this->error = 'ErrorLineIDDoesNotMatchWithObjectID';
1030 return -1;
1031 }
1032
1033 // Memorize previous line for triggers
1034 $staticline = clone $line;
1035 $line->oldline = $staticline;
1036
1037 if ($line->delete($user) > 0) {
1038 $this->update_price(1);
1039
1040 $this->db->commit();
1041 return 1;
1042 } else {
1043 $this->error = $line->error;
1044 $this->errors = $line->errors;
1045 $this->db->rollback();
1046 return -1;
1047 }
1048 } else {
1049 $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
1050 return -2;
1051 }
1052 }
1053
1054
1063 public function create($user, $notrigger = 0)
1064 {
1065 global $conf, $hookmanager, $mysoc;
1066 $error = 0;
1067
1068 $now = dol_now();
1069
1070 // Clean parameters
1071 if (empty($this->date)) {
1072 $this->date = $this->datep;
1073 }
1074 $this->fin_validite = $this->date + ($this->duree_validite * 24 * 3600);
1075 if (empty($this->availability_id)) {
1076 $this->availability_id = 0;
1077 }
1078 if (empty($this->demand_reason_id)) {
1079 $this->demand_reason_id = 0;
1080 }
1081
1082 // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1083 if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1084 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date);
1085 } else {
1086 $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1087 }
1088 if (empty($this->fk_multicurrency)) {
1089 $this->multicurrency_code = $conf->currency;
1090 $this->fk_multicurrency = 0;
1091 $this->multicurrency_tx = 1;
1092 }
1093
1094 // Set tmp vars
1095 $delivery_date = $this->delivery_date;
1096
1097 dol_syslog(get_class($this)."::create");
1098
1099 // Check parameters
1100 $result = $this->fetch_thirdparty();
1101 if ($result < 0) {
1102 $this->error = "Failed to fetch company";
1103 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
1104 return -3;
1105 }
1106
1107 // Check parameters
1108 if (!empty($this->ref)) { // We check that ref is not already used
1109 $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
1110 if ($result > 0) {
1111 $this->error = 'ErrorRefAlreadyExists';
1112 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
1113 $this->db->rollback();
1114 return -1;
1115 }
1116 }
1117
1118 if (empty($this->date)) {
1119 $this->error = "Date of proposal is required";
1120 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
1121 return -4;
1122 }
1123
1124
1125 $this->db->begin();
1126
1127 // Insert into database
1128 $sql = "INSERT INTO ".MAIN_DB_PREFIX."propal (";
1129 $sql .= "fk_soc";
1130 $sql .= ", price";
1131 $sql .= ", total_tva";
1132 $sql .= ", total_ttc";
1133 $sql .= ", datep";
1134 $sql .= ", datec";
1135 $sql .= ", ref";
1136 $sql .= ", fk_user_author";
1137 $sql .= ", note_private";
1138 $sql .= ", note_public";
1139 $sql .= ", model_pdf";
1140 $sql .= ", fin_validite";
1141 $sql .= ", fk_cond_reglement";
1142 $sql .= ", deposit_percent";
1143 $sql .= ", fk_mode_reglement";
1144 $sql .= ", fk_account";
1145 $sql .= ", ref_client";
1146 $sql .= ", ref_ext";
1147 $sql .= ", date_livraison";
1148 $sql .= ", fk_shipping_method";
1149 $sql .= ", fk_warehouse";
1150 $sql .= ", fk_availability";
1151 $sql .= ", fk_input_reason";
1152 $sql .= ", fk_projet";
1153 $sql .= ", fk_incoterms";
1154 $sql .= ", location_incoterms";
1155 $sql .= ", entity";
1156 $sql .= ", fk_multicurrency";
1157 $sql .= ", multicurrency_code";
1158 $sql .= ", multicurrency_tx";
1159 $sql .= ") ";
1160 $sql .= " VALUES (";
1161 $sql .= $this->socid;
1162 $sql .= ", 0";
1163 $sql .= ", 0";
1164 $sql .= ", 0";
1165 $sql .= ", '".$this->db->idate($this->date)."'";
1166 $sql .= ", '".$this->db->idate($now)."'";
1167 $sql .= ", '(PROV)'";
1168 $sql .= ", ".($user->id > 0 ? ((int) $user->id) : "NULL");
1169 $sql .= ", '".$this->db->escape($this->note_private)."'";
1170 $sql .= ", '".$this->db->escape($this->note_public)."'";
1171 $sql .= ", '".$this->db->escape($this->model_pdf)."'";
1172 $sql .= ", ".($this->fin_validite != '' ? "'".$this->db->idate($this->fin_validite)."'" : "NULL");
1173 $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : 'NULL');
1174 $sql .= ", ".(!empty($this->deposit_percent) ? "'".$this->db->escape($this->deposit_percent)."'" : 'NULL');
1175 $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : 'NULL');
1176 $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
1177 $sql .= ", '".$this->db->escape($this->ref_client)."'";
1178 $sql .= ", '".$this->db->escape($this->ref_ext)."'";
1179 $sql .= ", ".(!isDolTms($delivery_date) ? "NULL" : "'".$this->db->idate($delivery_date)."'");
1180 $sql .= ", ".($this->shipping_method_id > 0 ? $this->shipping_method_id : 'NULL');
1181 $sql .= ", ".($this->warehouse_id > 0 ? $this->warehouse_id : 'NULL');
1182 $sql .= ", ".$this->availability_id;
1183 $sql .= ", ".$this->demand_reason_id;
1184 $sql .= ", ".($this->fk_project ? $this->fk_project : "null");
1185 $sql .= ", ".(int) $this->fk_incoterms;
1186 $sql .= ", '".$this->db->escape($this->location_incoterms)."'";
1187 $sql .= ", ".setEntity($this);
1188 $sql .= ", ".(int) $this->fk_multicurrency;
1189 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1190 $sql .= ", ".(float) $this->multicurrency_tx;
1191 $sql .= ")";
1192
1193 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1194 $resql = $this->db->query($sql);
1195 if ($resql) {
1196 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."propal");
1197
1198 if ($this->id) {
1199 $this->ref = '(PROV'.$this->id.')';
1200 $sql = 'UPDATE '.MAIN_DB_PREFIX."propal SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
1201
1202 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1203 $resql = $this->db->query($sql);
1204 if (!$resql) {
1205 $error++;
1206 }
1207
1208 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1209 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1210 }
1211
1212 // Add object linked
1213 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1214 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1215 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, ...))
1216 foreach ($tmp_origin_id as $origin_id) {
1217 $ret = $this->add_object_linked($origin, $origin_id);
1218 if (!$ret) {
1219 $this->error = $this->db->lasterror();
1220 $error++;
1221 }
1222 }
1223 } else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1224 $origin_id = $tmp_origin_id;
1225 $ret = $this->add_object_linked($origin, $origin_id);
1226 if (!$ret) {
1227 $this->error = $this->db->lasterror();
1228 $error++;
1229 }
1230 }
1231 }
1232 }
1233
1234 /*
1235 * Insertion du detail des produits dans la base
1236 * Insert products detail in database
1237 */
1238 if (!$error) {
1239 $fk_parent_line = 0;
1240 $num = count($this->lines);
1241
1242 for ($i = 0; $i < $num; $i++) {
1243 if (!is_object($this->lines[$i])) { // If this->lines is not array of objects, coming from REST API
1244 // Convert into object this->lines[$i].
1245 $line = (object) $this->lines[$i];
1246 } else {
1247 $line = $this->lines[$i];
1248 }
1249 // Reset fk_parent_line for line that are not child lines or special product
1250 if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
1251 $fk_parent_line = 0;
1252 }
1253 // Complete vat rate with code
1254 $vatrate = $line->tva_tx;
1255 if ($line->vat_src_code && !preg_match('/\‍(.*\‍)/', $vatrate)) {
1256 $vatrate .= ' ('.$line->vat_src_code.')';
1257 }
1258
1259 if (getDolGlobalString('MAIN_CREATEFROM_KEEP_LINE_ORIGIN_INFORMATION')) {
1260 $originid = $line->origin_id;
1261 $origintype = $line->origin;
1262 } else {
1263 $originid = $line->id;
1264 $origintype = $this->element;
1265 }
1266
1267 $result = $this->addline(
1268 $line->desc,
1269 $line->subprice,
1270 $line->qty,
1271 $vatrate,
1272 $line->localtax1_tx,
1273 $line->localtax2_tx,
1274 $line->fk_product,
1275 $line->remise_percent,
1276 'HT',
1277 0,
1278 $line->info_bits,
1279 $line->product_type,
1280 $line->rang,
1281 $line->special_code,
1282 $fk_parent_line,
1283 $line->fk_fournprice,
1284 $line->pa_ht,
1285 $line->label,
1286 $line->date_start,
1287 $line->date_end,
1288 $line->array_options,
1289 $line->fk_unit,
1290 $origintype,
1291 $originid,
1292 0,
1293 0,
1294 1
1295 );
1296
1297 if ($result < 0) {
1298 $error++;
1299 $this->error = $this->db->error;
1300 dol_print_error($this->db);
1301 break;
1302 }
1303
1304 // Set the id on created row
1305 $line->id = $result;
1306
1307 // Defined the new fk_parent_line
1308 if ($result > 0 && $line->product_type == 9) {
1309 $fk_parent_line = $result;
1310 }
1311 }
1312 }
1313
1314 // Set delivery address
1315 /*if (! $error && $this->fk_delivery_address)
1316 {
1317 $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
1318 $sql.= " SET fk_delivery_address = ".((int) $this->fk_delivery_address);
1319 $sql.= " WHERE ref = '".$this->db->escape($this->ref)."'";
1320 $sql.= " AND entity = ".setEntity($this);
1321
1322 $result=$this->db->query($sql);
1323 }*/
1324
1325 if (!$error) {
1326 // Mise a jour infos denormalisees
1327 $resql = $this->update_price(1, 'auto', 0, $mysoc);
1328 if ($resql) {
1329 $action = 'update';
1330
1331 // Actions on extra fields
1332 if (!$error) {
1333 $result = $this->insertExtraFields();
1334 if ($result < 0) {
1335 $error++;
1336 }
1337 }
1338
1339 if (!$error && !$notrigger) {
1340 // Call trigger
1341 $result = $this->call_trigger('PROPAL_CREATE', $user);
1342 if ($result < 0) {
1343 $error++;
1344 }
1345 // End call triggers
1346 }
1347 } else {
1348 $this->error = $this->db->lasterror();
1349 $error++;
1350 }
1351 }
1352 } else {
1353 $this->error = $this->db->lasterror();
1354 $error++;
1355 }
1356
1357 if (!$error) {
1358 $this->db->commit();
1359 dol_syslog(get_class($this)."::create done id=".$this->id);
1360 return $this->id;
1361 } else {
1362 $this->db->rollback();
1363 return -2;
1364 }
1365 } else {
1366 $this->error = $this->db->lasterror();
1367 $this->db->rollback();
1368 return -1;
1369 }
1370 }
1371
1382 public function createFromClone(User $user, $socid = 0, $forceentity = null, $update_prices = false, $update_desc = false)
1383 {
1384 global $conf, $hookmanager, $mysoc;
1385
1386 dol_include_once('/projet/class/project.class.php');
1387
1388 $error = 0;
1389 $now = dol_now();
1390
1391 dol_syslog(__METHOD__, LOG_DEBUG);
1392
1393 $object = new self($this->db);
1394
1395 $this->db->begin();
1396
1397 // Load source object
1398 $object->fetch($this->id);
1399
1400 $objsoc = new Societe($this->db);
1401
1402 // Change socid if needed
1403 if (!empty($socid) && $socid != $object->socid) {
1404 if ($objsoc->fetch($socid) > 0) {
1405 $object->socid = $objsoc->id;
1406 $object->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1407 $object->deposit_percent = (!empty($objsoc->deposit_percent) ? $objsoc->deposit_percent : null);
1408 $object->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1409 $object->fk_delivery_address = 0;
1410
1411 /*if (isModEnabled('project'))
1412 {
1413 $project = new Project($db);
1414 if ($this->fk_project > 0 && $project->fetch($this->fk_project)) {
1415 if ($project->socid <= 0) $clonedObj->fk_project = $this->fk_project;
1416 else $clonedObj->fk_project = '';
1417 } else {
1418 $clonedObj->fk_project = '';
1419 }
1420 }*/
1421 $object->fk_project = 0; // A cloned proposal is set by default to no project.
1422 }
1423
1424 // reset ref_client
1425 if (!getDolGlobalString('MAIN_KEEP_REF_CUSTOMER_ON_CLONING')) {
1426 $object->ref_client = '';
1427 $object->ref_customer = '';
1428 }
1429
1430 // TODO Change product price if multi-prices
1431 } else {
1432 $objsoc->fetch($object->socid);
1433 }
1434
1435 // update prices
1436 if ($update_prices === true || $update_desc === true) {
1437 if ($objsoc->id > 0 && !empty($object->lines)) {
1438 if ($update_prices === true && getDolGlobalString('PRODUIT_CUSTOMER_PRICES')) {
1439 // If price per customer
1440 require_once DOL_DOCUMENT_ROOT . '/product/class/productcustomerprice.class.php';
1441 }
1442
1443 foreach ($object->lines as $line) {
1444 $line->id = 0;
1445
1446 if ($line->fk_product > 0) {
1447 $prod = new Product($this->db);
1448 $res = $prod->fetch($line->fk_product);
1449 if ($res > 0) {
1450 if ($update_prices === true) {
1451 $pu_ht = $prod->price;
1452 $tva_tx = get_default_tva($mysoc, $objsoc, $prod->id);
1453 $remise_percent = $objsoc->remise_percent;
1454
1455 if (getDolGlobalString('PRODUIT_MULTIPRICES') && $objsoc->price_level > 0) {
1456 $pu_ht = $prod->multiprices[$objsoc->price_level];
1457 if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) { // using this option is a bug. kept for backward compatibility
1458 if (isset($prod->multiprices_tva_tx[$objsoc->price_level])) {
1459 $tva_tx = $prod->multiprices_tva_tx[$objsoc->price_level];
1460 }
1461 }
1462 } elseif (getDolGlobalString('PRODUIT_CUSTOMER_PRICES')) {
1463 $prodcustprice = new ProductCustomerPrice($this->db);
1464 $filter = array('t.fk_product' => $prod->id, 't.fk_soc' => $objsoc->id);
1465 $result = $prodcustprice->fetchAll('', '', 0, 0, $filter);
1466 if ($result) {
1467 // If there is some prices specific to the customer
1468 if (count($prodcustprice->lines) > 0) {
1469 $pu_ht = price($prodcustprice->lines[0]->price);
1470 $tva_tx = ($prodcustprice->lines[0]->default_vat_code ? $prodcustprice->lines[0]->tva_tx.' ('.$prodcustprice->lines[0]->default_vat_code.' )' : $prodcustprice->lines[0]->tva_tx);
1471 if ($prodcustprice->lines[0]->default_vat_code && !preg_match('/\‍(.*\‍)/', $tva_tx)) {
1472 $tva_tx .= ' ('.$prodcustprice->lines[0]->default_vat_code.')';
1473 }
1474 }
1475 }
1476 }
1477
1478 $line->subprice = $pu_ht;
1479 $line->tva_tx = $tva_tx;
1480 $line->remise_percent = $remise_percent;
1481 }
1482 if ($update_desc === true) {
1483 $line->desc = $prod->description;
1484 }
1485 }
1486 }
1487 }
1488 }
1489 }
1490
1491 $object->id = 0;
1492 $object->ref = '';
1493 $object->entity = (!empty($forceentity) ? $forceentity : $object->entity);
1494 $object->statut = self::STATUS_DRAFT;
1495
1496 // Clear fields
1497 $object->user_creation_id = $user->id;
1498 $object->user_validation_id = 0;
1499 $object->date = $now;
1500 $object->datep = $now; // deprecated
1501 $object->fin_validite = $object->date + ($object->duree_validite * 24 * 3600);
1502 if (!getDolGlobalString('MAIN_KEEP_REF_CUSTOMER_ON_CLONING')) {
1503 $object->ref_client = '';
1504 $object->ref_customer = '';
1505 }
1506 if (getDolGlobalInt('MAIN_DONT_KEEP_NOTE_ON_CLONING') == 1) {
1507 $object->note_private = '';
1508 $object->note_public = '';
1509 }
1510 // Create clone
1511 $object->context['createfromclone'] = 'createfromclone';
1512 $result = $object->create($user);
1513 if ($result < 0) {
1514 $this->error = $object->error;
1515 $this->errors = array_merge($this->errors, $object->errors);
1516 $error++;
1517 }
1518
1519 if (!$error && !getDolGlobalInt('MAIN_IGNORE_CONTACTS_ON_CLONING')) {
1520 // copy internal contacts
1521 if ($object->copy_linked_contact($this, 'internal') < 0) {
1522 $error++;
1523 }
1524 }
1525
1526 if (!$error) {
1527 // copy external contacts if same company
1528 if ($this->socid == $object->socid) {
1529 if ($object->copy_linked_contact($this, 'external') < 0) {
1530 $error++;
1531 }
1532 }
1533 }
1534
1535 if (!$error) {
1536 // Hook of thirdparty module
1537 if (is_object($hookmanager)) {
1538 $parameters = array('objFrom' => $this, 'clonedObj' => $object);
1539 $action = '';
1540 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
1541 if ($reshook < 0) {
1542 $this->setErrorsFromObject($hookmanager);
1543 $error++;
1544 }
1545 }
1546 }
1547
1548 unset($object->context['createfromclone']);
1549
1550 // End
1551 if (!$error) {
1552 $this->db->commit();
1553 return $object->id;
1554 } else {
1555 $this->db->rollback();
1556 return -1;
1557 }
1558 }
1559
1569 public function fetch($rowid, $ref = '', $ref_ext = '', $forceentity = 0)
1570 {
1571 $sql = "SELECT p.rowid, p.ref, p.entity, p.fk_soc";
1572 $sql .= ", p.total_ttc, p.total_tva, p.localtax1, p.localtax2, p.total_ht";
1573 $sql .= ", p.datec";
1574 $sql .= ", p.date_signature as dates";
1575 $sql .= ", p.date_valid as datev";
1576 $sql .= ", p.datep as dp";
1577 $sql .= ", p.fin_validite as dfv";
1578 $sql .= ", p.date_livraison as delivery_date";
1579 $sql .= ", p.model_pdf, p.last_main_doc, p.ref_client, ref_ext, p.extraparams";
1580 $sql .= ", p.note_private, p.note_public";
1581 $sql .= ", p.fk_projet as fk_project, p.fk_statut";
1582 $sql .= ", p.fk_user_author, p.fk_user_valid, p.fk_user_cloture";
1583 $sql .= ", p.fk_delivery_address";
1584 $sql .= ", p.fk_availability";
1585 $sql .= ", p.fk_input_reason";
1586 $sql .= ", p.fk_cond_reglement";
1587 $sql .= ", p.fk_mode_reglement";
1588 $sql .= ', p.fk_account';
1589 $sql .= ", p.fk_shipping_method";
1590 $sql .= ", p.fk_warehouse";
1591 $sql .= ", p.fk_incoterms, p.location_incoterms";
1592 $sql .= ", p.fk_multicurrency, p.multicurrency_code, p.multicurrency_tx, p.multicurrency_total_ht, p.multicurrency_total_tva, p.multicurrency_total_ttc";
1593 $sql .= ", p.tms as date_modification";
1594 $sql .= ", i.libelle as label_incoterms";
1595 $sql .= ", c.label as statut_label";
1596 $sql .= ", ca.code as availability_code, ca.label as availability";
1597 $sql .= ", dr.code as demand_reason_code, dr.label as demand_reason";
1598 $sql .= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc, p.deposit_percent";
1599 $sql .= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement";
1600 $sql .= " FROM ".MAIN_DB_PREFIX."propal as p";
1601 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_propalst as c ON p.fk_statut = c.id';
1602 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id AND cp.entity IN ('.getEntity('c_paiement').')';
1603 $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').')';
1604 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_availability as ca ON p.fk_availability = ca.rowid';
1605 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_input_reason as dr ON p.fk_input_reason = dr.rowid';
1606 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON p.fk_incoterms = i.rowid';
1607
1608 if (!empty($ref)) {
1609 if (!empty($forceentity)) {
1610 $sql .= " WHERE p.entity = ".(int) $forceentity; // Check only the current entity because we may have the same reference in several entities
1611 } else {
1612 $sql .= " WHERE p.entity IN (".getEntity('propal').")";
1613 }
1614 $sql .= " AND p.ref='".$this->db->escape($ref)."'";
1615 } else {
1616 // Don't use entity if you use rowid
1617 $sql .= " WHERE p.rowid = ".((int) $rowid);
1618 }
1619
1620 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1621 $resql = $this->db->query($sql);
1622 if ($resql) {
1623 if ($this->db->num_rows($resql)) {
1624 $obj = $this->db->fetch_object($resql);
1625
1626 $this->id = $obj->rowid;
1627 $this->entity = $obj->entity;
1628
1629 $this->ref = $obj->ref;
1630 $this->ref_client = $obj->ref_client;
1631 $this->ref_customer = $obj->ref_client;
1632 $this->ref_ext = $obj->ref_ext;
1633
1634 $this->total = $obj->total_ttc; // TODO deprecated
1635 $this->total_ttc = $obj->total_ttc;
1636 $this->total_ht = $obj->total_ht;
1637 $this->total_tva = $obj->total_tva;
1638 $this->total_localtax1 = $obj->localtax1;
1639 $this->total_localtax2 = $obj->localtax2;
1640
1641 $this->socid = $obj->fk_soc;
1642 $this->thirdparty = null; // Clear if another value was already set by fetch_thirdparty
1643
1644 $this->fk_project = $obj->fk_project;
1645 $this->project = null; // Clear if another value was already set by fetch_projet
1646
1647 $this->model_pdf = $obj->model_pdf;
1648 $this->last_main_doc = $obj->last_main_doc;
1649 $this->note = $obj->note_private; // TODO deprecated
1650 $this->note_private = $obj->note_private;
1651 $this->note_public = $obj->note_public;
1652
1653 $this->status = (int) $obj->fk_statut;
1654 $this->statut = $this->status; // deprecated
1655
1656 $this->datec = $this->db->jdate($obj->datec); // TODO deprecated
1657 $this->datev = $this->db->jdate($obj->datev); // TODO deprecated
1658 $this->date_creation = $this->db->jdate($obj->datec); //Creation date
1659 $this->date_validation = $this->db->jdate($obj->datev); //Validation date
1660 $this->date_modification = $this->db->jdate($obj->date_modification); // tms
1661 $this->date_signature = $this->db->jdate($obj->dates); // Signature date
1662 $this->date = $this->db->jdate($obj->dp); // Proposal date
1663 $this->datep = $this->db->jdate($obj->dp); // deprecated
1664 $this->fin_validite = $this->db->jdate($obj->dfv);
1665 $this->delivery_date = $this->db->jdate($obj->delivery_date);
1666 $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1667 $this->warehouse_id = ($obj->fk_warehouse > 0) ? $obj->fk_warehouse : null;
1668 $this->availability_id = $obj->fk_availability;
1669 $this->availability_code = $obj->availability_code;
1670 $this->availability = $obj->availability;
1671 $this->demand_reason_id = $obj->fk_input_reason;
1672 $this->demand_reason_code = $obj->demand_reason_code;
1673 $this->demand_reason = $obj->demand_reason;
1674 $this->fk_address = $obj->fk_delivery_address;
1675
1676 $this->mode_reglement_id = $obj->fk_mode_reglement;
1677 $this->mode_reglement_code = $obj->mode_reglement_code;
1678 $this->mode_reglement = $obj->mode_reglement;
1679 $this->fk_account = ($obj->fk_account > 0) ? $obj->fk_account : null;
1680 $this->cond_reglement_id = $obj->fk_cond_reglement;
1681 $this->cond_reglement_code = $obj->cond_reglement_code;
1682 $this->cond_reglement = $obj->cond_reglement;
1683 $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1684 $this->deposit_percent = $obj->deposit_percent;
1685
1686 $this->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
1687
1688 $this->user_author_id = $obj->fk_user_author;
1689 $this->user_validation_id = $obj->fk_user_valid;
1690 $this->user_closing_id = $obj->fk_user_cloture;
1691
1692 //Incoterms
1693 $this->fk_incoterms = $obj->fk_incoterms;
1694 $this->location_incoterms = $obj->location_incoterms;
1695 $this->label_incoterms = $obj->label_incoterms;
1696
1697 // Multicurrency
1698 $this->fk_multicurrency = $obj->fk_multicurrency;
1699 $this->multicurrency_code = $obj->multicurrency_code;
1700 $this->multicurrency_tx = $obj->multicurrency_tx;
1701 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1702 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1703 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1704
1705 // Retrieve all extrafield
1706 // fetch optionals attributes and labels
1707 $this->fetch_optionals();
1708
1709 $this->db->free($resql);
1710
1711 $this->lines = array();
1712
1713 // Lines
1714 $result = $this->fetch_lines();
1715 if ($result < 0) {
1716 return -3;
1717 }
1718
1719 return 1;
1720 }
1721
1722 $this->error = "Record Not Found";
1723 return 0;
1724 } else {
1725 $this->error = $this->db->lasterror();
1726 return -1;
1727 }
1728 }
1729
1737 public function update(User $user, $notrigger = 0)
1738 {
1739 global $conf;
1740
1741 $error = 0;
1742
1743 // Clean parameters
1744 if (isset($this->ref)) {
1745 $this->ref = trim($this->ref);
1746 }
1747 if (isset($this->ref_client)) {
1748 $this->ref_client = trim($this->ref_client);
1749 }
1750 if (isset($this->note) || isset($this->note_private)) {
1751 $this->note_private = (isset($this->note_private) ? trim($this->note_private) : trim($this->note));
1752 }
1753 if (isset($this->note_public)) {
1754 $this->note_public = trim($this->note_public);
1755 }
1756 if (isset($this->model_pdf)) {
1757 $this->model_pdf = trim($this->model_pdf);
1758 }
1759 if (isset($this->import_key)) {
1760 $this->import_key = trim($this->import_key);
1761 }
1762 if (!empty($this->duree_validite) && is_numeric($this->duree_validite)) {
1763 $this->fin_validite = $this->date + ($this->duree_validite * 24 * 3600);
1764 }
1765
1766 // Check parameters
1767 // Put here code to add control on parameters values
1768
1769 // Update request
1770 $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET";
1771 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1772 $sql .= " ref_client=".(isset($this->ref_client) ? "'".$this->db->escape($this->ref_client)."'" : "null").",";
1773 $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1774 $sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
1775 $sql .= " datep=".(strval($this->date) != '' ? "'".$this->db->idate($this->date)."'" : 'null').",";
1776 if (!empty($this->fin_validite)) {
1777 $sql .= " fin_validite=".(strval($this->fin_validite) != '' ? "'".$this->db->idate($this->fin_validite)."'" : 'null').",";
1778 }
1779 $sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
1780 $sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
1781 $sql .= " localtax1=".(isset($this->total_localtax1) ? $this->total_localtax1 : "null").",";
1782 $sql .= " localtax2=".(isset($this->total_localtax2) ? $this->total_localtax2 : "null").",";
1783 $sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
1784 $sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
1785 $sql .= " fk_statut=".(isset($this->status) ? $this->status : "null").",";
1786 $sql .= " fk_user_author=".(isset($this->user_author_id) ? $this->user_author_id : "null").",";
1787 $sql .= " fk_user_valid=".(isset($this->user_validation_id) ? $this->user_validation_id : "null").",";
1788 $sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
1789 $sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
1790 $sql .= " deposit_percent=".(!empty($this->deposit_percent) ? "'".$this->db->escape($this->deposit_percent)."'" : "null").",";
1791 $sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
1792 $sql .= " fk_input_reason=".(isset($this->demand_reason_id) ? $this->demand_reason_id : "null").",";
1793 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1794 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1795 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1796 $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null");
1797 $sql .= " WHERE rowid=".((int) $this->id);
1798
1799 $this->db->begin();
1800
1801 dol_syslog(get_class($this)."::update", LOG_DEBUG);
1802 $resql = $this->db->query($sql);
1803 if (!$resql) {
1804 $error++;
1805 $this->errors[] = "Error ".$this->db->lasterror();
1806 }
1807
1808 if (!$error) {
1809 $result = $this->insertExtraFields();
1810 if ($result < 0) {
1811 $error++;
1812 }
1813 }
1814
1815 if (!$error && !$notrigger) {
1816 // Call trigger
1817 $result = $this->call_trigger('PROPAL_MODIFY', $user);
1818 if ($result < 0) {
1819 $error++;
1820 }
1821 // End call triggers
1822 }
1823
1824 // Commit or rollback
1825 if ($error) {
1826 foreach ($this->errors as $errmsg) {
1827 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1828 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1829 }
1830 $this->db->rollback();
1831 return -1 * $error;
1832 } else {
1833 $this->db->commit();
1834 return 1;
1835 }
1836 }
1837
1838
1839 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1848 public function fetch_lines($only_product = 0, $loadalsotranslation = 0, $sqlforgedfilters = '')
1849 {
1850 // phpcs:enable
1851 $this->lines = array();
1852
1853 $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,';
1854 $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,';
1855 $sql .= ' d.fk_unit,';
1856 $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,';
1857 $sql .= ' p.weight, p.weight_units, p.volume, p.volume_units,';
1858 $sql .= ' d.date_start, d.date_end,';
1859 $sql .= ' d.fk_multicurrency, d.multicurrency_code, d.multicurrency_subprice, d.multicurrency_total_ht, d.multicurrency_total_tva, d.multicurrency_total_ttc';
1860 $sql .= ' FROM '.MAIN_DB_PREFIX.'propaldet as d';
1861 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (d.fk_product = p.rowid)';
1862 $sql .= ' WHERE d.fk_propal = '.((int) $this->id);
1863 if ($only_product) {
1864 $sql .= ' AND p.fk_product_type = 0';
1865 }
1866 if ($sqlforgedfilters) {
1867 $sql .= $sqlforgedfilters;
1868 }
1869 $sql .= ' ORDER by d.rang';
1870
1871 dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
1872 $result = $this->db->query($sql);
1873 if ($result) {
1874 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
1875
1876 $num = $this->db->num_rows($result);
1877
1878 $i = 0;
1879 while ($i < $num) {
1880 $objp = $this->db->fetch_object($result);
1881
1882 $line = new PropaleLigne($this->db);
1883
1884 $line->rowid = $objp->rowid; //Deprecated
1885 $line->id = $objp->rowid;
1886 $line->fk_propal = $objp->fk_propal;
1887 $line->fk_parent_line = $objp->fk_parent_line;
1888 $line->product_type = $objp->product_type;
1889 $line->label = $objp->custom_label;
1890 $line->desc = $objp->description; // Description ligne
1891 $line->description = $objp->description; // Description ligne
1892 $line->qty = $objp->qty;
1893 $line->vat_src_code = $objp->vat_src_code;
1894 $line->tva_tx = $objp->tva_tx;
1895 $line->localtax1_tx = $objp->localtax1_tx;
1896 $line->localtax2_tx = $objp->localtax2_tx;
1897 $line->localtax1_type = $objp->localtax1_type;
1898 $line->localtax2_type = $objp->localtax2_type;
1899 $line->subprice = $objp->subprice;
1900 $line->fk_remise_except = $objp->fk_remise_except;
1901 $line->remise_percent = $objp->remise_percent;
1902 $line->price = $objp->price; // TODO deprecated
1903
1904 $line->info_bits = $objp->info_bits;
1905 $line->total_ht = $objp->total_ht;
1906 $line->total_tva = $objp->total_tva;
1907 $line->total_localtax1 = $objp->total_localtax1;
1908 $line->total_localtax2 = $objp->total_localtax2;
1909 $line->total_ttc = $objp->total_ttc;
1910 $line->fk_fournprice = $objp->fk_fournprice;
1911 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1912 $line->pa_ht = $marginInfos[0];
1913 $line->marge_tx = $marginInfos[1];
1914 $line->marque_tx = $marginInfos[2];
1915 $line->special_code = $objp->special_code;
1916 $line->rang = $objp->rang;
1917
1918 $line->fk_product = $objp->fk_product;
1919
1920 $line->ref = $objp->product_ref; // deprecated
1921 $line->libelle = $objp->product_label; // deprecated
1922
1923 $line->product_ref = $objp->product_ref;
1924 $line->product_label = $objp->product_label;
1925 $line->product_desc = $objp->product_desc; // Description produit
1926 $line->product_tobatch = $objp->product_tobatch;
1927 $line->product_barcode = $objp->product_barcode;
1928
1929 $line->fk_product_type = $objp->fk_product_type; // deprecated
1930 $line->fk_unit = $objp->fk_unit;
1931 $line->weight = $objp->weight;
1932 $line->weight_units = $objp->weight_units;
1933 $line->volume = $objp->volume;
1934 $line->volume_units = $objp->volume_units;
1935
1936 $line->date_start = $this->db->jdate($objp->date_start);
1937 $line->date_end = $this->db->jdate($objp->date_end);
1938
1939 // Multicurrency
1940 $line->fk_multicurrency = $objp->fk_multicurrency;
1941 $line->multicurrency_code = $objp->multicurrency_code;
1942 $line->multicurrency_subprice = $objp->multicurrency_subprice;
1943 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
1944 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
1945 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
1946
1947 $line->fetch_optionals();
1948
1949 // multilangs
1950 if (getDolGlobalInt('MAIN_MULTILANGS') && !empty($objp->fk_product) && !empty($loadalsotranslation)) {
1951 $tmpproduct = new Product($this->db);
1952 $tmpproduct->fetch($objp->fk_product);
1953 $tmpproduct->getMultiLangs();
1954
1955 $line->multilangs = $tmpproduct->multilangs;
1956 }
1957
1958 $this->lines[$i] = $line;
1959
1960 $i++;
1961 }
1962
1963 $this->db->free($result);
1964
1965 return $num;
1966 } else {
1967 $this->error = $this->db->lasterror();
1968 return -3;
1969 }
1970 }
1971
1979 public function valid($user, $notrigger = 0)
1980 {
1981 global $conf;
1982
1983 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1984
1985 $error = 0;
1986
1987 // Protection
1988 if ($this->status == self::STATUS_VALIDATED) {
1989 dol_syslog(get_class($this)."::valid action abandoned: already validated", LOG_WARNING);
1990 return 0;
1991 }
1992
1993 if (!((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('propal', 'creer'))
1994 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('propal', 'propal_advance', 'validate')))) {
1995 $this->error = 'ErrorPermissionDenied';
1996 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
1997 return -1;
1998 }
1999
2000 $now = dol_now();
2001
2002 $this->db->begin();
2003
2004 // Numbering module definition
2005 $soc = new Societe($this->db);
2006 $soc->fetch($this->socid);
2007
2008 // Define new ref
2009 if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
2010 $num = $this->getNextNumRef($soc);
2011 } else {
2012 $num = $this->ref;
2013 }
2014 $this->newref = dol_sanitizeFileName($num);
2015
2016 $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2017 $sql .= " SET ref = '".$this->db->escape($num)."',";
2018 $sql .= " fk_statut = ".self::STATUS_VALIDATED.", date_valid='".$this->db->idate($now)."', fk_user_valid=".((int) $user->id);
2019 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = ".self::STATUS_DRAFT;
2020
2021 dol_syslog(get_class($this)."::valid", LOG_DEBUG);
2022 $resql = $this->db->query($sql);
2023 if (!$resql) {
2024 dol_print_error($this->db);
2025 $error++;
2026 }
2027
2028 // Trigger calls
2029 if (!$error && !$notrigger) {
2030 // Call trigger
2031 $result = $this->call_trigger('PROPAL_VALIDATE', $user);
2032 if ($result < 0) {
2033 $error++;
2034 }
2035 // End call triggers
2036 }
2037
2038 if (!$error) {
2039 $this->oldref = $this->ref;
2040
2041 // Rename directory if dir was a temporary ref
2042 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
2043 // Now we rename also files into index
2044 $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)."'";
2045 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'propale/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
2046 $resql = $this->db->query($sql);
2047 if (!$resql) {
2048 $error++;
2049 $this->error = $this->db->lasterror();
2050 }
2051 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'propale/".$this->db->escape($this->newref)."'";
2052 $sql .= " WHERE filepath = 'propale/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
2053 $resql = $this->db->query($sql);
2054 if (!$resql) {
2055 $error++;
2056 $this->error = $this->db->lasterror();
2057 }
2058
2059 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
2060 $oldref = dol_sanitizeFileName($this->ref);
2061 $newref = dol_sanitizeFileName($num);
2062 $dirsource = $conf->propal->multidir_output[$this->entity].'/'.$oldref;
2063 $dirdest = $conf->propal->multidir_output[$this->entity].'/'.$newref;
2064 if (!$error && file_exists($dirsource)) {
2065 dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
2066 if (@rename($dirsource, $dirdest)) {
2067 dol_syslog("Rename ok");
2068 // Rename docs starting with $oldref with $newref
2069 $listoffiles = dol_dir_list($dirdest, 'files', 1, '^'.preg_quote($oldref, '/'));
2070 foreach ($listoffiles as $fileentry) {
2071 $dirsource = $fileentry['name'];
2072 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
2073 $dirsource = $fileentry['path'].'/'.$dirsource;
2074 $dirdest = $fileentry['path'].'/'.$dirdest;
2075 @rename($dirsource, $dirdest);
2076 }
2077 }
2078 }
2079 }
2080
2081 $this->ref = $num;
2084 $this->user_validation_id = $user->id;
2085 $this->datev = $now;
2086 $this->date_validation = $now;
2087
2088 $this->db->commit();
2089 return 1;
2090 } else {
2091 $this->db->rollback();
2092 return -1;
2093 }
2094 }
2095
2096
2097 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2106 public function set_date($user, $date, $notrigger = 0)
2107 {
2108 // phpcs:enable
2109 if (empty($date)) {
2110 $this->error = 'ErrorBadParameter';
2111 dol_syslog(get_class($this)."::set_date ".$this->error, LOG_ERR);
2112 return -1;
2113 }
2114
2115 if ($user->hasRight('propal', 'creer')) {
2116 $error = 0;
2117
2118 $this->db->begin();
2119
2120 $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET datep = '".$this->db->idate($date)."'";
2121 $sql .= " WHERE rowid = ".((int) $this->id);
2122
2123 dol_syslog(__METHOD__, LOG_DEBUG);
2124 $resql = $this->db->query($sql);
2125 if (!$resql) {
2126 $this->errors[] = $this->db->error();
2127 $error++;
2128 }
2129
2130 if (!$error) {
2131 $this->oldcopy = clone $this;
2132 $this->date = $date;
2133 $this->datep = $date; // deprecated
2134 }
2135
2136 if (!$notrigger && empty($error)) {
2137 // Call trigger
2138 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2139 if ($result < 0) {
2140 $error++;
2141 }
2142 // End call triggers
2143 }
2144
2145 if (!$error) {
2146 $this->db->commit();
2147 return 1;
2148 } else {
2149 foreach ($this->errors as $errmsg) {
2150 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2151 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2152 }
2153 $this->db->rollback();
2154 return -1 * $error;
2155 }
2156 }
2157
2158 return -1;
2159 }
2160
2161 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2170 public function set_echeance($user, $date_end_validity, $notrigger = 0)
2171 {
2172 // phpcs:enable
2173 if ($user->hasRight('propal', 'creer')) {
2174 $error = 0;
2175
2176 $this->db->begin();
2177
2178 $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET fin_validite = ".($date_end_validity != '' ? "'".$this->db->idate($date_end_validity)."'" : 'null');
2179 $sql .= " WHERE rowid = ".((int) $this->id);
2180
2181 dol_syslog(__METHOD__, LOG_DEBUG);
2182
2183 $resql = $this->db->query($sql);
2184 if (!$resql) {
2185 $this->errors[] = $this->db->error();
2186 $error++;
2187 }
2188
2189
2190 if (!$error) {
2191 $this->oldcopy = clone $this;
2192 $this->fin_validite = $date_end_validity;
2193 }
2194
2195 if (!$notrigger && empty($error)) {
2196 // Call trigger
2197 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2198 if ($result < 0) {
2199 $error++;
2200 }
2201 // End call triggers
2202 }
2203
2204 if (!$error) {
2205 $this->db->commit();
2206 return 1;
2207 } else {
2208 foreach ($this->errors as $errmsg) {
2209 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2210 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2211 }
2212 $this->db->rollback();
2213 return -1 * $error;
2214 }
2215 }
2216
2217 return -1;
2218 }
2219
2220 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2230 public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2231 {
2232 // phpcs:enable
2233 return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2234 }
2235
2244 public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2245 {
2246 if ($user->hasRight('propal', 'creer')) {
2247 $error = 0;
2248
2249 $this->db->begin();
2250
2251 $sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
2252 $sql .= " SET date_livraison = ".(isDolTms($delivery_date) ? "'".$this->db->idate($delivery_date)."'" : 'null');
2253 $sql .= " WHERE rowid = ".((int) $this->id);
2254
2255 dol_syslog(__METHOD__, LOG_DEBUG);
2256 $resql = $this->db->query($sql);
2257 if (!$resql) {
2258 $this->errors[] = $this->db->error();
2259 $error++;
2260 }
2261
2262 if (!$error) {
2263 $this->oldcopy = clone $this;
2264 $this->delivery_date = $delivery_date;
2265 }
2266
2267 if (!$notrigger && empty($error)) {
2268 // Call trigger
2269 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2270 if ($result < 0) {
2271 $error++;
2272 }
2273 // End call triggers
2274 }
2275
2276 if (!$error) {
2277 $this->db->commit();
2278 return 1;
2279 } else {
2280 foreach ($this->errors as $errmsg) {
2281 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2282 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2283 }
2284 $this->db->rollback();
2285 return -1 * $error;
2286 }
2287 }
2288
2289 return -1;
2290 }
2291
2292 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2301 public function set_availability($user, $id, $notrigger = 0)
2302 {
2303 // phpcs:enable
2304 if ($user->hasRight('propal', 'creer') && $this->status >= self::STATUS_DRAFT) {
2305 $error = 0;
2306
2307 $this->db->begin();
2308
2309 $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2310 $sql .= " SET fk_availability = ".((int) $id);
2311 $sql .= " WHERE rowid = ".((int) $this->id);
2312
2313 dol_syslog(__METHOD__.' availability('.$id.')', LOG_DEBUG);
2314 $resql = $this->db->query($sql);
2315 if (!$resql) {
2316 $this->errors[] = $this->db->error();
2317 $error++;
2318 }
2319
2320 if (!$error) {
2321 $this->oldcopy = clone $this;
2322 $this->fk_availability = $id;
2323 $this->availability_id = $id;
2324 }
2325
2326 if (!$notrigger && empty($error)) {
2327 // Call trigger
2328 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2329 if ($result < 0) {
2330 $error++;
2331 }
2332 // End call triggers
2333 }
2334
2335 if (!$error) {
2336 $this->db->commit();
2337 return 1;
2338 } else {
2339 foreach ($this->errors as $errmsg) {
2340 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2341 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2342 }
2343 $this->db->rollback();
2344 return -1 * $error;
2345 }
2346 } else {
2347 $error_str = 'Propal status do not meet requirement '.$this->status;
2348 dol_syslog(__METHOD__.$error_str, LOG_ERR);
2349 $this->error = $error_str;
2350 $this->errors[] = $this->error;
2351 return -2;
2352 }
2353 }
2354
2355 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2364 public function set_demand_reason($user, $id, $notrigger = 0)
2365 {
2366 // phpcs:enable
2367 if ($user->hasRight('propal', 'creer') && $this->status >= self::STATUS_DRAFT) {
2368 $error = 0;
2369
2370 $this->db->begin();
2371
2372 $sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
2373 $sql .= " SET fk_input_reason = ".((int) $id);
2374 $sql .= " WHERE rowid = ".((int) $this->id);
2375
2376 dol_syslog(__METHOD__, LOG_DEBUG);
2377 $resql = $this->db->query($sql);
2378 if (!$resql) {
2379 $this->errors[] = $this->db->error();
2380 $error++;
2381 }
2382
2383
2384 if (!$error) {
2385 $this->oldcopy = clone $this;
2386
2387 $this->demand_reason_id = $id;
2388 }
2389
2390
2391 if (!$notrigger && empty($error)) {
2392 // Call trigger
2393 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2394 if ($result < 0) {
2395 $error++;
2396 }
2397 // End call triggers
2398 }
2399
2400 if (!$error) {
2401 $this->db->commit();
2402 return 1;
2403 } else {
2404 foreach ($this->errors as $errmsg) {
2405 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2406 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2407 }
2408 $this->db->rollback();
2409 return -1 * $error;
2410 }
2411 } else {
2412 $error_str = 'Propal status do not meet requirement '.$this->status;
2413 dol_syslog(__METHOD__.$error_str, LOG_ERR);
2414 $this->error = $error_str;
2415 $this->errors[] = $this->error;
2416 return -2;
2417 }
2418 }
2419
2420 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2429 public function set_ref_client($user, $ref_client, $notrigger = 0)
2430 {
2431 // phpcs:enable
2432 if ($user->hasRight('propal', 'creer')) {
2433 $error = 0;
2434
2435 $this->db->begin();
2436
2437 $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET ref_client = ".(empty($ref_client) ? 'NULL' : "'".$this->db->escape($ref_client)."'");
2438 $sql .= " WHERE rowid = ".((int) $this->id);
2439
2440 dol_syslog(__METHOD__.' $this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
2441 $resql = $this->db->query($sql);
2442 if (!$resql) {
2443 $this->errors[] = $this->db->error();
2444 $error++;
2445 }
2446
2447 if (!$error) {
2448 $this->oldcopy = clone $this;
2449 $this->ref_client = $ref_client;
2450 }
2451
2452 if (!$notrigger && empty($error)) {
2453 // Call trigger
2454 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2455 if ($result < 0) {
2456 $error++;
2457 }
2458 // End call triggers
2459 }
2460
2461 if (!$error) {
2462 $this->db->commit();
2463 return 1;
2464 } else {
2465 foreach ($this->errors as $errmsg) {
2466 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2467 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2468 }
2469 $this->db->rollback();
2470 return -1 * $error;
2471 }
2472 }
2473
2474 return -1;
2475 }
2476
2477
2487 public function reopen($user, $status, $note = '', $notrigger = 0)
2488 {
2489 $error = 0;
2490
2491 $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2492 $sql .= " SET fk_statut = ".((int) $status).",";
2493 if (!empty($note)) {
2494 $sql .= " note_private = '".$this->db->escape($note)."',";
2495 }
2496 $sql .= " date_cloture=NULL, fk_user_cloture=NULL";
2497 $sql .= " WHERE rowid = ".((int) $this->id);
2498
2499 $this->db->begin();
2500
2501 dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
2502 $resql = $this->db->query($sql);
2503 if (!$resql) {
2504 $error++;
2505 $this->errors[] = "Error ".$this->db->lasterror();
2506 }
2507 if (!$error) {
2508 if (!$notrigger) {
2509 // Call trigger
2510 $result = $this->call_trigger('PROPAL_REOPEN', $user);
2511 if ($result < 0) {
2512 $error++;
2513 }
2514 // End call triggers
2515 }
2516 }
2517
2518 // Commit or rollback
2519 if ($error) {
2520 if (!empty($this->errors)) {
2521 foreach ($this->errors as $errmsg) {
2522 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
2523 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2524 }
2525 }
2526 $this->db->rollback();
2527 return -1 * $error;
2528 } else {
2529 $this->statut = $status;
2530 $this->status = $status;
2531
2532 $this->db->commit();
2533 return 1;
2534 }
2535 }
2536
2546 public function closeProposal($user, $status, $note = '', $notrigger = 0)
2547 {
2548 global $langs,$conf;
2549
2550 $error = 0;
2551 $now = dol_now();
2552
2553 $this->db->begin();
2554
2555 $newprivatenote = dol_concatdesc($this->note_private, $note);
2556
2557 if (!getDolGlobalString('PROPALE_KEEP_OLD_SIGNATURE_INFO')) {
2558 $date_signature = $now;
2559 $fk_user_signature = $user->id;
2560 } else {
2561 $this->info($this->id);
2562 if (!isset($this->date_signature) || $this->date_signature == '') {
2563 $date_signature = $now;
2564 $fk_user_signature = $user->id;
2565 } else {
2566 $date_signature = $this->date_signature;
2567 $fk_user_signature = $this->user_signature->id;
2568 }
2569 }
2570
2571 $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2572 $sql .= " SET fk_statut = ".((int) $status).", note_private = '".$this->db->escape($newprivatenote)."'";
2573 if ($status == self::STATUS_SIGNED) {
2574 $sql .= ", date_signature='".$this->db->idate($now)."', fk_user_signature = ".($fk_user_signature);
2575 }
2576 $sql .= " WHERE rowid = ".((int) $this->id);
2577
2578 $resql = $this->db->query($sql);
2579 if ($resql) {
2580 // Status self::STATUS_REFUSED by default
2581 $modelpdf = getDolGlobalString('PROPALE_ADDON_PDF_ODT_CLOSED', $this->model_pdf);
2582 $trigger_name = 'PROPAL_CLOSE_REFUSED'; // used later in call_trigger()
2583
2584 if ($status == self::STATUS_SIGNED) { // Status self::STATUS_SIGNED
2585 $trigger_name = 'PROPAL_CLOSE_SIGNED'; // used later in call_trigger()
2586 $modelpdf = getDolGlobalString('PROPALE_ADDON_PDF_ODT_TOBILL') ? $conf->global->PROPALE_ADDON_PDF_ODT_TOBILL : $this->model_pdf;
2587
2588 // The connected company is classified as a client
2589 $soc = new Societe($this->db);
2590 $soc->id = $this->socid;
2591 $result = $soc->setAsCustomer();
2592
2593 if ($result < 0) {
2594 $this->error = $this->db->lasterror();
2595 $this->db->rollback();
2596 return -2;
2597 }
2598 }
2599
2600 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE') && !getDolGlobalInt('PROPAL_DISABLE_AUTOUPDATE_ON_CLOSE')) {
2601 // Define output language
2602 $outputlangs = $langs;
2603 if (getDolGlobalInt('MAIN_MULTILANGS')) {
2604 $outputlangs = new Translate("", $conf);
2605 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
2606 $outputlangs->setDefaultLang($newlang);
2607 }
2608
2609 // PDF
2610 $hidedetails = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 1 : 0);
2611 $hidedesc = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 1 : 0);
2612 $hideref = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 1 : 0);
2613
2614 //$ret=$object->fetch($id); // Reload to get new records
2615 $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
2616 }
2617
2618 if (!$error) {
2619 $this->oldcopy = clone $this;
2620 $this->statut = $status;
2621 $this->status = $status;
2622 $this->date_signature = $date_signature;
2623 $this->note_private = $newprivatenote;
2624 }
2625
2626 if (!$notrigger && empty($error)) {
2627 // Call trigger
2628 $result = $this->call_trigger($trigger_name, $user);
2629 if ($result < 0) {
2630 $error++;
2631 }
2632 // End call triggers
2633 }
2634
2635 if (!$error) {
2636 $this->db->commit();
2637 return 1;
2638 } else {
2639 $this->statut = $this->oldcopy->status;
2640 $this->status = $this->oldcopy->status;
2641 $this->date_signature = $this->oldcopy->date_signature;
2642 $this->note_private = $this->oldcopy->note_private;
2643
2644 $this->db->rollback();
2645 return -1;
2646 }
2647 } else {
2648 $this->error = $this->db->lasterror();
2649 $this->db->rollback();
2650 return -1;
2651 }
2652 }
2653
2662 public function classifyBilled(User $user, $notrigger = 0, $note = '')
2663 {
2664 global $conf, $langs;
2665
2666 $error = 0;
2667
2668 $now = dol_now();
2669 $num = 0;
2670
2671 $triggerName = 'PROPAL_CLASSIFY_BILLED';
2672
2673 $this->db->begin();
2674
2675 $newprivatenote = dol_concatdesc($this->note_private, $note);
2676
2677 $sql = 'UPDATE '.MAIN_DB_PREFIX.'propal SET fk_statut = '.self::STATUS_BILLED.", ";
2678 $sql .= " note_private = '".$this->db->escape($newprivatenote)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".((int) $user->id);
2679 $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.((int) self::STATUS_SIGNED);
2680
2681 dol_syslog(__METHOD__, LOG_DEBUG);
2682 $resql = $this->db->query($sql);
2683 if (!$resql) {
2684 $this->errors[] = $this->db->error();
2685 $error++;
2686 } else {
2687 $num = $this->db->affected_rows($resql);
2688 }
2689
2690 if (!$error) {
2691 $modelpdf = getDolGlobalString('PROPALE_ADDON_PDF_ODT_CLOSED', $this->model_pdf);
2692
2693 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
2694 // Define output language
2695 $outputlangs = $langs;
2696 if (getDolGlobalInt('MAIN_MULTILANGS')) {
2697 $outputlangs = new Translate("", $conf);
2698 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
2699 $outputlangs->setDefaultLang($newlang);
2700 }
2701
2702 // PDF
2703 $hidedetails = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 1 : 0);
2704 $hidedesc = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 1 : 0);
2705 $hideref = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 1 : 0);
2706
2707 //$ret=$object->fetch($id); // Reload to get new records
2708 $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
2709 }
2710
2711 $this->oldcopy = clone $this;
2712 $this->statut = self::STATUS_BILLED;
2713 $this->status = self::STATUS_BILLED;
2714 $this->date_cloture = $now;
2715 $this->note_private = $newprivatenote;
2716 }
2717
2718 if (!$notrigger && empty($error)) {
2719 // Call trigger
2720 $result = $this->call_trigger($triggerName, $user);
2721 if ($result < 0) {
2722 $error++;
2723 }
2724 // End call triggers
2725 }
2726
2727 if (!$error) {
2728 $this->db->commit();
2729 return $num;
2730 } else {
2731 foreach ($this->errors as $errmsg) {
2732 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2733 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2734 }
2735 $this->db->rollback();
2736 return -1 * $error;
2737 }
2738 }
2739
2746 public function setCancel(User $user)
2747 {
2748 $error = 0;
2749
2750 $this->db->begin();
2751
2752 $sql = "UPDATE ". MAIN_DB_PREFIX . "propal";
2753 $sql .= " SET fk_statut = " . self::STATUS_CANCELED . ",";
2754 $sql .= " fk_user_modif = " . ((int) $user->id);
2755 $sql .= " WHERE rowid = " . ((int) $this->id);
2756
2757 dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
2758 if ($this->db->query($sql)) {
2759 if (!$error) {
2760 // Call trigger
2761 $result = $this->call_trigger('PROPAL_CANCEL', $user);
2762 if ($result < 0) {
2763 $error++;
2764 }
2765 // End call triggers
2766 }
2767
2768 if (!$error) {
2771 $this->db->commit();
2772 return 1;
2773 } else {
2774 foreach ($this->errors as $errmsg) {
2775 dol_syslog(get_class($this)."::cancel ".$errmsg, LOG_ERR);
2776 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2777 }
2778 $this->db->rollback();
2779 return -1;
2780 }
2781 } else {
2782 $this->error = $this->db->error();
2783 $this->db->rollback();
2784 return -1;
2785 }
2786 }
2787
2788 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2796 public function setDraft($user, $notrigger = 0)
2797 {
2798 // phpcs:enable
2799 $error = 0;
2800
2801 // Protection
2802 if ($this->status <= self::STATUS_DRAFT) {
2803 return 0;
2804 }
2805
2806 dol_syslog(get_class($this)."::setDraft", LOG_DEBUG);
2807
2808 $this->db->begin();
2809
2810 $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2811 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
2812 $sql .= ", online_sign_ip = NULL , online_sign_name = NULL";
2813 $sql .= " WHERE rowid = ".((int) $this->id);
2814
2815 $resql = $this->db->query($sql);
2816 if (!$resql) {
2817 $this->errors[] = $this->db->error();
2818 $error++;
2819 }
2820
2821 if (!$error) {
2822 $this->oldcopy = clone $this;
2823 }
2824
2825 if (!$notrigger && empty($error)) {
2826 // Call trigger
2827 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2828 if ($result < 0) {
2829 $error++;
2830 }
2831 // End call triggers
2832 }
2833
2834 if (!$error) {
2835 $this->statut = self::STATUS_DRAFT;
2836 $this->status = self::STATUS_DRAFT;
2837
2838 $this->db->commit();
2839 return 1;
2840 } else {
2841 foreach ($this->errors as $errmsg) {
2842 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2843 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2844 }
2845 $this->db->rollback();
2846 return -1 * $error;
2847 }
2848 }
2849
2850
2851 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2865 public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datep', $sortorder = 'DESC')
2866 {
2867 // phpcs:enable
2868 global $user;
2869
2870 $ga = array();
2871
2872 $sql = "SELECT s.rowid, s.nom as name, s.client,";
2873 $sql .= " p.rowid as propalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
2874 $sql .= " p.datep as dp, p.fin_validite as datelimite";
2875 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."propal as p, ".MAIN_DB_PREFIX."c_propalst as c";
2876 $sql .= " WHERE p.entity IN (".getEntity('propal').")";
2877 $sql .= " AND p.fk_soc = s.rowid";
2878 $sql .= " AND p.fk_statut = c.id";
2879
2880 // If the internal user must only see his customers, force searching by him
2881 $search_sale = 0;
2882 if (!$user->hasRight('societe', 'client', 'voir')) {
2883 $search_sale = $user->id;
2884 }
2885 // Search on sale representative
2886 if ($search_sale && $search_sale != '-1') {
2887 if ($search_sale == -2) {
2888 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
2889 } elseif ($search_sale > 0) {
2890 $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).")";
2891 }
2892 }
2893 // Search on socid
2894 if ($socid) {
2895 $sql .= " AND p.fk_soc = ".((int) $socid);
2896 }
2897 if ($draft) {
2898 $sql .= " AND p.fk_statut = ".((int) self::STATUS_DRAFT);
2899 }
2900 if ($notcurrentuser > 0) {
2901 $sql .= " AND p.fk_user_author <> ".((int) $user->id);
2902 }
2903 $sql .= $this->db->order($sortfield, $sortorder);
2904 $sql .= $this->db->plimit($limit, $offset);
2905
2906 $result = $this->db->query($sql);
2907 if ($result) {
2908 $num = $this->db->num_rows($result);
2909 if ($num) {
2910 $i = 0;
2911 while ($i < $num) {
2912 $obj = $this->db->fetch_object($result);
2913
2914 if ($shortlist == 1) {
2915 $ga[$obj->propalid] = $obj->ref;
2916 } elseif ($shortlist == 2) {
2917 $ga[$obj->propalid] = $obj->ref.' ('.$obj->name.')';
2918 } else {
2919 $ga[$i]['id'] = $obj->propalid;
2920 $ga[$i]['ref'] = $obj->ref;
2921 $ga[$i]['name'] = $obj->name;
2922 }
2923
2924 $i++;
2925 }
2926 }
2927 return $ga;
2928 } else {
2929 dol_print_error($this->db);
2930 return -1;
2931 }
2932 }
2933
2939 public function getInvoiceArrayList()
2940 {
2941 return $this->InvoiceArrayList($this->id);
2942 }
2943
2944 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2951 public function InvoiceArrayList($id)
2952 {
2953 // phpcs:enable
2954 $ga = array();
2955 $linkedInvoices = array();
2956
2957 $this->fetchObjectLinked($id, $this->element);
2958 foreach ($this->linkedObjectsIds as $objecttype => $objectid) {
2959 // Nouveau système du common object renvoi des rowid et non un id linéaire de 1 à n
2960 // On parcourt donc une liste d'objets en tant qu'objet unique
2961 foreach ($objectid as $key => $object) {
2962 // Cas des factures liees directement
2963 if ($objecttype == 'facture') {
2964 $linkedInvoices[] = $object;
2965 } else {
2966 // Cas des factures liees par un autre object (ex: commande)
2967 $this->fetchObjectLinked($object, $objecttype);
2968 foreach ($this->linkedObjectsIds as $subobjecttype => $subobjectid) {
2969 foreach ($subobjectid as $subkey => $subobject) {
2970 if ($subobjecttype == 'facture') {
2971 $linkedInvoices[] = $subobject;
2972 }
2973 }
2974 }
2975 }
2976 }
2977 }
2978
2979 if (count($linkedInvoices) > 0) {
2980 $sql = "SELECT rowid as facid, ref, total_ht as total, datef as df, fk_user_author, fk_statut, paye";
2981 $sql .= " FROM ".MAIN_DB_PREFIX."facture";
2982 $sql .= " WHERE rowid IN (".$this->db->sanitize(implode(',', $linkedInvoices)).")";
2983
2984 dol_syslog(get_class($this)."::InvoiceArrayList", LOG_DEBUG);
2985 $resql = $this->db->query($sql);
2986
2987 if ($resql) {
2988 $tab_sqlobj = array();
2989 $nump = $this->db->num_rows($resql);
2990 for ($i = 0; $i < $nump; $i++) {
2991 $sqlobj = $this->db->fetch_object($resql);
2992 $tab_sqlobj[] = $sqlobj;
2993 }
2994 $this->db->free($resql);
2995
2996 $nump = count($tab_sqlobj);
2997
2998 if ($nump) {
2999 $i = 0;
3000 while ($i < $nump) {
3001 $obj = array_shift($tab_sqlobj);
3002
3003 $ga[$i] = $obj;
3004
3005 $i++;
3006 }
3007 }
3008 return $ga;
3009 } else {
3010 return -1;
3011 }
3012 } else {
3013 return $ga;
3014 }
3015 }
3016
3024 public function delete($user, $notrigger = 0)
3025 {
3026 global $conf;
3027 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3028
3029 $error = 0;
3030
3031 $this->db->begin();
3032
3033 if (!$notrigger) {
3034 // Call trigger
3035 $result = $this->call_trigger('PROPAL_DELETE', $user);
3036 if ($result < 0) {
3037 $error++;
3038 }
3039 // End call triggers
3040 }
3041
3042 // Delete extrafields of lines and lines
3043 if (!$error && !empty($this->table_element_line)) {
3044 $tabletodelete = $this->table_element_line;
3045 $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).")";
3046 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id);
3047 if (!$this->db->query($sqlef) || !$this->db->query($sql)) {
3048 $error++;
3049 $this->error = $this->db->lasterror();
3050 $this->errors[] = $this->error;
3051 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3052 }
3053 }
3054
3055 if (!$error) {
3056 // Delete linked object
3057 $res = $this->deleteObjectLinked();
3058 if ($res < 0) {
3059 $error++;
3060 }
3061 }
3062
3063 if (!$error) {
3064 // Delete linked contacts
3065 $res = $this->delete_linked_contact();
3066 if ($res < 0) {
3067 $error++;
3068 }
3069 }
3070
3071 // Removed extrafields of object
3072 if (!$error) {
3073 $result = $this->deleteExtraFields();
3074 if ($result < 0) {
3075 $error++;
3076 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3077 }
3078 }
3079
3080 // Delete main record
3081 if (!$error) {
3082 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element." WHERE rowid = ".((int) $this->id);
3083 $res = $this->db->query($sql);
3084 if (!$res) {
3085 $error++;
3086 $this->error = $this->db->lasterror();
3087 $this->errors[] = $this->error;
3088 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3089 }
3090 }
3091
3092 // Delete record into ECM index and physically
3093 if (!$error) {
3094 $res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
3095 $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
3096 if (!$res) {
3097 $error++;
3098 }
3099 }
3100
3101 if (!$error) {
3102 // We remove directory
3103 $ref = dol_sanitizeFileName($this->ref);
3104 if ($conf->propal->multidir_output[$this->entity] && !empty($this->ref)) {
3105 $dir = $conf->propal->multidir_output[$this->entity]."/".$ref;
3106 $file = $dir."/".$ref.".pdf";
3107 if (file_exists($file)) {
3108 dol_delete_preview($this);
3109
3110 if (!dol_delete_file($file, 0, 0, 0, $this)) {
3111 $this->error = 'ErrorFailToDeleteFile';
3112 $this->errors[] = $this->error;
3113 $this->db->rollback();
3114 return 0;
3115 }
3116 }
3117 if (file_exists($dir)) {
3118 $res = @dol_delete_dir_recursive($dir); // delete files physically + into ecm tables
3119 if (!$res) {
3120 $this->error = 'ErrorFailToDeleteDir';
3121 $this->errors[] = $this->error;
3122 $this->db->rollback();
3123 return 0;
3124 }
3125 }
3126 }
3127 }
3128
3129 if (!$error) {
3130 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
3131 $this->db->commit();
3132 return 1;
3133 } else {
3134 $this->db->rollback();
3135 return -1;
3136 }
3137 }
3138
3147 public function availability($availability_id, $notrigger = 0)
3148 {
3149 global $user;
3150
3151 if ($this->status >= self::STATUS_DRAFT) {
3152 $error = 0;
3153
3154 $this->db->begin();
3155
3156 $sql = 'UPDATE '.MAIN_DB_PREFIX.'propal';
3157 $sql .= ' SET fk_availability = '.((int) $availability_id);
3158 $sql .= ' WHERE rowid='.((int) $this->id);
3159
3160 dol_syslog(__METHOD__.' availability('.$availability_id.')', LOG_DEBUG);
3161 $resql = $this->db->query($sql);
3162 if (!$resql) {
3163 $this->errors[] = $this->db->error();
3164 $error++;
3165 }
3166
3167 if (!$error) {
3168 $this->oldcopy = clone $this;
3169 $this->availability_id = $availability_id;
3170 }
3171
3172 if (!$notrigger && empty($error)) {
3173 // Call trigger
3174 $result = $this->call_trigger('PROPAL_MODIFY', $user);
3175 if ($result < 0) {
3176 $error++;
3177 }
3178 // End call triggers
3179 }
3180
3181 if (!$error) {
3182 $this->db->commit();
3183 return 1;
3184 } else {
3185 foreach ($this->errors as $errmsg) {
3186 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
3187 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3188 }
3189 $this->db->rollback();
3190 return -1 * $error;
3191 }
3192 } else {
3193 $error_str = 'Propal status do not meet requirement '.$this->status;
3194 dol_syslog(__METHOD__.$error_str, LOG_ERR);
3195 $this->error = $error_str;
3196 $this->errors[] = $this->error;
3197 return -2;
3198 }
3199 }
3200
3201 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3210 public function demand_reason($demand_reason_id, $notrigger = 0)
3211 {
3212 // phpcs:enable
3213 global $user;
3214
3215 if ($this->status >= self::STATUS_DRAFT) {
3216 $error = 0;
3217
3218 $this->db->begin();
3219
3220 $sql = 'UPDATE '.MAIN_DB_PREFIX.'propal';
3221 $sql .= ' SET fk_input_reason = '.((int) $demand_reason_id);
3222 $sql .= ' WHERE rowid='.((int) $this->id);
3223
3224 dol_syslog(__METHOD__.' demand_reason('.$demand_reason_id.')', LOG_DEBUG);
3225 $resql = $this->db->query($sql);
3226 if (!$resql) {
3227 $this->errors[] = $this->db->error();
3228 $error++;
3229 }
3230
3231 if (!$error) {
3232 $this->oldcopy = clone $this;
3233 $this->demand_reason_id = $demand_reason_id;
3234 }
3235
3236 if (!$notrigger && empty($error)) {
3237 // Call trigger
3238 $result = $this->call_trigger('PROPAL_MODIFY', $user);
3239 if ($result < 0) {
3240 $error++;
3241 }
3242 // End call triggers
3243 }
3244
3245 if (!$error) {
3246 $this->db->commit();
3247 return 1;
3248 } else {
3249 foreach ($this->errors as $errmsg) {
3250 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
3251 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3252 }
3253 $this->db->rollback();
3254 return -1 * $error;
3255 }
3256 } else {
3257 $error_str = 'Propal status do not meet requirement '.$this->status;
3258 dol_syslog(__METHOD__.$error_str, LOG_ERR);
3259 $this->error = $error_str;
3260 $this->errors[] = $this->error;
3261 return -2;
3262 }
3263 }
3264
3265
3272 public function info($id)
3273 {
3274 $sql = "SELECT c.rowid, ";
3275 $sql .= " c.datec, c.date_valid as datev, c.date_signature, c.date_cloture,";
3276 $sql .= " c.fk_user_author, c.fk_user_valid, c.fk_user_signature, c.fk_user_cloture";
3277 $sql .= " FROM ".MAIN_DB_PREFIX."propal as c";
3278 $sql .= " WHERE c.rowid = ".((int) $id);
3279
3280 $result = $this->db->query($sql);
3281
3282 if ($result) {
3283 if ($this->db->num_rows($result)) {
3284 $obj = $this->db->fetch_object($result);
3285
3286 $this->id = $obj->rowid;
3287
3288 $this->date_creation = $this->db->jdate($obj->datec);
3289 $this->date_validation = $this->db->jdate($obj->datev);
3290 $this->date_signature = $this->db->jdate($obj->date_signature);
3291 $this->date_cloture = $this->db->jdate($obj->date_cloture);
3292
3293 $this->user_creation_id = $obj->fk_user_author;
3294 $this->user_validation_id = $obj->fk_user_valid;
3295
3296 if ($obj->fk_user_signature) {
3297 $user_signature = new User($this->db);
3298 $user_signature->fetch($obj->fk_user_signature);
3299 $this->user_signature = $user_signature;
3300 }
3301
3302 $this->user_closing_id = $obj->fk_user_cloture;
3303 }
3304 $this->db->free($result);
3305 } else {
3306 dol_print_error($this->db);
3307 }
3308 }
3309
3310
3317 public function getLibStatut($mode = 0)
3318 {
3319 return $this->LibStatut($this->status, $mode);
3320 }
3321
3322 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3330 public function LibStatut($status, $mode = 1)
3331 {
3332 // phpcs:enable
3333 global $hookmanager;
3334
3335 // Init/load array of translation of status
3336 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
3337 global $langs;
3338 $langs->load("propal");
3339 $this->labelStatus[-1] = $langs->transnoentitiesnoconv("PropalStatusCanceled");
3340 $this->labelStatus[0] = $langs->transnoentitiesnoconv("PropalStatusDraft");
3341 $this->labelStatus[1] = $langs->transnoentitiesnoconv("PropalStatusValidated");
3342 $this->labelStatus[2] = $langs->transnoentitiesnoconv("PropalStatusSigned");
3343 $this->labelStatus[3] = $langs->transnoentitiesnoconv("PropalStatusNotSigned");
3344 $this->labelStatus[4] = $langs->transnoentitiesnoconv("PropalStatusBilled");
3345 $this->labelStatusShort[-1] = $langs->transnoentitiesnoconv("PropalStatusCanceledShort");
3346 $this->labelStatusShort[0] = $langs->transnoentitiesnoconv("PropalStatusDraftShort");
3347 $this->labelStatusShort[1] = $langs->transnoentitiesnoconv("PropalStatusValidatedShort");
3348 $this->labelStatusShort[2] = $langs->transnoentitiesnoconv("PropalStatusSignedShort");
3349 $this->labelStatusShort[3] = $langs->transnoentitiesnoconv("PropalStatusNotSignedShort");
3350 $this->labelStatusShort[4] = $langs->transnoentitiesnoconv("PropalStatusBilledShort");
3351 }
3352
3353 $statusType = '';
3354 if ($status == self::STATUS_CANCELED) {
3355 $statusType = 'status9';
3356 } elseif ($status == self::STATUS_DRAFT) {
3357 $statusType = 'status0';
3358 } elseif ($status == self::STATUS_VALIDATED) {
3359 $statusType = 'status1';
3360 } elseif ($status == self::STATUS_SIGNED) {
3361 $statusType = 'status4';
3362 } elseif ($status == self::STATUS_NOTSIGNED) {
3363 $statusType = 'status9';
3364 } elseif ($status == self::STATUS_BILLED) {
3365 $statusType = 'status6';
3366 }
3367
3368 $parameters = array('status' => $status, 'mode' => $mode);
3369 $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
3370
3371 if ($reshook > 0) {
3372 return $hookmanager->resPrint;
3373 }
3374
3375 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
3376 }
3377
3378
3379 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3387 public function load_board($user, $mode)
3388 {
3389 // phpcs:enable
3390 global $conf, $langs;
3391
3392 $clause = " WHERE";
3393
3394 $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.fin_validite as datefin, p.total_ht";
3395 $sql .= " FROM ".MAIN_DB_PREFIX."propal as p";
3396 $sql .= $clause." p.entity IN (".getEntity('propal').")";
3397 if ($mode == 'opened') {
3398 $sql .= " AND p.fk_statut = ".self::STATUS_VALIDATED;
3399 }
3400 if ($mode == 'signed') {
3401 $sql .= " AND p.fk_statut = ".self::STATUS_SIGNED;
3402 }
3403 // If the internal user must only see his customers, force searching by him
3404 $search_sale = 0;
3405 if (!$user->hasRight('societe', 'client', 'voir')) {
3406 $search_sale = $user->id;
3407 }
3408 // Search on sale representative
3409 if ($search_sale && $search_sale != '-1') {
3410 if ($search_sale == -2) {
3411 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
3412 } elseif ($search_sale > 0) {
3413 $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).")";
3414 }
3415 }
3416
3417 $resql = $this->db->query($sql);
3418 if ($resql) {
3419 $langs->load("propal");
3420 $now = dol_now();
3421
3422 $delay_warning = 0;
3423 $status = 0;
3424 $label = $labelShort = '';
3425 if ($mode == 'opened') {
3426 $delay_warning = $conf->propal->cloture->warning_delay;
3427 $status = self::STATUS_VALIDATED;
3428 $label = $langs->transnoentitiesnoconv("PropalsToClose");
3429 $labelShort = $langs->transnoentitiesnoconv("ToAcceptRefuse");
3430 }
3431 if ($mode == 'signed') {
3432 $delay_warning = $conf->propal->facturation->warning_delay;
3433 $status = self::STATUS_SIGNED;
3434 $label = $langs->trans("PropalsToBill"); // We set here bill but may be billed or ordered
3435 $labelShort = $langs->trans("ToBill");
3436 }
3437
3438 $response = new WorkboardResponse();
3439 $response->warning_delay = $delay_warning / 60 / 60 / 24;
3440 $response->label = $label;
3441 $response->labelShort = $labelShort;
3442 $response->url = DOL_URL_ROOT.'/comm/propal/list.php?search_status='.$status.'&mainmenu=commercial&leftmenu=propals';
3443 $response->url_late = DOL_URL_ROOT.'/comm/propal/list.php?search_status='.$status.'&mainmenu=commercial&leftmenu=propals&sortfield=p.datep&sortorder=asc';
3444 $response->img = img_object('', "propal");
3445
3446 // This assignment in condition is not a bug. It allows walking the results.
3447 while ($obj = $this->db->fetch_object($resql)) {
3448 $response->nbtodo++;
3449 $response->total += $obj->total_ht;
3450
3451 if ($mode == 'opened') {
3452 $datelimit = $this->db->jdate($obj->datefin);
3453 if ($datelimit < ($now - $delay_warning)) {
3454 $response->nbtodolate++;
3455 }
3456 }
3457 // TODO Definir regle des propales a facturer en retard
3458 // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
3459 }
3460
3461 return $response;
3462 } else {
3463 $this->error = $this->db->error();
3464 return -1;
3465 }
3466 }
3467
3468
3476 public function initAsSpecimen()
3477 {
3478 global $conf, $langs;
3479
3480 // Load array of products prodids
3481 $num_prods = 0;
3482 $prodids = array();
3483 $sql = "SELECT rowid";
3484 $sql .= " FROM ".MAIN_DB_PREFIX."product";
3485 $sql .= " WHERE entity IN (".getEntity('product').")";
3486 $sql .= $this->db->plimit(100);
3487
3488 $resql = $this->db->query($sql);
3489 if ($resql) {
3490 $num_prods = $this->db->num_rows($resql);
3491 $i = 0;
3492 while ($i < $num_prods) {
3493 $i++;
3494 $row = $this->db->fetch_row($resql);
3495 $prodids[$i] = $row[0];
3496 }
3497 }
3498
3499 // Initialise parameters
3500 $this->id = 0;
3501 $this->ref = 'SPECIMEN';
3502 $this->ref_client = 'NEMICEPS';
3503 $this->specimen = 1;
3504 $this->socid = 1;
3505 $this->date = time();
3506 $this->fin_validite = $this->date + 3600 * 24 * 30;
3507 $this->cond_reglement_id = 1;
3508 $this->cond_reglement_code = 'RECEP';
3509 $this->mode_reglement_id = 7;
3510 $this->mode_reglement_code = 'CHQ';
3511 $this->availability_id = 1;
3512 $this->availability_code = 'AV_NOW';
3513 $this->demand_reason_id = 1;
3514 $this->demand_reason_code = 'SRC_00';
3515 $this->note_public = 'This is a comment (public)';
3516 $this->note_private = 'This is a comment (private)';
3517
3518 $this->multicurrency_tx = 1;
3519 $this->multicurrency_code = $conf->currency;
3520
3521 // Lines
3522 $nbp = 5;
3523 $xnbp = 0;
3524 while ($xnbp < $nbp) {
3525 $line = new PropaleLigne($this->db);
3526 $line->desc = $langs->trans("Description")." ".$xnbp;
3527 $line->qty = 1;
3528 $line->subprice = 100;
3529 $line->price = 100;
3530 $line->tva_tx = 20;
3531 $line->localtax1_tx = 0;
3532 $line->localtax2_tx = 0;
3533 if ($xnbp == 2) {
3534 $line->total_ht = 50;
3535 $line->total_ttc = 60;
3536 $line->total_tva = 10;
3537 $line->remise_percent = 50;
3538 } else {
3539 $line->total_ht = 100;
3540 $line->total_ttc = 120;
3541 $line->total_tva = 20;
3542 $line->remise_percent = 00;
3543 }
3544
3545 if ($num_prods > 0) {
3546 $prodid = mt_rand(1, $num_prods);
3547 $line->fk_product = $prodids[$prodid];
3548 $line->product_ref = 'SPECIMEN';
3549 }
3550
3551 $this->lines[$xnbp] = $line;
3552
3553 $this->total_ht += $line->total_ht;
3554 $this->total_tva += $line->total_tva;
3555 $this->total_ttc += $line->total_ttc;
3556
3557 $xnbp++;
3558 }
3559
3560 return 1;
3561 }
3562
3568 public function loadStateBoard()
3569 {
3570 global $user;
3571
3572 $this->nb = array();
3573 $clause = "WHERE";
3574
3575 $sql = "SELECT count(p.rowid) as nb";
3576 $sql .= " FROM ".MAIN_DB_PREFIX."propal as p";
3577 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
3578 $sql .= " ".$clause." p.entity IN (".getEntity('propal').")";
3579
3580 // If the internal user must only see his customers, force searching by him
3581 $search_sale = 0;
3582 if (!$user->hasRight('societe', 'client', 'voir')) {
3583 $search_sale = $user->id;
3584 }
3585 // Search on sale representative
3586 if ($search_sale && $search_sale != '-1') {
3587 if ($search_sale == -2) {
3588 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
3589 } elseif ($search_sale > 0) {
3590 $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).")";
3591 }
3592 }
3593
3594 $resql = $this->db->query($sql);
3595 if ($resql) {
3596 // This assignment in condition is not a bug. It allows walking the results.
3597 while ($obj = $this->db->fetch_object($resql)) {
3598 $this->nb["proposals"] = $obj->nb;
3599 }
3600 $this->db->free($resql);
3601 return 1;
3602 } else {
3603 dol_print_error($this->db);
3604 $this->error = $this->db->error();
3605 return -1;
3606 }
3607 }
3608
3609
3617 public function getNextNumRef($soc)
3618 {
3619 global $conf, $langs;
3620 $langs->load("propal");
3621
3622 $classname = getDolGlobalString('PROPALE_ADDON');
3623
3624 if (!empty($classname)) {
3625 $mybool = false;
3626
3627 $file = $classname.".php";
3628
3629 // Include file with class
3630 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
3631 foreach ($dirmodels as $reldir) {
3632 $dir = dol_buildpath($reldir."core/modules/propale/");
3633
3634 // Load file with numbering class (if found)
3635 $mybool = ((bool) @include_once $dir.$file) || $mybool;
3636 }
3637
3638 if (!$mybool) {
3639 dol_print_error(null, "Failed to include file ".$file);
3640 return '';
3641 }
3642
3643 $obj = new $classname();
3644 $numref = "";
3645 $numref = $obj->getNextValue($soc, $this);
3646
3647 if ($numref != "") {
3648 return $numref;
3649 } else {
3650 $this->error = $obj->error;
3651 //dol_print_error($db,"Propale::getNextNumRef ".$obj->error);
3652 return "";
3653 }
3654 } else {
3655 $langs->load("errors");
3656 print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("Proposal"));
3657 return "";
3658 }
3659 }
3660
3667 public function getTooltipContentArray($params)
3668 {
3669 global $conf, $langs, $user;
3670
3671 $langs->load('propal');
3672 $datas = [];
3673 $nofetch = !empty($params['nofetch']);
3674
3675 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3676 return ['optimize' => $langs->trans("Proposal")];
3677 }
3678 if ($user->hasRight('propal', 'lire')) {
3679 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("Proposal").'</u>';
3680 if (isset($this->status)) {
3681 $datas['status'] = ' '.$this->getLibStatut(5);
3682 }
3683 if (!empty($this->ref)) {
3684 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
3685 }
3686 if (!$nofetch) {
3687 $langs->load('companies');
3688 if (empty($this->thirdparty)) {
3689 $this->fetch_thirdparty();
3690 }
3691 $datas['customer'] = '<br><b>'.$langs->trans('Customer').':</b> '.$this->thirdparty->getNomUrl(1, '', 0, 1);
3692 }
3693 if (!empty($this->ref_customer)) {
3694 $datas['refcustomer'] = '<br><b>'.$langs->trans('RefCustomer').':</b> '.$this->ref_customer;
3695 }
3696 if (!$nofetch) {
3697 $langs->load('project');
3698 if (is_null($this->project) || (is_object($this->project) && $this->project->isEmpty())) {
3699 $res = $this->fetch_project();
3700 if ($res > 0 && $this->project instanceof Project) {
3701 $datas['project'] = '<br><b>'.$langs->trans('Project').':</b> '.$this->project->getNomUrl(1, '', 0, 1);
3702 }
3703 }
3704 }
3705 if (!empty($this->total_ht)) {
3706 $datas['amountht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3707 }
3708 if (!empty($this->total_tva)) {
3709 $datas['vat'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
3710 }
3711 if (!empty($this->total_ttc)) {
3712 $datas['amountttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3713 }
3714 if (!empty($this->date)) {
3715 $datas['date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
3716 }
3717 if (!empty($this->delivery_date)) {
3718 $datas['deliverydate'] = '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
3719 }
3720 }
3721
3722 return $datas;
3723 }
3724
3736 public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = -1)
3737 {
3738 global $langs, $conf, $user, $hookmanager;
3739
3740 if (!empty($conf->dol_no_mouse_hover)) {
3741 $notooltip = 1; // Force disable tooltips
3742 }
3743
3744 $result = '';
3745 $params = [
3746 'id' => $this->id,
3747 'objecttype' => $this->element,
3748 'option' => $option,
3749 'nofetch' => 1,
3750 ];
3751 $classfortooltip = 'classfortooltip';
3752 $dataparams = '';
3753 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
3754 $classfortooltip = 'classforajaxtooltip';
3755 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
3756 $label = '';
3757 } else {
3758 $label = implode($this->getTooltipContentArray($params));
3759 }
3760
3761 $url = '';
3762 if ($user->hasRight('propal', 'lire')) {
3763 if ($option == '') {
3764 $url = DOL_URL_ROOT.'/comm/propal/card.php?id='.$this->id.$get_params;
3765 } elseif ($option == 'compta') { // deprecated
3766 $url = DOL_URL_ROOT.'/comm/propal/card.php?id='.$this->id.$get_params;
3767 } elseif ($option == 'expedition') {
3768 $url = DOL_URL_ROOT.'/expedition/propal.php?id='.$this->id.$get_params;
3769 } elseif ($option == 'document') {
3770 $url = DOL_URL_ROOT.'/comm/propal/document.php?id='.$this->id.$get_params;
3771 }
3772
3773 if ($option != 'nolink') {
3774 // Add param to save lastsearch_values or not
3775 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
3776 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
3777 $add_save_lastsearch_values = 1;
3778 }
3779 if ($add_save_lastsearch_values) {
3780 $url .= '&save_lastsearch_values=1';
3781 }
3782 }
3783 }
3784
3785 $linkclose = '';
3786 if (empty($notooltip) && $user->hasRight('propal', 'lire')) {
3787 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3788 $label = $langs->trans("Proposal");
3789 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
3790 }
3791 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
3792 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
3793 }
3794
3795 $linkstart = '<a href="'.$url.'"';
3796 $linkstart .= $linkclose.'>';
3797 $linkend = '</a>';
3798
3799 $result .= $linkstart;
3800 if ($withpicto) {
3801 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
3802 }
3803 if ($withpicto != 2) {
3804 $result .= $this->ref;
3805 }
3806 $result .= $linkend;
3807
3808 if ($addlinktonotes >= 0) {
3809 $txttoshow = '';
3810
3811 if ($addlinktonotes == 0) {
3812 if (!empty($this->note_private) || !empty($this->note_public)) {
3813 $txttoshow = $langs->trans('ViewPrivateNote');
3814 }
3815 } elseif ($addlinktonotes == 1) {
3816 if (!empty($this->note_private)) {
3817 $txttoshow .= ($user->socid > 0 ? '' : dol_string_nohtmltag($this->note_private, 1));
3818 }
3819 } elseif ($addlinktonotes == 2) {
3820 if (!empty($this->note_public)) {
3821 $txttoshow .= dol_string_nohtmltag($this->note_public, 1);
3822 }
3823 } elseif ($addlinktonotes == 3) {
3824 if ($user->socid > 0) {
3825 if (!empty($this->note_public)) {
3826 $txttoshow .= dol_string_nohtmltag($this->note_public, 1);
3827 }
3828 } else {
3829 if (!empty($this->note_public)) {
3830 $txttoshow .= dol_string_nohtmltag($this->note_public, 1);
3831 }
3832 if (!empty($this->note_private)) {
3833 if (!empty($txttoshow)) {
3834 $txttoshow .= '<br><br>';
3835 }
3836 $txttoshow .= dol_string_nohtmltag($this->note_private, 1);
3837 }
3838 }
3839 }
3840
3841 if ($txttoshow) {
3842 $result .= ' <span class="note inline-block">';
3843 $result .= '<a href="'.DOL_URL_ROOT.'/comm/propal/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($txttoshow).'">';
3844 $result .= img_picto('', 'note');
3845 $result .= '</a>';
3846 $result .= '</span>';
3847 }
3848 }
3849
3850 global $action;
3851 $hookmanager->initHooks(array($this->element . 'dao'));
3852 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
3853 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3854 if ($reshook > 0) {
3855 $result = $hookmanager->resPrint;
3856 } else {
3857 $result .= $hookmanager->resPrint;
3858 }
3859 return $result;
3860 }
3861
3868 public function getLinesArray($sqlforgedfilters = '')
3869 {
3870 return $this->fetch_lines(0, 0, $sqlforgedfilters);
3871 }
3872
3884 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3885 {
3886 global $conf, $langs;
3887
3888 $langs->load("propale");
3889 $outputlangs->load("products");
3890
3891 if (!dol_strlen($modele)) {
3892 $modele = 'azur';
3893
3894 if ($this->model_pdf) {
3895 $modele = $this->model_pdf;
3896 } elseif (getDolGlobalString('PROPALE_ADDON_PDF')) {
3897 $modele = getDolGlobalString('PROPALE_ADDON_PDF');
3898 }
3899 }
3900
3901 $modelpath = "core/modules/propale/doc/";
3902
3903 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3904 }
3905
3914 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3915 {
3916 $tables = array(
3917 'propal'
3918 );
3919
3920 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3921 }
3922
3931 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3932 {
3933 $tables = array(
3934 'propaldet'
3935 );
3936
3937 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3938 }
3939
3947 public function getKanbanView($option = '', $arraydata = null)
3948 {
3949 global $langs;
3950
3951 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3952
3953 $return = '<div class="box-flex-item box-flex-grow-zero">';
3954 $return .= '<div class="info-box info-box-sm">';
3955 $return .= '<div class="info-box-icon bg-infobox-action">';
3956 $return .= img_picto('', $this->picto);
3957 $return .= '</div>';
3958 $return .= '<div class="info-box-content">';
3959 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
3960 if ($selected >= 0) {
3961 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3962 }
3963 if (!empty($arraydata['projectlink'])) {
3964 $return .= '<span class="info-box-ref"> | '.$arraydata['projectlink'].'</span>';
3965 }
3966 $return .= '<br>';
3967 if (property_exists($this, 'thirdparty') && is_object($this->thirdparty)) {
3968 $return .= '<div class="info-box-ref tdoverflowmax150">'.$this->thirdparty->getNomUrl(1).'</div>';
3969 }
3970 if (property_exists($this, 'total_ht')) {
3971 $return .= '<span class="info-box-label amount" title="'.$langs->trans("AmountHT").'">'.price($this->total_ht).'</span>';
3972 }
3973 if (!empty($arraydata['authorlink'])) {
3974 $return .= ' &nbsp; <span class="info-box-label">'.$arraydata['authorlink'].'</span>';
3975 }
3976 if (method_exists($this, 'getLibStatut')) {
3977 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
3978 }
3979 $return .= '</div>';
3980 $return .= '</div>';
3981 $return .= '</div>';
3982 return $return;
3983 }
3984}
3985
3990{
3994 public $element = 'propaldet';
3995
3999 public $table_element = 'propaldet';
4000
4004 public $parent_element = 'propal';
4005
4009 public $fk_parent_attribute = 'fk_propal';
4010
4011 public $oldline;
4012
4013 // From llx_propaldet
4014 public $fk_propal;
4015 public $fk_parent_line;
4016 public $desc; // Description ligne
4017 public $fk_product; // Id produit predefini
4028 public $product_type = Product::TYPE_PRODUCT;
4029
4030 public $qty;
4031
4032 public $tva_tx;
4033 public $vat_src_code;
4034
4039 public $subprice;
4040 public $remise_percent;
4041 public $fk_remise_except;
4042
4043 public $rang = 0;
4044
4045 public $fk_fournprice;
4046 public $pa_ht;
4047 public $marge_tx;
4048 public $marque_tx;
4049
4056 public $special_code; // Tag for special lines (exclusive tags)
4057
4058 public $info_bits = 0; // Some other info:
4059 // Bit 0: 0 si TVA normal - 1 if TVA NPR
4060 // Bit 1: 0 ligne normal - 1 if line with fixed discount
4061
4062 public $total_ht; // Total HT de la ligne toute quantite et incluant la remise ligne
4063 public $total_tva; // Total TVA de la ligne toute quantite et incluant la remise ligne
4064 public $total_ttc; // Total TTC de la ligne toute quantite et incluant la remise ligne
4065
4070 public $remise;
4075 public $price;
4076
4077 // From llx_product
4082 public $ref;
4087 public $product_ref;
4092 public $libelle;
4097 public $label;
4102 public $product_label;
4107 public $product_desc;
4108
4113 public $product_tobatch;
4114
4119 public $product_barcode;
4120
4121 public $localtax1_tx; // Local tax 1
4122 public $localtax2_tx; // Local tax 2
4123 public $localtax1_type; // Local tax 1 type
4124 public $localtax2_type; // Local tax 2 type
4125 public $total_localtax1; // Line total local tax 1
4126 public $total_localtax2; // Line total local tax 2
4127
4128 public $date_start;
4129 public $date_end;
4130
4131 public $skip_update_total; // Skip update price total for special lines
4132
4133 // Multicurrency
4134 public $fk_multicurrency;
4135 public $multicurrency_code;
4136 public $multicurrency_subprice;
4137 public $multicurrency_total_ht;
4138 public $multicurrency_total_tva;
4139 public $multicurrency_total_ttc;
4140
4141
4147 public function __construct($db)
4148 {
4149 $this->db = $db;
4150 }
4151
4158 public function fetch($rowid)
4159 {
4160 $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,';
4161 $sql .= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice,';
4162 $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,';
4163 $sql .= ' pd.fk_unit,';
4164 $sql .= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
4165 $sql .= ' pd.fk_multicurrency, pd.multicurrency_code, pd.multicurrency_subprice, pd.multicurrency_total_ht, pd.multicurrency_total_tva, pd.multicurrency_total_ttc,';
4166 $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
4167 $sql .= ' pd.date_start, pd.date_end, pd.product_type';
4168 $sql .= ' FROM '.MAIN_DB_PREFIX.'propaldet as pd';
4169 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
4170 $sql .= ' WHERE pd.rowid = '.((int) $rowid);
4171
4172 $result = $this->db->query($sql);
4173 if ($result) {
4174 $objp = $this->db->fetch_object($result);
4175
4176 if ($objp) {
4177 $this->id = $objp->rowid;
4178 $this->rowid = $objp->rowid; // deprecated
4179 $this->fk_propal = $objp->fk_propal;
4180 $this->fk_parent_line = $objp->fk_parent_line;
4181 $this->label = $objp->custom_label;
4182 $this->desc = $objp->description;
4183 $this->qty = $objp->qty;
4184 $this->price = $objp->price; // deprecated
4185 $this->subprice = $objp->subprice;
4186 $this->vat_src_code = $objp->vat_src_code;
4187 $this->tva_tx = $objp->tva_tx;
4188 $this->remise = $objp->remise; // deprecated
4189 $this->remise_percent = $objp->remise_percent;
4190 $this->fk_remise_except = $objp->fk_remise_except;
4191 $this->fk_product = $objp->fk_product;
4192 $this->info_bits = $objp->info_bits;
4193
4194 $this->total_ht = $objp->total_ht;
4195 $this->total_tva = $objp->total_tva;
4196 $this->total_ttc = $objp->total_ttc;
4197
4198 $this->fk_fournprice = $objp->fk_fournprice;
4199
4200 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
4201 $this->pa_ht = $marginInfos[0];
4202 $this->marge_tx = $marginInfos[1];
4203 $this->marque_tx = $marginInfos[2];
4204
4205 $this->special_code = $objp->special_code;
4206 $this->product_type = $objp->product_type;
4207 $this->rang = $objp->rang;
4208
4209 $this->ref = $objp->product_ref; // deprecated
4210 $this->product_ref = $objp->product_ref;
4211 $this->libelle = $objp->product_label; // deprecated
4212 $this->product_label = $objp->product_label;
4213 $this->product_desc = $objp->product_desc;
4214 $this->fk_unit = $objp->fk_unit;
4215
4216 $this->date_start = $this->db->jdate($objp->date_start);
4217 $this->date_end = $this->db->jdate($objp->date_end);
4218
4219 // Multicurrency
4220 $this->fk_multicurrency = $objp->fk_multicurrency;
4221 $this->multicurrency_code = $objp->multicurrency_code;
4222 $this->multicurrency_subprice = $objp->multicurrency_subprice;
4223 $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
4224 $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
4225 $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
4226
4227 $this->fetch_optionals();
4228
4229 $this->db->free($result);
4230
4231 return 1;
4232 } else {
4233 return 0;
4234 }
4235 } else {
4236 return -1;
4237 }
4238 }
4239
4246 public function insert($notrigger = 0)
4247 {
4248 global $conf, $user;
4249
4250 $error = 0;
4251
4252 dol_syslog(get_class($this)."::insert rang=".$this->rang);
4253
4254 $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'.
4255
4256 // Clean parameters
4257 if (empty($this->tva_tx)) {
4258 $this->tva_tx = 0;
4259 }
4260 if (empty($this->localtax1_tx)) {
4261 $this->localtax1_tx = 0;
4262 }
4263 if (empty($this->localtax2_tx)) {
4264 $this->localtax2_tx = 0;
4265 }
4266 if (empty($this->localtax1_type)) {
4267 $this->localtax1_type = 0;
4268 }
4269 if (empty($this->localtax2_type)) {
4270 $this->localtax2_type = 0;
4271 }
4272 if (empty($this->total_localtax1)) {
4273 $this->total_localtax1 = 0;
4274 }
4275 if (empty($this->total_localtax2)) {
4276 $this->total_localtax2 = 0;
4277 }
4278 if (empty($this->rang)) {
4279 $this->rang = 0;
4280 }
4281 if (empty($this->remise_percent) || !is_numeric($this->remise_percent)) {
4282 $this->remise_percent = 0;
4283 }
4284 if (empty($this->info_bits)) {
4285 $this->info_bits = 0;
4286 }
4287 if (empty($this->special_code)) {
4288 $this->special_code = 0;
4289 }
4290 if (empty($this->fk_parent_line)) {
4291 $this->fk_parent_line = 0;
4292 }
4293 if (empty($this->fk_fournprice)) {
4294 $this->fk_fournprice = 0;
4295 }
4296 if (!is_numeric($this->qty)) {
4297 $this->qty = 0;
4298 }
4299 if (empty($this->pa_ht)) {
4300 $this->pa_ht = 0;
4301 }
4302 if (empty($this->multicurrency_subprice)) {
4303 $this->multicurrency_subprice = 0;
4304 }
4305 if (empty($this->multicurrency_total_ht)) {
4306 $this->multicurrency_total_ht = 0;
4307 }
4308 if (empty($this->multicurrency_total_tva)) {
4309 $this->multicurrency_total_tva = 0;
4310 }
4311 if (empty($this->multicurrency_total_ttc)) {
4312 $this->multicurrency_total_ttc = 0;
4313 }
4314
4315 // if buy price not defined, define buyprice as configured in margin admin
4316 if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
4317 if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0) {
4318 return $result;
4319 } else {
4320 $this->pa_ht = $result;
4321 }
4322 }
4323
4324 // Check parameters
4325 if ($this->product_type < 0) {
4326 return -1;
4327 }
4328
4329 $this->db->begin();
4330
4331 // Insert line into database
4332 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'propaldet';
4333 $sql .= ' (fk_propal, fk_parent_line, label, description, fk_product, product_type,';
4334 $sql .= ' fk_remise_except, qty, vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
4335 $sql .= ' subprice, remise_percent, ';
4336 $sql .= ' info_bits, ';
4337 $sql .= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
4338 $sql .= ' fk_unit,';
4339 $sql .= ' date_start, date_end';
4340 $sql .= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc)';
4341 $sql .= " VALUES (".$this->fk_propal.",";
4342 $sql .= " ".($this->fk_parent_line > 0 ? "'".$this->db->escape($this->fk_parent_line)."'" : "null").",";
4343 $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
4344 $sql .= " '".$this->db->escape($this->desc)."',";
4345 $sql .= " ".($this->fk_product ? "'".$this->db->escape($this->fk_product)."'" : "null").",";
4346 $sql .= " '".$this->db->escape($this->product_type)."',";
4347 $sql .= " ".($this->fk_remise_except ? "'".$this->db->escape($this->fk_remise_except)."'" : "null").",";
4348 $sql .= " ".price2num($this->qty, 'MS').",";
4349 $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
4350 $sql .= " ".price2num($this->tva_tx).",";
4351 $sql .= " ".price2num($this->localtax1_tx).",";
4352 $sql .= " ".price2num($this->localtax2_tx).",";
4353 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
4354 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
4355 $sql .= " ".(price2num($this->subprice) !== '' ? price2num($this->subprice, 'MU') : "null").",";
4356 $sql .= " ".price2num($this->remise_percent).",";
4357 $sql .= " ".(isset($this->info_bits) ? ((int) $this->info_bits) : "null").",";
4358 $sql .= " ".price2num($this->total_ht, 'MT').",";
4359 $sql .= " ".price2num($this->total_tva, 'MT').",";
4360 $sql .= " ".price2num($this->total_localtax1, 'MT').",";
4361 $sql .= " ".price2num($this->total_localtax2, 'MT').",";
4362 $sql .= " ".price2num($this->total_ttc, 'MT').",";
4363 $sql .= " ".(!empty($this->fk_fournprice) ? "'".$this->db->escape($this->fk_fournprice)."'" : "null").",";
4364 $sql .= " ".(isset($this->pa_ht) ? "'".price2num($this->pa_ht)."'" : "null").",";
4365 $sql .= ' '.((int) $this->special_code).',';
4366 $sql .= ' '.((int) $this->rang).',';
4367 $sql .= ' '.(empty($this->fk_unit) ? 'NULL' : ((int) $this->fk_unit)).',';
4368 $sql .= " ".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null").',';
4369 $sql .= " ".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
4370 $sql .= ", ".($this->fk_multicurrency > 0 ? ((int) $this->fk_multicurrency) : 'null');
4371 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
4372 $sql .= ", ".price2num($this->multicurrency_subprice, 'CU');
4373 $sql .= ", ".price2num($this->multicurrency_total_ht, 'CT');
4374 $sql .= ", ".price2num($this->multicurrency_total_tva, 'CT');
4375 $sql .= ", ".price2num($this->multicurrency_total_ttc, 'CT');
4376 $sql .= ')';
4377
4378 dol_syslog(get_class($this).'::insert', LOG_DEBUG);
4379 $resql = $this->db->query($sql);
4380 if ($resql) {
4381 $this->rowid = $this->db->last_insert_id(MAIN_DB_PREFIX.'propaldet');
4382
4383 if (!$error) {
4384 $this->id = $this->rowid;
4385 $result = $this->insertExtraFields();
4386 if ($result < 0) {
4387 $error++;
4388 }
4389 }
4390
4391 if (!$error && !$notrigger) {
4392 // Call trigger
4393 $result = $this->call_trigger('LINEPROPAL_INSERT', $user);
4394 if ($result < 0) {
4395 $this->db->rollback();
4396 return -1;
4397 }
4398 // End call triggers
4399 }
4400
4401 $this->db->commit();
4402 return 1;
4403 } else {
4404 $this->error = $this->db->error()." sql=".$sql;
4405 $this->db->rollback();
4406 return -1;
4407 }
4408 }
4409
4417 public function delete(User $user, $notrigger = 0)
4418 {
4419 global $conf;
4420
4421 $error = 0;
4422 $this->db->begin();
4423
4424 if (!$notrigger) {
4425 // Call trigger
4426 $result = $this->call_trigger('LINEPROPAL_DELETE', $user);
4427 if ($result < 0) {
4428 $error++;
4429 }
4430 }
4431 // End call triggers
4432
4433 if (!$error) {
4434 $sql = "DELETE FROM " . MAIN_DB_PREFIX . "propaldet WHERE rowid = " . ((int) $this->rowid);
4435 dol_syslog("PropaleLigne::delete", LOG_DEBUG);
4436 if ($this->db->query($sql)) {
4437 // Remove extrafields
4438 if (!$error) {
4439 $this->id = $this->rowid;
4440 $result = $this->deleteExtraFields();
4441 if ($result < 0) {
4442 $error++;
4443 dol_syslog(get_class($this) . "::delete error -4 " . $this->error, LOG_ERR);
4444 }
4445 }
4446 } else {
4447 $this->error = $this->db->error() . " sql=" . $sql;
4448 $error++;
4449 }
4450 }
4451
4452 if ($error) {
4453 $this->db->rollback();
4454 return -1;
4455 } else {
4456 $this->db->commit();
4457 return 1;
4458 }
4459 }
4460
4467 public function update($notrigger = 0)
4468 {
4469 global $conf, $user;
4470
4471 $error = 0;
4472
4473 $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'.
4474
4475 if (empty($this->id) && !empty($this->rowid)) {
4476 $this->id = $this->rowid;
4477 }
4478
4479 // Clean parameters
4480 if (empty($this->tva_tx)) {
4481 $this->tva_tx = 0;
4482 }
4483 if (empty($this->localtax1_tx)) {
4484 $this->localtax1_tx = 0;
4485 }
4486 if (empty($this->localtax2_tx)) {
4487 $this->localtax2_tx = 0;
4488 }
4489 if (empty($this->total_localtax1)) {
4490 $this->total_localtax1 = 0;
4491 }
4492 if (empty($this->total_localtax2)) {
4493 $this->total_localtax2 = 0;
4494 }
4495 if (empty($this->localtax1_type)) {
4496 $this->localtax1_type = 0;
4497 }
4498 if (empty($this->localtax2_type)) {
4499 $this->localtax2_type = 0;
4500 }
4501 if (empty($this->marque_tx)) {
4502 $this->marque_tx = 0;
4503 }
4504 if (empty($this->marge_tx)) {
4505 $this->marge_tx = 0;
4506 }
4507 if (empty($this->price)) {
4508 $this->price = 0; // TODO A virer
4509 }
4510 if (empty($this->remise_percent)) {
4511 $this->remise_percent = 0;
4512 }
4513 if (empty($this->info_bits)) {
4514 $this->info_bits = 0;
4515 }
4516 if (empty($this->special_code)) {
4517 $this->special_code = 0;
4518 }
4519 if (empty($this->fk_parent_line)) {
4520 $this->fk_parent_line = 0;
4521 }
4522 if (empty($this->fk_fournprice)) {
4523 $this->fk_fournprice = 0;
4524 }
4525 if (empty($this->subprice)) {
4526 $this->subprice = 0;
4527 }
4528 if (empty($this->pa_ht)) {
4529 $this->pa_ht = 0;
4530 }
4531
4532 // if buy price not defined, define buyprice as configured in margin admin
4533 if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
4534 if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0) {
4535 return $result;
4536 } else {
4537 $this->pa_ht = $result;
4538 }
4539 }
4540
4541 $this->db->begin();
4542
4543 // Mise a jour ligne en base
4544 $sql = "UPDATE ".MAIN_DB_PREFIX."propaldet SET";
4545 $sql .= " description='".$this->db->escape($this->desc)."'";
4546 $sql .= ", label=".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
4547 $sql .= ", product_type=".$this->product_type;
4548 $sql .= ", vat_src_code = '".(empty($this->vat_src_code) ? '' : $this->vat_src_code)."'";
4549 $sql .= ", tva_tx='".price2num($this->tva_tx)."'";
4550 $sql .= ", localtax1_tx=".price2num($this->localtax1_tx);
4551 $sql .= ", localtax2_tx=".price2num($this->localtax2_tx);
4552 $sql .= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4553 $sql .= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4554 $sql .= ", qty='".price2num($this->qty)."'";
4555 $sql .= ", subprice=".price2num($this->subprice);
4556 $sql .= ", remise_percent=".price2num($this->remise_percent);
4557 $sql .= ", price=".(float) price2num($this->price); // TODO A virer
4558 $sql .= ", remise=".(float) price2num($this->remise); // TODO A virer
4559 $sql .= ", info_bits='".$this->db->escape($this->info_bits)."'";
4560 if (empty($this->skip_update_total)) {
4561 $sql .= ", total_ht=".price2num($this->total_ht);
4562 $sql .= ", total_tva=".price2num($this->total_tva);
4563 $sql .= ", total_ttc=".price2num($this->total_ttc);
4564 $sql .= ", total_localtax1=".price2num($this->total_localtax1);
4565 $sql .= ", total_localtax2=".price2num($this->total_localtax2);
4566 }
4567 $sql .= ", fk_product_fournisseur_price=".(!empty($this->fk_fournprice) ? "'".$this->db->escape($this->fk_fournprice)."'" : "null");
4568 $sql .= ", buy_price_ht=".price2num($this->pa_ht);
4569 $sql .= ", special_code=".((int) $this->special_code);
4570 $sql .= ", fk_parent_line=".($this->fk_parent_line > 0 ? (int) $this->fk_parent_line : "null");
4571 if (!empty($this->rang)) {
4572 $sql .= ", rang=".((int) $this->rang);
4573 }
4574 $sql .= ", date_start=".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null");
4575 $sql .= ", date_end=".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
4576 $sql .= ", fk_unit=".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4577
4578 // Multicurrency
4579 $sql .= ", multicurrency_subprice=".price2num($this->multicurrency_subprice);
4580 $sql .= ", multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
4581 $sql .= ", multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
4582 $sql .= ", multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
4583
4584 $sql .= " WHERE rowid = ".((int) $this->id);
4585
4586 dol_syslog(get_class($this)."::update", LOG_DEBUG);
4587 $resql = $this->db->query($sql);
4588 if ($resql) {
4589 if (!$error) {
4590 $result = $this->insertExtraFields();
4591 if ($result < 0) {
4592 $error++;
4593 }
4594 }
4595
4596 if (!$error && !$notrigger) {
4597 // Call trigger
4598 $result = $this->call_trigger('LINEPROPAL_MODIFY', $user);
4599 if ($result < 0) {
4600 $this->db->rollback();
4601 return -1;
4602 }
4603 // End call triggers
4604 }
4605
4606 $this->db->commit();
4607 return 1;
4608 } else {
4609 $this->error = $this->db->error();
4610 $this->db->rollback();
4611 return -2;
4612 }
4613 }
4614
4615 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4622 public function update_total()
4623 {
4624 // phpcs:enable
4625 $this->db->begin();
4626
4627 // Mise a jour ligne en base
4628 $sql = "UPDATE ".MAIN_DB_PREFIX."propaldet SET";
4629 $sql .= " total_ht=".price2num($this->total_ht, 'MT');
4630 $sql .= ",total_tva=".price2num($this->total_tva, 'MT');
4631 $sql .= ",total_ttc=".price2num($this->total_ttc, 'MT');
4632 $sql .= " WHERE rowid = ".((int) $this->rowid);
4633
4634 dol_syslog("PropaleLigne::update_total", LOG_DEBUG);
4635
4636 $resql = $this->db->query($sql);
4637 if ($resql) {
4638 $this->db->commit();
4639 return 1;
4640 } else {
4641 $this->error = $this->db->error();
4642 $this->db->rollback();
4643 return -2;
4644 }
4645 }
4646}
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:58
print $langs trans("AuditedSecurityEvents").'</strong >< span class="opacitymedium"></span >< br > status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition security.php:626
$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 '.
isDolTms($timestamp)
isDolTms check if a timestamp is valid.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that returns whether VAT must be recoverable collected VAT (e.g.: VAT NPR in France)
dol_concatdesc($text1, $text2, $forxml=false, $invert=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return 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:1929
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:1929