dolibarr 25.0.0-alpha
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-2026 MDW <mdeweerd@users.noreply.github.com>
20 * Copyright (C) 2026 Vincent de Grandpré <vincent@de-grandpre.quebec>
21 * Copyright (C) 2026 Lionel Vessiller <lvessiller@open-dsi.fr>
22 * Copyright (C) 2026 Jose Martinez <jose.martinez@pichinov.com>
23 *
24 * This program is free software; you can redistribute it and/or modify
25 * it under the terms of the GNU General Public License as published by
26 * the Free Software Foundation; either version 3 of the License, or
27 * (at your option) any later version.
28 *
29 * This program is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 * GNU General Public License for more details.
33 *
34 * You should have received a copy of the GNU General Public License
35 * along with this program. If not, see <https://www.gnu.org/licenses/>.
36 */
37
43require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
44require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
45require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
46require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
47require_once DOL_DOCUMENT_ROOT.'/margin/lib/margins.lib.php';
48require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
49require_once DOL_DOCUMENT_ROOT.'/core/class/commonincoterm.class.php';
50require_once DOL_DOCUMENT_ROOT.'/subtotals/class/commonsubtotal.class.php';
55{
56 use CommonIncoterm;
57 use CommonSubtotal;
58
62 public $element = 'supplier_proposal';
63
67 public $table_element = 'supplier_proposal';
68
72 public $table_element_line = 'supplier_proposaldet';
73
77 public $class_element_line = 'SupplierProposalLine';
81 public $fk_element = 'fk_supplier_proposal';
82
86 public $picto = 'supplier_proposal';
87
92 public $restrictiononfksoc = 1;
93
97 protected $table_ref_field = 'ref';
98
102 public $socid; // Id client
103
109 public $author;
110
114 public $ref_fourn;
118 public $ref_supplier;
119
124 public $statut; // 0 (draft), 1 (validated), 2 (signed), 3 (not signed), 4 (processed/billed)
125
129 public $date;
130
134 public $delivery_date;
135
141 public $datec;
142
148 public $datev;
149
153 public $user_author_id;
154
160 public $price;
161
167 public $tva;
168
174 public $total;
175
179 public $cond_reglement_code;
183 public $cond_reglement_doc; // label doc
184
188 public $mode_reglement_code;
193 public $mode_reglement;
194
200 public $deposit_percent;
201
202
206 public $extraparams = array();
207
211 public $lines = array();
212
216 public $line;
217
218 public $labelStatus = array();
219 public $labelStatusShort = array();
220
224 public $nbtodo;
228 public $nbtodolate;
229
230 // Multicurrency
234 public $fk_multicurrency;
235
239 public $multicurrency_code;
243 public $multicurrency_tx;
247 public $multicurrency_total_ht;
251 public $multicurrency_total_tva;
255 public $multicurrency_total_ttc;
256
257 public $fields = array(
258 'ref' => array('type' => 'varchar(255)', 'label' => 'Ref', 'enabled' => 1, 'visible' => 1, 'showoncombobox' => 1, 'position' => 25, 'searchall' => 1),
259 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 750, 'searchall' => 1),
260 );
261
265 const STATUS_DRAFT = 0;
266
271
275 const STATUS_SIGNED = 2;
276
281
285 const STATUS_CLOSE = 4;
286
287
288
296 public function __construct($db, $socid = 0, $supplier_proposalid = 0)
297 {
298 $this->db = $db;
299
300 $this->ismultientitymanaged = 1;
301 $this->socid = $socid;
302 $this->id = $supplier_proposalid;
303 }
304
305
306 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
317 public function add_product($idproduct, $qty, $remise_percent = 0)
318 {
319 // phpcs:enable
320 global $mysoc;
321
322 if (!$qty) {
323 $qty = 1;
324 }
325
326 dol_syslog(get_class($this)."::add_product $idproduct, $qty, $remise_percent");
327 if ($idproduct > 0) {
328 $prod = new Product($this->db);
329 $prod->fetch($idproduct);
330
331 $productdesc = $prod->description;
332
333 $tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
334 $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
335 if (empty($tva_tx)) {
336 $tva_npr = 0;
337 }
338 $localtax1_tx = get_localtax($tva_tx, 1, $mysoc, $this->thirdparty, $tva_npr);
339 $localtax2_tx = get_localtax($tva_tx, 2, $mysoc, $this->thirdparty, $tva_npr);
340
341 // multiprix
342 if (getDolGlobalString('PRODUIT_MULTIPRICES') && $this->thirdparty->price_level) {
343 $price = $prod->multiprices[$this->thirdparty->price_level];
344 } else {
345 $price = $prod->price;
346 }
347
348 $line = new SupplierProposalLine($this->db);
349
350 $line->fk_product = $idproduct;
351 $line->desc = $productdesc;
352 $line->qty = $qty;
353 $line->subprice = $price;
354 $line->remise_percent = $remise_percent;
355 $line->tva_tx = $tva_tx;
356
357 $this->lines[] = $line;
358 return 1;
359 }
360 return -1;
361 }
362
363 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
370 public function insert_discount($idremise)
371 {
372 // phpcs:enable
373 global $langs;
374
375 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
376 include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
377
378 $this->db->begin();
379
380 $remise = new DiscountAbsolute($this->db);
381 $result = $remise->fetch($idremise);
382
383 if ($result > 0) {
384 if ($remise->fk_facture) { // Protection against multiple submission
385 $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
386 $this->db->rollback();
387 return -5;
388 }
389
390 $supplier_proposalligne = new SupplierProposalLine($this->db);
391 $supplier_proposalligne->fk_supplier_proposal = $this->id;
392 $supplier_proposalligne->fk_remise_except = $remise->id;
393 $supplier_proposalligne->desc = $remise->description; // Description of the proposal line
394 $supplier_proposalligne->tva_tx = $remise->tva_tx;
395 $supplier_proposalligne->localtax1_tx = $remise->localtax1_tx;
396 $supplier_proposalligne->localtax1_type = $remise->localtax1_type;
397 $supplier_proposalligne->localtax2_tx = $remise->localtax1_tx;
398 $supplier_proposalligne->localtax2_type = $remise->localtax1_type;
399 $supplier_proposalligne->subprice = -(float) $remise->amount_ht;
400 $supplier_proposalligne->fk_product = 0; // Predefined Product ID
401 $supplier_proposalligne->qty = 1;
402 $supplier_proposalligne->remise_percent = 0;
403 $supplier_proposalligne->rang = -1;
404 $supplier_proposalligne->info_bits = 2;
405
406 $supplier_proposalligne->total_ht = -(float) $remise->amount_ht;
407 $supplier_proposalligne->total_tva = -(float) $remise->amount_tva;
408 $supplier_proposalligne->total_ttc = -(float) $remise->amount_ttc;
409 $supplier_proposalligne->total_localtax1 = -(float) $remise->total_localtax1;
410 $supplier_proposalligne->total_localtax2 = -(float) $remise->total_localtax2;
411
412 $result = $supplier_proposalligne->insert();
413 if ($result > 0) {
414 $result = $this->update_price(1);
415 if ($result > 0) {
416 $this->db->commit();
417 return 1;
418 } else {
419 $this->db->rollback();
420 return -1;
421 }
422 } else {
423 $this->setErrorsFromObject($supplier_proposalligne);
424 $this->db->rollback();
425 return -2;
426 }
427 } else {
428 $this->db->rollback();
429 return -2;
430 }
431 }
432
469 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)
470 {
471 global $mysoc, $langs;
472
473 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");
474 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
475
476 // Clean parameters
477 if (empty($remise_percent)) {
478 $remise_percent = 0;
479 }
480 if (empty($qty)) {
481 $qty = 0;
482 }
483 if (empty($info_bits)) {
484 $info_bits = 0;
485 }
486 if (empty($rang)) {
487 $rang = 0;
488 }
489 if (empty($fk_parent_line) || $fk_parent_line < 0) {
490 $fk_parent_line = 0;
491 }
492 if (empty($pu_ht)) {
493 $pu_ht = 0;
494 }
495
496 $remise_percent = price2num($remise_percent);
497 $qty = (float) price2num($qty);
498 $pu_ht = price2num($pu_ht);
499 $pu_ttc = price2num($pu_ttc);
500 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
501 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
502 }
503 $txlocaltax1 = price2num($txlocaltax1);
504 $txlocaltax2 = price2num($txlocaltax2);
505 $pa_ht = price2num($pa_ht);
506 if ($price_base_type == 'HT') {
507 $pu = $pu_ht;
508 } else {
509 $pu = $pu_ttc;
510 }
511
512 // Check parameters
513 if ($type < 0) {
514 return -1;
515 }
516
517 if ($this->status == self::STATUS_DRAFT) {
518 $this->db->begin();
519
520 if ($fk_product > 0) {
521 if (getDolGlobalInt('SUPPLIER_PROPOSAL_WITH_PREDEFINED_PRICES_ONLY') == 1) { // Not the common case
522 // Check quantity is enough
523 dol_syslog(get_class($this)."::addline we check supplier prices fk_product=".$fk_product." fk_fournprice=".$fk_fournprice." qty=".$qty." ref_supplier=".$ref_supplier);
524 $productsupplier = new ProductFournisseur($this->db);
525 if ($productsupplier->fetch($fk_product) > 0) {
526 $product_type = $productsupplier->type;
527 $label = $productsupplier->label;
528 $fk_prod_fourn_price = $fk_fournprice;
529
530 // We use 'none' instead of $ref_supplier, because fourn_ref may not exists anymore. So we will take the first supplier price ok.
531 // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
532 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
533 $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
534 if ($result > 0) {
535 $pu = $productsupplier->fourn_pu; // Unit price supplier price set by get_buyprice
536 $ref_supplier = $productsupplier->ref_supplier; // Ref supplier price set by get_buyprice
537 // is remise percent not keyed but present for the product we add it
538 if ($remise_percent == 0 && $productsupplier->remise_percent != 0) {
539 $remise_percent = $productsupplier->remise_percent;
540 }
541 }
542 if ($result == 0) { // If result == 0, we failed to found the supplier reference price
543 $langs->load("errors");
544 $this->error = "Ref ".$productsupplier->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
545 $this->db->rollback();
546 dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
547 //$pu = $productsupplier->fourn_pu; // We do not overwrite unit price
548 //$ref = $productsupplier_fourn; // We do not overwrite ref supplier price
549 return -1;
550 }
551 if ($result == -1) {
552 $langs->load("errors");
553 $this->error = "Ref ".$productsupplier->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
554 $this->db->rollback();
555 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
556 return -1;
557 }
558 if ($result < -1) {
559 $this->setErrorsFromObject($productsupplier);
560 $this->db->rollback();
561 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
562 return -1;
563 }
564 } else {
565 $this->setErrorsFromObject($productsupplier);
566 $this->db->rollback();
567 return -1;
568 }
569 }
570 } else {
571 $product_type = $type;
572 }
573
574 // Calculation of the gross total (TTC) and VAT for the line from qty, pu, remise_percent and txtva
575 // VERY IMPORTANT: It's at the time of line insertion that we must store the net, VAT, and gross amounts,
576 // and this is done at the line level, which has its own VAT rate
577
578 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
579
580 // Clean vat code
581 $reg = array();
582 $vat_src_code = '';
583 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
584 $vat_src_code = $reg[1];
585 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
586 }
587
588 if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
589 $pu = 0;
590 }
591
592 $tabprice = calcul_price_total(
593 $qty,
594 $pu,
595 (float) $remise_percent,
596 $txtva,
597 (float) $txlocaltax1,
598 (float) $txlocaltax2,
599 0,
600 $price_base_type,
601 $info_bits,
602 $type,
603 $this->thirdparty,
604 $localtaxes_type,
605 100,
606 (float) $this->multicurrency_tx,
607 $pu_ht_devise
608 );
609 $total_ht = $tabprice[0];
610 $total_tva = $tabprice[1];
611 $total_ttc = $tabprice[2];
612 $total_localtax1 = $tabprice[9];
613 $total_localtax2 = $tabprice[10];
614 $pu = $pu_ht = $tabprice[3];
615
616 // MultiCurrency
617 $multicurrency_total_ht = $tabprice[16];
618 $multicurrency_total_tva = $tabprice[17];
619 $multicurrency_total_ttc = $tabprice[18];
620 $pu_ht_devise = $tabprice[19];
621
622 // Rang to use
623 $ranktouse = $rang;
624 if ($ranktouse == -1) {
625 $rangmax = $this->line_max($fk_parent_line);
626 $ranktouse = $rangmax + 1;
627 }
628
629 // Insert line
630 $this->line = new SupplierProposalLine($this->db);
631
632 $this->line->fk_supplier_proposal = $this->id;
633 $this->line->label = $label;
634 $this->line->desc = $desc;
635 $this->line->qty = $qty;
636
637 $this->line->vat_src_code = $vat_src_code;
638 $this->line->tva_tx = $txtva;
639 $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
640 $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
641 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
642 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
643 $this->line->fk_product = $fk_product;
644 $this->line->remise_percent = $remise_percent;
645 $this->line->subprice = (float) $pu_ht;
646 // Persist the original entry mode of the line so updateline() can preserve it later.
647 $this->line->subprice_ttc = ($price_base_type === 'TTC') ? (float) $pu_ttc : 0;
648 $this->line->rang = $ranktouse;
649 $this->line->info_bits = $info_bits;
650 $this->line->total_ht = (float) $total_ht;
651 $this->line->total_tva = (float) $total_tva;
652 $this->line->total_localtax1 = (float) $total_localtax1;
653 $this->line->total_localtax2 = (float) $total_localtax2;
654 $this->line->total_ttc = (float) $total_ttc;
655 $this->line->product_type = $type;
656 $this->line->special_code = $special_code;
657 $this->line->fk_parent_line = $fk_parent_line;
658 $this->line->fk_unit = $fk_unit;
659 $this->line->origin = $origin;
660 $this->line->origin_type = $origin;
661 $this->line->origin_id = $origin_id;
662 $this->line->ref_fourn = $this->db->escape($ref_supplier);
663 $this->line->date_start = $date_start;
664 $this->line->date_end = $date_end;
665
666 // infos merge
667 if (!empty($fk_product) && $fk_product > 0 && empty($fk_fournprice) && empty($pa_ht)) {
668 // When fk_fournprice is 0, we take the lowest buying price
669 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
670 $productFournisseur = new ProductFournisseur($this->db);
671 $productFournisseur->find_min_price_product_fournisseur($fk_product);
672 $this->line->fk_fournprice = $productFournisseur->product_fourn_price_id;
673 } else {
674 $this->line->fk_fournprice = ($fk_fournprice > 0 ? $fk_fournprice : 0); // If fk_fournprice is -1, we will not use fk_fournprice
675 }
676 $this->line->pa_ht = $pa_ht;
677 //var_dump($this->line->fk_fournprice);exit;
678
679 // Multicurrency
680 $this->line->fk_multicurrency = $this->fk_multicurrency;
681 $this->line->multicurrency_code = $this->multicurrency_code;
682 $this->line->multicurrency_subprice = (float) $pu_ht_devise;
683 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
684 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
685 $this->line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
686
687 // Set the proposal line as optional
688 if (empty($qty) && empty($special_code)) {
689 $this->line->special_code = 3;
690 }
691
692 if (is_array($array_options) && count($array_options) > 0) {
693 $this->line->array_options = $array_options;
694 }
695
696 $result = $this->line->insert();
697 if ($result > 0) {
698 // Reorder if child line
699 if (!empty($fk_parent_line)) {
700 $this->line_order(true, 'DESC');
701 } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) { // Update all rank of all other lines
702 $linecount = count($this->lines);
703 for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
704 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
705 }
706 }
707
708 // Update denormalized information at the present proposal level
709 $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.
710 if ($result > 0) {
711 $this->db->commit();
712 return $this->line->id;
713 } else {
714 $this->db->rollback();
715 return -1;
716 }
717 } else {
718 $this->setErrorsFromObject($this->line);
719 $this->db->rollback();
720 return -2;
721 }
722 } else {
723 $this->error = 'BadStatusOfObjectToAddLine';
724 return -5;
725 }
726 }
727
728
755 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)
756 {
757 global $mysoc;
758
759 dol_syslog(get_class($this)."::updateLine $rowid, $pu, $qty, $remise_percent, $txtva, $desc, $price_base_type, $info_bits");
760 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
761
762 // Clean parameters
763 $remise_percent = price2num($remise_percent);
764 $qty = (float) price2num($qty);
765 $pu = price2num($pu);
766 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
767 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
768 }
769 $txlocaltax1 = price2num($txlocaltax1);
770 $txlocaltax2 = price2num($txlocaltax2);
771 $pa_ht = price2num($pa_ht);
772 if (empty($qty) && empty($special_code)) {
773 $special_code = 3; // Set option tag
774 }
775 if (!empty($qty) && $special_code == 3) {
776 $special_code = 0; // Remove option tag
777 }
778
779 if ($this->status == 0) {
780 $this->db->begin();
781
782 // Calculation of the gross total (TTC) and VAT for the line from qty, pu, remise_percent and txtva
783 // VERY IMPORTANT: It's at the time of line insertion that we must store the net, VAT, and gross amounts,
784 // and this is done at the line level, which has its own VAT rate
785
786 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
787
788 // Clean vat code
789 $reg = array();
790 $vat_src_code = '';
791 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
792 $vat_src_code = $reg[1];
793 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
794 }
795
796 if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
797 $pu = 0;
798 }
799
800 $tabprice = calcul_price_total(
801 $qty,
802 $pu,
803 (float) $remise_percent,
804 $txtva,
805 (float) $txlocaltax1,
806 (float) $txlocaltax2,
807 0,
808 $price_base_type,
809 $info_bits,
810 $type,
811 $this->thirdparty,
812 $localtaxes_type,
813 100,
814 (float) $this->multicurrency_tx,
815 $pu_ht_devise
816 );
817 $total_ht = $tabprice[0];
818 $total_tva = $tabprice[1];
819 $total_ttc = $tabprice[2];
820 $total_localtax1 = $tabprice[9];
821 $total_localtax2 = $tabprice[10];
822 $pu_ht = $tabprice[3];
823 $pu_tva = $tabprice[4];
824 $pu_ttc = $tabprice[5];
825
826 // MultiCurrency
827 $multicurrency_total_ht = $tabprice[16];
828 $multicurrency_total_tva = $tabprice[17];
829 $multicurrency_total_ttc = $tabprice[18];
830 $pu_ht_devise = $tabprice[19];
831
832 // Fetch current line from the database and then clone the object and set it in $oldline property
833 $line = new SupplierProposalLine($this->db);
834 $line->fetch($rowid);
835 $line->fetch_optionals();
836
837 $fk_product = $line->fk_product;
838
839 // Stock previous line records
840 $staticline = clone $line;
841
842 $line->oldline = $staticline;
843 $this->line = $line;
844 $this->line->context = $this->context;
845
846 // Reorder if fk_parent_line change
847 if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
848 $rangmax = $this->line_max($fk_parent_line);
849 $this->line->rang = $rangmax + 1;
850 }
851
852 $this->line->id = $rowid;
853 $this->line->label = $label;
854 $this->line->desc = $desc;
855 $this->line->qty = $qty;
856 $this->line->product_type = $type;
857
858 $this->line->vat_src_code = $vat_src_code;
859 $this->line->tva_tx = $txtva;
860 $this->line->localtax1_tx = $txlocaltax1;
861 $this->line->localtax2_tx = $txlocaltax2;
862 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
863 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
864 $this->line->remise_percent = $remise_percent;
865 $this->line->subprice = (float) $pu_ht;
866 // Persist the original entry mode of the line so a no-op edit can preserve it later.
867 $this->line->subprice_ttc = ($price_base_type === 'TTC') ? (float) $pu_ttc : 0;
868 $this->line->info_bits = $info_bits;
869 $this->line->total_ht = (float) $total_ht;
870 $this->line->total_tva = (float) $total_tva;
871 $this->line->total_localtax1 = (float) $total_localtax1;
872 $this->line->total_localtax2 = (float) $total_localtax2;
873 $this->line->total_ttc = (float) $total_ttc;
874 $this->line->special_code = $special_code;
875 $this->line->fk_parent_line = $fk_parent_line;
876 $this->line->skip_update_total = $skip_update_total;
877 $this->line->ref_fourn = $ref_supplier;
878 $this->line->fk_unit = $fk_unit;
879
880 // infos merge
881 if (!empty($fk_product) && $fk_product > 0 && empty($fk_fournprice) && empty($pa_ht)) {
882 // by external module, take lowest buying price
883 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
884 $productFournisseur = new ProductFournisseur($this->db);
885 $productFournisseur->find_min_price_product_fournisseur($fk_product);
886 $this->line->fk_fournprice = $productFournisseur->product_fourn_price_id;
887 } else {
888 $this->line->fk_fournprice = $fk_fournprice;
889 }
890 $this->line->pa_ht = $pa_ht;
891
892 if (is_array($array_options) && count($array_options) > 0) {
893 // We replace values in this->line->array_options only for entries defined into $array_options
894 foreach ($array_options as $key => $value) {
895 $this->line->array_options[$key] = $array_options[$key];
896 }
897 }
898
899 // Multicurrency
900 $this->line->multicurrency_subprice = (float) $pu_ht_devise;
901 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
902 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
903 $this->line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
904
905 $result = $this->line->update();
906 if ($result > 0) {
907 // Reorder if child line
908 if (!empty($fk_parent_line)) {
909 $this->line_order(true, 'DESC');
910 }
911
912 $this->update_price(1);
913
914 $this->db->commit();
915 return $result;
916 } else {
917 $this->error = $this->db->error();
918 $this->db->rollback();
919 return -1;
920 }
921 } else {
922 dol_syslog(get_class($this)."::updateline Erreur -2 SupplierProposal en mode incompatible pour cette action");
923 return -2;
924 }
925 }
926
927
934 public function deleteLine($lineid)
935 {
936 global $user;
937
938 if ($this->status == 0) {
939 $line = new SupplierProposalLine($this->db);
940
941 // For triggers
942 $line->fetch($lineid);
943
944 if ($line->delete($user) > 0) {
945 $this->update_price(1);
946
947 return 1;
948 } else {
949 return -1;
950 }
951 } else {
952 return -2;
953 }
954 }
955
956
965 public function create($user, $notrigger = 0)
966 {
967 global $conf, $mysoc, $hookmanager;
968 $error = 0;
969
970 $now = dol_now();
971
972 dol_syslog(get_class($this)."::create");
973
974 // Check parameters
975 $result = $this->fetch_thirdparty();
976 if ($result < 0) {
977 $this->error = "Failed to fetch company";
978 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
979 return -3;
980 }
981 if (!empty($this->ref)) { // We check that ref is not already used
982 $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
983 if ($result > 0) {
984 $this->error = 'ErrorRefAlreadyExists';
985 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
986 $this->db->rollback();
987 return -1;
988 }
989 }
990
991 // Set tmp vars
992 $delivery_date = $this->delivery_date;
993
994 // Multicurrency
995 if (!empty($this->multicurrency_code)) {
996 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $now);
997 }
998 if (empty($this->fk_multicurrency)) {
999 $this->multicurrency_code = getDolCurrency();
1000 $this->fk_multicurrency = 0;
1001 $this->multicurrency_tx = 1;
1002 }
1003
1004 $this->db->begin();
1005
1006 // Insert into database
1007 $sql = "INSERT INTO ".MAIN_DB_PREFIX."supplier_proposal (";
1008 $sql .= "fk_soc";
1009 $sql .= ", price";
1010 $sql .= ", total_tva";
1011 $sql .= ", total_ttc";
1012 $sql .= ", datec";
1013 $sql .= ", ref";
1014 $sql .= ", fk_user_author";
1015 $sql .= ", note_private";
1016 $sql .= ", note_public";
1017 $sql .= ", model_pdf";
1018 $sql .= ", fk_cond_reglement";
1019 $sql .= ", deposit_percent";
1020 $sql .= ", fk_mode_reglement";
1021 $sql .= ", fk_account";
1022 $sql .= ", date_livraison";
1023 $sql .= ", fk_shipping_method";
1024 $sql .= ", fk_projet";
1025 $sql .= ", entity";
1026 $sql .= ", fk_multicurrency";
1027 $sql .= ", multicurrency_code";
1028 $sql .= ", multicurrency_tx";
1029 $sql .= ") ";
1030 $sql .= " VALUES (";
1031 $sql .= ((int) $this->socid);
1032 $sql .= ", 0";
1033 $sql .= ", 0";
1034 $sql .= ", 0";
1035 $sql .= ", '".$this->db->idate($now)."'";
1036 $sql .= ", '(PROV)'";
1037 $sql .= ", ".($user->id > 0 ? ((int) $user->id) : "null");
1038 $sql .= ", '".$this->db->escape($this->note_private)."'";
1039 $sql .= ", '".$this->db->escape($this->note_public)."'";
1040 $sql .= ", '".$this->db->escape($this->model_pdf)."'";
1041 $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : 'NULL');
1042 $sql .= ", ".(!empty($this->deposit_percent) ? "'" . $this->db->escape($this->deposit_percent) . "'" : 'NULL');
1043 $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : 'NULL');
1044 $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
1045 $sql .= ", ".(isDolTms($delivery_date) ? "'".$this->db->idate($delivery_date)."'" : "null");
1046 $sql .= ", ".($this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : 'NULL');
1047 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
1048 $sql .= ", ".((int) $conf->entity);
1049 $sql .= ", ".((int) $this->fk_multicurrency);
1050 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1051 $sql .= ", ".((float) $this->multicurrency_tx);
1052 $sql .= ")";
1053
1054 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1055 $resql = $this->db->query($sql);
1056 if ($resql) {
1057 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."supplier_proposal");
1058
1059 if ($this->id) {
1060 $this->ref = '(PROV'.$this->id.')';
1061 $sql = 'UPDATE '.MAIN_DB_PREFIX."supplier_proposal SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
1062
1063 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1064 $resql = $this->db->query($sql);
1065 if (!$resql) {
1066 $error++;
1067 }
1068
1069 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1070 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1071 }
1072
1073 // Add object linked
1074 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1075 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1076 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, ...))
1077 foreach ($tmp_origin_id as $origin_id) {
1078 $ret = $this->add_object_linked($origin, $origin_id);
1079 if (!$ret) {
1080 dol_print_error($this->db);
1081 $error++;
1082 }
1083 }
1084 }
1085 }
1086 }
1087
1088 /*
1089 * Insert products details into database
1090 */
1091 if (!$error) {
1092 $fk_parent_line = 0;
1093 $num = count($this->lines);
1094
1095 for ($i = 0; $i < $num; $i++) {
1096 // Reset fk_parent_line for no child products and special product
1097 if (($this->lines[$i]->product_type != 9 && empty($this->lines[$i]->fk_parent_line)) || $this->lines[$i]->product_type == 9) {
1098 $fk_parent_line = 0;
1099 }
1100
1101 // Preserve the original entry mode of the line so the total is computed from the typed value (no rounding drift).
1102 $line_price_base_type = $this->lines[$i]->getPriceBaseType();
1103 $result = $this->addline(
1104 $this->lines[$i]->desc,
1105 (float) $this->lines[$i]->subprice,
1106 $this->lines[$i]->qty,
1107 $this->lines[$i]->tva_tx,
1108 $this->lines[$i]->localtax1_tx,
1109 $this->lines[$i]->localtax2_tx,
1110 $this->lines[$i]->fk_product,
1111 $this->lines[$i]->remise_percent,
1112 $line_price_base_type,
1113 (float) $this->lines[$i]->subprice_ttc,
1114 0,
1115 $this->lines[$i]->product_type,
1116 $this->lines[$i]->rang,
1117 $this->lines[$i]->special_code,
1118 $fk_parent_line,
1119 $this->lines[$i]->fk_fournprice,
1120 $this->lines[$i]->pa_ht,
1121 empty($this->lines[$i]->label) ? '' : $this->lines[$i]->label, // deprecated
1122 $this->lines[$i]->array_options,
1123 $this->lines[$i]->ref_fourn,
1124 $this->lines[$i]->fk_unit,
1125 'supplier_proposal',
1126 $this->lines[$i]->rowid
1127 );
1128
1129 if ($result < 0) {
1130 $error++;
1131 $this->error = $this->db->error;
1132 dol_print_error($this->db);
1133 break;
1134 }
1135 // Defined the new fk_parent_line
1136 if ($result > 0 && $this->lines[$i]->product_type == 9) {
1137 $fk_parent_line = $result;
1138 }
1139 }
1140 }
1141
1142 if (!$error) {
1143 // Update denormalized data
1144 $resql = $this->update_price(1);
1145 if ($resql) {
1146 $action = 'update';
1147
1148 // Actions on extra fields
1149 if (!$error) {
1150 $result = $this->insertExtraFields();
1151 if ($result < 0) {
1152 $error++;
1153 }
1154 }
1155
1156 if (!$error && !$notrigger) {
1157 // Call trigger
1158 $result = $this->call_trigger('PROPOSAL_SUPPLIER_CREATE', $user);
1159 if ($result < 0) {
1160 $error++;
1161 }
1162 // End call triggers
1163 }
1164 } else {
1165 $this->error = $this->db->lasterror();
1166 $error++;
1167 }
1168 }
1169 } else {
1170 $this->error = $this->db->lasterror();
1171 $error++;
1172 }
1173
1174 if (!$error) {
1175 $this->db->commit();
1176 dol_syslog(get_class($this)."::create done id=".$this->id);
1177 return $this->id;
1178 } else {
1179 $this->db->rollback();
1180 return -2;
1181 }
1182 } else {
1183 $this->error = $this->db->lasterror();
1184 $this->db->rollback();
1185 return -1;
1186 }
1187 }
1188
1196 public function createFromClone(User $user, $fromid = 0)
1197 {
1198 global $hookmanager;
1199
1200 $error = 0;
1201 $now = dol_now();
1202
1203 $this->db->begin();
1204
1205 // get extrafields so they will be clone
1206 foreach ($this->lines as $line) {
1207 $line->fetch_optionals();
1208 }
1209
1210 // Load source object
1211 $objFrom = clone $this;
1212
1213 $objsoc = new Societe($this->db);
1214
1215 // Change socid if needed
1216 if (!empty($fromid) && $fromid != $this->socid) {
1217 if ($objsoc->fetch($fromid) > 0) {
1218 $this->socid = $objsoc->id;
1219 $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1220 $this->deposit_percent = (!empty($objsoc->deposit_percent) ? $objsoc->deposit_percent : 0);
1221 $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1222 unset($this->fk_project);
1223 }
1224
1225 // TODO Change product price if multi-prices
1226 } else {
1227 $objsoc->fetch($this->socid);
1228 }
1229
1230 $this->id = 0;
1231 $this->status = 0;
1232 $this->statut = 0;
1233
1234 if (!getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') || !is_readable(DOL_DOCUMENT_ROOT."/core/modules/supplier_proposal/" . getDolGlobalString('SUPPLIER_PROPOSAL_ADDON').".php")) {
1235 $this->error = 'ErrorSetupNotComplete';
1236 return -1;
1237 }
1238
1239 // Clear fields
1240 $this->user_author_id = $user->id;
1241 $this->user_validation_id = 0;
1242 $this->date = $now;
1243
1244 // Set ref
1245 require_once DOL_DOCUMENT_ROOT."/core/modules/supplier_proposal/" . getDolGlobalString('SUPPLIER_PROPOSAL_ADDON').'.php';
1246 $obj = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
1247 $modSupplierProposal = new $obj();
1248 '@phan-var-force ModeleNumRefSupplierProposal $modSupplierProposal';
1249 $this->ref = $modSupplierProposal->getNextValue($objsoc, $this);
1250
1251 // Create clone
1252 $this->context['createfromclone'] = 'createfromclone';
1253 $result = $this->create($user);
1254 if ($result < 0) {
1255 $error++;
1256 }
1257
1258 if (!$error) {
1259 // Hook of thirdparty module
1260 if (is_object($hookmanager)) {
1261 $parameters = array('objFrom' => $objFrom);
1262 $action = '';
1263 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1264 if ($reshook < 0) {
1265 $this->setErrorsFromObject($hookmanager);
1266 $error++;
1267 }
1268 }
1269 }
1270
1271 unset($this->context['createfromclone']);
1272
1273 // End
1274 if (!$error) {
1275 $this->db->commit();
1276 return $this->id;
1277 } else {
1278 $this->db->rollback();
1279 return -1;
1280 }
1281 }
1282
1290 public function fetch($rowid, $ref = '')
1291 {
1292 $sql = "SELECT p.rowid, p.entity, p.ref, p.fk_soc as socid";
1293 $sql .= ", p.total_ttc, p.total_tva, p.localtax1, p.localtax2, p.total_ht";
1294 $sql .= ", p.datec, GREATEST(p.tms, pef.tms) as date_modification";
1295 $sql .= ", p.date_valid as datev";
1296 $sql .= ", p.date_livraison as delivery_date";
1297 $sql .= ", p.model_pdf, p.extraparams";
1298 $sql .= ", p.note_private, p.note_public";
1299 $sql .= ", p.fk_projet as fk_project, p.fk_statut as status";
1300 $sql .= ", p.fk_user_author, p.fk_user_modif, p.fk_user_valid, p.fk_user_cloture";
1301 $sql .= ", p.fk_cond_reglement";
1302 $sql .= ", p.fk_mode_reglement";
1303 $sql .= ', p.fk_account';
1304 $sql .= ", p.fk_shipping_method";
1305 $sql .= ", p.last_main_doc";
1306 $sql .= ", p.fk_multicurrency, p.multicurrency_code, p.multicurrency_tx, p.multicurrency_total_ht, p.multicurrency_total_tva, p.multicurrency_total_ttc";
1307 $sql .= ", c.label as statut_label";
1308 $sql .= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc, p.deposit_percent";
1309 $sql .= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement";
1310 $sql .= " FROM ".MAIN_DB_PREFIX."c_propalst as c, ".MAIN_DB_PREFIX."supplier_proposal as p";
1311 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."supplier_proposal_extrafields as pef ON pef.fk_object=p.rowid";
1312 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id';
1313 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON p.fk_cond_reglement = cr.rowid';
1314 $sql .= " WHERE p.fk_statut = c.id";
1315 $sql .= " AND p.entity IN (".getEntity('supplier_proposal').")";
1316 if ($ref) {
1317 $sql .= " AND p.ref = '".$this->db->escape($ref)."'";
1318 } else {
1319 $sql .= " AND p.rowid = ".((int) $rowid);
1320 }
1321
1322 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1323 $resql = $this->db->query($sql);
1324 if ($resql) {
1325 if ($this->db->num_rows($resql)) {
1326 $obj = $this->db->fetch_object($resql);
1327
1328 $this->id = $obj->rowid;
1329 $this->entity = $obj->entity;
1330
1331 $this->ref = $obj->ref;
1332 $this->total_ht = $obj->total_ht;
1333 $this->total_tva = $obj->total_tva;
1334 $this->total_localtax1 = $obj->localtax1;
1335 $this->total_localtax2 = $obj->localtax2;
1336 $this->total_ttc = $obj->total_ttc;
1337 $this->socid = $obj->socid;
1338 $this->fk_project = $obj->fk_project;
1339 $this->model_pdf = $obj->model_pdf;
1340 $this->note = $obj->note_private; // TODO deprecated
1341 $this->note_private = $obj->note_private;
1342 $this->note_public = $obj->note_public;
1343 $this->statut = (int) $obj->status;
1344 $this->status = (int) $obj->status;
1345 $this->datec = $this->db->jdate($obj->datec); // TODO deprecated
1346 $this->datev = $this->db->jdate($obj->datev); // TODO deprecated
1347 $this->date_creation = $this->db->jdate($obj->datec); // Creation date
1348 $this->date_modification = $this->db->jdate($obj->date_modification); // Modification date
1349 $this->date = $this->date_creation;
1350 $this->date_validation = $this->db->jdate($obj->datev); // Validation date
1351 $this->delivery_date = $this->db->jdate($obj->delivery_date);
1352 $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1353
1354 $this->last_main_doc = $obj->last_main_doc;
1355 $this->mode_reglement_id = $obj->fk_mode_reglement;
1356 $this->mode_reglement_code = $obj->mode_reglement_code;
1357 $this->mode_reglement = $obj->mode_reglement;
1358 $this->deposit_percent = $obj->deposit_percent;
1359 $this->fk_account = ($obj->fk_account > 0) ? $obj->fk_account : null;
1360 $this->cond_reglement_id = $obj->fk_cond_reglement;
1361 $this->cond_reglement_code = $obj->cond_reglement_code;
1362 $this->cond_reglement = $obj->cond_reglement;
1363 $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1364
1365 $this->extraparams = (array) (!empty($obj->extraparams) ? json_decode($obj->extraparams, true) : array());
1366
1367 $this->user_author_id = $obj->fk_user_author;
1368 $this->user_modification_id = $obj->fk_user_modif;
1369 $this->user_validation_id = $obj->fk_user_valid;
1370 $this->user_closing_id = $obj->fk_user_cloture;
1371
1372 // Multicurrency
1373 $this->fk_multicurrency = $obj->fk_multicurrency;
1374 $this->multicurrency_code = $obj->multicurrency_code;
1375 $this->multicurrency_tx = $obj->multicurrency_tx;
1376 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1377 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1378 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1379
1380 $this->db->free($resql);
1381
1382 // Retrieve all extrafield
1383 // fetch optionals attributes and labels
1384 $this->fetch_optionals();
1385
1386 $this->lines = array();
1387
1388 // Lines of supplier proposals
1389 $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.subprice_ttc, d.fk_product,";
1390 $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,";
1391 $sql .= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label,';
1392 $sql .= ' d.ref_fourn as ref_produit_fourn, d.extraparams,';
1393 $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';
1394 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposaldet as d";
1395 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON d.fk_product = p.rowid";
1396 $sql .= " WHERE d.fk_supplier_proposal = ".((int) $this->id);
1397 $sql .= " ORDER by d.rang";
1398
1399 $result = $this->db->query($sql);
1400 if ($result) {
1401 $num = $this->db->num_rows($result);
1402 $i = 0;
1403
1404 while ($i < $num) {
1405 $objp = $this->db->fetch_object($result);
1406
1407 $line = new SupplierProposalLine($this->db);
1408
1409 $line->rowid = $objp->rowid; // deprecated
1410 $line->id = $objp->rowid;
1411 $line->fk_supplier_proposal = $objp->fk_supplier_proposal;
1412 $line->fk_parent_line = $objp->fk_parent_line;
1413 $line->product_type = $objp->product_type;
1414 $line->label = $objp->custom_label;
1415 $line->desc = $objp->description; // Description of the line
1416 $line->qty = $objp->qty;
1417 $line->tva_tx = $objp->tva_tx;
1418 $line->localtax1_tx = $objp->localtax1_tx;
1419 $line->localtax2_tx = $objp->localtax2_tx;
1420 $line->subprice = $objp->subprice;
1421 $line->subprice_ttc = $objp->subprice_ttc;
1422 $line->fk_remise_except = $objp->fk_remise_except;
1423 $line->remise_percent = $objp->remise_percent;
1424
1425 $line->info_bits = $objp->info_bits;
1426 $line->total_ht = $objp->total_ht;
1427 $line->total_tva = $objp->total_tva;
1428 $line->total_localtax1 = $objp->total_localtax1;
1429 $line->total_localtax2 = $objp->total_localtax2;
1430 $line->total_ttc = $objp->total_ttc;
1431 $line->fk_fournprice = $objp->fk_fournprice;
1432 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1433 $line->pa_ht = $marginInfos[0];
1434 $line->marge_tx = $marginInfos[1];
1435 $line->marque_tx = $marginInfos[2];
1436 $line->special_code = $objp->special_code;
1437 $line->rang = $objp->rang;
1438
1439 $line->fk_product = $objp->fk_product;
1440
1441 $line->ref = $objp->product_ref; // deprecated
1442 $line->product_ref = $objp->product_ref;
1443 $line->libelle = $objp->product_label; // deprecated
1444 $line->product_label = $objp->product_label;
1445 $line->product_desc = $objp->product_desc; // Description produit
1446 $line->fk_product_type = $objp->fk_product_type;
1447
1448 $line->ref_fourn = $objp->ref_produit_fourn;
1449
1450 $line->extraparams = !empty($objp->extraparams) ? (array) json_decode($objp->extraparams, true) : array();
1451
1452 // Multicurrency
1453 $line->fk_multicurrency = $objp->fk_multicurrency;
1454 $line->multicurrency_code = $objp->multicurrency_code;
1455 $line->multicurrency_subprice = $objp->multicurrency_subprice;
1456 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
1457 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
1458 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
1459 $line->fk_unit = $objp->fk_unit;
1460
1461 $line->fetch_optionals();
1462
1463 $this->lines[$i] = $line;
1464
1465 $i++;
1466 }
1467 $this->db->free($result);
1468 } else {
1469 $this->error = $this->db->error();
1470 return -1;
1471 }
1472
1473 return 1;
1474 }
1475
1476 $this->error = "Record Not Found";
1477 return 0;
1478 } else {
1479 $this->error = $this->db->error();
1480 return -1;
1481 }
1482 }
1483
1491 public function valid($user, $notrigger = 0)
1492 {
1493 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1494
1495 global $conf;
1496
1497 $error = 0;
1498 $now = dol_now();
1499
1500 if ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'creer'))
1501 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'validate_advance'))) {
1502 $this->db->begin();
1503
1504 // Numbering module definition
1505 $soc = new Societe($this->db);
1506 $result = $soc->fetch($this->socid);
1507
1508 if ($result < 0) {
1509 return -1;
1510 }
1511 if (!getDolGlobalBool('SUPPLIER_PROPOSAL_NOCHECK_ONBUY_PRODUCTS_ONVALID') && !$this->checkActiveProductInLines('onbuy')) {
1512 dol_syslog(get_class($this)."::valid checkActiveProductInLines ".$this->error, LOG_INFO);
1513 return -1;
1514 }
1515 // Define new ref
1516 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1517 $num = $this->getNextNumRef($soc);
1518 } else {
1519 $num = (string) $this->ref;
1520 }
1521 $this->newref = dol_sanitizeFileName($num);
1522
1523 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1524 $sql .= " SET ref = '".$this->db->escape($num)."',";
1525 $sql .= " fk_statut = 1, date_valid='".$this->db->idate($now)."', fk_user_valid=".((int) $user->id);
1526 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1527
1528 dol_syslog(get_class($this)."::valid", LOG_DEBUG);
1529 $resql = $this->db->query($sql);
1530 if (!$resql) {
1531 dol_print_error($this->db);
1532 $error++;
1533 }
1534
1535 // Trigger calls
1536 if (!$error && !$notrigger) {
1537 // Call trigger
1538 $result = $this->call_trigger('PROPOSAL_SUPPLIER_VALIDATE', $user);
1539 if ($result < 0) {
1540 $error++;
1541 }
1542 // End call triggers
1543 }
1544
1545 if (!$error) {
1546 $this->oldref = $this->ref;
1547
1548 // Rename directory if dir was a temporary ref
1549 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1550 // Now we rename also files into index
1551 $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)."'";
1552 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
1553 $resql = $this->db->query($sql);
1554 if (!$resql) {
1555 $error++;
1556 $this->error = $this->db->lasterror();
1557 }
1558 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'supplier_proposal/".$this->db->escape($this->newref)."'";
1559 $sql .= " WHERE filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
1560 $resql = $this->db->query($sql);
1561 if (!$resql) {
1562 $error++;
1563 $this->error = $this->db->lasterror();
1564 }
1565
1566 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1567 $oldref = dol_sanitizeFileName($this->ref);
1568 $newref = dol_sanitizeFileName($num);
1569 $dirsource = $conf->supplier_proposal->dir_output.'/'.$oldref;
1570 $dirdest = $conf->supplier_proposal->dir_output.'/'.$newref;
1571 if (!$error && file_exists($dirsource)) {
1572 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
1573 if (@rename($dirsource, $dirdest)) {
1574 dol_syslog("Rename ok");
1575 // Rename docs starting with $oldref with $newref
1576 $listoffiles = dol_dir_list($conf->supplier_proposal->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1577 foreach ($listoffiles as $fileentry) {
1578 $dirsource = $fileentry['name'];
1579 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1580 $dirsource = $fileentry['path'].'/'.$dirsource;
1581 $dirdest = $fileentry['path'].'/'.$dirdest;
1582 @rename($dirsource, $dirdest);
1583 }
1584 }
1585 }
1586 }
1587
1588 $this->ref = $num;
1589 $this->statut = self::STATUS_VALIDATED;
1591 $this->user_validation_id = $user->id;
1592 $this->datev = $now;
1593 $this->date_validation = $now;
1594
1595 if (getDolGlobalString('SUPPLIER_PROPOSAL_AUTOADD_USER_CONTACT')) {
1596 $result = $this->add_contact($user->id, 'SALESREPFOLL', 'internal', 1);
1597 if ($result < 0 && $result != -2) { // -2 means already exists
1598 $error++;
1599 }
1600 }
1601
1602 $this->db->commit();
1603 return 1;
1604 } else {
1605 $this->db->rollback();
1606 return -1;
1607 }
1608 } else {
1609 dol_syslog("You don't have permission to validate supplier proposal", LOG_WARNING);
1610 return -2;
1611 }
1612 }
1613
1614 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1623 public function set_date_livraison($user, $delivery_date)
1624 {
1625 // phpcs:enable
1626 return $this->setDeliveryDate($user, $delivery_date);
1627 }
1628
1636 public function setDeliveryDate($user, $delivery_date)
1637 {
1638 if ($user->hasRight('supplier_proposal', 'creer')) {
1639 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1640 $sql .= " SET date_livraison = ".(isDolTms($delivery_date) ? "'".$this->db->idate($delivery_date)."'" : 'null');
1641 $sql .= " WHERE rowid = ".((int) $this->id);
1642
1643 if ($this->db->query($sql)) {
1644 $this->delivery_date = $delivery_date;
1645 return 1;
1646 } else {
1647 $this->error = $this->db->error();
1648 dol_syslog(get_class($this)."::setDeliveryDate Erreur SQL");
1649 return -1;
1650 }
1651 }
1652 return 0;
1653 }
1654
1655
1665 public function reopen($user, $status, $note = '', $notrigger = 0)
1666 {
1667 $error = 0;
1668
1669 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1670 $sql .= " SET fk_statut = ".((int) $status).",";
1671 if (!empty($note)) {
1672 $sql .= " note_private = '".$this->db->escape($note)."',";
1673 }
1674 $sql .= " date_cloture = NULL, fk_user_cloture = NULL";
1675 $sql .= " WHERE rowid = ".((int) $this->id);
1676
1677 $this->db->begin();
1678
1679 dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
1680 $resql = $this->db->query($sql);
1681 if (!$resql) {
1682 $error++;
1683 $this->errors[] = "Error ".$this->db->lasterror();
1684 }
1685 if (!$error) {
1686 if (!$notrigger) {
1687 // Call trigger
1688 $result = $this->call_trigger('PROPOSAL_SUPPLIER_REOPEN', $user);
1689 if ($result < 0) {
1690 $error++;
1691 }
1692 // End call triggers
1693 }
1694 }
1695
1696 // Commit or rollback
1697 if ($error) {
1698 if (!empty($this->errors)) {
1699 foreach ($this->errors as $errmsg) {
1700 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1701 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1702 }
1703 }
1704 $this->db->rollback();
1705 return -1 * $error;
1706 } else {
1707 $this->statut = $status;
1708 $this->status = $status;
1709
1710 $this->db->commit();
1711 return 1;
1712 }
1713 }
1714
1715
1724 public function cloture($user, $status, $note)
1725 {
1726 global $langs, $conf;
1727 $hidedetails = 0;
1728 $hidedesc = 0;
1729 $hideref = 0;
1730
1731 $error = 0;
1732 $now = dol_now();
1733
1734 $this->db->begin();
1735
1736 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1737 $sql .= " SET fk_statut = ".((int) $status).", note_private = '".$this->db->escape($note)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".((int) $user->id);
1738 $sql .= " WHERE rowid = ".((int) $this->id);
1739
1740 $resql = $this->db->query($sql);
1741 if ($resql) {
1742 $this->statut = $status;
1743 $this->status = $status;
1744
1745 $modelpdf = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED', (empty($this->model_pdf) ? '' : $this->model_pdf));
1746 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_REFUSED';
1747
1748 if ($status == 2) {
1749 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_SIGNED';
1750 $modelpdf = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL', (empty($this->model_pdf) ? '' : $this->model_pdf));
1751
1752 if (getDolGlobalString('SUPPLIER_PROPOSAL_UPDATE_PRICE_ON_SUPPlIER_PROPOSAL')) { // TODO This option was not tested correctly. Error if product ref does not exists
1753 $result = $this->updateOrCreatePriceFournisseur($user);
1754 }
1755 }
1756 if ($status == 4) {
1757 $triggerName = 'PROPOSAL_SUPPLIER_CLASSIFY_BILLED';
1758 }
1759
1760 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1761 // Define output language
1762 $outputlangs = $langs;
1763 if (getDolGlobalInt('MAIN_MULTILANGS')) {
1764 $outputlangs = new Translate("", $conf);
1765 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
1766 $outputlangs->setDefaultLang($newlang);
1767 }
1768
1769 $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1770 }
1771
1772 // Call trigger
1773 $result = $this->call_trigger($triggerName, $user);
1774 if ($result < 0) {
1775 $error++;
1776 }
1777 // End call triggers
1778
1779 if (!$error) {
1780 $this->db->commit();
1781 return 1;
1782 } else {
1783 $this->db->rollback();
1784 return -1;
1785 }
1786 } else {
1787 $this->error = $this->db->lasterror();
1788 $this->errors[] = $this->db->lasterror();
1789 $this->db->rollback();
1790 return -1;
1791 }
1792 }
1793
1800 public function updateOrCreatePriceFournisseur($user)
1801 {
1802 dol_syslog(get_class($this)."::updateOrCreatePriceFournisseur", LOG_DEBUG);
1803
1804 foreach ($this->lines as $product) {
1805 if ($product->subprice <= 0) {
1806 continue;
1807 }
1808 $productsupplier = new ProductFournisseur($this->db);
1809
1810 $multicurrency_tx = 1;
1811 $fk_multicurrency = 0;
1812
1813 if (empty($this->thirdparty)) {
1814 $this->fetch_thirdparty();
1815 }
1816
1817 $ref_fourn = $product->ref_fourn;
1818 if (empty($ref_fourn)) {
1819 $ref_fourn = $product->ref_supplier;
1820 }
1821 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1822 list($fk_multicurrency, $multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $product->multicurrency_code);
1823 }
1824 $productsupplier->id = $product->fk_product;
1825
1826 $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);
1827 }
1828
1829 return 1;
1830 }
1831
1840 public function updatePriceFournisseur($idProductFournPrice, $product, $user)
1841 {
1842 $price = price2num($product->subprice * $product->qty, 'MU');
1843 $unitPrice = price2num($product->subprice, 'MU');
1844
1845 $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);
1846
1847 $resql = $this->db->query($sql);
1848 if (!$resql) {
1849 $this->error = $this->db->error();
1850 $this->db->rollback();
1851 return -1;
1852 }
1853 return 1;
1854 }
1855
1863 public function createPriceFournisseur($product, $user)
1864 {
1865 $price = price2num($product->subprice * $product->qty, 'MU');
1866 $qty = price2num($product->qty);
1867 $unitPrice = price2num($product->subprice, 'MU');
1868
1869 $now = dol_now();
1870
1871 $sanitized_values = array(
1872 "'".$this->db->idate($now)."'",
1873 (int) $product->fk_product,
1874 (int) $this->thirdparty->id,
1875 "'".$this->db->escape($product->ref_fourn)."'",
1876 (float) $price,
1877 (float) $qty,
1878 (float) $unitPrice,
1879 (float) $product->tva_tx,
1880 (int) $user->id
1881 );
1882 if (isModEnabled("multicurrency")) {
1883 if (!empty($product->multicurrency_code)) {
1884 include_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
1885 $multicurrency = new MultiCurrency($this->db); //need to fetch because empty fk_multicurrency and rate
1886 $multicurrency->fetch(0, $product->multicurrency_code);
1887 if (!empty($multicurrency->id)) {
1888 $sanitized_values[] = (int) $multicurrency->id;
1889 $sanitized_values[] = "'".$this->db->escape($product->multicurrency_code)."'";
1890 $sanitized_values[] = (float) $product->multicurrency_subprice;
1891 $sanitized_values[] = (float) $product->multicurrency_total_ht;
1892 $sanitized_values[] = (float) $multicurrency->rate->rate;
1893 } else {
1894 for ($i = 0; $i < 5; $i++) {
1895 $sanitized_values[] = 'NULL';
1896 }
1897 }
1898 }
1899 }
1900
1901 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'product_fournisseur_price ';
1902 $sql .= '(datec, fk_product, fk_soc, ref_fourn, price, quantity, unitprice, tva_tx, fk_user';
1903 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1904 $sql .= ',fk_multicurrency, multicurrency_code, multicurrency_unitprice, multicurrency_price, multicurrency_tx';
1905 }
1906 $sql .= ') VALUES ('.implode(',', $sanitized_values).')';
1907
1908 $resql = $this->db->query($sql);
1909 if (!$resql) {
1910 $this->error = $this->db->error();
1911 $this->db->rollback();
1912 return -1;
1913 }
1914 return 1;
1915 }
1916
1917 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1924 public function setDraft($user)
1925 {
1926 // phpcs:enable
1927 $error = 0;
1928
1929 if ($this->status == self::STATUS_DRAFT) {
1930 dol_syslog(get_class($this)."::setDraft already draft status", LOG_WARNING);
1931 return 0;
1932 }
1933
1934 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1935 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1936 $sql .= " WHERE rowid = ".((int) $this->id);
1937
1938 if ($this->db->query($sql)) {
1939 if (!$error) {
1940 $this->oldcopy = clone $this;
1941 }
1942
1943 if (!$error) {
1944 // Call trigger
1945 $result = $this->call_trigger('PROPOSAL_SUPPLIER_UNVALIDATE', $user);
1946 if ($result < 0) {
1947 $error++;
1948 }
1949 }
1950
1951 if (!$error) {
1952 $this->status = self::STATUS_DRAFT;
1953 $this->statut = self::STATUS_DRAFT; // deprecated
1954 $this->db->commit();
1955 return 1;
1956 } else {
1957 $this->db->rollback();
1958 return -1;
1959 }
1960 } else {
1961 return -1;
1962 }
1963 }
1964
1965
1966 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1980 public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datec', $sortorder = 'DESC')
1981 {
1982 // phpcs:enable
1983 global $user;
1984
1985 $ga = array();
1986
1987 $search_sale = 0;
1988 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
1989 $search_sale = $user->id;
1990 }
1991
1992 $sql = "SELECT s.rowid, s.nom as name, s.client,";
1993 $sql .= " p.rowid as supplier_proposalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
1994 $sql .= " p.datep as dp, p.fin_validite as datelimite";
1995 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."supplier_proposal as p, ".MAIN_DB_PREFIX."c_propalst as c";
1996 $sql .= " WHERE p.entity IN (".getEntity('supplier_proposal').")";
1997 $sql .= " AND p.fk_soc = s.rowid";
1998 $sql .= " AND p.fk_statut = c.id";
1999 if ($socid) {
2000 $sql .= " AND s.rowid = ".((int) $socid);
2001 }
2002 if ($draft) {
2003 $sql .= " AND p.fk_statut = 0";
2004 }
2005 if ($notcurrentuser > 0) {
2006 $sql .= " AND p.fk_user_author <> ".((int) $user->id);
2007 }
2008 // Search on sale representative
2009 if ($search_sale && $search_sale != '-1') {
2010 if ($search_sale == -2) {
2011 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
2012 } elseif ($search_sale > 0) {
2013 $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).")";
2014 }
2015 }
2016 $sql .= $this->db->order($sortfield, $sortorder);
2017 $sql .= $this->db->plimit($limit, $offset);
2018
2019 $result = $this->db->query($sql);
2020 if ($result) {
2021 $num = $this->db->num_rows($result);
2022 if ($num) {
2023 $i = 0;
2024 while ($i < $num) {
2025 $obj = $this->db->fetch_object($result);
2026
2027 if ($shortlist == 1) {
2028 $ga[$obj->supplier_proposalid] = $obj->ref;
2029 } elseif ($shortlist == 2) {
2030 $ga[$obj->supplier_proposalid] = $obj->ref.' ('.$obj->name.')';
2031 } else {
2032 $ga[$i]['id'] = $obj->supplier_proposalid;
2033 $ga[$i]['ref'] = $obj->ref;
2034 $ga[$i]['name'] = $obj->name;
2035 }
2036
2037 $i++;
2038 }
2039 }
2040 return $ga;
2041 } else {
2042 dol_print_error($this->db);
2043 return -1;
2044 }
2045 }
2046
2054 public function delete($user, $notrigger = 0)
2055 {
2056 global $conf;
2057 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2058
2059 $error = 0;
2060
2061 $this->db->begin();
2062
2063 if (!$notrigger) {
2064 // Call trigger
2065 $result = $this->call_trigger('PROPOSAL_SUPPLIER_DELETE', $user);
2066 if ($result < 0) {
2067 $error++;
2068 }
2069 // End call triggers
2070 }
2071
2072 // Remove linked categories.
2073 if (!$error) {
2074 $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_supplier_proposal";
2075 $sql .= " WHERE fk_supplier_proposal = ".((int) $this->id);
2076
2077 $result = $this->db->query($sql);
2078 if (!$result) {
2079 $error++;
2080 $this->error = $this->db->lasterror();
2081 $this->errors[] = $this->error;
2082 }
2083 }
2084
2085 if (!$error) {
2086 $sanitized_main = MAIN_DB_PREFIX.'supplier_proposaldet';
2087 $sanitized_ef = $sanitized_main."_extrafields";
2088 $sqlef = "DELETE FROM $sanitized_ef WHERE fk_object IN (SELECT rowid FROM $sanitized_main WHERE fk_supplier_proposal = ".((int) $this->id).")";
2089 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet WHERE fk_supplier_proposal = ".((int) $this->id);
2090 if ($this->db->query($sql)) {
2091 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposal WHERE rowid = ".((int) $this->id);
2092 if ($this->db->query($sqlef) && $this->db->query($sql)) {
2093 // Delete linked object
2094 $res = $this->deleteObjectLinked();
2095 if ($res < 0) {
2096 $error++;
2097 }
2098
2099 if (!$error) {
2100 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2101 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
2102 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
2103
2104 // We remove directory
2105 $ref = dol_sanitizeFileName($this->ref);
2106 if ($conf->supplier_proposal->dir_output && !empty($this->ref)) {
2107 $dir = $conf->supplier_proposal->dir_output."/".$ref;
2108 $file = $dir."/".$ref.".pdf";
2109 if (file_exists($file)) {
2110 dol_delete_preview($this);
2111
2112 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2113 $this->error = 'ErrorFailToDeleteFile';
2114 $this->errors = array('ErrorFailToDeleteFile');
2115 $this->db->rollback();
2116 return 0;
2117 }
2118 }
2119 if (file_exists($dir)) {
2120 $res = @dol_delete_dir_recursive($dir);
2121 if (!$res) {
2122 $this->error = 'ErrorFailToDeleteDir';
2123 $this->errors = array('ErrorFailToDeleteDir');
2124 $this->db->rollback();
2125 return 0;
2126 }
2127 }
2128 }
2129 }
2130
2131 // Removed extrafields
2132 if (!$error) {
2133 $result = $this->deleteExtraFields();
2134 if ($result < 0) {
2135 $error++;
2136 $errorflag = -4;
2137 dol_syslog(get_class($this)."::delete erreur ".$errorflag." ".$this->error, LOG_ERR);
2138 }
2139 }
2140
2141 if (!$error) {
2142 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
2143 $this->db->commit();
2144 return 1;
2145 } else {
2146 $this->error = $this->db->lasterror();
2147 $this->db->rollback();
2148 return 0;
2149 }
2150 } else {
2151 $this->error = $this->db->lasterror();
2152 $this->db->rollback();
2153 return -3;
2154 }
2155 } else {
2156 $this->error = $this->db->lasterror();
2157 $this->db->rollback();
2158 return -2;
2159 }
2160 } else {
2161 $this->db->rollback();
2162 return -1;
2163 }
2164 }
2165
2172 public function info($id)
2173 {
2174 $sql = "SELECT c.rowid, GREATEST(c.tms, cef.tms) as date_modification,";
2175 $sql .= " c.datec as date_creation, c.date_valid as date_validation, c.date_cloture as date_closure,";
2176 $sql .= " c.fk_user_author, c.fk_user_modif, c.fk_user_valid, c.fk_user_cloture";
2177 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as c";
2178 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."supplier_proposal_extrafields as cef ON cef.fk_object=c.rowid";
2179 $sql .= " WHERE c.rowid = ".((int) $id);
2180
2181 $result = $this->db->query($sql);
2182
2183 if ($result) {
2184 if ($this->db->num_rows($result)) {
2185 $obj = $this->db->fetch_object($result);
2186
2187 $this->id = $obj->rowid;
2188
2189 $this->date_creation = $this->db->jdate($obj->date_creation);
2190 $this->date_modification = empty($obj->date_modification) ? '' : $this->db->jdate($obj->date_modification);
2191 $this->date_validation = $this->db->jdate($obj->date_validation);
2192 $this->date_cloture = $this->db->jdate($obj->date_closure);
2193
2194 $this->user_creation_id = $obj->fk_user_author;
2195 $this->user_modification_id = $obj->fk_user_modif;
2196 $this->user_validation_id = $obj->fk_user_valid;
2197 $this->user_closing_id = $obj->fk_user_cloture;
2198 }
2199 $this->db->free($result);
2200 } else {
2201 dol_print_error($this->db);
2202 }
2203 }
2204
2205
2212 public function getLibStatut($mode = 0)
2213 {
2214 return $this->LibStatut((isset($this->status) ? $this->status : $this->statut), $mode);
2215 }
2216
2217 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2225 public function LibStatut($status, $mode = 1)
2226 {
2227 // phpcs:enable
2228
2229 // Init/load array of translation of status
2230 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
2231 global $langs;
2232 $langs->load("supplier_proposal");
2233 $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraft");
2234 $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidated");
2235 $this->labelStatus[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSigned");
2236 $this->labelStatus[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSigned");
2237 $this->labelStatus[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosed");
2238 $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraftShort");
2239 $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidatedShort");
2240 $this->labelStatusShort[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSignedShort");
2241 $this->labelStatusShort[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSignedShort");
2242 $this->labelStatusShort[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosedShort");
2243 }
2244
2245 $statusnew = '';
2246 if ($status == self::STATUS_DRAFT) {
2247 $statusnew = 'status0';
2248 } elseif ($status == self::STATUS_VALIDATED) {
2249 $statusnew = 'status1';
2250 } elseif ($status == self::STATUS_SIGNED) {
2251 $statusnew = 'status4';
2252 } elseif ($status == self::STATUS_NOTSIGNED) {
2253 $statusnew = 'status9';
2254 } elseif ($status == self::STATUS_CLOSE) {
2255 $statusnew = 'status6';
2256 }
2257
2258 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusnew, $mode);
2259 }
2260
2261
2262 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2270 public function load_board($user, $mode)
2271 {
2272 // phpcs:enable
2273 global $conf, $langs;
2274
2275 $now = dol_now();
2276
2277 $clause = " WHERE";
2278
2279 $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.date_cloture as datefin";
2280 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2281 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2282 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
2283 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2284 $clause = " AND";
2285 }
2286 $sql .= $clause." p.entity IN (".getEntity('supplier_proposal').")";
2287 if ($mode == 'opened') {
2288 $sql .= " AND p.fk_statut = 1";
2289 }
2290 if ($mode == 'signed') {
2291 $sql .= " AND p.fk_statut = 2";
2292 }
2293 if ($user->socid) {
2294 $sql .= " AND p.fk_soc = ".((int) $user->socid);
2295 }
2296
2297 $resql = $this->db->query($sql);
2298 if ($resql) {
2299 $label = $labelShort = '';
2300 $status = '';
2301 $delay_warning = 0;
2302 if ($mode == 'opened') {
2303 $delay_warning = !empty($conf->supplier_proposal->cloture->warning_delay) ? $conf->supplier_proposal->cloture->warning_delay : 0;
2304 $status = self::STATUS_VALIDATED;
2305 $label = $langs->trans("SupplierProposalsToClose");
2306 $labelShort = $langs->trans("ToAcceptRefuse");
2307 }
2308 if ($mode == 'signed') {
2309 $delay_warning = !empty($conf->supplier_proposal->facturation->warning_delay) ? $conf->supplier_proposal->facturation->warning_delay : 0;
2310 $status = self::STATUS_SIGNED;
2311 $label = $langs->trans("SupplierProposalsToProcess"); // May be billed or ordered
2312 $labelShort = $langs->trans("ToClose");
2313 }
2314
2315 $response = new WorkboardResponse();
2316 $response->warning_delay = $delay_warning / 60 / 60 / 24;
2317 $response->label = $label;
2318 $response->labelShort = $labelShort;
2319 $response->url = DOL_URL_ROOT.'/supplier_proposal/list.php?search_status='.$status;
2320 $response->img = img_object('', "propal");
2321
2322 // This assignment in condition is not a bug. It allows walking the results.
2323 while ($obj = $this->db->fetch_object($resql)) {
2324 $response->nbtodo++;
2325 if ($mode == 'opened') {
2326 $datelimit = $this->db->jdate($obj->datefin);
2327 if ($datelimit < ($now - $delay_warning)) {
2328 $response->nbtodolate++;
2329 }
2330 }
2331 // TODO Definir regle des propales a facturer en retard
2332 // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
2333 }
2334 return $response;
2335 } else {
2336 $this->error = $this->db->lasterror();
2337 return -1;
2338 }
2339 }
2340
2341
2350 public function initAsSpecimen($param = array())
2351 {
2352 global $conf, $langs;
2353
2354 // Load array of products prodids
2355 $num_prods = 0;
2356 $prodids = array();
2357 $sql = "SELECT rowid";
2358 $sql .= " FROM ".MAIN_DB_PREFIX."product";
2359 $sql .= " WHERE entity IN (".getEntity('product').")";
2360 if (array_key_exists('tobuy', $param)) {
2361 $sql .= " AND tobuy = ".((int) $param['tobuy']);
2362 }
2363 $sql .= $this->db->plimit(100);
2364
2365 $resql = $this->db->query($sql);
2366 if ($resql) {
2367 $num_prods = $this->db->num_rows($resql);
2368 $i = 0;
2369 while ($i < $num_prods) {
2370 $i++;
2371 $row = $this->db->fetch_row($resql);
2372 $prodids[$i] = $row[0];
2373 }
2374 }
2375
2376 // Initialise parameters
2377 $this->id = 0;
2378 $this->ref = 'SPECIMEN';
2379 $this->ref_supplier = 'NEMICEPS';
2380 $this->specimen = 1;
2381 $this->socid = 1;
2382 $this->date = time();
2383 $this->cond_reglement_id = 1;
2384 $this->cond_reglement_code = 'RECEP';
2385 $this->mode_reglement_id = 7;
2386 $this->mode_reglement_code = 'CHQ';
2387 $this->note_public = 'This is a comment (public)';
2388 $this->note_private = 'This is a comment (private)';
2389
2390 $this->multicurrency_tx = 1;
2391 $this->multicurrency_code = getDolCurrency();
2392
2393 // Lines
2394 $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)
2395 $xnbp = 0;
2396 while ($xnbp < $nbp) {
2397 $line = new SupplierProposalLine($this->db);
2398 $line->desc = $langs->trans("Description")." ".$xnbp;
2399 $line->qty = 1;
2400 $line->subprice = 100;
2401 $line->tva_tx = 19.6;
2402 $line->localtax1_tx = 0;
2403 $line->localtax2_tx = 0;
2404 if ($xnbp == 2) {
2405 $line->total_ht = 50;
2406 $line->total_ttc = 59.8;
2407 $line->total_tva = 9.8;
2408 $line->remise_percent = 50;
2409 } else {
2410 $line->total_ht = 100;
2411 $line->total_ttc = 119.6;
2412 $line->total_tva = 19.6;
2413 $line->remise_percent = 00;
2414 }
2415
2416 if ($num_prods > 0) {
2417 $prodid = mt_rand(1, $num_prods);
2418 $line->fk_product = $prodids[$prodid];
2419 }
2420
2421 $this->lines[$xnbp] = $line;
2422
2423 $this->total_ht += $line->total_ht;
2424 $this->total_tva += $line->total_tva;
2425 $this->total_ttc += $line->total_ttc;
2426
2427 $xnbp++;
2428 }
2429
2430 return 1;
2431 }
2432
2438 public function loadStateBoard()
2439 {
2440 global $user;
2441
2442 $this->nb = array();
2443 $clause = "WHERE";
2444
2445 $sql = "SELECT count(p.rowid) as nb";
2446 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2447 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
2448 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2449 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2450 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2451 $clause = "AND";
2452 }
2453 $sql .= " ".$clause." p.entity IN (".getEntity('supplier_proposal').")";
2454
2455 $resql = $this->db->query($sql);
2456 if ($resql) {
2457 // This assignment in condition is not a bug. It allows walking the results.
2458 while ($obj = $this->db->fetch_object($resql)) {
2459 $this->nb["supplier_proposals"] = $obj->nb;
2460 }
2461 $this->db->free($resql);
2462 return 1;
2463 } else {
2464 dol_print_error($this->db);
2465 $this->error = $this->db->lasterror();
2466 return -1;
2467 }
2468 }
2469
2470
2478 public function getNextNumRef($soc)
2479 {
2480 global $conf, $db, $langs;
2481 $langs->load("supplier_proposal");
2482
2483 if (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON')) {
2484 $mybool = false;
2485
2486 $file = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') . ".php";
2487 $classname = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
2488
2489 // Include file with class
2490 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2491 foreach ($dirmodels as $reldir) {
2492 $dir = dol_buildpath($reldir."core/modules/supplier_proposal/");
2493
2494 // Load file with numbering class (if found)
2495 $mybool = ((bool) @include_once $dir.$file) || $mybool;
2496 }
2497
2498 if (!$mybool) {
2499 dol_print_error(null, "Failed to include file ".$file);
2500 return '';
2501 }
2502
2503 $obj = new $classname();
2504 '@phan-var-force ModeleNumRefSupplierProposal $obj';
2506 $numref = "";
2507 $numref = $obj->getNextValue($soc, $this);
2508
2509 if ($numref != "") {
2510 return $numref;
2511 } else {
2512 $this->error = $obj->error;
2513 return "";
2514 }
2515 } else {
2516 $langs->load("errors");
2517 print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("SupplierProposal"));
2518 return "";
2519 }
2520 }
2521
2528 public function getTooltipContentArray($params)
2529 {
2530 global $conf, $langs;
2531
2532 $langs->load('supplier_proposal');
2533
2534 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2535 return ['optimize' => $langs->trans("ShowSupplierProposal")];
2536 }
2537
2538 //$option = $params['option'] ?? '';
2539 $datas = [];
2540
2541 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("SupplierProposal").'</u>';
2542 if (isset($this->status)) {
2543 $datas['picto'] .= ' '.$this->getLibStatut(5);
2544 }
2545 if (!empty($this->ref)) {
2546 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2547 }
2548 if (!empty($this->ref_fourn)) {
2549 $datas['ref_supplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_fourn;
2550 }
2551 if (empty($params['nofetch'])) {
2552 $langs->load('companies');
2553 if (empty($this->thirdparty)) {
2554 $this->fetch_thirdparty();
2555 }
2556 if (is_object($this->thirdparty)) {
2557 $datas['supplier'] = '<br><b>'.$langs->trans('Supplier').':</b> '.$this->thirdparty->getNomUrl(1, '', 0, 1);
2558 }
2559 }
2560 if (!empty($this->total_ht)) {
2561 $datas['amount_ht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, getDolCurrency());
2562 }
2563 if (!empty($this->total_tva)) {
2564 $datas['amount_vat'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, getDolCurrency());
2565 }
2566 if (!empty($this->total_ttc)) {
2567 $datas['amount_ttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, getDolCurrency());
2568 }
2569
2570 return $datas;
2571 }
2572
2584 public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2585 {
2586 global $langs, $conf, $user, $hookmanager;
2587
2588 if (!empty($conf->dol_no_mouse_hover)) {
2589 $notooltip = 1; // Force disable tooltips
2590 }
2591
2592 $url = '';
2593 $result = '';
2594 $params = [
2595 'id' => $this->id,
2596 'objecttype' => $this->element,
2597 'option' => $option,
2598 ];
2599 $classfortooltip = 'classfortooltip';
2600 $dataparams = '';
2601 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2602 $classfortooltip = 'classforajaxtooltip';
2603 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
2604 $label = '';
2605 } else {
2606 $label = implode($this->getTooltipContentArray($params));
2607 }
2608
2609 if ($option == '') {
2610 $url = DOL_URL_ROOT.'/supplier_proposal/card.php?id='.$this->id.$get_params;
2611 }
2612 if ($option == 'document') {
2613 $url = DOL_URL_ROOT.'/supplier_proposal/document.php?id='.$this->id.$get_params;
2614 }
2615
2616 if ($option !== 'nolink') {
2617 // Add param to save lastsearch_values or not
2618 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2619 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2620 $add_save_lastsearch_values = 1;
2621 }
2622 if ($add_save_lastsearch_values) {
2623 $url .= '&save_lastsearch_values=1';
2624 }
2625 }
2626
2627 $linkclose = '';
2628 if (empty($notooltip) && $user->hasRight('propal', 'lire')) {
2629 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2630 $label = $langs->trans("ShowSupplierProposal");
2631 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
2632 }
2633 $linkclose .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
2634 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
2635 }
2636
2637 $linkstart = '<a href="'.$url.'"';
2638 $linkstart .= $linkclose.'>';
2639 $linkend = '</a>';
2640
2641 $result .= $linkstart;
2642 if ($withpicto) {
2643 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
2644 }
2645 if ($withpicto != 2) {
2646 $result .= $this->ref;
2647 }
2648 $result .= $linkend;
2649
2650 if ($addlinktonotes) {
2651 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2652 if ($txttoshow) {
2653 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2654 $result .= ' <span class="note inline-block">';
2655 $result .= '<a href="'.DOL_URL_ROOT.'/supplier_proposal/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2656 $result .= img_picto('', 'note');
2657 $result .= '</a>';
2658 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
2659 //$result.='</a>';
2660 $result .= '</span>';
2661 }
2662 }
2663 global $action;
2664 $hookmanager->initHooks(array($this->element . 'dao'));
2665 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
2666 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2667 if ($reshook > 0) {
2668 $result = $hookmanager->resPrint;
2669 } else {
2670 $result .= $hookmanager->resPrint;
2671 }
2672 return $result;
2673 }
2674
2680 public function getLinesArray()
2681 {
2682 // For other object, here we call fetch_lines. But fetch_lines does not exists on supplier proposal
2683
2684 $sql = 'SELECT pt.rowid, pt.label as custom_label, pt.description, pt.fk_product, pt.fk_remise_except,';
2685 $sql .= ' pt.qty, pt.tva_tx, pt.vat_src_code, pt.remise_percent, pt.subprice, pt.info_bits,';
2686 $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,';
2687 $sql .= ' pt.product_type, pt.rang, pt.fk_parent_line, pt.extraparams,';
2688 $sql .= ' p.label as product_label, p.ref, p.fk_product_type, p.rowid as prodid,';
2689 $sql .= ' p.description as product_desc, pt.ref_fourn as ref_supplier,';
2690 $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';
2691 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pt';
2692 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pt.fk_product=p.rowid';
2693 $sql .= ' WHERE pt.fk_supplier_proposal = '.((int) $this->id);
2694 $sql .= ' ORDER BY pt.rang ASC, pt.rowid';
2695
2696 dol_syslog(get_class($this).'::getLinesArray', LOG_DEBUG);
2697 $resql = $this->db->query($sql);
2698 if ($resql) {
2699 $num = $this->db->num_rows($resql);
2700 $i = 0;
2701
2702 while ($i < $num) {
2703 $obj = $this->db->fetch_object($resql);
2704
2705 $this->lines[$i] = new SupplierProposalLine($this->db);
2706 $this->lines[$i]->id = $obj->rowid; // for backward compatibility
2707 $this->lines[$i]->rowid = $obj->rowid;
2708
2709 $this->lines[$i]->label = $obj->custom_label;
2710 $this->lines[$i]->description = $obj->description; // deprecated
2711 $this->lines[$i]->desc = $obj->description;
2712
2713 $this->lines[$i]->fk_product = $obj->fk_product;
2714 $this->lines[$i]->ref = $obj->ref;
2715 $this->lines[$i]->product_label = $obj->product_label;
2716 $this->lines[$i]->product_desc = $obj->product_desc;
2717 $this->lines[$i]->fk_product_type = $obj->fk_product_type; // deprecated
2718 $this->lines[$i]->product_type = $obj->product_type;
2719 $this->lines[$i]->qty = $obj->qty;
2720 $this->lines[$i]->subprice = $obj->subprice;
2721 $this->lines[$i]->fk_remise_except = $obj->fk_remise_except;
2722 $this->lines[$i]->remise_percent = $obj->remise_percent;
2723 $this->lines[$i]->tva_tx = $obj->tva_tx;
2724 $this->lines[$i]->vat_src_code = $obj->vat_src_code;
2725 $this->lines[$i]->info_bits = $obj->info_bits;
2726 $this->lines[$i]->total_ht = $obj->total_ht;
2727 $this->lines[$i]->total_tva = $obj->total_tva;
2728 $this->lines[$i]->total_ttc = $obj->total_ttc;
2729 $this->lines[$i]->fk_fournprice = $obj->fk_fournprice;
2730 $marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->lines[$i]->fk_fournprice, $obj->pa_ht);
2731 $this->lines[$i]->pa_ht = $marginInfos[0];
2732 $this->lines[$i]->marge_tx = $marginInfos[1];
2733 $this->lines[$i]->marque_tx = $marginInfos[2];
2734 $this->lines[$i]->fk_parent_line = $obj->fk_parent_line;
2735 $this->lines[$i]->special_code = $obj->special_code;
2736 $this->lines[$i]->rang = $obj->rang;
2737
2738 $this->lines[$i]->ref_fourn = $obj->ref_supplier; // deprecated
2739 $this->lines[$i]->ref_supplier = $obj->ref_supplier;
2740
2741 // Multicurrency
2742 $this->lines[$i]->fk_multicurrency = $obj->fk_multicurrency;
2743 $this->lines[$i]->multicurrency_code = $obj->multicurrency_code;
2744 $this->lines[$i]->multicurrency_subprice = $obj->multicurrency_subprice;
2745 $this->lines[$i]->multicurrency_total_ht = $obj->multicurrency_total_ht;
2746 $this->lines[$i]->multicurrency_total_tva = $obj->multicurrency_total_tva;
2747 $this->lines[$i]->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
2748 $this->lines[$i]->fk_unit = $obj->fk_unit;
2749 $this->lines[$i]->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
2750
2751 $this->lines[$i]->fetch_optionals();
2752
2753 $i++;
2754 }
2755 $this->db->free($resql);
2756
2757 return 1;
2758 } else {
2759 $this->error = $this->db->error();
2760 return -1;
2761 }
2762 }
2763
2775 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2776 {
2777 global $langs;
2778
2779 $langs->load("supplier_proposal");
2780 $outputlangs->load("products");
2781
2782 if (!dol_strlen($modele)) {
2783 $modele = ''; // On supplier documents, template can be empty( no doc generated in this case)
2784
2785 if ($this->model_pdf) {
2786 $modele = $this->model_pdf;
2787 } elseif (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF')) {
2788 $modele = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF');
2789 }
2790 }
2791
2792 if (empty($modele)) {
2793 return 1;
2794 }
2795
2796 $modelpath = "core/modules/supplier_proposal/doc/";
2797
2798 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2799 }
2800
2801
2810 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2811 {
2812 $tables = array(
2813 'supplier_proposal'
2814 );
2815
2816 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2817 }
2818
2827 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
2828 {
2829 $tables = array(
2830 'supplier_proposaldet'
2831 );
2832
2833 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
2834 }
2835
2836
2844 public function getKanbanView($option = '', $arraydata = null)
2845 {
2846 global $langs;
2847
2848 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2849
2850 $return = '<div class="box-flex-item box-flex-grow-zero">';
2851 $return .= '<div class="info-box info-box-sm">';
2852 $return .= '<span class="info-box-icon bg-infobox-action">';
2853 $return .= img_picto('', $this->picto);
2854 //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2855 $return .= '</span>';
2856 $return .= '<div class="info-box-content">';
2857 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
2858 if ($selected >= 0) {
2859 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2860 }
2861 if (property_exists($this, 'socid')) {
2862 $return .= '<span class="info-box-ref"> | '.$this->socid.'</span>';
2863 }
2864 if (property_exists($this, 'delivery_date')) {
2865 $return .= '<br><span class="opacitymedium">'.$langs->trans("DateEnd").'</span> : <span class="info-box-label">'.dol_print_date($this->delivery_date).'</span>';
2866 }
2867 if (property_exists($this, 'total_ttc')) {
2868 $return .= '<br><span class="opacitymedium" >'.$langs->trans("AmountHT").' : </span><span class="info-box-label amount">'.price($this->total_ttc).'</span>';
2869 }
2870 if (method_exists($this, 'getLibStatut')) {
2871 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
2872 }
2873 $return .= '</div>';
2874 $return .= '</div>';
2875 $return .= '</div>';
2876 return $return;
2877 }
2878
2889 public function setCategories($categories)
2890 {
2891 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2892 return parent::setCategoriesCommon($categories, Categorie::TYPE_SUPPLIER_PROPOSAL);
2893 }
2894}
2895
2896
2901{
2905 public $db;
2906
2910 public $error = '';
2911
2915 public $element = 'supplier_proposaldet';
2916
2920 public $table_element = 'supplier_proposaldet';
2921
2925 public $parent_element = 'supplier_proposal';
2926
2930 public $fk_parent_attribute = 'fk_supplier_proposal';
2931
2935 public $oldline;
2936
2940 public $id;
2941
2945 public $fk_supplier_proposal;
2946
2950 public $fk_parent_line;
2951
2955 public $desc; // Description of the line
2956
2960 public $fk_product; // Predefined Product ID
2961
2967 public $fk_product_type;
2973 public $product_type = Product::TYPE_PRODUCT;
2974
2978 public $qty;
2982 public $tva_tx;
2986 public $vat_src_code;
2987
2991 public $remise_percent;
2992
2996 public $fk_remise_except;
2997
3001 public $rang = 0;
3002
3006 public $fk_fournprice;
3007
3011 public $pa_ht;
3015 public $marge_tx;
3019 public $marque_tx;
3020
3024 public $special_code; // Tag for special lines (exclusive tags)
3025 // 1: frais de port
3026 // 2: ecotaxe
3027 // 3: option line (when qty = 0)
3028
3032 public $info_bits = 0; // Liste d'options cumulables:
3033 // Bit 0: 0 if TVA normal - 1 if TVA NPR
3034 // Bit 1: 0 normal line - 1 if fixed reduction
3035
3039 public $total_ht; // Line Net Total (HT), including all quantity and the line discount
3043 public $total_tva; // Line Net Total (VAT), including all quantity and the line discount
3047 public $total_ttc; // Line Net Total (TTC), including all quantity and the line discount
3048
3052 public $date_start;
3056 public $date_end;
3057
3058 // From llx_product
3064 public $ref;
3065
3070 public $product_ref;
3071
3077 public $libelle;
3078
3083 public $product_label;
3084
3089 public $label;
3090
3095 public $product_desc;
3096
3100 public $localtax1_tx; // Local tax 1
3104 public $localtax2_tx; // Local tax 2
3108 public $localtax1_type; // Local tax 1 type
3112 public $localtax2_type; // Local tax 2 type
3116 public $total_localtax1; // Line total local tax 1
3120 public $total_localtax2; // Line total local tax 2
3121
3125 public $skip_update_total; // Skip update price total for special lines
3126
3130 public $ref_fourn;
3134 public $ref_supplier;
3135
3136 // Multicurrency
3140 public $fk_multicurrency;
3141
3145 public $multicurrency_code;
3149 public $multicurrency_subprice;
3153 public $multicurrency_total_ht;
3157 public $multicurrency_total_tva;
3161 public $multicurrency_total_ttc;
3162
3168 public function __construct($db)
3169 {
3170 $this->db = $db;
3171 }
3172
3179 public function fetch($rowid)
3180 {
3181 $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,';
3182 $sql .= ' pd.date_start, pd.date_end,';
3183 $sql .= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice, pd.subprice_ttc,';
3184 $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,';
3185 $sql .= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
3186 $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
3187 $sql .= ' pd.product_type, pd.ref_fourn as ref_produit_fourn, pd.extraparams,';
3188 $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';
3189 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pd';
3190 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
3191 $sql .= ' WHERE pd.rowid = '.((int) $rowid);
3192
3193 $result = $this->db->query($sql);
3194 if ($result) {
3195 if ($objp = $this->db->fetch_object($result)) {
3196 $this->id = $objp->rowid;
3197 $this->fk_supplier_proposal = $objp->fk_supplier_proposal;
3198 $this->fk_parent_line = $objp->fk_parent_line;
3199 $this->label = $objp->custom_label;
3200 $this->desc = $objp->description;
3201 $this->qty = $objp->qty;
3202
3203 $this->subprice = $objp->subprice;
3204 $this->subprice_ttc = $objp->subprice_ttc;
3205
3206 $this->tva_tx = $objp->tva_tx;
3207 $this->remise_percent = $objp->remise_percent;
3208 $this->fk_remise_except = $objp->fk_remise_except;
3209 $this->fk_product = $objp->fk_product;
3210 $this->info_bits = $objp->info_bits;
3211 $this->date_start = $this->db->jdate($objp->date_start);
3212 $this->date_end = $this->db->jdate($objp->date_end);
3213
3214 $this->total_ht = $objp->total_ht;
3215 $this->total_tva = $objp->total_tva;
3216 $this->total_ttc = $objp->total_ttc;
3217
3218 $this->fk_fournprice = $objp->fk_fournprice;
3219
3220 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
3221 $this->pa_ht = $marginInfos[0];
3222 $this->marge_tx = $marginInfos[1];
3223 $this->marque_tx = $marginInfos[2];
3224
3225 $this->special_code = $objp->special_code;
3226 $this->product_type = $objp->product_type;
3227 $this->rang = $objp->rang;
3228
3229 $this->product_ref = $objp->product_ref;
3230 $this->product_label = $objp->product_label;
3231 $this->product_desc = $objp->product_desc;
3232
3233 $this->ref_fourn = $objp->ref_produit_fourn;
3234
3235 $this->extraparams = !empty($objp->extraparams) ? (array) json_decode($objp->extraparams, true) : array();
3236
3237 // Multicurrency
3238 $this->fk_multicurrency = $objp->fk_multicurrency;
3239 $this->multicurrency_code = $objp->multicurrency_code;
3240 $this->multicurrency_subprice = $objp->multicurrency_subprice;
3241 $this->multicurrency_subprice_ttc = $objp->multicurrency_subprice_ttc;
3242 $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3243 $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3244 $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3245 $this->fk_unit = $objp->fk_unit;
3246
3247 $this->db->free($result);
3248 return 1;
3249 }
3250 return 0;
3251 } else {
3252 dol_print_error($this->db);
3253 return -1;
3254 }
3255 }
3256
3263 public function insert($notrigger = 0)
3264 {
3265 global $user;
3266
3267 $error = 0;
3268
3269 dol_syslog(get_class($this)."::insert rang=".$this->rang);
3270
3271 // Clean parameters
3272 if (empty($this->tva_tx)) {
3273 $this->tva_tx = 0;
3274 }
3275 if (empty($this->vat_src_code)) {
3276 $this->vat_src_code = '';
3277 }
3278 if (empty($this->localtax1_tx)) {
3279 $this->localtax1_tx = 0;
3280 }
3281 if (empty($this->localtax2_tx)) {
3282 $this->localtax2_tx = 0;
3283 }
3284 if (empty($this->localtax1_type)) {
3285 $this->localtax1_type = '';
3286 }
3287 if (empty($this->localtax2_type)) {
3288 $this->localtax2_type = '';
3289 }
3290 if (empty($this->total_localtax1)) {
3291 $this->total_localtax1 = 0;
3292 }
3293 if (empty($this->total_localtax2)) {
3294 $this->total_localtax2 = 0;
3295 }
3296 if (empty($this->rang)) {
3297 $this->rang = 0;
3298 }
3299 if (empty($this->remise_percent)) {
3300 $this->remise_percent = 0;
3301 }
3302 if (empty($this->info_bits)) {
3303 $this->info_bits = 0;
3304 }
3305 if (empty($this->special_code)) {
3306 $this->special_code = 0;
3307 }
3308 if (empty($this->fk_parent_line)) {
3309 $this->fk_parent_line = 0;
3310 }
3311 if (empty($this->fk_fournprice)) {
3312 $this->fk_fournprice = 0;
3313 }
3314 if (empty($this->fk_unit)) {
3315 $this->fk_unit = 0;
3316 }
3317 if (empty($this->subprice)) {
3318 $this->subprice = 0;
3319 }
3320
3321 if (empty($this->pa_ht)) {
3322 $this->pa_ht = 0;
3323 }
3324
3325 // if buy price not defined, define buyprice as configured in margin admin
3326 if ($this->pa_ht == 0) {
3327 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3328 if ($result < 0) {
3329 return $result;
3330 } else {
3331 $this->pa_ht = $result;
3332 }
3333 }
3334
3335 // Check parameters
3336 if ($this->product_type < 0) {
3337 return -1;
3338 }
3339
3340 $this->db->begin();
3341
3342 // Insert line into database
3343 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'supplier_proposaldet';
3344 $sql .= ' (fk_supplier_proposal, fk_parent_line, label, description, fk_product, product_type,';
3345 $sql .= ' date_start, date_end,';
3346 $sql .= ' fk_remise_except, qty, tva_tx, vat_src_code, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3347 $sql .= ' subprice, subprice_ttc, remise_percent, ';
3348 $sql .= ' info_bits, ';
3349 $sql .= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
3350 $sql .= ' ref_fourn,';
3351 $sql .= ' fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_subprice_ttc, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc, fk_unit)';
3352 $sql .= " VALUES (".((int) $this->fk_supplier_proposal).",";
3353 $sql .= " ".($this->fk_parent_line > 0 ? ((int) $this->fk_parent_line) : "null").",";
3354 $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
3355 $sql .= " '".$this->db->escape($this->desc)."',";
3356 $sql .= " ".($this->fk_product ? ((int) $this->fk_product) : "null").",";
3357 $sql .= " ".((int) $this->product_type).",";
3358 $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3359 $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3360 $sql .= " ".($this->fk_remise_except ? ((int) $this->fk_remise_except) : "null").",";
3361 $sql .= " ".(float) price2num($this->qty, 'MS').",";
3362 $sql .= " ".(float) price2num($this->tva_tx).",";
3363 $sql .= " '".$this->db->escape($this->vat_src_code)."',";
3364 $sql .= " ".(float) price2num($this->localtax1_tx).",";
3365 $sql .= " ".(float) price2num($this->localtax2_tx).",";
3366 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3367 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3368 $sql .= " ".(float) price2num($this->subprice, 'MU') .",";
3369 $sql .= " ".(float) price2num($this->subprice_ttc, 'MU') .",";
3370 $sql .= " ".((float) $this->remise_percent).",";
3371 $sql .= " ".(isset($this->info_bits) ? ((int) $this->info_bits) : "null").",";
3372 $sql .= " ".(float) price2num($this->total_ht, 'MT').",";
3373 $sql .= " ".(float) price2num($this->total_tva, 'MT').",";
3374 $sql .= " ".(float) price2num($this->total_localtax1, 'MT').",";
3375 $sql .= " ".(float) price2num($this->total_localtax2, 'MT').",";
3376 $sql .= " ".(float) price2num($this->total_ttc, 'MT').",";
3377 $sql .= " ".(!empty($this->fk_fournprice) ? ((int) $this->fk_fournprice) : "null").",";
3378 $sql .= " ".(isset($this->pa_ht) ? (float) price2num($this->pa_ht, 'MU') : "null").",";
3379 $sql .= ' '.((int) $this->special_code).',';
3380 $sql .= ' '.((int) $this->rang).',';
3381 $sql .= " '".$this->db->escape($this->ref_fourn)."'";
3382 $sql .= ", ".($this->fk_multicurrency > 0 ? ((int) $this->fk_multicurrency) : 'null');
3383 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3384 $sql .= ", ".(float) price2num($this->multicurrency_subprice, 'CU');
3385 $sql .= ", ".(float) price2num($this->multicurrency_subprice_ttc, 'CU');
3386 $sql .= ", ".(float) price2num($this->multicurrency_total_ht, 'CT');
3387 $sql .= ", ".(float) price2num($this->multicurrency_total_tva, 'CT');
3388 $sql .= ", ".(float) price2num($this->multicurrency_total_ttc, 'CT');
3389 $sql .= ", ".($this->fk_unit ? ((int) $this->fk_unit) : 'null');
3390 $sql .= ')';
3391
3392 dol_syslog(get_class($this).'::insert', LOG_DEBUG);
3393 $resql = $this->db->query($sql);
3394 if ($resql) {
3395 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'supplier_proposaldet');
3396
3397 if (!$error) {
3398 $result = $this->insertExtraFields();
3399 if ($result < 0) {
3400 $error++;
3401 }
3402 }
3403
3404 if (!$error && !$notrigger) {
3405 // Call trigger
3406 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_INSERT', $user);
3407 if ($result < 0) {
3408 $this->db->rollback();
3409 return -1;
3410 }
3411 // End call triggers
3412 }
3413
3414 $this->db->commit();
3415 return 1;
3416 } else {
3417 $this->error = $this->db->error()." sql=".$sql;
3418 $this->db->rollback();
3419 return -1;
3420 }
3421 }
3422
3429 public function delete($user)
3430 {
3431 $error = 0;
3432
3433 $this->db->begin();
3434
3435 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet";
3436 $sql .= " WHERE rowid = ".((int) $this->id);
3437
3438 if ($this->db->query($sql)) {
3439 // Remove extrafields
3440 if (!$error) {
3441 $result = $this->deleteExtraFields();
3442 if ($result < 0) {
3443 $error++;
3444 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3445 }
3446 }
3447
3448 // Call trigger
3449 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_DELETE', $user);
3450 if ($result < 0) {
3451 $this->db->rollback();
3452 return -1;
3453 }
3454 // End call triggers
3455
3456 $this->db->commit();
3457
3458 return 1;
3459 } else {
3460 $this->error = $this->db->error()." sql=".$sql;
3461 $this->db->rollback();
3462 return -1;
3463 }
3464 }
3465
3472 public function update($notrigger = 0)
3473 {
3474 global $user;
3475
3476 $error = 0;
3477
3478 // Clean parameters
3479 if (empty($this->tva_tx)) {
3480 $this->tva_tx = 0;
3481 }
3482 if (empty($this->localtax1_tx)) {
3483 $this->localtax1_tx = 0;
3484 }
3485 if (empty($this->localtax2_tx)) {
3486 $this->localtax2_tx = 0;
3487 }
3488 if (empty($this->total_localtax1)) {
3489 $this->total_localtax1 = 0;
3490 }
3491 if (empty($this->total_localtax2)) {
3492 $this->total_localtax2 = 0;
3493 }
3494 if (empty($this->localtax1_type)) {
3495 $this->localtax1_type = '';
3496 }
3497 if (empty($this->localtax2_type)) {
3498 $this->localtax2_type = '';
3499 }
3500 if (empty($this->marque_tx)) {
3501 $this->marque_tx = 0;
3502 }
3503 if (empty($this->marge_tx)) {
3504 $this->marge_tx = 0;
3505 }
3506 if (empty($this->remise_percent)) {
3507 $this->remise_percent = 0;
3508 }
3509 if (empty($this->info_bits)) {
3510 $this->info_bits = 0;
3511 }
3512 if (empty($this->special_code)) {
3513 $this->special_code = 0;
3514 }
3515 if (empty($this->fk_parent_line)) {
3516 $this->fk_parent_line = 0;
3517 }
3518 if (empty($this->fk_fournprice)) {
3519 $this->fk_fournprice = 0;
3520 }
3521 if (empty($this->fk_unit)) {
3522 $this->fk_unit = 0;
3523 }
3524 if (empty($this->subprice)) {
3525 $this->subprice = 0;
3526 }
3527
3528 if (empty($this->pa_ht)) {
3529 $this->pa_ht = 0;
3530 }
3531
3532 // if buy price not defined, define buyprice as configured in margin admin
3533 if ($this->pa_ht == 0) {
3534 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3535 if ($result < 0) {
3536 return $result;
3537 } else {
3538 $this->pa_ht = $result;
3539 }
3540 }
3541
3542 $this->db->begin();
3543
3544 // Update line in database
3545 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3546 $sql .= " description = '".$this->db->escape($this->desc)."'";
3547 $sql .= " , label = ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
3548 $sql .= " , product_type = ".((int) $this->product_type);
3549 $sql .= " , date_start = ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null");
3550 $sql .= " , date_end = ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null");
3551 $sql .= " , tva_tx = '".price2num($this->tva_tx)."'";
3552 $sql .= " , localtax1_tx = ".(float) price2num($this->localtax1_tx);
3553 $sql .= " , localtax2_tx = ".(float) price2num($this->localtax2_tx);
3554 $sql .= " , localtax1_type = '".$this->db->escape($this->localtax1_type)."'";
3555 $sql .= " , localtax2_type = '".$this->db->escape($this->localtax2_type)."'";
3556 $sql .= " , qty = ".(float) price2num($this->qty);
3557 $sql .= " , subprice = ".(float) price2num($this->subprice);
3558 $sql .= " , subprice_ttc = ".(float) price2num($this->subprice_ttc);
3559 $sql .= " , remise_percent = ".(float) price2num($this->remise_percent);
3560 $sql .= " , info_bits = ".((int) $this->info_bits);
3561 if (empty($this->skip_update_total)) {
3562 $sql .= " , total_ht = ".(float) price2num($this->total_ht);
3563 $sql .= " , total_tva = ".(float) price2num($this->total_tva);
3564 $sql .= " , total_ttc = ".(float) price2num($this->total_ttc);
3565 $sql .= " , total_localtax1 = ".(float) price2num($this->total_localtax1);
3566 $sql .= " , total_localtax2 = ".(float) price2num($this->total_localtax2);
3567 }
3568 $sql .= " , fk_product_fournisseur_price = ".(!empty($this->fk_fournprice) ? "'".$this->db->escape((string) $this->fk_fournprice)."'" : "null");
3569 $sql .= " , buy_price_ht = ".(float) price2num($this->pa_ht);
3570 $sql .= " , special_code = ".((int) $this->special_code);
3571 $sql .= " , fk_parent_line = ".($this->fk_parent_line > 0 ? ((int) $this->fk_parent_line) : "null");
3572 if (!empty($this->rang)) {
3573 $sql .= ", rang = ".((int) $this->rang);
3574 }
3575 $sql .= " , ref_fourn = ".(!empty($this->ref_fourn) ? "'".$this->db->escape($this->ref_fourn)."'" : "null");
3576 $sql .= " , fk_unit = ".($this->fk_unit ? (int) $this->fk_unit : 'null');
3577
3578 // Multicurrency
3579 $sql .= " , multicurrency_subprice = ".(float) price2num($this->multicurrency_subprice);
3580 $sql .= " , multicurrency_subprice_ttc = ".(float) price2num($this->multicurrency_subprice_ttc);
3581 $sql .= " , multicurrency_total_ht = ".(float) price2num($this->multicurrency_total_ht);
3582 $sql .= " , multicurrency_total_tva = ".(float) price2num($this->multicurrency_total_tva);
3583 $sql .= " , multicurrency_total_ttc = ".(float) price2num($this->multicurrency_total_ttc);
3584
3585 $sql .= " WHERE rowid = ".((int) $this->id);
3586
3587 dol_syslog(get_class($this)."::update", LOG_DEBUG);
3588 $resql = $this->db->query($sql);
3589 if ($resql) {
3590 if (!$error) {
3591 $result = $this->insertExtraFields();
3592 if ($result < 0) {
3593 $error++;
3594 }
3595 }
3596
3597 if (!$error && !$notrigger) {
3598 // Call trigger
3599 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_MODIFY', $user);
3600 if ($result < 0) {
3601 $this->db->rollback();
3602 return -1;
3603 }
3604 // End call triggers
3605 }
3606
3607 $this->db->commit();
3608 return 1;
3609 } else {
3610 $this->error = $this->db->error();
3611 $this->db->rollback();
3612 return -2;
3613 }
3614 }
3615
3616 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3623 public function update_total()
3624 {
3625 // phpcs:enable
3626 $this->db->begin();
3627
3628 // Update line in database
3629 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3630 $sql .= " total_ht = ".(float) price2num($this->total_ht, 'MT');
3631 $sql .= ",total_tva = ".(float) price2num($this->total_tva, 'MT');
3632 $sql .= ",total_ttc = ".(float) price2num($this->total_ttc, 'MT');
3633 $sql .= " WHERE rowid = ".((int) $this->id);
3634
3635 dol_syslog("SupplierProposalLine::update_total", LOG_DEBUG);
3636
3637 $resql = $this->db->query($sql);
3638 if ($resql) {
3639 $this->db->commit();
3640 return 1;
3641 } else {
3642 $this->error = $this->db->error();
3643 $this->db->rollback();
3644 return -2;
3645 }
3646 }
3647}
$object ref
Definition info.php:90
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) The parameters are already suppo...
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)
Reopen the commercial 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 lines 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:169
global $mysoc
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
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:65
dol_delete_preview($object)
Delete all preview files linked to object instance.
$date_start
Variables from include:
dol_now($mode='gmt')
Return date for now.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_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.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0, $nodefault=0)
Return value of a param into GET or POST supervariable.
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', ...)
GETPOSTINT($paramname, $method=0, $nodefault=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
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.
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.
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)
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
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...
Definition html.lib.php:172
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
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, $notused, $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
print $langs trans('Date')." left Ref 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 Paid right PaymentTypeShortLIQ right SELECT p pos_change as p datep as date
Definition receipt.php:487