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