dolibarr 24.0.0-beta
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 *
22 * This program is free software; you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License as published by
24 * the Free Software Foundation; either version 3 of the License, or
25 * (at your option) any later version.
26 *
27 * This program is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU General Public License for more details.
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this program. If not, see <https://www.gnu.org/licenses/>.
34 */
35
41require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
42require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
43require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
44require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
45require_once DOL_DOCUMENT_ROOT.'/margin/lib/margins.lib.php';
46require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
47require_once DOL_DOCUMENT_ROOT.'/core/class/commonincoterm.class.php';
48require_once DOL_DOCUMENT_ROOT.'/subtotals/class/commonsubtotal.class.php';
53{
54 use CommonIncoterm, CommonSubtotal;
55
59 public $element = 'supplier_proposal';
60
64 public $table_element = 'supplier_proposal';
65
69 public $table_element_line = 'supplier_proposaldet';
70
74 public $class_element_line = 'SupplierProposalLine';
78 public $fk_element = 'fk_supplier_proposal';
79
83 public $picto = 'supplier_proposal';
84
89 public $restrictiononfksoc = 1;
90
94 protected $table_ref_field = 'ref';
95
99 public $socid; // Id client
100
106 public $author;
107
111 public $ref_fourn;
115 public $ref_supplier;
116
121 public $statut; // 0 (draft), 1 (validated), 2 (signed), 3 (not signed), 4 (processed/billed)
122
126 public $date;
127
131 public $delivery_date;
132
138 public $datec;
139
145 public $datev;
146
150 public $user_author_id;
151
157 public $price;
158
164 public $tva;
165
171 public $total;
172
176 public $cond_reglement_code;
180 public $cond_reglement_doc; // label doc
181
185 public $mode_reglement_code;
190 public $mode_reglement;
191
197 public $deposit_percent;
198
199
203 public $extraparams = array();
204
208 public $lines = array();
209
213 public $line;
214
215 public $labelStatus = array();
216 public $labelStatusShort = array();
217
221 public $nbtodo;
225 public $nbtodolate;
226
227 // Multicurrency
231 public $fk_multicurrency;
232
236 public $multicurrency_code;
240 public $multicurrency_tx;
244 public $multicurrency_total_ht;
248 public $multicurrency_total_tva;
252 public $multicurrency_total_ttc;
253
254 public $fields = array(
255 'ref' => array('type' => 'varchar(255)', 'label' => 'Ref', 'enabled' => 1, 'visible' => 1, 'showoncombobox' => 1, 'position' => 25, 'searchall' => 1),
256 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 750, 'searchall' => 1),
257 );
258
262 const STATUS_DRAFT = 0;
263
268
272 const STATUS_SIGNED = 2;
273
278
282 const STATUS_CLOSE = 4;
283
284
285
293 public function __construct($db, $socid = 0, $supplier_proposalid = 0)
294 {
295 $this->db = $db;
296
297 $this->ismultientitymanaged = 1;
298 $this->socid = $socid;
299 $this->id = $supplier_proposalid;
300 }
301
302
303 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
314 public function add_product($idproduct, $qty, $remise_percent = 0)
315 {
316 // phpcs:enable
317 global $mysoc;
318
319 if (!$qty) {
320 $qty = 1;
321 }
322
323 dol_syslog(get_class($this)."::add_product $idproduct, $qty, $remise_percent");
324 if ($idproduct > 0) {
325 $prod = new Product($this->db);
326 $prod->fetch($idproduct);
327
328 $productdesc = $prod->description;
329
330 $tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
331 $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
332 if (empty($tva_tx)) {
333 $tva_npr = 0;
334 }
335 $localtax1_tx = get_localtax($tva_tx, 1, $mysoc, $this->thirdparty, $tva_npr);
336 $localtax2_tx = get_localtax($tva_tx, 2, $mysoc, $this->thirdparty, $tva_npr);
337
338 // multiprix
339 if (getDolGlobalString('PRODUIT_MULTIPRICES') && $this->thirdparty->price_level) {
340 $price = $prod->multiprices[$this->thirdparty->price_level];
341 } else {
342 $price = $prod->price;
343 }
344
345 $line = new SupplierProposalLine($this->db);
346
347 $line->fk_product = $idproduct;
348 $line->desc = $productdesc;
349 $line->qty = $qty;
350 $line->subprice = $price;
351 $line->remise_percent = $remise_percent;
352 $line->tva_tx = $tva_tx;
353
354 $this->lines[] = $line;
355 return 1;
356 }
357 return -1;
358 }
359
360 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
367 public function insert_discount($idremise)
368 {
369 // phpcs:enable
370 global $langs;
371
372 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
373 include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
374
375 $this->db->begin();
376
377 $remise = new DiscountAbsolute($this->db);
378 $result = $remise->fetch($idremise);
379
380 if ($result > 0) {
381 if ($remise->fk_facture) { // Protection against multiple submission
382 $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
383 $this->db->rollback();
384 return -5;
385 }
386
387 $supplier_proposalligne = new SupplierProposalLine($this->db);
388 $supplier_proposalligne->fk_supplier_proposal = $this->id;
389 $supplier_proposalligne->fk_remise_except = $remise->id;
390 $supplier_proposalligne->desc = $remise->description; // Description of the proposal line
391 $supplier_proposalligne->tva_tx = $remise->tva_tx;
392 $supplier_proposalligne->localtax1_tx = $remise->localtax1_tx;
393 $supplier_proposalligne->localtax1_type = $remise->localtax1_type;
394 $supplier_proposalligne->localtax2_tx = $remise->localtax1_tx;
395 $supplier_proposalligne->localtax2_type = $remise->localtax1_type;
396 $supplier_proposalligne->subprice = -(float) $remise->amount_ht;
397 $supplier_proposalligne->fk_product = 0; // Predefined Product ID
398 $supplier_proposalligne->qty = 1;
399 $supplier_proposalligne->remise_percent = 0;
400 $supplier_proposalligne->rang = -1;
401 $supplier_proposalligne->info_bits = 2;
402
403 $supplier_proposalligne->total_ht = -(float) $remise->amount_ht;
404 $supplier_proposalligne->total_tva = -(float) $remise->amount_tva;
405 $supplier_proposalligne->total_ttc = -(float) $remise->amount_ttc;
406 $supplier_proposalligne->total_localtax1 = -(float) $remise->total_localtax1;
407 $supplier_proposalligne->total_localtax2 = -(float) $remise->total_localtax2;
408
409 $result = $supplier_proposalligne->insert();
410 if ($result > 0) {
411 $result = $this->update_price(1);
412 if ($result > 0) {
413 $this->db->commit();
414 return 1;
415 } else {
416 $this->db->rollback();
417 return -1;
418 }
419 } else {
420 $this->setErrorsFromObject($supplier_proposalligne);
421 $this->db->rollback();
422 return -2;
423 }
424 } else {
425 $this->db->rollback();
426 return -2;
427 }
428 }
429
466 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)
467 {
468 global $mysoc, $langs;
469
470 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");
471 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
472
473 // Clean parameters
474 if (empty($remise_percent)) {
475 $remise_percent = 0;
476 }
477 if (empty($qty)) {
478 $qty = 0;
479 }
480 if (empty($info_bits)) {
481 $info_bits = 0;
482 }
483 if (empty($rang)) {
484 $rang = 0;
485 }
486 if (empty($fk_parent_line) || $fk_parent_line < 0) {
487 $fk_parent_line = 0;
488 }
489 if (empty($pu_ht)) {
490 $pu_ht = 0;
491 }
492
493 $remise_percent = price2num($remise_percent);
494 $qty = (float) price2num($qty);
495 $pu_ht = price2num($pu_ht);
496 $pu_ttc = price2num($pu_ttc);
497 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
498 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
499 }
500 $txlocaltax1 = price2num($txlocaltax1);
501 $txlocaltax2 = price2num($txlocaltax2);
502 $pa_ht = price2num($pa_ht);
503 if ($price_base_type == 'HT') {
504 $pu = $pu_ht;
505 } else {
506 $pu = $pu_ttc;
507 }
508
509 // Check parameters
510 if ($type < 0) {
511 return -1;
512 }
513
514 if ($this->status == self::STATUS_DRAFT) {
515 $this->db->begin();
516
517 if ($fk_product > 0) {
518 if (getDolGlobalInt('SUPPLIER_PROPOSAL_WITH_PREDEFINED_PRICES_ONLY') == 1) { // Not the common case
519 // Check quantity is enough
520 dol_syslog(get_class($this)."::addline we check supplier prices fk_product=".$fk_product." fk_fournprice=".$fk_fournprice." qty=".$qty." ref_supplier=".$ref_supplier);
521 $productsupplier = new ProductFournisseur($this->db);
522 if ($productsupplier->fetch($fk_product) > 0) {
523 $product_type = $productsupplier->type;
524 $label = $productsupplier->label;
525 $fk_prod_fourn_price = $fk_fournprice;
526
527 // We use 'none' instead of $ref_supplier, because fourn_ref may not exists anymore. So we will take the first supplier price ok.
528 // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
529 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
530 $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
531 if ($result > 0) {
532 $pu = $productsupplier->fourn_pu; // Unit price supplier price set by get_buyprice
533 $ref_supplier = $productsupplier->ref_supplier; // Ref supplier price set by get_buyprice
534 // is remise percent not keyed but present for the product we add it
535 if ($remise_percent == 0 && $productsupplier->remise_percent != 0) {
536 $remise_percent = $productsupplier->remise_percent;
537 }
538 }
539 if ($result == 0) { // If result == 0, we failed to found the supplier reference price
540 $langs->load("errors");
541 $this->error = "Ref ".$productsupplier->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
542 $this->db->rollback();
543 dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
544 //$pu = $productsupplier->fourn_pu; // We do not overwrite unit price
545 //$ref = $productsupplier_fourn; // We do not overwrite ref supplier price
546 return -1;
547 }
548 if ($result == -1) {
549 $langs->load("errors");
550 $this->error = "Ref ".$productsupplier->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
551 $this->db->rollback();
552 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
553 return -1;
554 }
555 if ($result < -1) {
556 $this->setErrorsFromObject($productsupplier);
557 $this->db->rollback();
558 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
559 return -1;
560 }
561 } else {
562 $this->setErrorsFromObject($productsupplier);
563 $this->db->rollback();
564 return -1;
565 }
566 }
567 } else {
568 $product_type = $type;
569 }
570
571 // Calculation of the gross total (TTC) and VAT for the line from qty, pu, remise_percent and txtva
572 // VERY IMPORTANT: It's at the time of line insertion that we must store the net, VAT, and gross amounts,
573 // and this is done at the line level, which has its own VAT rate
574
575 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
576
577 // Clean vat code
578 $reg = array();
579 $vat_src_code = '';
580 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
581 $vat_src_code = $reg[1];
582 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
583 }
584
585 if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
586 $pu = 0;
587 }
588
589 $tabprice = calcul_price_total(
590 $qty,
591 $pu,
592 (float) $remise_percent,
593 $txtva,
594 (float) $txlocaltax1,
595 (float) $txlocaltax2,
596 0,
597 $price_base_type,
598 $info_bits,
599 $type,
600 $this->thirdparty,
601 $localtaxes_type,
602 100,
603 (float) $this->multicurrency_tx,
604 $pu_ht_devise
605 );
606 $total_ht = $tabprice[0];
607 $total_tva = $tabprice[1];
608 $total_ttc = $tabprice[2];
609 $total_localtax1 = $tabprice[9];
610 $total_localtax2 = $tabprice[10];
611 $pu = $pu_ht = $tabprice[3];
612
613 // MultiCurrency
614 $multicurrency_total_ht = $tabprice[16];
615 $multicurrency_total_tva = $tabprice[17];
616 $multicurrency_total_ttc = $tabprice[18];
617 $pu_ht_devise = $tabprice[19];
618
619 // Rang to use
620 $ranktouse = $rang;
621 if ($ranktouse == -1) {
622 $rangmax = $this->line_max($fk_parent_line);
623 $ranktouse = $rangmax + 1;
624 }
625
626 // Insert line
627 $this->line = new SupplierProposalLine($this->db);
628
629 $this->line->fk_supplier_proposal = $this->id;
630 $this->line->label = $label;
631 $this->line->desc = $desc;
632 $this->line->qty = $qty;
633
634 $this->line->vat_src_code = $vat_src_code;
635 $this->line->tva_tx = $txtva;
636 $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
637 $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
638 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
639 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
640 $this->line->fk_product = $fk_product;
641 $this->line->remise_percent = $remise_percent;
642 $this->line->subprice = (float) $pu_ht;
643 $this->line->rang = $ranktouse;
644 $this->line->info_bits = $info_bits;
645 $this->line->total_ht = (float) $total_ht;
646 $this->line->total_tva = (float) $total_tva;
647 $this->line->total_localtax1 = (float) $total_localtax1;
648 $this->line->total_localtax2 = (float) $total_localtax2;
649 $this->line->total_ttc = (float) $total_ttc;
650 $this->line->product_type = $type;
651 $this->line->special_code = $special_code;
652 $this->line->fk_parent_line = $fk_parent_line;
653 $this->line->fk_unit = $fk_unit;
654 $this->line->origin = $origin;
655 $this->line->origin_type = $origin;
656 $this->line->origin_id = $origin_id;
657 $this->line->ref_fourn = $this->db->escape($ref_supplier);
658 $this->line->date_start = $date_start;
659 $this->line->date_end = $date_end;
660
661 // infos merge
662 if (!empty($fk_product) && $fk_product > 0 && empty($fk_fournprice) && empty($pa_ht)) {
663 // When fk_fournprice is 0, we take the lowest buying price
664 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
665 $productFournisseur = new ProductFournisseur($this->db);
666 $productFournisseur->find_min_price_product_fournisseur($fk_product);
667 $this->line->fk_fournprice = $productFournisseur->product_fourn_price_id;
668 } else {
669 $this->line->fk_fournprice = ($fk_fournprice > 0 ? $fk_fournprice : 0); // If fk_fournprice is -1, we will not use fk_fournprice
670 }
671 $this->line->pa_ht = $pa_ht;
672 //var_dump($this->line->fk_fournprice);exit;
673
674 // Multicurrency
675 $this->line->fk_multicurrency = $this->fk_multicurrency;
676 $this->line->multicurrency_code = $this->multicurrency_code;
677 $this->line->multicurrency_subprice = (float) $pu_ht_devise;
678 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
679 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
680 $this->line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
681
682 // Set the proposal line as optional
683 if (empty($qty) && empty($special_code)) {
684 $this->line->special_code = 3;
685 }
686
687 if (is_array($array_options) && count($array_options) > 0) {
688 $this->line->array_options = $array_options;
689 }
690
691 $result = $this->line->insert();
692 if ($result > 0) {
693 // Reorder if child line
694 if (!empty($fk_parent_line)) {
695 $this->line_order(true, 'DESC');
696 } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) { // Update all rank of all other lines
697 $linecount = count($this->lines);
698 for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
699 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
700 }
701 }
702
703 // Update denormalized information at the present proposal level
704 $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.
705 if ($result > 0) {
706 $this->db->commit();
707 return $this->line->id;
708 } else {
709 $this->db->rollback();
710 return -1;
711 }
712 } else {
713 $this->setErrorsFromObject($this->line);
714 $this->db->rollback();
715 return -2;
716 }
717 } else {
718 $this->error = 'BadStatusOfObjectToAddLine';
719 return -5;
720 }
721 }
722
723
750 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)
751 {
752 global $mysoc;
753
754 dol_syslog(get_class($this)."::updateLine $rowid, $pu, $qty, $remise_percent, $txtva, $desc, $price_base_type, $info_bits");
755 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
756
757 // Clean parameters
758 $remise_percent = price2num($remise_percent);
759 $qty = (float) price2num($qty);
760 $pu = price2num($pu);
761 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
762 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
763 }
764 $txlocaltax1 = price2num($txlocaltax1);
765 $txlocaltax2 = price2num($txlocaltax2);
766 $pa_ht = price2num($pa_ht);
767 if (empty($qty) && empty($special_code)) {
768 $special_code = 3; // Set option tag
769 }
770 if (!empty($qty) && $special_code == 3) {
771 $special_code = 0; // Remove option tag
772 }
773
774 if ($this->status == 0) {
775 $this->db->begin();
776
777 // Calculation of the gross total (TTC) and VAT for the line from qty, pu, remise_percent and txtva
778 // VERY IMPORTANT: It's at the time of line insertion that we must store the net, VAT, and gross amounts,
779 // and this is done at the line level, which has its own VAT rate
780
781 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
782
783 // Clean vat code
784 $reg = array();
785 $vat_src_code = '';
786 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
787 $vat_src_code = $reg[1];
788 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
789 }
790
791 if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
792 $pu = 0;
793 }
794
795 $tabprice = calcul_price_total(
796 $qty,
797 $pu,
798 (float) $remise_percent,
799 $txtva,
800 (float) $txlocaltax1,
801 (float) $txlocaltax2,
802 0,
803 $price_base_type,
804 $info_bits,
805 $type,
806 $this->thirdparty,
807 $localtaxes_type,
808 100,
809 (float) $this->multicurrency_tx,
810 $pu_ht_devise
811 );
812 $total_ht = $tabprice[0];
813 $total_tva = $tabprice[1];
814 $total_ttc = $tabprice[2];
815 $total_localtax1 = $tabprice[9];
816 $total_localtax2 = $tabprice[10];
817 $pu_ht = $tabprice[3];
818 $pu_tva = $tabprice[4];
819 $pu_ttc = $tabprice[5];
820
821 // MultiCurrency
822 $multicurrency_total_ht = $tabprice[16];
823 $multicurrency_total_tva = $tabprice[17];
824 $multicurrency_total_ttc = $tabprice[18];
825 $pu_ht_devise = $tabprice[19];
826
827 $pu = $pu_ht;
828 if ($price_base_type == 'TTC') {
829 $pu = $pu_ttc;
830 }
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;
866 $this->line->info_bits = $info_bits;
867 $this->line->total_ht = (float) $total_ht;
868 $this->line->total_tva = (float) $total_tva;
869 $this->line->total_localtax1 = (float) $total_localtax1;
870 $this->line->total_localtax2 = (float) $total_localtax2;
871 $this->line->total_ttc = (float) $total_ttc;
872 $this->line->special_code = $special_code;
873 $this->line->fk_parent_line = $fk_parent_line;
874 $this->line->skip_update_total = $skip_update_total;
875 $this->line->ref_fourn = $ref_supplier;
876 $this->line->fk_unit = $fk_unit;
877
878 // infos merge
879 if (!empty($fk_product) && $fk_product > 0 && empty($fk_fournprice) && empty($pa_ht)) {
880 // by external module, take lowest buying price
881 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
882 $productFournisseur = new ProductFournisseur($this->db);
883 $productFournisseur->find_min_price_product_fournisseur($fk_product);
884 $this->line->fk_fournprice = $productFournisseur->product_fourn_price_id;
885 } else {
886 $this->line->fk_fournprice = $fk_fournprice;
887 }
888 $this->line->pa_ht = $pa_ht;
889
890 if (is_array($array_options) && count($array_options) > 0) {
891 // We replace values in this->line->array_options only for entries defined into $array_options
892 foreach ($array_options as $key => $value) {
893 $this->line->array_options[$key] = $array_options[$key];
894 }
895 }
896
897 // Multicurrency
898 $this->line->multicurrency_subprice = (float) $pu_ht_devise;
899 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
900 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
901 $this->line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
902
903 $result = $this->line->update();
904 if ($result > 0) {
905 // Reorder if child line
906 if (!empty($fk_parent_line)) {
907 $this->line_order(true, 'DESC');
908 }
909
910 $this->update_price(1);
911
912 $this->db->commit();
913 return $result;
914 } else {
915 $this->error = $this->db->error();
916 $this->db->rollback();
917 return -1;
918 }
919 } else {
920 dol_syslog(get_class($this)."::updateline Erreur -2 SupplierProposal en mode incompatible pour cette action");
921 return -2;
922 }
923 }
924
925
932 public function deleteLine($lineid)
933 {
934 global $user;
935
936 if ($this->status == 0) {
937 $line = new SupplierProposalLine($this->db);
938
939 // For triggers
940 $line->fetch($lineid);
941
942 if ($line->delete($user) > 0) {
943 $this->update_price(1);
944
945 return 1;
946 } else {
947 return -1;
948 }
949 } else {
950 return -2;
951 }
952 }
953
954
963 public function create($user, $notrigger = 0)
964 {
965 global $conf, $mysoc, $hookmanager;
966 $error = 0;
967
968 $now = dol_now();
969
970 dol_syslog(get_class($this)."::create");
971
972 // Check parameters
973 $result = $this->fetch_thirdparty();
974 if ($result < 0) {
975 $this->error = "Failed to fetch company";
976 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
977 return -3;
978 }
979 if (!empty($this->ref)) { // We check that ref is not already used
980 $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
981 if ($result > 0) {
982 $this->error = 'ErrorRefAlreadyExists';
983 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
984 $this->db->rollback();
985 return -1;
986 }
987 }
988
989 // Set tmp vars
990 $delivery_date = $this->delivery_date;
991
992 // Multicurrency
993 if (!empty($this->multicurrency_code)) {
994 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $now);
995 }
996 if (empty($this->fk_multicurrency)) {
997 $this->multicurrency_code = getDolCurrency();
998 $this->fk_multicurrency = 0;
999 $this->multicurrency_tx = 1;
1000 }
1001
1002 $this->db->begin();
1003
1004 // Insert into database
1005 $sql = "INSERT INTO ".MAIN_DB_PREFIX."supplier_proposal (";
1006 $sql .= "fk_soc";
1007 $sql .= ", price";
1008 $sql .= ", total_tva";
1009 $sql .= ", total_ttc";
1010 $sql .= ", datec";
1011 $sql .= ", ref";
1012 $sql .= ", fk_user_author";
1013 $sql .= ", note_private";
1014 $sql .= ", note_public";
1015 $sql .= ", model_pdf";
1016 $sql .= ", fk_cond_reglement";
1017 $sql .= ", deposit_percent";
1018 $sql .= ", fk_mode_reglement";
1019 $sql .= ", fk_account";
1020 $sql .= ", date_livraison";
1021 $sql .= ", fk_shipping_method";
1022 $sql .= ", fk_projet";
1023 $sql .= ", entity";
1024 $sql .= ", fk_multicurrency";
1025 $sql .= ", multicurrency_code";
1026 $sql .= ", multicurrency_tx";
1027 $sql .= ") ";
1028 $sql .= " VALUES (";
1029 $sql .= ((int) $this->socid);
1030 $sql .= ", 0";
1031 $sql .= ", 0";
1032 $sql .= ", 0";
1033 $sql .= ", '".$this->db->idate($now)."'";
1034 $sql .= ", '(PROV)'";
1035 $sql .= ", ".($user->id > 0 ? ((int) $user->id) : "null");
1036 $sql .= ", '".$this->db->escape($this->note_private)."'";
1037 $sql .= ", '".$this->db->escape($this->note_public)."'";
1038 $sql .= ", '".$this->db->escape($this->model_pdf)."'";
1039 $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : 'NULL');
1040 $sql .= ", ".(!empty($this->deposit_percent) ? "'" . $this->db->escape($this->deposit_percent) . "'" : 'NULL');
1041 $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : 'NULL');
1042 $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
1043 $sql .= ", ".(isDolTms($delivery_date) ? "'".$this->db->idate($delivery_date)."'" : "null");
1044 $sql .= ", ".($this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : 'NULL');
1045 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
1046 $sql .= ", ".((int) $conf->entity);
1047 $sql .= ", ".((int) $this->fk_multicurrency);
1048 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1049 $sql .= ", ".((float) $this->multicurrency_tx);
1050 $sql .= ")";
1051
1052 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1053 $resql = $this->db->query($sql);
1054 if ($resql) {
1055 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."supplier_proposal");
1056
1057 if ($this->id) {
1058 $this->ref = '(PROV'.$this->id.')';
1059 $sql = 'UPDATE '.MAIN_DB_PREFIX."supplier_proposal SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
1060
1061 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1062 $resql = $this->db->query($sql);
1063 if (!$resql) {
1064 $error++;
1065 }
1066
1067 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1068 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1069 }
1070
1071 // Add object linked
1072 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1073 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1074 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, ...))
1075 foreach ($tmp_origin_id as $origin_id) {
1076 $ret = $this->add_object_linked($origin, $origin_id);
1077 if (!$ret) {
1078 dol_print_error($this->db);
1079 $error++;
1080 }
1081 }
1082 }
1083 }
1084 }
1085
1086 /*
1087 * Insert products details into database
1088 */
1089 if (!$error) {
1090 $fk_parent_line = 0;
1091 $num = count($this->lines);
1092
1093 for ($i = 0; $i < $num; $i++) {
1094 // Reset fk_parent_line for no child products and special product
1095 if (($this->lines[$i]->product_type != 9 && empty($this->lines[$i]->fk_parent_line)) || $this->lines[$i]->product_type == 9) {
1096 $fk_parent_line = 0;
1097 }
1098
1099 $result = $this->addline(
1100 $this->lines[$i]->desc,
1101 $this->lines[$i]->subprice,
1102 $this->lines[$i]->qty,
1103 $this->lines[$i]->tva_tx,
1104 $this->lines[$i]->localtax1_tx,
1105 $this->lines[$i]->localtax2_tx,
1106 $this->lines[$i]->fk_product,
1107 $this->lines[$i]->remise_percent,
1108 'HT',
1109 0,
1110 0,
1111 $this->lines[$i]->product_type,
1112 $this->lines[$i]->rang,
1113 $this->lines[$i]->special_code,
1114 $fk_parent_line,
1115 $this->lines[$i]->fk_fournprice,
1116 $this->lines[$i]->pa_ht,
1117 empty($this->lines[$i]->label) ? '' : $this->lines[$i]->label, // deprecated
1118 $this->lines[$i]->array_options,
1119 $this->lines[$i]->ref_fourn,
1120 $this->lines[$i]->fk_unit,
1121 'supplier_proposal',
1122 $this->lines[$i]->rowid
1123 );
1124
1125 if ($result < 0) {
1126 $error++;
1127 $this->error = $this->db->error;
1128 dol_print_error($this->db);
1129 break;
1130 }
1131 // Defined the new fk_parent_line
1132 if ($result > 0 && $this->lines[$i]->product_type == 9) {
1133 $fk_parent_line = $result;
1134 }
1135 }
1136 }
1137
1138 if (!$error) {
1139 // Update denormalized data
1140 $resql = $this->update_price(1);
1141 if ($resql) {
1142 $action = 'update';
1143
1144 // Actions on extra fields
1145 if (!$error) {
1146 $result = $this->insertExtraFields();
1147 if ($result < 0) {
1148 $error++;
1149 }
1150 }
1151
1152 if (!$error && !$notrigger) {
1153 // Call trigger
1154 $result = $this->call_trigger('PROPOSAL_SUPPLIER_CREATE', $user);
1155 if ($result < 0) {
1156 $error++;
1157 }
1158 // End call triggers
1159 }
1160 } else {
1161 $this->error = $this->db->lasterror();
1162 $error++;
1163 }
1164 }
1165 } else {
1166 $this->error = $this->db->lasterror();
1167 $error++;
1168 }
1169
1170 if (!$error) {
1171 $this->db->commit();
1172 dol_syslog(get_class($this)."::create done id=".$this->id);
1173 return $this->id;
1174 } else {
1175 $this->db->rollback();
1176 return -2;
1177 }
1178 } else {
1179 $this->error = $this->db->lasterror();
1180 $this->db->rollback();
1181 return -1;
1182 }
1183 }
1184
1192 public function createFromClone(User $user, $fromid = 0)
1193 {
1194 global $hookmanager;
1195
1196 $error = 0;
1197 $now = dol_now();
1198
1199 $this->db->begin();
1200
1201 // get extrafields so they will be clone
1202 foreach ($this->lines as $line) {
1203 $line->fetch_optionals();
1204 }
1205
1206 // Load source object
1207 $objFrom = clone $this;
1208
1209 $objsoc = new Societe($this->db);
1210
1211 // Change socid if needed
1212 if (!empty($fromid) && $fromid != $this->socid) {
1213 if ($objsoc->fetch($fromid) > 0) {
1214 $this->socid = $objsoc->id;
1215 $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1216 $this->deposit_percent = (!empty($objsoc->deposit_percent) ? $objsoc->deposit_percent : 0);
1217 $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1218 unset($this->fk_project);
1219 }
1220
1221 // TODO Change product price if multi-prices
1222 } else {
1223 $objsoc->fetch($this->socid);
1224 }
1225
1226 $this->id = 0;
1227 $this->status = 0;
1228 $this->statut = 0;
1229
1230 if (!getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') || !is_readable(DOL_DOCUMENT_ROOT."/core/modules/supplier_proposal/" . getDolGlobalString('SUPPLIER_PROPOSAL_ADDON').".php")) {
1231 $this->error = 'ErrorSetupNotComplete';
1232 return -1;
1233 }
1234
1235 // Clear fields
1236 $this->user_author_id = $user->id;
1237 $this->user_validation_id = 0;
1238 $this->date = $now;
1239
1240 // Set ref
1241 require_once DOL_DOCUMENT_ROOT."/core/modules/supplier_proposal/" . getDolGlobalString('SUPPLIER_PROPOSAL_ADDON').'.php';
1242 $obj = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
1243 $modSupplierProposal = new $obj();
1244 '@phan-var-force ModeleNumRefSupplierProposal $modSupplierProposal';
1245 $this->ref = $modSupplierProposal->getNextValue($objsoc, $this);
1246
1247 // Create clone
1248 $this->context['createfromclone'] = 'createfromclone';
1249 $result = $this->create($user);
1250 if ($result < 0) {
1251 $error++;
1252 }
1253
1254 if (!$error) {
1255 // Hook of thirdparty module
1256 if (is_object($hookmanager)) {
1257 $parameters = array('objFrom' => $objFrom);
1258 $action = '';
1259 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1260 if ($reshook < 0) {
1261 $this->setErrorsFromObject($hookmanager);
1262 $error++;
1263 }
1264 }
1265 }
1266
1267 unset($this->context['createfromclone']);
1268
1269 // End
1270 if (!$error) {
1271 $this->db->commit();
1272 return $this->id;
1273 } else {
1274 $this->db->rollback();
1275 return -1;
1276 }
1277 }
1278
1286 public function fetch($rowid, $ref = '')
1287 {
1288 $sql = "SELECT p.rowid, p.entity, p.ref, p.fk_soc as socid";
1289 $sql .= ", p.total_ttc, p.total_tva, p.localtax1, p.localtax2, p.total_ht";
1290 $sql .= ", p.datec, GREATEST(p.tms, pef.tms) as date_modification";
1291 $sql .= ", p.date_valid as datev";
1292 $sql .= ", p.date_livraison as delivery_date";
1293 $sql .= ", p.model_pdf, p.extraparams";
1294 $sql .= ", p.note_private, p.note_public";
1295 $sql .= ", p.fk_projet as fk_project, p.fk_statut as status";
1296 $sql .= ", p.fk_user_author, p.fk_user_modif, p.fk_user_valid, p.fk_user_cloture";
1297 $sql .= ", p.fk_cond_reglement";
1298 $sql .= ", p.fk_mode_reglement";
1299 $sql .= ', p.fk_account';
1300 $sql .= ", p.fk_shipping_method";
1301 $sql .= ", p.last_main_doc";
1302 $sql .= ", p.fk_multicurrency, p.multicurrency_code, p.multicurrency_tx, p.multicurrency_total_ht, p.multicurrency_total_tva, p.multicurrency_total_ttc";
1303 $sql .= ", c.label as statut_label";
1304 $sql .= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc, p.deposit_percent";
1305 $sql .= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement";
1306 $sql .= " FROM ".MAIN_DB_PREFIX."c_propalst as c, ".MAIN_DB_PREFIX."supplier_proposal as p";
1307 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."supplier_proposal_extrafields as pef ON pef.fk_object=p.rowid";
1308 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id';
1309 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON p.fk_cond_reglement = cr.rowid';
1310 $sql .= " WHERE p.fk_statut = c.id";
1311 $sql .= " AND p.entity IN (".getEntity('supplier_proposal').")";
1312 if ($ref) {
1313 $sql .= " AND p.ref = '".$this->db->escape($ref)."'";
1314 } else {
1315 $sql .= " AND p.rowid = ".((int) $rowid);
1316 }
1317
1318 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1319 $resql = $this->db->query($sql);
1320 if ($resql) {
1321 if ($this->db->num_rows($resql)) {
1322 $obj = $this->db->fetch_object($resql);
1323
1324 $this->id = $obj->rowid;
1325 $this->entity = $obj->entity;
1326
1327 $this->ref = $obj->ref;
1328 $this->total_ht = $obj->total_ht;
1329 $this->total_tva = $obj->total_tva;
1330 $this->total_localtax1 = $obj->localtax1;
1331 $this->total_localtax2 = $obj->localtax2;
1332 $this->total_ttc = $obj->total_ttc;
1333 $this->socid = $obj->socid;
1334 $this->fk_project = $obj->fk_project;
1335 $this->model_pdf = $obj->model_pdf;
1336 $this->note = $obj->note_private; // TODO deprecated
1337 $this->note_private = $obj->note_private;
1338 $this->note_public = $obj->note_public;
1339 $this->statut = (int) $obj->status;
1340 $this->status = (int) $obj->status;
1341 $this->datec = $this->db->jdate($obj->datec); // TODO deprecated
1342 $this->datev = $this->db->jdate($obj->datev); // TODO deprecated
1343 $this->date_creation = $this->db->jdate($obj->datec); // Creation date
1344 $this->date_modification = $this->db->jdate($obj->date_modification); // Modification date
1345 $this->date = $this->date_creation;
1346 $this->date_validation = $this->db->jdate($obj->datev); // Validation date
1347 $this->delivery_date = $this->db->jdate($obj->delivery_date);
1348 $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1349
1350 $this->last_main_doc = $obj->last_main_doc;
1351 $this->mode_reglement_id = $obj->fk_mode_reglement;
1352 $this->mode_reglement_code = $obj->mode_reglement_code;
1353 $this->mode_reglement = $obj->mode_reglement;
1354 $this->deposit_percent = $obj->deposit_percent;
1355 $this->fk_account = ($obj->fk_account > 0) ? $obj->fk_account : null;
1356 $this->cond_reglement_id = $obj->fk_cond_reglement;
1357 $this->cond_reglement_code = $obj->cond_reglement_code;
1358 $this->cond_reglement = $obj->cond_reglement;
1359 $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1360
1361 $this->extraparams = (array) (!empty($obj->extraparams) ? json_decode($obj->extraparams, true) : array());
1362
1363 $this->user_author_id = $obj->fk_user_author;
1364 $this->user_modification_id = $obj->fk_user_modif;
1365 $this->user_validation_id = $obj->fk_user_valid;
1366 $this->user_closing_id = $obj->fk_user_cloture;
1367
1368 // Multicurrency
1369 $this->fk_multicurrency = $obj->fk_multicurrency;
1370 $this->multicurrency_code = $obj->multicurrency_code;
1371 $this->multicurrency_tx = $obj->multicurrency_tx;
1372 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1373 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1374 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1375
1376 $this->db->free($resql);
1377
1378 // Retrieve all extrafield
1379 // fetch optionals attributes and labels
1380 $this->fetch_optionals();
1381
1382 $this->lines = array();
1383
1384 // Lines of supplier proposals
1385 $sql = "SELECT d.rowid, d.fk_supplier_proposal, d.fk_parent_line, d.label as custom_label, d.description, d.price, d.tva_tx, d.localtax1_tx, d.localtax2_tx, d.qty, d.fk_remise_except, d.remise_percent, d.subprice, d.fk_product,";
1386 $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,";
1387 $sql .= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label,';
1388 $sql .= ' d.ref_fourn as ref_produit_fourn, d.extraparams,';
1389 $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';
1390 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposaldet as d";
1391 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON d.fk_product = p.rowid";
1392 $sql .= " WHERE d.fk_supplier_proposal = ".((int) $this->id);
1393 $sql .= " ORDER by d.rang";
1394
1395 $result = $this->db->query($sql);
1396 if ($result) {
1397 $num = $this->db->num_rows($result);
1398 $i = 0;
1399
1400 while ($i < $num) {
1401 $objp = $this->db->fetch_object($result);
1402
1403 $line = new SupplierProposalLine($this->db);
1404
1405 $line->rowid = $objp->rowid; // deprecated
1406 $line->id = $objp->rowid;
1407 $line->fk_supplier_proposal = $objp->fk_supplier_proposal;
1408 $line->fk_parent_line = $objp->fk_parent_line;
1409 $line->product_type = $objp->product_type;
1410 $line->label = $objp->custom_label;
1411 $line->desc = $objp->description; // Description of the line
1412 $line->qty = $objp->qty;
1413 $line->tva_tx = $objp->tva_tx;
1414 $line->localtax1_tx = $objp->localtax1_tx;
1415 $line->localtax2_tx = $objp->localtax2_tx;
1416 $line->subprice = $objp->subprice;
1417 $line->fk_remise_except = $objp->fk_remise_except;
1418 $line->remise_percent = $objp->remise_percent;
1419
1420 $line->info_bits = $objp->info_bits;
1421 $line->total_ht = $objp->total_ht;
1422 $line->total_tva = $objp->total_tva;
1423 $line->total_localtax1 = $objp->total_localtax1;
1424 $line->total_localtax2 = $objp->total_localtax2;
1425 $line->total_ttc = $objp->total_ttc;
1426 $line->fk_fournprice = $objp->fk_fournprice;
1427 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1428 $line->pa_ht = $marginInfos[0];
1429 $line->marge_tx = $marginInfos[1];
1430 $line->marque_tx = $marginInfos[2];
1431 $line->special_code = $objp->special_code;
1432 $line->rang = $objp->rang;
1433
1434 $line->fk_product = $objp->fk_product;
1435
1436 $line->ref = $objp->product_ref; // deprecated
1437 $line->product_ref = $objp->product_ref;
1438 $line->libelle = $objp->product_label; // deprecated
1439 $line->product_label = $objp->product_label;
1440 $line->product_desc = $objp->product_desc; // Description produit
1441 $line->fk_product_type = $objp->fk_product_type;
1442
1443 $line->ref_fourn = $objp->ref_produit_fourn;
1444
1445 $line->extraparams = !empty($objp->extraparams) ? (array) json_decode($objp->extraparams, true) : array();
1446
1447 // Multicurrency
1448 $line->fk_multicurrency = $objp->fk_multicurrency;
1449 $line->multicurrency_code = $objp->multicurrency_code;
1450 $line->multicurrency_subprice = $objp->multicurrency_subprice;
1451 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
1452 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
1453 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
1454 $line->fk_unit = $objp->fk_unit;
1455
1456 $line->fetch_optionals();
1457
1458 $this->lines[$i] = $line;
1459
1460 $i++;
1461 }
1462 $this->db->free($result);
1463 } else {
1464 $this->error = $this->db->error();
1465 return -1;
1466 }
1467
1468 return 1;
1469 }
1470
1471 $this->error = "Record Not Found";
1472 return 0;
1473 } else {
1474 $this->error = $this->db->error();
1475 return -1;
1476 }
1477 }
1478
1486 public function valid($user, $notrigger = 0)
1487 {
1488 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1489
1490 global $conf;
1491
1492 $error = 0;
1493 $now = dol_now();
1494
1495 if ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'creer'))
1496 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'validate_advance'))) {
1497 $this->db->begin();
1498
1499 // Numbering module definition
1500 $soc = new Societe($this->db);
1501 $result = $soc->fetch($this->socid);
1502
1503 if ($result < 0) {
1504 return -1;
1505 }
1506 if (!getDolGlobalBool('SUPPLIER_PROPOSAL_NOCHECK_ONBUY_PRODUCTS_ONVALID') && !$this->checkActiveProductInLines('onbuy')) {
1507 dol_syslog(get_class($this)."::valid checkActiveProductInLines ".$this->error, LOG_INFO);
1508 return -1;
1509 }
1510 // Define new ref
1511 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1512 $num = $this->getNextNumRef($soc);
1513 } else {
1514 $num = (string) $this->ref;
1515 }
1516 $this->newref = dol_sanitizeFileName($num);
1517
1518 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1519 $sql .= " SET ref = '".$this->db->escape($num)."',";
1520 $sql .= " fk_statut = 1, date_valid='".$this->db->idate($now)."', fk_user_valid=".((int) $user->id);
1521 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1522
1523 dol_syslog(get_class($this)."::valid", LOG_DEBUG);
1524 $resql = $this->db->query($sql);
1525 if (!$resql) {
1526 dol_print_error($this->db);
1527 $error++;
1528 }
1529
1530 // Trigger calls
1531 if (!$error && !$notrigger) {
1532 // Call trigger
1533 $result = $this->call_trigger('PROPOSAL_SUPPLIER_VALIDATE', $user);
1534 if ($result < 0) {
1535 $error++;
1536 }
1537 // End call triggers
1538 }
1539
1540 if (!$error) {
1541 $this->oldref = $this->ref;
1542
1543 // Rename directory if dir was a temporary ref
1544 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1545 // Now we rename also files into index
1546 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'supplier_proposal/".$this->db->escape($this->newref)."'";
1547 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1548 $resql = $this->db->query($sql);
1549 if (!$resql) {
1550 $error++;
1551 $this->error = $this->db->lasterror();
1552 }
1553 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'supplier_proposal/".$this->db->escape($this->newref)."'";
1554 $sql .= " WHERE filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1555 $resql = $this->db->query($sql);
1556 if (!$resql) {
1557 $error++;
1558 $this->error = $this->db->lasterror();
1559 }
1560
1561 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1562 $oldref = dol_sanitizeFileName($this->ref);
1563 $newref = dol_sanitizeFileName($num);
1564 $dirsource = $conf->supplier_proposal->dir_output.'/'.$oldref;
1565 $dirdest = $conf->supplier_proposal->dir_output.'/'.$newref;
1566 if (!$error && file_exists($dirsource)) {
1567 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
1568 if (@rename($dirsource, $dirdest)) {
1569 dol_syslog("Rename ok");
1570 // Rename docs starting with $oldref with $newref
1571 $listoffiles = dol_dir_list($conf->supplier_proposal->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1572 foreach ($listoffiles as $fileentry) {
1573 $dirsource = $fileentry['name'];
1574 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1575 $dirsource = $fileentry['path'].'/'.$dirsource;
1576 $dirdest = $fileentry['path'].'/'.$dirdest;
1577 @rename($dirsource, $dirdest);
1578 }
1579 }
1580 }
1581 }
1582
1583 $this->ref = $num;
1584 $this->statut = self::STATUS_VALIDATED;
1586 $this->user_validation_id = $user->id;
1587 $this->datev = $now;
1588 $this->date_validation = $now;
1589
1590 if (getDolGlobalString('SUPPLIER_PROPOSAL_AUTOADD_USER_CONTACT')) {
1591 $result = $this->add_contact($user->id, 'SALESREPFOLL', 'internal', 1);
1592 if ($result < 0 && $result != -2) { // -2 means already exists
1593 $error++;
1594 }
1595 }
1596
1597 $this->db->commit();
1598 return 1;
1599 } else {
1600 $this->db->rollback();
1601 return -1;
1602 }
1603 } else {
1604 dol_syslog("You don't have permission to validate supplier proposal", LOG_WARNING);
1605 return -2;
1606 }
1607 }
1608
1609 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1618 public function set_date_livraison($user, $delivery_date)
1619 {
1620 // phpcs:enable
1621 return $this->setDeliveryDate($user, $delivery_date);
1622 }
1623
1631 public function setDeliveryDate($user, $delivery_date)
1632 {
1633 if ($user->hasRight('supplier_proposal', 'creer')) {
1634 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1635 $sql .= " SET date_livraison = ".(isDolTms($delivery_date) ? "'".$this->db->idate($delivery_date)."'" : 'null');
1636 $sql .= " WHERE rowid = ".((int) $this->id);
1637
1638 if ($this->db->query($sql)) {
1639 $this->delivery_date = $delivery_date;
1640 return 1;
1641 } else {
1642 $this->error = $this->db->error();
1643 dol_syslog(get_class($this)."::setDeliveryDate Erreur SQL");
1644 return -1;
1645 }
1646 }
1647 return 0;
1648 }
1649
1650
1660 public function reopen($user, $status, $note = '', $notrigger = 0)
1661 {
1662 $error = 0;
1663
1664 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1665 $sql .= " SET fk_statut = ".((int) $status).",";
1666 if (!empty($note)) {
1667 $sql .= " note_private = '".$this->db->escape($note)."',";
1668 }
1669 $sql .= " date_cloture = NULL, fk_user_cloture = NULL";
1670 $sql .= " WHERE rowid = ".((int) $this->id);
1671
1672 $this->db->begin();
1673
1674 dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
1675 $resql = $this->db->query($sql);
1676 if (!$resql) {
1677 $error++;
1678 $this->errors[] = "Error ".$this->db->lasterror();
1679 }
1680 if (!$error) {
1681 if (!$notrigger) {
1682 // Call trigger
1683 $result = $this->call_trigger('PROPOSAL_SUPPLIER_REOPEN', $user);
1684 if ($result < 0) {
1685 $error++;
1686 }
1687 // End call triggers
1688 }
1689 }
1690
1691 // Commit or rollback
1692 if ($error) {
1693 if (!empty($this->errors)) {
1694 foreach ($this->errors as $errmsg) {
1695 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1696 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1697 }
1698 }
1699 $this->db->rollback();
1700 return -1 * $error;
1701 } else {
1702 $this->statut = $status;
1703 $this->status = $status;
1704
1705 $this->db->commit();
1706 return 1;
1707 }
1708 }
1709
1710
1719 public function cloture($user, $status, $note)
1720 {
1721 global $langs, $conf;
1722 $hidedetails = 0;
1723 $hidedesc = 0;
1724 $hideref = 0;
1725
1726 $error = 0;
1727 $now = dol_now();
1728
1729 $this->db->begin();
1730
1731 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1732 $sql .= " SET fk_statut = ".((int) $status).", note_private = '".$this->db->escape($note)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".((int) $user->id);
1733 $sql .= " WHERE rowid = ".((int) $this->id);
1734
1735 $resql = $this->db->query($sql);
1736 if ($resql) {
1737 $this->statut = $status;
1738 $this->status = $status;
1739
1740 $modelpdf = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED', (empty($this->model_pdf) ? '' : $this->model_pdf));
1741 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_REFUSED';
1742
1743 if ($status == 2) {
1744 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_SIGNED';
1745 $modelpdf = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL', (empty($this->model_pdf) ? '' : $this->model_pdf));
1746
1747 if (getDolGlobalString('SUPPLIER_PROPOSAL_UPDATE_PRICE_ON_SUPPlIER_PROPOSAL')) { // TODO This option was not tested correctly. Error if product ref does not exists
1748 $result = $this->updateOrCreatePriceFournisseur($user);
1749 }
1750 }
1751 if ($status == 4) {
1752 $triggerName = 'PROPOSAL_SUPPLIER_CLASSIFY_BILLED';
1753 }
1754
1755 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1756 // Define output language
1757 $outputlangs = $langs;
1758 if (getDolGlobalInt('MAIN_MULTILANGS')) {
1759 $outputlangs = new Translate("", $conf);
1760 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
1761 $outputlangs->setDefaultLang($newlang);
1762 }
1763
1764 $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1765 }
1766
1767 // Call trigger
1768 $result = $this->call_trigger($triggerName, $user);
1769 if ($result < 0) {
1770 $error++;
1771 }
1772 // End call triggers
1773
1774 if (!$error) {
1775 $this->db->commit();
1776 return 1;
1777 } else {
1778 $this->db->rollback();
1779 return -1;
1780 }
1781 } else {
1782 $this->error = $this->db->lasterror();
1783 $this->errors[] = $this->db->lasterror();
1784 $this->db->rollback();
1785 return -1;
1786 }
1787 }
1788
1795 public function updateOrCreatePriceFournisseur($user)
1796 {
1797 dol_syslog(get_class($this)."::updateOrCreatePriceFournisseur", LOG_DEBUG);
1798
1799 foreach ($this->lines as $product) {
1800 if ($product->subprice <= 0) {
1801 continue;
1802 }
1803 $productsupplier = new ProductFournisseur($this->db);
1804
1805 $multicurrency_tx = 1;
1806 $fk_multicurrency = 0;
1807
1808 if (empty($this->thirdparty)) {
1809 $this->fetch_thirdparty();
1810 }
1811
1812 $ref_fourn = $product->ref_fourn;
1813 if (empty($ref_fourn)) {
1814 $ref_fourn = $product->ref_supplier;
1815 }
1816 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1817 list($fk_multicurrency, $multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $product->multicurrency_code);
1818 }
1819 $productsupplier->id = $product->fk_product;
1820
1821 $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);
1822 }
1823
1824 return 1;
1825 }
1826
1835 public function updatePriceFournisseur($idProductFournPrice, $product, $user)
1836 {
1837 $price = price2num($product->subprice * $product->qty, 'MU');
1838 $unitPrice = price2num($product->subprice, 'MU');
1839
1840 $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);
1841
1842 $resql = $this->db->query($sql);
1843 if (!$resql) {
1844 $this->error = $this->db->error();
1845 $this->db->rollback();
1846 return -1;
1847 }
1848 return 1;
1849 }
1850
1858 public function createPriceFournisseur($product, $user)
1859 {
1860 $price = price2num($product->subprice * $product->qty, 'MU');
1861 $qty = price2num($product->qty);
1862 $unitPrice = price2num($product->subprice, 'MU');
1863
1864 $now = dol_now();
1865
1866 $values = array(
1867 "'".$this->db->idate($now)."'",
1868 (int) $product->fk_product,
1869 (int) $this->thirdparty->id,
1870 "'".$this->db->escape($product->ref_fourn)."'",
1871 (float) $price,
1872 (float) $qty,
1873 (float) $unitPrice,
1874 (float) $product->tva_tx,
1875 (int) $user->id
1876 );
1877 if (isModEnabled("multicurrency")) {
1878 if (!empty($product->multicurrency_code)) {
1879 include_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
1880 $multicurrency = new MultiCurrency($this->db); //need to fetch because empty fk_multicurrency and rate
1881 $multicurrency->fetch(0, $product->multicurrency_code);
1882 if (!empty($multicurrency->id)) {
1883 $values[] = (int) $multicurrency->id;
1884 $values[] = "'".$this->db->escape($product->multicurrency_code)."'";
1885 $values[] = (float) $product->multicurrency_subprice;
1886 $values[] = (float) $product->multicurrency_total_ht;
1887 $values[] = (float) $multicurrency->rate->rate;
1888 } else {
1889 for ($i = 0; $i < 5; $i++) {
1890 $values[] = 'NULL';
1891 }
1892 }
1893 }
1894 }
1895
1896 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'product_fournisseur_price ';
1897 $sql .= '(datec, fk_product, fk_soc, ref_fourn, price, quantity, unitprice, tva_tx, fk_user';
1898 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1899 $sql .= ',fk_multicurrency, multicurrency_code, multicurrency_unitprice, multicurrency_price, multicurrency_tx';
1900 }
1901 $sql .= ') VALUES ('.implode(',', $values).')';
1902
1903 $resql = $this->db->query($sql);
1904 if (!$resql) {
1905 $this->error = $this->db->error();
1906 $this->db->rollback();
1907 return -1;
1908 }
1909 return 1;
1910 }
1911
1912 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1919 public function setDraft($user)
1920 {
1921 // phpcs:enable
1922 $error = 0;
1923
1924 if ($this->status == self::STATUS_DRAFT) {
1925 dol_syslog(get_class($this)."::setDraft already draft status", LOG_WARNING);
1926 return 0;
1927 }
1928
1929 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1930 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1931 $sql .= " WHERE rowid = ".((int) $this->id);
1932
1933 if ($this->db->query($sql)) {
1934 if (!$error) {
1935 $this->oldcopy = clone $this;
1936 }
1937
1938 if (!$error) {
1939 // Call trigger
1940 $result = $this->call_trigger('PROPOSAL_SUPPLIER_UNVALIDATE', $user);
1941 if ($result < 0) {
1942 $error++;
1943 }
1944 }
1945
1946 if (!$error) {
1947 $this->status = self::STATUS_DRAFT;
1948 $this->statut = self::STATUS_DRAFT; // deprecated
1949 $this->db->commit();
1950 return 1;
1951 } else {
1952 $this->db->rollback();
1953 return -1;
1954 }
1955 } else {
1956 return -1;
1957 }
1958 }
1959
1960
1961 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1975 public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datec', $sortorder = 'DESC')
1976 {
1977 // phpcs:enable
1978 global $user;
1979
1980 $ga = array();
1981
1982 $search_sale = 0;
1983 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
1984 $search_sale = $user->id;
1985 }
1986
1987 $sql = "SELECT s.rowid, s.nom as name, s.client,";
1988 $sql .= " p.rowid as supplier_proposalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
1989 $sql .= " p.datep as dp, p.fin_validite as datelimite";
1990 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."supplier_proposal as p, ".MAIN_DB_PREFIX."c_propalst as c";
1991 $sql .= " WHERE p.entity IN (".getEntity('supplier_proposal').")";
1992 $sql .= " AND p.fk_soc = s.rowid";
1993 $sql .= " AND p.fk_statut = c.id";
1994 if ($socid) {
1995 $sql .= " AND s.rowid = ".((int) $socid);
1996 }
1997 if ($draft) {
1998 $sql .= " AND p.fk_statut = 0";
1999 }
2000 if ($notcurrentuser > 0) {
2001 $sql .= " AND p.fk_user_author <> ".((int) $user->id);
2002 }
2003 // Search on sale representative
2004 if ($search_sale && $search_sale != '-1') {
2005 if ($search_sale == -2) {
2006 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
2007 } elseif ($search_sale > 0) {
2008 $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).")";
2009 }
2010 }
2011 $sql .= $this->db->order($sortfield, $sortorder);
2012 $sql .= $this->db->plimit($limit, $offset);
2013
2014 $result = $this->db->query($sql);
2015 if ($result) {
2016 $num = $this->db->num_rows($result);
2017 if ($num) {
2018 $i = 0;
2019 while ($i < $num) {
2020 $obj = $this->db->fetch_object($result);
2021
2022 if ($shortlist == 1) {
2023 $ga[$obj->supplier_proposalid] = $obj->ref;
2024 } elseif ($shortlist == 2) {
2025 $ga[$obj->supplier_proposalid] = $obj->ref.' ('.$obj->name.')';
2026 } else {
2027 $ga[$i]['id'] = $obj->supplier_proposalid;
2028 $ga[$i]['ref'] = $obj->ref;
2029 $ga[$i]['name'] = $obj->name;
2030 }
2031
2032 $i++;
2033 }
2034 }
2035 return $ga;
2036 } else {
2037 dol_print_error($this->db);
2038 return -1;
2039 }
2040 }
2041
2049 public function delete($user, $notrigger = 0)
2050 {
2051 global $conf;
2052 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2053
2054 $error = 0;
2055
2056 $this->db->begin();
2057
2058 if (!$notrigger) {
2059 // Call trigger
2060 $result = $this->call_trigger('PROPOSAL_SUPPLIER_DELETE', $user);
2061 if ($result < 0) {
2062 $error++;
2063 }
2064 // End call triggers
2065 }
2066
2067 // Remove linked categories.
2068 if (!$error) {
2069 $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_supplier_proposal";
2070 $sql .= " WHERE fk_supplier_proposal = ".((int) $this->id);
2071
2072 $result = $this->db->query($sql);
2073 if (!$result) {
2074 $error++;
2075 $this->error = $this->db->lasterror();
2076 $this->errors[] = $this->error;
2077 }
2078 }
2079
2080 if (!$error) {
2081 $main = MAIN_DB_PREFIX.'supplier_proposaldet';
2082 $ef = $main."_extrafields";
2083 $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_supplier_proposal = ".((int) $this->id).")";
2084 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet WHERE fk_supplier_proposal = ".((int) $this->id);
2085 if ($this->db->query($sql)) {
2086 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposal WHERE rowid = ".((int) $this->id);
2087 if ($this->db->query($sqlef) && $this->db->query($sql)) {
2088 // Delete linked object
2089 $res = $this->deleteObjectLinked();
2090 if ($res < 0) {
2091 $error++;
2092 }
2093
2094 if (!$error) {
2095 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2096 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
2097 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
2098
2099 // We remove directory
2100 $ref = dol_sanitizeFileName($this->ref);
2101 if ($conf->supplier_proposal->dir_output && !empty($this->ref)) {
2102 $dir = $conf->supplier_proposal->dir_output."/".$ref;
2103 $file = $dir."/".$ref.".pdf";
2104 if (file_exists($file)) {
2105 dol_delete_preview($this);
2106
2107 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2108 $this->error = 'ErrorFailToDeleteFile';
2109 $this->errors = array('ErrorFailToDeleteFile');
2110 $this->db->rollback();
2111 return 0;
2112 }
2113 }
2114 if (file_exists($dir)) {
2115 $res = @dol_delete_dir_recursive($dir);
2116 if (!$res) {
2117 $this->error = 'ErrorFailToDeleteDir';
2118 $this->errors = array('ErrorFailToDeleteDir');
2119 $this->db->rollback();
2120 return 0;
2121 }
2122 }
2123 }
2124 }
2125
2126 // Removed extrafields
2127 if (!$error) {
2128 $result = $this->deleteExtraFields();
2129 if ($result < 0) {
2130 $error++;
2131 $errorflag = -4;
2132 dol_syslog(get_class($this)."::delete erreur ".$errorflag." ".$this->error, LOG_ERR);
2133 }
2134 }
2135
2136 if (!$error) {
2137 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
2138 $this->db->commit();
2139 return 1;
2140 } else {
2141 $this->error = $this->db->lasterror();
2142 $this->db->rollback();
2143 return 0;
2144 }
2145 } else {
2146 $this->error = $this->db->lasterror();
2147 $this->db->rollback();
2148 return -3;
2149 }
2150 } else {
2151 $this->error = $this->db->lasterror();
2152 $this->db->rollback();
2153 return -2;
2154 }
2155 } else {
2156 $this->db->rollback();
2157 return -1;
2158 }
2159 }
2160
2167 public function info($id)
2168 {
2169 $sql = "SELECT c.rowid, GREATEST(c.tms, cef.tms) as date_modification,";
2170 $sql .= " c.datec as date_creation, c.date_valid as date_validation, c.date_cloture as date_closure,";
2171 $sql .= " c.fk_user_author, c.fk_user_modif, c.fk_user_valid, c.fk_user_cloture";
2172 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as c";
2173 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."supplier_proposal_extrafields as cef ON cef.fk_object=c.rowid";
2174 $sql .= " WHERE c.rowid = ".((int) $id);
2175
2176 $result = $this->db->query($sql);
2177
2178 if ($result) {
2179 if ($this->db->num_rows($result)) {
2180 $obj = $this->db->fetch_object($result);
2181
2182 $this->id = $obj->rowid;
2183
2184 $this->date_creation = $this->db->jdate($obj->date_creation);
2185 $this->date_modification = empty($obj->date_modification) ? '' : $this->db->jdate($obj->date_modification);
2186 $this->date_validation = $this->db->jdate($obj->date_validation);
2187 $this->date_cloture = $this->db->jdate($obj->date_closure);
2188
2189 $this->user_creation_id = $obj->fk_user_author;
2190 $this->user_modification_id = $obj->fk_user_modif;
2191 $this->user_validation_id = $obj->fk_user_valid;
2192 $this->user_closing_id = $obj->fk_user_cloture;
2193 }
2194 $this->db->free($result);
2195 } else {
2196 dol_print_error($this->db);
2197 }
2198 }
2199
2200
2207 public function getLibStatut($mode = 0)
2208 {
2209 return $this->LibStatut((isset($this->status) ? $this->status : $this->statut), $mode);
2210 }
2211
2212 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2220 public function LibStatut($status, $mode = 1)
2221 {
2222 // phpcs:enable
2223
2224 // Init/load array of translation of status
2225 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
2226 global $langs;
2227 $langs->load("supplier_proposal");
2228 $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraft");
2229 $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidated");
2230 $this->labelStatus[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSigned");
2231 $this->labelStatus[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSigned");
2232 $this->labelStatus[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosed");
2233 $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraftShort");
2234 $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidatedShort");
2235 $this->labelStatusShort[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSignedShort");
2236 $this->labelStatusShort[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSignedShort");
2237 $this->labelStatusShort[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosedShort");
2238 }
2239
2240 $statusnew = '';
2241 if ($status == self::STATUS_DRAFT) {
2242 $statusnew = 'status0';
2243 } elseif ($status == self::STATUS_VALIDATED) {
2244 $statusnew = 'status1';
2245 } elseif ($status == self::STATUS_SIGNED) {
2246 $statusnew = 'status4';
2247 } elseif ($status == self::STATUS_NOTSIGNED) {
2248 $statusnew = 'status9';
2249 } elseif ($status == self::STATUS_CLOSE) {
2250 $statusnew = 'status6';
2251 }
2252
2253 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusnew, $mode);
2254 }
2255
2256
2257 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2265 public function load_board($user, $mode)
2266 {
2267 // phpcs:enable
2268 global $conf, $langs;
2269
2270 $now = dol_now();
2271
2272 $clause = " WHERE";
2273
2274 $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.date_cloture as datefin";
2275 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2276 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2277 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
2278 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2279 $clause = " AND";
2280 }
2281 $sql .= $clause." p.entity IN (".getEntity('supplier_proposal').")";
2282 if ($mode == 'opened') {
2283 $sql .= " AND p.fk_statut = 1";
2284 }
2285 if ($mode == 'signed') {
2286 $sql .= " AND p.fk_statut = 2";
2287 }
2288 if ($user->socid) {
2289 $sql .= " AND p.fk_soc = ".((int) $user->socid);
2290 }
2291
2292 $resql = $this->db->query($sql);
2293 if ($resql) {
2294 $label = $labelShort = '';
2295 $status = '';
2296 $delay_warning = 0;
2297 if ($mode == 'opened') {
2298 $delay_warning = !empty($conf->supplier_proposal->cloture->warning_delay) ? $conf->supplier_proposal->cloture->warning_delay : 0;
2299 $status = self::STATUS_VALIDATED;
2300 $label = $langs->trans("SupplierProposalsToClose");
2301 $labelShort = $langs->trans("ToAcceptRefuse");
2302 }
2303 if ($mode == 'signed') {
2304 $delay_warning = !empty($conf->supplier_proposal->facturation->warning_delay) ? $conf->supplier_proposal->facturation->warning_delay : 0;
2305 $status = self::STATUS_SIGNED;
2306 $label = $langs->trans("SupplierProposalsToProcess"); // May be billed or ordered
2307 $labelShort = $langs->trans("ToClose");
2308 }
2309
2310 $response = new WorkboardResponse();
2311 $response->warning_delay = $delay_warning / 60 / 60 / 24;
2312 $response->label = $label;
2313 $response->labelShort = $labelShort;
2314 $response->url = DOL_URL_ROOT.'/supplier_proposal/list.php?search_status='.$status;
2315 $response->img = img_object('', "propal");
2316
2317 // This assignment in condition is not a bug. It allows walking the results.
2318 while ($obj = $this->db->fetch_object($resql)) {
2319 $response->nbtodo++;
2320 if ($mode == 'opened') {
2321 $datelimit = $this->db->jdate($obj->datefin);
2322 if ($datelimit < ($now - $delay_warning)) {
2323 $response->nbtodolate++;
2324 }
2325 }
2326 // TODO Definir regle des propales a facturer en retard
2327 // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
2328 }
2329 return $response;
2330 } else {
2331 $this->error = $this->db->lasterror();
2332 return -1;
2333 }
2334 }
2335
2336
2345 public function initAsSpecimen($param = array())
2346 {
2347 global $conf, $langs;
2348
2349 // Load array of products prodids
2350 $num_prods = 0;
2351 $prodids = array();
2352 $sql = "SELECT rowid";
2353 $sql .= " FROM ".MAIN_DB_PREFIX."product";
2354 $sql .= " WHERE entity IN (".getEntity('product').")";
2355 if (array_key_exists('tobuy', $param)) {
2356 $sql .= " AND tobuy = ".((int) $param['tobuy']);
2357 }
2358 $sql .= $this->db->plimit(100);
2359
2360 $resql = $this->db->query($sql);
2361 if ($resql) {
2362 $num_prods = $this->db->num_rows($resql);
2363 $i = 0;
2364 while ($i < $num_prods) {
2365 $i++;
2366 $row = $this->db->fetch_row($resql);
2367 $prodids[$i] = $row[0];
2368 }
2369 }
2370
2371 // Initialise parameters
2372 $this->id = 0;
2373 $this->ref = 'SPECIMEN';
2374 $this->ref_supplier = 'NEMICEPS';
2375 $this->specimen = 1;
2376 $this->socid = 1;
2377 $this->date = time();
2378 $this->cond_reglement_id = 1;
2379 $this->cond_reglement_code = 'RECEP';
2380 $this->mode_reglement_id = 7;
2381 $this->mode_reglement_code = 'CHQ';
2382 $this->note_public = 'This is a comment (public)';
2383 $this->note_private = 'This is a comment (private)';
2384
2385 $this->multicurrency_tx = 1;
2386 $this->multicurrency_code = getDolCurrency();
2387
2388 // Lines
2389 $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)
2390 $xnbp = 0;
2391 while ($xnbp < $nbp) {
2392 $line = new SupplierProposalLine($this->db);
2393 $line->desc = $langs->trans("Description")." ".$xnbp;
2394 $line->qty = 1;
2395 $line->subprice = 100;
2396 $line->tva_tx = 19.6;
2397 $line->localtax1_tx = 0;
2398 $line->localtax2_tx = 0;
2399 if ($xnbp == 2) {
2400 $line->total_ht = 50;
2401 $line->total_ttc = 59.8;
2402 $line->total_tva = 9.8;
2403 $line->remise_percent = 50;
2404 } else {
2405 $line->total_ht = 100;
2406 $line->total_ttc = 119.6;
2407 $line->total_tva = 19.6;
2408 $line->remise_percent = 00;
2409 }
2410
2411 if ($num_prods > 0) {
2412 $prodid = mt_rand(1, $num_prods);
2413 $line->fk_product = $prodids[$prodid];
2414 }
2415
2416 $this->lines[$xnbp] = $line;
2417
2418 $this->total_ht += $line->total_ht;
2419 $this->total_tva += $line->total_tva;
2420 $this->total_ttc += $line->total_ttc;
2421
2422 $xnbp++;
2423 }
2424
2425 return 1;
2426 }
2427
2433 public function loadStateBoard()
2434 {
2435 global $user;
2436
2437 $this->nb = array();
2438 $clause = "WHERE";
2439
2440 $sql = "SELECT count(p.rowid) as nb";
2441 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2442 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
2443 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2444 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2445 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2446 $clause = "AND";
2447 }
2448 $sql .= " ".$clause." p.entity IN (".getEntity('supplier_proposal').")";
2449
2450 $resql = $this->db->query($sql);
2451 if ($resql) {
2452 // This assignment in condition is not a bug. It allows walking the results.
2453 while ($obj = $this->db->fetch_object($resql)) {
2454 $this->nb["supplier_proposals"] = $obj->nb;
2455 }
2456 $this->db->free($resql);
2457 return 1;
2458 } else {
2459 dol_print_error($this->db);
2460 $this->error = $this->db->lasterror();
2461 return -1;
2462 }
2463 }
2464
2465
2473 public function getNextNumRef($soc)
2474 {
2475 global $conf, $db, $langs;
2476 $langs->load("supplier_proposal");
2477
2478 if (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON')) {
2479 $mybool = false;
2480
2481 $file = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') . ".php";
2482 $classname = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
2483
2484 // Include file with class
2485 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2486 foreach ($dirmodels as $reldir) {
2487 $dir = dol_buildpath($reldir."core/modules/supplier_proposal/");
2488
2489 // Load file with numbering class (if found)
2490 $mybool = ((bool) @include_once $dir.$file) || $mybool;
2491 }
2492
2493 if (!$mybool) {
2494 dol_print_error(null, "Failed to include file ".$file);
2495 return '';
2496 }
2497
2498 $obj = new $classname();
2499 '@phan-var-force ModeleNumRefSupplierProposal $obj';
2501 $numref = "";
2502 $numref = $obj->getNextValue($soc, $this);
2503
2504 if ($numref != "") {
2505 return $numref;
2506 } else {
2507 $this->error = $obj->error;
2508 return "";
2509 }
2510 } else {
2511 $langs->load("errors");
2512 print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("SupplierProposal"));
2513 return "";
2514 }
2515 }
2516
2523 public function getTooltipContentArray($params)
2524 {
2525 global $conf, $langs;
2526
2527 $langs->load('supplier_proposal');
2528
2529 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2530 return ['optimize' => $langs->trans("ShowSupplierProposal")];
2531 }
2532
2533 //$option = $params['option'] ?? '';
2534 $datas = [];
2535
2536 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("SupplierProposal").'</u>';
2537 if (isset($this->status)) {
2538 $datas['picto'] .= ' '.$this->getLibStatut(5);
2539 }
2540 if (!empty($this->ref)) {
2541 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2542 }
2543 if (!empty($this->ref_fourn)) {
2544 $datas['ref_supplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_fourn;
2545 }
2546 if (!empty($this->total_ht)) {
2547 $datas['amount_ht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, getDolCurrency());
2548 }
2549 if (!empty($this->total_tva)) {
2550 $datas['amount_vat'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, getDolCurrency());
2551 }
2552 if (!empty($this->total_ttc)) {
2553 $datas['amount_ttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, getDolCurrency());
2554 }
2555
2556 return $datas;
2557 }
2558
2570 public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2571 {
2572 global $langs, $conf, $user, $hookmanager;
2573
2574 if (!empty($conf->dol_no_mouse_hover)) {
2575 $notooltip = 1; // Force disable tooltips
2576 }
2577
2578 $url = '';
2579 $result = '';
2580 $params = [
2581 'id' => $this->id,
2582 'objecttype' => $this->element,
2583 'option' => $option,
2584 ];
2585 $classfortooltip = 'classfortooltip';
2586 $dataparams = '';
2587 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2588 $classfortooltip = 'classforajaxtooltip';
2589 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
2590 $label = '';
2591 } else {
2592 $label = implode($this->getTooltipContentArray($params));
2593 }
2594
2595 if ($option == '') {
2596 $url = DOL_URL_ROOT.'/supplier_proposal/card.php?id='.$this->id.$get_params;
2597 }
2598 if ($option == 'document') {
2599 $url = DOL_URL_ROOT.'/supplier_proposal/document.php?id='.$this->id.$get_params;
2600 }
2601
2602 if ($option !== 'nolink') {
2603 // Add param to save lastsearch_values or not
2604 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2605 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2606 $add_save_lastsearch_values = 1;
2607 }
2608 if ($add_save_lastsearch_values) {
2609 $url .= '&save_lastsearch_values=1';
2610 }
2611 }
2612
2613 $linkclose = '';
2614 if (empty($notooltip) && $user->hasRight('propal', 'lire')) {
2615 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2616 $label = $langs->trans("ShowSupplierProposal");
2617 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
2618 }
2619 $linkclose .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
2620 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
2621 }
2622
2623 $linkstart = '<a href="'.$url.'"';
2624 $linkstart .= $linkclose.'>';
2625 $linkend = '</a>';
2626
2627 $result .= $linkstart;
2628 if ($withpicto) {
2629 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
2630 }
2631 if ($withpicto != 2) {
2632 $result .= $this->ref;
2633 }
2634 $result .= $linkend;
2635
2636 if ($addlinktonotes) {
2637 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2638 if ($txttoshow) {
2639 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2640 $result .= ' <span class="note inline-block">';
2641 $result .= '<a href="'.DOL_URL_ROOT.'/supplier_proposal/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2642 $result .= img_picto('', 'note');
2643 $result .= '</a>';
2644 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
2645 //$result.='</a>';
2646 $result .= '</span>';
2647 }
2648 }
2649 global $action;
2650 $hookmanager->initHooks(array($this->element . 'dao'));
2651 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
2652 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2653 if ($reshook > 0) {
2654 $result = $hookmanager->resPrint;
2655 } else {
2656 $result .= $hookmanager->resPrint;
2657 }
2658 return $result;
2659 }
2660
2666 public function getLinesArray()
2667 {
2668 // For other object, here we call fetch_lines. But fetch_lines does not exists on supplier proposal
2669
2670 $sql = 'SELECT pt.rowid, pt.label as custom_label, pt.description, pt.fk_product, pt.fk_remise_except,';
2671 $sql .= ' pt.qty, pt.tva_tx, pt.vat_src_code, pt.remise_percent, pt.subprice, pt.info_bits,';
2672 $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,';
2673 $sql .= ' pt.product_type, pt.rang, pt.fk_parent_line, pt.extraparams,';
2674 $sql .= ' p.label as product_label, p.ref, p.fk_product_type, p.rowid as prodid,';
2675 $sql .= ' p.description as product_desc, pt.ref_fourn as ref_supplier,';
2676 $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';
2677 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pt';
2678 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pt.fk_product=p.rowid';
2679 $sql .= ' WHERE pt.fk_supplier_proposal = '.((int) $this->id);
2680 $sql .= ' ORDER BY pt.rang ASC, pt.rowid';
2681
2682 dol_syslog(get_class($this).'::getLinesArray', LOG_DEBUG);
2683 $resql = $this->db->query($sql);
2684 if ($resql) {
2685 $num = $this->db->num_rows($resql);
2686 $i = 0;
2687
2688 while ($i < $num) {
2689 $obj = $this->db->fetch_object($resql);
2690
2691 $this->lines[$i] = new SupplierProposalLine($this->db);
2692 $this->lines[$i]->id = $obj->rowid; // for backward compatibility
2693 $this->lines[$i]->rowid = $obj->rowid;
2694
2695 $this->lines[$i]->label = $obj->custom_label;
2696 $this->lines[$i]->description = $obj->description; // deprecated
2697 $this->lines[$i]->desc = $obj->description;
2698
2699 $this->lines[$i]->fk_product = $obj->fk_product;
2700 $this->lines[$i]->ref = $obj->ref;
2701 $this->lines[$i]->product_label = $obj->product_label;
2702 $this->lines[$i]->product_desc = $obj->product_desc;
2703 $this->lines[$i]->fk_product_type = $obj->fk_product_type; // deprecated
2704 $this->lines[$i]->product_type = $obj->product_type;
2705 $this->lines[$i]->qty = $obj->qty;
2706 $this->lines[$i]->subprice = $obj->subprice;
2707 $this->lines[$i]->fk_remise_except = $obj->fk_remise_except;
2708 $this->lines[$i]->remise_percent = $obj->remise_percent;
2709 $this->lines[$i]->tva_tx = $obj->tva_tx;
2710 $this->lines[$i]->vat_src_code = $obj->vat_src_code;
2711 $this->lines[$i]->info_bits = $obj->info_bits;
2712 $this->lines[$i]->total_ht = $obj->total_ht;
2713 $this->lines[$i]->total_tva = $obj->total_tva;
2714 $this->lines[$i]->total_ttc = $obj->total_ttc;
2715 $this->lines[$i]->fk_fournprice = $obj->fk_fournprice;
2716 $marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->lines[$i]->fk_fournprice, $obj->pa_ht);
2717 $this->lines[$i]->pa_ht = $marginInfos[0];
2718 $this->lines[$i]->marge_tx = $marginInfos[1];
2719 $this->lines[$i]->marque_tx = $marginInfos[2];
2720 $this->lines[$i]->fk_parent_line = $obj->fk_parent_line;
2721 $this->lines[$i]->special_code = $obj->special_code;
2722 $this->lines[$i]->rang = $obj->rang;
2723
2724 $this->lines[$i]->ref_fourn = $obj->ref_supplier; // deprecated
2725 $this->lines[$i]->ref_supplier = $obj->ref_supplier;
2726
2727 // Multicurrency
2728 $this->lines[$i]->fk_multicurrency = $obj->fk_multicurrency;
2729 $this->lines[$i]->multicurrency_code = $obj->multicurrency_code;
2730 $this->lines[$i]->multicurrency_subprice = $obj->multicurrency_subprice;
2731 $this->lines[$i]->multicurrency_total_ht = $obj->multicurrency_total_ht;
2732 $this->lines[$i]->multicurrency_total_tva = $obj->multicurrency_total_tva;
2733 $this->lines[$i]->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
2734 $this->lines[$i]->fk_unit = $obj->fk_unit;
2735 $this->lines[$i]->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
2736
2737 $this->lines[$i]->fetch_optionals();
2738
2739 $i++;
2740 }
2741 $this->db->free($resql);
2742
2743 return 1;
2744 } else {
2745 $this->error = $this->db->error();
2746 return -1;
2747 }
2748 }
2749
2761 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2762 {
2763 global $langs;
2764
2765 $langs->load("supplier_proposal");
2766 $outputlangs->load("products");
2767
2768 if (!dol_strlen($modele)) {
2769 $modele = ''; // On supplier documents, template can be empty( no doc generated in this case)
2770
2771 if ($this->model_pdf) {
2772 $modele = $this->model_pdf;
2773 } elseif (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF')) {
2774 $modele = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF');
2775 }
2776 }
2777
2778 if (empty($modele)) {
2779 return 1;
2780 }
2781
2782 $modelpath = "core/modules/supplier_proposal/doc/";
2783
2784 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2785 }
2786
2787
2796 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2797 {
2798 $tables = array(
2799 'supplier_proposal'
2800 );
2801
2802 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2803 }
2804
2813 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
2814 {
2815 $tables = array(
2816 'supplier_proposaldet'
2817 );
2818
2819 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
2820 }
2821
2822
2830 public function getKanbanView($option = '', $arraydata = null)
2831 {
2832 global $langs;
2833
2834 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2835
2836 $return = '<div class="box-flex-item box-flex-grow-zero">';
2837 $return .= '<div class="info-box info-box-sm">';
2838 $return .= '<span class="info-box-icon bg-infobox-action">';
2839 $return .= img_picto('', $this->picto);
2840 //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2841 $return .= '</span>';
2842 $return .= '<div class="info-box-content">';
2843 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
2844 if ($selected >= 0) {
2845 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2846 }
2847 if (property_exists($this, 'socid')) {
2848 $return .= '<span class="info-box-ref"> | '.$this->socid.'</span>';
2849 }
2850 if (property_exists($this, 'delivery_date')) {
2851 $return .= '<br><span class="opacitymedium">'.$langs->trans("DateEnd").'</span> : <span class="info-box-label">'.dol_print_date($this->delivery_date).'</span>';
2852 }
2853 if (property_exists($this, 'total_ttc')) {
2854 $return .= '<br><span class="opacitymedium" >'.$langs->trans("AmountHT").' : </span><span class="info-box-label amount">'.price($this->total_ttc).'</span>';
2855 }
2856 if (method_exists($this, 'getLibStatut')) {
2857 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
2858 }
2859 $return .= '</div>';
2860 $return .= '</div>';
2861 $return .= '</div>';
2862 return $return;
2863 }
2864
2875 public function setCategories($categories)
2876 {
2877 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2878 return parent::setCategoriesCommon($categories, Categorie::TYPE_SUPPLIER_PROPOSAL);
2879 }
2880}
2881
2882
2887{
2891 public $db;
2892
2896 public $error = '';
2897
2901 public $element = 'supplier_proposaldet';
2902
2906 public $table_element = 'supplier_proposaldet';
2907
2911 public $parent_element = 'supplier_proposal';
2912
2916 public $fk_parent_attribute = 'fk_supplier_proposal';
2917
2921 public $oldline;
2922
2926 public $id;
2927
2931 public $fk_supplier_proposal;
2932
2936 public $fk_parent_line;
2937
2941 public $desc; // Description of the line
2942
2946 public $fk_product; // Predefined Product ID
2947
2953 public $fk_product_type;
2959 public $product_type = Product::TYPE_PRODUCT;
2960
2964 public $qty;
2968 public $tva_tx;
2972 public $vat_src_code;
2973
2977 public $remise_percent;
2978
2982 public $fk_remise_except;
2983
2987 public $rang = 0;
2988
2992 public $fk_fournprice;
2993
2997 public $pa_ht;
3001 public $marge_tx;
3005 public $marque_tx;
3006
3010 public $special_code; // Tag for special lines (exclusive tags)
3011 // 1: frais de port
3012 // 2: ecotaxe
3013 // 3: option line (when qty = 0)
3014
3018 public $info_bits = 0; // Liste d'options cumulables:
3019 // Bit 0: 0 if TVA normal - 1 if TVA NPR
3020 // Bit 1: 0 normal line - 1 if fixed reduction
3021
3025 public $total_ht; // Line Net Total (HT), including all quantity and the line discount
3029 public $total_tva; // Line Net Total (VAT), including all quantity and the line discount
3033 public $total_ttc; // Line Net Total (TTC), including all quantity and the line discount
3034
3038 public $date_start;
3042 public $date_end;
3043
3044 // From llx_product
3050 public $ref;
3051
3056 public $product_ref;
3057
3063 public $libelle;
3064
3069 public $product_label;
3070
3075 public $label;
3076
3081 public $product_desc;
3082
3086 public $localtax1_tx; // Local tax 1
3090 public $localtax2_tx; // Local tax 2
3094 public $localtax1_type; // Local tax 1 type
3098 public $localtax2_type; // Local tax 2 type
3102 public $total_localtax1; // Line total local tax 1
3106 public $total_localtax2; // Line total local tax 2
3107
3111 public $skip_update_total; // Skip update price total for special lines
3112
3116 public $ref_fourn;
3120 public $ref_supplier;
3121
3122 // Multicurrency
3126 public $fk_multicurrency;
3127
3131 public $multicurrency_code;
3135 public $multicurrency_subprice;
3139 public $multicurrency_total_ht;
3143 public $multicurrency_total_tva;
3147 public $multicurrency_total_ttc;
3148
3154 public function __construct($db)
3155 {
3156 $this->db = $db;
3157 }
3158
3165 public function fetch($rowid)
3166 {
3167 $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,';
3168 $sql .= ' pd.date_start, pd.date_end,';
3169 $sql .= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice, pd.subprice_ttc,';
3170 $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,';
3171 $sql .= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
3172 $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
3173 $sql .= ' pd.product_type, pd.ref_fourn as ref_produit_fourn, pd.extraparams,';
3174 $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';
3175 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pd';
3176 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
3177 $sql .= ' WHERE pd.rowid = '.((int) $rowid);
3178
3179 $result = $this->db->query($sql);
3180 if ($result) {
3181 if ($objp = $this->db->fetch_object($result)) {
3182 $this->id = $objp->rowid;
3183 $this->fk_supplier_proposal = $objp->fk_supplier_proposal;
3184 $this->fk_parent_line = $objp->fk_parent_line;
3185 $this->label = $objp->custom_label;
3186 $this->desc = $objp->description;
3187 $this->qty = $objp->qty;
3188
3189 $this->subprice = $objp->subprice;
3190 $this->subprice_ttc = $objp->subprice_ttc;
3191
3192 $this->tva_tx = $objp->tva_tx;
3193 $this->remise_percent = $objp->remise_percent;
3194 $this->fk_remise_except = $objp->fk_remise_except;
3195 $this->fk_product = $objp->fk_product;
3196 $this->info_bits = $objp->info_bits;
3197 $this->date_start = $this->db->jdate($objp->date_start);
3198 $this->date_end = $this->db->jdate($objp->date_end);
3199
3200 $this->total_ht = $objp->total_ht;
3201 $this->total_tva = $objp->total_tva;
3202 $this->total_ttc = $objp->total_ttc;
3203
3204 $this->fk_fournprice = $objp->fk_fournprice;
3205
3206 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
3207 $this->pa_ht = $marginInfos[0];
3208 $this->marge_tx = $marginInfos[1];
3209 $this->marque_tx = $marginInfos[2];
3210
3211 $this->special_code = $objp->special_code;
3212 $this->product_type = $objp->product_type;
3213 $this->rang = $objp->rang;
3214
3215 $this->product_ref = $objp->product_ref;
3216 $this->product_label = $objp->product_label;
3217 $this->product_desc = $objp->product_desc;
3218
3219 $this->ref_fourn = $objp->ref_produit_fourn;
3220
3221 $this->extraparams = !empty($objp->extraparams) ? (array) json_decode($objp->extraparams, true) : array();
3222
3223 // Multicurrency
3224 $this->fk_multicurrency = $objp->fk_multicurrency;
3225 $this->multicurrency_code = $objp->multicurrency_code;
3226 $this->multicurrency_subprice = $objp->multicurrency_subprice;
3227 $this->multicurrency_subprice_ttc = $objp->multicurrency_subprice_ttc;
3228 $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3229 $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3230 $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3231 $this->fk_unit = $objp->fk_unit;
3232
3233 $this->db->free($result);
3234 return 1;
3235 }
3236 return 0;
3237 } else {
3238 dol_print_error($this->db);
3239 return -1;
3240 }
3241 }
3242
3249 public function insert($notrigger = 0)
3250 {
3251 global $user;
3252
3253 $error = 0;
3254
3255 dol_syslog(get_class($this)."::insert rang=".$this->rang);
3256
3257 // Clean parameters
3258 if (empty($this->tva_tx)) {
3259 $this->tva_tx = 0;
3260 }
3261 if (empty($this->vat_src_code)) {
3262 $this->vat_src_code = '';
3263 }
3264 if (empty($this->localtax1_tx)) {
3265 $this->localtax1_tx = 0;
3266 }
3267 if (empty($this->localtax2_tx)) {
3268 $this->localtax2_tx = 0;
3269 }
3270 if (empty($this->localtax1_type)) {
3271 $this->localtax1_type = '';
3272 }
3273 if (empty($this->localtax2_type)) {
3274 $this->localtax2_type = '';
3275 }
3276 if (empty($this->total_localtax1)) {
3277 $this->total_localtax1 = 0;
3278 }
3279 if (empty($this->total_localtax2)) {
3280 $this->total_localtax2 = 0;
3281 }
3282 if (empty($this->rang)) {
3283 $this->rang = 0;
3284 }
3285 if (empty($this->remise_percent)) {
3286 $this->remise_percent = 0;
3287 }
3288 if (empty($this->info_bits)) {
3289 $this->info_bits = 0;
3290 }
3291 if (empty($this->special_code)) {
3292 $this->special_code = 0;
3293 }
3294 if (empty($this->fk_parent_line)) {
3295 $this->fk_parent_line = 0;
3296 }
3297 if (empty($this->fk_fournprice)) {
3298 $this->fk_fournprice = 0;
3299 }
3300 if (empty($this->fk_unit)) {
3301 $this->fk_unit = 0;
3302 }
3303 if (empty($this->subprice)) {
3304 $this->subprice = 0;
3305 }
3306
3307 if (empty($this->pa_ht)) {
3308 $this->pa_ht = 0;
3309 }
3310
3311 // if buy price not defined, define buyprice as configured in margin admin
3312 if ($this->pa_ht == 0) {
3313 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3314 if ($result < 0) {
3315 return $result;
3316 } else {
3317 $this->pa_ht = $result;
3318 }
3319 }
3320
3321 // Check parameters
3322 if ($this->product_type < 0) {
3323 return -1;
3324 }
3325
3326 $this->db->begin();
3327
3328 // Insert line into database
3329 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'supplier_proposaldet';
3330 $sql .= ' (fk_supplier_proposal, fk_parent_line, label, description, fk_product, product_type,';
3331 $sql .= ' date_start, date_end,';
3332 $sql .= ' fk_remise_except, qty, tva_tx, vat_src_code, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3333 $sql .= ' subprice, subprice_ttc, remise_percent, ';
3334 $sql .= ' info_bits, ';
3335 $sql .= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
3336 $sql .= ' ref_fourn,';
3337 $sql .= ' fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_subprice_ttc, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc, fk_unit)';
3338 $sql .= " VALUES (".$this->fk_supplier_proposal.",";
3339 $sql .= " ".($this->fk_parent_line > 0 ? ((int) $this->fk_parent_line) : "null").",";
3340 $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
3341 $sql .= " '".$this->db->escape($this->desc)."',";
3342 $sql .= " ".($this->fk_product ? ((int) $this->fk_product) : "null").",";
3343 $sql .= " ".((int) $this->product_type).",";
3344 $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3345 $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3346 $sql .= " ".($this->fk_remise_except ? ((int) $this->fk_remise_except) : "null").",";
3347 $sql .= " ".(float) price2num($this->qty, 'MS').",";
3348 $sql .= " ".(float) price2num($this->tva_tx).",";
3349 $sql .= " '".$this->db->escape($this->vat_src_code)."',";
3350 $sql .= " ".(float) price2num($this->localtax1_tx).",";
3351 $sql .= " ".(float) price2num($this->localtax2_tx).",";
3352 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3353 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3354 $sql .= " ".(float) price2num($this->subprice, 'MU') .",";
3355 $sql .= " ".(float) price2num($this->subprice_ttc, 'MU') .",";
3356 $sql .= " ".((float) $this->remise_percent).",";
3357 $sql .= " ".(isset($this->info_bits) ? ((int) $this->info_bits) : "null").",";
3358 $sql .= " ".(float) price2num($this->total_ht, 'MT').",";
3359 $sql .= " ".(float) price2num($this->total_tva, 'MT').",";
3360 $sql .= " ".(float) price2num($this->total_localtax1, 'MT').",";
3361 $sql .= " ".(float) price2num($this->total_localtax2, 'MT').",";
3362 $sql .= " ".(float) price2num($this->total_ttc, 'MT').",";
3363 $sql .= " ".(!empty($this->fk_fournprice) ? ((int) $this->fk_fournprice) : "null").",";
3364 $sql .= " ".(isset($this->pa_ht) ? (float) price2num($this->pa_ht, 'MU') : "null").",";
3365 $sql .= ' '.((int) $this->special_code).',';
3366 $sql .= ' '.((int) $this->rang).',';
3367 $sql .= " '".$this->db->escape($this->ref_fourn)."'";
3368 $sql .= ", ".($this->fk_multicurrency > 0 ? ((int) $this->fk_multicurrency) : 'null');
3369 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3370 $sql .= ", ".(float) price2num($this->multicurrency_subprice, 'CU');
3371 $sql .= ", ".(float) price2num($this->multicurrency_subprice_ttc, 'CU');
3372 $sql .= ", ".(float) price2num($this->multicurrency_total_ht, 'CT');
3373 $sql .= ", ".(float) price2num($this->multicurrency_total_tva, 'CT');
3374 $sql .= ", ".(float) price2num($this->multicurrency_total_ttc, 'CT');
3375 $sql .= ", ".($this->fk_unit ? ((int) $this->fk_unit) : 'null');
3376 $sql .= ')';
3377
3378 dol_syslog(get_class($this).'::insert', LOG_DEBUG);
3379 $resql = $this->db->query($sql);
3380 if ($resql) {
3381 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'supplier_proposaldet');
3382
3383 if (!$error) {
3384 $result = $this->insertExtraFields();
3385 if ($result < 0) {
3386 $error++;
3387 }
3388 }
3389
3390 if (!$error && !$notrigger) {
3391 // Call trigger
3392 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_INSERT', $user);
3393 if ($result < 0) {
3394 $this->db->rollback();
3395 return -1;
3396 }
3397 // End call triggers
3398 }
3399
3400 $this->db->commit();
3401 return 1;
3402 } else {
3403 $this->error = $this->db->error()." sql=".$sql;
3404 $this->db->rollback();
3405 return -1;
3406 }
3407 }
3408
3415 public function delete($user)
3416 {
3417 $error = 0;
3418
3419 $this->db->begin();
3420
3421 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet";
3422 $sql .= " WHERE rowid = ".((int) $this->id);
3423
3424 if ($this->db->query($sql)) {
3425 // Remove extrafields
3426 if (!$error) {
3427 $result = $this->deleteExtraFields();
3428 if ($result < 0) {
3429 $error++;
3430 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3431 }
3432 }
3433
3434 // Call trigger
3435 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_DELETE', $user);
3436 if ($result < 0) {
3437 $this->db->rollback();
3438 return -1;
3439 }
3440 // End call triggers
3441
3442 $this->db->commit();
3443
3444 return 1;
3445 } else {
3446 $this->error = $this->db->error()." sql=".$sql;
3447 $this->db->rollback();
3448 return -1;
3449 }
3450 }
3451
3458 public function update($notrigger = 0)
3459 {
3460 global $user;
3461
3462 $error = 0;
3463
3464 // Clean parameters
3465 if (empty($this->tva_tx)) {
3466 $this->tva_tx = 0;
3467 }
3468 if (empty($this->localtax1_tx)) {
3469 $this->localtax1_tx = 0;
3470 }
3471 if (empty($this->localtax2_tx)) {
3472 $this->localtax2_tx = 0;
3473 }
3474 if (empty($this->total_localtax1)) {
3475 $this->total_localtax1 = 0;
3476 }
3477 if (empty($this->total_localtax2)) {
3478 $this->total_localtax2 = 0;
3479 }
3480 if (empty($this->localtax1_type)) {
3481 $this->localtax1_type = '';
3482 }
3483 if (empty($this->localtax2_type)) {
3484 $this->localtax2_type = '';
3485 }
3486 if (empty($this->marque_tx)) {
3487 $this->marque_tx = 0;
3488 }
3489 if (empty($this->marge_tx)) {
3490 $this->marge_tx = 0;
3491 }
3492 if (empty($this->remise_percent)) {
3493 $this->remise_percent = 0;
3494 }
3495 if (empty($this->info_bits)) {
3496 $this->info_bits = 0;
3497 }
3498 if (empty($this->special_code)) {
3499 $this->special_code = 0;
3500 }
3501 if (empty($this->fk_parent_line)) {
3502 $this->fk_parent_line = 0;
3503 }
3504 if (empty($this->fk_fournprice)) {
3505 $this->fk_fournprice = 0;
3506 }
3507 if (empty($this->fk_unit)) {
3508 $this->fk_unit = 0;
3509 }
3510 if (empty($this->subprice)) {
3511 $this->subprice = 0;
3512 }
3513
3514 if (empty($this->pa_ht)) {
3515 $this->pa_ht = 0;
3516 }
3517
3518 // if buy price not defined, define buyprice as configured in margin admin
3519 if ($this->pa_ht == 0) {
3520 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3521 if ($result < 0) {
3522 return $result;
3523 } else {
3524 $this->pa_ht = $result;
3525 }
3526 }
3527
3528 $this->db->begin();
3529
3530 // Update line in database
3531 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3532 $sql .= " description = '".$this->db->escape($this->desc)."'";
3533 $sql .= " , label = ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
3534 $sql .= " , product_type = ".((int) $this->product_type);
3535 $sql .= " , date_start = ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null");
3536 $sql .= " , date_end = ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null");
3537 $sql .= " , tva_tx = '".price2num($this->tva_tx)."'";
3538 $sql .= " , localtax1_tx = ".(float) price2num($this->localtax1_tx);
3539 $sql .= " , localtax2_tx = ".(float) price2num($this->localtax2_tx);
3540 $sql .= " , localtax1_type = '".$this->db->escape($this->localtax1_type)."'";
3541 $sql .= " , localtax2_type = '".$this->db->escape($this->localtax2_type)."'";
3542 $sql .= " , qty = ".(float) price2num($this->qty);
3543 $sql .= " , subprice = ".(float) price2num($this->subprice);
3544 $sql .= " , subprice_ttc = ".(float) price2num($this->subprice_ttc);
3545 $sql .= " , remise_percent = ".(float) price2num($this->remise_percent);
3546 $sql .= " , info_bits = ".((int) $this->info_bits);
3547 if (empty($this->skip_update_total)) {
3548 $sql .= " , total_ht = ".(float) price2num($this->total_ht);
3549 $sql .= " , total_tva = ".(float) price2num($this->total_tva);
3550 $sql .= " , total_ttc = ".(float) price2num($this->total_ttc);
3551 $sql .= " , total_localtax1 = ".(float) price2num($this->total_localtax1);
3552 $sql .= " , total_localtax2 = ".(float) price2num($this->total_localtax2);
3553 }
3554 $sql .= " , fk_product_fournisseur_price = ".(!empty($this->fk_fournprice) ? "'".$this->db->escape((string) $this->fk_fournprice)."'" : "null");
3555 $sql .= " , buy_price_ht = ".(float) price2num($this->pa_ht);
3556 $sql .= " , special_code = ".((int) $this->special_code);
3557 $sql .= " , fk_parent_line = ".($this->fk_parent_line > 0 ? $this->fk_parent_line : "null");
3558 if (!empty($this->rang)) {
3559 $sql .= ", rang = ".((int) $this->rang);
3560 }
3561 $sql .= " , ref_fourn = ".(!empty($this->ref_fourn) ? "'".$this->db->escape($this->ref_fourn)."'" : "null");
3562 $sql .= " , fk_unit = ".($this->fk_unit ? (int) $this->fk_unit : 'null');
3563
3564 // Multicurrency
3565 $sql .= " , multicurrency_subprice = ".(float) price2num($this->multicurrency_subprice);
3566 $sql .= " , multicurrency_subprice_ttc = ".(float) price2num($this->multicurrency_subprice_ttc);
3567 $sql .= " , multicurrency_total_ht = ".(float) price2num($this->multicurrency_total_ht);
3568 $sql .= " , multicurrency_total_tva = ".(float) price2num($this->multicurrency_total_tva);
3569 $sql .= " , multicurrency_total_ttc = ".(float) price2num($this->multicurrency_total_ttc);
3570
3571 $sql .= " WHERE rowid = ".((int) $this->id);
3572
3573 dol_syslog(get_class($this)."::update", LOG_DEBUG);
3574 $resql = $this->db->query($sql);
3575 if ($resql) {
3576 if (!$error) {
3577 $result = $this->insertExtraFields();
3578 if ($result < 0) {
3579 $error++;
3580 }
3581 }
3582
3583 if (!$error && !$notrigger) {
3584 // Call trigger
3585 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_MODIFY', $user);
3586 if ($result < 0) {
3587 $this->db->rollback();
3588 return -1;
3589 }
3590 // End call triggers
3591 }
3592
3593 $this->db->commit();
3594 return 1;
3595 } else {
3596 $this->error = $this->db->error();
3597 $this->db->rollback();
3598 return -2;
3599 }
3600 }
3601
3602 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3609 public function update_total()
3610 {
3611 // phpcs:enable
3612 $this->db->begin();
3613
3614 // Update line in database
3615 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3616 $sql .= " total_ht = ".(float) price2num($this->total_ht, 'MT');
3617 $sql .= ",total_tva = ".(float) price2num($this->total_tva, 'MT');
3618 $sql .= ",total_ttc = ".(float) price2num($this->total_ttc, 'MT');
3619 $sql .= " WHERE rowid = ".((int) $this->id);
3620
3621 dol_syslog("SupplierProposalLine::update_total", LOG_DEBUG);
3622
3623 $resql = $this->db->query($sql);
3624 if ($resql) {
3625 $this->db->commit();
3626 return 1;
3627 } else {
3628 $this->error = $this->db->error();
3629 $this->db->rollback();
3630 return -2;
3631 }
3632 }
3633}
$object ref
Definition info.php:90
Parent class of all other business classes (invoices, contracts, proposals, orders,...
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
Save a new position (field rang) for details lines.
deleteEcmFiles($mode=0)
Delete related files of object in database.
update_price($exclspec=0, $roundingadjust='auto', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
add_object_linked($origin=null, $origin_id=null, $f_user=null, $notrigger=0)
Add an object link into llx_element_element.
defineBuyPrice($unitPrice=0.0, $discountPercent=0.0, $fk_product=0)
Get buy price to use for margin calculation.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty.
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid=0, $f_user=null, $notrigger=0)
Delete all links between an object $this.
setErrorsFromObject($object)
setErrorsFromObject
static isExistingObject($element, $id, $ref='', $ref_ext='')
Check if an object id or ref exists If you don't need or want to instantiate the object and just need...
updateRangOfLine($rowid, $rang)
Update position of line (rang)
checkActiveProductInLines($status='onsale')
Check if all products have the right status (on sale, on buy) called during validation of propal,...
deleteExtraFields()
Delete all extra fields values for the current object.
static commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
static commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product id with another one.
line_max($fk_parent_line=0)
Get max value used for position of line (rang)
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
add_contact($fk_socpeople, $type_contact, $source='external', $notrigger=0)
Add a link between element $this->element and a contact.
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
Class to manage absolute discounts.
Class to manage Dolibarr database access.
Class Currency.
static getIdAndTxFromCode($dbs, $code, $date_document=0)
Get id and rate of currency from code.
Class to manage predefined suppliers products.
Class to manage products or services.
const TYPE_PRODUCT
Regular product.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage price ask supplier.
updatePriceFournisseur($idProductFournPrice, $product, $user)
Update ProductFournisseur.
updateline($rowid, $pu, $qty, $remise_percent, $txtva, $txlocaltax1=0, $txlocaltax2=0, $desc='', $price_base_type='HT', $info_bits=0, $special_code=0, $fk_parent_line=0, $skip_update_total=0, $fk_fournprice=0, $pa_ht=0, $label='', $type=0, $array_options=[], $ref_supplier='', $fk_unit=0, $pu_ht_devise=0)
Update a proposal line.
cloture($user, $status, $note)
Close the askprice.
initAsSpecimen($param=array())
Initialise an instance with random values.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template module.
addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1=0, $txlocaltax2=0, $fk_product=0, $remise_percent=0, $price_base_type='HT', $pu_ttc=0, $info_bits=0, $type=0, $rang=-1, $special_code=0, $fk_parent_line=0, $fk_fournprice=0, $pa_ht=0, $label='', $array_options=[], $ref_supplier='', $fk_unit=0, $origin='', $origin_id=0, $pu_ht_devise=0, $date_start=0, $date_end=0)
Add a proposal line into database (linked to product/service or not) 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:168
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:64
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.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that returns whether VAT must be recoverable collected VAT (e.g.: VAT NPR in France)
getDolCurrency()
Return the main currency ('EUR', 'USD', ...)
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
getDolGlobalBool($key, $default=false)
Return a Dolibarr global constant boolean value.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that return vat rate of a product line (according to seller, buyer and product vat rate) VAT...
get_localtax($vatrate, $local, $thirdparty_buyer=null, $thirdparty_seller=null, $vatnpr=0)
Return localtax rate for a particular VAT rate, when selling a product with vat $vatrate,...
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
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
buildzip.php
getMarginInfos($pv_ht, $remise_percent, $tva_tx, $localtax1_tx, $localtax2_tx, $fk_pa, $pa_ht)
Return an array with margins information of a line.
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller=null, $localtaxes_array=[], $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition price.lib.php:90
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