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