dolibarr 19.0.4
propal.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2002-2004 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2004 Eric Seigne <eric.seigne@ryxeo.com>
4 * Copyright (C) 2004-2011 Laurent Destailleur <eldy@users.sourceforge.net>
5 * Copyright (C) 2005 Marc Barilley <marc@ocebo.com>
6 * Copyright (C) 2005-2013 Regis Houssin <regis.houssin@inodbox.com>
7 * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
8 * Copyright (C) 2008 Raphael Bertrand <raphael.bertrand@resultic.fr>
9 * Copyright (C) 2010-2020 Juanjo Menent <jmenent@2byte.es>
10 * Copyright (C) 2010-2022 Philippe Grand <philippe.grand@atoo-net.com>
11 * Copyright (C) 2012-2014 Christophe Battarel <christophe.battarel@altairis.fr>
12 * Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
13 * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
14 * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
15 * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
16 * Copyright (C) 2018-2021 Frédéric France <frederic.france@netlogic.fr>
17 * Copyright (C) 2018 Ferran Marcet <fmarcet@2byte.es>
18 * Copyright (C) 2022 ATM Consulting <contact@atm-consulting.fr>
19 * Copyright (C) 2022 OpenDSI <support@open-dsi.fr>
20 * Copyright (C) 2022 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
21 *
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 = ".(!empty($this->user_validation_id) ? (int) $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;
2069 $this->user_validation_id = $user->id;
2070 $this->datev = $now;
2071 $this->date_validation = $now;
2072
2073 $this->db->commit();
2074 return 1;
2075 } else {
2076 $this->db->rollback();
2077 return -1;
2078 }
2079 }
2080
2081
2082 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2091 public function set_date($user, $date, $notrigger = 0)
2092 {
2093 // phpcs:enable
2094 if (empty($date)) {
2095 $this->error = 'ErrorBadParameter';
2096 dol_syslog(get_class($this)."::set_date ".$this->error, LOG_ERR);
2097 return -1;
2098 }
2099
2100 if ($user->hasRight('propal', 'creer')) {
2101 $error = 0;
2102
2103 $this->db->begin();
2104
2105 $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET datep = '".$this->db->idate($date)."'";
2106 $sql .= " WHERE rowid = ".((int) $this->id);
2107
2108 dol_syslog(__METHOD__, LOG_DEBUG);
2109 $resql = $this->db->query($sql);
2110 if (!$resql) {
2111 $this->errors[] = $this->db->error();
2112 $error++;
2113 }
2114
2115 if (!$error) {
2116 $this->oldcopy = clone $this;
2117 $this->date = $date;
2118 $this->datep = $date; // deprecated
2119 }
2120
2121 if (!$notrigger && empty($error)) {
2122 // Call trigger
2123 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2124 if ($result < 0) {
2125 $error++;
2126 }
2127 // End call triggers
2128 }
2129
2130 if (!$error) {
2131 $this->db->commit();
2132 return 1;
2133 } else {
2134 foreach ($this->errors as $errmsg) {
2135 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2136 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2137 }
2138 $this->db->rollback();
2139 return -1 * $error;
2140 }
2141 }
2142
2143 return -1;
2144 }
2145
2146 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2155 public function set_echeance($user, $date_end_validity, $notrigger = 0)
2156 {
2157 // phpcs:enable
2158 if ($user->hasRight('propal', 'creer')) {
2159 $error = 0;
2160
2161 $this->db->begin();
2162
2163 $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET fin_validite = ".($date_end_validity != '' ? "'".$this->db->idate($date_end_validity)."'" : 'null');
2164 $sql .= " WHERE rowid = ".((int) $this->id);
2165
2166 dol_syslog(__METHOD__, LOG_DEBUG);
2167
2168 $resql = $this->db->query($sql);
2169 if (!$resql) {
2170 $this->errors[] = $this->db->error();
2171 $error++;
2172 }
2173
2174
2175 if (!$error) {
2176 $this->oldcopy = clone $this;
2177 $this->fin_validite = $date_end_validity;
2178 }
2179
2180 if (!$notrigger && empty($error)) {
2181 // Call trigger
2182 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2183 if ($result < 0) {
2184 $error++;
2185 }
2186 // End call triggers
2187 }
2188
2189 if (!$error) {
2190 $this->db->commit();
2191 return 1;
2192 } else {
2193 foreach ($this->errors as $errmsg) {
2194 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2195 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2196 }
2197 $this->db->rollback();
2198 return -1 * $error;
2199 }
2200 }
2201
2202 return -1;
2203 }
2204
2205 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2215 public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2216 {
2217 // phpcs:enable
2218 return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2219 }
2220
2229 public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2230 {
2231 if ($user->hasRight('propal', 'creer')) {
2232 $error = 0;
2233
2234 $this->db->begin();
2235
2236 $sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
2237 $sql .= " SET date_livraison = ".($delivery_date != '' ? "'".$this->db->idate($delivery_date)."'" : 'null');
2238 $sql .= " WHERE rowid = ".((int) $this->id);
2239
2240 dol_syslog(__METHOD__, LOG_DEBUG);
2241 $resql = $this->db->query($sql);
2242 if (!$resql) {
2243 $this->errors[] = $this->db->error();
2244 $error++;
2245 }
2246
2247 if (!$error) {
2248 $this->oldcopy = clone $this;
2249 $this->delivery_date = $delivery_date;
2250 }
2251
2252 if (!$notrigger && empty($error)) {
2253 // Call trigger
2254 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2255 if ($result < 0) {
2256 $error++;
2257 }
2258 // End call triggers
2259 }
2260
2261 if (!$error) {
2262 $this->db->commit();
2263 return 1;
2264 } else {
2265 foreach ($this->errors as $errmsg) {
2266 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2267 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2268 }
2269 $this->db->rollback();
2270 return -1 * $error;
2271 }
2272 }
2273
2274 return -1;
2275 }
2276
2277 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2286 public function set_availability($user, $id, $notrigger = 0)
2287 {
2288 // phpcs:enable
2289 if ($user->hasRight('propal', 'creer') && $this->statut >= self::STATUS_DRAFT) {
2290 $error = 0;
2291
2292 $this->db->begin();
2293
2294 $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2295 $sql .= " SET fk_availability = ".((int) $id);
2296 $sql .= " WHERE rowid = ".((int) $this->id);
2297
2298 dol_syslog(__METHOD__.' availability('.$id.')', LOG_DEBUG);
2299 $resql = $this->db->query($sql);
2300 if (!$resql) {
2301 $this->errors[] = $this->db->error();
2302 $error++;
2303 }
2304
2305 if (!$error) {
2306 $this->oldcopy = clone $this;
2307 $this->fk_availability = $id;
2308 $this->availability_id = $id;
2309 }
2310
2311 if (!$notrigger && empty($error)) {
2312 // Call trigger
2313 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2314 if ($result < 0) {
2315 $error++;
2316 }
2317 // End call triggers
2318 }
2319
2320 if (!$error) {
2321 $this->db->commit();
2322 return 1;
2323 } else {
2324 foreach ($this->errors as $errmsg) {
2325 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2326 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2327 }
2328 $this->db->rollback();
2329 return -1 * $error;
2330 }
2331 } else {
2332 $error_str = 'Propal status do not meet requirement '.$this->statut;
2333 dol_syslog(__METHOD__.$error_str, LOG_ERR);
2334 $this->error = $error_str;
2335 $this->errors[] = $this->error;
2336 return -2;
2337 }
2338 }
2339
2340 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2349 public function set_demand_reason($user, $id, $notrigger = 0)
2350 {
2351 // phpcs:enable
2352 if ($user->hasRight('propal', 'creer') && $this->statut >= self::STATUS_DRAFT) {
2353 $error = 0;
2354
2355 $this->db->begin();
2356
2357 $sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
2358 $sql .= " SET fk_input_reason = ".((int) $id);
2359 $sql .= " WHERE rowid = ".((int) $this->id);
2360
2361 dol_syslog(__METHOD__, LOG_DEBUG);
2362 $resql = $this->db->query($sql);
2363 if (!$resql) {
2364 $this->errors[] = $this->db->error();
2365 $error++;
2366 }
2367
2368
2369 if (!$error) {
2370 $this->oldcopy = clone $this;
2371
2372 $this->demand_reason_id = $id;
2373 }
2374
2375
2376 if (!$notrigger && empty($error)) {
2377 // Call trigger
2378 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2379 if ($result < 0) {
2380 $error++;
2381 }
2382 // End call triggers
2383 }
2384
2385 if (!$error) {
2386 $this->db->commit();
2387 return 1;
2388 } else {
2389 foreach ($this->errors as $errmsg) {
2390 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2391 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2392 }
2393 $this->db->rollback();
2394 return -1 * $error;
2395 }
2396 } else {
2397 $error_str = 'Propal status do not meet requirement '.$this->statut;
2398 dol_syslog(__METHOD__.$error_str, LOG_ERR);
2399 $this->error = $error_str;
2400 $this->errors[] = $this->error;
2401 return -2;
2402 }
2403 }
2404
2405 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2414 public function set_ref_client($user, $ref_client, $notrigger = 0)
2415 {
2416 // phpcs:enable
2417 if ($user->hasRight('propal', 'creer')) {
2418 $error = 0;
2419
2420 $this->db->begin();
2421
2422 $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET ref_client = ".(empty($ref_client) ? 'NULL' : "'".$this->db->escape($ref_client)."'");
2423 $sql .= " WHERE rowid = ".((int) $this->id);
2424
2425 dol_syslog(__METHOD__.' $this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
2426 $resql = $this->db->query($sql);
2427 if (!$resql) {
2428 $this->errors[] = $this->db->error();
2429 $error++;
2430 }
2431
2432 if (!$error) {
2433 $this->oldcopy = clone $this;
2434 $this->ref_client = $ref_client;
2435 }
2436
2437 if (!$notrigger && empty($error)) {
2438 // Call trigger
2439 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2440 if ($result < 0) {
2441 $error++;
2442 }
2443 // End call triggers
2444 }
2445
2446 if (!$error) {
2447 $this->db->commit();
2448 return 1;
2449 } else {
2450 foreach ($this->errors as $errmsg) {
2451 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2452 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2453 }
2454 $this->db->rollback();
2455 return -1 * $error;
2456 }
2457 }
2458
2459 return -1;
2460 }
2461
2462 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2472 /*
2473 public function set_remise_percent($user, $remise, $notrigger = 0)
2474 {
2475 // phpcs:enable
2476 $remise = trim($remise) ?trim($remise) : 0;
2477
2478 if ($user->hasRight('propal', 'creer')) {
2479 $remise = price2num($remise, 2);
2480
2481 $error = 0;
2482
2483 $this->db->begin();
2484
2485 $sql = "UPDATE ".MAIN_DB_PREFIX."propal SET remise_percent = ".((float) $remise);
2486 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = ".self::STATUS_DRAFT;
2487
2488 dol_syslog(__METHOD__, LOG_DEBUG);
2489 $resql = $this->db->query($sql);
2490 if (!$resql) {
2491 $this->errors[] = $this->db->error();
2492 $error++;
2493 }
2494
2495 if (!$error) {
2496 $this->oldcopy = clone $this;
2497 $this->remise_percent = $remise;
2498 $this->update_price(1);
2499 }
2500
2501 if (!$notrigger && empty($error)) {
2502 // Call trigger
2503 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2504 if ($result < 0) {
2505 $error++;
2506 }
2507 // End call triggers
2508 }
2509
2510 if (!$error) {
2511 $this->db->commit();
2512 return 1;
2513 } else {
2514 foreach ($this->errors as $errmsg) {
2515 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2516 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2517 }
2518 $this->db->rollback();
2519 return -1 * $error;
2520 }
2521 }
2522
2523 return -1;
2524 }
2525 */
2526
2527 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2536 /*
2537 public function set_remise_absolue($user, $remise, $notrigger = 0)
2538 {
2539 // phpcs:enable
2540 if (empty($remise)) {
2541 $remise = 0;
2542 }
2543 $remise = price2num($remise);
2544
2545 if ($user->hasRight('propal', 'creer')) {
2546 $error = 0;
2547
2548 $this->db->begin();
2549
2550 $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2551 $sql .= " SET remise_absolue = ".((float) $remise);
2552 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = ".self::STATUS_DRAFT;
2553
2554 dol_syslog(__METHOD__, LOG_DEBUG);
2555 $resql = $this->db->query($sql);
2556 if (!$resql) {
2557 $this->errors[] = $this->db->error();
2558 $error++;
2559 }
2560
2561 if (!$error) {
2562 $this->oldcopy = clone $this;
2563 $this->update_price(1);
2564 }
2565
2566 if (!$notrigger && empty($error)) {
2567 // Call trigger
2568 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2569 if ($result < 0) {
2570 $error++;
2571 }
2572 // End call triggers
2573 }
2574
2575 if (!$error) {
2576 $this->db->commit();
2577 return 1;
2578 } else {
2579 foreach ($this->errors as $errmsg) {
2580 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2581 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2582 }
2583 $this->db->rollback();
2584 return -1 * $error;
2585 }
2586 }
2587
2588 return -1;
2589 }
2590 */
2591
2592
2602 public function reopen($user, $status, $note = '', $notrigger = 0)
2603 {
2604 $error = 0;
2605
2606 $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2607 $sql .= " SET fk_statut = ".((int) $status).",";
2608 if (!empty($note)) {
2609 $sql .= " note_private = '".$this->db->escape($note)."',";
2610 }
2611 $sql .= " date_cloture=NULL, fk_user_cloture=NULL";
2612 $sql .= " WHERE rowid = ".((int) $this->id);
2613
2614 $this->db->begin();
2615
2616 dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
2617 $resql = $this->db->query($sql);
2618 if (!$resql) {
2619 $error++;
2620 $this->errors[] = "Error ".$this->db->lasterror();
2621 }
2622 if (!$error) {
2623 if (!$notrigger) {
2624 // Call trigger
2625 $result = $this->call_trigger('PROPAL_REOPEN', $user);
2626 if ($result < 0) {
2627 $error++;
2628 }
2629 // End call triggers
2630 }
2631 }
2632
2633 // Commit or rollback
2634 if ($error) {
2635 if (!empty($this->errors)) {
2636 foreach ($this->errors as $errmsg) {
2637 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
2638 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2639 }
2640 }
2641 $this->db->rollback();
2642 return -1 * $error;
2643 } else {
2644 $this->statut = $status;
2645 $this->status = $status;
2646
2647 $this->db->commit();
2648 return 1;
2649 }
2650 }
2651
2661 public function closeProposal($user, $status, $note = '', $notrigger = 0)
2662 {
2663 global $langs,$conf;
2664
2665 $error = 0;
2666 $now = dol_now();
2667
2668 $this->db->begin();
2669
2670 $newprivatenote = dol_concatdesc($this->note_private, $note);
2671
2672 if (!getDolGlobalString('PROPALE_KEEP_OLD_SIGNATURE_INFO')) {
2673 $date_signature = $now;
2674 $fk_user_signature = $user->id;
2675 } else {
2676 $this->info($this->id);
2677 if (!isset($this->date_signature) || $this->date_signature == '') {
2678 $date_signature = $now;
2679 $fk_user_signature = $user->id;
2680 } else {
2681 $date_signature = $this->date_signature;
2682 $fk_user_signature = $this->user_signature->id;
2683 }
2684 }
2685
2686 $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2687 $sql .= " SET fk_statut = ".((int) $status).", note_private = '".$this->db->escape($newprivatenote)."'";
2688 if ($status == self::STATUS_SIGNED) {
2689 $sql .= ", date_signature='".$this->db->idate($now)."', fk_user_signature = ".($fk_user_signature);
2690 }
2691 $sql .= " WHERE rowid = ".((int) $this->id);
2692
2693 $resql = $this->db->query($sql);
2694 if ($resql) {
2695 // Status self::STATUS_REFUSED by default
2696 $modelpdf = getDolGlobalString('PROPALE_ADDON_PDF_ODT_CLOSED') ? $conf->global->PROPALE_ADDON_PDF_ODT_CLOSED : $this->model_pdf;
2697 $trigger_name = 'PROPAL_CLOSE_REFUSED'; // used later in call_trigger()
2698
2699 if ($status == self::STATUS_SIGNED) { // Status self::STATUS_SIGNED
2700 $trigger_name = 'PROPAL_CLOSE_SIGNED'; // used later in call_trigger()
2701 $modelpdf = getDolGlobalString('PROPALE_ADDON_PDF_ODT_TOBILL') ? $conf->global->PROPALE_ADDON_PDF_ODT_TOBILL : $this->model_pdf;
2702
2703 // The connected company is classified as a client
2704 $soc=new Societe($this->db);
2705 $soc->id = $this->socid;
2706 $result = $soc->setAsCustomer();
2707
2708 if ($result < 0) {
2709 $this->error=$this->db->lasterror();
2710 $this->db->rollback();
2711 return -2;
2712 }
2713 }
2714
2715 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE') && !getDolGlobalInt('PROPAL_DISABLE_AUTOUPDATE_ON_CLOSE')) {
2716 // Define output language
2717 $outputlangs = $langs;
2718 if (getDolGlobalInt('MAIN_MULTILANGS')) {
2719 $outputlangs = new Translate("", $conf);
2720 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
2721 $outputlangs->setDefaultLang($newlang);
2722 }
2723
2724 // PDF
2725 $hidedetails = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 1 : 0);
2726 $hidedesc = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 1 : 0);
2727 $hideref = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 1 : 0);
2728
2729 //$ret=$object->fetch($id); // Reload to get new records
2730 $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
2731 }
2732
2733 if (!$error) {
2734 $this->oldcopy= clone $this;
2735 $this->statut = $status;
2736 $this->status = $status;
2737 $this->date_signature = $date_signature;
2738 $this->note_private = $newprivatenote;
2739 }
2740
2741 if (!$notrigger && empty($error)) {
2742 // Call trigger
2743 $result=$this->call_trigger($trigger_name, $user);
2744 if ($result < 0) {
2745 $error++;
2746 }
2747 // End call triggers
2748 }
2749
2750 if (!$error) {
2751 $this->db->commit();
2752 return 1;
2753 } else {
2754 $this->statut = $this->oldcopy->statut;
2755 $this->status = $this->oldcopy->statut;
2756 $this->date_signature = $this->oldcopy->date_signature;
2757 $this->note_private = $this->oldcopy->note_private;
2758
2759 $this->db->rollback();
2760 return -1;
2761 }
2762 } else {
2763 $this->error = $this->db->lasterror();
2764 $this->db->rollback();
2765 return -1;
2766 }
2767 }
2768
2777 public function classifyBilled(User $user, $notrigger = 0, $note = '')
2778 {
2779 global $conf, $langs;
2780
2781 $error = 0;
2782
2783 $now = dol_now();
2784 $num = 0;
2785
2786 $triggerName = 'PROPAL_CLASSIFY_BILLED';
2787
2788 $this->db->begin();
2789
2790 $newprivatenote = dol_concatdesc($this->note_private, $note);
2791
2792 $sql = 'UPDATE '.MAIN_DB_PREFIX.'propal SET fk_statut = '.self::STATUS_BILLED.", ";
2793 $sql .= " note_private = '".$this->db->escape($newprivatenote)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".((int) $user->id);
2794 $sql .= ' WHERE rowid = '.((int) $this->id).' AND fk_statut = '.((int) self::STATUS_SIGNED);
2795
2796 dol_syslog(__METHOD__, LOG_DEBUG);
2797 $resql = $this->db->query($sql);
2798 if (!$resql) {
2799 $this->errors[] = $this->db->error();
2800 $error++;
2801 } else {
2802 $num = $this->db->affected_rows($resql);
2803 }
2804
2805 if (!$error) {
2806 $modelpdf = $conf->global->PROPALE_ADDON_PDF_ODT_CLOSED ? $conf->global->PROPALE_ADDON_PDF_ODT_CLOSED : $this->model_pdf;
2807
2808 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
2809 // Define output language
2810 $outputlangs = $langs;
2811 if (getDolGlobalInt('MAIN_MULTILANGS')) {
2812 $outputlangs = new Translate("", $conf);
2813 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
2814 $outputlangs->setDefaultLang($newlang);
2815 }
2816
2817 // PDF
2818 $hidedetails = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 1 : 0);
2819 $hidedesc = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 1 : 0);
2820 $hideref = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 1 : 0);
2821
2822 //$ret=$object->fetch($id); // Reload to get new records
2823 $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
2824 }
2825
2826 $this->oldcopy = clone $this;
2827 $this->statut = self::STATUS_BILLED;
2828 $this->date_cloture = $now;
2829 $this->note_private = $newprivatenote;
2830 }
2831
2832 if (!$notrigger && empty($error)) {
2833 // Call trigger
2834 $result = $this->call_trigger($triggerName, $user);
2835 if ($result < 0) {
2836 $error++;
2837 }
2838 // End call triggers
2839 }
2840
2841 if (!$error) {
2842 $this->db->commit();
2843 return $num;
2844 } else {
2845 foreach ($this->errors as $errmsg) {
2846 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2847 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2848 }
2849 $this->db->rollback();
2850 return -1 * $error;
2851 }
2852 }
2853
2854 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2862 public function setDraft($user, $notrigger = 0)
2863 {
2864 // phpcs:enable
2865 $error = 0;
2866
2867 // Protection
2868 if ($this->statut <= self::STATUS_DRAFT) {
2869 return 0;
2870 }
2871
2872 dol_syslog(get_class($this)."::setDraft", LOG_DEBUG);
2873
2874 $this->db->begin();
2875
2876 $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2877 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
2878 $sql .= ", online_sign_ip = NULL , online_sign_name = NULL";
2879 $sql .= " WHERE rowid = ".((int) $this->id);
2880
2881 $resql = $this->db->query($sql);
2882 if (!$resql) {
2883 $this->errors[] = $this->db->error();
2884 $error++;
2885 }
2886
2887 if (!$error) {
2888 $this->oldcopy = clone $this;
2889 }
2890
2891 if (!$notrigger && empty($error)) {
2892 // Call trigger
2893 $result = $this->call_trigger('PROPAL_MODIFY', $user);
2894 if ($result < 0) {
2895 $error++;
2896 }
2897 // End call triggers
2898 }
2899
2900 if (!$error) {
2901 $this->statut = self::STATUS_DRAFT;
2902 $this->status = self::STATUS_DRAFT;
2903
2904 $this->db->commit();
2905 return 1;
2906 } else {
2907 foreach ($this->errors as $errmsg) {
2908 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2909 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2910 }
2911 $this->db->rollback();
2912 return -1 * $error;
2913 }
2914 }
2915
2916
2917 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2931 public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datep', $sortorder = 'DESC')
2932 {
2933 // phpcs:enable
2934 global $user;
2935
2936 $ga = array();
2937
2938 $sql = "SELECT s.rowid, s.nom as name, s.client,";
2939 $sql .= " p.rowid as propalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
2940 $sql .= " p.datep as dp, p.fin_validite as datelimite";
2941 if (!$user->hasRight('societe', 'client', 'voir') && !$socid) {
2942 $sql .= ", sc.fk_soc, sc.fk_user";
2943 }
2944 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."propal as p, ".MAIN_DB_PREFIX."c_propalst as c";
2945 if (!$user->hasRight('societe', 'client', 'voir') && !$socid) {
2946 $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
2947 }
2948 $sql .= " WHERE p.entity IN (".getEntity('propal').")";
2949 $sql .= " AND p.fk_soc = s.rowid";
2950 $sql .= " AND p.fk_statut = c.id";
2951 if (!$user->hasRight('societe', 'client', 'voir') && !$socid) { //restriction
2952 $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
2953 }
2954 if ($socid) {
2955 $sql .= " AND s.rowid = ".((int) $socid);
2956 }
2957 if ($draft) {
2958 $sql .= " AND p.fk_statut = ".self::STATUS_DRAFT;
2959 }
2960 if ($notcurrentuser > 0) {
2961 $sql .= " AND p.fk_user_author <> ".((int) $user->id);
2962 }
2963 $sql .= $this->db->order($sortfield, $sortorder);
2964 $sql .= $this->db->plimit($limit, $offset);
2965
2966 $result = $this->db->query($sql);
2967 if ($result) {
2968 $num = $this->db->num_rows($result);
2969 if ($num) {
2970 $i = 0;
2971 while ($i < $num) {
2972 $obj = $this->db->fetch_object($result);
2973
2974 if ($shortlist == 1) {
2975 $ga[$obj->propalid] = $obj->ref;
2976 } elseif ($shortlist == 2) {
2977 $ga[$obj->propalid] = $obj->ref.' ('.$obj->name.')';
2978 } else {
2979 $ga[$i]['id'] = $obj->propalid;
2980 $ga[$i]['ref'] = $obj->ref;
2981 $ga[$i]['name'] = $obj->name;
2982 }
2983
2984 $i++;
2985 }
2986 }
2987 return $ga;
2988 } else {
2989 dol_print_error($this->db);
2990 return -1;
2991 }
2992 }
2993
2999 public function getInvoiceArrayList()
3000 {
3001 return $this->InvoiceArrayList($this->id);
3002 }
3003
3004 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3011 public function InvoiceArrayList($id)
3012 {
3013 // phpcs:enable
3014 $ga = array();
3015 $linkedInvoices = array();
3016
3017 $this->fetchObjectLinked($id, $this->element);
3018 foreach ($this->linkedObjectsIds as $objecttype => $objectid) {
3019 // Nouveau système du comon object renvoi des rowid et non un id linéaire de 1 à n
3020 // On parcourt donc une liste d'objets en tant qu'objet unique
3021 foreach ($objectid as $key => $object) {
3022 // Cas des factures liees directement
3023 if ($objecttype == 'facture') {
3024 $linkedInvoices[] = $object;
3025 } else {
3026 // Cas des factures liees par un autre objet (ex: commande)
3027 $this->fetchObjectLinked($object, $objecttype);
3028 foreach ($this->linkedObjectsIds as $subobjecttype => $subobjectid) {
3029 foreach ($subobjectid as $subkey => $subobject) {
3030 if ($subobjecttype == 'facture') {
3031 $linkedInvoices[] = $subobject;
3032 }
3033 }
3034 }
3035 }
3036 }
3037 }
3038
3039 if (count($linkedInvoices) > 0) {
3040 $sql = "SELECT rowid as facid, ref, total_ht as total, datef as df, fk_user_author, fk_statut, paye";
3041 $sql .= " FROM ".MAIN_DB_PREFIX."facture";
3042 $sql .= " WHERE rowid IN (".$this->db->sanitize(implode(',', $linkedInvoices)).")";
3043
3044 dol_syslog(get_class($this)."::InvoiceArrayList", LOG_DEBUG);
3045 $resql = $this->db->query($sql);
3046
3047 if ($resql) {
3048 $tab_sqlobj = array();
3049 $nump = $this->db->num_rows($resql);
3050 for ($i = 0; $i < $nump; $i++) {
3051 $sqlobj = $this->db->fetch_object($resql);
3052 $tab_sqlobj[] = $sqlobj;
3053 }
3054 $this->db->free($resql);
3055
3056 $nump = count($tab_sqlobj);
3057
3058 if ($nump) {
3059 $i = 0;
3060 while ($i < $nump) {
3061 $obj = array_shift($tab_sqlobj);
3062
3063 $ga[$i] = $obj;
3064
3065 $i++;
3066 }
3067 }
3068 return $ga;
3069 } else {
3070 return -1;
3071 }
3072 } else {
3073 return $ga;
3074 }
3075 }
3076
3084 public function delete($user, $notrigger = 0)
3085 {
3086 global $conf;
3087 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3088
3089 $error = 0;
3090
3091 $this->db->begin();
3092
3093 if (!$notrigger) {
3094 // Call trigger
3095 $result = $this->call_trigger('PROPAL_DELETE', $user);
3096 if ($result < 0) {
3097 $error++;
3098 }
3099 // End call triggers
3100 }
3101
3102 // Delete extrafields of lines and lines
3103 if (!$error && !empty($this->table_element_line)) {
3104 $tabletodelete = $this->table_element_line;
3105 $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).")";
3106 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id);
3107 if (!$this->db->query($sqlef) || !$this->db->query($sql)) {
3108 $error++;
3109 $this->error = $this->db->lasterror();
3110 $this->errors[] = $this->error;
3111 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3112 }
3113 }
3114
3115 if (!$error) {
3116 // Delete linked object
3117 $res = $this->deleteObjectLinked();
3118 if ($res < 0) {
3119 $error++;
3120 }
3121 }
3122
3123 if (!$error) {
3124 // Delete linked contacts
3125 $res = $this->delete_linked_contact();
3126 if ($res < 0) {
3127 $error++;
3128 }
3129 }
3130
3131 // Removed extrafields of object
3132 if (!$error) {
3133 $result = $this->deleteExtraFields();
3134 if ($result < 0) {
3135 $error++;
3136 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3137 }
3138 }
3139
3140 // Delete main record
3141 if (!$error) {
3142 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element." WHERE rowid = ".((int) $this->id);
3143 $res = $this->db->query($sql);
3144 if (!$res) {
3145 $error++;
3146 $this->error = $this->db->lasterror();
3147 $this->errors[] = $this->error;
3148 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
3149 }
3150 }
3151
3152 // Delete record into ECM index and physically
3153 if (!$error) {
3154 $res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
3155 $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
3156 if (!$res) {
3157 $error++;
3158 }
3159 }
3160
3161 if (!$error) {
3162 // We remove directory
3163 $ref = dol_sanitizeFileName($this->ref);
3164 if ($conf->propal->multidir_output[$this->entity] && !empty($this->ref)) {
3165 $dir = $conf->propal->multidir_output[$this->entity]."/".$ref;
3166 $file = $dir."/".$ref.".pdf";
3167 if (file_exists($file)) {
3168 dol_delete_preview($this);
3169
3170 if (!dol_delete_file($file, 0, 0, 0, $this)) {
3171 $this->error = 'ErrorFailToDeleteFile';
3172 $this->errors[] = $this->error;
3173 $this->db->rollback();
3174 return 0;
3175 }
3176 }
3177 if (file_exists($dir)) {
3178 $res = @dol_delete_dir_recursive($dir); // delete files physically + into ecm tables
3179 if (!$res) {
3180 $this->error = 'ErrorFailToDeleteDir';
3181 $this->errors[] = $this->error;
3182 $this->db->rollback();
3183 return 0;
3184 }
3185 }
3186 }
3187 }
3188
3189 if (!$error) {
3190 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
3191 $this->db->commit();
3192 return 1;
3193 } else {
3194 $this->db->rollback();
3195 return -1;
3196 }
3197 }
3198
3207 public function availability($availability_id, $notrigger = 0)
3208 {
3209 global $user;
3210
3211 if ($this->statut >= self::STATUS_DRAFT) {
3212 $error = 0;
3213
3214 $this->db->begin();
3215
3216 $sql = 'UPDATE '.MAIN_DB_PREFIX.'propal';
3217 $sql .= ' SET fk_availability = '.((int) $availability_id);
3218 $sql .= ' WHERE rowid='.((int) $this->id);
3219
3220 dol_syslog(__METHOD__.' availability('.$availability_id.')', LOG_DEBUG);
3221 $resql = $this->db->query($sql);
3222 if (!$resql) {
3223 $this->errors[] = $this->db->error();
3224 $error++;
3225 }
3226
3227 if (!$error) {
3228 $this->oldcopy = clone $this;
3229 $this->availability_id = $availability_id;
3230 }
3231
3232 if (!$notrigger && empty($error)) {
3233 // Call trigger
3234 $result = $this->call_trigger('PROPAL_MODIFY', $user);
3235 if ($result < 0) {
3236 $error++;
3237 }
3238 // End call triggers
3239 }
3240
3241 if (!$error) {
3242 $this->db->commit();
3243 return 1;
3244 } else {
3245 foreach ($this->errors as $errmsg) {
3246 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
3247 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3248 }
3249 $this->db->rollback();
3250 return -1 * $error;
3251 }
3252 } else {
3253 $error_str = 'Propal status do not meet requirement '.$this->statut;
3254 dol_syslog(__METHOD__.$error_str, LOG_ERR);
3255 $this->error = $error_str;
3256 $this->errors[] = $this->error;
3257 return -2;
3258 }
3259 }
3260
3261 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3270 public function demand_reason($demand_reason_id, $notrigger = 0)
3271 {
3272 // phpcs:enable
3273 global $user;
3274
3275 if ($this->status >= self::STATUS_DRAFT) {
3276 $error = 0;
3277
3278 $this->db->begin();
3279
3280 $sql = 'UPDATE '.MAIN_DB_PREFIX.'propal';
3281 $sql .= ' SET fk_input_reason = '.((int) $demand_reason_id);
3282 $sql .= ' WHERE rowid='.((int) $this->id);
3283
3284 dol_syslog(__METHOD__.' demand_reason('.$demand_reason_id.')', LOG_DEBUG);
3285 $resql = $this->db->query($sql);
3286 if (!$resql) {
3287 $this->errors[] = $this->db->error();
3288 $error++;
3289 }
3290
3291 if (!$error) {
3292 $this->oldcopy = clone $this;
3293 $this->demand_reason_id = $demand_reason_id;
3294 }
3295
3296 if (!$notrigger && empty($error)) {
3297 // Call trigger
3298 $result = $this->call_trigger('PROPAL_MODIFY', $user);
3299 if ($result < 0) {
3300 $error++;
3301 }
3302 // End call triggers
3303 }
3304
3305 if (!$error) {
3306 $this->db->commit();
3307 return 1;
3308 } else {
3309 foreach ($this->errors as $errmsg) {
3310 dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
3311 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3312 }
3313 $this->db->rollback();
3314 return -1 * $error;
3315 }
3316 } else {
3317 $error_str = 'Propal status do not meet requirement '.$this->statut;
3318 dol_syslog(__METHOD__.$error_str, LOG_ERR);
3319 $this->error = $error_str;
3320 $this->errors[] = $this->error;
3321 return -2;
3322 }
3323 }
3324
3325
3332 public function info($id)
3333 {
3334 $sql = "SELECT c.rowid, ";
3335 $sql .= " c.datec, c.date_valid as datev, c.date_signature, c.date_cloture,";
3336 $sql .= " c.fk_user_author, c.fk_user_valid, c.fk_user_signature, c.fk_user_cloture";
3337 $sql .= " FROM ".MAIN_DB_PREFIX."propal as c";
3338 $sql .= " WHERE c.rowid = ".((int) $id);
3339
3340 $result = $this->db->query($sql);
3341
3342 if ($result) {
3343 if ($this->db->num_rows($result)) {
3344 $obj = $this->db->fetch_object($result);
3345
3346 $this->id = $obj->rowid;
3347
3348 $this->date_creation = $this->db->jdate($obj->datec);
3349 $this->date_validation = $this->db->jdate($obj->datev);
3350 $this->date_signature = $this->db->jdate($obj->date_signature);
3351 $this->date_cloture = $this->db->jdate($obj->date_cloture);
3352
3353 $this->user_creation_id = $obj->fk_user_author;
3354 $this->user_validation_id = $obj->fk_user_valid;
3355
3356 if ($obj->fk_user_signature) {
3357 $user_signature = new User($this->db);
3358 $user_signature->fetch($obj->fk_user_signature);
3359 $this->user_signature = $user_signature;
3360 }
3361
3362 $this->user_closing_id = $obj->fk_user_cloture;
3363 }
3364 $this->db->free($result);
3365 } else {
3366 dol_print_error($this->db);
3367 }
3368 }
3369
3370
3377 public function getLibStatut($mode = 0)
3378 {
3379 return $this->LibStatut($this->statut, $mode);
3380 }
3381
3382 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3390 public function LibStatut($status, $mode = 1)
3391 {
3392 // phpcs:enable
3393 global $conf, $hookmanager;
3394
3395 // Init/load array of translation of status
3396 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
3397 global $langs;
3398 $langs->load("propal");
3399 $this->labelStatus[0] = $langs->transnoentitiesnoconv("PropalStatusDraft");
3400 $this->labelStatus[1] = $langs->transnoentitiesnoconv("PropalStatusValidated");
3401 $this->labelStatus[2] = $langs->transnoentitiesnoconv("PropalStatusSigned");
3402 $this->labelStatus[3] = $langs->transnoentitiesnoconv("PropalStatusNotSigned");
3403 $this->labelStatus[4] = $langs->transnoentitiesnoconv("PropalStatusBilled");
3404 $this->labelStatusShort[0] = $langs->transnoentitiesnoconv("PropalStatusDraftShort");
3405 $this->labelStatusShort[1] = $langs->transnoentitiesnoconv("PropalStatusValidatedShort");
3406 $this->labelStatusShort[2] = $langs->transnoentitiesnoconv("PropalStatusSignedShort");
3407 $this->labelStatusShort[3] = $langs->transnoentitiesnoconv("PropalStatusNotSignedShort");
3408 $this->labelStatusShort[4] = $langs->transnoentitiesnoconv("PropalStatusBilledShort");
3409 }
3410
3411 $statusType = '';
3412 if ($status == self::STATUS_DRAFT) {
3413 $statusType = 'status0';
3414 } elseif ($status == self::STATUS_VALIDATED) {
3415 $statusType = 'status1';
3416 } elseif ($status == self::STATUS_SIGNED) {
3417 $statusType = 'status4';
3418 } elseif ($status == self::STATUS_NOTSIGNED) {
3419 $statusType = 'status9';
3420 } elseif ($status == self::STATUS_BILLED) {
3421 $statusType = 'status6';
3422 }
3423
3424
3425 $parameters = array('status' => $status, 'mode' => $mode);
3426 $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
3427
3428 if ($reshook > 0) {
3429 return $hookmanager->resPrint;
3430 }
3431
3432 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
3433 }
3434
3435
3436 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3444 public function load_board($user, $mode)
3445 {
3446 // phpcs:enable
3447 global $conf, $langs;
3448
3449 $clause = " WHERE";
3450
3451 $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.fin_validite as datefin, p.total_ht";
3452 $sql .= " FROM ".MAIN_DB_PREFIX."propal as p";
3453 if (!$user->hasRight('societe', 'client', 'voir') && !$user->socid) {
3454 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
3455 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3456 $clause = " AND";
3457 }
3458 $sql .= $clause." p.entity IN (".getEntity('propal').")";
3459 if ($mode == 'opened') {
3460 $sql .= " AND p.fk_statut = ".self::STATUS_VALIDATED;
3461 }
3462 if ($mode == 'signed') {
3463 $sql .= " AND p.fk_statut = ".self::STATUS_SIGNED;
3464 }
3465 if ($user->socid) {
3466 $sql .= " AND p.fk_soc = ".((int) $user->socid);
3467 }
3468
3469 $resql = $this->db->query($sql);
3470 if ($resql) {
3471 $langs->load("propal");
3472 $now = dol_now();
3473
3474 $delay_warning = 0;
3475 $status = 0;
3476 $label = $labelShort = '';
3477 if ($mode == 'opened') {
3478 $delay_warning = $conf->propal->cloture->warning_delay;
3479 $status = self::STATUS_VALIDATED;
3480 $label = $langs->transnoentitiesnoconv("PropalsToClose");
3481 $labelShort = $langs->transnoentitiesnoconv("ToAcceptRefuse");
3482 }
3483 if ($mode == 'signed') {
3484 $delay_warning = $conf->propal->facturation->warning_delay;
3485 $status = self::STATUS_SIGNED;
3486 $label = $langs->trans("PropalsToBill"); // We set here bill but may be billed or ordered
3487 $labelShort = $langs->trans("ToBill");
3488 }
3489
3490 $response = new WorkboardResponse();
3491 $response->warning_delay = $delay_warning / 60 / 60 / 24;
3492 $response->label = $label;
3493 $response->labelShort = $labelShort;
3494 $response->url = DOL_URL_ROOT.'/comm/propal/list.php?search_status='.$status.'&mainmenu=commercial&leftmenu=propals';
3495 $response->url_late = DOL_URL_ROOT.'/comm/propal/list.php?search_option=late&mainmenu=commercial&leftmenu=propals&sortfield=p.datep&sortorder=asc';
3496 $response->img = img_object('', "propal");
3497
3498 // This assignment in condition is not a bug. It allows walking the results.
3499 while ($obj = $this->db->fetch_object($resql)) {
3500 $response->nbtodo++;
3501 $response->total += $obj->total_ht;
3502
3503 if ($mode == 'opened') {
3504 $datelimit = $this->db->jdate($obj->datefin);
3505 if ($datelimit < ($now - $delay_warning)) {
3506 $response->nbtodolate++;
3507 }
3508 }
3509 // TODO Definir regle des propales a facturer en retard
3510 // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
3511 }
3512
3513 return $response;
3514 } else {
3515 $this->error = $this->db->error();
3516 return -1;
3517 }
3518 }
3519
3520
3528 public function initAsSpecimen()
3529 {
3530 global $conf, $langs;
3531
3532 // Load array of products prodids
3533 $num_prods = 0;
3534 $prodids = array();
3535 $sql = "SELECT rowid";
3536 $sql .= " FROM ".MAIN_DB_PREFIX."product";
3537 $sql .= " WHERE entity IN (".getEntity('product').")";
3538 $sql .= $this->db->plimit(100);
3539
3540 $resql = $this->db->query($sql);
3541 if ($resql) {
3542 $num_prods = $this->db->num_rows($resql);
3543 $i = 0;
3544 while ($i < $num_prods) {
3545 $i++;
3546 $row = $this->db->fetch_row($resql);
3547 $prodids[$i] = $row[0];
3548 }
3549 }
3550
3551 // Initialise parametres
3552 $this->id = 0;
3553 $this->ref = 'SPECIMEN';
3554 $this->ref_client = 'NEMICEPS';
3555 $this->specimen = 1;
3556 $this->socid = 1;
3557 $this->date = time();
3558 $this->fin_validite = $this->date + 3600 * 24 * 30;
3559 $this->cond_reglement_id = 1;
3560 $this->cond_reglement_code = 'RECEP';
3561 $this->mode_reglement_id = 7;
3562 $this->mode_reglement_code = 'CHQ';
3563 $this->availability_id = 1;
3564 $this->availability_code = 'AV_NOW';
3565 $this->demand_reason_id = 1;
3566 $this->demand_reason_code = 'SRC_00';
3567 $this->note_public = 'This is a comment (public)';
3568 $this->note_private = 'This is a comment (private)';
3569
3570 $this->multicurrency_tx = 1;
3571 $this->multicurrency_code = $conf->currency;
3572
3573 // Lines
3574 $nbp = 5;
3575 $xnbp = 0;
3576 while ($xnbp < $nbp) {
3577 $line = new PropaleLigne($this->db);
3578 $line->desc = $langs->trans("Description")." ".$xnbp;
3579 $line->qty = 1;
3580 $line->subprice = 100;
3581 $line->price = 100;
3582 $line->tva_tx = 20;
3583 $line->localtax1_tx = 0;
3584 $line->localtax2_tx = 0;
3585 if ($xnbp == 2) {
3586 $line->total_ht = 50;
3587 $line->total_ttc = 60;
3588 $line->total_tva = 10;
3589 $line->remise_percent = 50;
3590 } else {
3591 $line->total_ht = 100;
3592 $line->total_ttc = 120;
3593 $line->total_tva = 20;
3594 $line->remise_percent = 00;
3595 }
3596
3597 if ($num_prods > 0) {
3598 $prodid = mt_rand(1, $num_prods);
3599 $line->fk_product = $prodids[$prodid];
3600 $line->product_ref = 'SPECIMEN';
3601 }
3602
3603 $this->lines[$xnbp] = $line;
3604
3605 $this->total_ht += $line->total_ht;
3606 $this->total_tva += $line->total_tva;
3607 $this->total_ttc += $line->total_ttc;
3608
3609 $xnbp++;
3610 }
3611 }
3612
3613 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3619 public function load_state_board()
3620 {
3621 // phpcs:enable
3622 global $user;
3623
3624 $this->nb = array();
3625 $clause = "WHERE";
3626
3627 $sql = "SELECT count(p.rowid) as nb";
3628 $sql .= " FROM ".MAIN_DB_PREFIX."propal as p";
3629 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
3630 if (!$user->hasRight('societe', 'client', 'voir') && !$user->socid) {
3631 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3632 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3633 $clause = "AND";
3634 }
3635 $sql .= " ".$clause." p.entity IN (".getEntity('propal').")";
3636
3637 $resql = $this->db->query($sql);
3638 if ($resql) {
3639 // This assignment in condition is not a bug. It allows walking the results.
3640 while ($obj = $this->db->fetch_object($resql)) {
3641 $this->nb["proposals"] = $obj->nb;
3642 }
3643 $this->db->free($resql);
3644 return 1;
3645 } else {
3646 dol_print_error($this->db);
3647 $this->error = $this->db->error();
3648 return -1;
3649 }
3650 }
3651
3652
3660 public function getNextNumRef($soc)
3661 {
3662 global $conf, $langs;
3663 $langs->load("propal");
3664
3665 $classname = $conf->global->PROPALE_ADDON;
3666
3667 if (!empty($classname)) {
3668 $mybool = false;
3669
3670 $file = $classname.".php";
3671
3672 // Include file with class
3673 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
3674 foreach ($dirmodels as $reldir) {
3675 $dir = dol_buildpath($reldir."core/modules/propale/");
3676
3677 // Load file with numbering class (if found)
3678 $mybool |= @include_once $dir.$file;
3679 }
3680
3681 if (!$mybool) {
3682 dol_print_error('', "Failed to include file ".$file);
3683 return '';
3684 }
3685
3686 $obj = new $classname();
3687 $numref = "";
3688 $numref = $obj->getNextValue($soc, $this);
3689
3690 if ($numref != "") {
3691 return $numref;
3692 } else {
3693 $this->error = $obj->error;
3694 //dol_print_error($db,"Propale::getNextNumRef ".$obj->error);
3695 return "";
3696 }
3697 } else {
3698 $langs->load("errors");
3699 print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("Proposal"));
3700 return "";
3701 }
3702 }
3703
3710 public function getTooltipContentArray($params)
3711 {
3712 global $conf, $langs, $user;
3713
3714 $langs->load('propal');
3715 $datas = [];
3716 $nofetch = !empty($params['nofetch']);
3717
3718 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3719 return ['optimize' => $langs->trans("Proposal")];
3720 }
3721 if ($user->hasRight('propal', 'lire')) {
3722 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("Proposal").'</u>';
3723 if (isset($this->statut)) {
3724 $datas['status'] = ' '.$this->getLibStatut(5);
3725 }
3726 if (!empty($this->ref)) {
3727 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
3728 }
3729 if (!$nofetch) {
3730 $langs->load('companies');
3731 if (empty($this->thirdparty)) {
3732 $this->fetch_thirdparty();
3733 }
3734 $datas['customer'] = '<br><b>'.$langs->trans('Customer').':</b> '.$this->thirdparty->getNomUrl(1, '', 0, 1);
3735 }
3736 if (!empty($this->ref_client)) {
3737 $datas['refcustomer'] = '<br><b>'.$langs->trans('RefCustomer').':</b> '.$this->ref_client;
3738 }
3739 if (!$nofetch) {
3740 $langs->load('project');
3741 if (empty($this->project)) {
3742 $res = $this->fetch_project();
3743 if ($res > 0) {
3744 $datas['project'] = '<br><b>'.$langs->trans('Project').':</b> '.$this->project->getNomUrl(1, '', 0, 1);
3745 }
3746 }
3747 }
3748 if (!empty($this->total_ht)) {
3749 $datas['amountht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3750 }
3751 if (!empty($this->total_tva)) {
3752 $datas['vat'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
3753 }
3754 if (!empty($this->total_ttc)) {
3755 $datas['amountttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3756 }
3757 if (!empty($this->date)) {
3758 $datas['date'] = '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
3759 }
3760 if (!empty($this->delivery_date)) {
3761 $datas['deliverydate'] = '<br><b>'.$langs->trans('DeliveryDate').':</b> '.dol_print_date($this->delivery_date, 'dayhour');
3762 }
3763 }
3764
3765 return $datas;
3766 }
3767
3779 public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = -1)
3780 {
3781 global $langs, $conf, $user, $hookmanager;
3782
3783 if (!empty($conf->dol_no_mouse_hover)) {
3784 $notooltip = 1; // Force disable tooltips
3785 }
3786
3787 $result = '';
3788 $params = [
3789 'id' => $this->id,
3790 'objecttype' => $this->element,
3791 'option' => $option,
3792 'nofetch' => 1,
3793 ];
3794 $classfortooltip = 'classfortooltip';
3795 $dataparams = '';
3796 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
3797 $classfortooltip = 'classforajaxtooltip';
3798 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
3799 $label = '';
3800 } else {
3801 $label = implode($this->getTooltipContentArray($params));
3802 }
3803
3804 $url = '';
3805 if ($user->hasRight('propal', 'lire')) {
3806 if ($option == '') {
3807 $url = DOL_URL_ROOT.'/comm/propal/card.php?id='.$this->id.$get_params;
3808 } elseif ($option == 'compta') { // deprecated
3809 $url = DOL_URL_ROOT.'/comm/propal/card.php?id='.$this->id.$get_params;
3810 } elseif ($option == 'expedition') {
3811 $url = DOL_URL_ROOT.'/expedition/propal.php?id='.$this->id.$get_params;
3812 } elseif ($option == 'document') {
3813 $url = DOL_URL_ROOT.'/comm/propal/document.php?id='.$this->id.$get_params;
3814 }
3815
3816 if ($option != 'nolink') {
3817 // Add param to save lastsearch_values or not
3818 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
3819 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
3820 $add_save_lastsearch_values = 1;
3821 }
3822 if ($add_save_lastsearch_values) {
3823 $url .= '&save_lastsearch_values=1';
3824 }
3825 }
3826 }
3827
3828 $linkclose = '';
3829 if (empty($notooltip) && $user->hasRight('propal', 'lire')) {
3830 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3831 $label = $langs->trans("Proposal");
3832 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
3833 }
3834 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
3835 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
3836 }
3837
3838 $linkstart = '<a href="'.$url.'"';
3839 $linkstart .= $linkclose.'>';
3840 $linkend = '</a>';
3841
3842 $result .= $linkstart;
3843 if ($withpicto) {
3844 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
3845 }
3846 if ($withpicto != 2) {
3847 $result .= $this->ref;
3848 }
3849 $result .= $linkend;
3850
3851 if ($addlinktonotes >= 0) {
3852 $txttoshow = '';
3853
3854 if ($addlinktonotes == 0) {
3855 if (!empty($this->note_private) || !empty($this->note_public)) {
3856 $txttoshow = $langs->trans('ViewPrivateNote');
3857 }
3858 } elseif ($addlinktonotes == 1) {
3859 if (!empty($this->note_private)) {
3860 $txttoshow .= ($user->socid > 0 ? '' : dol_string_nohtmltag($this->note_private, 1));
3861 }
3862 } elseif ($addlinktonotes == 2) {
3863 if (!empty($this->note_public)) {
3864 $txttoshow .= dol_string_nohtmltag($this->note_public, 1);
3865 }
3866 } elseif ($addlinktonotes == 3) {
3867 if ($user->socid > 0) {
3868 if (!empty($this->note_public)) {
3869 $txttoshow .= dol_string_nohtmltag($this->note_public, 1);
3870 }
3871 } else {
3872 if (!empty($this->note_public)) {
3873 $txttoshow .= dol_string_nohtmltag($this->note_public, 1);
3874 }
3875 if (!empty($this->note_private)) {
3876 if (!empty($txttoshow)) {
3877 $txttoshow .= '<br><br>';
3878 }
3879 $txttoshow .= dol_string_nohtmltag($this->note_private, 1);
3880 }
3881 }
3882 }
3883
3884 if ($txttoshow) {
3885 $result .= ' <span class="note inline-block">';
3886 $result .= '<a href="'.DOL_URL_ROOT.'/comm/propal/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($txttoshow).'">';
3887 $result .= img_picto('', 'note');
3888 $result .= '</a>';
3889 $result .= '</span>';
3890 }
3891 }
3892
3893 global $action;
3894 $hookmanager->initHooks(array($this->element . 'dao'));
3895 $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
3896 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3897 if ($reshook > 0) {
3898 $result = $hookmanager->resPrint;
3899 } else {
3900 $result .= $hookmanager->resPrint;
3901 }
3902 return $result;
3903 }
3904
3911 public function getLinesArray($sqlforgedfilters = '')
3912 {
3913 return $this->fetch_lines(0, 0, $sqlforgedfilters);
3914 }
3915
3927 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3928 {
3929 global $conf, $langs;
3930
3931 $langs->load("propale");
3932 $outputlangs->load("products");
3933
3934 if (!dol_strlen($modele)) {
3935 $modele = 'azur';
3936
3937 if ($this->model_pdf) {
3938 $modele = $this->model_pdf;
3939 } elseif (getDolGlobalString('PROPALE_ADDON_PDF')) {
3940 $modele = $conf->global->PROPALE_ADDON_PDF;
3941 }
3942 }
3943
3944 $modelpath = "core/modules/propale/doc/";
3945
3946 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3947 }
3948
3957 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3958 {
3959 $tables = array(
3960 'propal'
3961 );
3962
3963 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3964 }
3965
3974 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
3975 {
3976 $tables = array(
3977 'propaldet'
3978 );
3979
3980 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
3981 }
3982
3990 public function getKanbanView($option = '', $arraydata = null)
3991 {
3992 global $langs;
3993
3994 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3995
3996 $return = '<div class="box-flex-item box-flex-grow-zero">';
3997 $return .= '<div class="info-box info-box-sm">';
3998 $return .= '<span class="info-box-icon bg-infobox-action">';
3999 $return .= img_picto('', $this->picto);
4000 $return .= '</span>';
4001 $return .= '<div class="info-box-content">';
4002 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
4003 if ($selected >= 0) {
4004 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
4005 }
4006 if (!empty($arraydata['projectlink'])) {
4007 $return .= '<span class="info-box-ref"> | '.$arraydata['projectlink'].'</span>';
4008 }
4009 if (!empty($arraydata['authorlink'])) {
4010 $return .= '<br><span class="info-box-label">'.$arraydata['authorlink'].'</span>';
4011 }
4012 if (property_exists($this, 'total_ht')) {
4013 $return .='<br><span class="info-box-label amount" title="'.$langs->trans("AmountHT").'">'.price($this->total_ht).'</span>';
4014 }
4015 if (method_exists($this, 'getLibStatut')) {
4016 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
4017 }
4018 $return .= '</div>';
4019 $return .= '</div>';
4020 $return .= '</div>';
4021 return $return;
4022 }
4023}
4024
4029{
4033 public $element = 'propaldet';
4034
4038 public $table_element = 'propaldet';
4039
4040 public $oldline;
4041
4042 // From llx_propaldet
4043 public $fk_propal;
4044 public $fk_parent_line;
4045 public $desc; // Description ligne
4046 public $fk_product; // Id produit predefini
4057 public $product_type = Product::TYPE_PRODUCT;
4058
4059 public $qty;
4060
4061 public $tva_tx;
4062 public $vat_src_code;
4063
4064 public $subprice;
4065 public $remise_percent;
4066 public $fk_remise_except;
4067
4068 public $rang = 0;
4069
4070 public $fk_fournprice;
4071 public $pa_ht;
4072 public $marge_tx;
4073 public $marque_tx;
4074
4075 public $special_code; // Tag for special lines (exlusive tags)
4076 // 1: frais de port
4077 // 2: ecotaxe
4078 // 3: option line (when qty = 0)
4079
4080 public $info_bits = 0; // Some other info:
4081 // Bit 0: 0 si TVA normal - 1 si TVA NPR
4082 // Bit 1: 0 ligne normale - 1 si ligne de remise fixe
4083
4084 public $total_ht; // Total HT de la ligne toute quantite et incluant la remise ligne
4085 public $total_tva; // Total TVA de la ligne toute quantite et incluant la remise ligne
4086 public $total_ttc; // Total TTC de la ligne toute quantite et incluant la remise ligne
4087
4092 public $remise;
4097 public $price;
4098
4099 // From llx_product
4104 public $ref;
4109 public $product_ref;
4114 public $libelle;
4119 public $label;
4124 public $product_label;
4129 public $product_desc;
4130
4135 public $product_tobatch;
4136
4141 public $product_barcode;
4142
4143 public $localtax1_tx; // Local tax 1
4144 public $localtax2_tx; // Local tax 2
4145 public $localtax1_type; // Local tax 1 type
4146 public $localtax2_type; // Local tax 2 type
4147 public $total_localtax1; // Line total local tax 1
4148 public $total_localtax2; // Line total local tax 2
4149
4150 public $date_start;
4151 public $date_end;
4152
4153 public $skip_update_total; // Skip update price total for special lines
4154
4155 // Multicurrency
4156 public $fk_multicurrency;
4157 public $multicurrency_code;
4158 public $multicurrency_subprice;
4159 public $multicurrency_total_ht;
4160 public $multicurrency_total_tva;
4161 public $multicurrency_total_ttc;
4162
4163
4169 public function __construct($db)
4170 {
4171 $this->db = $db;
4172 }
4173
4180 public function fetch($rowid)
4181 {
4182 $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,';
4183 $sql .= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice,';
4184 $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,';
4185 $sql .= ' pd.fk_unit,';
4186 $sql .= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
4187 $sql .= ' pd.fk_multicurrency, pd.multicurrency_code, pd.multicurrency_subprice, pd.multicurrency_total_ht, pd.multicurrency_total_tva, pd.multicurrency_total_ttc,';
4188 $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
4189 $sql .= ' pd.date_start, pd.date_end, pd.product_type';
4190 $sql .= ' FROM '.MAIN_DB_PREFIX.'propaldet as pd';
4191 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
4192 $sql .= ' WHERE pd.rowid = '.((int) $rowid);
4193
4194 $result = $this->db->query($sql);
4195 if ($result) {
4196 $objp = $this->db->fetch_object($result);
4197
4198 if ($objp) {
4199 $this->id = $objp->rowid;
4200 $this->rowid = $objp->rowid; // deprecated
4201 $this->fk_propal = $objp->fk_propal;
4202 $this->fk_parent_line = $objp->fk_parent_line;
4203 $this->label = $objp->custom_label;
4204 $this->desc = $objp->description;
4205 $this->qty = $objp->qty;
4206 $this->price = $objp->price; // deprecated
4207 $this->subprice = $objp->subprice;
4208 $this->vat_src_code = $objp->vat_src_code;
4209 $this->tva_tx = $objp->tva_tx;
4210 $this->remise = $objp->remise; // deprecated
4211 $this->remise_percent = $objp->remise_percent;
4212 $this->fk_remise_except = $objp->fk_remise_except;
4213 $this->fk_product = $objp->fk_product;
4214 $this->info_bits = $objp->info_bits;
4215
4216 $this->total_ht = $objp->total_ht;
4217 $this->total_tva = $objp->total_tva;
4218 $this->total_ttc = $objp->total_ttc;
4219
4220 $this->fk_fournprice = $objp->fk_fournprice;
4221
4222 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
4223 $this->pa_ht = $marginInfos[0];
4224 $this->marge_tx = $marginInfos[1];
4225 $this->marque_tx = $marginInfos[2];
4226
4227 $this->special_code = $objp->special_code;
4228 $this->product_type = $objp->product_type;
4229 $this->rang = $objp->rang;
4230
4231 $this->ref = $objp->product_ref; // deprecated
4232 $this->product_ref = $objp->product_ref;
4233 $this->libelle = $objp->product_label; // deprecated
4234 $this->product_label = $objp->product_label;
4235 $this->product_desc = $objp->product_desc;
4236 $this->fk_unit = $objp->fk_unit;
4237
4238 $this->date_start = $this->db->jdate($objp->date_start);
4239 $this->date_end = $this->db->jdate($objp->date_end);
4240
4241 // Multicurrency
4242 $this->fk_multicurrency = $objp->fk_multicurrency;
4243 $this->multicurrency_code = $objp->multicurrency_code;
4244 $this->multicurrency_subprice = $objp->multicurrency_subprice;
4245 $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
4246 $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
4247 $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
4248
4249 $this->fetch_optionals();
4250
4251 $this->db->free($result);
4252
4253 return 1;
4254 } else {
4255 return 0;
4256 }
4257 } else {
4258 return -1;
4259 }
4260 }
4261
4268 public function insert($notrigger = 0)
4269 {
4270 global $conf, $user;
4271
4272 $error = 0;
4273
4274 dol_syslog(get_class($this)."::insert rang=".$this->rang);
4275
4276 $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'.
4277
4278 // Clean parameters
4279 if (empty($this->tva_tx)) {
4280 $this->tva_tx = 0;
4281 }
4282 if (empty($this->localtax1_tx)) {
4283 $this->localtax1_tx = 0;
4284 }
4285 if (empty($this->localtax2_tx)) {
4286 $this->localtax2_tx = 0;
4287 }
4288 if (empty($this->localtax1_type)) {
4289 $this->localtax1_type = 0;
4290 }
4291 if (empty($this->localtax2_type)) {
4292 $this->localtax2_type = 0;
4293 }
4294 if (empty($this->total_localtax1)) {
4295 $this->total_localtax1 = 0;
4296 }
4297 if (empty($this->total_localtax2)) {
4298 $this->total_localtax2 = 0;
4299 }
4300 if (empty($this->rang)) {
4301 $this->rang = 0;
4302 }
4303 if (empty($this->remise_percent) || !is_numeric($this->remise_percent)) {
4304 $this->remise_percent = 0;
4305 }
4306 if (empty($this->info_bits)) {
4307 $this->info_bits = 0;
4308 }
4309 if (empty($this->special_code)) {
4310 $this->special_code = 0;
4311 }
4312 if (empty($this->fk_parent_line)) {
4313 $this->fk_parent_line = 0;
4314 }
4315 if (empty($this->fk_fournprice)) {
4316 $this->fk_fournprice = 0;
4317 }
4318 if (!is_numeric($this->qty)) {
4319 $this->qty = 0;
4320 }
4321 if (empty($this->pa_ht)) {
4322 $this->pa_ht = 0;
4323 }
4324 if (empty($this->multicurrency_subprice)) {
4325 $this->multicurrency_subprice = 0;
4326 }
4327 if (empty($this->multicurrency_total_ht)) {
4328 $this->multicurrency_total_ht = 0;
4329 }
4330 if (empty($this->multicurrency_total_tva)) {
4331 $this->multicurrency_total_tva = 0;
4332 }
4333 if (empty($this->multicurrency_total_ttc)) {
4334 $this->multicurrency_total_ttc = 0;
4335 }
4336
4337 // if buy price not defined, define buyprice as configured in margin admin
4338 if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
4339 if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0) {
4340 return $result;
4341 } else {
4342 $this->pa_ht = $result;
4343 }
4344 }
4345
4346 // Check parameters
4347 if ($this->product_type < 0) {
4348 return -1;
4349 }
4350
4351 $this->db->begin();
4352
4353 // Insert line into database
4354 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'propaldet';
4355 $sql .= ' (fk_propal, fk_parent_line, label, description, fk_product, product_type,';
4356 $sql .= ' fk_remise_except, qty, vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
4357 $sql .= ' subprice, remise_percent, ';
4358 $sql .= ' info_bits, ';
4359 $sql .= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
4360 $sql .= ' fk_unit,';
4361 $sql .= ' date_start, date_end';
4362 $sql .= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc)';
4363 $sql .= " VALUES (".$this->fk_propal.",";
4364 $sql .= " ".($this->fk_parent_line > 0 ? "'".$this->db->escape($this->fk_parent_line)."'" : "null").",";
4365 $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
4366 $sql .= " '".$this->db->escape($this->desc)."',";
4367 $sql .= " ".($this->fk_product ? "'".$this->db->escape($this->fk_product)."'" : "null").",";
4368 $sql .= " '".$this->db->escape($this->product_type)."',";
4369 $sql .= " ".($this->fk_remise_except ? "'".$this->db->escape($this->fk_remise_except)."'" : "null").",";
4370 $sql .= " ".price2num($this->qty, 'MS').",";
4371 $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
4372 $sql .= " ".price2num($this->tva_tx).",";
4373 $sql .= " ".price2num($this->localtax1_tx).",";
4374 $sql .= " ".price2num($this->localtax2_tx).",";
4375 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
4376 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
4377 $sql .= " ".(price2num($this->subprice) !== '' ? price2num($this->subprice, 'MU') : "null").",";
4378 $sql .= " ".price2num($this->remise_percent, 3).",";
4379 $sql .= " ".(isset($this->info_bits) ? ((int) $this->info_bits) : "null").",";
4380 $sql .= " ".price2num($this->total_ht, 'MT').",";
4381 $sql .= " ".price2num($this->total_tva, 'MT').",";
4382 $sql .= " ".price2num($this->total_localtax1, 'MT').",";
4383 $sql .= " ".price2num($this->total_localtax2, 'MT').",";
4384 $sql .= " ".price2num($this->total_ttc, 'MT').",";
4385 $sql .= " ".(!empty($this->fk_fournprice) ? "'".$this->db->escape($this->fk_fournprice)."'" : "null").",";
4386 $sql .= " ".(isset($this->pa_ht) ? "'".price2num($this->pa_ht)."'" : "null").",";
4387 $sql .= ' '.((int) $this->special_code).',';
4388 $sql .= ' '.((int) $this->rang).',';
4389 $sql .= ' '.(empty($this->fk_unit) ? 'NULL' : ((int) $this->fk_unit)).',';
4390 $sql .= " ".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null").',';
4391 $sql .= " ".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
4392 $sql .= ", ".($this->fk_multicurrency > 0 ? ((int) $this->fk_multicurrency) : 'null');
4393 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
4394 $sql .= ", ".price2num($this->multicurrency_subprice, 'CU');
4395 $sql .= ", ".price2num($this->multicurrency_total_ht, 'CT');
4396 $sql .= ", ".price2num($this->multicurrency_total_tva, 'CT');
4397 $sql .= ", ".price2num($this->multicurrency_total_ttc, 'CT');
4398 $sql .= ')';
4399
4400 dol_syslog(get_class($this).'::insert', LOG_DEBUG);
4401 $resql = $this->db->query($sql);
4402 if ($resql) {
4403 $this->rowid = $this->db->last_insert_id(MAIN_DB_PREFIX.'propaldet');
4404
4405 if (!$error) {
4406 $this->id = $this->rowid;
4407 $result = $this->insertExtraFields();
4408 if ($result < 0) {
4409 $error++;
4410 }
4411 }
4412
4413 if (!$error && !$notrigger) {
4414 // Call trigger
4415 $result = $this->call_trigger('LINEPROPAL_INSERT', $user);
4416 if ($result < 0) {
4417 $this->db->rollback();
4418 return -1;
4419 }
4420 // End call triggers
4421 }
4422
4423 if (!$error) {
4424 $this->db->commit();
4425 return 1;
4426 }
4427
4428 foreach ($this->errors as $errmsg) {
4429 dol_syslog(get_class($this)."::insert ".$errmsg, LOG_ERR);
4430 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4431 }
4432 $this->db->rollback();
4433 return -1 * $error;
4434 } else {
4435 $this->error = $this->db->error()." sql=".$sql;
4436 $this->db->rollback();
4437 return -1;
4438 }
4439 }
4440
4448 public function delete(User $user, $notrigger = 0)
4449 {
4450 global $conf;
4451
4452 $error = 0;
4453 $this->db->begin();
4454
4455 if (!$notrigger) {
4456 // Call trigger
4457 $result = $this->call_trigger('LINEPROPAL_DELETE', $user);
4458 if ($result < 0) {
4459 $error++;
4460 }
4461 }
4462 // End call triggers
4463
4464 if (!$error) {
4465 $sql = "DELETE FROM " . MAIN_DB_PREFIX . "propaldet WHERE rowid = " . ((int) $this->rowid);
4466 dol_syslog("PropaleLigne::delete", LOG_DEBUG);
4467 if ($this->db->query($sql)) {
4468 // Remove extrafields
4469 if (!$error) {
4470 $this->id = $this->rowid;
4471 $result = $this->deleteExtraFields();
4472 if ($result < 0) {
4473 $error++;
4474 dol_syslog(get_class($this) . "::delete error -4 " . $this->error, LOG_ERR);
4475 }
4476 }
4477 } else {
4478 $this->error = $this->db->error() . " sql=" . $sql;
4479 $error++;
4480 }
4481 }
4482
4483 if ($error) {
4484 $this->db->rollback();
4485 return -1;
4486 } else {
4487 $this->db->commit();
4488 return 1;
4489 }
4490 }
4491
4498 public function update($notrigger = 0)
4499 {
4500 global $conf, $user;
4501
4502 $error = 0;
4503
4504 $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'.
4505
4506 if (empty($this->id) && !empty($this->rowid)) {
4507 $this->id = $this->rowid;
4508 }
4509
4510 // Clean parameters
4511 if (empty($this->tva_tx)) {
4512 $this->tva_tx = 0;
4513 }
4514 if (empty($this->localtax1_tx)) {
4515 $this->localtax1_tx = 0;
4516 }
4517 if (empty($this->localtax2_tx)) {
4518 $this->localtax2_tx = 0;
4519 }
4520 if (empty($this->total_localtax1)) {
4521 $this->total_localtax1 = 0;
4522 }
4523 if (empty($this->total_localtax2)) {
4524 $this->total_localtax2 = 0;
4525 }
4526 if (empty($this->localtax1_type)) {
4527 $this->localtax1_type = 0;
4528 }
4529 if (empty($this->localtax2_type)) {
4530 $this->localtax2_type = 0;
4531 }
4532 if (empty($this->marque_tx)) {
4533 $this->marque_tx = 0;
4534 }
4535 if (empty($this->marge_tx)) {
4536 $this->marge_tx = 0;
4537 }
4538 if (empty($this->price)) {
4539 $this->price = 0; // TODO A virer
4540 }
4541 if (empty($this->remise_percent)) {
4542 $this->remise_percent = 0;
4543 }
4544 if (empty($this->info_bits)) {
4545 $this->info_bits = 0;
4546 }
4547 if (empty($this->special_code)) {
4548 $this->special_code = 0;
4549 }
4550 if (empty($this->fk_parent_line)) {
4551 $this->fk_parent_line = 0;
4552 }
4553 if (empty($this->fk_fournprice)) {
4554 $this->fk_fournprice = 0;
4555 }
4556 if (empty($this->subprice)) {
4557 $this->subprice = 0;
4558 }
4559 if (empty($this->pa_ht)) {
4560 $this->pa_ht = 0;
4561 }
4562
4563 // if buy price not defined, define buyprice as configured in margin admin
4564 if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
4565 if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0) {
4566 return $result;
4567 } else {
4568 $this->pa_ht = $result;
4569 }
4570 }
4571
4572 $this->db->begin();
4573
4574 // Mise a jour ligne en base
4575 $sql = "UPDATE ".MAIN_DB_PREFIX."propaldet SET";
4576 $sql .= " description='".$this->db->escape($this->desc)."'";
4577 $sql .= ", label=".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
4578 $sql .= ", product_type=".$this->product_type;
4579 $sql .= ", vat_src_code = '".(empty($this->vat_src_code) ? '' : $this->vat_src_code)."'";
4580 $sql .= ", tva_tx='".price2num($this->tva_tx)."'";
4581 $sql .= ", localtax1_tx=".price2num($this->localtax1_tx);
4582 $sql .= ", localtax2_tx=".price2num($this->localtax2_tx);
4583 $sql .= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4584 $sql .= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4585 $sql .= ", qty='".price2num($this->qty)."'";
4586 $sql .= ", subprice=".price2num($this->subprice);
4587 $sql .= ", remise_percent=".price2num($this->remise_percent);
4588 $sql .= ", price=".(float) price2num($this->price); // TODO A virer
4589 $sql .= ", remise=".(float) price2num($this->remise); // TODO A virer
4590 $sql .= ", info_bits='".$this->db->escape($this->info_bits)."'";
4591 if (empty($this->skip_update_total)) {
4592 $sql .= ", total_ht=".price2num($this->total_ht);
4593 $sql .= ", total_tva=".price2num($this->total_tva);
4594 $sql .= ", total_ttc=".price2num($this->total_ttc);
4595 $sql .= ", total_localtax1=".price2num($this->total_localtax1);
4596 $sql .= ", total_localtax2=".price2num($this->total_localtax2);
4597 }
4598 $sql .= ", fk_product_fournisseur_price=".(!empty($this->fk_fournprice) ? "'".$this->db->escape($this->fk_fournprice)."'" : "null");
4599 $sql .= ", buy_price_ht=".price2num($this->pa_ht);
4600 if (strlen($this->special_code)) {
4601 $sql .= ", special_code=".$this->special_code;
4602 }
4603 $sql .= ", fk_parent_line=".($this->fk_parent_line > 0 ? $this->fk_parent_line : "null");
4604 if (!empty($this->rang)) {
4605 $sql .= ", rang=".((int) $this->rang);
4606 }
4607 $sql .= ", date_start=".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null");
4608 $sql .= ", date_end=".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
4609 $sql .= ", fk_unit=".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4610
4611 // Multicurrency
4612 $sql .= ", multicurrency_subprice=".price2num($this->multicurrency_subprice);
4613 $sql .= ", multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
4614 $sql .= ", multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
4615 $sql .= ", multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
4616
4617 $sql .= " WHERE rowid = ".((int) $this->id);
4618
4619 dol_syslog(get_class($this)."::update", LOG_DEBUG);
4620 $resql = $this->db->query($sql);
4621 if ($resql) {
4622 if (!$error) {
4623 $result = $this->insertExtraFields();
4624 if ($result < 0) {
4625 $error++;
4626 }
4627 }
4628
4629 if (!$error && !$notrigger) {
4630 // Call trigger
4631 $result = $this->call_trigger('LINEPROPAL_MODIFY', $user);
4632 if ($result < 0) {
4633 $this->db->rollback();
4634 return -1;
4635 }
4636 // End call triggers
4637 }
4638
4639 if (!$error) {
4640 $this->db->commit();
4641 return 1;
4642 }
4643
4644 foreach ($this->errors as $errmsg) {
4645 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
4646 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4647 }
4648 $this->db->rollback();
4649 return -1 * $error;
4650 } else {
4651 $this->error = $this->db->error();
4652 $this->db->rollback();
4653 return -2;
4654 }
4655 }
4656
4657 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4664 public function update_total()
4665 {
4666 // phpcs:enable
4667 $this->db->begin();
4668
4669 // Mise a jour ligne en base
4670 $sql = "UPDATE ".MAIN_DB_PREFIX."propaldet SET";
4671 $sql .= " total_ht=".price2num($this->total_ht, 'MT');
4672 $sql .= ",total_tva=".price2num($this->total_tva, 'MT');
4673 $sql .= ",total_ttc=".price2num($this->total_ttc, 'MT');
4674 $sql .= " WHERE rowid = ".((int) $this->rowid);
4675
4676 dol_syslog("PropaleLigne::update_total", LOG_DEBUG);
4677
4678 $resql = $this->db->query($sql);
4679 if ($resql) {
4680 $this->db->commit();
4681 return 1;
4682 } else {
4683 $this->error = $this->db->error();
4684 $this->db->rollback();
4685 return -2;
4686 }
4687 }
4688}
print $langs trans("AuditedSecurityEvents").'</strong >< span class="opacitymedium"></span >< br > status
Definition security.php:604
$object ref
Definition info.php:79
Parent class of all other business classes (invoices, contracts, proposals, orders,...
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
Save a new position (field rang) for details lines.
deleteEcmFiles($mode=0)
Delete related files of object in database.
add_object_linked($origin=null, $origin_id=null, $f_user=null, $notrigger=0)
Add an object link into llx_element_element.
defineBuyPrice($unitPrice=0.0, $discountPercent=0.0, $fk_product=0)
Get buy price to use for margin calculation.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty.
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid=0, $f_user=null, $notrigger=0)
Delete all links between an object $this.
setErrorsFromObject($object)
setErrorsFromObject
static isExistingObject($element, $id, $ref='', $ref_ext='')
Check an object id/ref exists If you don't need/want to instantiate object and just need to know if o...
updateRangOfLine($rowid, $rang)
Update position of line (rang)
fetch_project()
Load the project with id $this->fk_project into this->project.
update_price($exclspec=0, $roundingadjust='none', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
deleteExtraFields()
Delete all extra fields values for the current object.
fetchObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $clause='OR', $alsosametype=1, $orderby='sourcetype', $loadalsoobjects=1)
Fetch array of objects linked to current object (object of enabled modules only).
static commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
static commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product id with another one.
line_max($fk_parent_line=0)
Get max value used for position of line (rang)
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
delete_linked_contact($source='', $code='')
Delete all links between an object $this and all its contacts in llx_element_contact.
call_trigger($triggerName, $user)
Call trigger based on this instance.
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
Class to manage absolute discounts.
Class to manage Dolibarr database access.
static getIdFromCode($dbs, $code)
Get id of currency from code.
static getIdAndTxFromCode($dbs, $code, $date_document='')
Get id and rate of currency from code.
File of class to manage predefined price products or services by customer.
Class to manage products or services.
const TYPE_PRODUCT
Regular product.
Class to manage proposals.
getTooltipContentArray($params)
getTooltipContentArray
const STATUS_DRAFT
Draft status.
set_date($user, $date, $notrigger=0)
Define proposal date.
const STATUS_SIGNED
Signed quote.
getNomUrl($withpicto=0, $option='', $get_params='', $notooltip=0, $save_lastsearch_value=-1, $addlinktonotes=-1)
Return clicable link of object (with eventually picto)
getKanbanView($option='', $arraydata=null)
Return clicable link of object (with eventually picto)
InvoiceArrayList($id)
Returns an array with id and ref of related invoices.
availability($availability_id, $notrigger=0)
Change the delivery time.
const STATUS_NOTSIGNED
Not signed quote.
$table_ref_field
{}
setDeliveryDate($user, $delivery_date, $notrigger=0)
Set delivery date.
load_state_board()
Charge indicateurs this->nb de tableau de bord.
classifyBilled(User $user, $notrigger=0, $note='')
Classify the proposal to status Billed.
getLinesArray($sqlforgedfilters='')
Retrieve an array of proposal lines.
fetch($rowid, $ref='', $ref_ext='', $forceentity=0)
Load a proposal from database.
set_availability($user, $id, $notrigger=0)
Set delivery.
fetch_lines($only_product=0, $loadalsotranslation=0, $sqlforgedfilters='')
Load array lines.
updateline($rowid, $pu, $qty, $remise_percent, $txtva, $txlocaltax1=0.0, $txlocaltax2=0.0, $desc='', $price_base_type='HT', $info_bits=0, $special_code=0, $fk_parent_line=0, $skip_update_total=0, $fk_fournprice=0, $pa_ht=0, $label='', $type=0, $date_start='', $date_end='', $array_options=array(), $fk_unit=null, $pu_ht_devise=0, $notrigger=0, $rang=0)
Update a proposal line.
update(User $user, $notrigger=0)
Update database.
demand_reason($demand_reason_id, $notrigger=0)
Change source demand.
info($id)
Object Proposal Information.
const STATUS_BILLED
Billed or processed quote.
static replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
getLibStatut($mode=0)
Return label of status of proposal (draft, validated, ...)
insert_discount($idremise)
Adding line of fixed discount in the proposal in DB.
getNextNumRef($soc)
Returns the reference to the following non used Proposal used depending on the active numbering modul...
LibStatut($status, $mode=1)
Return label of a status (draft, validated, ...)
valid($user, $notrigger=0)
Set status to validated.
static replaceProduct(DoliDB $db, $origin_id, $dest_id)
Function used to replace a product id with another one.
set_ref_client($user, $ref_client, $notrigger=0)
Set customer reference number.
setDraft($user, $notrigger=0)
Set draft status.
set_echeance($user, $date_end_validity, $notrigger=0)
Define end validity date.
set_demand_reason($user, $id, $notrigger=0)
Set source of demand.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template module.
deleteline($lineid, $id=0)
Delete detail line.
create($user, $notrigger=0)
Create commercial proposal into database this->ref can be set or empty.
liste_array($shortlist=0, $draft=0, $notcurrentuser=0, $socid=0, $limit=0, $offset=0, $sortfield='p.datep', $sortorder='DESC')
Return list of proposal (eventually filtered on user) into an array.
add_product($idproduct, $qty, $remise_percent=0)
Add line into array ->lines $this->thirdparty should be loaded.
initAsSpecimen()
Initialise an instance with random values.
closeProposal($user, $status, $note='', $notrigger=0)
Close/set the commercial proposal to status signed or refused (fill also date signature)
reopen($user, $status, $note='', $notrigger=0)
Set an overall discount on the proposal.
__construct($db, $socid=0, $propalid=0)
Constructor.
load_board($user, $mode)
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1=0.0, $txlocaltax2=0.0, $fk_product=0, $remise_percent=0.0, $price_base_type='HT', $pu_ttc=0.0, $info_bits=0, $type=0, $rang=-1, $special_code=0, $fk_parent_line=0, $fk_fournprice=0, $pa_ht=0, $label='', $date_start='', $date_end='', $array_options=array(), $fk_unit=null, $origin='', $origin_id=0, $pu_ht_devise=0, $fk_remise_except=0, $noupdateafterinsertline=0)
Add a proposal line into database (linked to product/service or not) The parameters are already suppo...
getInvoiceArrayList()
Returns an array with the numbers of related invoices.
set_date_livraison($user, $delivery_date, $notrigger=0)
Set delivery date.
const STATUS_VALIDATED
Validated status.
createFromClone(User $user, $socid=0, $forceentity=null, $update_prices=false, $update_desc=false)
Load an object from its id and create a new one in database.
Class to manage commercial proposal lines.
__construct($db)
Class line Contructor.
update($notrigger=0)
Update propal line object into DB.
fetch($rowid)
Retrieve the propal line object.
insert($notrigger=0)
Insert object line propal in database.
update_total()
Update DB line fields total_xxx Used by migration.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage translations.
Class to manage Dolibarr users.
trait CommonIncoterm
Superclass for incoterm classes.
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1, $nolog=0)
Remove a file or several files with a mask.
dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:62
dol_delete_preview($object)
Delete all preview files linked to object instance.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that returns whether VAT must be recoverable collected VAT (e.g.: VAT NPR in France)
dol_concatdesc($text1, $text2, $forxml=false, $invert=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
get_localtax($vatrate, $local, $thirdparty_buyer="", $thirdparty_seller="", $vatnpr=0)
Return localtax rate for a particular vat, when selling a product with vat $vatrate,...
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that return vat rate of a product line (according to seller, buyer and product vat rate) VAT...
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
publicphonebutton2 phonegreen basiclayout basiclayout TotalHT VATCode TotalVAT TotalLT1 TotalLT2 TotalTTC TotalHT clearboth nowraponall right right takeposterminal SELECT e rowid
Definition invoice.php:1954
publicphonebutton2 phonegreen basiclayout basiclayout TotalHT VATCode TotalVAT TotalLT1 TotalLT2 TotalTTC TotalHT clearboth nowraponall right right takeposterminal SELECT e e e e e statut
Definition invoice.php:1954
getMarginInfos($pvht, $remise_percent, $tva_tx, $localtax1_tx, $localtax2_tx, $fk_pa, $paht)
Return an array with margins information of a line.
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller='', $localtaxes_array='', $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition price.lib.php:86