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