dolibarr 23.0.4
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 if (!$this->isLineOfObject($rowid)) {
750 $this->error = 'ErrorLineIDDoesNotMatchWithObjectID';
751 return -1;
752 }
753
754 dol_syslog(get_class($this)."::updateLine $rowid, $pu, $qty, $remise_percent, $txtva, $desc, $price_base_type, $info_bits");
755 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
756
757 // Clean parameters
758 $remise_percent = price2num($remise_percent);
759 $qty = (float) price2num($qty);
760 $pu = price2num($pu);
761 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
762 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
763 }
764 $txlocaltax1 = price2num($txlocaltax1);
765 $txlocaltax2 = price2num($txlocaltax2);
766 $pa_ht = price2num($pa_ht);
767 if (empty($qty) && empty($special_code)) {
768 $special_code = 3; // Set option tag
769 }
770 if (!empty($qty) && $special_code == 3) {
771 $special_code = 0; // Remove option tag
772 }
773
774 if ($this->status == 0) {
775 $this->db->begin();
776
777 // Calcul du total TTC et de la TVA pour la ligne a partir de
778 // qty, pu, remise_percent et txtva
779 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
780 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
781
782 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
783
784 // Clean vat code
785 $reg = array();
786 $vat_src_code = '';
787 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
788 $vat_src_code = $reg[1];
789 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
790 }
791
792 if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
793 $pu = 0;
794 }
795
796 $tabprice = calcul_price_total(
797 $qty,
798 $pu,
799 (float) $remise_percent,
800 $txtva,
801 (float) $txlocaltax1,
802 (float) $txlocaltax2,
803 0,
804 $price_base_type,
805 $info_bits,
806 $type,
807 $this->thirdparty,
808 $localtaxes_type,
809 100,
810 (float) $this->multicurrency_tx,
811 $pu_ht_devise
812 );
813 $total_ht = $tabprice[0];
814 $total_tva = $tabprice[1];
815 $total_ttc = $tabprice[2];
816 $total_localtax1 = $tabprice[9];
817 $total_localtax2 = $tabprice[10];
818 $pu_ht = $tabprice[3];
819 $pu_tva = $tabprice[4];
820 $pu_ttc = $tabprice[5];
821
822 // MultiCurrency
823 $multicurrency_total_ht = $tabprice[16];
824 $multicurrency_total_tva = $tabprice[17];
825 $multicurrency_total_ttc = $tabprice[18];
826 $pu_ht_devise = $tabprice[19];
827
828 $pu = $pu_ht;
829 if ($price_base_type == 'TTC') {
830 $pu = $pu_ttc;
831 }
832
833 // Fetch current line from the database and then clone the object and set it in $oldline property
834 $line = new SupplierProposalLine($this->db);
835 $line->fetch($rowid);
836 $line->fetch_optionals();
837
838 $fk_product = $line->fk_product;
839
840 // Stock previous line records
841 $staticline = clone $line;
842
843 $line->oldline = $staticline;
844 $this->line = $line;
845 $this->line->context = $this->context;
846
847 // Reorder if fk_parent_line change
848 if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
849 $rangmax = $this->line_max($fk_parent_line);
850 $this->line->rang = $rangmax + 1;
851 }
852
853 $this->line->id = $rowid;
854 $this->line->label = $label;
855 $this->line->desc = $desc;
856 $this->line->qty = $qty;
857 $this->line->product_type = $type;
858
859 $this->line->vat_src_code = $vat_src_code;
860 $this->line->tva_tx = $txtva;
861 $this->line->localtax1_tx = $txlocaltax1;
862 $this->line->localtax2_tx = $txlocaltax2;
863 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
864 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
865 $this->line->remise_percent = $remise_percent;
866 $this->line->subprice = (float) $pu;
867 $this->line->info_bits = $info_bits;
868 $this->line->total_ht = (float) $total_ht;
869 $this->line->total_tva = (float) $total_tva;
870 $this->line->total_localtax1 = (float) $total_localtax1;
871 $this->line->total_localtax2 = (float) $total_localtax2;
872 $this->line->total_ttc = (float) $total_ttc;
873 $this->line->special_code = $special_code;
874 $this->line->fk_parent_line = $fk_parent_line;
875 $this->line->skip_update_total = $skip_update_total;
876 $this->line->ref_fourn = $ref_supplier;
877 $this->line->fk_unit = $fk_unit;
878
879 // infos merge
880 if (!empty($fk_product) && $fk_product > 0 && empty($fk_fournprice) && empty($pa_ht)) {
881 // by external module, take lowest buying price
882 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
883 $productFournisseur = new ProductFournisseur($this->db);
884 $productFournisseur->find_min_price_product_fournisseur($fk_product);
885 $this->line->fk_fournprice = $productFournisseur->product_fourn_price_id;
886 } else {
887 $this->line->fk_fournprice = $fk_fournprice;
888 }
889 $this->line->pa_ht = $pa_ht;
890
891 if (is_array($array_options) && count($array_options) > 0) {
892 // We replace values in this->line->array_options only for entries defined into $array_options
893 foreach ($array_options as $key => $value) {
894 $this->line->array_options[$key] = $array_options[$key];
895 }
896 }
897
898 // Multicurrency
899 $this->line->multicurrency_subprice = (float) $pu_ht_devise;
900 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
901 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
902 $this->line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
903
904 $result = $this->line->update();
905 if ($result > 0) {
906 // Reorder if child line
907 if (!empty($fk_parent_line)) {
908 $this->line_order(true, 'DESC');
909 }
910
911 $this->update_price(1);
912
913 $this->db->commit();
914 return $result;
915 } else {
916 $this->error = $this->db->error();
917 $this->db->rollback();
918 return -1;
919 }
920 } else {
921 dol_syslog(get_class($this)."::updateline Erreur -2 SupplierProposal en mode incompatible pour cette action");
922 return -2;
923 }
924 }
925
926
933 public function deleteLine($lineid)
934 {
935 global $user;
936
937 if ($this->status == 0) {
938 $line = new SupplierProposalLine($this->db);
939
940 // For triggers
941 $line->fetch($lineid);
942
943 if ($this->id > 0 && (int) $line->fk_supplier_proposal !== (int) $this->id) {
944 $this->error = 'ErrorLineIDDoesNotMatchWithObjectID';
945 return -1;
946 }
947
948 if ($line->delete($user) > 0) {
949 $this->update_price(1);
950
951 return 1;
952 } else {
953 return -1;
954 }
955 } else {
956 return -2;
957 }
958 }
959
960
969 public function create($user, $notrigger = 0)
970 {
971 global $langs, $conf, $mysoc, $hookmanager;
972 $error = 0;
973
974 $now = dol_now();
975
976 dol_syslog(get_class($this)."::create");
977
978 // Check parameters
979 $result = $this->fetch_thirdparty();
980 if ($result < 0) {
981 $this->error = "Failed to fetch company";
982 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
983 return -3;
984 }
985 if (!empty($this->ref)) { // We check that ref is not already used
986 $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
987 if ($result > 0) {
988 $this->error = 'ErrorRefAlreadyExists';
989 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
990 $this->db->rollback();
991 return -1;
992 }
993 }
994
995 // Set tmp vars
996 $delivery_date = $this->delivery_date;
997
998 // Multicurrency
999 if (!empty($this->multicurrency_code)) {
1000 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $now);
1001 }
1002 if (empty($this->fk_multicurrency)) {
1003 $this->multicurrency_code = getDolCurrency();
1004 $this->fk_multicurrency = 0;
1005 $this->multicurrency_tx = 1;
1006 }
1007
1008 $this->db->begin();
1009
1010 // Insert into database
1011 $sql = "INSERT INTO ".MAIN_DB_PREFIX."supplier_proposal (";
1012 $sql .= "fk_soc";
1013 $sql .= ", price";
1014 $sql .= ", total_tva";
1015 $sql .= ", total_ttc";
1016 $sql .= ", datec";
1017 $sql .= ", ref";
1018 $sql .= ", fk_user_author";
1019 $sql .= ", note_private";
1020 $sql .= ", note_public";
1021 $sql .= ", model_pdf";
1022 $sql .= ", fk_cond_reglement";
1023 $sql .= ", deposit_percent";
1024 $sql .= ", fk_mode_reglement";
1025 $sql .= ", fk_account";
1026 $sql .= ", date_livraison";
1027 $sql .= ", fk_shipping_method";
1028 $sql .= ", fk_projet";
1029 $sql .= ", entity";
1030 $sql .= ", fk_multicurrency";
1031 $sql .= ", multicurrency_code";
1032 $sql .= ", multicurrency_tx";
1033 $sql .= ") ";
1034 $sql .= " VALUES (";
1035 $sql .= ((int) $this->socid);
1036 $sql .= ", 0";
1037 $sql .= ", 0";
1038 $sql .= ", 0";
1039 $sql .= ", '".$this->db->idate($now)."'";
1040 $sql .= ", '(PROV)'";
1041 $sql .= ", ".($user->id > 0 ? ((int) $user->id) : "null");
1042 $sql .= ", '".$this->db->escape($this->note_private)."'";
1043 $sql .= ", '".$this->db->escape($this->note_public)."'";
1044 $sql .= ", '".$this->db->escape($this->model_pdf)."'";
1045 $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : 'NULL');
1046 $sql .= ", ".(!empty($this->deposit_percent) ? "'" . $this->db->escape($this->deposit_percent) . "'" : 'NULL');
1047 $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : 'NULL');
1048 $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
1049 $sql .= ", ".(isDolTms($delivery_date) ? "'".$this->db->idate($delivery_date)."'" : "null");
1050 $sql .= ", ".($this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : 'NULL');
1051 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
1052 $sql .= ", ".((int) $conf->entity);
1053 $sql .= ", ".((int) $this->fk_multicurrency);
1054 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1055 $sql .= ", ".((float) $this->multicurrency_tx);
1056 $sql .= ")";
1057
1058 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1059 $resql = $this->db->query($sql);
1060 if ($resql) {
1061 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."supplier_proposal");
1062
1063 if ($this->id) {
1064 $this->ref = '(PROV'.$this->id.')';
1065 $sql = 'UPDATE '.MAIN_DB_PREFIX."supplier_proposal SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
1066
1067 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1068 $resql = $this->db->query($sql);
1069 if (!$resql) {
1070 $error++;
1071 }
1072
1073 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1074 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1075 }
1076
1077 // Add object linked
1078 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1079 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1080 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, ...))
1081 foreach ($tmp_origin_id as $origin_id) {
1082 $ret = $this->add_object_linked($origin, $origin_id);
1083 if (!$ret) {
1084 dol_print_error($this->db);
1085 $error++;
1086 }
1087 }
1088 }
1089 }
1090 }
1091
1092 /*
1093 * Insertion du detail des produits dans la base
1094 */
1095 if (!$error) {
1096 $fk_parent_line = 0;
1097 $num = count($this->lines);
1098
1099 for ($i = 0; $i < $num; $i++) {
1100 // Reset fk_parent_line for no child products and special product
1101 if (($this->lines[$i]->product_type != 9 && empty($this->lines[$i]->fk_parent_line)) || $this->lines[$i]->product_type == 9) {
1102 $fk_parent_line = 0;
1103 }
1104
1105 $result = $this->addline(
1106 $this->lines[$i]->desc,
1107 $this->lines[$i]->subprice,
1108 $this->lines[$i]->qty,
1109 $this->lines[$i]->tva_tx,
1110 $this->lines[$i]->localtax1_tx,
1111 $this->lines[$i]->localtax2_tx,
1112 $this->lines[$i]->fk_product,
1113 $this->lines[$i]->remise_percent,
1114 'HT',
1115 0,
1116 0,
1117 $this->lines[$i]->product_type,
1118 $this->lines[$i]->rang,
1119 $this->lines[$i]->special_code,
1120 $fk_parent_line,
1121 $this->lines[$i]->fk_fournprice,
1122 $this->lines[$i]->pa_ht,
1123 empty($this->lines[$i]->label) ? '' : $this->lines[$i]->label, // deprecated
1124 $this->lines[$i]->array_options,
1125 $this->lines[$i]->ref_fourn,
1126 $this->lines[$i]->fk_unit,
1127 'supplier_proposal',
1128 $this->lines[$i]->rowid
1129 );
1130
1131 if ($result < 0) {
1132 $error++;
1133 $this->error = $this->db->error;
1134 dol_print_error($this->db);
1135 break;
1136 }
1137 // Defined the new fk_parent_line
1138 if ($result > 0 && $this->lines[$i]->product_type == 9) {
1139 $fk_parent_line = $result;
1140 }
1141 }
1142 }
1143
1144 if (!$error) {
1145 // Update denormalized data
1146 $resql = $this->update_price(1);
1147 if ($resql) {
1148 $action = 'update';
1149
1150 // Actions on extra fields
1151 if (!$error) {
1152 $result = $this->insertExtraFields();
1153 if ($result < 0) {
1154 $error++;
1155 }
1156 }
1157
1158 if (!$error && !$notrigger) {
1159 // Call trigger
1160 $result = $this->call_trigger('PROPOSAL_SUPPLIER_CREATE', $user);
1161 if ($result < 0) {
1162 $error++;
1163 }
1164 // End call triggers
1165 }
1166 } else {
1167 $this->error = $this->db->lasterror();
1168 $error++;
1169 }
1170 }
1171 } else {
1172 $this->error = $this->db->lasterror();
1173 $error++;
1174 }
1175
1176 if (!$error) {
1177 $this->db->commit();
1178 dol_syslog(get_class($this)."::create done id=".$this->id);
1179 return $this->id;
1180 } else {
1181 $this->db->rollback();
1182 return -2;
1183 }
1184 } else {
1185 $this->error = $this->db->lasterror();
1186 $this->db->rollback();
1187 return -1;
1188 }
1189 }
1190
1198 public function createFromClone(User $user, $fromid = 0)
1199 {
1200 global $conf, $hookmanager;
1201
1202 $error = 0;
1203 $now = dol_now();
1204
1205 $this->db->begin();
1206
1207 // get extrafields so they will be clone
1208 foreach ($this->lines as $line) {
1209 $line->fetch_optionals();
1210 }
1211
1212 // Load source object
1213 $objFrom = clone $this;
1214
1215 $objsoc = new Societe($this->db);
1216
1217 // Change socid if needed
1218 if (!empty($fromid) && $fromid != $this->socid) {
1219 if ($objsoc->fetch($fromid) > 0) {
1220 $this->socid = $objsoc->id;
1221 $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1222 $this->deposit_percent = (!empty($objsoc->deposit_percent) ? $objsoc->deposit_percent : 0);
1223 $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1224 unset($this->fk_project);
1225 }
1226
1227 // TODO Change product price if multi-prices
1228 } else {
1229 $objsoc->fetch($this->socid);
1230 }
1231
1232 $this->id = 0;
1233 $this->status = 0;
1234 $this->statut = 0;
1235
1236 if (!getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') || !is_readable(DOL_DOCUMENT_ROOT."/core/modules/supplier_proposal/" . getDolGlobalString('SUPPLIER_PROPOSAL_ADDON').".php")) {
1237 $this->error = 'ErrorSetupNotComplete';
1238 return -1;
1239 }
1240
1241 // Clear fields
1242 $this->user_author_id = $user->id;
1243 $this->user_validation_id = 0;
1244 $this->date = $now;
1245
1246 // Set ref
1247 require_once DOL_DOCUMENT_ROOT."/core/modules/supplier_proposal/" . getDolGlobalString('SUPPLIER_PROPOSAL_ADDON').'.php';
1248 $obj = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
1249 $modSupplierProposal = new $obj();
1250 '@phan-var-force ModeleNumRefSupplierProposal $modSupplierProposal';
1251 $this->ref = $modSupplierProposal->getNextValue($objsoc, $this);
1252
1253 // Create clone
1254 $this->context['createfromclone'] = 'createfromclone';
1255 $result = $this->create($user);
1256 if ($result < 0) {
1257 $error++;
1258 }
1259
1260 if (!$error) {
1261 // Hook of thirdparty module
1262 if (is_object($hookmanager)) {
1263 $parameters = array('objFrom' => $objFrom);
1264 $action = '';
1265 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1266 if ($reshook < 0) {
1267 $this->setErrorsFromObject($hookmanager);
1268 $error++;
1269 }
1270 }
1271 }
1272
1273 unset($this->context['createfromclone']);
1274
1275 // End
1276 if (!$error) {
1277 $this->db->commit();
1278 return $this->id;
1279 } else {
1280 $this->db->rollback();
1281 return -1;
1282 }
1283 }
1284
1292 public function fetch($rowid, $ref = '')
1293 {
1294 $sql = "SELECT p.rowid, p.entity, p.ref, p.fk_soc as socid";
1295 $sql .= ", p.total_ttc, p.total_tva, p.localtax1, p.localtax2, p.total_ht";
1296 $sql .= ", p.datec, GREATEST(p.tms, COALESCE(pef.tms, p.tms)) as date_modification";
1297 $sql .= ", p.date_valid as datev";
1298 $sql .= ", p.date_livraison as delivery_date";
1299 $sql .= ", p.model_pdf, p.extraparams";
1300 $sql .= ", p.note_private, p.note_public";
1301 $sql .= ", p.fk_projet as fk_project, p.fk_statut as status";
1302 $sql .= ", p.fk_user_author, p.fk_user_modif, p.fk_user_valid, p.fk_user_cloture";
1303 $sql .= ", p.fk_cond_reglement";
1304 $sql .= ", p.fk_mode_reglement";
1305 $sql .= ', p.fk_account';
1306 $sql .= ", p.fk_shipping_method";
1307 $sql .= ", p.last_main_doc";
1308 $sql .= ", p.fk_multicurrency, p.multicurrency_code, p.multicurrency_tx, p.multicurrency_total_ht, p.multicurrency_total_tva, p.multicurrency_total_ttc";
1309 $sql .= ", c.label as statut_label";
1310 $sql .= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc, p.deposit_percent";
1311 $sql .= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement";
1312 $sql .= " FROM ".MAIN_DB_PREFIX."c_propalst as c, ".MAIN_DB_PREFIX."supplier_proposal as p";
1313 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."supplier_proposal_extrafields as pef ON pef.fk_object=p.rowid";
1314 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id';
1315 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON p.fk_cond_reglement = cr.rowid';
1316 $sql .= " WHERE p.fk_statut = c.id";
1317 $sql .= " AND p.entity IN (".getEntity('supplier_proposal').")";
1318 if ($ref) {
1319 $sql .= " AND p.ref = '".$this->db->escape($ref)."'";
1320 } else {
1321 $sql .= " AND p.rowid = ".((int) $rowid);
1322 }
1323
1324 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1325 $resql = $this->db->query($sql);
1326 if ($resql) {
1327 if ($this->db->num_rows($resql)) {
1328 $obj = $this->db->fetch_object($resql);
1329
1330 $this->id = $obj->rowid;
1331 $this->entity = $obj->entity;
1332
1333 $this->ref = $obj->ref;
1334 $this->total_ht = $obj->total_ht;
1335 $this->total_tva = $obj->total_tva;
1336 $this->total_localtax1 = $obj->localtax1;
1337 $this->total_localtax2 = $obj->localtax2;
1338 $this->total_ttc = $obj->total_ttc;
1339 $this->socid = $obj->socid;
1340 $this->fk_project = $obj->fk_project;
1341 $this->model_pdf = $obj->model_pdf;
1342 $this->note = $obj->note_private; // TODO deprecated
1343 $this->note_private = $obj->note_private;
1344 $this->note_public = $obj->note_public;
1345 $this->statut = (int) $obj->status;
1346 $this->status = (int) $obj->status;
1347 $this->datec = $this->db->jdate($obj->datec); // TODO deprecated
1348 $this->datev = $this->db->jdate($obj->datev); // TODO deprecated
1349 $this->date_creation = $this->db->jdate($obj->datec); // Creation date
1350 $this->date_modification = $this->db->jdate($obj->date_modification); // Modification date
1351 $this->date = $this->date_creation;
1352 $this->date_validation = $this->db->jdate($obj->datev); // Validation date
1353 $this->delivery_date = $this->db->jdate($obj->delivery_date);
1354 $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1355
1356 $this->last_main_doc = $obj->last_main_doc;
1357 $this->mode_reglement_id = $obj->fk_mode_reglement;
1358 $this->mode_reglement_code = $obj->mode_reglement_code;
1359 $this->mode_reglement = $obj->mode_reglement;
1360 $this->deposit_percent = $obj->deposit_percent;
1361 $this->fk_account = ($obj->fk_account > 0) ? $obj->fk_account : null;
1362 $this->cond_reglement_id = $obj->fk_cond_reglement;
1363 $this->cond_reglement_code = $obj->cond_reglement_code;
1364 $this->cond_reglement = $obj->cond_reglement;
1365 $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1366
1367 $this->extraparams = (array) (!empty($obj->extraparams) ? json_decode($obj->extraparams, true) : array());
1368
1369 $this->user_author_id = $obj->fk_user_author;
1370 $this->user_modification_id = $obj->fk_user_modif;
1371 $this->user_validation_id = $obj->fk_user_valid;
1372 $this->user_closing_id = $obj->fk_user_cloture;
1373
1374 // Multicurrency
1375 $this->fk_multicurrency = $obj->fk_multicurrency;
1376 $this->multicurrency_code = $obj->multicurrency_code;
1377 $this->multicurrency_tx = $obj->multicurrency_tx;
1378 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1379 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1380 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1381
1382 $this->db->free($resql);
1383
1384 // Retrieve all extrafield
1385 // fetch optionals attributes and labels
1386 $this->fetch_optionals();
1387
1388 $this->lines = array();
1389
1390 // Lines of supplier proposals
1391 $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,";
1392 $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,";
1393 $sql .= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label,';
1394 $sql .= ' d.ref_fourn as ref_produit_fourn, d.extraparams,';
1395 $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';
1396 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposaldet as d";
1397 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON d.fk_product = p.rowid";
1398 $sql .= " WHERE d.fk_supplier_proposal = ".((int) $this->id);
1399 $sql .= " ORDER by d.rang";
1400
1401 $result = $this->db->query($sql);
1402 if ($result) {
1403 $num = $this->db->num_rows($result);
1404 $i = 0;
1405
1406 while ($i < $num) {
1407 $objp = $this->db->fetch_object($result);
1408
1409 $line = new SupplierProposalLine($this->db);
1410
1411 $line->rowid = $objp->rowid; // deprecated
1412 $line->id = $objp->rowid;
1413 $line->fk_supplier_proposal = $objp->fk_supplier_proposal;
1414 $line->fk_parent_line = $objp->fk_parent_line;
1415 $line->product_type = $objp->product_type;
1416 $line->label = $objp->custom_label;
1417 $line->desc = $objp->description; // Description ligne
1418 $line->qty = $objp->qty;
1419 $line->tva_tx = $objp->tva_tx;
1420 $line->localtax1_tx = $objp->localtax1_tx;
1421 $line->localtax2_tx = $objp->localtax2_tx;
1422 $line->subprice = $objp->subprice;
1423 $line->fk_remise_except = $objp->fk_remise_except;
1424 $line->remise_percent = $objp->remise_percent;
1425
1426 $line->info_bits = $objp->info_bits;
1427 $line->total_ht = $objp->total_ht;
1428 $line->total_tva = $objp->total_tva;
1429 $line->total_localtax1 = $objp->total_localtax1;
1430 $line->total_localtax2 = $objp->total_localtax2;
1431 $line->total_ttc = $objp->total_ttc;
1432 $line->fk_fournprice = $objp->fk_fournprice;
1433 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1434 $line->pa_ht = $marginInfos[0];
1435 $line->marge_tx = $marginInfos[1];
1436 $line->marque_tx = $marginInfos[2];
1437 $line->special_code = $objp->special_code;
1438 $line->rang = $objp->rang;
1439
1440 $line->fk_product = $objp->fk_product;
1441
1442 $line->ref = $objp->product_ref; // deprecated
1443 $line->product_ref = $objp->product_ref;
1444 $line->libelle = $objp->product_label; // deprecated
1445 $line->product_label = $objp->product_label;
1446 $line->product_desc = $objp->product_desc; // Description produit
1447 $line->fk_product_type = $objp->fk_product_type;
1448
1449 $line->ref_fourn = $objp->ref_produit_fourn;
1450
1451 $line->extraparams = !empty($objp->extraparams) ? (array) json_decode($objp->extraparams, true) : array();
1452
1453 // Multicurrency
1454 $line->fk_multicurrency = $objp->fk_multicurrency;
1455 $line->multicurrency_code = $objp->multicurrency_code;
1456 $line->multicurrency_subprice = $objp->multicurrency_subprice;
1457 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
1458 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
1459 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
1460 $line->fk_unit = $objp->fk_unit;
1461
1462 $line->fetch_optionals();
1463
1464 $this->lines[$i] = $line;
1465
1466 $i++;
1467 }
1468 $this->db->free($result);
1469 } else {
1470 $this->error = $this->db->error();
1471 return -1;
1472 }
1473
1474 return 1;
1475 }
1476
1477 $this->error = "Record Not Found";
1478 return 0;
1479 } else {
1480 $this->error = $this->db->error();
1481 return -1;
1482 }
1483 }
1484
1492 public function valid($user, $notrigger = 0)
1493 {
1494 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1495
1496 global $conf, $langs;
1497
1498 $error = 0;
1499 $now = dol_now();
1500
1501 if ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'creer'))
1502 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'validate_advance'))) {
1503 $this->db->begin();
1504
1505 // Numbering module definition
1506 $soc = new Societe($this->db);
1507 $result = $soc->fetch($this->socid);
1508
1509 if ($result < 0) {
1510 return -1;
1511 }
1512 if (!getDolGlobalBool('SUPPLIER_PROPOSAL_NOCHECK_ONBUY_PRODUCTS_ONVALID') && !$this->checkActiveProductInLines('onbuy')) {
1513 dol_syslog(get_class($this)."::valid checkActiveProductInLines ".$this->error, LOG_INFO);
1514 return -1;
1515 }
1516 // Define new ref
1517 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1518 $num = $this->getNextNumRef($soc);
1519 } else {
1520 $num = (string) $this->ref;
1521 }
1522 $this->newref = dol_sanitizeFileName($num);
1523
1524 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1525 $sql .= " SET ref = '".$this->db->escape($num)."',";
1526 $sql .= " fk_statut = 1, date_valid='".$this->db->idate($now)."', fk_user_valid=".((int) $user->id);
1527 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1528
1529 dol_syslog(get_class($this)."::valid", LOG_DEBUG);
1530 $resql = $this->db->query($sql);
1531 if (!$resql) {
1532 dol_print_error($this->db);
1533 $error++;
1534 }
1535
1536 // Trigger calls
1537 if (!$error && !$notrigger) {
1538 // Call trigger
1539 $result = $this->call_trigger('PROPOSAL_SUPPLIER_VALIDATE', $user);
1540 if ($result < 0) {
1541 $error++;
1542 }
1543 // End call triggers
1544 }
1545
1546 if (!$error) {
1547 $this->oldref = $this->ref;
1548
1549 // Rename directory if dir was a temporary ref
1550 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1551 // Now we rename also files into index
1552 $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)."'";
1553 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1554 $resql = $this->db->query($sql);
1555 if (!$resql) {
1556 $error++;
1557 $this->error = $this->db->lasterror();
1558 }
1559 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'supplier_proposal/".$this->db->escape($this->newref)."'";
1560 $sql .= " WHERE filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1561 $resql = $this->db->query($sql);
1562 if (!$resql) {
1563 $error++;
1564 $this->error = $this->db->lasterror();
1565 }
1566
1567 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1568 $oldref = dol_sanitizeFileName($this->ref);
1569 $newref = dol_sanitizeFileName($num);
1570 $dirsource = $conf->supplier_proposal->dir_output.'/'.$oldref;
1571 $dirdest = $conf->supplier_proposal->dir_output.'/'.$newref;
1572 if (!$error && file_exists($dirsource)) {
1573 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
1574 if (@rename($dirsource, $dirdest)) {
1575 dol_syslog("Rename ok");
1576 // Rename docs starting with $oldref with $newref
1577 $listoffiles = dol_dir_list($conf->supplier_proposal->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1578 foreach ($listoffiles as $fileentry) {
1579 $dirsource = $fileentry['name'];
1580 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1581 $dirsource = $fileentry['path'].'/'.$dirsource;
1582 $dirdest = $fileentry['path'].'/'.$dirdest;
1583 @rename($dirsource, $dirdest);
1584 }
1585 }
1586 }
1587 }
1588
1589 $this->ref = $num;
1590 $this->statut = self::STATUS_VALIDATED;
1592 $this->user_validation_id = $user->id;
1593 $this->datev = $now;
1594 $this->date_validation = $now;
1595
1596 if (getDolGlobalString('SUPPLIER_PROPOSAL_AUTOADD_USER_CONTACT')) {
1597 $result = $this->add_contact($user->id, 'SALESREPFOLL', 'internal', 1);
1598 if ($result < 0 && $result != -2) { // -2 means already exists
1599 $error++;
1600 }
1601 }
1602
1603 $this->db->commit();
1604 return 1;
1605 } else {
1606 $this->db->rollback();
1607 return -1;
1608 }
1609 } else {
1610 dol_syslog("You don't have permission to validate supplier proposal", LOG_WARNING);
1611 return -2;
1612 }
1613 }
1614
1615 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1624 public function set_date_livraison($user, $delivery_date)
1625 {
1626 // phpcs:enable
1627 return $this->setDeliveryDate($user, $delivery_date);
1628 }
1629
1637 public function setDeliveryDate($user, $delivery_date)
1638 {
1639 if ($user->hasRight('supplier_proposal', 'creer')) {
1640 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1641 $sql .= " SET date_livraison = ".(isDolTms($delivery_date) ? "'".$this->db->idate($delivery_date)."'" : 'null');
1642 $sql .= " WHERE rowid = ".((int) $this->id);
1643
1644 if ($this->db->query($sql)) {
1645 $this->delivery_date = $delivery_date;
1646 return 1;
1647 } else {
1648 $this->error = $this->db->error();
1649 dol_syslog(get_class($this)."::setDeliveryDate Erreur SQL");
1650 return -1;
1651 }
1652 }
1653 return 0;
1654 }
1655
1656 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1664 /*
1665 public function set_remise_percent($user, $remise)
1666 {
1667 // phpcs:enable
1668 $remise = trim($remise) ?trim($remise) : 0;
1669
1670 if ($user->hasRight('supplier_proposal', 'creer')) {
1671 $remise = price2num($remise, 2);
1672
1673 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal SET remise_percent = ".((float) $remise);
1674 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1675
1676 if ($this->db->query($sql)) {
1677 $this->remise_percent = ((float) $remise);
1678 $this->update_price(1);
1679 return 1;
1680 } else {
1681 $this->error = $this->db->error();
1682 return -1;
1683 }
1684 }
1685 return 0;
1686 }
1687 */
1688
1689 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1697 /*
1698 public function set_remise_absolue($user, $remise)
1699 {
1700 // phpcs:enable
1701 if (empty($remise)) {
1702 $remise = 0;
1703 }
1704
1705 $remise = price2num($remise);
1706
1707 if ($user->hasRight('supplier_proposal', 'creer')) {
1708 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1709 $sql .= " SET remise_absolue = ".((float) $remise);
1710 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1711
1712 if ($this->db->query($sql)) {
1713 $this->remise_absolue = $remise;
1714 $this->update_price(1);
1715 return 1;
1716 } else {
1717 $this->error = $this->db->error();
1718 return -1;
1719 }
1720 }
1721 return 0;
1722 }
1723 */
1724
1725
1735 public function reopen($user, $status, $note = '', $notrigger = 0)
1736 {
1737 global $langs, $conf;
1738
1739 $error = 0;
1740
1741 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1742 $sql .= " SET fk_statut = ".((int) $status).",";
1743 if (!empty($note)) {
1744 $sql .= " note_private = '".$this->db->escape($note)."',";
1745 }
1746 $sql .= " date_cloture = NULL, fk_user_cloture = NULL";
1747 $sql .= " WHERE rowid = ".((int) $this->id);
1748
1749 $this->db->begin();
1750
1751 dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
1752 $resql = $this->db->query($sql);
1753 if (!$resql) {
1754 $error++;
1755 $this->errors[] = "Error ".$this->db->lasterror();
1756 }
1757 if (!$error) {
1758 if (!$notrigger) {
1759 // Call trigger
1760 $result = $this->call_trigger('PROPOSAL_SUPPLIER_REOPEN', $user);
1761 if ($result < 0) {
1762 $error++;
1763 }
1764 // End call triggers
1765 }
1766 }
1767
1768 // Commit or rollback
1769 if ($error) {
1770 if (!empty($this->errors)) {
1771 foreach ($this->errors as $errmsg) {
1772 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1773 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1774 }
1775 }
1776 $this->db->rollback();
1777 return -1 * $error;
1778 } else {
1779 $this->statut = $status;
1780 $this->status = $status;
1781
1782 $this->db->commit();
1783 return 1;
1784 }
1785 }
1786
1787
1796 public function cloture($user, $status, $note)
1797 {
1798 global $langs, $conf;
1799 $hidedetails = 0;
1800 $hidedesc = 0;
1801 $hideref = 0;
1802
1803 $error = 0;
1804 $now = dol_now();
1805
1806 $this->db->begin();
1807
1808 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1809 $sql .= " SET fk_statut = ".((int) $status).", note_private = '".$this->db->escape($note)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".((int) $user->id);
1810 $sql .= " WHERE rowid = ".((int) $this->id);
1811
1812 $resql = $this->db->query($sql);
1813 if ($resql) {
1814 $this->statut = $status;
1815 $this->status = $status;
1816
1817 $modelpdf = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED', (empty($this->model_pdf) ? '' : $this->model_pdf));
1818 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_REFUSED';
1819
1820 if ($status == 2) {
1821 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_SIGNED';
1822 $modelpdf = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL', (empty($this->model_pdf) ? '' : $this->model_pdf));
1823
1824 if (getDolGlobalString('SUPPLIER_PROPOSAL_UPDATE_PRICE_ON_SUPPlIER_PROPOSAL')) { // TODO This option was not tested correctly. Error if product ref does not exists
1825 $result = $this->updateOrCreatePriceFournisseur($user);
1826 }
1827 }
1828 if ($status == 4) {
1829 $triggerName = 'PROPOSAL_SUPPLIER_CLASSIFY_BILLED';
1830 }
1831
1832 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1833 // Define output language
1834 $outputlangs = $langs;
1835 if (getDolGlobalInt('MAIN_MULTILANGS')) {
1836 $outputlangs = new Translate("", $conf);
1837 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
1838 $outputlangs->setDefaultLang($newlang);
1839 }
1840
1841 $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1842 }
1843
1844 // Call trigger
1845 $result = $this->call_trigger($triggerName, $user);
1846 if ($result < 0) {
1847 $error++;
1848 }
1849 // End call triggers
1850
1851 if (!$error) {
1852 $this->db->commit();
1853 return 1;
1854 } else {
1855 $this->db->rollback();
1856 return -1;
1857 }
1858 } else {
1859 $this->error = $this->db->lasterror();
1860 $this->errors[] = $this->db->lasterror();
1861 $this->db->rollback();
1862 return -1;
1863 }
1864 }
1865
1872 public function updateOrCreatePriceFournisseur($user)
1873 {
1874 global $conf;
1875
1876 dol_syslog(get_class($this)."::updateOrCreatePriceFournisseur", LOG_DEBUG);
1877 foreach ($this->lines as $product) {
1878 if ($product->subprice <= 0) {
1879 continue;
1880 }
1881 $productsupplier = new ProductFournisseur($this->db);
1882
1883 $multicurrency_tx = 1;
1884 $fk_multicurrency = 0;
1885
1886 if (empty($this->thirdparty)) {
1887 $this->fetch_thirdparty();
1888 }
1889
1890 $ref_fourn = $product->ref_fourn;
1891 if (empty($ref_fourn)) {
1892 $ref_fourn = $product->ref_supplier;
1893 }
1894 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1895 list($fk_multicurrency, $multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $product->multicurrency_code);
1896 }
1897 $productsupplier->id = $product->fk_product;
1898
1899 $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);
1900 }
1901
1902 return 1;
1903 }
1904
1913 public function updatePriceFournisseur($idProductFournPrice, $product, $user)
1914 {
1915 $price = price2num($product->subprice * $product->qty, 'MU');
1916 $unitPrice = price2num($product->subprice, 'MU');
1917
1918 $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);
1919
1920 $resql = $this->db->query($sql);
1921 if (!$resql) {
1922 $this->error = $this->db->error();
1923 $this->db->rollback();
1924 return -1;
1925 }
1926 return 1;
1927 }
1928
1936 public function createPriceFournisseur($product, $user)
1937 {
1938 global $conf;
1939
1940 $price = price2num($product->subprice * $product->qty, 'MU');
1941 $qty = price2num($product->qty);
1942 $unitPrice = price2num($product->subprice, 'MU');
1943
1944 $now = dol_now();
1945
1946 $values = array(
1947 "'".$this->db->idate($now)."'",
1948 (int) $product->fk_product,
1949 (int) $this->thirdparty->id,
1950 "'".$this->db->escape($product->ref_fourn)."'",
1951 (float) $price,
1952 (float) $qty,
1953 (float) $unitPrice,
1954 (float) $product->tva_tx,
1955 (int) $user->id
1956 );
1957 if (isModEnabled("multicurrency")) {
1958 if (!empty($product->multicurrency_code)) {
1959 include_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
1960 $multicurrency = new MultiCurrency($this->db); //need to fetch because empty fk_multicurrency and rate
1961 $multicurrency->fetch(0, $product->multicurrency_code);
1962 if (!empty($multicurrency->id)) {
1963 $values[] = (int) $multicurrency->id;
1964 $values[] = "'".$this->db->escape($product->multicurrency_code)."'";
1965 $values[] = (float) $product->multicurrency_subprice;
1966 $values[] = (float) $product->multicurrency_total_ht;
1967 $values[] = (float) $multicurrency->rate->rate;
1968 } else {
1969 for ($i = 0; $i < 5; $i++) {
1970 $values[] = 'NULL';
1971 }
1972 }
1973 }
1974 }
1975
1976 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'product_fournisseur_price ';
1977 $sql .= '(datec, fk_product, fk_soc, ref_fourn, price, quantity, unitprice, tva_tx, fk_user';
1978 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1979 $sql .= ',fk_multicurrency, multicurrency_code, multicurrency_unitprice, multicurrency_price, multicurrency_tx';
1980 }
1981 $sql .= ') VALUES ('.implode(',', $values).')';
1982
1983 $resql = $this->db->query($sql);
1984 if (!$resql) {
1985 $this->error = $this->db->error();
1986 $this->db->rollback();
1987 return -1;
1988 }
1989 return 1;
1990 }
1991
1992 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1999 public function setDraft($user)
2000 {
2001 // phpcs:enable
2002 global $conf, $langs;
2003
2004 $error = 0;
2005
2006 if ($this->status == self::STATUS_DRAFT) {
2007 dol_syslog(get_class($this)."::setDraft already draft status", LOG_WARNING);
2008 return 0;
2009 }
2010
2011 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
2012 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
2013 $sql .= " WHERE rowid = ".((int) $this->id);
2014
2015 if ($this->db->query($sql)) {
2016 if (!$error) {
2017 $this->oldcopy = clone $this;
2018 }
2019
2020 if (!$error) {
2021 // Call trigger
2022 $result = $this->call_trigger('PROPOSAL_SUPPLIER_UNVALIDATE', $user);
2023 if ($result < 0) {
2024 $error++;
2025 }
2026 }
2027
2028 if (!$error) {
2029 $this->status = self::STATUS_DRAFT;
2030 $this->statut = self::STATUS_DRAFT; // deprecated
2031 $this->db->commit();
2032 return 1;
2033 } else {
2034 $this->db->rollback();
2035 return -1;
2036 }
2037 } else {
2038 return -1;
2039 }
2040 }
2041
2042
2043 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2057 public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datec', $sortorder = 'DESC')
2058 {
2059 // phpcs:enable
2060 global $user;
2061
2062 $ga = array();
2063
2064 $search_sale = 0;
2065 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2066 $search_sale = $user->id;
2067 }
2068
2069 $sql = "SELECT s.rowid, s.nom as name, s.client,";
2070 $sql .= " p.rowid as supplier_proposalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
2071 $sql .= " p.datep as dp, p.fin_validite as datelimite";
2072 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."supplier_proposal as p, ".MAIN_DB_PREFIX."c_propalst as c";
2073 $sql .= " WHERE p.entity IN (".getEntity('supplier_proposal').")";
2074 $sql .= " AND p.fk_soc = s.rowid";
2075 $sql .= " AND p.fk_statut = c.id";
2076 if ($socid) {
2077 $sql .= " AND s.rowid = ".((int) $socid);
2078 }
2079 if ($draft) {
2080 $sql .= " AND p.fk_statut = 0";
2081 }
2082 if ($notcurrentuser > 0) {
2083 $sql .= " AND p.fk_user_author <> ".((int) $user->id);
2084 }
2085 // Search on sale representative
2086 if ($search_sale && $search_sale != '-1') {
2087 if ($search_sale == -2) {
2088 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
2089 } elseif ($search_sale > 0) {
2090 $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).")";
2091 }
2092 }
2093 $sql .= $this->db->order($sortfield, $sortorder);
2094 $sql .= $this->db->plimit($limit, $offset);
2095
2096 $result = $this->db->query($sql);
2097 if ($result) {
2098 $num = $this->db->num_rows($result);
2099 if ($num) {
2100 $i = 0;
2101 while ($i < $num) {
2102 $obj = $this->db->fetch_object($result);
2103
2104 if ($shortlist == 1) {
2105 $ga[$obj->supplier_proposalid] = $obj->ref;
2106 } elseif ($shortlist == 2) {
2107 $ga[$obj->supplier_proposalid] = $obj->ref.' ('.$obj->name.')';
2108 } else {
2109 $ga[$i]['id'] = $obj->supplier_proposalid;
2110 $ga[$i]['ref'] = $obj->ref;
2111 $ga[$i]['name'] = $obj->name;
2112 }
2113
2114 $i++;
2115 }
2116 }
2117 return $ga;
2118 } else {
2119 dol_print_error($this->db);
2120 return -1;
2121 }
2122 }
2123
2131 public function delete($user, $notrigger = 0)
2132 {
2133 global $conf, $langs;
2134 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2135
2136 $error = 0;
2137
2138 $this->db->begin();
2139
2140 if (!$notrigger) {
2141 // Call trigger
2142 $result = $this->call_trigger('PROPOSAL_SUPPLIER_DELETE', $user);
2143 if ($result < 0) {
2144 $error++;
2145 }
2146 // End call triggers
2147 }
2148
2149 // Remove linked categories.
2150 if (!$error) {
2151 $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_supplier_proposal";
2152 $sql .= " WHERE fk_supplier_proposal = ".((int) $this->id);
2153
2154 $result = $this->db->query($sql);
2155 if (!$result) {
2156 $error++;
2157 $this->error = $this->db->lasterror();
2158 $this->errors[] = $this->error;
2159 }
2160 }
2161
2162 if (!$error) {
2163 $main = MAIN_DB_PREFIX.'supplier_proposaldet';
2164 $ef = $main."_extrafields";
2165 $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_supplier_proposal = ".((int) $this->id).")";
2166 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet WHERE fk_supplier_proposal = ".((int) $this->id);
2167 if ($this->db->query($sql)) {
2168 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposal WHERE rowid = ".((int) $this->id);
2169 if ($this->db->query($sqlef) && $this->db->query($sql)) {
2170 // Delete linked object
2171 $res = $this->deleteObjectLinked();
2172 if ($res < 0) {
2173 $error++;
2174 }
2175
2176 if (!$error) {
2177 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2178 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
2179 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
2180
2181 // We remove directory
2182 $ref = dol_sanitizeFileName($this->ref);
2183 if ($conf->supplier_proposal->dir_output && !empty($this->ref)) {
2184 $dir = $conf->supplier_proposal->dir_output."/".$ref;
2185 $file = $dir."/".$ref.".pdf";
2186 if (file_exists($file)) {
2187 dol_delete_preview($this);
2188
2189 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2190 $this->error = 'ErrorFailToDeleteFile';
2191 $this->errors = array('ErrorFailToDeleteFile');
2192 $this->db->rollback();
2193 return 0;
2194 }
2195 }
2196 if (file_exists($dir)) {
2197 $res = @dol_delete_dir_recursive($dir);
2198 if (!$res) {
2199 $this->error = 'ErrorFailToDeleteDir';
2200 $this->errors = array('ErrorFailToDeleteDir');
2201 $this->db->rollback();
2202 return 0;
2203 }
2204 }
2205 }
2206 }
2207
2208 // Removed extrafields
2209 if (!$error) {
2210 $result = $this->deleteExtraFields();
2211 if ($result < 0) {
2212 $error++;
2213 $errorflag = -4;
2214 dol_syslog(get_class($this)."::delete erreur ".$errorflag." ".$this->error, LOG_ERR);
2215 }
2216 }
2217
2218 if (!$error) {
2219 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
2220 $this->db->commit();
2221 return 1;
2222 } else {
2223 $this->error = $this->db->lasterror();
2224 $this->db->rollback();
2225 return 0;
2226 }
2227 } else {
2228 $this->error = $this->db->lasterror();
2229 $this->db->rollback();
2230 return -3;
2231 }
2232 } else {
2233 $this->error = $this->db->lasterror();
2234 $this->db->rollback();
2235 return -2;
2236 }
2237 } else {
2238 $this->db->rollback();
2239 return -1;
2240 }
2241 }
2242
2249 public function info($id)
2250 {
2251 $sql = "SELECT c.rowid, GREATEST(c.tms, COALESCE(cef.tms, c.tms)) as date_modification,";
2252 $sql .= " c.datec as date_creation, c.date_valid as date_validation, c.date_cloture as date_closure,";
2253 $sql .= " c.fk_user_author, c.fk_user_modif, c.fk_user_valid, c.fk_user_cloture";
2254 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as c";
2255 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."supplier_proposal_extrafields as cef ON cef.fk_object=c.rowid";
2256 $sql .= " WHERE c.rowid = ".((int) $id);
2257
2258 $result = $this->db->query($sql);
2259
2260 if ($result) {
2261 if ($this->db->num_rows($result)) {
2262 $obj = $this->db->fetch_object($result);
2263
2264 $this->id = $obj->rowid;
2265
2266 $this->date_creation = $this->db->jdate($obj->date_creation);
2267 $this->date_modification = empty($obj->date_modification) ? '' : $this->db->jdate($obj->date_modification);
2268 $this->date_validation = $this->db->jdate($obj->date_validation);
2269 $this->date_cloture = $this->db->jdate($obj->date_closure);
2270
2271 $this->user_creation_id = $obj->fk_user_author;
2272 $this->user_modification_id = $obj->fk_user_modif;
2273 $this->user_validation_id = $obj->fk_user_valid;
2274 $this->user_closing_id = $obj->fk_user_cloture;
2275 }
2276 $this->db->free($result);
2277 } else {
2278 dol_print_error($this->db);
2279 }
2280 }
2281
2282
2289 public function getLibStatut($mode = 0)
2290 {
2291 return $this->LibStatut((isset($this->status) ? $this->status : $this->statut), $mode);
2292 }
2293
2294 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2302 public function LibStatut($status, $mode = 1)
2303 {
2304 // phpcs:enable
2305
2306 // Init/load array of translation of status
2307 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
2308 global $langs;
2309 $langs->load("supplier_proposal");
2310 $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraft");
2311 $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidated");
2312 $this->labelStatus[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSigned");
2313 $this->labelStatus[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSigned");
2314 $this->labelStatus[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosed");
2315 $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraftShort");
2316 $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidatedShort");
2317 $this->labelStatusShort[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSignedShort");
2318 $this->labelStatusShort[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSignedShort");
2319 $this->labelStatusShort[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosedShort");
2320 }
2321
2322 $statusnew = '';
2323 if ($status == self::STATUS_DRAFT) {
2324 $statusnew = 'status0';
2325 } elseif ($status == self::STATUS_VALIDATED) {
2326 $statusnew = 'status1';
2327 } elseif ($status == self::STATUS_SIGNED) {
2328 $statusnew = 'status4';
2329 } elseif ($status == self::STATUS_NOTSIGNED) {
2330 $statusnew = 'status9';
2331 } elseif ($status == self::STATUS_CLOSE) {
2332 $statusnew = 'status6';
2333 }
2334
2335 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusnew, $mode);
2336 }
2337
2338
2339 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2347 public function load_board($user, $mode)
2348 {
2349 // phpcs:enable
2350 global $conf, $langs;
2351
2352 $now = dol_now();
2353
2354 $clause = " WHERE";
2355
2356 $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.date_cloture as datefin";
2357 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2358 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2359 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
2360 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2361 $clause = " AND";
2362 }
2363 $sql .= $clause." p.entity IN (".getEntity('supplier_proposal').")";
2364 if ($mode == 'opened') {
2365 $sql .= " AND p.fk_statut = 1";
2366 }
2367 if ($mode == 'signed') {
2368 $sql .= " AND p.fk_statut = 2";
2369 }
2370 if ($user->socid) {
2371 $sql .= " AND p.fk_soc = ".((int) $user->socid);
2372 }
2373
2374 $resql = $this->db->query($sql);
2375 if ($resql) {
2376 $label = $labelShort = '';
2377 $status = '';
2378 $delay_warning = 0;
2379 if ($mode == 'opened') {
2380 $delay_warning = !empty($conf->supplier_proposal->cloture->warning_delay) ? $conf->supplier_proposal->cloture->warning_delay : 0;
2381 $status = self::STATUS_VALIDATED;
2382 $label = $langs->trans("SupplierProposalsToClose");
2383 $labelShort = $langs->trans("ToAcceptRefuse");
2384 }
2385 if ($mode == 'signed') {
2386 $delay_warning = !empty($conf->supplier_proposal->facturation->warning_delay) ? $conf->supplier_proposal->facturation->warning_delay : 0;
2387 $status = self::STATUS_SIGNED;
2388 $label = $langs->trans("SupplierProposalsToProcess"); // May be billed or ordered
2389 $labelShort = $langs->trans("ToClose");
2390 }
2391
2392 $response = new WorkboardResponse();
2393 $response->warning_delay = $delay_warning / 60 / 60 / 24;
2394 $response->label = $label;
2395 $response->labelShort = $labelShort;
2396 $response->url = DOL_URL_ROOT.'/supplier_proposal/list.php?search_status='.$status;
2397 $response->img = img_object('', "propal");
2398
2399 // This assignment in condition is not a bug. It allows walking the results.
2400 while ($obj = $this->db->fetch_object($resql)) {
2401 $response->nbtodo++;
2402 if ($mode == 'opened') {
2403 $datelimit = $this->db->jdate($obj->datefin);
2404 if ($datelimit < ($now - $delay_warning)) {
2405 $response->nbtodolate++;
2406 }
2407 }
2408 // TODO Definir regle des propales a facturer en retard
2409 // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
2410 }
2411 return $response;
2412 } else {
2413 $this->error = $this->db->lasterror();
2414 return -1;
2415 }
2416 }
2417
2418
2427 public function initAsSpecimen($param = array())
2428 {
2429 global $conf, $langs;
2430
2431 // Load array of products prodids
2432 $num_prods = 0;
2433 $prodids = array();
2434 $sql = "SELECT rowid";
2435 $sql .= " FROM ".MAIN_DB_PREFIX."product";
2436 $sql .= " WHERE entity IN (".getEntity('product').")";
2437 if (array_key_exists('tobuy', $param)) {
2438 $sql .= " AND tobuy = ".((int) $param['tobuy']);
2439 }
2440 $sql .= $this->db->plimit(100);
2441
2442 $resql = $this->db->query($sql);
2443 if ($resql) {
2444 $num_prods = $this->db->num_rows($resql);
2445 $i = 0;
2446 while ($i < $num_prods) {
2447 $i++;
2448 $row = $this->db->fetch_row($resql);
2449 $prodids[$i] = $row[0];
2450 }
2451 }
2452
2453 // Initialise parameters
2454 $this->id = 0;
2455 $this->ref = 'SPECIMEN';
2456 $this->ref_supplier = 'NEMICEPS';
2457 $this->specimen = 1;
2458 $this->socid = 1;
2459 $this->date = time();
2460 $this->cond_reglement_id = 1;
2461 $this->cond_reglement_code = 'RECEP';
2462 $this->mode_reglement_id = 7;
2463 $this->mode_reglement_code = 'CHQ';
2464 $this->note_public = 'This is a comment (public)';
2465 $this->note_private = 'This is a comment (private)';
2466
2467 $this->multicurrency_tx = 1;
2468 $this->multicurrency_code = getDolCurrency();
2469
2470 // Lines
2471 $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)
2472 $xnbp = 0;
2473 while ($xnbp < $nbp) {
2474 $line = new SupplierProposalLine($this->db);
2475 $line->desc = $langs->trans("Description")." ".$xnbp;
2476 $line->qty = 1;
2477 $line->subprice = 100;
2478 $line->tva_tx = 19.6;
2479 $line->localtax1_tx = 0;
2480 $line->localtax2_tx = 0;
2481 if ($xnbp == 2) {
2482 $line->total_ht = 50;
2483 $line->total_ttc = 59.8;
2484 $line->total_tva = 9.8;
2485 $line->remise_percent = 50;
2486 } else {
2487 $line->total_ht = 100;
2488 $line->total_ttc = 119.6;
2489 $line->total_tva = 19.6;
2490 $line->remise_percent = 00;
2491 }
2492
2493 if ($num_prods > 0) {
2494 $prodid = mt_rand(1, $num_prods);
2495 $line->fk_product = $prodids[$prodid];
2496 }
2497
2498 $this->lines[$xnbp] = $line;
2499
2500 $this->total_ht += $line->total_ht;
2501 $this->total_tva += $line->total_tva;
2502 $this->total_ttc += $line->total_ttc;
2503
2504 $xnbp++;
2505 }
2506
2507 return 1;
2508 }
2509
2515 public function loadStateBoard()
2516 {
2517 global $user;
2518
2519 $this->nb = array();
2520 $clause = "WHERE";
2521
2522 $sql = "SELECT count(p.rowid) as nb";
2523 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2524 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
2525 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2526 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2527 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2528 $clause = "AND";
2529 }
2530 $sql .= " ".$clause." p.entity IN (".getEntity('supplier_proposal').")";
2531
2532 $resql = $this->db->query($sql);
2533 if ($resql) {
2534 // This assignment in condition is not a bug. It allows walking the results.
2535 while ($obj = $this->db->fetch_object($resql)) {
2536 $this->nb["supplier_proposals"] = $obj->nb;
2537 }
2538 $this->db->free($resql);
2539 return 1;
2540 } else {
2541 dol_print_error($this->db);
2542 $this->error = $this->db->lasterror();
2543 return -1;
2544 }
2545 }
2546
2547
2555 public function getNextNumRef($soc)
2556 {
2557 global $conf, $db, $langs;
2558 $langs->load("supplier_proposal");
2559
2560 if (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON')) {
2561 $mybool = false;
2562
2563 $file = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') . ".php";
2564 $classname = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
2565
2566 // Include file with class
2567 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2568 foreach ($dirmodels as $reldir) {
2569 $dir = dol_buildpath($reldir."core/modules/supplier_proposal/");
2570
2571 // Load file with numbering class (if found)
2572 $mybool = ((bool) @include_once $dir.$file) || $mybool;
2573 }
2574
2575 if (!$mybool) {
2576 dol_print_error(null, "Failed to include file ".$file);
2577 return '';
2578 }
2579
2580 $obj = new $classname();
2581 '@phan-var-force ModeleNumRefSupplierProposal $obj';
2583 $numref = "";
2584 $numref = $obj->getNextValue($soc, $this);
2585
2586 if ($numref != "") {
2587 return $numref;
2588 } else {
2589 $this->error = $obj->error;
2590 return "";
2591 }
2592 } else {
2593 $langs->load("errors");
2594 print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("SupplierProposal"));
2595 return "";
2596 }
2597 }
2598
2605 public function getTooltipContentArray($params)
2606 {
2607 global $conf, $langs;
2608
2609 $langs->load('supplier_proposal');
2610
2611 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2612 return ['optimize' => $langs->trans("ShowSupplierProposal")];
2613 }
2614
2615 //$option = $params['option'] ?? '';
2616 $datas = [];
2617
2618 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("SupplierProposal").'</u>';
2619 if (isset($this->status)) {
2620 $datas['picto'] .= ' '.$this->getLibStatut(5);
2621 }
2622 if (!empty($this->ref)) {
2623 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2624 }
2625 if (!empty($this->ref_fourn)) {
2626 $datas['ref_supplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_fourn;
2627 }
2628 if (!empty($this->total_ht)) {
2629 $datas['amount_ht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, getDolCurrency());
2630 }
2631 if (!empty($this->total_tva)) {
2632 $datas['amount_vat'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, getDolCurrency());
2633 }
2634 if (!empty($this->total_ttc)) {
2635 $datas['amount_ttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, getDolCurrency());
2636 }
2637
2638 return $datas;
2639 }
2640
2652 public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2653 {
2654 global $langs, $conf, $user, $hookmanager;
2655
2656 if (!empty($conf->dol_no_mouse_hover)) {
2657 $notooltip = 1; // Force disable tooltips
2658 }
2659
2660 $url = '';
2661 $result = '';
2662 $params = [
2663 'id' => $this->id,
2664 'objecttype' => $this->element,
2665 'option' => $option,
2666 ];
2667 $classfortooltip = 'classfortooltip';
2668 $dataparams = '';
2669 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2670 $classfortooltip = 'classforajaxtooltip';
2671 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
2672 $label = '';
2673 } else {
2674 $label = implode($this->getTooltipContentArray($params));
2675 }
2676
2677 if ($option == '') {
2678 $url = DOL_URL_ROOT.'/supplier_proposal/card.php?id='.$this->id.$get_params;
2679 }
2680 if ($option == 'document') {
2681 $url = DOL_URL_ROOT.'/supplier_proposal/document.php?id='.$this->id.$get_params;
2682 }
2683
2684 if ($option !== 'nolink') {
2685 // Add param to save lastsearch_values or not
2686 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2687 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2688 $add_save_lastsearch_values = 1;
2689 }
2690 if ($add_save_lastsearch_values) {
2691 $url .= '&save_lastsearch_values=1';
2692 }
2693 }
2694
2695 $linkclose = '';
2696 if (empty($notooltip) && $user->hasRight('propal', 'lire')) {
2697 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2698 $label = $langs->trans("ShowSupplierProposal");
2699 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
2700 }
2701 $linkclose .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
2702 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
2703 }
2704
2705 $linkstart = '<a href="'.$url.'"';
2706 $linkstart .= $linkclose.'>';
2707 $linkend = '</a>';
2708
2709 $result .= $linkstart;
2710 if ($withpicto) {
2711 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
2712 }
2713 if ($withpicto != 2) {
2714 $result .= $this->ref;
2715 }
2716 $result .= $linkend;
2717
2718 if ($addlinktonotes) {
2719 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2720 if ($txttoshow) {
2721 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2722 $result .= ' <span class="note inline-block">';
2723 $result .= '<a href="'.DOL_URL_ROOT.'/supplier_proposal/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2724 $result .= img_picto('', 'note');
2725 $result .= '</a>';
2726 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
2727 //$result.='</a>';
2728 $result .= '</span>';
2729 }
2730 }
2731 global $action;
2732 $hookmanager->initHooks(array($this->element . 'dao'));
2733 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
2734 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2735 if ($reshook > 0) {
2736 $result = $hookmanager->resPrint;
2737 } else {
2738 $result .= $hookmanager->resPrint;
2739 }
2740 return $result;
2741 }
2742
2748 public function getLinesArray()
2749 {
2750 // For other object, here we call fetch_lines. But fetch_lines does not exists on supplier proposal
2751
2752 $sql = 'SELECT pt.rowid, pt.label as custom_label, pt.description, pt.fk_product, pt.fk_remise_except,';
2753 $sql .= ' pt.qty, pt.tva_tx, pt.vat_src_code, pt.remise_percent, pt.subprice, pt.info_bits,';
2754 $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,';
2755 $sql .= ' pt.product_type, pt.rang, pt.fk_parent_line, pt.extraparams,';
2756 $sql .= ' p.label as product_label, p.ref, p.fk_product_type, p.rowid as prodid,';
2757 $sql .= ' p.description as product_desc, pt.ref_fourn as ref_supplier,';
2758 $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';
2759 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pt';
2760 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pt.fk_product=p.rowid';
2761 $sql .= ' WHERE pt.fk_supplier_proposal = '.((int) $this->id);
2762 $sql .= ' ORDER BY pt.rang ASC, pt.rowid';
2763
2764 dol_syslog(get_class($this).'::getLinesArray', LOG_DEBUG);
2765 $resql = $this->db->query($sql);
2766 if ($resql) {
2767 $num = $this->db->num_rows($resql);
2768 $i = 0;
2769
2770 while ($i < $num) {
2771 $obj = $this->db->fetch_object($resql);
2772
2773 $this->lines[$i] = new SupplierProposalLine($this->db);
2774 $this->lines[$i]->id = $obj->rowid; // for backward compatibility
2775 $this->lines[$i]->rowid = $obj->rowid;
2776
2777 $this->lines[$i]->label = $obj->custom_label;
2778 $this->lines[$i]->description = $obj->description; // deprecated
2779 $this->lines[$i]->desc = $obj->description;
2780
2781 $this->lines[$i]->fk_product = $obj->fk_product;
2782 $this->lines[$i]->ref = $obj->ref;
2783 $this->lines[$i]->product_label = $obj->product_label;
2784 $this->lines[$i]->product_desc = $obj->product_desc;
2785 $this->lines[$i]->fk_product_type = $obj->fk_product_type; // deprecated
2786 $this->lines[$i]->product_type = $obj->product_type;
2787 $this->lines[$i]->qty = $obj->qty;
2788 $this->lines[$i]->subprice = $obj->subprice;
2789 $this->lines[$i]->fk_remise_except = $obj->fk_remise_except;
2790 $this->lines[$i]->remise_percent = $obj->remise_percent;
2791 $this->lines[$i]->tva_tx = $obj->tva_tx;
2792 $this->lines[$i]->vat_src_code = $obj->vat_src_code;
2793 $this->lines[$i]->info_bits = $obj->info_bits;
2794 $this->lines[$i]->total_ht = $obj->total_ht;
2795 $this->lines[$i]->total_tva = $obj->total_tva;
2796 $this->lines[$i]->total_ttc = $obj->total_ttc;
2797 $this->lines[$i]->fk_fournprice = $obj->fk_fournprice;
2798 $marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->lines[$i]->fk_fournprice, $obj->pa_ht);
2799 $this->lines[$i]->pa_ht = $marginInfos[0];
2800 $this->lines[$i]->marge_tx = $marginInfos[1];
2801 $this->lines[$i]->marque_tx = $marginInfos[2];
2802 $this->lines[$i]->fk_parent_line = $obj->fk_parent_line;
2803 $this->lines[$i]->special_code = $obj->special_code;
2804 $this->lines[$i]->rang = $obj->rang;
2805
2806 $this->lines[$i]->ref_fourn = $obj->ref_supplier; // deprecated
2807 $this->lines[$i]->ref_supplier = $obj->ref_supplier;
2808
2809 // Multicurrency
2810 $this->lines[$i]->fk_multicurrency = $obj->fk_multicurrency;
2811 $this->lines[$i]->multicurrency_code = $obj->multicurrency_code;
2812 $this->lines[$i]->multicurrency_subprice = $obj->multicurrency_subprice;
2813 $this->lines[$i]->multicurrency_total_ht = $obj->multicurrency_total_ht;
2814 $this->lines[$i]->multicurrency_total_tva = $obj->multicurrency_total_tva;
2815 $this->lines[$i]->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
2816 $this->lines[$i]->fk_unit = $obj->fk_unit;
2817 $this->lines[$i]->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
2818
2819 $this->lines[$i]->fetch_optionals();
2820
2821 $i++;
2822 }
2823 $this->db->free($resql);
2824
2825 return 1;
2826 } else {
2827 $this->error = $this->db->error();
2828 return -1;
2829 }
2830 }
2831
2843 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2844 {
2845 global $langs;
2846
2847 $langs->load("supplier_proposal");
2848 $outputlangs->load("products");
2849
2850 if (!dol_strlen($modele)) {
2851 $modele = ''; // On supplier documents, template can be empty( no doc generated in this case)
2852
2853 if ($this->model_pdf) {
2854 $modele = $this->model_pdf;
2855 } elseif (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF')) {
2856 $modele = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF');
2857 }
2858 }
2859
2860 if (empty($modele)) {
2861 return 1;
2862 }
2863
2864 $modelpath = "core/modules/supplier_proposal/doc/";
2865
2866 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2867 }
2868
2869
2878 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2879 {
2880 $tables = array(
2881 'supplier_proposal'
2882 );
2883
2884 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2885 }
2886
2895 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
2896 {
2897 $tables = array(
2898 'supplier_proposaldet'
2899 );
2900
2901 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
2902 }
2903
2904
2912 public function getKanbanView($option = '', $arraydata = null)
2913 {
2914 global $langs;
2915
2916 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2917
2918 $return = '<div class="box-flex-item box-flex-grow-zero">';
2919 $return .= '<div class="info-box info-box-sm">';
2920 $return .= '<span class="info-box-icon bg-infobox-action">';
2921 $return .= img_picto('', $this->picto);
2922 //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2923 $return .= '</span>';
2924 $return .= '<div class="info-box-content">';
2925 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
2926 if ($selected >= 0) {
2927 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2928 }
2929 if (property_exists($this, 'socid')) {
2930 $return .= '<span class="info-box-ref"> | '.$this->socid.'</span>';
2931 }
2932 if (property_exists($this, 'delivery_date')) {
2933 $return .= '<br><span class="opacitymedium">'.$langs->trans("DateEnd").'</span> : <span class="info-box-label">'.dol_print_date($this->delivery_date).'</span>';
2934 }
2935 if (property_exists($this, 'total_ttc')) {
2936 $return .= '<br><span class="opacitymedium" >'.$langs->trans("AmountHT").' : </span><span class="info-box-label amount">'.price($this->total_ttc).'</span>';
2937 }
2938 if (method_exists($this, 'getLibStatut')) {
2939 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
2940 }
2941 $return .= '</div>';
2942 $return .= '</div>';
2943 $return .= '</div>';
2944 return $return;
2945 }
2946
2957 public function setCategories($categories)
2958 {
2959 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2960 return parent::setCategoriesCommon($categories, Categorie::TYPE_SUPPLIER_PROPOSAL);
2961 }
2962}
2963
2964
2969{
2973 public $db;
2974
2978 public $error = '';
2979
2983 public $element = 'supplier_proposaldet';
2984
2988 public $table_element = 'supplier_proposaldet';
2989
2993 public $parent_element = 'supplier_proposal';
2994
2998 public $fk_parent_attribute = 'fk_supplier_proposal';
2999
3003 public $oldline;
3004
3008 public $id;
3009
3013 public $fk_supplier_proposal;
3014
3018 public $fk_parent_line;
3019
3023 public $desc; // Description ligne
3024
3028 public $fk_product; // Id produit predefini
3029
3035 public $fk_product_type;
3041 public $product_type = Product::TYPE_PRODUCT;
3042
3046 public $qty;
3050 public $tva_tx;
3054 public $vat_src_code;
3055
3059 public $remise_percent;
3060
3064 public $fk_remise_except;
3065
3069 public $rang = 0;
3070
3074 public $fk_fournprice;
3075
3079 public $pa_ht;
3083 public $marge_tx;
3087 public $marque_tx;
3088
3092 public $special_code; // Tag for special lines (exclusive tags)
3093 // 1: frais de port
3094 // 2: ecotaxe
3095 // 3: option line (when qty = 0)
3096
3100 public $info_bits = 0; // Liste d'options cumulables:
3101 // Bit 0: 0 si TVA normal - 1 if TVA NPR
3102 // Bit 1: 0 ligne normal - 1 if fixed reduction
3103
3107 public $total_ht; // Total HT de la ligne toute quantite et incluant la remise ligne
3111 public $total_tva; // Total TVA de la ligne toute quantite et incluant la remise ligne
3115 public $total_ttc; // Total TTC de la ligne toute quantite et incluant la remise ligne
3116
3120 public $date_start;
3124 public $date_end;
3125
3126 // From llx_product
3132 public $ref;
3133
3138 public $product_ref;
3139
3145 public $libelle;
3146
3151 public $product_label;
3152
3157 public $label;
3158
3163 public $product_desc;
3164
3168 public $localtax1_tx; // Local tax 1
3172 public $localtax2_tx; // Local tax 2
3176 public $localtax1_type; // Local tax 1 type
3180 public $localtax2_type; // Local tax 2 type
3184 public $total_localtax1; // Line total local tax 1
3188 public $total_localtax2; // Line total local tax 2
3189
3193 public $skip_update_total; // Skip update price total for special lines
3194
3198 public $ref_fourn;
3202 public $ref_supplier;
3203
3204 // Multicurrency
3208 public $fk_multicurrency;
3209
3213 public $multicurrency_code;
3217 public $multicurrency_subprice;
3221 public $multicurrency_total_ht;
3225 public $multicurrency_total_tva;
3229 public $multicurrency_total_ttc;
3230
3236 public function __construct($db)
3237 {
3238 $this->db = $db;
3239 }
3240
3247 public function fetch($rowid)
3248 {
3249 $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,';
3250 $sql .= ' pd.date_start, pd.date_end,';
3251 $sql .= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice, pd.subprice_ttc,';
3252 $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,';
3253 $sql .= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
3254 $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
3255 $sql .= ' pd.product_type, pd.ref_fourn as ref_produit_fourn, pd.extraparams,';
3256 $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';
3257 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pd';
3258 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
3259 $sql .= ' WHERE pd.rowid = '.((int) $rowid);
3260
3261 $result = $this->db->query($sql);
3262 if ($result) {
3263 if ($objp = $this->db->fetch_object($result)) {
3264 $this->id = $objp->rowid;
3265 $this->fk_supplier_proposal = $objp->fk_supplier_proposal;
3266 $this->fk_parent_line = $objp->fk_parent_line;
3267 $this->label = $objp->custom_label;
3268 $this->desc = $objp->description;
3269 $this->qty = $objp->qty;
3270
3271 $this->subprice = $objp->subprice;
3272 $this->subprice_ttc = $objp->subprice_ttc;
3273
3274 $this->tva_tx = $objp->tva_tx;
3275 $this->remise_percent = $objp->remise_percent;
3276 $this->fk_remise_except = $objp->fk_remise_except;
3277 $this->fk_product = $objp->fk_product;
3278 $this->info_bits = $objp->info_bits;
3279 $this->date_start = $this->db->jdate($objp->date_start);
3280 $this->date_end = $this->db->jdate($objp->date_end);
3281
3282 $this->total_ht = $objp->total_ht;
3283 $this->total_tva = $objp->total_tva;
3284 $this->total_ttc = $objp->total_ttc;
3285
3286 $this->fk_fournprice = $objp->fk_fournprice;
3287
3288 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
3289 $this->pa_ht = $marginInfos[0];
3290 $this->marge_tx = $marginInfos[1];
3291 $this->marque_tx = $marginInfos[2];
3292
3293 $this->special_code = $objp->special_code;
3294 $this->product_type = $objp->product_type;
3295 $this->rang = $objp->rang;
3296
3297 $this->product_ref = $objp->product_ref;
3298 $this->product_label = $objp->product_label;
3299 $this->product_desc = $objp->product_desc;
3300
3301 $this->ref_fourn = $objp->ref_produit_fourn;
3302
3303 $this->extraparams = !empty($objp->extraparams) ? (array) json_decode($objp->extraparams, true) : array();
3304
3305 // Multicurrency
3306 $this->fk_multicurrency = $objp->fk_multicurrency;
3307 $this->multicurrency_code = $objp->multicurrency_code;
3308 $this->multicurrency_subprice = $objp->multicurrency_subprice;
3309 $this->multicurrency_subprice_ttc = $objp->multicurrency_subprice_ttc;
3310 $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3311 $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3312 $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3313 $this->fk_unit = $objp->fk_unit;
3314
3315 $this->db->free($result);
3316 return 1;
3317 }
3318 return 0;
3319 } else {
3320 dol_print_error($this->db);
3321 return -1;
3322 }
3323 }
3324
3331 public function insert($notrigger = 0)
3332 {
3333 global $user;
3334
3335 $error = 0;
3336
3337 dol_syslog(get_class($this)."::insert rang=".$this->rang);
3338
3339 // Clean parameters
3340 if (empty($this->tva_tx)) {
3341 $this->tva_tx = 0;
3342 }
3343 if (empty($this->vat_src_code)) {
3344 $this->vat_src_code = '';
3345 }
3346 if (empty($this->localtax1_tx)) {
3347 $this->localtax1_tx = 0;
3348 }
3349 if (empty($this->localtax2_tx)) {
3350 $this->localtax2_tx = 0;
3351 }
3352 if (empty($this->localtax1_type)) {
3353 $this->localtax1_type = '';
3354 }
3355 if (empty($this->localtax2_type)) {
3356 $this->localtax2_type = '';
3357 }
3358 if (empty($this->total_localtax1)) {
3359 $this->total_localtax1 = 0;
3360 }
3361 if (empty($this->total_localtax2)) {
3362 $this->total_localtax2 = 0;
3363 }
3364 if (empty($this->rang)) {
3365 $this->rang = 0;
3366 }
3367 if (empty($this->remise_percent)) {
3368 $this->remise_percent = 0;
3369 }
3370 if (empty($this->info_bits)) {
3371 $this->info_bits = 0;
3372 }
3373 if (empty($this->special_code)) {
3374 $this->special_code = 0;
3375 }
3376 if (empty($this->fk_parent_line)) {
3377 $this->fk_parent_line = 0;
3378 }
3379 if (empty($this->fk_fournprice)) {
3380 $this->fk_fournprice = 0;
3381 }
3382 if (empty($this->fk_unit)) {
3383 $this->fk_unit = 0;
3384 }
3385 if (empty($this->subprice)) {
3386 $this->subprice = 0;
3387 }
3388
3389 if (empty($this->pa_ht)) {
3390 $this->pa_ht = 0;
3391 }
3392
3393 // if buy price not defined, define buyprice as configured in margin admin
3394 if ($this->pa_ht == 0) {
3395 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3396 if ($result < 0) {
3397 return $result;
3398 } else {
3399 $this->pa_ht = $result;
3400 }
3401 }
3402
3403 // Check parameters
3404 if ($this->product_type < 0) {
3405 return -1;
3406 }
3407
3408 $this->db->begin();
3409
3410 // Insert line into database
3411 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'supplier_proposaldet';
3412 $sql .= ' (fk_supplier_proposal, fk_parent_line, label, description, fk_product, product_type,';
3413 $sql .= ' date_start, date_end,';
3414 $sql .= ' fk_remise_except, qty, tva_tx, vat_src_code, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3415 $sql .= ' subprice, subprice_ttc, remise_percent, ';
3416 $sql .= ' info_bits, ';
3417 $sql .= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
3418 $sql .= ' ref_fourn,';
3419 $sql .= ' fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_subprice_ttc, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc, fk_unit)';
3420 $sql .= " VALUES (".$this->fk_supplier_proposal.",";
3421 $sql .= " ".($this->fk_parent_line > 0 ? ((int) $this->fk_parent_line) : "null").",";
3422 $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
3423 $sql .= " '".$this->db->escape($this->desc)."',";
3424 $sql .= " ".($this->fk_product ? ((int) $this->fk_product) : "null").",";
3425 $sql .= " ".((int) $this->product_type).",";
3426 $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3427 $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3428 $sql .= " ".($this->fk_remise_except ? ((int) $this->fk_remise_except) : "null").",";
3429 $sql .= " ".(float) price2num($this->qty, 'MS').",";
3430 $sql .= " ".(float) price2num($this->tva_tx).",";
3431 $sql .= " '".$this->db->escape($this->vat_src_code)."',";
3432 $sql .= " ".(float) price2num($this->localtax1_tx).",";
3433 $sql .= " ".(float) price2num($this->localtax2_tx).",";
3434 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3435 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3436 $sql .= " ".(float) price2num($this->subprice, 'MU') .",";
3437 $sql .= " ".(float) price2num($this->subprice_ttc, 'MU') .",";
3438 $sql .= " ".((float) $this->remise_percent).",";
3439 $sql .= " ".(isset($this->info_bits) ? ((int) $this->info_bits) : "null").",";
3440 $sql .= " ".(float) price2num($this->total_ht, 'MT').",";
3441 $sql .= " ".(float) price2num($this->total_tva, 'MT').",";
3442 $sql .= " ".(float) price2num($this->total_localtax1, 'MT').",";
3443 $sql .= " ".(float) price2num($this->total_localtax2, 'MT').",";
3444 $sql .= " ".(float) price2num($this->total_ttc, 'MT').",";
3445 $sql .= " ".(!empty($this->fk_fournprice) ? ((int) $this->fk_fournprice) : "null").",";
3446 $sql .= " ".(isset($this->pa_ht) ? (float) price2num($this->pa_ht, 'MU') : "null").",";
3447 $sql .= ' '.((int) $this->special_code).',';
3448 $sql .= ' '.((int) $this->rang).',';
3449 $sql .= " '".$this->db->escape($this->ref_fourn)."'";
3450 $sql .= ", ".($this->fk_multicurrency > 0 ? ((int) $this->fk_multicurrency) : 'null');
3451 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3452 $sql .= ", ".(float) price2num($this->multicurrency_subprice, 'CU');
3453 $sql .= ", ".(float) price2num($this->multicurrency_subprice_ttc, 'CU');
3454 $sql .= ", ".(float) price2num($this->multicurrency_total_ht, 'CT');
3455 $sql .= ", ".(float) price2num($this->multicurrency_total_tva, 'CT');
3456 $sql .= ", ".(float) price2num($this->multicurrency_total_ttc, 'CT');
3457 $sql .= ", ".($this->fk_unit ? ((int) $this->fk_unit) : 'null');
3458 $sql .= ')';
3459
3460 dol_syslog(get_class($this).'::insert', LOG_DEBUG);
3461 $resql = $this->db->query($sql);
3462 if ($resql) {
3463 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'supplier_proposaldet');
3464
3465 if (!$error) {
3466 $result = $this->insertExtraFields();
3467 if ($result < 0) {
3468 $error++;
3469 }
3470 }
3471
3472 if (!$error && !$notrigger) {
3473 // Call trigger
3474 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_INSERT', $user);
3475 if ($result < 0) {
3476 $this->db->rollback();
3477 return -1;
3478 }
3479 // End call triggers
3480 }
3481
3482 $this->db->commit();
3483 return 1;
3484 } else {
3485 $this->error = $this->db->error()." sql=".$sql;
3486 $this->db->rollback();
3487 return -1;
3488 }
3489 }
3490
3497 public function delete($user)
3498 {
3499 $error = 0;
3500
3501 $this->db->begin();
3502
3503 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet";
3504 $sql .= " WHERE rowid = ".((int) $this->id);
3505
3506 if ($this->db->query($sql)) {
3507 // Remove extrafields
3508 if (!$error) {
3509 $result = $this->deleteExtraFields();
3510 if ($result < 0) {
3511 $error++;
3512 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3513 }
3514 }
3515
3516 // Call trigger
3517 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_DELETE', $user);
3518 if ($result < 0) {
3519 $this->db->rollback();
3520 return -1;
3521 }
3522 // End call triggers
3523
3524 $this->db->commit();
3525
3526 return 1;
3527 } else {
3528 $this->error = $this->db->error()." sql=".$sql;
3529 $this->db->rollback();
3530 return -1;
3531 }
3532 }
3533
3540 public function update($notrigger = 0)
3541 {
3542 global $user;
3543
3544 $error = 0;
3545
3546 // Clean parameters
3547 if (empty($this->tva_tx)) {
3548 $this->tva_tx = 0;
3549 }
3550 if (empty($this->localtax1_tx)) {
3551 $this->localtax1_tx = 0;
3552 }
3553 if (empty($this->localtax2_tx)) {
3554 $this->localtax2_tx = 0;
3555 }
3556 if (empty($this->total_localtax1)) {
3557 $this->total_localtax1 = 0;
3558 }
3559 if (empty($this->total_localtax2)) {
3560 $this->total_localtax2 = 0;
3561 }
3562 if (empty($this->localtax1_type)) {
3563 $this->localtax1_type = '';
3564 }
3565 if (empty($this->localtax2_type)) {
3566 $this->localtax2_type = '';
3567 }
3568 if (empty($this->marque_tx)) {
3569 $this->marque_tx = 0;
3570 }
3571 if (empty($this->marge_tx)) {
3572 $this->marge_tx = 0;
3573 }
3574 if (empty($this->remise_percent)) {
3575 $this->remise_percent = 0;
3576 }
3577 if (empty($this->info_bits)) {
3578 $this->info_bits = 0;
3579 }
3580 if (empty($this->special_code)) {
3581 $this->special_code = 0;
3582 }
3583 if (empty($this->fk_parent_line)) {
3584 $this->fk_parent_line = 0;
3585 }
3586 if (empty($this->fk_fournprice)) {
3587 $this->fk_fournprice = 0;
3588 }
3589 if (empty($this->fk_unit)) {
3590 $this->fk_unit = 0;
3591 }
3592 if (empty($this->subprice)) {
3593 $this->subprice = 0;
3594 }
3595
3596 if (empty($this->pa_ht)) {
3597 $this->pa_ht = 0;
3598 }
3599
3600 // if buy price not defined, define buyprice as configured in margin admin
3601 if ($this->pa_ht == 0) {
3602 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3603 if ($result < 0) {
3604 return $result;
3605 } else {
3606 $this->pa_ht = $result;
3607 }
3608 }
3609
3610 $this->db->begin();
3611
3612 // Mise a jour ligne en base
3613 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3614 $sql .= " description = '".$this->db->escape($this->desc)."'";
3615 $sql .= " , label = ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
3616 $sql .= " , product_type = ".((int) $this->product_type);
3617 $sql .= " , date_start = ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null");
3618 $sql .= " , date_end = ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null");
3619 $sql .= " , tva_tx = '".price2num($this->tva_tx)."'";
3620 $sql .= " , localtax1_tx = ".(float) price2num($this->localtax1_tx);
3621 $sql .= " , localtax2_tx = ".(float) price2num($this->localtax2_tx);
3622 $sql .= " , localtax1_type = '".$this->db->escape($this->localtax1_type)."'";
3623 $sql .= " , localtax2_type = '".$this->db->escape($this->localtax2_type)."'";
3624 $sql .= " , qty = ".(float) price2num($this->qty);
3625 $sql .= " , subprice = ".(float) price2num($this->subprice);
3626 $sql .= " , subprice_ttc = ".(float) price2num($this->subprice_ttc);
3627 $sql .= " , remise_percent = ".(float) price2num($this->remise_percent);
3628 $sql .= " , info_bits = ".((int) $this->info_bits);
3629 if (empty($this->skip_update_total)) {
3630 $sql .= " , total_ht = ".(float) price2num($this->total_ht);
3631 $sql .= " , total_tva = ".(float) price2num($this->total_tva);
3632 $sql .= " , total_ttc = ".(float) price2num($this->total_ttc);
3633 $sql .= " , total_localtax1 = ".(float) price2num($this->total_localtax1);
3634 $sql .= " , total_localtax2 = ".(float) price2num($this->total_localtax2);
3635 }
3636 $sql .= " , fk_product_fournisseur_price = ".(!empty($this->fk_fournprice) ? "'".$this->db->escape((string) $this->fk_fournprice)."'" : "null");
3637 $sql .= " , buy_price_ht = ".(float) price2num($this->pa_ht);
3638 $sql .= " , special_code = ".((int) $this->special_code);
3639 $sql .= " , fk_parent_line = ".($this->fk_parent_line > 0 ? $this->fk_parent_line : "null");
3640 if (!empty($this->rang)) {
3641 $sql .= ", rang = ".((int) $this->rang);
3642 }
3643 $sql .= " , ref_fourn = ".(!empty($this->ref_fourn) ? "'".$this->db->escape($this->ref_fourn)."'" : "null");
3644 $sql .= " , fk_unit = ".($this->fk_unit ? (int) $this->fk_unit : 'null');
3645
3646 // Multicurrency
3647 $sql .= " , multicurrency_subprice = ".(float) price2num($this->multicurrency_subprice);
3648 $sql .= " , multicurrency_subprice_ttc = ".(float) price2num($this->multicurrency_subprice_ttc);
3649 $sql .= " , multicurrency_total_ht = ".(float) price2num($this->multicurrency_total_ht);
3650 $sql .= " , multicurrency_total_tva = ".(float) price2num($this->multicurrency_total_tva);
3651 $sql .= " , multicurrency_total_ttc = ".(float) price2num($this->multicurrency_total_ttc);
3652
3653 $sql .= " WHERE rowid = ".((int) $this->id);
3654
3655 dol_syslog(get_class($this)."::update", LOG_DEBUG);
3656 $resql = $this->db->query($sql);
3657 if ($resql) {
3658 if (!$error) {
3659 $result = $this->insertExtraFields();
3660 if ($result < 0) {
3661 $error++;
3662 }
3663 }
3664
3665 if (!$error && !$notrigger) {
3666 // Call trigger
3667 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_MODIFY', $user);
3668 if ($result < 0) {
3669 $this->db->rollback();
3670 return -1;
3671 }
3672 // End call triggers
3673 }
3674
3675 $this->db->commit();
3676 return 1;
3677 } else {
3678 $this->error = $this->db->error();
3679 $this->db->rollback();
3680 return -2;
3681 }
3682 }
3683
3684 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3691 public function update_total()
3692 {
3693 // phpcs:enable
3694 $this->db->begin();
3695
3696 // Mise a jour ligne en base
3697 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3698 $sql .= " total_ht = ".(float) price2num($this->total_ht, 'MT');
3699 $sql .= ",total_tva = ".(float) price2num($this->total_tva, 'MT');
3700 $sql .= ",total_ttc = ".(float) price2num($this->total_ttc, 'MT');
3701 $sql .= " WHERE rowid = ".((int) $this->id);
3702
3703 dol_syslog("SupplierProposalLine::update_total", LOG_DEBUG);
3704
3705 $resql = $this->db->query($sql);
3706 if ($resql) {
3707 $this->db->commit();
3708 return 1;
3709 } else {
3710 $this->error = $this->db->error();
3711 $this->db->rollback();
3712 return -2;
3713 }
3714 }
3715}
$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
isLineOfObject($lineid)
Check that a line belongs to this object, using $this->table_element_line and $this->fk_element.
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:469