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 // Retrieve all extrafield
1377 // fetch optionals attributes and labels
1378 $this->fetch_optionals();
1379
1380 $this->db->free($resql);
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 // Retrieve all extrafield
1469 // fetch optionals attributes and labels
1470 $this->fetch_optionals();
1471
1472 return 1;
1473 }
1474
1475 $this->error = "Record Not Found";
1476 return 0;
1477 } else {
1478 $this->error = $this->db->error();
1479 return -1;
1480 }
1481 }
1482
1490 public function valid($user, $notrigger = 0)
1491 {
1492 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1493
1494 global $conf;
1495
1496 $error = 0;
1497 $now = dol_now();
1498
1499 if ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'creer'))
1500 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'validate_advance'))) {
1501 $this->db->begin();
1502
1503 // Numbering module definition
1504 $soc = new Societe($this->db);
1505 $result = $soc->fetch($this->socid);
1506
1507 if ($result < 0) {
1508 return -1;
1509 }
1510 if (!getDolGlobalBool('SUPPLIER_PROPOSAL_NOCHECK_ONBUY_PRODUCTS_ONVALID') && !$this->checkActiveProductInLines('onbuy')) {
1511 dol_syslog(get_class($this)."::valid checkActiveProductInLines ".$this->error, LOG_INFO);
1512 return -1;
1513 }
1514 // Define new ref
1515 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1516 $num = $this->getNextNumRef($soc);
1517 } else {
1518 $num = (string) $this->ref;
1519 }
1520 $this->newref = dol_sanitizeFileName($num);
1521
1522 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1523 $sql .= " SET ref = '".$this->db->escape($num)."',";
1524 $sql .= " fk_statut = 1, date_valid='".$this->db->idate($now)."', fk_user_valid=".((int) $user->id);
1525 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1526
1527 dol_syslog(get_class($this)."::valid", LOG_DEBUG);
1528 $resql = $this->db->query($sql);
1529 if (!$resql) {
1530 dol_print_error($this->db);
1531 $error++;
1532 }
1533
1534 // Trigger calls
1535 if (!$error && !$notrigger) {
1536 // Call trigger
1537 $result = $this->call_trigger('PROPOSAL_SUPPLIER_VALIDATE', $user);
1538 if ($result < 0) {
1539 $error++;
1540 }
1541 // End call triggers
1542 }
1543
1544 if (!$error) {
1545 $this->oldref = $this->ref;
1546
1547 // Rename directory if dir was a temporary ref
1548 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1549 // Now we rename also files into index
1550 $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)."'";
1551 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1552 $resql = $this->db->query($sql);
1553 if (!$resql) {
1554 $error++;
1555 $this->error = $this->db->lasterror();
1556 }
1557 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'supplier_proposal/".$this->db->escape($this->newref)."'";
1558 $sql .= " WHERE filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1559 $resql = $this->db->query($sql);
1560 if (!$resql) {
1561 $error++;
1562 $this->error = $this->db->lasterror();
1563 }
1564
1565 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1566 $oldref = dol_sanitizeFileName($this->ref);
1567 $newref = dol_sanitizeFileName($num);
1568 $dirsource = $conf->supplier_proposal->dir_output.'/'.$oldref;
1569 $dirdest = $conf->supplier_proposal->dir_output.'/'.$newref;
1570 if (!$error && file_exists($dirsource)) {
1571 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
1572 if (@rename($dirsource, $dirdest)) {
1573 dol_syslog("Rename ok");
1574 // Rename docs starting with $oldref with $newref
1575 $listoffiles = dol_dir_list($conf->supplier_proposal->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1576 foreach ($listoffiles as $fileentry) {
1577 $dirsource = $fileentry['name'];
1578 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1579 $dirsource = $fileentry['path'].'/'.$dirsource;
1580 $dirdest = $fileentry['path'].'/'.$dirdest;
1581 @rename($dirsource, $dirdest);
1582 }
1583 }
1584 }
1585 }
1586
1587 $this->ref = $num;
1588 $this->statut = self::STATUS_VALIDATED;
1590 $this->user_validation_id = $user->id;
1591 $this->datev = $now;
1592 $this->date_validation = $now;
1593
1594 if (getDolGlobalString('SUPPLIER_PROPOSAL_AUTOADD_USER_CONTACT')) {
1595 $result = $this->add_contact($user->id, 'SALESREPFOLL', 'internal', 1);
1596 if ($result < 0 && $result != -2) { // -2 means already exists
1597 $error++;
1598 }
1599 }
1600
1601 $this->db->commit();
1602 return 1;
1603 } else {
1604 $this->db->rollback();
1605 return -1;
1606 }
1607 } else {
1608 dol_syslog("You don't have permission to validate supplier proposal", LOG_WARNING);
1609 return -2;
1610 }
1611 }
1612
1613 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1622 public function set_date_livraison($user, $delivery_date)
1623 {
1624 // phpcs:enable
1625 return $this->setDeliveryDate($user, $delivery_date);
1626 }
1627
1635 public function setDeliveryDate($user, $delivery_date)
1636 {
1637 if ($user->hasRight('supplier_proposal', 'creer')) {
1638 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1639 $sql .= " SET date_livraison = ".(isDolTms($delivery_date) ? "'".$this->db->idate($delivery_date)."'" : 'null');
1640 $sql .= " WHERE rowid = ".((int) $this->id);
1641
1642 if ($this->db->query($sql)) {
1643 $this->delivery_date = $delivery_date;
1644 return 1;
1645 } else {
1646 $this->error = $this->db->error();
1647 dol_syslog(get_class($this)."::setDeliveryDate Erreur SQL");
1648 return -1;
1649 }
1650 }
1651 return 0;
1652 }
1653
1654
1664 public function reopen($user, $status, $note = '', $notrigger = 0)
1665 {
1666 $error = 0;
1667
1668 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1669 $sql .= " SET fk_statut = ".((int) $status).",";
1670 if (!empty($note)) {
1671 $sql .= " note_private = '".$this->db->escape($note)."',";
1672 }
1673 $sql .= " date_cloture = NULL, fk_user_cloture = NULL";
1674 $sql .= " WHERE rowid = ".((int) $this->id);
1675
1676 $this->db->begin();
1677
1678 dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
1679 $resql = $this->db->query($sql);
1680 if (!$resql) {
1681 $error++;
1682 $this->errors[] = "Error ".$this->db->lasterror();
1683 }
1684 if (!$error) {
1685 if (!$notrigger) {
1686 // Call trigger
1687 $result = $this->call_trigger('PROPOSAL_SUPPLIER_REOPEN', $user);
1688 if ($result < 0) {
1689 $error++;
1690 }
1691 // End call triggers
1692 }
1693 }
1694
1695 // Commit or rollback
1696 if ($error) {
1697 if (!empty($this->errors)) {
1698 foreach ($this->errors as $errmsg) {
1699 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1700 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1701 }
1702 }
1703 $this->db->rollback();
1704 return -1 * $error;
1705 } else {
1706 $this->statut = $status;
1707 $this->status = $status;
1708
1709 $this->db->commit();
1710 return 1;
1711 }
1712 }
1713
1714
1723 public function cloture($user, $status, $note)
1724 {
1725 global $langs, $conf;
1726 $hidedetails = 0;
1727 $hidedesc = 0;
1728 $hideref = 0;
1729
1730 $error = 0;
1731 $now = dol_now();
1732
1733 $this->db->begin();
1734
1735 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1736 $sql .= " SET fk_statut = ".((int) $status).", note_private = '".$this->db->escape($note)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".((int) $user->id);
1737 $sql .= " WHERE rowid = ".((int) $this->id);
1738
1739 $resql = $this->db->query($sql);
1740 if ($resql) {
1741 $this->statut = $status;
1742 $this->status = $status;
1743
1744 $modelpdf = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED', (empty($this->model_pdf) ? '' : $this->model_pdf));
1745 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_REFUSED';
1746
1747 if ($status == 2) {
1748 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_SIGNED';
1749 $modelpdf = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL', (empty($this->model_pdf) ? '' : $this->model_pdf));
1750
1751 if (getDolGlobalString('SUPPLIER_PROPOSAL_UPDATE_PRICE_ON_SUPPlIER_PROPOSAL')) { // TODO This option was not tested correctly. Error if product ref does not exists
1752 $result = $this->updateOrCreatePriceFournisseur($user);
1753 }
1754 }
1755 if ($status == 4) {
1756 $triggerName = 'PROPOSAL_SUPPLIER_CLASSIFY_BILLED';
1757 }
1758
1759 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1760 // Define output language
1761 $outputlangs = $langs;
1762 if (getDolGlobalInt('MAIN_MULTILANGS')) {
1763 $outputlangs = new Translate("", $conf);
1764 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
1765 $outputlangs->setDefaultLang($newlang);
1766 }
1767
1768 $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1769 }
1770
1771 // Call trigger
1772 $result = $this->call_trigger($triggerName, $user);
1773 if ($result < 0) {
1774 $error++;
1775 }
1776 // End call triggers
1777
1778 if (!$error) {
1779 $this->db->commit();
1780 return 1;
1781 } else {
1782 $this->db->rollback();
1783 return -1;
1784 }
1785 } else {
1786 $this->error = $this->db->lasterror();
1787 $this->errors[] = $this->db->lasterror();
1788 $this->db->rollback();
1789 return -1;
1790 }
1791 }
1792
1799 public function updateOrCreatePriceFournisseur($user)
1800 {
1801 dol_syslog(get_class($this)."::updateOrCreatePriceFournisseur", LOG_DEBUG);
1802
1803 foreach ($this->lines as $product) {
1804 if ($product->subprice <= 0) {
1805 continue;
1806 }
1807 $productsupplier = new ProductFournisseur($this->db);
1808
1809 $multicurrency_tx = 1;
1810 $fk_multicurrency = 0;
1811
1812 if (empty($this->thirdparty)) {
1813 $this->fetch_thirdparty();
1814 }
1815
1816 $ref_fourn = $product->ref_fourn;
1817 if (empty($ref_fourn)) {
1818 $ref_fourn = $product->ref_supplier;
1819 }
1820 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1821 list($fk_multicurrency, $multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $product->multicurrency_code);
1822 }
1823 $productsupplier->id = $product->fk_product;
1824
1825 $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);
1826 }
1827
1828 return 1;
1829 }
1830
1839 public function updatePriceFournisseur($idProductFournPrice, $product, $user)
1840 {
1841 $price = price2num($product->subprice * $product->qty, 'MU');
1842 $unitPrice = price2num($product->subprice, 'MU');
1843
1844 $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);
1845
1846 $resql = $this->db->query($sql);
1847 if (!$resql) {
1848 $this->error = $this->db->error();
1849 $this->db->rollback();
1850 return -1;
1851 }
1852 return 1;
1853 }
1854
1862 public function createPriceFournisseur($product, $user)
1863 {
1864 $price = price2num($product->subprice * $product->qty, 'MU');
1865 $qty = price2num($product->qty);
1866 $unitPrice = price2num($product->subprice, 'MU');
1867
1868 $now = dol_now();
1869
1870 $values = array(
1871 "'".$this->db->idate($now)."'",
1872 (int) $product->fk_product,
1873 (int) $this->thirdparty->id,
1874 "'".$this->db->escape($product->ref_fourn)."'",
1875 (float) $price,
1876 (float) $qty,
1877 (float) $unitPrice,
1878 (float) $product->tva_tx,
1879 (int) $user->id
1880 );
1881 if (isModEnabled("multicurrency")) {
1882 if (!empty($product->multicurrency_code)) {
1883 include_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
1884 $multicurrency = new MultiCurrency($this->db); //need to fetch because empty fk_multicurrency and rate
1885 $multicurrency->fetch(0, $product->multicurrency_code);
1886 if (!empty($multicurrency->id)) {
1887 $values[] = (int) $multicurrency->id;
1888 $values[] = "'".$this->db->escape($product->multicurrency_code)."'";
1889 $values[] = (float) $product->multicurrency_subprice;
1890 $values[] = (float) $product->multicurrency_total_ht;
1891 $values[] = (float) $multicurrency->rate->rate;
1892 } else {
1893 for ($i = 0; $i < 5; $i++) {
1894 $values[] = 'NULL';
1895 }
1896 }
1897 }
1898 }
1899
1900 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'product_fournisseur_price ';
1901 $sql .= '(datec, fk_product, fk_soc, ref_fourn, price, quantity, unitprice, tva_tx, fk_user';
1902 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1903 $sql .= ',fk_multicurrency, multicurrency_code, multicurrency_unitprice, multicurrency_price, multicurrency_tx';
1904 }
1905 $sql .= ') VALUES ('.implode(',', $values).')';
1906
1907 $resql = $this->db->query($sql);
1908 if (!$resql) {
1909 $this->error = $this->db->error();
1910 $this->db->rollback();
1911 return -1;
1912 }
1913 return 1;
1914 }
1915
1916 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1923 public function setDraft($user)
1924 {
1925 // phpcs:enable
1926 $error = 0;
1927
1928 if ($this->status == self::STATUS_DRAFT) {
1929 dol_syslog(get_class($this)."::setDraft already draft status", LOG_WARNING);
1930 return 0;
1931 }
1932
1933 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1934 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1935 $sql .= " WHERE rowid = ".((int) $this->id);
1936
1937 if ($this->db->query($sql)) {
1938 if (!$error) {
1939 $this->oldcopy = clone $this;
1940 }
1941
1942 if (!$error) {
1943 // Call trigger
1944 $result = $this->call_trigger('PROPOSAL_SUPPLIER_UNVALIDATE', $user);
1945 if ($result < 0) {
1946 $error++;
1947 }
1948 }
1949
1950 if (!$error) {
1951 $this->status = self::STATUS_DRAFT;
1952 $this->statut = self::STATUS_DRAFT; // deprecated
1953 $this->db->commit();
1954 return 1;
1955 } else {
1956 $this->db->rollback();
1957 return -1;
1958 }
1959 } else {
1960 return -1;
1961 }
1962 }
1963
1964
1965 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1979 public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datec', $sortorder = 'DESC')
1980 {
1981 // phpcs:enable
1982 global $user;
1983
1984 $ga = array();
1985
1986 $search_sale = 0;
1987 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
1988 $search_sale = $user->id;
1989 }
1990
1991 $sql = "SELECT s.rowid, s.nom as name, s.client,";
1992 $sql .= " p.rowid as supplier_proposalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
1993 $sql .= " p.datep as dp, p.fin_validite as datelimite";
1994 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."supplier_proposal as p, ".MAIN_DB_PREFIX."c_propalst as c";
1995 $sql .= " WHERE p.entity IN (".getEntity('supplier_proposal').")";
1996 $sql .= " AND p.fk_soc = s.rowid";
1997 $sql .= " AND p.fk_statut = c.id";
1998 if ($socid) {
1999 $sql .= " AND s.rowid = ".((int) $socid);
2000 }
2001 if ($draft) {
2002 $sql .= " AND p.fk_statut = 0";
2003 }
2004 if ($notcurrentuser > 0) {
2005 $sql .= " AND p.fk_user_author <> ".((int) $user->id);
2006 }
2007 // Search on sale representative
2008 if ($search_sale && $search_sale != '-1') {
2009 if ($search_sale == -2) {
2010 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
2011 } elseif ($search_sale > 0) {
2012 $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).")";
2013 }
2014 }
2015 $sql .= $this->db->order($sortfield, $sortorder);
2016 $sql .= $this->db->plimit($limit, $offset);
2017
2018 $result = $this->db->query($sql);
2019 if ($result) {
2020 $num = $this->db->num_rows($result);
2021 if ($num) {
2022 $i = 0;
2023 while ($i < $num) {
2024 $obj = $this->db->fetch_object($result);
2025
2026 if ($shortlist == 1) {
2027 $ga[$obj->supplier_proposalid] = $obj->ref;
2028 } elseif ($shortlist == 2) {
2029 $ga[$obj->supplier_proposalid] = $obj->ref.' ('.$obj->name.')';
2030 } else {
2031 $ga[$i]['id'] = $obj->supplier_proposalid;
2032 $ga[$i]['ref'] = $obj->ref;
2033 $ga[$i]['name'] = $obj->name;
2034 }
2035
2036 $i++;
2037 }
2038 }
2039 return $ga;
2040 } else {
2041 dol_print_error($this->db);
2042 return -1;
2043 }
2044 }
2045
2053 public function delete($user, $notrigger = 0)
2054 {
2055 global $conf;
2056 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2057
2058 $error = 0;
2059
2060 $this->db->begin();
2061
2062 if (!$notrigger) {
2063 // Call trigger
2064 $result = $this->call_trigger('PROPOSAL_SUPPLIER_DELETE', $user);
2065 if ($result < 0) {
2066 $error++;
2067 }
2068 // End call triggers
2069 }
2070
2071 if (!$error) {
2072 $main = MAIN_DB_PREFIX.'supplier_proposaldet';
2073 $ef = $main."_extrafields";
2074 $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_supplier_proposal = ".((int) $this->id).")";
2075 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet WHERE fk_supplier_proposal = ".((int) $this->id);
2076 if ($this->db->query($sql)) {
2077 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposal WHERE rowid = ".((int) $this->id);
2078 if ($this->db->query($sqlef) && $this->db->query($sql)) {
2079 // Delete linked object
2080 $res = $this->deleteObjectLinked();
2081 if ($res < 0) {
2082 $error++;
2083 }
2084
2085 if (!$error) {
2086 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2087 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
2088 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
2089
2090 // We remove directory
2091 $ref = dol_sanitizeFileName($this->ref);
2092 if ($conf->supplier_proposal->dir_output && !empty($this->ref)) {
2093 $dir = $conf->supplier_proposal->dir_output."/".$ref;
2094 $file = $dir."/".$ref.".pdf";
2095 if (file_exists($file)) {
2096 dol_delete_preview($this);
2097
2098 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2099 $this->error = 'ErrorFailToDeleteFile';
2100 $this->errors = array('ErrorFailToDeleteFile');
2101 $this->db->rollback();
2102 return 0;
2103 }
2104 }
2105 if (file_exists($dir)) {
2106 $res = @dol_delete_dir_recursive($dir);
2107 if (!$res) {
2108 $this->error = 'ErrorFailToDeleteDir';
2109 $this->errors = array('ErrorFailToDeleteDir');
2110 $this->db->rollback();
2111 return 0;
2112 }
2113 }
2114 }
2115 }
2116
2117 // Removed extrafields
2118 if (!$error) {
2119 $result = $this->deleteExtraFields();
2120 if ($result < 0) {
2121 $error++;
2122 $errorflag = -4;
2123 dol_syslog(get_class($this)."::delete erreur ".$errorflag." ".$this->error, LOG_ERR);
2124 }
2125 }
2126
2127 if (!$error) {
2128 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
2129 $this->db->commit();
2130 return 1;
2131 } else {
2132 $this->error = $this->db->lasterror();
2133 $this->db->rollback();
2134 return 0;
2135 }
2136 } else {
2137 $this->error = $this->db->lasterror();
2138 $this->db->rollback();
2139 return -3;
2140 }
2141 } else {
2142 $this->error = $this->db->lasterror();
2143 $this->db->rollback();
2144 return -2;
2145 }
2146 } else {
2147 $this->db->rollback();
2148 return -1;
2149 }
2150 }
2151
2158 public function info($id)
2159 {
2160 $sql = "SELECT c.rowid, GREATEST(c.tms, cef.tms) as date_modification,";
2161 $sql .= " c.datec as date_creation, c.date_valid as date_validation, c.date_cloture as date_closure,";
2162 $sql .= " c.fk_user_author, c.fk_user_modif, c.fk_user_valid, c.fk_user_cloture";
2163 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as c";
2164 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."supplier_proposal_extrafields as cef ON cef.fk_object=c.rowid";
2165 $sql .= " WHERE c.rowid = ".((int) $id);
2166
2167 $result = $this->db->query($sql);
2168
2169 if ($result) {
2170 if ($this->db->num_rows($result)) {
2171 $obj = $this->db->fetch_object($result);
2172
2173 $this->id = $obj->rowid;
2174
2175 $this->date_creation = $this->db->jdate($obj->date_creation);
2176 $this->date_modification = empty($obj->date_modification) ? '' : $this->db->jdate($obj->date_modification);
2177 $this->date_validation = $this->db->jdate($obj->date_validation);
2178 $this->date_cloture = $this->db->jdate($obj->date_closure);
2179
2180 $this->user_creation_id = $obj->fk_user_author;
2181 $this->user_modification_id = $obj->fk_user_modif;
2182 $this->user_validation_id = $obj->fk_user_valid;
2183 $this->user_closing_id = $obj->fk_user_cloture;
2184 }
2185 $this->db->free($result);
2186 } else {
2187 dol_print_error($this->db);
2188 }
2189 }
2190
2191
2198 public function getLibStatut($mode = 0)
2199 {
2200 return $this->LibStatut((isset($this->status) ? $this->status : $this->statut), $mode);
2201 }
2202
2203 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2211 public function LibStatut($status, $mode = 1)
2212 {
2213 // phpcs:enable
2214
2215 // Init/load array of translation of status
2216 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
2217 global $langs;
2218 $langs->load("supplier_proposal");
2219 $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraft");
2220 $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidated");
2221 $this->labelStatus[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSigned");
2222 $this->labelStatus[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSigned");
2223 $this->labelStatus[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosed");
2224 $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraftShort");
2225 $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidatedShort");
2226 $this->labelStatusShort[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSignedShort");
2227 $this->labelStatusShort[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSignedShort");
2228 $this->labelStatusShort[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosedShort");
2229 }
2230
2231 $statusnew = '';
2232 if ($status == self::STATUS_DRAFT) {
2233 $statusnew = 'status0';
2234 } elseif ($status == self::STATUS_VALIDATED) {
2235 $statusnew = 'status1';
2236 } elseif ($status == self::STATUS_SIGNED) {
2237 $statusnew = 'status4';
2238 } elseif ($status == self::STATUS_NOTSIGNED) {
2239 $statusnew = 'status9';
2240 } elseif ($status == self::STATUS_CLOSE) {
2241 $statusnew = 'status6';
2242 }
2243
2244 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusnew, $mode);
2245 }
2246
2247
2248 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2256 public function load_board($user, $mode)
2257 {
2258 // phpcs:enable
2259 global $conf, $langs;
2260
2261 $now = dol_now();
2262
2263 $clause = " WHERE";
2264
2265 $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.date_cloture as datefin";
2266 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2267 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2268 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
2269 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2270 $clause = " AND";
2271 }
2272 $sql .= $clause." p.entity IN (".getEntity('supplier_proposal').")";
2273 if ($mode == 'opened') {
2274 $sql .= " AND p.fk_statut = 1";
2275 }
2276 if ($mode == 'signed') {
2277 $sql .= " AND p.fk_statut = 2";
2278 }
2279 if ($user->socid) {
2280 $sql .= " AND p.fk_soc = ".((int) $user->socid);
2281 }
2282
2283 $resql = $this->db->query($sql);
2284 if ($resql) {
2285 $label = $labelShort = '';
2286 $status = '';
2287 $delay_warning = 0;
2288 if ($mode == 'opened') {
2289 $delay_warning = !empty($conf->supplier_proposal->cloture->warning_delay) ? $conf->supplier_proposal->cloture->warning_delay : 0;
2290 $status = self::STATUS_VALIDATED;
2291 $label = $langs->trans("SupplierProposalsToClose");
2292 $labelShort = $langs->trans("ToAcceptRefuse");
2293 }
2294 if ($mode == 'signed') {
2295 $delay_warning = !empty($conf->supplier_proposal->facturation->warning_delay) ? $conf->supplier_proposal->facturation->warning_delay : 0;
2296 $status = self::STATUS_SIGNED;
2297 $label = $langs->trans("SupplierProposalsToProcess"); // May be billed or ordered
2298 $labelShort = $langs->trans("ToClose");
2299 }
2300
2301 $response = new WorkboardResponse();
2302 $response->warning_delay = $delay_warning / 60 / 60 / 24;
2303 $response->label = $label;
2304 $response->labelShort = $labelShort;
2305 $response->url = DOL_URL_ROOT.'/supplier_proposal/list.php?search_status='.$status;
2306 $response->img = img_object('', "propal");
2307
2308 // This assignment in condition is not a bug. It allows walking the results.
2309 while ($obj = $this->db->fetch_object($resql)) {
2310 $response->nbtodo++;
2311 if ($mode == 'opened') {
2312 $datelimit = $this->db->jdate($obj->datefin);
2313 if ($datelimit < ($now - $delay_warning)) {
2314 $response->nbtodolate++;
2315 }
2316 }
2317 // TODO Definir regle des propales a facturer en retard
2318 // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
2319 }
2320 return $response;
2321 } else {
2322 $this->error = $this->db->lasterror();
2323 return -1;
2324 }
2325 }
2326
2327
2336 public function initAsSpecimen($param = array())
2337 {
2338 global $conf, $langs;
2339
2340 // Load array of products prodids
2341 $num_prods = 0;
2342 $prodids = array();
2343 $sql = "SELECT rowid";
2344 $sql .= " FROM ".MAIN_DB_PREFIX."product";
2345 $sql .= " WHERE entity IN (".getEntity('product').")";
2346 if (array_key_exists('tobuy', $param)) {
2347 $sql .= " AND tobuy = ".((int) $param['tobuy']);
2348 }
2349 $sql .= $this->db->plimit(100);
2350
2351 $resql = $this->db->query($sql);
2352 if ($resql) {
2353 $num_prods = $this->db->num_rows($resql);
2354 $i = 0;
2355 while ($i < $num_prods) {
2356 $i++;
2357 $row = $this->db->fetch_row($resql);
2358 $prodids[$i] = $row[0];
2359 }
2360 }
2361
2362 // Initialise parameters
2363 $this->id = 0;
2364 $this->ref = 'SPECIMEN';
2365 $this->ref_supplier = 'NEMICEPS';
2366 $this->specimen = 1;
2367 $this->socid = 1;
2368 $this->date = time();
2369 $this->cond_reglement_id = 1;
2370 $this->cond_reglement_code = 'RECEP';
2371 $this->mode_reglement_id = 7;
2372 $this->mode_reglement_code = 'CHQ';
2373 $this->note_public = 'This is a comment (public)';
2374 $this->note_private = 'This is a comment (private)';
2375
2376 $this->multicurrency_tx = 1;
2377 $this->multicurrency_code = getDolCurrency();
2378
2379 // Lines
2380 $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)
2381 $xnbp = 0;
2382 while ($xnbp < $nbp) {
2383 $line = new SupplierProposalLine($this->db);
2384 $line->desc = $langs->trans("Description")." ".$xnbp;
2385 $line->qty = 1;
2386 $line->subprice = 100;
2387 $line->tva_tx = 19.6;
2388 $line->localtax1_tx = 0;
2389 $line->localtax2_tx = 0;
2390 if ($xnbp == 2) {
2391 $line->total_ht = 50;
2392 $line->total_ttc = 59.8;
2393 $line->total_tva = 9.8;
2394 $line->remise_percent = 50;
2395 } else {
2396 $line->total_ht = 100;
2397 $line->total_ttc = 119.6;
2398 $line->total_tva = 19.6;
2399 $line->remise_percent = 00;
2400 }
2401
2402 if ($num_prods > 0) {
2403 $prodid = mt_rand(1, $num_prods);
2404 $line->fk_product = $prodids[$prodid];
2405 }
2406
2407 $this->lines[$xnbp] = $line;
2408
2409 $this->total_ht += $line->total_ht;
2410 $this->total_tva += $line->total_tva;
2411 $this->total_ttc += $line->total_ttc;
2412
2413 $xnbp++;
2414 }
2415
2416 return 1;
2417 }
2418
2424 public function loadStateBoard()
2425 {
2426 global $user;
2427
2428 $this->nb = array();
2429 $clause = "WHERE";
2430
2431 $sql = "SELECT count(p.rowid) as nb";
2432 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2433 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
2434 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2435 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2436 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2437 $clause = "AND";
2438 }
2439 $sql .= " ".$clause." p.entity IN (".getEntity('supplier_proposal').")";
2440
2441 $resql = $this->db->query($sql);
2442 if ($resql) {
2443 // This assignment in condition is not a bug. It allows walking the results.
2444 while ($obj = $this->db->fetch_object($resql)) {
2445 $this->nb["supplier_proposals"] = $obj->nb;
2446 }
2447 $this->db->free($resql);
2448 return 1;
2449 } else {
2450 dol_print_error($this->db);
2451 $this->error = $this->db->lasterror();
2452 return -1;
2453 }
2454 }
2455
2456
2464 public function getNextNumRef($soc)
2465 {
2466 global $conf, $db, $langs;
2467 $langs->load("supplier_proposal");
2468
2469 if (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON')) {
2470 $mybool = false;
2471
2472 $file = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') . ".php";
2473 $classname = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
2474
2475 // Include file with class
2476 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2477 foreach ($dirmodels as $reldir) {
2478 $dir = dol_buildpath($reldir."core/modules/supplier_proposal/");
2479
2480 // Load file with numbering class (if found)
2481 $mybool = ((bool) @include_once $dir.$file) || $mybool;
2482 }
2483
2484 if (!$mybool) {
2485 dol_print_error(null, "Failed to include file ".$file);
2486 return '';
2487 }
2488
2489 $obj = new $classname();
2490 '@phan-var-force ModeleNumRefSupplierProposal $obj';
2492 $numref = "";
2493 $numref = $obj->getNextValue($soc, $this);
2494
2495 if ($numref != "") {
2496 return $numref;
2497 } else {
2498 $this->error = $obj->error;
2499 return "";
2500 }
2501 } else {
2502 $langs->load("errors");
2503 print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("SupplierProposal"));
2504 return "";
2505 }
2506 }
2507
2514 public function getTooltipContentArray($params)
2515 {
2516 global $conf, $langs;
2517
2518 $langs->load('supplier_proposal');
2519
2520 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2521 return ['optimize' => $langs->trans("ShowSupplierProposal")];
2522 }
2523
2524 //$option = $params['option'] ?? '';
2525 $datas = [];
2526
2527 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("SupplierProposal").'</u>';
2528 if (isset($this->status)) {
2529 $datas['picto'] .= ' '.$this->getLibStatut(5);
2530 }
2531 if (!empty($this->ref)) {
2532 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2533 }
2534 if (!empty($this->ref_fourn)) {
2535 $datas['ref_supplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_fourn;
2536 }
2537 if (!empty($this->total_ht)) {
2538 $datas['amount_ht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, getDolCurrency());
2539 }
2540 if (!empty($this->total_tva)) {
2541 $datas['amount_vat'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, getDolCurrency());
2542 }
2543 if (!empty($this->total_ttc)) {
2544 $datas['amount_ttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, getDolCurrency());
2545 }
2546
2547 return $datas;
2548 }
2549
2561 public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2562 {
2563 global $langs, $conf, $user, $hookmanager;
2564
2565 if (!empty($conf->dol_no_mouse_hover)) {
2566 $notooltip = 1; // Force disable tooltips
2567 }
2568
2569 $url = '';
2570 $result = '';
2571 $params = [
2572 'id' => $this->id,
2573 'objecttype' => $this->element,
2574 'option' => $option,
2575 ];
2576 $classfortooltip = 'classfortooltip';
2577 $dataparams = '';
2578 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2579 $classfortooltip = 'classforajaxtooltip';
2580 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
2581 $label = '';
2582 } else {
2583 $label = implode($this->getTooltipContentArray($params));
2584 }
2585
2586 if ($option == '') {
2587 $url = DOL_URL_ROOT.'/supplier_proposal/card.php?id='.$this->id.$get_params;
2588 }
2589 if ($option == 'document') {
2590 $url = DOL_URL_ROOT.'/supplier_proposal/document.php?id='.$this->id.$get_params;
2591 }
2592
2593 if ($option !== 'nolink') {
2594 // Add param to save lastsearch_values or not
2595 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2596 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2597 $add_save_lastsearch_values = 1;
2598 }
2599 if ($add_save_lastsearch_values) {
2600 $url .= '&save_lastsearch_values=1';
2601 }
2602 }
2603
2604 $linkclose = '';
2605 if (empty($notooltip) && $user->hasRight('propal', 'lire')) {
2606 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2607 $label = $langs->trans("ShowSupplierProposal");
2608 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
2609 }
2610 $linkclose .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
2611 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
2612 }
2613
2614 $linkstart = '<a href="'.$url.'"';
2615 $linkstart .= $linkclose.'>';
2616 $linkend = '</a>';
2617
2618 $result .= $linkstart;
2619 if ($withpicto) {
2620 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
2621 }
2622 if ($withpicto != 2) {
2623 $result .= $this->ref;
2624 }
2625 $result .= $linkend;
2626
2627 if ($addlinktonotes) {
2628 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2629 if ($txttoshow) {
2630 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2631 $result .= ' <span class="note inline-block">';
2632 $result .= '<a href="'.DOL_URL_ROOT.'/supplier_proposal/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2633 $result .= img_picto('', 'note');
2634 $result .= '</a>';
2635 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
2636 //$result.='</a>';
2637 $result .= '</span>';
2638 }
2639 }
2640 global $action;
2641 $hookmanager->initHooks(array($this->element . 'dao'));
2642 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
2643 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2644 if ($reshook > 0) {
2645 $result = $hookmanager->resPrint;
2646 } else {
2647 $result .= $hookmanager->resPrint;
2648 }
2649 return $result;
2650 }
2651
2657 public function getLinesArray()
2658 {
2659 // For other object, here we call fetch_lines. But fetch_lines does not exists on supplier proposal
2660
2661 $sql = 'SELECT pt.rowid, pt.label as custom_label, pt.description, pt.fk_product, pt.fk_remise_except,';
2662 $sql .= ' pt.qty, pt.tva_tx, pt.vat_src_code, pt.remise_percent, pt.subprice, pt.info_bits,';
2663 $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,';
2664 $sql .= ' pt.product_type, pt.rang, pt.fk_parent_line, pt.extraparams,';
2665 $sql .= ' p.label as product_label, p.ref, p.fk_product_type, p.rowid as prodid,';
2666 $sql .= ' p.description as product_desc, pt.ref_fourn as ref_supplier,';
2667 $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';
2668 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pt';
2669 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pt.fk_product=p.rowid';
2670 $sql .= ' WHERE pt.fk_supplier_proposal = '.((int) $this->id);
2671 $sql .= ' ORDER BY pt.rang ASC, pt.rowid';
2672
2673 dol_syslog(get_class($this).'::getLinesArray', LOG_DEBUG);
2674 $resql = $this->db->query($sql);
2675 if ($resql) {
2676 $num = $this->db->num_rows($resql);
2677 $i = 0;
2678
2679 while ($i < $num) {
2680 $obj = $this->db->fetch_object($resql);
2681
2682 $this->lines[$i] = new SupplierProposalLine($this->db);
2683 $this->lines[$i]->id = $obj->rowid; // for backward compatibility
2684 $this->lines[$i]->rowid = $obj->rowid;
2685 $this->lines[$i]->label = $obj->custom_label;
2686 $this->lines[$i]->description = $obj->description; // deprecated
2687 $this->lines[$i]->desc = $obj->description;
2688 $this->lines[$i]->fk_product = $obj->fk_product;
2689 $this->lines[$i]->ref = $obj->ref;
2690 $this->lines[$i]->product_label = $obj->product_label;
2691 $this->lines[$i]->product_desc = $obj->product_desc;
2692 $this->lines[$i]->fk_product_type = $obj->fk_product_type; // deprecated
2693 $this->lines[$i]->product_type = $obj->product_type;
2694 $this->lines[$i]->qty = $obj->qty;
2695 $this->lines[$i]->subprice = $obj->subprice;
2696 $this->lines[$i]->fk_remise_except = $obj->fk_remise_except;
2697 $this->lines[$i]->remise_percent = $obj->remise_percent;
2698 $this->lines[$i]->tva_tx = $obj->tva_tx;
2699 $this->lines[$i]->vat_src_code = $obj->vat_src_code;
2700 $this->lines[$i]->info_bits = $obj->info_bits;
2701 $this->lines[$i]->total_ht = $obj->total_ht;
2702 $this->lines[$i]->total_tva = $obj->total_tva;
2703 $this->lines[$i]->total_ttc = $obj->total_ttc;
2704 $this->lines[$i]->fk_fournprice = $obj->fk_fournprice;
2705 $marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->lines[$i]->fk_fournprice, $obj->pa_ht);
2706 $this->lines[$i]->pa_ht = $marginInfos[0];
2707 $this->lines[$i]->marge_tx = $marginInfos[1];
2708 $this->lines[$i]->marque_tx = $marginInfos[2];
2709 $this->lines[$i]->fk_parent_line = $obj->fk_parent_line;
2710 $this->lines[$i]->special_code = $obj->special_code;
2711 $this->lines[$i]->rang = $obj->rang;
2712
2713 $this->lines[$i]->ref_fourn = $obj->ref_supplier; // deprecated
2714 $this->lines[$i]->ref_supplier = $obj->ref_supplier;
2715
2716 // Multicurrency
2717 $this->lines[$i]->fk_multicurrency = $obj->fk_multicurrency;
2718 $this->lines[$i]->multicurrency_code = $obj->multicurrency_code;
2719 $this->lines[$i]->multicurrency_subprice = $obj->multicurrency_subprice;
2720 $this->lines[$i]->multicurrency_total_ht = $obj->multicurrency_total_ht;
2721 $this->lines[$i]->multicurrency_total_tva = $obj->multicurrency_total_tva;
2722 $this->lines[$i]->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
2723 $this->lines[$i]->fk_unit = $obj->fk_unit;
2724 $this->lines[$i]->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
2725
2726 $this->lines[$i]->fetch_optionals();
2727
2728 $i++;
2729 }
2730 $this->db->free($resql);
2731
2732 return 1;
2733 } else {
2734 $this->error = $this->db->error();
2735 return -1;
2736 }
2737 }
2738
2750 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2751 {
2752 global $langs;
2753
2754 $langs->load("supplier_proposal");
2755 $outputlangs->load("products");
2756
2757 if (!dol_strlen($modele)) {
2758 $modele = ''; // On supplier documents, template can be empty( no doc generated in this case)
2759
2760 if ($this->model_pdf) {
2761 $modele = $this->model_pdf;
2762 } elseif (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF')) {
2763 $modele = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF');
2764 }
2765 }
2766
2767 if (empty($modele)) {
2768 return 1;
2769 }
2770
2771 $modelpath = "core/modules/supplier_proposal/doc/";
2772
2773 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2774 }
2775
2776
2785 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2786 {
2787 $tables = array(
2788 'supplier_proposal'
2789 );
2790
2791 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2792 }
2793
2802 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
2803 {
2804 $tables = array(
2805 'supplier_proposaldet'
2806 );
2807
2808 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
2809 }
2810
2811
2819 public function getKanbanView($option = '', $arraydata = null)
2820 {
2821 global $langs;
2822
2823 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2824
2825 $return = '<div class="box-flex-item box-flex-grow-zero">';
2826 $return .= '<div class="info-box info-box-sm">';
2827 $return .= '<span class="info-box-icon bg-infobox-action">';
2828 $return .= img_picto('', $this->picto);
2829 //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2830 $return .= '</span>';
2831 $return .= '<div class="info-box-content">';
2832 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
2833 if ($selected >= 0) {
2834 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2835 }
2836 if (property_exists($this, 'socid')) {
2837 $return .= '<span class="info-box-ref"> | '.$this->socid.'</span>';
2838 }
2839 if (property_exists($this, 'delivery_date')) {
2840 $return .= '<br><span class="opacitymedium">'.$langs->trans("DateEnd").'</span> : <span class="info-box-label">'.dol_print_date($this->delivery_date).'</span>';
2841 }
2842 if (property_exists($this, 'total_ttc')) {
2843 $return .= '<br><span class="opacitymedium" >'.$langs->trans("AmountHT").' : </span><span class="info-box-label amount">'.price($this->total_ttc).'</span>';
2844 }
2845 if (method_exists($this, 'getLibStatut')) {
2846 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
2847 }
2848 $return .= '</div>';
2849 $return .= '</div>';
2850 $return .= '</div>';
2851 return $return;
2852 }
2853
2864 public function setCategories($categories)
2865 {
2866 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2867 return parent::setCategoriesCommon($categories, Categorie::TYPE_SUPPLIER_PROPOSAL);
2868 }
2869}
2870
2871
2876{
2880 public $db;
2881
2885 public $error = '';
2886
2890 public $element = 'supplier_proposaldet';
2891
2895 public $table_element = 'supplier_proposaldet';
2896
2900 public $parent_element = 'supplier_proposal';
2901
2905 public $fk_parent_attribute = 'fk_supplier_proposal';
2906
2910 public $oldline;
2911
2915 public $id;
2916
2920 public $fk_supplier_proposal;
2921
2925 public $fk_parent_line;
2926
2930 public $desc; // Description of the line
2931
2935 public $fk_product; // Predefined Product ID
2936
2942 public $fk_product_type;
2948 public $product_type = Product::TYPE_PRODUCT;
2949
2953 public $qty;
2957 public $tva_tx;
2961 public $vat_src_code;
2962
2966 public $remise_percent;
2967
2971 public $fk_remise_except;
2972
2976 public $rang = 0;
2977
2981 public $fk_fournprice;
2982
2986 public $pa_ht;
2990 public $marge_tx;
2994 public $marque_tx;
2995
2999 public $special_code; // Tag for special lines (exclusive tags)
3000 // 1: frais de port
3001 // 2: ecotaxe
3002 // 3: option line (when qty = 0)
3003
3007 public $info_bits = 0; // Liste d'options cumulables:
3008 // Bit 0: 0 if TVA normal - 1 if TVA NPR
3009 // Bit 1: 0 normal line - 1 if fixed reduction
3010
3014 public $total_ht; // Line Net Total (HT), including all quantity and the line discount
3018 public $total_tva; // Line Net Total (VAT), including all quantity and the line discount
3022 public $total_ttc; // Line Net Total (TTC), including all quantity and the line discount
3023
3027 public $date_start;
3031 public $date_end;
3032
3033 // From llx_product
3039 public $ref;
3040
3045 public $product_ref;
3046
3052 public $libelle;
3053
3058 public $product_label;
3059
3064 public $label;
3065
3070 public $product_desc;
3071
3075 public $localtax1_tx; // Local tax 1
3079 public $localtax2_tx; // Local tax 2
3083 public $localtax1_type; // Local tax 1 type
3087 public $localtax2_type; // Local tax 2 type
3091 public $total_localtax1; // Line total local tax 1
3095 public $total_localtax2; // Line total local tax 2
3096
3100 public $skip_update_total; // Skip update price total for special lines
3101
3105 public $ref_fourn;
3109 public $ref_supplier;
3110
3111 // Multicurrency
3115 public $fk_multicurrency;
3116
3120 public $multicurrency_code;
3124 public $multicurrency_subprice;
3128 public $multicurrency_total_ht;
3132 public $multicurrency_total_tva;
3136 public $multicurrency_total_ttc;
3137
3143 public function __construct($db)
3144 {
3145 $this->db = $db;
3146 }
3147
3154 public function fetch($rowid)
3155 {
3156 $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,';
3157 $sql .= ' pd.date_start, pd.date_end,';
3158 $sql .= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice, pd.subprice_ttc,';
3159 $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,';
3160 $sql .= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
3161 $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
3162 $sql .= ' pd.product_type, pd.ref_fourn as ref_produit_fourn, pd.extraparams,';
3163 $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';
3164 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pd';
3165 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
3166 $sql .= ' WHERE pd.rowid = '.((int) $rowid);
3167
3168 $result = $this->db->query($sql);
3169 if ($result) {
3170 if ($objp = $this->db->fetch_object($result)) {
3171 $this->id = $objp->rowid;
3172 $this->fk_supplier_proposal = $objp->fk_supplier_proposal;
3173 $this->fk_parent_line = $objp->fk_parent_line;
3174 $this->label = $objp->custom_label;
3175 $this->desc = $objp->description;
3176 $this->qty = $objp->qty;
3177
3178 $this->subprice = $objp->subprice;
3179 $this->subprice_ttc = $objp->subprice_ttc;
3180
3181 $this->tva_tx = $objp->tva_tx;
3182 $this->remise_percent = $objp->remise_percent;
3183 $this->fk_remise_except = $objp->fk_remise_except;
3184 $this->fk_product = $objp->fk_product;
3185 $this->info_bits = $objp->info_bits;
3186 $this->date_start = $this->db->jdate($objp->date_start);
3187 $this->date_end = $this->db->jdate($objp->date_end);
3188
3189 $this->total_ht = $objp->total_ht;
3190 $this->total_tva = $objp->total_tva;
3191 $this->total_ttc = $objp->total_ttc;
3192
3193 $this->fk_fournprice = $objp->fk_fournprice;
3194
3195 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
3196 $this->pa_ht = $marginInfos[0];
3197 $this->marge_tx = $marginInfos[1];
3198 $this->marque_tx = $marginInfos[2];
3199
3200 $this->special_code = $objp->special_code;
3201 $this->product_type = $objp->product_type;
3202 $this->rang = $objp->rang;
3203
3204 $this->product_ref = $objp->product_ref;
3205 $this->product_label = $objp->product_label;
3206 $this->product_desc = $objp->product_desc;
3207
3208 $this->ref_fourn = $objp->ref_produit_fourn;
3209
3210 $this->extraparams = !empty($objp->extraparams) ? (array) json_decode($objp->extraparams, true) : array();
3211
3212 // Multicurrency
3213 $this->fk_multicurrency = $objp->fk_multicurrency;
3214 $this->multicurrency_code = $objp->multicurrency_code;
3215 $this->multicurrency_subprice = $objp->multicurrency_subprice;
3216 $this->multicurrency_subprice_ttc = $objp->multicurrency_subprice_ttc;
3217 $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3218 $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3219 $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3220 $this->fk_unit = $objp->fk_unit;
3221
3222 $this->db->free($result);
3223 return 1;
3224 }
3225 return 0;
3226 } else {
3227 dol_print_error($this->db);
3228 return -1;
3229 }
3230 }
3231
3238 public function insert($notrigger = 0)
3239 {
3240 global $user;
3241
3242 $error = 0;
3243
3244 dol_syslog(get_class($this)."::insert rang=".$this->rang);
3245
3246 // Clean parameters
3247 if (empty($this->tva_tx)) {
3248 $this->tva_tx = 0;
3249 }
3250 if (empty($this->vat_src_code)) {
3251 $this->vat_src_code = '';
3252 }
3253 if (empty($this->localtax1_tx)) {
3254 $this->localtax1_tx = 0;
3255 }
3256 if (empty($this->localtax2_tx)) {
3257 $this->localtax2_tx = 0;
3258 }
3259 if (empty($this->localtax1_type)) {
3260 $this->localtax1_type = '';
3261 }
3262 if (empty($this->localtax2_type)) {
3263 $this->localtax2_type = '';
3264 }
3265 if (empty($this->total_localtax1)) {
3266 $this->total_localtax1 = 0;
3267 }
3268 if (empty($this->total_localtax2)) {
3269 $this->total_localtax2 = 0;
3270 }
3271 if (empty($this->rang)) {
3272 $this->rang = 0;
3273 }
3274 if (empty($this->remise_percent)) {
3275 $this->remise_percent = 0;
3276 }
3277 if (empty($this->info_bits)) {
3278 $this->info_bits = 0;
3279 }
3280 if (empty($this->special_code)) {
3281 $this->special_code = 0;
3282 }
3283 if (empty($this->fk_parent_line)) {
3284 $this->fk_parent_line = 0;
3285 }
3286 if (empty($this->fk_fournprice)) {
3287 $this->fk_fournprice = 0;
3288 }
3289 if (empty($this->fk_unit)) {
3290 $this->fk_unit = 0;
3291 }
3292 if (empty($this->subprice)) {
3293 $this->subprice = 0;
3294 }
3295
3296 if (empty($this->pa_ht)) {
3297 $this->pa_ht = 0;
3298 }
3299
3300 // if buy price not defined, define buyprice as configured in margin admin
3301 if ($this->pa_ht == 0) {
3302 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3303 if ($result < 0) {
3304 return $result;
3305 } else {
3306 $this->pa_ht = $result;
3307 }
3308 }
3309
3310 // Check parameters
3311 if ($this->product_type < 0) {
3312 return -1;
3313 }
3314
3315 $this->db->begin();
3316
3317 // Insert line into database
3318 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'supplier_proposaldet';
3319 $sql .= ' (fk_supplier_proposal, fk_parent_line, label, description, fk_product, product_type,';
3320 $sql .= ' date_start, date_end,';
3321 $sql .= ' fk_remise_except, qty, tva_tx, vat_src_code, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3322 $sql .= ' subprice, subprice_ttc, remise_percent, ';
3323 $sql .= ' info_bits, ';
3324 $sql .= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
3325 $sql .= ' ref_fourn,';
3326 $sql .= ' fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_subprice_ttc, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc, fk_unit)';
3327 $sql .= " VALUES (".$this->fk_supplier_proposal.",";
3328 $sql .= " ".($this->fk_parent_line > 0 ? ((int) $this->fk_parent_line) : "null").",";
3329 $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
3330 $sql .= " '".$this->db->escape($this->desc)."',";
3331 $sql .= " ".($this->fk_product ? ((int) $this->fk_product) : "null").",";
3332 $sql .= " ".((int) $this->product_type).",";
3333 $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3334 $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3335 $sql .= " ".($this->fk_remise_except ? ((int) $this->fk_remise_except) : "null").",";
3336 $sql .= " ".(float) price2num($this->qty, 'MS').",";
3337 $sql .= " ".(float) price2num($this->tva_tx).",";
3338 $sql .= " '".$this->db->escape($this->vat_src_code)."',";
3339 $sql .= " ".(float) price2num($this->localtax1_tx).",";
3340 $sql .= " ".(float) price2num($this->localtax2_tx).",";
3341 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3342 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3343 $sql .= " ".(float) price2num($this->subprice, 'MU') .",";
3344 $sql .= " ".(float) price2num($this->subprice_ttc, 'MU') .",";
3345 $sql .= " ".((float) $this->remise_percent).",";
3346 $sql .= " ".(isset($this->info_bits) ? ((int) $this->info_bits) : "null").",";
3347 $sql .= " ".(float) price2num($this->total_ht, 'MT').",";
3348 $sql .= " ".(float) price2num($this->total_tva, 'MT').",";
3349 $sql .= " ".(float) price2num($this->total_localtax1, 'MT').",";
3350 $sql .= " ".(float) price2num($this->total_localtax2, 'MT').",";
3351 $sql .= " ".(float) price2num($this->total_ttc, 'MT').",";
3352 $sql .= " ".(!empty($this->fk_fournprice) ? ((int) $this->fk_fournprice) : "null").",";
3353 $sql .= " ".(isset($this->pa_ht) ? (float) price2num($this->pa_ht, 'MU') : "null").",";
3354 $sql .= ' '.((int) $this->special_code).',';
3355 $sql .= ' '.((int) $this->rang).',';
3356 $sql .= " '".$this->db->escape($this->ref_fourn)."'";
3357 $sql .= ", ".($this->fk_multicurrency > 0 ? ((int) $this->fk_multicurrency) : 'null');
3358 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3359 $sql .= ", ".(float) price2num($this->multicurrency_subprice, 'CU');
3360 $sql .= ", ".(float) price2num($this->multicurrency_subprice_ttc, 'CU');
3361 $sql .= ", ".(float) price2num($this->multicurrency_total_ht, 'CT');
3362 $sql .= ", ".(float) price2num($this->multicurrency_total_tva, 'CT');
3363 $sql .= ", ".(float) price2num($this->multicurrency_total_ttc, 'CT');
3364 $sql .= ", ".($this->fk_unit ? ((int) $this->fk_unit) : 'null');
3365 $sql .= ')';
3366
3367 dol_syslog(get_class($this).'::insert', LOG_DEBUG);
3368 $resql = $this->db->query($sql);
3369 if ($resql) {
3370 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'supplier_proposaldet');
3371
3372 if (!$error) {
3373 $result = $this->insertExtraFields();
3374 if ($result < 0) {
3375 $error++;
3376 }
3377 }
3378
3379 if (!$error && !$notrigger) {
3380 // Call trigger
3381 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_INSERT', $user);
3382 if ($result < 0) {
3383 $this->db->rollback();
3384 return -1;
3385 }
3386 // End call triggers
3387 }
3388
3389 $this->db->commit();
3390 return 1;
3391 } else {
3392 $this->error = $this->db->error()." sql=".$sql;
3393 $this->db->rollback();
3394 return -1;
3395 }
3396 }
3397
3404 public function delete($user)
3405 {
3406 $error = 0;
3407
3408 $this->db->begin();
3409
3410 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet";
3411 $sql .= " WHERE rowid = ".((int) $this->id);
3412
3413 if ($this->db->query($sql)) {
3414 // Remove extrafields
3415 if (!$error) {
3416 $result = $this->deleteExtraFields();
3417 if ($result < 0) {
3418 $error++;
3419 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3420 }
3421 }
3422
3423 // Call trigger
3424 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_DELETE', $user);
3425 if ($result < 0) {
3426 $this->db->rollback();
3427 return -1;
3428 }
3429 // End call triggers
3430
3431 $this->db->commit();
3432
3433 return 1;
3434 } else {
3435 $this->error = $this->db->error()." sql=".$sql;
3436 $this->db->rollback();
3437 return -1;
3438 }
3439 }
3440
3447 public function update($notrigger = 0)
3448 {
3449 global $user;
3450
3451 $error = 0;
3452
3453 // Clean parameters
3454 if (empty($this->tva_tx)) {
3455 $this->tva_tx = 0;
3456 }
3457 if (empty($this->localtax1_tx)) {
3458 $this->localtax1_tx = 0;
3459 }
3460 if (empty($this->localtax2_tx)) {
3461 $this->localtax2_tx = 0;
3462 }
3463 if (empty($this->total_localtax1)) {
3464 $this->total_localtax1 = 0;
3465 }
3466 if (empty($this->total_localtax2)) {
3467 $this->total_localtax2 = 0;
3468 }
3469 if (empty($this->localtax1_type)) {
3470 $this->localtax1_type = '';
3471 }
3472 if (empty($this->localtax2_type)) {
3473 $this->localtax2_type = '';
3474 }
3475 if (empty($this->marque_tx)) {
3476 $this->marque_tx = 0;
3477 }
3478 if (empty($this->marge_tx)) {
3479 $this->marge_tx = 0;
3480 }
3481 if (empty($this->remise_percent)) {
3482 $this->remise_percent = 0;
3483 }
3484 if (empty($this->info_bits)) {
3485 $this->info_bits = 0;
3486 }
3487 if (empty($this->special_code)) {
3488 $this->special_code = 0;
3489 }
3490 if (empty($this->fk_parent_line)) {
3491 $this->fk_parent_line = 0;
3492 }
3493 if (empty($this->fk_fournprice)) {
3494 $this->fk_fournprice = 0;
3495 }
3496 if (empty($this->fk_unit)) {
3497 $this->fk_unit = 0;
3498 }
3499 if (empty($this->subprice)) {
3500 $this->subprice = 0;
3501 }
3502
3503 if (empty($this->pa_ht)) {
3504 $this->pa_ht = 0;
3505 }
3506
3507 // if buy price not defined, define buyprice as configured in margin admin
3508 if ($this->pa_ht == 0) {
3509 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3510 if ($result < 0) {
3511 return $result;
3512 } else {
3513 $this->pa_ht = $result;
3514 }
3515 }
3516
3517 $this->db->begin();
3518
3519 // Update line in database
3520 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3521 $sql .= " description = '".$this->db->escape($this->desc)."'";
3522 $sql .= " , label = ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
3523 $sql .= " , product_type = ".((int) $this->product_type);
3524 $sql .= " , date_start = ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null");
3525 $sql .= " , date_end = ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null");
3526 $sql .= " , tva_tx = '".price2num($this->tva_tx)."'";
3527 $sql .= " , localtax1_tx = ".(float) price2num($this->localtax1_tx);
3528 $sql .= " , localtax2_tx = ".(float) price2num($this->localtax2_tx);
3529 $sql .= " , localtax1_type = '".$this->db->escape($this->localtax1_type)."'";
3530 $sql .= " , localtax2_type = '".$this->db->escape($this->localtax2_type)."'";
3531 $sql .= " , qty = ".(float) price2num($this->qty);
3532 $sql .= " , subprice = ".(float) price2num($this->subprice);
3533 $sql .= " , subprice_ttc = ".(float) price2num($this->subprice_ttc);
3534 $sql .= " , remise_percent = ".(float) price2num($this->remise_percent);
3535 $sql .= " , info_bits = ".((int) $this->info_bits);
3536 if (empty($this->skip_update_total)) {
3537 $sql .= " , total_ht = ".(float) price2num($this->total_ht);
3538 $sql .= " , total_tva = ".(float) price2num($this->total_tva);
3539 $sql .= " , total_ttc = ".(float) price2num($this->total_ttc);
3540 $sql .= " , total_localtax1 = ".(float) price2num($this->total_localtax1);
3541 $sql .= " , total_localtax2 = ".(float) price2num($this->total_localtax2);
3542 }
3543 $sql .= " , fk_product_fournisseur_price = ".(!empty($this->fk_fournprice) ? "'".$this->db->escape((string) $this->fk_fournprice)."'" : "null");
3544 $sql .= " , buy_price_ht = ".(float) price2num($this->pa_ht);
3545 $sql .= " , special_code = ".((int) $this->special_code);
3546 $sql .= " , fk_parent_line = ".($this->fk_parent_line > 0 ? $this->fk_parent_line : "null");
3547 if (!empty($this->rang)) {
3548 $sql .= ", rang = ".((int) $this->rang);
3549 }
3550 $sql .= " , ref_fourn = ".(!empty($this->ref_fourn) ? "'".$this->db->escape($this->ref_fourn)."'" : "null");
3551 $sql .= " , fk_unit = ".($this->fk_unit ? (int) $this->fk_unit : 'null');
3552
3553 // Multicurrency
3554 $sql .= " , multicurrency_subprice = ".(float) price2num($this->multicurrency_subprice);
3555 $sql .= " , multicurrency_subprice_ttc = ".(float) price2num($this->multicurrency_subprice_ttc);
3556 $sql .= " , multicurrency_total_ht = ".(float) price2num($this->multicurrency_total_ht);
3557 $sql .= " , multicurrency_total_tva = ".(float) price2num($this->multicurrency_total_tva);
3558 $sql .= " , multicurrency_total_ttc = ".(float) price2num($this->multicurrency_total_ttc);
3559
3560 $sql .= " WHERE rowid = ".((int) $this->id);
3561
3562 dol_syslog(get_class($this)."::update", LOG_DEBUG);
3563 $resql = $this->db->query($sql);
3564 if ($resql) {
3565 if (!$error) {
3566 $result = $this->insertExtraFields();
3567 if ($result < 0) {
3568 $error++;
3569 }
3570 }
3571
3572 if (!$error && !$notrigger) {
3573 // Call trigger
3574 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_MODIFY', $user);
3575 if ($result < 0) {
3576 $this->db->rollback();
3577 return -1;
3578 }
3579 // End call triggers
3580 }
3581
3582 $this->db->commit();
3583 return 1;
3584 } else {
3585 $this->error = $this->db->error();
3586 $this->db->rollback();
3587 return -2;
3588 }
3589 }
3590
3591 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3598 public function update_total()
3599 {
3600 // phpcs:enable
3601 $this->db->begin();
3602
3603 // Update line in database
3604 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3605 $sql .= " total_ht = ".(float) price2num($this->total_ht, 'MT');
3606 $sql .= ",total_tva = ".(float) price2num($this->total_tva, 'MT');
3607 $sql .= ",total_ttc = ".(float) price2num($this->total_ttc, 'MT');
3608 $sql .= " WHERE rowid = ".((int) $this->id);
3609
3610 dol_syslog("SupplierProposalLine::update_total", LOG_DEBUG);
3611
3612 $resql = $this->db->query($sql);
3613 if ($resql) {
3614 $this->db->commit();
3615 return 1;
3616 } else {
3617 $this->error = $this->db->error();
3618 $this->db->rollback();
3619 return -2;
3620 }
3621 }
3622}
$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', ...)
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