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