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