dolibarr 23.0.3
supplier_proposal.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-2018 Philippe Grand <philippe.grand@atoo-net.com>
11 * Copyright (C) 2012-2014 Christophe Battarel <christophe.battarel@altairis.fr>
12 * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
13 * Copyright (C) 2014 Marcos García <marcosgdf@gmail.com>
14 * Copyright (C) 2016 Ferran Marcet <fmarcet@2byte.es>
15 * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
16 * Copyright (C) 2019-2025 Frédéric France <frederic.france@free.fr>
17 * Copyright (C) 2020 Tobias Sekan <tobias.sekan@startmail.com>
18 * Copyright (C) 2022 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
19 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
20 *
21 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 3 of the License, or
24 * (at your option) any later version.
25 *
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
30 *
31 * You should have received a copy of the GNU General Public License
32 * along with this program. If not, see <https://www.gnu.org/licenses/>.
33 */
34
40require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
41require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
42require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
43require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
44require_once DOL_DOCUMENT_ROOT.'/margin/lib/margins.lib.php';
45require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
46require_once DOL_DOCUMENT_ROOT.'/core/class/commonincoterm.class.php';
47
52{
53 use CommonIncoterm;
54
58 public $element = 'supplier_proposal';
59
63 public $table_element = 'supplier_proposal';
64
68 public $table_element_line = 'supplier_proposaldet';
69
73 public $class_element_line = 'SupplierProposalLine';
77 public $fk_element = 'fk_supplier_proposal';
78
82 public $picto = 'supplier_proposal';
83
88 public $restrictiononfksoc = 1;
89
93 protected $table_ref_field = 'ref';
94
98 public $socid; // Id client
99
105 public $author;
106
110 public $ref_fourn; //Reference saisie lors de l'ajout d'une ligne à la demande
114 public $ref_supplier; //Reference saisie lors de l'ajout d'une ligne à la demande
115
120 public $statut; // 0 (draft), 1 (validated), 2 (signed), 3 (not signed), 4 (processed/billed)
121
125 public $date;
126
130 public $delivery_date;
131
137 public $datec;
138
144 public $datev;
145
149 public $user_author_id;
150
156 public $price;
157
163 public $tva;
164
170 public $total;
171
175 public $cond_reglement_code;
179 public $cond_reglement_doc; // label doc
180
184 public $mode_reglement_code;
189 public $mode_reglement;
190
196 public $deposit_percent;
197
198
202 public $extraparams = array();
203
207 public $lines = array();
208
212 public $line;
213
214 public $labelStatus = array();
215 public $labelStatusShort = array();
216
220 public $nbtodo;
224 public $nbtodolate;
225
226 // Multicurrency
230 public $fk_multicurrency;
231
235 public $multicurrency_code;
239 public $multicurrency_tx;
243 public $multicurrency_total_ht;
247 public $multicurrency_total_tva;
251 public $multicurrency_total_ttc;
252
253 public $fields = array(
254 'ref' => array('type' => 'varchar(255)', 'label' => 'Ref', 'enabled' => 1, 'visible' => 1, 'showoncombobox' => 1, 'position' => 25, 'searchall' => 1),
255 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 750, 'searchall' => 1),
256 );
257
261 const STATUS_DRAFT = 0;
262
267
271 const STATUS_SIGNED = 2;
272
277
281 const STATUS_CLOSE = 4;
282
283
284
292 public function __construct($db, $socid = 0, $supplier_proposalid = 0)
293 {
294 $this->db = $db;
295
296 $this->ismultientitymanaged = 1;
297 $this->socid = $socid;
298 $this->id = $supplier_proposalid;
299 }
300
301
302 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
313 public function add_product($idproduct, $qty, $remise_percent = 0)
314 {
315 // phpcs:enable
316 global $conf, $mysoc;
317
318 if (!$qty) {
319 $qty = 1;
320 }
321
322 dol_syslog(get_class($this)."::add_product $idproduct, $qty, $remise_percent");
323 if ($idproduct > 0) {
324 $prod = new Product($this->db);
325 $prod->fetch($idproduct);
326
327 $productdesc = $prod->description;
328
329 $tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
330 $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
331 if (empty($tva_tx)) {
332 $tva_npr = 0;
333 }
334 $localtax1_tx = get_localtax($tva_tx, 1, $mysoc, $this->thirdparty, $tva_npr);
335 $localtax2_tx = get_localtax($tva_tx, 2, $mysoc, $this->thirdparty, $tva_npr);
336
337 // multiprix
338 if (getDolGlobalString('PRODUIT_MULTIPRICES') && $this->thirdparty->price_level) {
339 $price = $prod->multiprices[$this->thirdparty->price_level];
340 } else {
341 $price = $prod->price;
342 }
343
344 $line = new SupplierProposalLine($this->db);
345
346 $line->fk_product = $idproduct;
347 $line->desc = $productdesc;
348 $line->qty = $qty;
349 $line->subprice = $price;
350 $line->remise_percent = $remise_percent;
351 $line->tva_tx = $tva_tx;
352
353 $this->lines[] = $line;
354 return 1;
355 }
356 return -1;
357 }
358
359 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
366 public function insert_discount($idremise)
367 {
368 // phpcs:enable
369 global $langs;
370
371 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
372 include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
373
374 $this->db->begin();
375
376 $remise = new DiscountAbsolute($this->db);
377 $result = $remise->fetch($idremise);
378
379 if ($result > 0) {
380 if ($remise->fk_facture) { // Protection against multiple submission
381 $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
382 $this->db->rollback();
383 return -5;
384 }
385
386 $supplier_proposalligne = new SupplierProposalLine($this->db);
387 $supplier_proposalligne->fk_supplier_proposal = $this->id;
388 $supplier_proposalligne->fk_remise_except = $remise->id;
389 $supplier_proposalligne->desc = $remise->description; // Description ligne
390 $supplier_proposalligne->tva_tx = $remise->tva_tx;
391 $supplier_proposalligne->subprice = -(float) $remise->amount_ht;
392 $supplier_proposalligne->fk_product = 0; // Id produit predefini
393 $supplier_proposalligne->qty = 1;
394 $supplier_proposalligne->remise_percent = 0;
395 $supplier_proposalligne->rang = -1;
396 $supplier_proposalligne->info_bits = 2;
397
398 $supplier_proposalligne->total_ht = -(float) $remise->amount_ht;
399 $supplier_proposalligne->total_tva = -(float) $remise->amount_tva;
400 $supplier_proposalligne->total_ttc = -(float) $remise->amount_ttc;
401
402 $result = $supplier_proposalligne->insert();
403 if ($result > 0) {
404 $result = $this->update_price(1);
405 if ($result > 0) {
406 $this->db->commit();
407 return 1;
408 } else {
409 $this->db->rollback();
410 return -1;
411 }
412 } else {
413 $this->setErrorsFromObject($supplier_proposalligne);
414 $this->db->rollback();
415 return -2;
416 }
417 } else {
418 $this->db->rollback();
419 return -2;
420 }
421 }
422
460 public function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1 = 0, $txlocaltax2 = 0, $fk_product = 0, $remise_percent = 0, $price_base_type = 'HT', $pu_ttc = 0, $info_bits = 0, $type = 0, $rang = -1, $special_code = 0, $fk_parent_line = 0, $fk_fournprice = 0, $pa_ht = 0, $label = '', $array_options = [], $ref_supplier = '', $fk_unit = 0, $origin = '', $origin_id = 0, $pu_ht_devise = 0, $date_start = 0, $date_end = 0)
461 {
462 global $mysoc, $langs;
463
464 dol_syslog(get_class($this)."::addline supplier_proposalid=$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");
465 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
466
467 // Clean parameters
468 if (empty($remise_percent)) {
469 $remise_percent = 0;
470 }
471 if (empty($qty)) {
472 $qty = 0;
473 }
474 if (empty($info_bits)) {
475 $info_bits = 0;
476 }
477 if (empty($rang)) {
478 $rang = 0;
479 }
480 if (empty($fk_parent_line) || $fk_parent_line < 0) {
481 $fk_parent_line = 0;
482 }
483 if (empty($pu_ht)) {
484 $pu_ht = 0;
485 }
486
487 $remise_percent = price2num($remise_percent);
488 $qty = (float) price2num($qty);
489 $pu_ht = price2num($pu_ht);
490 $pu_ttc = price2num($pu_ttc);
491 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
492 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
493 }
494 $txlocaltax1 = price2num($txlocaltax1);
495 $txlocaltax2 = price2num($txlocaltax2);
496 $pa_ht = price2num($pa_ht);
497 if ($price_base_type == 'HT') {
498 $pu = $pu_ht;
499 } else {
500 $pu = $pu_ttc;
501 }
502
503 // Check parameters
504 if ($type < 0) {
505 return -1;
506 }
507
508 if ($this->status == self::STATUS_DRAFT) {
509 $this->db->begin();
510
511 if ($fk_product > 0) {
512 if (getDolGlobalInt('SUPPLIER_PROPOSAL_WITH_PREDEFINED_PRICES_ONLY') == 1) { // Not the common case
513 // Check quantity is enough
514 dol_syslog(get_class($this)."::addline we check supplier prices fk_product=".$fk_product." fk_fournprice=".$fk_fournprice." qty=".$qty." ref_supplier=".$ref_supplier);
515 $productsupplier = new ProductFournisseur($this->db);
516 if ($productsupplier->fetch($fk_product) > 0) {
517 $product_type = $productsupplier->type;
518 $label = $productsupplier->label;
519 $fk_prod_fourn_price = $fk_fournprice;
520
521 // We use 'none' instead of $ref_supplier, because fourn_ref may not exists anymore. So we will take the first supplier price ok.
522 // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
523 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
524 $result = $productsupplier->get_buyprice($fk_prod_fourn_price, $qty, $fk_product, 'none', $this->socid); // Search on couple $fk_prod_fourn_price/$qty first, then on triplet $qty/$fk_product/$ref_supplier/$this->socid
525 if ($result > 0) {
526 $pu = $productsupplier->fourn_pu; // Unit price supplier price set by get_buyprice
527 $ref_supplier = $productsupplier->ref_supplier; // Ref supplier price set by get_buyprice
528 // is remise percent not keyed but present for the product we add it
529 if ($remise_percent == 0 && $productsupplier->remise_percent != 0) {
530 $remise_percent = $productsupplier->remise_percent;
531 }
532 }
533 if ($result == 0) { // If result == 0, we failed to found the supplier reference price
534 $langs->load("errors");
535 $this->error = "Ref ".$productsupplier->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
536 $this->db->rollback();
537 dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
538 //$pu = $productsupplier->fourn_pu; // We do not overwrite unit price
539 //$ref = $productsupplier_fourn; // We do not overwrite ref supplier price
540 return -1;
541 }
542 if ($result == -1) {
543 $langs->load("errors");
544 $this->error = "Ref ".$productsupplier->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
545 $this->db->rollback();
546 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
547 return -1;
548 }
549 if ($result < -1) {
550 $this->setErrorsFromObject($productsupplier);
551 $this->db->rollback();
552 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
553 return -1;
554 }
555 } else {
556 $this->setErrorsFromObject($productsupplier);
557 $this->db->rollback();
558 return -1;
559 }
560 }
561 } else {
562 $product_type = $type;
563 }
564
565 // Calcul du total TTC et de la TVA pour la ligne a partir de
566 // qty, pu, remise_percent et txtva
567 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
568 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
569
570 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
571
572 // Clean vat code
573 $reg = array();
574 $vat_src_code = '';
575 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
576 $vat_src_code = $reg[1];
577 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
578 }
579
580 if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
581 $pu = 0;
582 }
583
584 $tabprice = calcul_price_total(
585 $qty,
586 $pu,
587 (float) $remise_percent,
588 $txtva,
589 (float) $txlocaltax1,
590 (float) $txlocaltax2,
591 0,
592 $price_base_type,
593 $info_bits,
594 $type,
595 $this->thirdparty,
596 $localtaxes_type,
597 100,
598 (float) $this->multicurrency_tx,
599 $pu_ht_devise
600 );
601 $total_ht = $tabprice[0];
602 $total_tva = $tabprice[1];
603 $total_ttc = $tabprice[2];
604 $total_localtax1 = $tabprice[9];
605 $total_localtax2 = $tabprice[10];
606 $pu = $pu_ht = $tabprice[3];
607
608 // MultiCurrency
609 $multicurrency_total_ht = $tabprice[16];
610 $multicurrency_total_tva = $tabprice[17];
611 $multicurrency_total_ttc = $tabprice[18];
612 $pu_ht_devise = $tabprice[19];
613
614 // Rang to use
615 $ranktouse = $rang;
616 if ($ranktouse == -1) {
617 $rangmax = $this->line_max($fk_parent_line);
618 $ranktouse = $rangmax + 1;
619 }
620
621 // Insert line
622 $this->line = new SupplierProposalLine($this->db);
623
624 $this->line->fk_supplier_proposal = $this->id;
625 $this->line->label = $label;
626 $this->line->desc = $desc;
627 $this->line->qty = $qty;
628
629 $this->line->vat_src_code = $vat_src_code;
630 $this->line->tva_tx = $txtva;
631 $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
632 $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
633 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
634 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
635 $this->line->fk_product = $fk_product;
636 $this->line->remise_percent = $remise_percent;
637 $this->line->subprice = (float) $pu_ht;
638 $this->line->rang = $ranktouse;
639 $this->line->info_bits = $info_bits;
640 $this->line->total_ht = (float) $total_ht;
641 $this->line->total_tva = (float) $total_tva;
642 $this->line->total_localtax1 = (float) $total_localtax1;
643 $this->line->total_localtax2 = (float) $total_localtax2;
644 $this->line->total_ttc = (float) $total_ttc;
645 $this->line->product_type = $type;
646 $this->line->special_code = $special_code;
647 $this->line->fk_parent_line = $fk_parent_line;
648 $this->line->fk_unit = $fk_unit;
649 $this->line->origin = $origin;
650 $this->line->origin_type = $origin;
651 $this->line->origin_id = $origin_id;
652 $this->line->ref_fourn = $this->db->escape($ref_supplier);
653 $this->line->date_start = $date_start;
654 $this->line->date_end = $date_end;
655
656 // infos merge
657 if (!empty($fk_product) && $fk_product > 0 && empty($fk_fournprice) && empty($pa_ht)) {
658 // When fk_fournprice is 0, we take the lowest buying price
659 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
660 $productFournisseur = new ProductFournisseur($this->db);
661 $productFournisseur->find_min_price_product_fournisseur($fk_product);
662 $this->line->fk_fournprice = $productFournisseur->product_fourn_price_id;
663 } else {
664 $this->line->fk_fournprice = ($fk_fournprice > 0 ? $fk_fournprice : 0); // If fk_fournprice is -1, we will not use fk_fournprice
665 }
666 $this->line->pa_ht = $pa_ht;
667 //var_dump($this->line->fk_fournprice);exit;
668
669 // Multicurrency
670 $this->line->fk_multicurrency = $this->fk_multicurrency;
671 $this->line->multicurrency_code = $this->multicurrency_code;
672 $this->line->multicurrency_subprice = (float) $pu_ht_devise;
673 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
674 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
675 $this->line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
676
677 // Mise en option de la ligne
678 if (empty($qty) && empty($special_code)) {
679 $this->line->special_code = 3;
680 }
681
682 if (is_array($array_options) && count($array_options) > 0) {
683 $this->line->array_options = $array_options;
684 }
685
686 $result = $this->line->insert();
687 if ($result > 0) {
688 // Reorder if child line
689 if (!empty($fk_parent_line)) {
690 $this->line_order(true, 'DESC');
691 } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) { // Update all rank of all other lines
692 $linecount = count($this->lines);
693 for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
694 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
695 }
696 }
697
698 // Mise a jour information denormalisees au niveau de la propale meme
699 $result = $this->update_price(1, 'auto', 0, $this->thirdparty); // This method is designed to add line from user input so total calculation must be done using 'auto' mode.
700 if ($result > 0) {
701 $this->db->commit();
702 return $this->line->id;
703 } else {
704 $this->db->rollback();
705 return -1;
706 }
707 } else {
708 $this->setErrorsFromObject($this->line);
709 $this->db->rollback();
710 return -2;
711 }
712 } else {
713 $this->error = 'BadStatusOfObjectToAddLine';
714 return -5;
715 }
716 }
717
718
745 public function updateline($rowid, $pu, $qty, $remise_percent, $txtva, $txlocaltax1 = 0, $txlocaltax2 = 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, $array_options = [], $ref_supplier = '', $fk_unit = 0, $pu_ht_devise = 0)
746 {
747 global $mysoc;
748
749 dol_syslog(get_class($this)."::updateLine $rowid, $pu, $qty, $remise_percent, $txtva, $desc, $price_base_type, $info_bits");
750 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
751
752 // Clean parameters
753 $remise_percent = price2num($remise_percent);
754 $qty = (float) price2num($qty);
755 $pu = price2num($pu);
756 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
757 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
758 }
759 $txlocaltax1 = price2num($txlocaltax1);
760 $txlocaltax2 = price2num($txlocaltax2);
761 $pa_ht = price2num($pa_ht);
762 if (empty($qty) && empty($special_code)) {
763 $special_code = 3; // Set option tag
764 }
765 if (!empty($qty) && $special_code == 3) {
766 $special_code = 0; // Remove option tag
767 }
768
769 if ($this->status == 0) {
770 $this->db->begin();
771
772 // Calcul du total TTC et de la TVA pour la ligne a partir de
773 // qty, pu, remise_percent et txtva
774 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
775 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
776
777 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
778
779 // Clean vat code
780 $reg = array();
781 $vat_src_code = '';
782 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
783 $vat_src_code = $reg[1];
784 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
785 }
786
787 if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
788 $pu = 0;
789 }
790
791 $tabprice = calcul_price_total(
792 $qty,
793 $pu,
794 (float) $remise_percent,
795 $txtva,
796 (float) $txlocaltax1,
797 (float) $txlocaltax2,
798 0,
799 $price_base_type,
800 $info_bits,
801 $type,
802 $this->thirdparty,
803 $localtaxes_type,
804 100,
805 (float) $this->multicurrency_tx,
806 $pu_ht_devise
807 );
808 $total_ht = $tabprice[0];
809 $total_tva = $tabprice[1];
810 $total_ttc = $tabprice[2];
811 $total_localtax1 = $tabprice[9];
812 $total_localtax2 = $tabprice[10];
813 $pu_ht = $tabprice[3];
814 $pu_tva = $tabprice[4];
815 $pu_ttc = $tabprice[5];
816
817 // MultiCurrency
818 $multicurrency_total_ht = $tabprice[16];
819 $multicurrency_total_tva = $tabprice[17];
820 $multicurrency_total_ttc = $tabprice[18];
821 $pu_ht_devise = $tabprice[19];
822
823 $pu = $pu_ht;
824 if ($price_base_type == 'TTC') {
825 $pu = $pu_ttc;
826 }
827
828 // Fetch current line from the database and then clone the object and set it in $oldline property
829 $line = new SupplierProposalLine($this->db);
830 $line->fetch($rowid);
831 $line->fetch_optionals();
832
833 $fk_product = $line->fk_product;
834
835 // Stock previous line records
836 $staticline = clone $line;
837
838 $line->oldline = $staticline;
839 $this->line = $line;
840 $this->line->context = $this->context;
841
842 // Reorder if fk_parent_line change
843 if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
844 $rangmax = $this->line_max($fk_parent_line);
845 $this->line->rang = $rangmax + 1;
846 }
847
848 $this->line->id = $rowid;
849 $this->line->label = $label;
850 $this->line->desc = $desc;
851 $this->line->qty = $qty;
852 $this->line->product_type = $type;
853
854 $this->line->vat_src_code = $vat_src_code;
855 $this->line->tva_tx = $txtva;
856 $this->line->localtax1_tx = $txlocaltax1;
857 $this->line->localtax2_tx = $txlocaltax2;
858 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
859 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
860 $this->line->remise_percent = $remise_percent;
861 $this->line->subprice = (float) $pu;
862 $this->line->info_bits = $info_bits;
863 $this->line->total_ht = (float) $total_ht;
864 $this->line->total_tva = (float) $total_tva;
865 $this->line->total_localtax1 = (float) $total_localtax1;
866 $this->line->total_localtax2 = (float) $total_localtax2;
867 $this->line->total_ttc = (float) $total_ttc;
868 $this->line->special_code = $special_code;
869 $this->line->fk_parent_line = $fk_parent_line;
870 $this->line->skip_update_total = $skip_update_total;
871 $this->line->ref_fourn = $ref_supplier;
872 $this->line->fk_unit = $fk_unit;
873
874 // infos merge
875 if (!empty($fk_product) && $fk_product > 0 && empty($fk_fournprice) && empty($pa_ht)) {
876 // by external module, take lowest buying price
877 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
878 $productFournisseur = new ProductFournisseur($this->db);
879 $productFournisseur->find_min_price_product_fournisseur($fk_product);
880 $this->line->fk_fournprice = $productFournisseur->product_fourn_price_id;
881 } else {
882 $this->line->fk_fournprice = $fk_fournprice;
883 }
884 $this->line->pa_ht = $pa_ht;
885
886 if (is_array($array_options) && count($array_options) > 0) {
887 // We replace values in this->line->array_options only for entries defined into $array_options
888 foreach ($array_options as $key => $value) {
889 $this->line->array_options[$key] = $array_options[$key];
890 }
891 }
892
893 // Multicurrency
894 $this->line->multicurrency_subprice = (float) $pu_ht_devise;
895 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
896 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
897 $this->line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
898
899 $result = $this->line->update();
900 if ($result > 0) {
901 // Reorder if child line
902 if (!empty($fk_parent_line)) {
903 $this->line_order(true, 'DESC');
904 }
905
906 $this->update_price(1);
907
908 $this->db->commit();
909 return $result;
910 } else {
911 $this->error = $this->db->error();
912 $this->db->rollback();
913 return -1;
914 }
915 } else {
916 dol_syslog(get_class($this)."::updateline Erreur -2 SupplierProposal en mode incompatible pour cette action");
917 return -2;
918 }
919 }
920
921
928 public function deleteLine($lineid)
929 {
930 global $user;
931
932 if ($this->status == 0) {
933 $line = new SupplierProposalLine($this->db);
934
935 // For triggers
936 $line->fetch($lineid);
937
938 if ($line->delete($user) > 0) {
939 $this->update_price(1);
940
941 return 1;
942 } else {
943 return -1;
944 }
945 } else {
946 return -2;
947 }
948 }
949
950
959 public function create($user, $notrigger = 0)
960 {
961 global $langs, $conf, $mysoc, $hookmanager;
962 $error = 0;
963
964 $now = dol_now();
965
966 dol_syslog(get_class($this)."::create");
967
968 // Check parameters
969 $result = $this->fetch_thirdparty();
970 if ($result < 0) {
971 $this->error = "Failed to fetch company";
972 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
973 return -3;
974 }
975 if (!empty($this->ref)) { // We check that ref is not already used
976 $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
977 if ($result > 0) {
978 $this->error = 'ErrorRefAlreadyExists';
979 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
980 $this->db->rollback();
981 return -1;
982 }
983 }
984
985 // Set tmp vars
986 $delivery_date = $this->delivery_date;
987
988 // Multicurrency
989 if (!empty($this->multicurrency_code)) {
990 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $now);
991 }
992 if (empty($this->fk_multicurrency)) {
993 $this->multicurrency_code = getDolCurrency();
994 $this->fk_multicurrency = 0;
995 $this->multicurrency_tx = 1;
996 }
997
998 $this->db->begin();
999
1000 // Insert into database
1001 $sql = "INSERT INTO ".MAIN_DB_PREFIX."supplier_proposal (";
1002 $sql .= "fk_soc";
1003 $sql .= ", price";
1004 $sql .= ", total_tva";
1005 $sql .= ", total_ttc";
1006 $sql .= ", datec";
1007 $sql .= ", ref";
1008 $sql .= ", fk_user_author";
1009 $sql .= ", note_private";
1010 $sql .= ", note_public";
1011 $sql .= ", model_pdf";
1012 $sql .= ", fk_cond_reglement";
1013 $sql .= ", deposit_percent";
1014 $sql .= ", fk_mode_reglement";
1015 $sql .= ", fk_account";
1016 $sql .= ", date_livraison";
1017 $sql .= ", fk_shipping_method";
1018 $sql .= ", fk_projet";
1019 $sql .= ", entity";
1020 $sql .= ", fk_multicurrency";
1021 $sql .= ", multicurrency_code";
1022 $sql .= ", multicurrency_tx";
1023 $sql .= ") ";
1024 $sql .= " VALUES (";
1025 $sql .= ((int) $this->socid);
1026 $sql .= ", 0";
1027 $sql .= ", 0";
1028 $sql .= ", 0";
1029 $sql .= ", '".$this->db->idate($now)."'";
1030 $sql .= ", '(PROV)'";
1031 $sql .= ", ".($user->id > 0 ? ((int) $user->id) : "null");
1032 $sql .= ", '".$this->db->escape($this->note_private)."'";
1033 $sql .= ", '".$this->db->escape($this->note_public)."'";
1034 $sql .= ", '".$this->db->escape($this->model_pdf)."'";
1035 $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : 'NULL');
1036 $sql .= ", ".(!empty($this->deposit_percent) ? "'" . $this->db->escape($this->deposit_percent) . "'" : 'NULL');
1037 $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : 'NULL');
1038 $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
1039 $sql .= ", ".(isDolTms($delivery_date) ? "'".$this->db->idate($delivery_date)."'" : "null");
1040 $sql .= ", ".($this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : 'NULL');
1041 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
1042 $sql .= ", ".((int) $conf->entity);
1043 $sql .= ", ".((int) $this->fk_multicurrency);
1044 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1045 $sql .= ", ".((float) $this->multicurrency_tx);
1046 $sql .= ")";
1047
1048 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1049 $resql = $this->db->query($sql);
1050 if ($resql) {
1051 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."supplier_proposal");
1052
1053 if ($this->id) {
1054 $this->ref = '(PROV'.$this->id.')';
1055 $sql = 'UPDATE '.MAIN_DB_PREFIX."supplier_proposal SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
1056
1057 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1058 $resql = $this->db->query($sql);
1059 if (!$resql) {
1060 $error++;
1061 }
1062
1063 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1064 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1065 }
1066
1067 // Add object linked
1068 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1069 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1070 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, ...))
1071 foreach ($tmp_origin_id as $origin_id) {
1072 $ret = $this->add_object_linked($origin, $origin_id);
1073 if (!$ret) {
1074 dol_print_error($this->db);
1075 $error++;
1076 }
1077 }
1078 }
1079 }
1080 }
1081
1082 /*
1083 * Insertion du detail des produits dans la base
1084 */
1085 if (!$error) {
1086 $fk_parent_line = 0;
1087 $num = count($this->lines);
1088
1089 for ($i = 0; $i < $num; $i++) {
1090 // Reset fk_parent_line for no child products and special product
1091 if (($this->lines[$i]->product_type != 9 && empty($this->lines[$i]->fk_parent_line)) || $this->lines[$i]->product_type == 9) {
1092 $fk_parent_line = 0;
1093 }
1094
1095 $result = $this->addline(
1096 $this->lines[$i]->desc,
1097 $this->lines[$i]->subprice,
1098 $this->lines[$i]->qty,
1099 $this->lines[$i]->tva_tx,
1100 $this->lines[$i]->localtax1_tx,
1101 $this->lines[$i]->localtax2_tx,
1102 $this->lines[$i]->fk_product,
1103 $this->lines[$i]->remise_percent,
1104 'HT',
1105 0,
1106 0,
1107 $this->lines[$i]->product_type,
1108 $this->lines[$i]->rang,
1109 $this->lines[$i]->special_code,
1110 $fk_parent_line,
1111 $this->lines[$i]->fk_fournprice,
1112 $this->lines[$i]->pa_ht,
1113 empty($this->lines[$i]->label) ? '' : $this->lines[$i]->label, // deprecated
1114 $this->lines[$i]->array_options,
1115 $this->lines[$i]->ref_fourn,
1116 $this->lines[$i]->fk_unit,
1117 'supplier_proposal',
1118 $this->lines[$i]->rowid
1119 );
1120
1121 if ($result < 0) {
1122 $error++;
1123 $this->error = $this->db->error;
1124 dol_print_error($this->db);
1125 break;
1126 }
1127 // Defined the new fk_parent_line
1128 if ($result > 0 && $this->lines[$i]->product_type == 9) {
1129 $fk_parent_line = $result;
1130 }
1131 }
1132 }
1133
1134 if (!$error) {
1135 // Update denormalized data
1136 $resql = $this->update_price(1);
1137 if ($resql) {
1138 $action = 'update';
1139
1140 // Actions on extra fields
1141 if (!$error) {
1142 $result = $this->insertExtraFields();
1143 if ($result < 0) {
1144 $error++;
1145 }
1146 }
1147
1148 if (!$error && !$notrigger) {
1149 // Call trigger
1150 $result = $this->call_trigger('PROPOSAL_SUPPLIER_CREATE', $user);
1151 if ($result < 0) {
1152 $error++;
1153 }
1154 // End call triggers
1155 }
1156 } else {
1157 $this->error = $this->db->lasterror();
1158 $error++;
1159 }
1160 }
1161 } else {
1162 $this->error = $this->db->lasterror();
1163 $error++;
1164 }
1165
1166 if (!$error) {
1167 $this->db->commit();
1168 dol_syslog(get_class($this)."::create done id=".$this->id);
1169 return $this->id;
1170 } else {
1171 $this->db->rollback();
1172 return -2;
1173 }
1174 } else {
1175 $this->error = $this->db->lasterror();
1176 $this->db->rollback();
1177 return -1;
1178 }
1179 }
1180
1188 public function createFromClone(User $user, $fromid = 0)
1189 {
1190 global $conf, $hookmanager;
1191
1192 $error = 0;
1193 $now = dol_now();
1194
1195 $this->db->begin();
1196
1197 // get extrafields so they will be clone
1198 foreach ($this->lines as $line) {
1199 $line->fetch_optionals();
1200 }
1201
1202 // Load source object
1203 $objFrom = clone $this;
1204
1205 $objsoc = new Societe($this->db);
1206
1207 // Change socid if needed
1208 if (!empty($fromid) && $fromid != $this->socid) {
1209 if ($objsoc->fetch($fromid) > 0) {
1210 $this->socid = $objsoc->id;
1211 $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1212 $this->deposit_percent = (!empty($objsoc->deposit_percent) ? $objsoc->deposit_percent : 0);
1213 $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1214 unset($this->fk_project);
1215 }
1216
1217 // TODO Change product price if multi-prices
1218 } else {
1219 $objsoc->fetch($this->socid);
1220 }
1221
1222 $this->id = 0;
1223 $this->status = 0;
1224 $this->statut = 0;
1225
1226 if (!getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') || !is_readable(DOL_DOCUMENT_ROOT."/core/modules/supplier_proposal/" . getDolGlobalString('SUPPLIER_PROPOSAL_ADDON').".php")) {
1227 $this->error = 'ErrorSetupNotComplete';
1228 return -1;
1229 }
1230
1231 // Clear fields
1232 $this->user_author_id = $user->id;
1233 $this->user_validation_id = 0;
1234 $this->date = $now;
1235
1236 // Set ref
1237 require_once DOL_DOCUMENT_ROOT."/core/modules/supplier_proposal/" . getDolGlobalString('SUPPLIER_PROPOSAL_ADDON').'.php';
1238 $obj = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
1239 $modSupplierProposal = new $obj();
1240 '@phan-var-force ModeleNumRefSupplierProposal $modSupplierProposal';
1241 $this->ref = $modSupplierProposal->getNextValue($objsoc, $this);
1242
1243 // Create clone
1244 $this->context['createfromclone'] = 'createfromclone';
1245 $result = $this->create($user);
1246 if ($result < 0) {
1247 $error++;
1248 }
1249
1250 if (!$error) {
1251 // Hook of thirdparty module
1252 if (is_object($hookmanager)) {
1253 $parameters = array('objFrom' => $objFrom);
1254 $action = '';
1255 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1256 if ($reshook < 0) {
1257 $this->setErrorsFromObject($hookmanager);
1258 $error++;
1259 }
1260 }
1261 }
1262
1263 unset($this->context['createfromclone']);
1264
1265 // End
1266 if (!$error) {
1267 $this->db->commit();
1268 return $this->id;
1269 } else {
1270 $this->db->rollback();
1271 return -1;
1272 }
1273 }
1274
1282 public function fetch($rowid, $ref = '')
1283 {
1284 $sql = "SELECT p.rowid, p.entity, p.ref, p.fk_soc as socid";
1285 $sql .= ", p.total_ttc, p.total_tva, p.localtax1, p.localtax2, p.total_ht";
1286 $sql .= ", p.datec, GREATEST(p.tms, pef.tms) as date_modification";
1287 $sql .= ", p.date_valid as datev";
1288 $sql .= ", p.date_livraison as delivery_date";
1289 $sql .= ", p.model_pdf, p.extraparams";
1290 $sql .= ", p.note_private, p.note_public";
1291 $sql .= ", p.fk_projet as fk_project, p.fk_statut as status";
1292 $sql .= ", p.fk_user_author, p.fk_user_modif, p.fk_user_valid, p.fk_user_cloture";
1293 $sql .= ", p.fk_cond_reglement";
1294 $sql .= ", p.fk_mode_reglement";
1295 $sql .= ', p.fk_account';
1296 $sql .= ", p.fk_shipping_method";
1297 $sql .= ", p.last_main_doc";
1298 $sql .= ", p.fk_multicurrency, p.multicurrency_code, p.multicurrency_tx, p.multicurrency_total_ht, p.multicurrency_total_tva, p.multicurrency_total_ttc";
1299 $sql .= ", c.label as statut_label";
1300 $sql .= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc, p.deposit_percent";
1301 $sql .= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement";
1302 $sql .= " FROM ".MAIN_DB_PREFIX."c_propalst as c, ".MAIN_DB_PREFIX."supplier_proposal as p";
1303 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."supplier_proposal_extrafields as pef ON pef.fk_object=p.rowid";
1304 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id';
1305 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON p.fk_cond_reglement = cr.rowid';
1306 $sql .= " WHERE p.fk_statut = c.id";
1307 $sql .= " AND p.entity IN (".getEntity('supplier_proposal').")";
1308 if ($ref) {
1309 $sql .= " AND p.ref = '".$this->db->escape($ref)."'";
1310 } else {
1311 $sql .= " AND p.rowid = ".((int) $rowid);
1312 }
1313
1314 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1315 $resql = $this->db->query($sql);
1316 if ($resql) {
1317 if ($this->db->num_rows($resql)) {
1318 $obj = $this->db->fetch_object($resql);
1319
1320 $this->id = $obj->rowid;
1321 $this->entity = $obj->entity;
1322
1323 $this->ref = $obj->ref;
1324 $this->total_ht = $obj->total_ht;
1325 $this->total_tva = $obj->total_tva;
1326 $this->total_localtax1 = $obj->localtax1;
1327 $this->total_localtax2 = $obj->localtax2;
1328 $this->total_ttc = $obj->total_ttc;
1329 $this->socid = $obj->socid;
1330 $this->fk_project = $obj->fk_project;
1331 $this->model_pdf = $obj->model_pdf;
1332 $this->note = $obj->note_private; // TODO deprecated
1333 $this->note_private = $obj->note_private;
1334 $this->note_public = $obj->note_public;
1335 $this->statut = (int) $obj->status;
1336 $this->status = (int) $obj->status;
1337 $this->datec = $this->db->jdate($obj->datec); // TODO deprecated
1338 $this->datev = $this->db->jdate($obj->datev); // TODO deprecated
1339 $this->date_creation = $this->db->jdate($obj->datec); // Creation date
1340 $this->date_modification = $this->db->jdate($obj->date_modification); // Modification date
1341 $this->date = $this->date_creation;
1342 $this->date_validation = $this->db->jdate($obj->datev); // Validation date
1343 $this->delivery_date = $this->db->jdate($obj->delivery_date);
1344 $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1345
1346 $this->last_main_doc = $obj->last_main_doc;
1347 $this->mode_reglement_id = $obj->fk_mode_reglement;
1348 $this->mode_reglement_code = $obj->mode_reglement_code;
1349 $this->mode_reglement = $obj->mode_reglement;
1350 $this->deposit_percent = $obj->deposit_percent;
1351 $this->fk_account = ($obj->fk_account > 0) ? $obj->fk_account : null;
1352 $this->cond_reglement_id = $obj->fk_cond_reglement;
1353 $this->cond_reglement_code = $obj->cond_reglement_code;
1354 $this->cond_reglement = $obj->cond_reglement;
1355 $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1356
1357 $this->extraparams = (array) (!empty($obj->extraparams) ? json_decode($obj->extraparams, true) : array());
1358
1359 $this->user_author_id = $obj->fk_user_author;
1360 $this->user_modification_id = $obj->fk_user_modif;
1361 $this->user_validation_id = $obj->fk_user_valid;
1362 $this->user_closing_id = $obj->fk_user_cloture;
1363
1364 // Multicurrency
1365 $this->fk_multicurrency = $obj->fk_multicurrency;
1366 $this->multicurrency_code = $obj->multicurrency_code;
1367 $this->multicurrency_tx = $obj->multicurrency_tx;
1368 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1369 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1370 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1371
1372 $this->db->free($resql);
1373
1374 // Retrieve all extrafield
1375 // fetch optionals attributes and labels
1376 $this->fetch_optionals();
1377
1378 $this->lines = array();
1379
1380 // Lines of supplier proposals
1381 $sql = "SELECT d.rowid, d.fk_supplier_proposal, d.fk_parent_line, d.label as custom_label, d.description, d.price, d.tva_tx, d.localtax1_tx, d.localtax2_tx, d.qty, d.fk_remise_except, d.remise_percent, d.subprice, d.fk_product,";
1382 $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,";
1383 $sql .= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label,';
1384 $sql .= ' d.ref_fourn as ref_produit_fourn, d.extraparams,';
1385 $sql .= ' d.fk_multicurrency, d.multicurrency_code, d.multicurrency_subprice, d.multicurrency_total_ht, d.multicurrency_total_tva, d.multicurrency_total_ttc, d.fk_unit';
1386 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposaldet as d";
1387 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON d.fk_product = p.rowid";
1388 $sql .= " WHERE d.fk_supplier_proposal = ".((int) $this->id);
1389 $sql .= " ORDER by d.rang";
1390
1391 $result = $this->db->query($sql);
1392 if ($result) {
1393 $num = $this->db->num_rows($result);
1394 $i = 0;
1395
1396 while ($i < $num) {
1397 $objp = $this->db->fetch_object($result);
1398
1399 $line = new SupplierProposalLine($this->db);
1400
1401 $line->rowid = $objp->rowid; // deprecated
1402 $line->id = $objp->rowid;
1403 $line->fk_supplier_proposal = $objp->fk_supplier_proposal;
1404 $line->fk_parent_line = $objp->fk_parent_line;
1405 $line->product_type = $objp->product_type;
1406 $line->label = $objp->custom_label;
1407 $line->desc = $objp->description; // Description ligne
1408 $line->qty = $objp->qty;
1409 $line->tva_tx = $objp->tva_tx;
1410 $line->localtax1_tx = $objp->localtax1_tx;
1411 $line->localtax2_tx = $objp->localtax2_tx;
1412 $line->subprice = $objp->subprice;
1413 $line->fk_remise_except = $objp->fk_remise_except;
1414 $line->remise_percent = $objp->remise_percent;
1415
1416 $line->info_bits = $objp->info_bits;
1417 $line->total_ht = $objp->total_ht;
1418 $line->total_tva = $objp->total_tva;
1419 $line->total_localtax1 = $objp->total_localtax1;
1420 $line->total_localtax2 = $objp->total_localtax2;
1421 $line->total_ttc = $objp->total_ttc;
1422 $line->fk_fournprice = $objp->fk_fournprice;
1423 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1424 $line->pa_ht = $marginInfos[0];
1425 $line->marge_tx = $marginInfos[1];
1426 $line->marque_tx = $marginInfos[2];
1427 $line->special_code = $objp->special_code;
1428 $line->rang = $objp->rang;
1429
1430 $line->fk_product = $objp->fk_product;
1431
1432 $line->ref = $objp->product_ref; // deprecated
1433 $line->product_ref = $objp->product_ref;
1434 $line->libelle = $objp->product_label; // deprecated
1435 $line->product_label = $objp->product_label;
1436 $line->product_desc = $objp->product_desc; // Description produit
1437 $line->fk_product_type = $objp->fk_product_type;
1438
1439 $line->ref_fourn = $objp->ref_produit_fourn;
1440
1441 $line->extraparams = !empty($objp->extraparams) ? (array) json_decode($objp->extraparams, true) : array();
1442
1443 // Multicurrency
1444 $line->fk_multicurrency = $objp->fk_multicurrency;
1445 $line->multicurrency_code = $objp->multicurrency_code;
1446 $line->multicurrency_subprice = $objp->multicurrency_subprice;
1447 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
1448 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
1449 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
1450 $line->fk_unit = $objp->fk_unit;
1451
1452 $line->fetch_optionals();
1453
1454 $this->lines[$i] = $line;
1455
1456 $i++;
1457 }
1458 $this->db->free($result);
1459 } else {
1460 $this->error = $this->db->error();
1461 return -1;
1462 }
1463
1464 return 1;
1465 }
1466
1467 $this->error = "Record Not Found";
1468 return 0;
1469 } else {
1470 $this->error = $this->db->error();
1471 return -1;
1472 }
1473 }
1474
1482 public function valid($user, $notrigger = 0)
1483 {
1484 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1485
1486 global $conf, $langs;
1487
1488 $error = 0;
1489 $now = dol_now();
1490
1491 if ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'creer'))
1492 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'validate_advance'))) {
1493 $this->db->begin();
1494
1495 // Numbering module definition
1496 $soc = new Societe($this->db);
1497 $result = $soc->fetch($this->socid);
1498
1499 if ($result < 0) {
1500 return -1;
1501 }
1502 if (!getDolGlobalBool('SUPPLIER_PROPOSAL_NOCHECK_ONBUY_PRODUCTS_ONVALID') && !$this->checkActiveProductInLines('onbuy')) {
1503 dol_syslog(get_class($this)."::valid checkActiveProductInLines ".$this->error, LOG_INFO);
1504 return -1;
1505 }
1506 // Define new ref
1507 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1508 $num = $this->getNextNumRef($soc);
1509 } else {
1510 $num = (string) $this->ref;
1511 }
1512 $this->newref = dol_sanitizeFileName($num);
1513
1514 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1515 $sql .= " SET ref = '".$this->db->escape($num)."',";
1516 $sql .= " fk_statut = 1, date_valid='".$this->db->idate($now)."', fk_user_valid=".((int) $user->id);
1517 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1518
1519 dol_syslog(get_class($this)."::valid", LOG_DEBUG);
1520 $resql = $this->db->query($sql);
1521 if (!$resql) {
1522 dol_print_error($this->db);
1523 $error++;
1524 }
1525
1526 // Trigger calls
1527 if (!$error && !$notrigger) {
1528 // Call trigger
1529 $result = $this->call_trigger('PROPOSAL_SUPPLIER_VALIDATE', $user);
1530 if ($result < 0) {
1531 $error++;
1532 }
1533 // End call triggers
1534 }
1535
1536 if (!$error) {
1537 $this->oldref = $this->ref;
1538
1539 // Rename directory if dir was a temporary ref
1540 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1541 // Now we rename also files into index
1542 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'supplier_proposal/".$this->db->escape($this->newref)."'";
1543 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1544 $resql = $this->db->query($sql);
1545 if (!$resql) {
1546 $error++;
1547 $this->error = $this->db->lasterror();
1548 }
1549 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'supplier_proposal/".$this->db->escape($this->newref)."'";
1550 $sql .= " WHERE filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1551 $resql = $this->db->query($sql);
1552 if (!$resql) {
1553 $error++;
1554 $this->error = $this->db->lasterror();
1555 }
1556
1557 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1558 $oldref = dol_sanitizeFileName($this->ref);
1559 $newref = dol_sanitizeFileName($num);
1560 $dirsource = $conf->supplier_proposal->dir_output.'/'.$oldref;
1561 $dirdest = $conf->supplier_proposal->dir_output.'/'.$newref;
1562 if (!$error && file_exists($dirsource)) {
1563 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
1564 if (@rename($dirsource, $dirdest)) {
1565 dol_syslog("Rename ok");
1566 // Rename docs starting with $oldref with $newref
1567 $listoffiles = dol_dir_list($conf->supplier_proposal->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1568 foreach ($listoffiles as $fileentry) {
1569 $dirsource = $fileentry['name'];
1570 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1571 $dirsource = $fileentry['path'].'/'.$dirsource;
1572 $dirdest = $fileentry['path'].'/'.$dirdest;
1573 @rename($dirsource, $dirdest);
1574 }
1575 }
1576 }
1577 }
1578
1579 $this->ref = $num;
1580 $this->statut = self::STATUS_VALIDATED;
1582 $this->user_validation_id = $user->id;
1583 $this->datev = $now;
1584 $this->date_validation = $now;
1585
1586 if (getDolGlobalString('SUPPLIER_PROPOSAL_AUTOADD_USER_CONTACT')) {
1587 $result = $this->add_contact($user->id, 'SALESREPFOLL', 'internal', 1);
1588 if ($result < 0 && $result != -2) { // -2 means already exists
1589 $error++;
1590 }
1591 }
1592
1593 $this->db->commit();
1594 return 1;
1595 } else {
1596 $this->db->rollback();
1597 return -1;
1598 }
1599 } else {
1600 dol_syslog("You don't have permission to validate supplier proposal", LOG_WARNING);
1601 return -2;
1602 }
1603 }
1604
1605 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1614 public function set_date_livraison($user, $delivery_date)
1615 {
1616 // phpcs:enable
1617 return $this->setDeliveryDate($user, $delivery_date);
1618 }
1619
1627 public function setDeliveryDate($user, $delivery_date)
1628 {
1629 if ($user->hasRight('supplier_proposal', 'creer')) {
1630 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1631 $sql .= " SET date_livraison = ".(isDolTms($delivery_date) ? "'".$this->db->idate($delivery_date)."'" : 'null');
1632 $sql .= " WHERE rowid = ".((int) $this->id);
1633
1634 if ($this->db->query($sql)) {
1635 $this->delivery_date = $delivery_date;
1636 return 1;
1637 } else {
1638 $this->error = $this->db->error();
1639 dol_syslog(get_class($this)."::setDeliveryDate Erreur SQL");
1640 return -1;
1641 }
1642 }
1643 return 0;
1644 }
1645
1646 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1654 /*
1655 public function set_remise_percent($user, $remise)
1656 {
1657 // phpcs:enable
1658 $remise = trim($remise) ?trim($remise) : 0;
1659
1660 if ($user->hasRight('supplier_proposal', 'creer')) {
1661 $remise = price2num($remise, 2);
1662
1663 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal SET remise_percent = ".((float) $remise);
1664 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1665
1666 if ($this->db->query($sql)) {
1667 $this->remise_percent = ((float) $remise);
1668 $this->update_price(1);
1669 return 1;
1670 } else {
1671 $this->error = $this->db->error();
1672 return -1;
1673 }
1674 }
1675 return 0;
1676 }
1677 */
1678
1679 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1687 /*
1688 public function set_remise_absolue($user, $remise)
1689 {
1690 // phpcs:enable
1691 if (empty($remise)) {
1692 $remise = 0;
1693 }
1694
1695 $remise = price2num($remise);
1696
1697 if ($user->hasRight('supplier_proposal', 'creer')) {
1698 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1699 $sql .= " SET remise_absolue = ".((float) $remise);
1700 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1701
1702 if ($this->db->query($sql)) {
1703 $this->remise_absolue = $remise;
1704 $this->update_price(1);
1705 return 1;
1706 } else {
1707 $this->error = $this->db->error();
1708 return -1;
1709 }
1710 }
1711 return 0;
1712 }
1713 */
1714
1715
1725 public function reopen($user, $status, $note = '', $notrigger = 0)
1726 {
1727 global $langs, $conf;
1728
1729 $error = 0;
1730
1731 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1732 $sql .= " SET fk_statut = ".((int) $status).",";
1733 if (!empty($note)) {
1734 $sql .= " note_private = '".$this->db->escape($note)."',";
1735 }
1736 $sql .= " date_cloture = NULL, fk_user_cloture = NULL";
1737 $sql .= " WHERE rowid = ".((int) $this->id);
1738
1739 $this->db->begin();
1740
1741 dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
1742 $resql = $this->db->query($sql);
1743 if (!$resql) {
1744 $error++;
1745 $this->errors[] = "Error ".$this->db->lasterror();
1746 }
1747 if (!$error) {
1748 if (!$notrigger) {
1749 // Call trigger
1750 $result = $this->call_trigger('PROPOSAL_SUPPLIER_REOPEN', $user);
1751 if ($result < 0) {
1752 $error++;
1753 }
1754 // End call triggers
1755 }
1756 }
1757
1758 // Commit or rollback
1759 if ($error) {
1760 if (!empty($this->errors)) {
1761 foreach ($this->errors as $errmsg) {
1762 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1763 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1764 }
1765 }
1766 $this->db->rollback();
1767 return -1 * $error;
1768 } else {
1769 $this->statut = $status;
1770 $this->status = $status;
1771
1772 $this->db->commit();
1773 return 1;
1774 }
1775 }
1776
1777
1786 public function cloture($user, $status, $note)
1787 {
1788 global $langs, $conf;
1789 $hidedetails = 0;
1790 $hidedesc = 0;
1791 $hideref = 0;
1792
1793 $error = 0;
1794 $now = dol_now();
1795
1796 $this->db->begin();
1797
1798 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1799 $sql .= " SET fk_statut = ".((int) $status).", note_private = '".$this->db->escape($note)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".((int) $user->id);
1800 $sql .= " WHERE rowid = ".((int) $this->id);
1801
1802 $resql = $this->db->query($sql);
1803 if ($resql) {
1804 $this->statut = $status;
1805 $this->status = $status;
1806
1807 $modelpdf = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED', (empty($this->model_pdf) ? '' : $this->model_pdf));
1808 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_REFUSED';
1809
1810 if ($status == 2) {
1811 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_SIGNED';
1812 $modelpdf = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL', (empty($this->model_pdf) ? '' : $this->model_pdf));
1813
1814 if (getDolGlobalString('SUPPLIER_PROPOSAL_UPDATE_PRICE_ON_SUPPlIER_PROPOSAL')) { // TODO This option was not tested correctly. Error if product ref does not exists
1815 $result = $this->updateOrCreatePriceFournisseur($user);
1816 }
1817 }
1818 if ($status == 4) {
1819 $triggerName = 'PROPOSAL_SUPPLIER_CLASSIFY_BILLED';
1820 }
1821
1822 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1823 // Define output language
1824 $outputlangs = $langs;
1825 if (getDolGlobalInt('MAIN_MULTILANGS')) {
1826 $outputlangs = new Translate("", $conf);
1827 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
1828 $outputlangs->setDefaultLang($newlang);
1829 }
1830
1831 $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1832 }
1833
1834 // Call trigger
1835 $result = $this->call_trigger($triggerName, $user);
1836 if ($result < 0) {
1837 $error++;
1838 }
1839 // End call triggers
1840
1841 if (!$error) {
1842 $this->db->commit();
1843 return 1;
1844 } else {
1845 $this->db->rollback();
1846 return -1;
1847 }
1848 } else {
1849 $this->error = $this->db->lasterror();
1850 $this->errors[] = $this->db->lasterror();
1851 $this->db->rollback();
1852 return -1;
1853 }
1854 }
1855
1862 public function updateOrCreatePriceFournisseur($user)
1863 {
1864 global $conf;
1865
1866 dol_syslog(get_class($this)."::updateOrCreatePriceFournisseur", LOG_DEBUG);
1867 foreach ($this->lines as $product) {
1868 if ($product->subprice <= 0) {
1869 continue;
1870 }
1871 $productsupplier = new ProductFournisseur($this->db);
1872
1873 $multicurrency_tx = 1;
1874 $fk_multicurrency = 0;
1875
1876 if (empty($this->thirdparty)) {
1877 $this->fetch_thirdparty();
1878 }
1879
1880 $ref_fourn = $product->ref_fourn;
1881 if (empty($ref_fourn)) {
1882 $ref_fourn = $product->ref_supplier;
1883 }
1884 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1885 list($fk_multicurrency, $multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $product->multicurrency_code);
1886 }
1887 $productsupplier->id = $product->fk_product;
1888
1889 $productsupplier->update_buyprice($product->qty, $product->total_ht, $user, 'HT', $this->thirdparty, 0, $ref_fourn, $product->tva_tx, 0, 0, 0, $product->info_bits, 0, '', array(), '', $product->multicurrency_total_ht, 'HT', $multicurrency_tx, $product->multicurrency_code, '', '', 0);
1890 }
1891
1892 return 1;
1893 }
1894
1903 public function updatePriceFournisseur($idProductFournPrice, $product, $user)
1904 {
1905 $price = price2num($product->subprice * $product->qty, 'MU');
1906 $unitPrice = price2num($product->subprice, 'MU');
1907
1908 $sql = 'UPDATE '.MAIN_DB_PREFIX.'product_fournisseur_price SET '.(!empty($product->ref_fourn) ? 'ref_fourn = "'.$this->db->escape($product->ref_fourn).'", ' : '').' price ='.((float) $price).', unitprice ='.((float) $unitPrice).' WHERE rowid = '.((int) $idProductFournPrice);
1909
1910 $resql = $this->db->query($sql);
1911 if (!$resql) {
1912 $this->error = $this->db->error();
1913 $this->db->rollback();
1914 return -1;
1915 }
1916 return 1;
1917 }
1918
1926 public function createPriceFournisseur($product, $user)
1927 {
1928 global $conf;
1929
1930 $price = price2num($product->subprice * $product->qty, 'MU');
1931 $qty = price2num($product->qty);
1932 $unitPrice = price2num($product->subprice, 'MU');
1933
1934 $now = dol_now();
1935
1936 $values = array(
1937 "'".$this->db->idate($now)."'",
1938 (int) $product->fk_product,
1939 (int) $this->thirdparty->id,
1940 "'".$this->db->escape($product->ref_fourn)."'",
1941 (float) $price,
1942 (float) $qty,
1943 (float) $unitPrice,
1944 (float) $product->tva_tx,
1945 (int) $user->id
1946 );
1947 if (isModEnabled("multicurrency")) {
1948 if (!empty($product->multicurrency_code)) {
1949 include_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
1950 $multicurrency = new MultiCurrency($this->db); //need to fetch because empty fk_multicurrency and rate
1951 $multicurrency->fetch(0, $product->multicurrency_code);
1952 if (!empty($multicurrency->id)) {
1953 $values[] = (int) $multicurrency->id;
1954 $values[] = "'".$this->db->escape($product->multicurrency_code)."'";
1955 $values[] = (float) $product->multicurrency_subprice;
1956 $values[] = (float) $product->multicurrency_total_ht;
1957 $values[] = (float) $multicurrency->rate->rate;
1958 } else {
1959 for ($i = 0; $i < 5; $i++) {
1960 $values[] = 'NULL';
1961 }
1962 }
1963 }
1964 }
1965
1966 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'product_fournisseur_price ';
1967 $sql .= '(datec, fk_product, fk_soc, ref_fourn, price, quantity, unitprice, tva_tx, fk_user';
1968 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1969 $sql .= ',fk_multicurrency, multicurrency_code, multicurrency_unitprice, multicurrency_price, multicurrency_tx';
1970 }
1971 $sql .= ') VALUES ('.implode(',', $values).')';
1972
1973 $resql = $this->db->query($sql);
1974 if (!$resql) {
1975 $this->error = $this->db->error();
1976 $this->db->rollback();
1977 return -1;
1978 }
1979 return 1;
1980 }
1981
1982 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1989 public function setDraft($user)
1990 {
1991 // phpcs:enable
1992 global $conf, $langs;
1993
1994 $error = 0;
1995
1996 if ($this->status == self::STATUS_DRAFT) {
1997 dol_syslog(get_class($this)."::setDraft already draft status", LOG_WARNING);
1998 return 0;
1999 }
2000
2001 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
2002 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
2003 $sql .= " WHERE rowid = ".((int) $this->id);
2004
2005 if ($this->db->query($sql)) {
2006 if (!$error) {
2007 $this->oldcopy = clone $this;
2008 }
2009
2010 if (!$error) {
2011 // Call trigger
2012 $result = $this->call_trigger('PROPOSAL_SUPPLIER_UNVALIDATE', $user);
2013 if ($result < 0) {
2014 $error++;
2015 }
2016 }
2017
2018 if (!$error) {
2019 $this->status = self::STATUS_DRAFT;
2020 $this->statut = self::STATUS_DRAFT; // deprecated
2021 $this->db->commit();
2022 return 1;
2023 } else {
2024 $this->db->rollback();
2025 return -1;
2026 }
2027 } else {
2028 return -1;
2029 }
2030 }
2031
2032
2033 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2047 public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datec', $sortorder = 'DESC')
2048 {
2049 // phpcs:enable
2050 global $user;
2051
2052 $ga = array();
2053
2054 $search_sale = 0;
2055 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2056 $search_sale = $user->id;
2057 }
2058
2059 $sql = "SELECT s.rowid, s.nom as name, s.client,";
2060 $sql .= " p.rowid as supplier_proposalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
2061 $sql .= " p.datep as dp, p.fin_validite as datelimite";
2062 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."supplier_proposal as p, ".MAIN_DB_PREFIX."c_propalst as c";
2063 $sql .= " WHERE p.entity IN (".getEntity('supplier_proposal').")";
2064 $sql .= " AND p.fk_soc = s.rowid";
2065 $sql .= " AND p.fk_statut = c.id";
2066 if ($socid) {
2067 $sql .= " AND s.rowid = ".((int) $socid);
2068 }
2069 if ($draft) {
2070 $sql .= " AND p.fk_statut = 0";
2071 }
2072 if ($notcurrentuser > 0) {
2073 $sql .= " AND p.fk_user_author <> ".((int) $user->id);
2074 }
2075 // Search on sale representative
2076 if ($search_sale && $search_sale != '-1') {
2077 if ($search_sale == -2) {
2078 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
2079 } elseif ($search_sale > 0) {
2080 $sql .= " AND EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc AND sc.fk_user = ".((int) $search_sale).")";
2081 }
2082 }
2083 $sql .= $this->db->order($sortfield, $sortorder);
2084 $sql .= $this->db->plimit($limit, $offset);
2085
2086 $result = $this->db->query($sql);
2087 if ($result) {
2088 $num = $this->db->num_rows($result);
2089 if ($num) {
2090 $i = 0;
2091 while ($i < $num) {
2092 $obj = $this->db->fetch_object($result);
2093
2094 if ($shortlist == 1) {
2095 $ga[$obj->supplier_proposalid] = $obj->ref;
2096 } elseif ($shortlist == 2) {
2097 $ga[$obj->supplier_proposalid] = $obj->ref.' ('.$obj->name.')';
2098 } else {
2099 $ga[$i]['id'] = $obj->supplier_proposalid;
2100 $ga[$i]['ref'] = $obj->ref;
2101 $ga[$i]['name'] = $obj->name;
2102 }
2103
2104 $i++;
2105 }
2106 }
2107 return $ga;
2108 } else {
2109 dol_print_error($this->db);
2110 return -1;
2111 }
2112 }
2113
2121 public function delete($user, $notrigger = 0)
2122 {
2123 global $conf, $langs;
2124 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2125
2126 $error = 0;
2127
2128 $this->db->begin();
2129
2130 if (!$notrigger) {
2131 // Call trigger
2132 $result = $this->call_trigger('PROPOSAL_SUPPLIER_DELETE', $user);
2133 if ($result < 0) {
2134 $error++;
2135 }
2136 // End call triggers
2137 }
2138
2139 // Remove linked categories.
2140 if (!$error) {
2141 $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_supplier_proposal";
2142 $sql .= " WHERE fk_supplier_proposal = ".((int) $this->id);
2143
2144 $result = $this->db->query($sql);
2145 if (!$result) {
2146 $error++;
2147 $this->error = $this->db->lasterror();
2148 $this->errors[] = $this->error;
2149 }
2150 }
2151
2152 if (!$error) {
2153 $main = MAIN_DB_PREFIX.'supplier_proposaldet';
2154 $ef = $main."_extrafields";
2155 $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_supplier_proposal = ".((int) $this->id).")";
2156 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet WHERE fk_supplier_proposal = ".((int) $this->id);
2157 if ($this->db->query($sql)) {
2158 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposal WHERE rowid = ".((int) $this->id);
2159 if ($this->db->query($sqlef) && $this->db->query($sql)) {
2160 // Delete linked object
2161 $res = $this->deleteObjectLinked();
2162 if ($res < 0) {
2163 $error++;
2164 }
2165
2166 if (!$error) {
2167 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2168 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
2169 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
2170
2171 // We remove directory
2172 $ref = dol_sanitizeFileName($this->ref);
2173 if ($conf->supplier_proposal->dir_output && !empty($this->ref)) {
2174 $dir = $conf->supplier_proposal->dir_output."/".$ref;
2175 $file = $dir."/".$ref.".pdf";
2176 if (file_exists($file)) {
2177 dol_delete_preview($this);
2178
2179 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2180 $this->error = 'ErrorFailToDeleteFile';
2181 $this->errors = array('ErrorFailToDeleteFile');
2182 $this->db->rollback();
2183 return 0;
2184 }
2185 }
2186 if (file_exists($dir)) {
2187 $res = @dol_delete_dir_recursive($dir);
2188 if (!$res) {
2189 $this->error = 'ErrorFailToDeleteDir';
2190 $this->errors = array('ErrorFailToDeleteDir');
2191 $this->db->rollback();
2192 return 0;
2193 }
2194 }
2195 }
2196 }
2197
2198 // Removed extrafields
2199 if (!$error) {
2200 $result = $this->deleteExtraFields();
2201 if ($result < 0) {
2202 $error++;
2203 $errorflag = -4;
2204 dol_syslog(get_class($this)."::delete erreur ".$errorflag." ".$this->error, LOG_ERR);
2205 }
2206 }
2207
2208 if (!$error) {
2209 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
2210 $this->db->commit();
2211 return 1;
2212 } else {
2213 $this->error = $this->db->lasterror();
2214 $this->db->rollback();
2215 return 0;
2216 }
2217 } else {
2218 $this->error = $this->db->lasterror();
2219 $this->db->rollback();
2220 return -3;
2221 }
2222 } else {
2223 $this->error = $this->db->lasterror();
2224 $this->db->rollback();
2225 return -2;
2226 }
2227 } else {
2228 $this->db->rollback();
2229 return -1;
2230 }
2231 }
2232
2239 public function info($id)
2240 {
2241 $sql = "SELECT c.rowid, GREATEST(c.tms, cef.tms) as date_modification,";
2242 $sql .= " c.datec as date_creation, c.date_valid as date_validation, c.date_cloture as date_closure,";
2243 $sql .= " c.fk_user_author, c.fk_user_modif, c.fk_user_valid, c.fk_user_cloture";
2244 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as c";
2245 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."supplier_proposal_extrafields as cef ON cef.fk_object=c.rowid";
2246 $sql .= " WHERE c.rowid = ".((int) $id);
2247
2248 $result = $this->db->query($sql);
2249
2250 if ($result) {
2251 if ($this->db->num_rows($result)) {
2252 $obj = $this->db->fetch_object($result);
2253
2254 $this->id = $obj->rowid;
2255
2256 $this->date_creation = $this->db->jdate($obj->date_creation);
2257 $this->date_modification = empty($obj->date_modification) ? '' : $this->db->jdate($obj->date_modification);
2258 $this->date_validation = $this->db->jdate($obj->date_validation);
2259 $this->date_cloture = $this->db->jdate($obj->date_closure);
2260
2261 $this->user_creation_id = $obj->fk_user_author;
2262 $this->user_modification_id = $obj->fk_user_modif;
2263 $this->user_validation_id = $obj->fk_user_valid;
2264 $this->user_closing_id = $obj->fk_user_cloture;
2265 }
2266 $this->db->free($result);
2267 } else {
2268 dol_print_error($this->db);
2269 }
2270 }
2271
2272
2279 public function getLibStatut($mode = 0)
2280 {
2281 return $this->LibStatut((isset($this->status) ? $this->status : $this->statut), $mode);
2282 }
2283
2284 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2292 public function LibStatut($status, $mode = 1)
2293 {
2294 // phpcs:enable
2295
2296 // Init/load array of translation of status
2297 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
2298 global $langs;
2299 $langs->load("supplier_proposal");
2300 $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraft");
2301 $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidated");
2302 $this->labelStatus[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSigned");
2303 $this->labelStatus[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSigned");
2304 $this->labelStatus[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosed");
2305 $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraftShort");
2306 $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidatedShort");
2307 $this->labelStatusShort[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSignedShort");
2308 $this->labelStatusShort[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSignedShort");
2309 $this->labelStatusShort[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosedShort");
2310 }
2311
2312 $statusnew = '';
2313 if ($status == self::STATUS_DRAFT) {
2314 $statusnew = 'status0';
2315 } elseif ($status == self::STATUS_VALIDATED) {
2316 $statusnew = 'status1';
2317 } elseif ($status == self::STATUS_SIGNED) {
2318 $statusnew = 'status4';
2319 } elseif ($status == self::STATUS_NOTSIGNED) {
2320 $statusnew = 'status9';
2321 } elseif ($status == self::STATUS_CLOSE) {
2322 $statusnew = 'status6';
2323 }
2324
2325 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusnew, $mode);
2326 }
2327
2328
2329 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2337 public function load_board($user, $mode)
2338 {
2339 // phpcs:enable
2340 global $conf, $langs;
2341
2342 $now = dol_now();
2343
2344 $clause = " WHERE";
2345
2346 $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.date_cloture as datefin";
2347 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2348 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2349 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
2350 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2351 $clause = " AND";
2352 }
2353 $sql .= $clause." p.entity IN (".getEntity('supplier_proposal').")";
2354 if ($mode == 'opened') {
2355 $sql .= " AND p.fk_statut = 1";
2356 }
2357 if ($mode == 'signed') {
2358 $sql .= " AND p.fk_statut = 2";
2359 }
2360 if ($user->socid) {
2361 $sql .= " AND p.fk_soc = ".((int) $user->socid);
2362 }
2363
2364 $resql = $this->db->query($sql);
2365 if ($resql) {
2366 $label = $labelShort = '';
2367 $status = '';
2368 $delay_warning = 0;
2369 if ($mode == 'opened') {
2370 $delay_warning = !empty($conf->supplier_proposal->cloture->warning_delay) ? $conf->supplier_proposal->cloture->warning_delay : 0;
2371 $status = self::STATUS_VALIDATED;
2372 $label = $langs->trans("SupplierProposalsToClose");
2373 $labelShort = $langs->trans("ToAcceptRefuse");
2374 }
2375 if ($mode == 'signed') {
2376 $delay_warning = !empty($conf->supplier_proposal->facturation->warning_delay) ? $conf->supplier_proposal->facturation->warning_delay : 0;
2377 $status = self::STATUS_SIGNED;
2378 $label = $langs->trans("SupplierProposalsToProcess"); // May be billed or ordered
2379 $labelShort = $langs->trans("ToClose");
2380 }
2381
2382 $response = new WorkboardResponse();
2383 $response->warning_delay = $delay_warning / 60 / 60 / 24;
2384 $response->label = $label;
2385 $response->labelShort = $labelShort;
2386 $response->url = DOL_URL_ROOT.'/supplier_proposal/list.php?search_status='.$status;
2387 $response->img = img_object('', "propal");
2388
2389 // This assignment in condition is not a bug. It allows walking the results.
2390 while ($obj = $this->db->fetch_object($resql)) {
2391 $response->nbtodo++;
2392 if ($mode == 'opened') {
2393 $datelimit = $this->db->jdate($obj->datefin);
2394 if ($datelimit < ($now - $delay_warning)) {
2395 $response->nbtodolate++;
2396 }
2397 }
2398 // TODO Definir regle des propales a facturer en retard
2399 // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
2400 }
2401 return $response;
2402 } else {
2403 $this->error = $this->db->lasterror();
2404 return -1;
2405 }
2406 }
2407
2408
2417 public function initAsSpecimen($param = array())
2418 {
2419 global $conf, $langs;
2420
2421 // Load array of products prodids
2422 $num_prods = 0;
2423 $prodids = array();
2424 $sql = "SELECT rowid";
2425 $sql .= " FROM ".MAIN_DB_PREFIX."product";
2426 $sql .= " WHERE entity IN (".getEntity('product').")";
2427 if (array_key_exists('tobuy', $param)) {
2428 $sql .= " AND tobuy = ".((int) $param['tobuy']);
2429 }
2430 $sql .= $this->db->plimit(100);
2431
2432 $resql = $this->db->query($sql);
2433 if ($resql) {
2434 $num_prods = $this->db->num_rows($resql);
2435 $i = 0;
2436 while ($i < $num_prods) {
2437 $i++;
2438 $row = $this->db->fetch_row($resql);
2439 $prodids[$i] = $row[0];
2440 }
2441 }
2442
2443 // Initialise parameters
2444 $this->id = 0;
2445 $this->ref = 'SPECIMEN';
2446 $this->ref_supplier = 'NEMICEPS';
2447 $this->specimen = 1;
2448 $this->socid = 1;
2449 $this->date = time();
2450 $this->cond_reglement_id = 1;
2451 $this->cond_reglement_code = 'RECEP';
2452 $this->mode_reglement_id = 7;
2453 $this->mode_reglement_code = 'CHQ';
2454 $this->note_public = 'This is a comment (public)';
2455 $this->note_private = 'This is a comment (private)';
2456
2457 $this->multicurrency_tx = 1;
2458 $this->multicurrency_code = getDolCurrency();
2459
2460 // Lines
2461 $nbp = min(1000, GETPOSTINT('nblines') ? GETPOSTINT('nblines') : 5); // We can force the nb of lines to test from command line (but not more than 1000)
2462 $xnbp = 0;
2463 while ($xnbp < $nbp) {
2464 $line = new SupplierProposalLine($this->db);
2465 $line->desc = $langs->trans("Description")." ".$xnbp;
2466 $line->qty = 1;
2467 $line->subprice = 100;
2468 $line->tva_tx = 19.6;
2469 $line->localtax1_tx = 0;
2470 $line->localtax2_tx = 0;
2471 if ($xnbp == 2) {
2472 $line->total_ht = 50;
2473 $line->total_ttc = 59.8;
2474 $line->total_tva = 9.8;
2475 $line->remise_percent = 50;
2476 } else {
2477 $line->total_ht = 100;
2478 $line->total_ttc = 119.6;
2479 $line->total_tva = 19.6;
2480 $line->remise_percent = 00;
2481 }
2482
2483 if ($num_prods > 0) {
2484 $prodid = mt_rand(1, $num_prods);
2485 $line->fk_product = $prodids[$prodid];
2486 }
2487
2488 $this->lines[$xnbp] = $line;
2489
2490 $this->total_ht += $line->total_ht;
2491 $this->total_tva += $line->total_tva;
2492 $this->total_ttc += $line->total_ttc;
2493
2494 $xnbp++;
2495 }
2496
2497 return 1;
2498 }
2499
2505 public function loadStateBoard()
2506 {
2507 global $user;
2508
2509 $this->nb = array();
2510 $clause = "WHERE";
2511
2512 $sql = "SELECT count(p.rowid) as nb";
2513 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2514 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
2515 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2516 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2517 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2518 $clause = "AND";
2519 }
2520 $sql .= " ".$clause." p.entity IN (".getEntity('supplier_proposal').")";
2521
2522 $resql = $this->db->query($sql);
2523 if ($resql) {
2524 // This assignment in condition is not a bug. It allows walking the results.
2525 while ($obj = $this->db->fetch_object($resql)) {
2526 $this->nb["supplier_proposals"] = $obj->nb;
2527 }
2528 $this->db->free($resql);
2529 return 1;
2530 } else {
2531 dol_print_error($this->db);
2532 $this->error = $this->db->lasterror();
2533 return -1;
2534 }
2535 }
2536
2537
2545 public function getNextNumRef($soc)
2546 {
2547 global $conf, $db, $langs;
2548 $langs->load("supplier_proposal");
2549
2550 if (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON')) {
2551 $mybool = false;
2552
2553 $file = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') . ".php";
2554 $classname = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
2555
2556 // Include file with class
2557 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2558 foreach ($dirmodels as $reldir) {
2559 $dir = dol_buildpath($reldir."core/modules/supplier_proposal/");
2560
2561 // Load file with numbering class (if found)
2562 $mybool = ((bool) @include_once $dir.$file) || $mybool;
2563 }
2564
2565 if (!$mybool) {
2566 dol_print_error(null, "Failed to include file ".$file);
2567 return '';
2568 }
2569
2570 $obj = new $classname();
2571 '@phan-var-force ModeleNumRefSupplierProposal $obj';
2573 $numref = "";
2574 $numref = $obj->getNextValue($soc, $this);
2575
2576 if ($numref != "") {
2577 return $numref;
2578 } else {
2579 $this->error = $obj->error;
2580 return "";
2581 }
2582 } else {
2583 $langs->load("errors");
2584 print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("SupplierProposal"));
2585 return "";
2586 }
2587 }
2588
2595 public function getTooltipContentArray($params)
2596 {
2597 global $conf, $langs;
2598
2599 $langs->load('supplier_proposal');
2600
2601 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2602 return ['optimize' => $langs->trans("ShowSupplierProposal")];
2603 }
2604
2605 //$option = $params['option'] ?? '';
2606 $datas = [];
2607
2608 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("SupplierProposal").'</u>';
2609 if (isset($this->status)) {
2610 $datas['picto'] .= ' '.$this->getLibStatut(5);
2611 }
2612 if (!empty($this->ref)) {
2613 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2614 }
2615 if (!empty($this->ref_fourn)) {
2616 $datas['ref_supplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_fourn;
2617 }
2618 if (!empty($this->total_ht)) {
2619 $datas['amount_ht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, getDolCurrency());
2620 }
2621 if (!empty($this->total_tva)) {
2622 $datas['amount_vat'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, getDolCurrency());
2623 }
2624 if (!empty($this->total_ttc)) {
2625 $datas['amount_ttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, getDolCurrency());
2626 }
2627
2628 return $datas;
2629 }
2630
2642 public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2643 {
2644 global $langs, $conf, $user, $hookmanager;
2645
2646 if (!empty($conf->dol_no_mouse_hover)) {
2647 $notooltip = 1; // Force disable tooltips
2648 }
2649
2650 $url = '';
2651 $result = '';
2652 $params = [
2653 'id' => $this->id,
2654 'objecttype' => $this->element,
2655 'option' => $option,
2656 ];
2657 $classfortooltip = 'classfortooltip';
2658 $dataparams = '';
2659 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2660 $classfortooltip = 'classforajaxtooltip';
2661 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
2662 $label = '';
2663 } else {
2664 $label = implode($this->getTooltipContentArray($params));
2665 }
2666
2667 if ($option == '') {
2668 $url = DOL_URL_ROOT.'/supplier_proposal/card.php?id='.$this->id.$get_params;
2669 }
2670 if ($option == 'document') {
2671 $url = DOL_URL_ROOT.'/supplier_proposal/document.php?id='.$this->id.$get_params;
2672 }
2673
2674 if ($option !== 'nolink') {
2675 // Add param to save lastsearch_values or not
2676 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2677 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2678 $add_save_lastsearch_values = 1;
2679 }
2680 if ($add_save_lastsearch_values) {
2681 $url .= '&save_lastsearch_values=1';
2682 }
2683 }
2684
2685 $linkclose = '';
2686 if (empty($notooltip) && $user->hasRight('propal', 'lire')) {
2687 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2688 $label = $langs->trans("ShowSupplierProposal");
2689 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
2690 }
2691 $linkclose .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
2692 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
2693 }
2694
2695 $linkstart = '<a href="'.$url.'"';
2696 $linkstart .= $linkclose.'>';
2697 $linkend = '</a>';
2698
2699 $result .= $linkstart;
2700 if ($withpicto) {
2701 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
2702 }
2703 if ($withpicto != 2) {
2704 $result .= $this->ref;
2705 }
2706 $result .= $linkend;
2707
2708 if ($addlinktonotes) {
2709 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2710 if ($txttoshow) {
2711 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2712 $result .= ' <span class="note inline-block">';
2713 $result .= '<a href="'.DOL_URL_ROOT.'/supplier_proposal/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2714 $result .= img_picto('', 'note');
2715 $result .= '</a>';
2716 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
2717 //$result.='</a>';
2718 $result .= '</span>';
2719 }
2720 }
2721 global $action;
2722 $hookmanager->initHooks(array($this->element . 'dao'));
2723 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
2724 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2725 if ($reshook > 0) {
2726 $result = $hookmanager->resPrint;
2727 } else {
2728 $result .= $hookmanager->resPrint;
2729 }
2730 return $result;
2731 }
2732
2738 public function getLinesArray()
2739 {
2740 // For other object, here we call fetch_lines. But fetch_lines does not exists on supplier proposal
2741
2742 $sql = 'SELECT pt.rowid, pt.label as custom_label, pt.description, pt.fk_product, pt.fk_remise_except,';
2743 $sql .= ' pt.qty, pt.tva_tx, pt.vat_src_code, pt.remise_percent, pt.subprice, pt.info_bits,';
2744 $sql .= ' pt.total_ht, pt.total_tva, pt.total_ttc, pt.fk_product_fournisseur_price as fk_fournprice, pt.buy_price_ht as pa_ht, pt.special_code, pt.localtax1_tx, pt.localtax2_tx,';
2745 $sql .= ' pt.product_type, pt.rang, pt.fk_parent_line, pt.extraparams,';
2746 $sql .= ' p.label as product_label, p.ref, p.fk_product_type, p.rowid as prodid,';
2747 $sql .= ' p.description as product_desc, pt.ref_fourn as ref_supplier,';
2748 $sql .= ' pt.fk_multicurrency, pt.multicurrency_code, pt.multicurrency_subprice, pt.multicurrency_total_ht, pt.multicurrency_total_tva, pt.multicurrency_total_ttc, pt.fk_unit';
2749 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pt';
2750 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pt.fk_product=p.rowid';
2751 $sql .= ' WHERE pt.fk_supplier_proposal = '.((int) $this->id);
2752 $sql .= ' ORDER BY pt.rang ASC, pt.rowid';
2753
2754 dol_syslog(get_class($this).'::getLinesArray', LOG_DEBUG);
2755 $resql = $this->db->query($sql);
2756 if ($resql) {
2757 $num = $this->db->num_rows($resql);
2758 $i = 0;
2759
2760 while ($i < $num) {
2761 $obj = $this->db->fetch_object($resql);
2762
2763 $this->lines[$i] = new SupplierProposalLine($this->db);
2764 $this->lines[$i]->id = $obj->rowid; // for backward compatibility
2765 $this->lines[$i]->rowid = $obj->rowid;
2766
2767 $this->lines[$i]->label = $obj->custom_label;
2768 $this->lines[$i]->description = $obj->description; // deprecated
2769 $this->lines[$i]->desc = $obj->description;
2770
2771 $this->lines[$i]->fk_product = $obj->fk_product;
2772 $this->lines[$i]->ref = $obj->ref;
2773 $this->lines[$i]->product_label = $obj->product_label;
2774 $this->lines[$i]->product_desc = $obj->product_desc;
2775 $this->lines[$i]->fk_product_type = $obj->fk_product_type; // deprecated
2776 $this->lines[$i]->product_type = $obj->product_type;
2777 $this->lines[$i]->qty = $obj->qty;
2778 $this->lines[$i]->subprice = $obj->subprice;
2779 $this->lines[$i]->fk_remise_except = $obj->fk_remise_except;
2780 $this->lines[$i]->remise_percent = $obj->remise_percent;
2781 $this->lines[$i]->tva_tx = $obj->tva_tx;
2782 $this->lines[$i]->vat_src_code = $obj->vat_src_code;
2783 $this->lines[$i]->info_bits = $obj->info_bits;
2784 $this->lines[$i]->total_ht = $obj->total_ht;
2785 $this->lines[$i]->total_tva = $obj->total_tva;
2786 $this->lines[$i]->total_ttc = $obj->total_ttc;
2787 $this->lines[$i]->fk_fournprice = $obj->fk_fournprice;
2788 $marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->lines[$i]->fk_fournprice, $obj->pa_ht);
2789 $this->lines[$i]->pa_ht = $marginInfos[0];
2790 $this->lines[$i]->marge_tx = $marginInfos[1];
2791 $this->lines[$i]->marque_tx = $marginInfos[2];
2792 $this->lines[$i]->fk_parent_line = $obj->fk_parent_line;
2793 $this->lines[$i]->special_code = $obj->special_code;
2794 $this->lines[$i]->rang = $obj->rang;
2795
2796 $this->lines[$i]->ref_fourn = $obj->ref_supplier; // deprecated
2797 $this->lines[$i]->ref_supplier = $obj->ref_supplier;
2798
2799 // Multicurrency
2800 $this->lines[$i]->fk_multicurrency = $obj->fk_multicurrency;
2801 $this->lines[$i]->multicurrency_code = $obj->multicurrency_code;
2802 $this->lines[$i]->multicurrency_subprice = $obj->multicurrency_subprice;
2803 $this->lines[$i]->multicurrency_total_ht = $obj->multicurrency_total_ht;
2804 $this->lines[$i]->multicurrency_total_tva = $obj->multicurrency_total_tva;
2805 $this->lines[$i]->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
2806 $this->lines[$i]->fk_unit = $obj->fk_unit;
2807 $this->lines[$i]->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
2808
2809 $this->lines[$i]->fetch_optionals();
2810
2811 $i++;
2812 }
2813 $this->db->free($resql);
2814
2815 return 1;
2816 } else {
2817 $this->error = $this->db->error();
2818 return -1;
2819 }
2820 }
2821
2833 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2834 {
2835 global $langs;
2836
2837 $langs->load("supplier_proposal");
2838 $outputlangs->load("products");
2839
2840 if (!dol_strlen($modele)) {
2841 $modele = ''; // On supplier documents, template can be empty( no doc generated in this case)
2842
2843 if ($this->model_pdf) {
2844 $modele = $this->model_pdf;
2845 } elseif (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF')) {
2846 $modele = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF');
2847 }
2848 }
2849
2850 if (empty($modele)) {
2851 return 1;
2852 }
2853
2854 $modelpath = "core/modules/supplier_proposal/doc/";
2855
2856 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2857 }
2858
2859
2868 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2869 {
2870 $tables = array(
2871 'supplier_proposal'
2872 );
2873
2874 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2875 }
2876
2885 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
2886 {
2887 $tables = array(
2888 'supplier_proposaldet'
2889 );
2890
2891 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
2892 }
2893
2894
2902 public function getKanbanView($option = '', $arraydata = null)
2903 {
2904 global $langs;
2905
2906 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2907
2908 $return = '<div class="box-flex-item box-flex-grow-zero">';
2909 $return .= '<div class="info-box info-box-sm">';
2910 $return .= '<span class="info-box-icon bg-infobox-action">';
2911 $return .= img_picto('', $this->picto);
2912 //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2913 $return .= '</span>';
2914 $return .= '<div class="info-box-content">';
2915 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
2916 if ($selected >= 0) {
2917 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2918 }
2919 if (property_exists($this, 'socid')) {
2920 $return .= '<span class="info-box-ref"> | '.$this->socid.'</span>';
2921 }
2922 if (property_exists($this, 'delivery_date')) {
2923 $return .= '<br><span class="opacitymedium">'.$langs->trans("DateEnd").'</span> : <span class="info-box-label">'.dol_print_date($this->delivery_date).'</span>';
2924 }
2925 if (property_exists($this, 'total_ttc')) {
2926 $return .= '<br><span class="opacitymedium" >'.$langs->trans("AmountHT").' : </span><span class="info-box-label amount">'.price($this->total_ttc).'</span>';
2927 }
2928 if (method_exists($this, 'getLibStatut')) {
2929 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
2930 }
2931 $return .= '</div>';
2932 $return .= '</div>';
2933 $return .= '</div>';
2934 return $return;
2935 }
2936
2947 public function setCategories($categories)
2948 {
2949 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2950 return parent::setCategoriesCommon($categories, Categorie::TYPE_SUPPLIER_PROPOSAL);
2951 }
2952}
2953
2954
2959{
2963 public $db;
2964
2968 public $error = '';
2969
2973 public $element = 'supplier_proposaldet';
2974
2978 public $table_element = 'supplier_proposaldet';
2979
2983 public $parent_element = 'supplier_proposal';
2984
2988 public $fk_parent_attribute = 'fk_supplier_proposal';
2989
2993 public $oldline;
2994
2998 public $id;
2999
3003 public $fk_supplier_proposal;
3004
3008 public $fk_parent_line;
3009
3013 public $desc; // Description ligne
3014
3018 public $fk_product; // Id produit predefini
3019
3025 public $fk_product_type;
3031 public $product_type = Product::TYPE_PRODUCT;
3032
3036 public $qty;
3040 public $tva_tx;
3044 public $vat_src_code;
3045
3049 public $remise_percent;
3050
3054 public $fk_remise_except;
3055
3059 public $rang = 0;
3060
3064 public $fk_fournprice;
3065
3069 public $pa_ht;
3073 public $marge_tx;
3077 public $marque_tx;
3078
3082 public $special_code; // Tag for special lines (exclusive tags)
3083 // 1: frais de port
3084 // 2: ecotaxe
3085 // 3: option line (when qty = 0)
3086
3090 public $info_bits = 0; // Liste d'options cumulables:
3091 // Bit 0: 0 si TVA normal - 1 if TVA NPR
3092 // Bit 1: 0 ligne normal - 1 if fixed reduction
3093
3097 public $total_ht; // Total HT de la ligne toute quantite et incluant la remise ligne
3101 public $total_tva; // Total TVA de la ligne toute quantite et incluant la remise ligne
3105 public $total_ttc; // Total TTC de la ligne toute quantite et incluant la remise ligne
3106
3110 public $date_start;
3114 public $date_end;
3115
3116 // From llx_product
3122 public $ref;
3123
3128 public $product_ref;
3129
3135 public $libelle;
3136
3141 public $product_label;
3142
3147 public $label;
3148
3153 public $product_desc;
3154
3158 public $localtax1_tx; // Local tax 1
3162 public $localtax2_tx; // Local tax 2
3166 public $localtax1_type; // Local tax 1 type
3170 public $localtax2_type; // Local tax 2 type
3174 public $total_localtax1; // Line total local tax 1
3178 public $total_localtax2; // Line total local tax 2
3179
3183 public $skip_update_total; // Skip update price total for special lines
3184
3188 public $ref_fourn;
3192 public $ref_supplier;
3193
3194 // Multicurrency
3198 public $fk_multicurrency;
3199
3203 public $multicurrency_code;
3207 public $multicurrency_subprice;
3211 public $multicurrency_total_ht;
3215 public $multicurrency_total_tva;
3219 public $multicurrency_total_ttc;
3220
3226 public function __construct($db)
3227 {
3228 $this->db = $db;
3229 }
3230
3237 public function fetch($rowid)
3238 {
3239 $sql = 'SELECT pd.rowid, pd.fk_supplier_proposal, pd.fk_parent_line, pd.fk_product, pd.label as custom_label, pd.description, pd.price, pd.qty, pd.tva_tx,';
3240 $sql .= ' pd.date_start, pd.date_end,';
3241 $sql .= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice, pd.subprice_ttc,';
3242 $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,';
3243 $sql .= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
3244 $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
3245 $sql .= ' pd.product_type, pd.ref_fourn as ref_produit_fourn, pd.extraparams,';
3246 $sql .= ' pd.fk_multicurrency, pd.multicurrency_code, pd.multicurrency_subprice, pd.multicurrency_subprice_ttc, pd.multicurrency_total_ht, pd.multicurrency_total_tva, pd.multicurrency_total_ttc, pd.fk_unit';
3247 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pd';
3248 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
3249 $sql .= ' WHERE pd.rowid = '.((int) $rowid);
3250
3251 $result = $this->db->query($sql);
3252 if ($result) {
3253 if ($objp = $this->db->fetch_object($result)) {
3254 $this->id = $objp->rowid;
3255 $this->fk_supplier_proposal = $objp->fk_supplier_proposal;
3256 $this->fk_parent_line = $objp->fk_parent_line;
3257 $this->label = $objp->custom_label;
3258 $this->desc = $objp->description;
3259 $this->qty = $objp->qty;
3260
3261 $this->subprice = $objp->subprice;
3262 $this->subprice_ttc = $objp->subprice_ttc;
3263
3264 $this->tva_tx = $objp->tva_tx;
3265 $this->remise_percent = $objp->remise_percent;
3266 $this->fk_remise_except = $objp->fk_remise_except;
3267 $this->fk_product = $objp->fk_product;
3268 $this->info_bits = $objp->info_bits;
3269 $this->date_start = $this->db->jdate($objp->date_start);
3270 $this->date_end = $this->db->jdate($objp->date_end);
3271
3272 $this->total_ht = $objp->total_ht;
3273 $this->total_tva = $objp->total_tva;
3274 $this->total_ttc = $objp->total_ttc;
3275
3276 $this->fk_fournprice = $objp->fk_fournprice;
3277
3278 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
3279 $this->pa_ht = $marginInfos[0];
3280 $this->marge_tx = $marginInfos[1];
3281 $this->marque_tx = $marginInfos[2];
3282
3283 $this->special_code = $objp->special_code;
3284 $this->product_type = $objp->product_type;
3285 $this->rang = $objp->rang;
3286
3287 $this->product_ref = $objp->product_ref;
3288 $this->product_label = $objp->product_label;
3289 $this->product_desc = $objp->product_desc;
3290
3291 $this->ref_fourn = $objp->ref_produit_fourn;
3292
3293 $this->extraparams = !empty($objp->extraparams) ? (array) json_decode($objp->extraparams, true) : array();
3294
3295 // Multicurrency
3296 $this->fk_multicurrency = $objp->fk_multicurrency;
3297 $this->multicurrency_code = $objp->multicurrency_code;
3298 $this->multicurrency_subprice = $objp->multicurrency_subprice;
3299 $this->multicurrency_subprice_ttc = $objp->multicurrency_subprice_ttc;
3300 $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3301 $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3302 $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3303 $this->fk_unit = $objp->fk_unit;
3304
3305 $this->db->free($result);
3306 return 1;
3307 }
3308 return 0;
3309 } else {
3310 dol_print_error($this->db);
3311 return -1;
3312 }
3313 }
3314
3321 public function insert($notrigger = 0)
3322 {
3323 global $user;
3324
3325 $error = 0;
3326
3327 dol_syslog(get_class($this)."::insert rang=".$this->rang);
3328
3329 // Clean parameters
3330 if (empty($this->tva_tx)) {
3331 $this->tva_tx = 0;
3332 }
3333 if (empty($this->vat_src_code)) {
3334 $this->vat_src_code = '';
3335 }
3336 if (empty($this->localtax1_tx)) {
3337 $this->localtax1_tx = 0;
3338 }
3339 if (empty($this->localtax2_tx)) {
3340 $this->localtax2_tx = 0;
3341 }
3342 if (empty($this->localtax1_type)) {
3343 $this->localtax1_type = '';
3344 }
3345 if (empty($this->localtax2_type)) {
3346 $this->localtax2_type = '';
3347 }
3348 if (empty($this->total_localtax1)) {
3349 $this->total_localtax1 = 0;
3350 }
3351 if (empty($this->total_localtax2)) {
3352 $this->total_localtax2 = 0;
3353 }
3354 if (empty($this->rang)) {
3355 $this->rang = 0;
3356 }
3357 if (empty($this->remise_percent)) {
3358 $this->remise_percent = 0;
3359 }
3360 if (empty($this->info_bits)) {
3361 $this->info_bits = 0;
3362 }
3363 if (empty($this->special_code)) {
3364 $this->special_code = 0;
3365 }
3366 if (empty($this->fk_parent_line)) {
3367 $this->fk_parent_line = 0;
3368 }
3369 if (empty($this->fk_fournprice)) {
3370 $this->fk_fournprice = 0;
3371 }
3372 if (empty($this->fk_unit)) {
3373 $this->fk_unit = 0;
3374 }
3375 if (empty($this->subprice)) {
3376 $this->subprice = 0;
3377 }
3378
3379 if (empty($this->pa_ht)) {
3380 $this->pa_ht = 0;
3381 }
3382
3383 // if buy price not defined, define buyprice as configured in margin admin
3384 if ($this->pa_ht == 0) {
3385 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3386 if ($result < 0) {
3387 return $result;
3388 } else {
3389 $this->pa_ht = $result;
3390 }
3391 }
3392
3393 // Check parameters
3394 if ($this->product_type < 0) {
3395 return -1;
3396 }
3397
3398 $this->db->begin();
3399
3400 // Insert line into database
3401 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'supplier_proposaldet';
3402 $sql .= ' (fk_supplier_proposal, fk_parent_line, label, description, fk_product, product_type,';
3403 $sql .= ' date_start, date_end,';
3404 $sql .= ' fk_remise_except, qty, tva_tx, vat_src_code, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3405 $sql .= ' subprice, subprice_ttc, remise_percent, ';
3406 $sql .= ' info_bits, ';
3407 $sql .= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
3408 $sql .= ' ref_fourn,';
3409 $sql .= ' fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_subprice_ttc, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc, fk_unit)';
3410 $sql .= " VALUES (".$this->fk_supplier_proposal.",";
3411 $sql .= " ".($this->fk_parent_line > 0 ? ((int) $this->fk_parent_line) : "null").",";
3412 $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
3413 $sql .= " '".$this->db->escape($this->desc)."',";
3414 $sql .= " ".($this->fk_product ? ((int) $this->fk_product) : "null").",";
3415 $sql .= " ".((int) $this->product_type).",";
3416 $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3417 $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3418 $sql .= " ".($this->fk_remise_except ? ((int) $this->fk_remise_except) : "null").",";
3419 $sql .= " ".(float) price2num($this->qty, 'MS').",";
3420 $sql .= " ".(float) price2num($this->tva_tx).",";
3421 $sql .= " '".$this->db->escape($this->vat_src_code)."',";
3422 $sql .= " ".(float) price2num($this->localtax1_tx).",";
3423 $sql .= " ".(float) price2num($this->localtax2_tx).",";
3424 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3425 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3426 $sql .= " ".(float) price2num($this->subprice, 'MU') .",";
3427 $sql .= " ".(float) price2num($this->subprice_ttc, 'MU') .",";
3428 $sql .= " ".((float) $this->remise_percent).",";
3429 $sql .= " ".(isset($this->info_bits) ? ((int) $this->info_bits) : "null").",";
3430 $sql .= " ".(float) price2num($this->total_ht, 'MT').",";
3431 $sql .= " ".(float) price2num($this->total_tva, 'MT').",";
3432 $sql .= " ".(float) price2num($this->total_localtax1, 'MT').",";
3433 $sql .= " ".(float) price2num($this->total_localtax2, 'MT').",";
3434 $sql .= " ".(float) price2num($this->total_ttc, 'MT').",";
3435 $sql .= " ".(!empty($this->fk_fournprice) ? ((int) $this->fk_fournprice) : "null").",";
3436 $sql .= " ".(isset($this->pa_ht) ? (float) price2num($this->pa_ht, 'MU') : "null").",";
3437 $sql .= ' '.((int) $this->special_code).',';
3438 $sql .= ' '.((int) $this->rang).',';
3439 $sql .= " '".$this->db->escape($this->ref_fourn)."'";
3440 $sql .= ", ".($this->fk_multicurrency > 0 ? ((int) $this->fk_multicurrency) : 'null');
3441 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3442 $sql .= ", ".(float) price2num($this->multicurrency_subprice, 'CU');
3443 $sql .= ", ".(float) price2num($this->multicurrency_subprice_ttc, 'CU');
3444 $sql .= ", ".(float) price2num($this->multicurrency_total_ht, 'CT');
3445 $sql .= ", ".(float) price2num($this->multicurrency_total_tva, 'CT');
3446 $sql .= ", ".(float) price2num($this->multicurrency_total_ttc, 'CT');
3447 $sql .= ", ".($this->fk_unit ? ((int) $this->fk_unit) : 'null');
3448 $sql .= ')';
3449
3450 dol_syslog(get_class($this).'::insert', LOG_DEBUG);
3451 $resql = $this->db->query($sql);
3452 if ($resql) {
3453 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'supplier_proposaldet');
3454
3455 if (!$error) {
3456 $result = $this->insertExtraFields();
3457 if ($result < 0) {
3458 $error++;
3459 }
3460 }
3461
3462 if (!$error && !$notrigger) {
3463 // Call trigger
3464 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_INSERT', $user);
3465 if ($result < 0) {
3466 $this->db->rollback();
3467 return -1;
3468 }
3469 // End call triggers
3470 }
3471
3472 $this->db->commit();
3473 return 1;
3474 } else {
3475 $this->error = $this->db->error()." sql=".$sql;
3476 $this->db->rollback();
3477 return -1;
3478 }
3479 }
3480
3487 public function delete($user)
3488 {
3489 $error = 0;
3490
3491 $this->db->begin();
3492
3493 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet";
3494 $sql .= " WHERE rowid = ".((int) $this->id);
3495
3496 if ($this->db->query($sql)) {
3497 // Remove extrafields
3498 if (!$error) {
3499 $result = $this->deleteExtraFields();
3500 if ($result < 0) {
3501 $error++;
3502 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3503 }
3504 }
3505
3506 // Call trigger
3507 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_DELETE', $user);
3508 if ($result < 0) {
3509 $this->db->rollback();
3510 return -1;
3511 }
3512 // End call triggers
3513
3514 $this->db->commit();
3515
3516 return 1;
3517 } else {
3518 $this->error = $this->db->error()." sql=".$sql;
3519 $this->db->rollback();
3520 return -1;
3521 }
3522 }
3523
3530 public function update($notrigger = 0)
3531 {
3532 global $user;
3533
3534 $error = 0;
3535
3536 // Clean parameters
3537 if (empty($this->tva_tx)) {
3538 $this->tva_tx = 0;
3539 }
3540 if (empty($this->localtax1_tx)) {
3541 $this->localtax1_tx = 0;
3542 }
3543 if (empty($this->localtax2_tx)) {
3544 $this->localtax2_tx = 0;
3545 }
3546 if (empty($this->total_localtax1)) {
3547 $this->total_localtax1 = 0;
3548 }
3549 if (empty($this->total_localtax2)) {
3550 $this->total_localtax2 = 0;
3551 }
3552 if (empty($this->localtax1_type)) {
3553 $this->localtax1_type = '';
3554 }
3555 if (empty($this->localtax2_type)) {
3556 $this->localtax2_type = '';
3557 }
3558 if (empty($this->marque_tx)) {
3559 $this->marque_tx = 0;
3560 }
3561 if (empty($this->marge_tx)) {
3562 $this->marge_tx = 0;
3563 }
3564 if (empty($this->remise_percent)) {
3565 $this->remise_percent = 0;
3566 }
3567 if (empty($this->info_bits)) {
3568 $this->info_bits = 0;
3569 }
3570 if (empty($this->special_code)) {
3571 $this->special_code = 0;
3572 }
3573 if (empty($this->fk_parent_line)) {
3574 $this->fk_parent_line = 0;
3575 }
3576 if (empty($this->fk_fournprice)) {
3577 $this->fk_fournprice = 0;
3578 }
3579 if (empty($this->fk_unit)) {
3580 $this->fk_unit = 0;
3581 }
3582 if (empty($this->subprice)) {
3583 $this->subprice = 0;
3584 }
3585
3586 if (empty($this->pa_ht)) {
3587 $this->pa_ht = 0;
3588 }
3589
3590 // if buy price not defined, define buyprice as configured in margin admin
3591 if ($this->pa_ht == 0) {
3592 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3593 if ($result < 0) {
3594 return $result;
3595 } else {
3596 $this->pa_ht = $result;
3597 }
3598 }
3599
3600 $this->db->begin();
3601
3602 // Mise a jour ligne en base
3603 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3604 $sql .= " description = '".$this->db->escape($this->desc)."'";
3605 $sql .= " , label = ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
3606 $sql .= " , product_type = ".((int) $this->product_type);
3607 $sql .= " , date_start = ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null");
3608 $sql .= " , date_end = ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null");
3609 $sql .= " , tva_tx = '".price2num($this->tva_tx)."'";
3610 $sql .= " , localtax1_tx = ".(float) price2num($this->localtax1_tx);
3611 $sql .= " , localtax2_tx = ".(float) price2num($this->localtax2_tx);
3612 $sql .= " , localtax1_type = '".$this->db->escape($this->localtax1_type)."'";
3613 $sql .= " , localtax2_type = '".$this->db->escape($this->localtax2_type)."'";
3614 $sql .= " , qty = ".(float) price2num($this->qty);
3615 $sql .= " , subprice = ".(float) price2num($this->subprice);
3616 $sql .= " , subprice_ttc = ".(float) price2num($this->subprice_ttc);
3617 $sql .= " , remise_percent = ".(float) price2num($this->remise_percent);
3618 $sql .= " , info_bits = ".((int) $this->info_bits);
3619 if (empty($this->skip_update_total)) {
3620 $sql .= " , total_ht = ".(float) price2num($this->total_ht);
3621 $sql .= " , total_tva = ".(float) price2num($this->total_tva);
3622 $sql .= " , total_ttc = ".(float) price2num($this->total_ttc);
3623 $sql .= " , total_localtax1 = ".(float) price2num($this->total_localtax1);
3624 $sql .= " , total_localtax2 = ".(float) price2num($this->total_localtax2);
3625 }
3626 $sql .= " , fk_product_fournisseur_price = ".(!empty($this->fk_fournprice) ? "'".$this->db->escape((string) $this->fk_fournprice)."'" : "null");
3627 $sql .= " , buy_price_ht = ".(float) price2num($this->pa_ht);
3628 $sql .= " , special_code = ".((int) $this->special_code);
3629 $sql .= " , fk_parent_line = ".($this->fk_parent_line > 0 ? $this->fk_parent_line : "null");
3630 if (!empty($this->rang)) {
3631 $sql .= ", rang = ".((int) $this->rang);
3632 }
3633 $sql .= " , ref_fourn = ".(!empty($this->ref_fourn) ? "'".$this->db->escape($this->ref_fourn)."'" : "null");
3634 $sql .= " , fk_unit = ".($this->fk_unit ? (int) $this->fk_unit : 'null');
3635
3636 // Multicurrency
3637 $sql .= " , multicurrency_subprice = ".(float) price2num($this->multicurrency_subprice);
3638 $sql .= " , multicurrency_subprice_ttc = ".(float) price2num($this->multicurrency_subprice_ttc);
3639 $sql .= " , multicurrency_total_ht = ".(float) price2num($this->multicurrency_total_ht);
3640 $sql .= " , multicurrency_total_tva = ".(float) price2num($this->multicurrency_total_tva);
3641 $sql .= " , multicurrency_total_ttc = ".(float) price2num($this->multicurrency_total_ttc);
3642
3643 $sql .= " WHERE rowid = ".((int) $this->id);
3644
3645 dol_syslog(get_class($this)."::update", LOG_DEBUG);
3646 $resql = $this->db->query($sql);
3647 if ($resql) {
3648 if (!$error) {
3649 $result = $this->insertExtraFields();
3650 if ($result < 0) {
3651 $error++;
3652 }
3653 }
3654
3655 if (!$error && !$notrigger) {
3656 // Call trigger
3657 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_MODIFY', $user);
3658 if ($result < 0) {
3659 $this->db->rollback();
3660 return -1;
3661 }
3662 // End call triggers
3663 }
3664
3665 $this->db->commit();
3666 return 1;
3667 } else {
3668 $this->error = $this->db->error();
3669 $this->db->rollback();
3670 return -2;
3671 }
3672 }
3673
3674 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3681 public function update_total()
3682 {
3683 // phpcs:enable
3684 $this->db->begin();
3685
3686 // Mise a jour ligne en base
3687 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3688 $sql .= " total_ht = ".(float) price2num($this->total_ht, 'MT');
3689 $sql .= ",total_tva = ".(float) price2num($this->total_tva, 'MT');
3690 $sql .= ",total_ttc = ".(float) price2num($this->total_ttc, 'MT');
3691 $sql .= " WHERE rowid = ".((int) $this->id);
3692
3693 dol_syslog("SupplierProposalLine::update_total", LOG_DEBUG);
3694
3695 $resql = $this->db->query($sql);
3696 if ($resql) {
3697 $this->db->commit();
3698 return 1;
3699 } else {
3700 $this->error = $this->db->error();
3701 $this->db->rollback();
3702 return -2;
3703 }
3704 }
3705}
$object ref
Definition info.php:90
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.
update_price($exclspec=0, $roundingadjust='auto', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
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 if an object id or ref exists If you don't need or want to instantiate the object and just need...
updateRangOfLine($rowid, $rang)
Update position of line (rang)
checkActiveProductInLines($status='onsale')
Check if all products have the right status (on sale, on buy) called during validation of propal,...
deleteExtraFields()
Delete all extra fields values for the current object.
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.
add_contact($fk_socpeople, $type_contact, $source='external', $notrigger=0)
Add a link between element $this->element and a contact.
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.
Class Currency.
static getIdAndTxFromCode($dbs, $code, $date_document=0)
Get id and rate of currency from code.
Class to manage predefined suppliers products.
Class to manage products or services.
const TYPE_PRODUCT
Regular product.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage price ask supplier.
updatePriceFournisseur($idProductFournPrice, $product, $user)
Update ProductFournisseur.
updateline($rowid, $pu, $qty, $remise_percent, $txtva, $txlocaltax1=0, $txlocaltax2=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, $array_options=[], $ref_supplier='', $fk_unit=0, $pu_ht_devise=0)
Update a proposal line.
cloture($user, $status, $note)
Close the askprice.
initAsSpecimen($param=array())
Initialise an instance with random values.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template module.
addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1=0, $txlocaltax2=0, $fk_product=0, $remise_percent=0, $price_base_type='HT', $pu_ttc=0, $info_bits=0, $type=0, $rang=-1, $special_code=0, $fk_parent_line=0, $fk_fournprice=0, $pa_ht=0, $label='', $array_options=[], $ref_supplier='', $fk_unit=0, $origin='', $origin_id=0, $pu_ht_devise=0, $date_start=0, $date_end=0)
Add a proposal line into database (linked to product/service or not) Les parameters sont deja cense e...
info($id)
Object SupplierProposal Information.
create($user, $notrigger=0)
Create commercial proposal into database this->ref can be set or empty.
insert_discount($idremise)
Adding line of fixed discount in the proposal in DB.
reopen($user, $status, $note='', $notrigger=0)
Set an overall discount on the proposal.
static replaceProduct(DoliDB $db, $origin_id, $dest_id)
Function used to replace a product id with another one.
fetch($rowid, $ref='')
Load a proposal from database and its ligne array.
load_board($user, $mode)
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
createFromClone(User $user, $fromid=0)
Load an object from its id and create a new one in database.
const STATUS_NOTSIGNED
Not signed quote, canceled.
const STATUS_DRAFT
Draft status.
liste_array($shortlist=0, $draft=0, $notcurrentuser=0, $socid=0, $limit=0, $offset=0, $sortfield='p.datec', $sortorder='DESC')
Return list of askprice (eventually filtered on user) into an array.
setDeliveryDate($user, $delivery_date)
Set delivery date.
__construct($db, $socid=0, $supplier_proposalid=0)
Constructor.
LibStatut($status, $mode=1)
Return label of a status (draft, validated, ...)
setCategories($categories)
Sets object to supplied categories.
static replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
updateOrCreatePriceFournisseur($user)
Add or update supplier price according to result of proposal.
loadStateBoard()
Load indicator this->nb of global stats widget.
deleteLine($lineid)
Delete detail line.
const STATUS_VALIDATED
Validated status.
createPriceFournisseur($product, $user)
Create Price Fournisseur.
getLibStatut($mode=0)
Return label of status of proposal (draft, validated, ...)
add_product($idproduct, $qty, $remise_percent=0)
Add line into array ->lines.
set_date_livraison($user, $delivery_date)
Set delivery date.
const STATUS_SIGNED
Signed quote.
getNomUrl($withpicto=0, $option='', $get_params='', $notooltip=0, $save_lastsearch_value=-1, $addlinktonotes=0)
Return clickable link of object (with eventually picto)
getKanbanView($option='', $arraydata=null)
Return clickable link of object (with eventually picto)
getTooltipContentArray($params)
getTooltipContentArray
valid($user, $notrigger=0)
Set status to validated.
const STATUS_CLOSE
Billed or closed/processed quote.
getLinesArray()
Retrieve an array of supplier proposal lines.
setDraft($user)
Set draft status.
Class to manage supplier_proposal lines.
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.
update($notrigger=0)
Update propal line object into DB.
__construct($db)
Class line Constructor.
Class to manage translations.
Class to manage Dolibarr users.
print $langs trans("Ref").' m titre as m m statut as status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition index.php:171
global $mysoc
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_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0, $level=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
dol_dir_list($utf8_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:64
dol_delete_preview($object)
Delete all preview files linked to object instance.
dol_now($mode='gmt')
Return date for now.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
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 '.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
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.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
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)
getDolCurrency()
Return the main currency ('EUR', 'USD', ...)
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
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.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
getDolGlobalBool($key, $default=false)
Return a Dolibarr global constant boolean value.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
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...
get_localtax($vatrate, $local, $thirdparty_buyer=null, $thirdparty_seller=null, $vatnpr=0)
Return localtax rate for a particular VAT rate, when selling a product with vat $vatrate,...
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...
getMarginInfos($pv_ht, $remise_percent, $tva_tx, $localtax1_tx, $localtax2_tx, $fk_pa, $pa_ht)
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=null, $localtaxes_array=[], $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition price.lib.php:90
if(getDolGlobalString( 'TAKEPOS_SHOW_CUSTOMER')) print $langs trans('Date')." left Label right Qty right Price right TotalHT right TotalTTC right right right right right right right right right centpercent right TotalHT right n right VAT right n right TotalVAT right n No sujeto a RE IRPF right TotalLT1 right n right TotalLT2 right n right TotalTTC right n takeposcustomercurrency takeposcustomercurrency takeposcustomercurrency takeposcustomercurrency right TotalTTC takeposcustomercurrency right takeposcustomercurrency n right PaymentTypeShortLIQ right SELECT p pos_change as p datep as date
Definition receipt.php:464