dolibarr 22.0.5
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-2024 Frédéric France <frederic.france@free.fr>
17 * Copyright (C) 2020 Tobias Sekan <tobias.sekan@startmail.com>
18 * Copyright (C) 2022 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
19 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
20 *
21 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 3 of the License, or
24 * (at your option) any later version.
25 *
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
30 *
31 * You should have received a copy of the GNU General Public License
32 * along with this program. If not, see <https://www.gnu.org/licenses/>.
33 */
34
40require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
41require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
42require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
43require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
44require_once DOL_DOCUMENT_ROOT.'/margin/lib/margins.lib.php';
45require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
46require_once DOL_DOCUMENT_ROOT.'/core/class/commonincoterm.class.php';
47
52{
53 use CommonIncoterm;
54
58 public $element = 'supplier_proposal';
59
63 public $table_element = 'supplier_proposal';
64
68 public $table_element_line = 'supplier_proposaldet';
69
73 public $class_element_line = 'SupplierProposalLine';
77 public $fk_element = 'fk_supplier_proposal';
78
82 public $picto = 'supplier_proposal';
83
88 public $restrictiononfksoc = 1;
89
93 protected $table_ref_field = 'ref';
94
98 public $socid; // Id client
99
105 public $author;
106
110 public $ref_fourn; //Reference saisie lors de l'ajout d'une ligne à la demande
114 public $ref_supplier; //Reference saisie lors de l'ajout d'une ligne à la demande
115
120 public $statut; // 0 (draft), 1 (validated), 2 (signed), 3 (not signed), 4 (processed/billed)
121
125 public $date;
126
130 public $delivery_date;
131
137 public $datec;
138
144 public $datev;
145
149 public $user_author_id;
150
156 public $price;
157
163 public $tva;
164
170 public $total;
171
175 public $cond_reglement_code;
179 public $cond_reglement_doc; // label doc
180
184 public $mode_reglement_code;
189 public $mode_reglement;
190
194 public $extraparams = array();
195
199 public $lines = array();
200
204 public $line;
205
206 public $labelStatus = array();
207 public $labelStatusShort = array();
208
212 public $nbtodo;
216 public $nbtodolate;
217
218 // Multicurrency
222 public $fk_multicurrency;
223
227 public $multicurrency_code;
231 public $multicurrency_tx;
235 public $multicurrency_total_ht;
239 public $multicurrency_total_tva;
243 public $multicurrency_total_ttc;
244
245 public $fields = array(
246 'ref' => array('type' => 'varchar(255)', 'label' => 'Ref', 'enabled' => 1, 'visible' => 1, 'showoncombobox' => 1, 'position' => 25, 'searchall' => 1),
247 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 750, 'searchall' => 1),
248 );
249
253 const STATUS_DRAFT = 0;
254
259
263 const STATUS_SIGNED = 2;
264
269
273 const STATUS_CLOSE = 4;
274
275
276
284 public function __construct($db, $socid = 0, $supplier_proposalid = 0)
285 {
286 global $conf, $langs;
287
288 $this->db = $db;
289
290 $this->ismultientitymanaged = 1;
291 $this->socid = $socid;
292 $this->id = $supplier_proposalid;
293 }
294
295
296 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
307 public function add_product($idproduct, $qty, $remise_percent = 0)
308 {
309 // phpcs:enable
310 global $conf, $mysoc;
311
312 if (!$qty) {
313 $qty = 1;
314 }
315
316 dol_syslog(get_class($this)."::add_product $idproduct, $qty, $remise_percent");
317 if ($idproduct > 0) {
318 $prod = new Product($this->db);
319 $prod->fetch($idproduct);
320
321 $productdesc = $prod->description;
322
323 $tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
324 $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
325 if (empty($tva_tx)) {
326 $tva_npr = 0;
327 }
328 $localtax1_tx = get_localtax($tva_tx, 1, $mysoc, $this->thirdparty, $tva_npr);
329 $localtax2_tx = get_localtax($tva_tx, 2, $mysoc, $this->thirdparty, $tva_npr);
330
331 // multiprix
332 if ($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level) {
333 $price = $prod->multiprices[$this->thirdparty->price_level];
334 } else {
335 $price = $prod->price;
336 }
337
338 $line = new SupplierProposalLine($this->db);
339
340 $line->fk_product = $idproduct;
341 $line->desc = $productdesc;
342 $line->qty = $qty;
343 $line->subprice = $price;
344 $line->remise_percent = $remise_percent;
345 $line->tva_tx = $tva_tx;
346
347 $this->lines[] = $line;
348 return 1;
349 }
350 return -1;
351 }
352
353 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
360 public function insert_discount($idremise)
361 {
362 // phpcs:enable
363 global $langs;
364
365 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
366 include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
367
368 $this->db->begin();
369
370 $remise = new DiscountAbsolute($this->db);
371 $result = $remise->fetch($idremise);
372
373 if ($result > 0) {
374 if ($remise->fk_facture) { // Protection against multiple submission
375 $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
376 $this->db->rollback();
377 return -5;
378 }
379
380 $supplier_proposalligne = new SupplierProposalLine($this->db);
381 $supplier_proposalligne->fk_supplier_proposal = $this->id;
382 $supplier_proposalligne->fk_remise_except = $remise->id;
383 $supplier_proposalligne->desc = $remise->description; // Description ligne
384 $supplier_proposalligne->tva_tx = $remise->tva_tx;
385 $supplier_proposalligne->subprice = -(float) $remise->amount_ht;
386 $supplier_proposalligne->fk_product = 0; // Id produit predefini
387 $supplier_proposalligne->qty = 1;
388 $supplier_proposalligne->remise_percent = 0;
389 $supplier_proposalligne->rang = -1;
390 $supplier_proposalligne->info_bits = 2;
391
392 $supplier_proposalligne->total_ht = -(float) $remise->amount_ht;
393 $supplier_proposalligne->total_tva = -(float) $remise->amount_tva;
394 $supplier_proposalligne->total_ttc = -(float) $remise->amount_ttc;
395
396 $result = $supplier_proposalligne->insert();
397 if ($result > 0) {
398 $result = $this->update_price(1);
399 if ($result > 0) {
400 $this->db->commit();
401 return 1;
402 } else {
403 $this->db->rollback();
404 return -1;
405 }
406 } else {
407 $this->error = $supplier_proposalligne->error;
408 $this->db->rollback();
409 return -2;
410 }
411 } else {
412 $this->db->rollback();
413 return -2;
414 }
415 }
416
454 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)
455 {
456 global $mysoc, $conf, $langs;
457
458 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");
459 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
460
461 // Clean parameters
462 if (empty($remise_percent)) {
463 $remise_percent = 0;
464 }
465 if (empty($qty)) {
466 $qty = 0;
467 }
468 if (empty($info_bits)) {
469 $info_bits = 0;
470 }
471 if (empty($rang)) {
472 $rang = 0;
473 }
474 if (empty($fk_parent_line) || $fk_parent_line < 0) {
475 $fk_parent_line = 0;
476 }
477 if (empty($pu_ht)) {
478 $pu_ht = 0;
479 }
480
481 $remise_percent = price2num($remise_percent);
482 $qty = (float) price2num($qty);
483 $pu_ht = price2num($pu_ht);
484 $pu_ttc = price2num($pu_ttc);
485 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
486 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
487 }
488 $txlocaltax1 = price2num($txlocaltax1);
489 $txlocaltax2 = price2num($txlocaltax2);
490 $pa_ht = price2num($pa_ht);
491 if ($price_base_type == 'HT') {
492 $pu = $pu_ht;
493 } else {
494 $pu = $pu_ttc;
495 }
496
497 // Check parameters
498 if ($type < 0) {
499 return -1;
500 }
501
502 if ($this->statut == self::STATUS_DRAFT) {
503 $this->db->begin();
504
505 if ($fk_product > 0) {
506 if (getDolGlobalInt('SUPPLIER_PROPOSAL_WITH_PREDEFINED_PRICES_ONLY') == 1) { // Not the common case
507 // Check quantity is enough
508 dol_syslog(get_class($this)."::addline we check supplier prices fk_product=".$fk_product." fk_fournprice=".$fk_fournprice." qty=".$qty." ref_supplier=".$ref_supplier);
509 $productsupplier = new ProductFournisseur($this->db);
510 if ($productsupplier->fetch($fk_product) > 0) {
511 $product_type = $productsupplier->type;
512 $label = $productsupplier->label;
513 $fk_prod_fourn_price = $fk_fournprice;
514
515 // We use 'none' instead of $ref_supplier, because fourn_ref may not exists anymore. So we will take the first supplier price ok.
516 // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
517 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
518 $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
519 if ($result > 0) {
520 $pu = $productsupplier->fourn_pu; // Unit price supplier price set by get_buyprice
521 $ref_supplier = $productsupplier->ref_supplier; // Ref supplier price set by get_buyprice
522 // is remise percent not keyed but present for the product we add it
523 if ($remise_percent == 0 && $productsupplier->remise_percent != 0) {
524 $remise_percent = $productsupplier->remise_percent;
525 }
526 }
527 if ($result == 0) { // If result == 0, we failed to found the supplier reference price
528 $langs->load("errors");
529 $this->error = "Ref ".$productsupplier->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
530 $this->db->rollback();
531 dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
532 //$pu = $productsupplier->fourn_pu; // We do not overwrite unit price
533 //$ref = $productsupplier_fourn; // We do not overwrite ref supplier price
534 return -1;
535 }
536 if ($result == -1) {
537 $langs->load("errors");
538 $this->error = "Ref ".$productsupplier->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
539 $this->db->rollback();
540 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
541 return -1;
542 }
543 if ($result < -1) {
544 $this->error = $productsupplier->error;
545 $this->errors = $productsupplier->errors;
546 $this->db->rollback();
547 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
548 return -1;
549 }
550 } else {
551 $this->error = $productsupplier->error;
552 $this->errors = $productsupplier->errors;
553 $this->db->rollback();
554 return -1;
555 }
556 }
557 } else {
558 $product_type = $type;
559 }
560
561 // Calcul du total TTC et de la TVA pour la ligne a partir de
562 // qty, pu, remise_percent et txtva
563 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
564 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
565
566 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
567
568 // Clean vat code
569 $reg = array();
570 $vat_src_code = '';
571 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
572 $vat_src_code = $reg[1];
573 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
574 }
575
576 if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
577 $pu = 0;
578 }
579
580 $tabprice = calcul_price_total($qty, $pu, (float) $remise_percent, $txtva, (float) $txlocaltax1, (float) $txlocaltax2, 0, $price_base_type, $info_bits, $type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
581 $total_ht = $tabprice[0];
582 $total_tva = $tabprice[1];
583 $total_ttc = $tabprice[2];
584 $total_localtax1 = $tabprice[9];
585 $total_localtax2 = $tabprice[10];
586 $pu = $pu_ht = $tabprice[3];
587
588 // MultiCurrency
589 $multicurrency_total_ht = $tabprice[16];
590 $multicurrency_total_tva = $tabprice[17];
591 $multicurrency_total_ttc = $tabprice[18];
592 $pu_ht_devise = $tabprice[19];
593
594 // Rang to use
595 $ranktouse = $rang;
596 if ($ranktouse == -1) {
597 $rangmax = $this->line_max($fk_parent_line);
598 $ranktouse = $rangmax + 1;
599 }
600
601 // TODO A virer
602 // Anciens indicateurs: $price, $remise (a ne plus utiliser)
603 $price = $pu;
604 $remise = 0;
605 if ($remise_percent > 0) {
606 $remise = round(((float) $pu * (float) $remise_percent / 100), 2);
607 $price = (float) $pu - $remise;
608 }
609
610 // Insert line
611 $this->line = new SupplierProposalLine($this->db);
612
613 $this->line->fk_supplier_proposal = $this->id;
614 $this->line->label = $label;
615 $this->line->desc = $desc;
616 $this->line->qty = $qty;
617
618 $this->line->vat_src_code = $vat_src_code;
619 $this->line->tva_tx = $txtva;
620 $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
621 $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
622 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
623 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
624 $this->line->fk_product = $fk_product;
625 $this->line->remise_percent = $remise_percent;
626 $this->line->subprice = (float) $pu_ht;
627 $this->line->rang = $ranktouse;
628 $this->line->info_bits = $info_bits;
629 $this->line->total_ht = (float) $total_ht;
630 $this->line->total_tva = (float) $total_tva;
631 $this->line->total_localtax1 = (float) $total_localtax1;
632 $this->line->total_localtax2 = (float) $total_localtax2;
633 $this->line->total_ttc = (float) $total_ttc;
634 $this->line->product_type = $type;
635 $this->line->special_code = $special_code;
636 $this->line->fk_parent_line = $fk_parent_line;
637 $this->line->fk_unit = $fk_unit;
638 $this->line->origin = $origin;
639 $this->line->origin_id = $origin_id;
640 $this->line->ref_fourn = $this->db->escape($ref_supplier);
641 $this->line->date_start = $date_start;
642 $this->line->date_end = $date_end;
643
644 // infos merge
645 if (!empty($fk_product) && $fk_product > 0 && empty($fk_fournprice) && empty($pa_ht)) {
646 // When fk_fournprice is 0, we take the lowest buying price
647 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
648 $productFournisseur = new ProductFournisseur($this->db);
649 $productFournisseur->find_min_price_product_fournisseur($fk_product);
650 $this->line->fk_fournprice = $productFournisseur->product_fourn_price_id;
651 } else {
652 $this->line->fk_fournprice = ($fk_fournprice > 0 ? $fk_fournprice : 0); // If fk_fournprice is -1, we will not use fk_fournprice
653 }
654 $this->line->pa_ht = $pa_ht;
655 //var_dump($this->line->fk_fournprice);exit;
656
657 // Multicurrency
658 $this->line->fk_multicurrency = $this->fk_multicurrency;
659 $this->line->multicurrency_code = $this->multicurrency_code;
660 $this->line->multicurrency_subprice = (float) $pu_ht_devise;
661 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
662 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
663 $this->line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
664
665 // Mise en option de la ligne
666 if (empty($qty) && empty($special_code)) {
667 $this->line->special_code = 3;
668 }
669
670 if (is_array($array_options) && count($array_options) > 0) {
671 $this->line->array_options = $array_options;
672 }
673
674 $result = $this->line->insert();
675 if ($result > 0) {
676 // Reorder if child line
677 if (!empty($fk_parent_line)) {
678 $this->line_order(true, 'DESC');
679 } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) { // Update all rank of all other lines
680 $linecount = count($this->lines);
681 for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
682 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
683 }
684 }
685
686 // Mise a jour information denormalisees au niveau de la propale meme
687 $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.
688 if ($result > 0) {
689 $this->db->commit();
690 return $this->line->id;
691 } else {
692 $this->db->rollback();
693 return -1;
694 }
695 } else {
696 $this->error = $this->line->error;
697 $this->errors = $this->line->errors;
698 $this->db->rollback();
699 return -2;
700 }
701 } else {
702 $this->error = 'BadStatusOfObjectToAddLine';
703 return -5;
704 }
705 }
706
707
734 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)
735 {
736 global $conf, $user, $langs, $mysoc;
737
738 dol_syslog(get_class($this)."::updateLine $rowid, $pu, $qty, $remise_percent, $txtva, $desc, $price_base_type, $info_bits");
739 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
740
741 // Clean parameters
742 $remise_percent = price2num($remise_percent);
743 $qty = (float) price2num($qty);
744 $pu = price2num($pu);
745 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
746 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
747 }
748 $txlocaltax1 = price2num($txlocaltax1);
749 $txlocaltax2 = price2num($txlocaltax2);
750 $pa_ht = price2num($pa_ht);
751 if (empty($qty) && empty($special_code)) {
752 $special_code = 3; // Set option tag
753 }
754 if (!empty($qty) && $special_code == 3) {
755 $special_code = 0; // Remove option tag
756 }
757
758 if ($this->status == 0) {
759 $this->db->begin();
760
761 // Calcul du total TTC et de la TVA pour la ligne a partir de
762 // qty, pu, remise_percent et txtva
763 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
764 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
765
766 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
767
768 // Clean vat code
769 $reg = array();
770 $vat_src_code = '';
771 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
772 $vat_src_code = $reg[1];
773 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
774 }
775
776 if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
777 $pu = 0;
778 }
779
780 $tabprice = calcul_price_total($qty, $pu, (float) $remise_percent, $txtva, (float) $txlocaltax1, (float) $txlocaltax2, 0, $price_base_type, $info_bits, $type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
781 $total_ht = $tabprice[0];
782 $total_tva = $tabprice[1];
783 $total_ttc = $tabprice[2];
784 $total_localtax1 = $tabprice[9];
785 $total_localtax2 = $tabprice[10];
786 $pu_ht = $tabprice[3];
787 $pu_tva = $tabprice[4];
788 $pu_ttc = $tabprice[5];
789
790 // MultiCurrency
791 $multicurrency_total_ht = $tabprice[16];
792 $multicurrency_total_tva = $tabprice[17];
793 $multicurrency_total_ttc = $tabprice[18];
794 $pu_ht_devise = $tabprice[19];
795
796 $pu = $pu_ht;
797 if ($price_base_type == 'TTC') {
798 $pu = $pu_ttc;
799 }
800
801 // Fetch current line from the database and then clone the object and set it in $oldline property
802 $line = new SupplierProposalLine($this->db);
803 $line->fetch($rowid);
804 $line->fetch_optionals();
805
806 $fk_product = $line->fk_product;
807
808 // Stock previous line records
809 $staticline = clone $line;
810
811 $line->oldline = $staticline;
812 $this->line = $line;
813 $this->line->context = $this->context;
814
815 // Reorder if fk_parent_line change
816 if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
817 $rangmax = $this->line_max($fk_parent_line);
818 $this->line->rang = $rangmax + 1;
819 }
820
821 $this->line->id = $rowid;
822 $this->line->label = $label;
823 $this->line->desc = $desc;
824 $this->line->qty = $qty;
825 $this->line->product_type = $type;
826
827 $this->line->vat_src_code = $vat_src_code;
828 $this->line->tva_tx = $txtva;
829 $this->line->localtax1_tx = $txlocaltax1;
830 $this->line->localtax2_tx = $txlocaltax2;
831 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
832 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
833 $this->line->remise_percent = $remise_percent;
834 $this->line->subprice = (float) $pu;
835 $this->line->info_bits = $info_bits;
836 $this->line->total_ht = (float) $total_ht;
837 $this->line->total_tva = (float) $total_tva;
838 $this->line->total_localtax1 = (float) $total_localtax1;
839 $this->line->total_localtax2 = (float) $total_localtax2;
840 $this->line->total_ttc = (float) $total_ttc;
841 $this->line->special_code = $special_code;
842 $this->line->fk_parent_line = $fk_parent_line;
843 $this->line->skip_update_total = $skip_update_total;
844 $this->line->ref_fourn = $ref_supplier;
845 $this->line->fk_unit = $fk_unit;
846
847 // infos merge
848 if (!empty($fk_product) && $fk_product > 0 && empty($fk_fournprice) && empty($pa_ht)) {
849 // by external module, take lowest buying price
850 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
851 $productFournisseur = new ProductFournisseur($this->db);
852 $productFournisseur->find_min_price_product_fournisseur($fk_product);
853 $this->line->fk_fournprice = $productFournisseur->product_fourn_price_id;
854 } else {
855 $this->line->fk_fournprice = $fk_fournprice;
856 }
857 $this->line->pa_ht = $pa_ht;
858
859 if (is_array($array_options) && count($array_options) > 0) {
860 // We replace values in this->line->array_options only for entries defined into $array_options
861 foreach ($array_options as $key => $value) {
862 $this->line->array_options[$key] = $array_options[$key];
863 }
864 }
865
866 // Multicurrency
867 $this->line->multicurrency_subprice = (float) $pu_ht_devise;
868 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
869 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
870 $this->line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
871
872 $result = $this->line->update();
873 if ($result > 0) {
874 // Reorder if child line
875 if (!empty($fk_parent_line)) {
876 $this->line_order(true, 'DESC');
877 }
878
879 $this->update_price(1);
880
881 $this->db->commit();
882 return $result;
883 } else {
884 $this->error = $this->db->error();
885 $this->db->rollback();
886 return -1;
887 }
888 } else {
889 dol_syslog(get_class($this)."::updateline Erreur -2 SupplierProposal en mode incompatible pour cette action");
890 return -2;
891 }
892 }
893
894
901 public function deleteLine($lineid)
902 {
903 global $user;
904
905 if ($this->statut == 0) {
906 $line = new SupplierProposalLine($this->db);
907
908 // For triggers
909 $line->fetch($lineid);
910
911 if ($this->id > 0 && (int) $line->fk_supplier_proposal !== (int) $this->id) {
912 $this->error = 'ErrorLineIDDoesNotMatchWithObjectID';
913 return -1;
914 }
915
916 if ($line->delete($user) > 0) {
917 $this->update_price(1);
918
919 return 1;
920 } else {
921 return -1;
922 }
923 } else {
924 return -2;
925 }
926 }
927
928
937 public function create($user, $notrigger = 0)
938 {
939 global $langs, $conf, $mysoc, $hookmanager;
940 $error = 0;
941
942 $now = dol_now();
943
944 dol_syslog(get_class($this)."::create");
945
946 // Check parameters
947 $result = $this->fetch_thirdparty();
948 if ($result < 0) {
949 $this->error = "Failed to fetch company";
950 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
951 return -3;
952 }
953 if (!empty($this->ref)) { // We check that ref is not already used
954 $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
955 if ($result > 0) {
956 $this->error = 'ErrorRefAlreadyExists';
957 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
958 $this->db->rollback();
959 return -1;
960 }
961 }
962
963 // Set tmp vars
964 $delivery_date = $this->delivery_date;
965
966 // Multicurrency
967 if (!empty($this->multicurrency_code)) {
968 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $now);
969 }
970 if (empty($this->fk_multicurrency)) {
971 $this->multicurrency_code = $conf->currency;
972 $this->fk_multicurrency = 0;
973 $this->multicurrency_tx = 1;
974 }
975
976 $this->db->begin();
977
978 // Insert into database
979 $sql = "INSERT INTO ".MAIN_DB_PREFIX."supplier_proposal (";
980 $sql .= "fk_soc";
981 $sql .= ", price";
982 $sql .= ", total_tva";
983 $sql .= ", total_ttc";
984 $sql .= ", datec";
985 $sql .= ", ref";
986 $sql .= ", fk_user_author";
987 $sql .= ", note_private";
988 $sql .= ", note_public";
989 $sql .= ", model_pdf";
990 $sql .= ", fk_cond_reglement";
991 $sql .= ", fk_mode_reglement";
992 $sql .= ", fk_account";
993 $sql .= ", date_livraison";
994 $sql .= ", fk_shipping_method";
995 $sql .= ", fk_projet";
996 $sql .= ", entity";
997 $sql .= ", fk_multicurrency";
998 $sql .= ", multicurrency_code";
999 $sql .= ", multicurrency_tx";
1000 $sql .= ") ";
1001 $sql .= " VALUES (";
1002 $sql .= ((int) $this->socid);
1003 $sql .= ", 0";
1004 $sql .= ", 0";
1005 $sql .= ", 0";
1006 $sql .= ", '".$this->db->idate($now)."'";
1007 $sql .= ", '(PROV)'";
1008 $sql .= ", ".($user->id > 0 ? ((int) $user->id) : "null");
1009 $sql .= ", '".$this->db->escape($this->note_private)."'";
1010 $sql .= ", '".$this->db->escape($this->note_public)."'";
1011 $sql .= ", '".$this->db->escape($this->model_pdf)."'";
1012 $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : 'NULL');
1013 $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : 'NULL');
1014 $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
1015 $sql .= ", ".(isDolTms($delivery_date) ? "'".$this->db->idate($delivery_date)."'" : "null");
1016 $sql .= ", ".($this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : 'NULL');
1017 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
1018 $sql .= ", ".((int) $conf->entity);
1019 $sql .= ", ".((int) $this->fk_multicurrency);
1020 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1021 $sql .= ", ".((float) $this->multicurrency_tx);
1022 $sql .= ")";
1023
1024 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1025 $resql = $this->db->query($sql);
1026 if ($resql) {
1027 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."supplier_proposal");
1028
1029 if ($this->id) {
1030 $this->ref = '(PROV'.$this->id.')';
1031 $sql = 'UPDATE '.MAIN_DB_PREFIX."supplier_proposal SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
1032
1033 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1034 $resql = $this->db->query($sql);
1035 if (!$resql) {
1036 $error++;
1037 }
1038
1039 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1040 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1041 }
1042
1043 // Add object linked
1044 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1045 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1046 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, ...))
1047 foreach ($tmp_origin_id as $origin_id) {
1048 $ret = $this->add_object_linked($origin, $origin_id);
1049 if (!$ret) {
1050 dol_print_error($this->db);
1051 $error++;
1052 }
1053 }
1054 }
1055 }
1056 }
1057
1058 /*
1059 * Insertion du detail des produits dans la base
1060 */
1061 if (!$error) {
1062 $fk_parent_line = 0;
1063 $num = count($this->lines);
1064
1065 for ($i = 0; $i < $num; $i++) {
1066 // Reset fk_parent_line for no child products and special product
1067 if (($this->lines[$i]->product_type != 9 && empty($this->lines[$i]->fk_parent_line)) || $this->lines[$i]->product_type == 9) {
1068 $fk_parent_line = 0;
1069 }
1070
1071 $result = $this->addline(
1072 $this->lines[$i]->desc,
1073 $this->lines[$i]->subprice,
1074 $this->lines[$i]->qty,
1075 $this->lines[$i]->tva_tx,
1076 $this->lines[$i]->localtax1_tx,
1077 $this->lines[$i]->localtax2_tx,
1078 $this->lines[$i]->fk_product,
1079 $this->lines[$i]->remise_percent,
1080 'HT',
1081 0,
1082 0,
1083 $this->lines[$i]->product_type,
1084 $this->lines[$i]->rang,
1085 $this->lines[$i]->special_code,
1086 $fk_parent_line,
1087 $this->lines[$i]->fk_fournprice,
1088 $this->lines[$i]->pa_ht,
1089 empty($this->lines[$i]->label) ? '' : $this->lines[$i]->label, // deprecated
1090 $this->lines[$i]->array_options,
1091 $this->lines[$i]->ref_fourn,
1092 $this->lines[$i]->fk_unit,
1093 'supplier_proposal',
1094 $this->lines[$i]->rowid
1095 );
1096
1097 if ($result < 0) {
1098 $error++;
1099 $this->error = $this->db->error;
1100 dol_print_error($this->db);
1101 break;
1102 }
1103 // Defined the new fk_parent_line
1104 if ($result > 0 && $this->lines[$i]->product_type == 9) {
1105 $fk_parent_line = $result;
1106 }
1107 }
1108 }
1109
1110 if (!$error) {
1111 // Mise a jour infos denormalisees
1112 $resql = $this->update_price(1);
1113 if ($resql) {
1114 $action = 'update';
1115
1116 // Actions on extra fields
1117 if (!$error) {
1118 $result = $this->insertExtraFields();
1119 if ($result < 0) {
1120 $error++;
1121 }
1122 }
1123
1124 if (!$error && !$notrigger) {
1125 // Call trigger
1126 $result = $this->call_trigger('PROPOSAL_SUPPLIER_CREATE', $user);
1127 if ($result < 0) {
1128 $error++;
1129 }
1130 // End call triggers
1131 }
1132 } else {
1133 $this->error = $this->db->lasterror();
1134 $error++;
1135 }
1136 }
1137 } else {
1138 $this->error = $this->db->lasterror();
1139 $error++;
1140 }
1141
1142 if (!$error) {
1143 $this->db->commit();
1144 dol_syslog(get_class($this)."::create done id=".$this->id);
1145 return $this->id;
1146 } else {
1147 $this->db->rollback();
1148 return -2;
1149 }
1150 } else {
1151 $this->error = $this->db->lasterror();
1152 $this->db->rollback();
1153 return -1;
1154 }
1155 }
1156
1164 public function createFromClone(User $user, $fromid = 0)
1165 {
1166 global $conf, $hookmanager;
1167
1168 $error = 0;
1169 $now = dol_now();
1170
1171 $this->db->begin();
1172
1173 // get extrafields so they will be clone
1174 foreach ($this->lines as $line) {
1175 $line->fetch_optionals();
1176 }
1177
1178 // Load source object
1179 $objFrom = clone $this;
1180
1181 $objsoc = new Societe($this->db);
1182
1183 // Change socid if needed
1184 if (!empty($fromid) && $fromid != $this->socid) {
1185 if ($objsoc->fetch($fromid) > 0) {
1186 $this->socid = $objsoc->id;
1187 $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1188 $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1189 unset($this->fk_project);
1190 }
1191
1192 // TODO Change product price if multi-prices
1193 } else {
1194 $objsoc->fetch($this->socid);
1195 }
1196
1197 $this->id = 0;
1198 $this->statut = 0;
1199
1200 if (!getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') || !is_readable(DOL_DOCUMENT_ROOT."/core/modules/supplier_proposal/" . getDolGlobalString('SUPPLIER_PROPOSAL_ADDON').".php")) {
1201 $this->error = 'ErrorSetupNotComplete';
1202 return -1;
1203 }
1204
1205 // Clear fields
1206 $this->user_author_id = $user->id;
1207 $this->user_validation_id = 0;
1208 $this->date = $now;
1209
1210 // Set ref
1211 require_once DOL_DOCUMENT_ROOT."/core/modules/supplier_proposal/" . getDolGlobalString('SUPPLIER_PROPOSAL_ADDON').'.php';
1212 $obj = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
1213 $modSupplierProposal = new $obj();
1214 '@phan-var-force ModeleNumRefSupplierProposal $modSupplierProposal';
1215 $this->ref = $modSupplierProposal->getNextValue($objsoc, $this);
1216
1217 // Create clone
1218 $this->context['createfromclone'] = 'createfromclone';
1219 $result = $this->create($user);
1220 if ($result < 0) {
1221 $error++;
1222 }
1223
1224 if (!$error) {
1225 // Hook of thirdparty module
1226 if (is_object($hookmanager)) {
1227 $parameters = array('objFrom' => $objFrom);
1228 $action = '';
1229 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1230 if ($reshook < 0) {
1231 $this->setErrorsFromObject($hookmanager);
1232 $error++;
1233 }
1234 }
1235 }
1236
1237 unset($this->context['createfromclone']);
1238
1239 // End
1240 if (!$error) {
1241 $this->db->commit();
1242 return $this->id;
1243 } else {
1244 $this->db->rollback();
1245 return -1;
1246 }
1247 }
1248
1256 public function fetch($rowid, $ref = '')
1257 {
1258 global $conf;
1259
1260 $sql = "SELECT p.rowid, p.entity, p.ref, p.fk_soc as socid";
1261 $sql .= ", p.total_ttc, p.total_tva, p.localtax1, p.localtax2, p.total_ht";
1262 $sql .= ", p.datec";
1263 $sql .= ", p.date_valid as datev";
1264 $sql .= ", p.date_livraison as delivery_date";
1265 $sql .= ", p.model_pdf, p.extraparams";
1266 $sql .= ", p.note_private, p.note_public";
1267 $sql .= ", p.fk_projet as fk_project, p.fk_statut";
1268 $sql .= ", p.fk_user_author, p.fk_user_valid, p.fk_user_cloture";
1269 $sql .= ", p.fk_cond_reglement";
1270 $sql .= ", p.fk_mode_reglement";
1271 $sql .= ', p.fk_account';
1272 $sql .= ", p.fk_shipping_method";
1273 $sql .= ", p.last_main_doc";
1274 $sql .= ", p.fk_multicurrency, p.multicurrency_code, p.multicurrency_tx, p.multicurrency_total_ht, p.multicurrency_total_tva, p.multicurrency_total_ttc";
1275 $sql .= ", c.label as statut_label";
1276 $sql .= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc";
1277 $sql .= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement";
1278 $sql .= " FROM ".MAIN_DB_PREFIX."c_propalst as c, ".MAIN_DB_PREFIX."supplier_proposal as p";
1279 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id';
1280 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON p.fk_cond_reglement = cr.rowid';
1281 $sql .= " WHERE p.fk_statut = c.id";
1282 $sql .= " AND p.entity IN (".getEntity('supplier_proposal').")";
1283 if ($ref) {
1284 $sql .= " AND p.ref = '".$this->db->escape($ref)."'";
1285 } else {
1286 $sql .= " AND p.rowid = ".((int) $rowid);
1287 }
1288
1289 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1290 $resql = $this->db->query($sql);
1291 if ($resql) {
1292 if ($this->db->num_rows($resql)) {
1293 $obj = $this->db->fetch_object($resql);
1294
1295 $this->id = $obj->rowid;
1296 $this->entity = $obj->entity;
1297
1298 $this->ref = $obj->ref;
1299 $this->total_ht = $obj->total_ht;
1300 $this->total_tva = $obj->total_tva;
1301 $this->total_localtax1 = $obj->localtax1;
1302 $this->total_localtax2 = $obj->localtax2;
1303 $this->total_ttc = $obj->total_ttc;
1304 $this->socid = $obj->socid;
1305 $this->fk_project = $obj->fk_project;
1306 $this->model_pdf = $obj->model_pdf;
1307 $this->note = $obj->note_private; // TODO deprecated
1308 $this->note_private = $obj->note_private;
1309 $this->note_public = $obj->note_public;
1310 $this->statut = (int) $obj->fk_statut;
1311 $this->status = (int) $obj->fk_statut;
1312 $this->datec = $this->db->jdate($obj->datec); // TODO deprecated
1313 $this->datev = $this->db->jdate($obj->datev); // TODO deprecated
1314 $this->date_creation = $this->db->jdate($obj->datec); // Creation date
1315 $this->date = $this->date_creation;
1316 $this->date_validation = $this->db->jdate($obj->datev); // Validation date
1317 $this->delivery_date = $this->db->jdate($obj->delivery_date);
1318 $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1319
1320 $this->last_main_doc = $obj->last_main_doc;
1321 $this->mode_reglement_id = $obj->fk_mode_reglement;
1322 $this->mode_reglement_code = $obj->mode_reglement_code;
1323 $this->mode_reglement = $obj->mode_reglement;
1324 $this->fk_account = ($obj->fk_account > 0) ? $obj->fk_account : null;
1325 $this->cond_reglement_id = $obj->fk_cond_reglement;
1326 $this->cond_reglement_code = $obj->cond_reglement_code;
1327 $this->cond_reglement = $obj->cond_reglement;
1328 $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1329
1330 $this->extraparams = (array) (!empty($obj->extraparams) ? json_decode($obj->extraparams, true) : array());
1331
1332 $this->user_author_id = $obj->fk_user_author;
1333 $this->user_validation_id = $obj->fk_user_valid;
1334 $this->user_closing_id = $obj->fk_user_cloture;
1335
1336 // Multicurrency
1337 $this->fk_multicurrency = $obj->fk_multicurrency;
1338 $this->multicurrency_code = $obj->multicurrency_code;
1339 $this->multicurrency_tx = $obj->multicurrency_tx;
1340 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1341 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1342 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1343
1344 $this->db->free($resql);
1345
1346 // Retrieve all extrafield
1347 // fetch optionals attributes and labels
1348 $this->fetch_optionals();
1349
1350 $this->lines = array();
1351
1352 // Lines of supplier proposals
1353 $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,";
1354 $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,";
1355 $sql .= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label,';
1356 $sql .= ' d.ref_fourn as ref_produit_fourn, d.extraparams,';
1357 $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';
1358 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposaldet as d";
1359 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON d.fk_product = p.rowid";
1360 $sql .= " WHERE d.fk_supplier_proposal = ".((int) $this->id);
1361 $sql .= " ORDER by d.rang";
1362
1363 $result = $this->db->query($sql);
1364 if ($result) {
1365 $num = $this->db->num_rows($result);
1366 $i = 0;
1367
1368 while ($i < $num) {
1369 $objp = $this->db->fetch_object($result);
1370
1371 $line = new SupplierProposalLine($this->db);
1372
1373 $line->rowid = $objp->rowid; // deprecated
1374 $line->id = $objp->rowid;
1375 $line->fk_supplier_proposal = $objp->fk_supplier_proposal;
1376 $line->fk_parent_line = $objp->fk_parent_line;
1377 $line->product_type = $objp->product_type;
1378 $line->label = $objp->custom_label;
1379 $line->desc = $objp->description; // Description ligne
1380 $line->qty = $objp->qty;
1381 $line->tva_tx = $objp->tva_tx;
1382 $line->localtax1_tx = $objp->localtax1_tx;
1383 $line->localtax2_tx = $objp->localtax2_tx;
1384 $line->subprice = $objp->subprice;
1385 $line->fk_remise_except = $objp->fk_remise_except;
1386 $line->remise_percent = $objp->remise_percent;
1387
1388 $line->info_bits = $objp->info_bits;
1389 $line->total_ht = $objp->total_ht;
1390 $line->total_tva = $objp->total_tva;
1391 $line->total_localtax1 = $objp->total_localtax1;
1392 $line->total_localtax2 = $objp->total_localtax2;
1393 $line->total_ttc = $objp->total_ttc;
1394 $line->fk_fournprice = $objp->fk_fournprice;
1395 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1396 $line->pa_ht = $marginInfos[0];
1397 $line->marge_tx = $marginInfos[1];
1398 $line->marque_tx = $marginInfos[2];
1399 $line->special_code = $objp->special_code;
1400 $line->rang = $objp->rang;
1401
1402 $line->fk_product = $objp->fk_product;
1403
1404 $line->ref = $objp->product_ref; // deprecated
1405 $line->product_ref = $objp->product_ref;
1406 $line->libelle = $objp->product_label; // deprecated
1407 $line->product_label = $objp->product_label;
1408 $line->product_desc = $objp->product_desc; // Description produit
1409 $line->fk_product_type = $objp->fk_product_type;
1410
1411 $line->ref_fourn = $objp->ref_produit_fourn;
1412
1413 $line->extraparams = !empty($objp->extraparams) ? (array) json_decode($objp->extraparams, true) : array();
1414
1415 // Multicurrency
1416 $line->fk_multicurrency = $objp->fk_multicurrency;
1417 $line->multicurrency_code = $objp->multicurrency_code;
1418 $line->multicurrency_subprice = $objp->multicurrency_subprice;
1419 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
1420 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
1421 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
1422 $line->fk_unit = $objp->fk_unit;
1423
1424 $line->fetch_optionals();
1425
1426 $this->lines[$i] = $line;
1427
1428 $i++;
1429 }
1430 $this->db->free($result);
1431 } else {
1432 $this->error = $this->db->error();
1433 return -1;
1434 }
1435
1436 return 1;
1437 }
1438
1439 $this->error = "Record Not Found";
1440 return 0;
1441 } else {
1442 $this->error = $this->db->error();
1443 return -1;
1444 }
1445 }
1446
1454 public function valid($user, $notrigger = 0)
1455 {
1456 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1457
1458 global $conf, $langs;
1459
1460 $error = 0;
1461 $now = dol_now();
1462
1463 if ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'creer'))
1464 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'validate_advance'))) {
1465 $this->db->begin();
1466
1467 // Numbering module definition
1468 $soc = new Societe($this->db);
1469 $result = $soc->fetch($this->socid);
1470
1471 if ($result < 0) {
1472 return -1;
1473 }
1474
1475 // Define new ref
1476 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1477 $num = $this->getNextNumRef($soc);
1478 } else {
1479 $num = $this->ref;
1480 }
1481 $this->newref = dol_sanitizeFileName($num);
1482
1483 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1484 $sql .= " SET ref = '".$this->db->escape($num)."',";
1485 $sql .= " fk_statut = 1, date_valid='".$this->db->idate($now)."', fk_user_valid=".((int) $user->id);
1486 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1487
1488 dol_syslog(get_class($this)."::valid", LOG_DEBUG);
1489 $resql = $this->db->query($sql);
1490 if (!$resql) {
1491 dol_print_error($this->db);
1492 $error++;
1493 }
1494
1495 // Trigger calls
1496 if (!$error && !$notrigger) {
1497 // Call trigger
1498 $result = $this->call_trigger('PROPOSAL_SUPPLIER_VALIDATE', $user);
1499 if ($result < 0) {
1500 $error++;
1501 }
1502 // End call triggers
1503 }
1504
1505 if (!$error) {
1506 $this->oldref = $this->ref;
1507
1508 // Rename directory if dir was a temporary ref
1509 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1510 // Now we rename also files into index
1511 $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)."'";
1512 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1513 $resql = $this->db->query($sql);
1514 if (!$resql) {
1515 $error++;
1516 $this->error = $this->db->lasterror();
1517 }
1518 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'supplier_proposal/".$this->db->escape($this->newref)."'";
1519 $sql .= " WHERE filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1520 $resql = $this->db->query($sql);
1521 if (!$resql) {
1522 $error++;
1523 $this->error = $this->db->lasterror();
1524 }
1525
1526 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1527 $oldref = dol_sanitizeFileName($this->ref);
1528 $newref = dol_sanitizeFileName($num);
1529 $dirsource = $conf->supplier_proposal->dir_output.'/'.$oldref;
1530 $dirdest = $conf->supplier_proposal->dir_output.'/'.$newref;
1531 if (!$error && file_exists($dirsource)) {
1532 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
1533 if (@rename($dirsource, $dirdest)) {
1534 dol_syslog("Rename ok");
1535 // Rename docs starting with $oldref with $newref
1536 $listoffiles = dol_dir_list($conf->supplier_proposal->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1537 foreach ($listoffiles as $fileentry) {
1538 $dirsource = $fileentry['name'];
1539 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1540 $dirsource = $fileentry['path'].'/'.$dirsource;
1541 $dirdest = $fileentry['path'].'/'.$dirdest;
1542 @rename($dirsource, $dirdest);
1543 }
1544 }
1545 }
1546 }
1547
1548 $this->ref = $num;
1549 $this->statut = self::STATUS_VALIDATED;
1551 $this->user_validation_id = $user->id;
1552 $this->datev = $now;
1553 $this->date_validation = $now;
1554
1555 if (getDolGlobalString('SUPPLIER_PROPOSAL_AUTOADD_USER_CONTACT')) {
1556 $result = $this->add_contact($user->id, 'SALESREPFOLL', 'internal', 1);
1557 if ($result < 0 && $result != -2) { // -2 means already exists
1558 $error++;
1559 }
1560 }
1561
1562 $this->db->commit();
1563 return 1;
1564 } else {
1565 $this->db->rollback();
1566 return -1;
1567 }
1568 } else {
1569 dol_syslog("You don't have permission to validate supplier proposal", LOG_WARNING);
1570 return -2;
1571 }
1572 }
1573
1574 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1583 public function set_date_livraison($user, $delivery_date)
1584 {
1585 // phpcs:enable
1586 return $this->setDeliveryDate($user, $delivery_date);
1587 }
1588
1596 public function setDeliveryDate($user, $delivery_date)
1597 {
1598 if ($user->hasRight('supplier_proposal', 'creer')) {
1599 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1600 $sql .= " SET date_livraison = ".(isDolTms($delivery_date) ? "'".$this->db->idate($delivery_date)."'" : 'null');
1601 $sql .= " WHERE rowid = ".((int) $this->id);
1602
1603 if ($this->db->query($sql)) {
1604 $this->delivery_date = $delivery_date;
1605 return 1;
1606 } else {
1607 $this->error = $this->db->error();
1608 dol_syslog(get_class($this)."::setDeliveryDate Erreur SQL");
1609 return -1;
1610 }
1611 }
1612 return 0;
1613 }
1614
1615 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1623 /*
1624 public function set_remise_percent($user, $remise)
1625 {
1626 // phpcs:enable
1627 $remise = trim($remise) ?trim($remise) : 0;
1628
1629 if ($user->hasRight('supplier_proposal', 'creer')) {
1630 $remise = price2num($remise, 2);
1631
1632 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal SET remise_percent = ".((float) $remise);
1633 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1634
1635 if ($this->db->query($sql)) {
1636 $this->remise_percent = ((float) $remise);
1637 $this->update_price(1);
1638 return 1;
1639 } else {
1640 $this->error = $this->db->error();
1641 return -1;
1642 }
1643 }
1644 return 0;
1645 }
1646 */
1647
1648 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1656 /*
1657 public function set_remise_absolue($user, $remise)
1658 {
1659 // phpcs:enable
1660 if (empty($remise)) {
1661 $remise = 0;
1662 }
1663
1664 $remise = price2num($remise);
1665
1666 if ($user->hasRight('supplier_proposal', 'creer')) {
1667 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1668 $sql .= " SET remise_absolue = ".((float) $remise);
1669 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1670
1671 if ($this->db->query($sql)) {
1672 $this->remise_absolue = $remise;
1673 $this->update_price(1);
1674 return 1;
1675 } else {
1676 $this->error = $this->db->error();
1677 return -1;
1678 }
1679 }
1680 return 0;
1681 }
1682 */
1683
1684
1694 public function reopen($user, $status, $note = '', $notrigger = 0)
1695 {
1696 $error = 0;
1697
1698 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1699 $sql .= " SET fk_statut = ".((int) $status).",";
1700 if (!empty($note)) {
1701 $sql .= " note_private = '".$this->db->escape($note)."',";
1702 }
1703 $sql .= " date_cloture = NULL, fk_user_cloture = NULL";
1704 $sql .= " WHERE rowid = ".((int) $this->id);
1705
1706 $this->db->begin();
1707
1708 dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
1709 $resql = $this->db->query($sql);
1710 if (!$resql) {
1711 $error++;
1712 $this->errors[] = "Error ".$this->db->lasterror();
1713 }
1714 if (!$error) {
1715 if (!$notrigger) {
1716 // Call trigger
1717 $result = $this->call_trigger('PROPOSAL_SUPPLIER_REOPEN', $user);
1718 if ($result < 0) {
1719 $error++;
1720 }
1721 // End call triggers
1722 }
1723 }
1724
1725 // Commit or rollback
1726 if ($error) {
1727 if (!empty($this->errors)) {
1728 foreach ($this->errors as $errmsg) {
1729 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1730 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1731 }
1732 }
1733 $this->db->rollback();
1734 return -1 * $error;
1735 } else {
1736 $this->statut = $status;
1737 $this->status = $status;
1738
1739 $this->db->commit();
1740 return 1;
1741 }
1742 }
1743
1744
1753 public function cloture($user, $status, $note)
1754 {
1755 global $langs, $conf;
1756 $hidedetails = 0;
1757 $hidedesc = 0;
1758 $hideref = 0;
1759
1760 $error = 0;
1761 $now = dol_now();
1762
1763 $this->db->begin();
1764
1765 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1766 $sql .= " SET fk_statut = ".((int) $status).", note_private = '".$this->db->escape($note)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".((int) $user->id);
1767 $sql .= " WHERE rowid = ".((int) $this->id);
1768
1769 $resql = $this->db->query($sql);
1770 if ($resql) {
1771 $this->statut = $status;
1772 $this->status = $status;
1773
1774 $modelpdf = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED', (empty($this->model_pdf) ? '' : $this->model_pdf));
1775 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_REFUSED';
1776
1777 if ($status == 2) {
1778 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_SIGNED';
1779 $modelpdf = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL', (empty($this->model_pdf) ? '' : $this->model_pdf));
1780
1781 if (getDolGlobalString('SUPPLIER_PROPOSAL_UPDATE_PRICE_ON_SUPPlIER_PROPOSAL')) { // TODO This option was not tested correctly. Error if product ref does not exists
1782 $result = $this->updateOrCreatePriceFournisseur($user);
1783 }
1784 }
1785 if ($status == 4) {
1786 $triggerName = 'PROPOSAL_SUPPLIER_CLASSIFY_BILLED';
1787 }
1788
1789 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1790 // Define output language
1791 $outputlangs = $langs;
1792 if (getDolGlobalInt('MAIN_MULTILANGS')) {
1793 $outputlangs = new Translate("", $conf);
1794 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
1795 $outputlangs->setDefaultLang($newlang);
1796 }
1797
1798 $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1799 }
1800
1801 // Call trigger
1802 $result = $this->call_trigger($triggerName, $user);
1803 if ($result < 0) {
1804 $error++;
1805 }
1806 // End call triggers
1807
1808 if (!$error) {
1809 $this->db->commit();
1810 return 1;
1811 } else {
1812 $this->db->rollback();
1813 return -1;
1814 }
1815 } else {
1816 $this->error = $this->db->lasterror();
1817 $this->errors[] = $this->db->lasterror();
1818 $this->db->rollback();
1819 return -1;
1820 }
1821 }
1822
1829 public function updateOrCreatePriceFournisseur($user)
1830 {
1831 global $conf;
1832
1833 dol_syslog(get_class($this)."::updateOrCreatePriceFournisseur", LOG_DEBUG);
1834 foreach ($this->lines as $product) {
1835 if ($product->subprice <= 0) {
1836 continue;
1837 }
1838 $productsupplier = new ProductFournisseur($this->db);
1839
1840 $multicurrency_tx = 1;
1841 $fk_multicurrency = 0;
1842
1843 if (empty($this->thirdparty)) {
1844 $this->fetch_thirdparty();
1845 }
1846
1847 $ref_fourn = $product->ref_fourn;
1848 if (empty($ref_fourn)) {
1849 $ref_fourn = $product->ref_supplier;
1850 }
1851 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1852 list($fk_multicurrency, $multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $product->multicurrency_code);
1853 }
1854 $productsupplier->id = $product->fk_product;
1855
1856 $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);
1857 }
1858
1859 return 1;
1860 }
1861
1870 public function updatePriceFournisseur($idProductFournPrice, $product, $user)
1871 {
1872 $price = price2num($product->subprice * $product->qty, 'MU');
1873 $unitPrice = price2num($product->subprice, 'MU');
1874
1875 $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);
1876
1877 $resql = $this->db->query($sql);
1878 if (!$resql) {
1879 $this->error = $this->db->error();
1880 $this->db->rollback();
1881 return -1;
1882 }
1883 return 1;
1884 }
1885
1893 public function createPriceFournisseur($product, $user)
1894 {
1895 global $conf;
1896
1897 $price = price2num($product->subprice * $product->qty, 'MU');
1898 $qty = price2num($product->qty);
1899 $unitPrice = price2num($product->subprice, 'MU');
1900
1901 $now = dol_now();
1902
1903 $values = array(
1904 "'".$this->db->idate($now)."'",
1905 (int) $product->fk_product,
1906 (int) $this->thirdparty->id,
1907 "'".$this->db->escape($product->ref_fourn)."'",
1908 (float) $price,
1909 (float) $qty,
1910 (float) $unitPrice,
1911 (float) $product->tva_tx,
1912 (int) $user->id
1913 );
1914 if (isModEnabled("multicurrency")) {
1915 if (!empty($product->multicurrency_code)) {
1916 include_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
1917 $multicurrency = new MultiCurrency($this->db); //need to fetch because empty fk_multicurrency and rate
1918 $multicurrency->fetch(0, $product->multicurrency_code);
1919 if (!empty($multicurrency->id)) {
1920 $values[] = (int) $multicurrency->id;
1921 $values[] = "'".$this->db->escape($product->multicurrency_code)."'";
1922 $values[] = (float) $product->multicurrency_subprice;
1923 $values[] = (float) $product->multicurrency_total_ht;
1924 $values[] = (float) $multicurrency->rate->rate;
1925 } else {
1926 for ($i = 0; $i < 5; $i++) {
1927 $values[] = 'NULL';
1928 }
1929 }
1930 }
1931 }
1932
1933 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'product_fournisseur_price ';
1934 $sql .= '(datec, fk_product, fk_soc, ref_fourn, price, quantity, unitprice, tva_tx, fk_user';
1935 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1936 $sql .= ',fk_multicurrency, multicurrency_code, multicurrency_unitprice, multicurrency_price, multicurrency_tx';
1937 }
1938 $sql .= ') VALUES ('.implode(',', $values).')';
1939
1940 $resql = $this->db->query($sql);
1941 if (!$resql) {
1942 $this->error = $this->db->error();
1943 $this->db->rollback();
1944 return -1;
1945 }
1946 return 1;
1947 }
1948
1949 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1956 public function setDraft($user)
1957 {
1958 // phpcs:enable
1959 global $conf, $langs;
1960
1961 $error = 0;
1962
1963 if ($this->status == self::STATUS_DRAFT) {
1964 dol_syslog(get_class($this)."::setDraft already draft status", LOG_WARNING);
1965 return 0;
1966 }
1967
1968 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1969 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1970 $sql .= " WHERE rowid = ".((int) $this->id);
1971
1972 if ($this->db->query($sql)) {
1973 if (!$error) {
1974 $this->oldcopy = clone $this;
1975 }
1976
1977 if (!$error) {
1978 // Call trigger
1979 $result = $this->call_trigger('PROPOSAL_SUPPLIER_UNVALIDATE', $user);
1980 if ($result < 0) {
1981 $error++;
1982 }
1983 }
1984
1985 if (!$error) {
1986 $this->status = self::STATUS_DRAFT;
1987 $this->statut = self::STATUS_DRAFT; // deprecated
1988 $this->db->commit();
1989 return 1;
1990 } else {
1991 $this->db->rollback();
1992 return -1;
1993 }
1994 } else {
1995 return -1;
1996 }
1997 }
1998
1999
2000 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2014 public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datec', $sortorder = 'DESC')
2015 {
2016 // phpcs:enable
2017 global $user;
2018
2019 $ga = array();
2020
2021 $search_sale = 0;
2022 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2023 $search_sale = $user->id;
2024 }
2025
2026 $sql = "SELECT s.rowid, s.nom as name, s.client,";
2027 $sql .= " p.rowid as supplier_proposalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
2028 $sql .= " p.datep as dp, p.fin_validite as datelimite";
2029 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."supplier_proposal as p, ".MAIN_DB_PREFIX."c_propalst as c";
2030 $sql .= " WHERE p.entity IN (".getEntity('supplier_proposal').")";
2031 $sql .= " AND p.fk_soc = s.rowid";
2032 $sql .= " AND p.fk_statut = c.id";
2033 if ($socid) {
2034 $sql .= " AND s.rowid = ".((int) $socid);
2035 }
2036 if ($draft) {
2037 $sql .= " AND p.fk_statut = 0";
2038 }
2039 if ($notcurrentuser > 0) {
2040 $sql .= " AND p.fk_user_author <> ".((int) $user->id);
2041 }
2042 // Search on sale representative
2043 if ($search_sale && $search_sale != '-1') {
2044 if ($search_sale == -2) {
2045 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
2046 } elseif ($search_sale > 0) {
2047 $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).")";
2048 }
2049 }
2050 $sql .= $this->db->order($sortfield, $sortorder);
2051 $sql .= $this->db->plimit($limit, $offset);
2052
2053 $result = $this->db->query($sql);
2054 if ($result) {
2055 $num = $this->db->num_rows($result);
2056 if ($num) {
2057 $i = 0;
2058 while ($i < $num) {
2059 $obj = $this->db->fetch_object($result);
2060
2061 if ($shortlist == 1) {
2062 $ga[$obj->supplier_proposalid] = $obj->ref;
2063 } elseif ($shortlist == 2) {
2064 $ga[$obj->supplier_proposalid] = $obj->ref.' ('.$obj->name.')';
2065 } else {
2066 $ga[$i]['id'] = $obj->supplier_proposalid;
2067 $ga[$i]['ref'] = $obj->ref;
2068 $ga[$i]['name'] = $obj->name;
2069 }
2070
2071 $i++;
2072 }
2073 }
2074 return $ga;
2075 } else {
2076 dol_print_error($this->db);
2077 return -1;
2078 }
2079 }
2080
2088 public function delete($user, $notrigger = 0)
2089 {
2090 global $conf, $langs;
2091 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2092
2093 $error = 0;
2094
2095 $this->db->begin();
2096
2097 if (!$notrigger) {
2098 // Call trigger
2099 $result = $this->call_trigger('PROPOSAL_SUPPLIER_DELETE', $user);
2100 if ($result < 0) {
2101 $error++;
2102 }
2103 // End call triggers
2104 }
2105
2106 if (!$error) {
2107 $main = MAIN_DB_PREFIX.'supplier_proposaldet';
2108 $ef = $main."_extrafields";
2109 $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_supplier_proposal = ".((int) $this->id).")";
2110 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet WHERE fk_supplier_proposal = ".((int) $this->id);
2111 if ($this->db->query($sql)) {
2112 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposal WHERE rowid = ".((int) $this->id);
2113 if ($this->db->query($sqlef) && $this->db->query($sql)) {
2114 // Delete linked object
2115 $res = $this->deleteObjectLinked();
2116 if ($res < 0) {
2117 $error++;
2118 }
2119
2120 if (!$error) {
2121 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2122 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
2123 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
2124
2125 // We remove directory
2126 $ref = dol_sanitizeFileName($this->ref);
2127 if ($conf->supplier_proposal->dir_output && !empty($this->ref)) {
2128 $dir = $conf->supplier_proposal->dir_output."/".$ref;
2129 $file = $dir."/".$ref.".pdf";
2130 if (file_exists($file)) {
2131 dol_delete_preview($this);
2132
2133 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2134 $this->error = 'ErrorFailToDeleteFile';
2135 $this->errors = array('ErrorFailToDeleteFile');
2136 $this->db->rollback();
2137 return 0;
2138 }
2139 }
2140 if (file_exists($dir)) {
2141 $res = @dol_delete_dir_recursive($dir);
2142 if (!$res) {
2143 $this->error = 'ErrorFailToDeleteDir';
2144 $this->errors = array('ErrorFailToDeleteDir');
2145 $this->db->rollback();
2146 return 0;
2147 }
2148 }
2149 }
2150 }
2151
2152 // Removed extrafields
2153 if (!$error) {
2154 $result = $this->deleteExtraFields();
2155 if ($result < 0) {
2156 $error++;
2157 $errorflag = -4;
2158 dol_syslog(get_class($this)."::delete erreur ".$errorflag." ".$this->error, LOG_ERR);
2159 }
2160 }
2161
2162 if (!$error) {
2163 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
2164 $this->db->commit();
2165 return 1;
2166 } else {
2167 $this->error = $this->db->lasterror();
2168 $this->db->rollback();
2169 return 0;
2170 }
2171 } else {
2172 $this->error = $this->db->lasterror();
2173 $this->db->rollback();
2174 return -3;
2175 }
2176 } else {
2177 $this->error = $this->db->lasterror();
2178 $this->db->rollback();
2179 return -2;
2180 }
2181 } else {
2182 $this->db->rollback();
2183 return -1;
2184 }
2185 }
2186
2193 public function info($id)
2194 {
2195 $sql = "SELECT c.rowid, ";
2196 $sql .= " c.datec as date_creation, c.date_valid as date_validation, c.date_cloture as date_closure,";
2197 $sql .= " c.fk_user_author, c.fk_user_valid, c.fk_user_cloture";
2198 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as c";
2199 $sql .= " WHERE c.rowid = ".((int) $id);
2200
2201 $result = $this->db->query($sql);
2202
2203 if ($result) {
2204 if ($this->db->num_rows($result)) {
2205 $obj = $this->db->fetch_object($result);
2206
2207 $this->id = $obj->rowid;
2208
2209 $this->date_creation = $this->db->jdate($obj->date_creation);
2210 $this->date_validation = $this->db->jdate($obj->date_validation);
2211 $this->date_cloture = $this->db->jdate($obj->date_closure);
2212
2213 $this->user_creation_id = $obj->fk_user_author;
2214 $this->user_validation_id = $obj->fk_user_valid;
2215 $this->user_closing_id = $obj->fk_user_cloture;
2216 }
2217 $this->db->free($result);
2218 } else {
2219 dol_print_error($this->db);
2220 }
2221 }
2222
2223
2230 public function getLibStatut($mode = 0)
2231 {
2232 return $this->LibStatut((isset($this->status) ? $this->status : $this->statut), $mode);
2233 }
2234
2235 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2243 public function LibStatut($status, $mode = 1)
2244 {
2245 // phpcs:enable
2246
2247 // Init/load array of translation of status
2248 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
2249 global $langs;
2250 $langs->load("supplier_proposal");
2251 $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraft");
2252 $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidated");
2253 $this->labelStatus[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSigned");
2254 $this->labelStatus[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSigned");
2255 $this->labelStatus[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosed");
2256 $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraftShort");
2257 $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidatedShort");
2258 $this->labelStatusShort[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSignedShort");
2259 $this->labelStatusShort[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSignedShort");
2260 $this->labelStatusShort[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosedShort");
2261 }
2262
2263 $statusnew = '';
2264 if ($status == self::STATUS_DRAFT) {
2265 $statusnew = 'status0';
2266 } elseif ($status == self::STATUS_VALIDATED) {
2267 $statusnew = 'status1';
2268 } elseif ($status == self::STATUS_SIGNED) {
2269 $statusnew = 'status4';
2270 } elseif ($status == self::STATUS_NOTSIGNED) {
2271 $statusnew = 'status9';
2272 } elseif ($status == self::STATUS_CLOSE) {
2273 $statusnew = 'status6';
2274 }
2275
2276 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusnew, $mode);
2277 }
2278
2279
2280 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2288 public function load_board($user, $mode)
2289 {
2290 // phpcs:enable
2291 global $conf, $langs;
2292
2293 $now = dol_now();
2294
2295 $clause = " WHERE";
2296
2297 $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.date_cloture as datefin";
2298 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2299 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2300 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
2301 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2302 $clause = " AND";
2303 }
2304 $sql .= $clause." p.entity IN (".getEntity('supplier_proposal').")";
2305 if ($mode == 'opened') {
2306 $sql .= " AND p.fk_statut = 1";
2307 }
2308 if ($mode == 'signed') {
2309 $sql .= " AND p.fk_statut = 2";
2310 }
2311 if ($user->socid) {
2312 $sql .= " AND p.fk_soc = ".((int) $user->socid);
2313 }
2314
2315 $resql = $this->db->query($sql);
2316 if ($resql) {
2317 $label = $labelShort = '';
2318 $status = '';
2319 $delay_warning = 0;
2320 if ($mode == 'opened') {
2321 $delay_warning = !empty($conf->supplier_proposal->cloture->warning_delay) ? $conf->supplier_proposal->cloture->warning_delay : 0;
2322 $status = self::STATUS_VALIDATED;
2323 $label = $langs->trans("SupplierProposalsToClose");
2324 $labelShort = $langs->trans("ToAcceptRefuse");
2325 }
2326 if ($mode == 'signed') {
2327 $delay_warning = !empty($conf->supplier_proposal->facturation->warning_delay) ? $conf->supplier_proposal->facturation->warning_delay : 0;
2328 $status = self::STATUS_SIGNED;
2329 $label = $langs->trans("SupplierProposalsToProcess"); // May be billed or ordered
2330 $labelShort = $langs->trans("ToClose");
2331 }
2332
2333 $response = new WorkboardResponse();
2334 $response->warning_delay = $delay_warning / 60 / 60 / 24;
2335 $response->label = $label;
2336 $response->labelShort = $labelShort;
2337 $response->url = DOL_URL_ROOT.'/supplier_proposal/list.php?search_status='.$status;
2338 $response->img = img_object('', "propal");
2339
2340 // This assignment in condition is not a bug. It allows walking the results.
2341 while ($obj = $this->db->fetch_object($resql)) {
2342 $response->nbtodo++;
2343 if ($mode == 'opened') {
2344 $datelimit = $this->db->jdate($obj->datefin);
2345 if ($datelimit < ($now - $delay_warning)) {
2346 $response->nbtodolate++;
2347 }
2348 }
2349 // TODO Definir regle des propales a facturer en retard
2350 // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
2351 }
2352 return $response;
2353 } else {
2354 $this->error = $this->db->lasterror();
2355 return -1;
2356 }
2357 }
2358
2359
2367 public function initAsSpecimen()
2368 {
2369 global $user, $langs, $conf;
2370
2371 // Load array of products prodids
2372 $num_prods = 0;
2373 $prodids = array();
2374 $sql = "SELECT rowid";
2375 $sql .= " FROM ".MAIN_DB_PREFIX."product";
2376 $sql .= " WHERE entity IN (".getEntity('product').")";
2377 $sql .= $this->db->plimit(100);
2378
2379 $resql = $this->db->query($sql);
2380 if ($resql) {
2381 $num_prods = $this->db->num_rows($resql);
2382 $i = 0;
2383 while ($i < $num_prods) {
2384 $i++;
2385 $row = $this->db->fetch_row($resql);
2386 $prodids[$i] = $row[0];
2387 }
2388 }
2389
2390 // Initialise parameters
2391 $this->id = 0;
2392 $this->ref = 'SPECIMEN';
2393 $this->specimen = 1;
2394 $this->socid = 1;
2395 $this->date = time();
2396 $this->cond_reglement_id = 1;
2397 $this->cond_reglement_code = 'RECEP';
2398 $this->mode_reglement_id = 7;
2399 $this->mode_reglement_code = 'CHQ';
2400 $this->note_public = 'This is a comment (public)';
2401 $this->note_private = 'This is a comment (private)';
2402 // Lines
2403 $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)
2404 $xnbp = 0;
2405 while ($xnbp < $nbp) {
2406 $line = new SupplierProposalLine($this->db);
2407 $line->desc = $langs->trans("Description")." ".$xnbp;
2408 $line->qty = 1;
2409 $line->subprice = 100;
2410 $line->tva_tx = 19.6;
2411 $line->localtax1_tx = 0;
2412 $line->localtax2_tx = 0;
2413 if ($xnbp == 2) {
2414 $line->total_ht = 50;
2415 $line->total_ttc = 59.8;
2416 $line->total_tva = 9.8;
2417 $line->remise_percent = 50;
2418 } else {
2419 $line->total_ht = 100;
2420 $line->total_ttc = 119.6;
2421 $line->total_tva = 19.6;
2422 $line->remise_percent = 00;
2423 }
2424
2425 if ($num_prods > 0) {
2426 $prodid = mt_rand(1, $num_prods);
2427 $line->fk_product = $prodids[$prodid];
2428 }
2429
2430 $this->lines[$xnbp] = $line;
2431
2432 $this->total_ht += $line->total_ht;
2433 $this->total_tva += $line->total_tva;
2434 $this->total_ttc += $line->total_ttc;
2435
2436 $xnbp++;
2437 }
2438
2439 return 1;
2440 }
2441
2447 public function loadStateBoard()
2448 {
2449 global $conf, $user;
2450
2451 $this->nb = array();
2452 $clause = "WHERE";
2453
2454 $sql = "SELECT count(p.rowid) as nb";
2455 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2456 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
2457 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2458 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2459 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2460 $clause = "AND";
2461 }
2462 $sql .= " ".$clause." p.entity IN (".getEntity('supplier_proposal').")";
2463
2464 $resql = $this->db->query($sql);
2465 if ($resql) {
2466 // This assignment in condition is not a bug. It allows walking the results.
2467 while ($obj = $this->db->fetch_object($resql)) {
2468 $this->nb["supplier_proposals"] = $obj->nb;
2469 }
2470 $this->db->free($resql);
2471 return 1;
2472 } else {
2473 dol_print_error($this->db);
2474 $this->error = $this->db->lasterror();
2475 return -1;
2476 }
2477 }
2478
2479
2487 public function getNextNumRef($soc)
2488 {
2489 global $conf, $db, $langs;
2490 $langs->load("supplier_proposal");
2491
2492 if (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON')) {
2493 $mybool = false;
2494
2495 $file = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') . ".php";
2496 $classname = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
2497
2498 // Include file with class
2499 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2500 foreach ($dirmodels as $reldir) {
2501 $dir = dol_buildpath($reldir."core/modules/supplier_proposal/");
2502
2503 // Load file with numbering class (if found)
2504 $mybool = ((bool) @include_once $dir.$file) || $mybool;
2505 }
2506
2507 if (!$mybool) {
2508 dol_print_error(null, "Failed to include file ".$file);
2509 return '';
2510 }
2511
2512 $obj = new $classname();
2513 '@phan-var-force ModeleNumRefSupplierProposal $obj';
2514 $numref = "";
2515 $numref = $obj->getNextValue($soc, $this);
2516
2517 if ($numref != "") {
2518 return $numref;
2519 } else {
2520 $this->error = $obj->error;
2521 return "";
2522 }
2523 } else {
2524 $langs->load("errors");
2525 print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("SupplierProposal"));
2526 return "";
2527 }
2528 }
2529
2536 public function getTooltipContentArray($params)
2537 {
2538 global $conf, $langs, $menumanager;
2539
2540 $langs->load('supplier_proposal');
2541
2542 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2543 return ['optimize' => $langs->trans("ShowSupplierProposal")];
2544 }
2545
2546 $option = $params['option'] ?? '';
2547 $datas = [];
2548
2549 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("SupplierProposal").'</u>';
2550 if (isset($this->status)) {
2551 $datas['picto'] .= ' '.$this->getLibStatut(5);
2552 }
2553 if (!empty($this->ref)) {
2554 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2555 }
2556 if (!empty($this->ref_fourn)) {
2557 $datas['ref_supplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_fourn;
2558 }
2559 if (!empty($this->total_ht)) {
2560 $datas['amount_ht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2561 }
2562 if (!empty($this->total_tva)) {
2563 $datas['amount_vat'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2564 }
2565 if (!empty($this->total_ttc)) {
2566 $datas['amount_ttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2567 }
2568
2569 return $datas;
2570 }
2571
2583 public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2584 {
2585 global $langs, $conf, $user, $hookmanager;
2586
2587 if (!empty($conf->dol_no_mouse_hover)) {
2588 $notooltip = 1; // Force disable tooltips
2589 }
2590
2591 $url = '';
2592 $result = '';
2593 $params = [
2594 'id' => $this->id,
2595 'objecttype' => $this->element,
2596 'option' => $option,
2597 ];
2598 $classfortooltip = 'classfortooltip';
2599 $dataparams = '';
2600 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2601 $classfortooltip = 'classforajaxtooltip';
2602 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
2603 $label = '';
2604 } else {
2605 $label = implode($this->getTooltipContentArray($params));
2606 }
2607
2608 if ($option == '') {
2609 $url = DOL_URL_ROOT.'/supplier_proposal/card.php?id='.$this->id.$get_params;
2610 }
2611 if ($option == 'document') {
2612 $url = DOL_URL_ROOT.'/supplier_proposal/document.php?id='.$this->id.$get_params;
2613 }
2614
2615 if ($option !== 'nolink') {
2616 // Add param to save lastsearch_values or not
2617 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2618 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2619 $add_save_lastsearch_values = 1;
2620 }
2621 if ($add_save_lastsearch_values) {
2622 $url .= '&save_lastsearch_values=1';
2623 }
2624 }
2625
2626 $linkclose = '';
2627 if (empty($notooltip) && $user->hasRight('propal', 'lire')) {
2628 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2629 $label = $langs->trans("ShowSupplierProposal");
2630 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
2631 }
2632 $linkclose .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
2633 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
2634 }
2635
2636 $linkstart = '<a href="'.$url.'"';
2637 $linkstart .= $linkclose.'>';
2638 $linkend = '</a>';
2639
2640 $result .= $linkstart;
2641 if ($withpicto) {
2642 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
2643 }
2644 if ($withpicto != 2) {
2645 $result .= $this->ref;
2646 }
2647 $result .= $linkend;
2648
2649 if ($addlinktonotes) {
2650 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2651 if ($txttoshow) {
2652 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2653 $result .= ' <span class="note inline-block">';
2654 $result .= '<a href="'.DOL_URL_ROOT.'/supplier_proposal/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2655 $result .= img_picto('', 'note');
2656 $result .= '</a>';
2657 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
2658 //$result.='</a>';
2659 $result .= '</span>';
2660 }
2661 }
2662 global $action;
2663 $hookmanager->initHooks(array($this->element . 'dao'));
2664 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
2665 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2666 if ($reshook > 0) {
2667 $result = $hookmanager->resPrint;
2668 } else {
2669 $result .= $hookmanager->resPrint;
2670 }
2671 return $result;
2672 }
2673
2679 public function getLinesArray()
2680 {
2681 // For other object, here we call fetch_lines. But fetch_lines does not exists on supplier proposal
2682
2683 $sql = 'SELECT pt.rowid, pt.label as custom_label, pt.description, pt.fk_product, pt.fk_remise_except,';
2684 $sql .= ' pt.qty, pt.tva_tx, pt.vat_src_code, pt.remise_percent, pt.subprice, pt.info_bits,';
2685 $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,';
2686 $sql .= ' pt.product_type, pt.rang, pt.fk_parent_line, pt.extraparams,';
2687 $sql .= ' p.label as product_label, p.ref, p.fk_product_type, p.rowid as prodid,';
2688 $sql .= ' p.description as product_desc, pt.ref_fourn as ref_supplier,';
2689 $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';
2690 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pt';
2691 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pt.fk_product=p.rowid';
2692 $sql .= ' WHERE pt.fk_supplier_proposal = '.((int) $this->id);
2693 $sql .= ' ORDER BY pt.rang ASC, pt.rowid';
2694
2695 dol_syslog(get_class($this).'::getLinesArray', LOG_DEBUG);
2696 $resql = $this->db->query($sql);
2697 if ($resql) {
2698 $num = $this->db->num_rows($resql);
2699 $i = 0;
2700
2701 while ($i < $num) {
2702 $obj = $this->db->fetch_object($resql);
2703
2704 $this->lines[$i] = new SupplierProposalLine($this->db);
2705 $this->lines[$i]->id = $obj->rowid; // for backward compatibility
2706 $this->lines[$i]->rowid = $obj->rowid;
2707 $this->lines[$i]->fetch_optionals();
2708 $this->lines[$i]->label = $obj->custom_label;
2709 $this->lines[$i]->description = $obj->description;
2710 $this->lines[$i]->fk_product = $obj->fk_product;
2711 $this->lines[$i]->ref = $obj->ref;
2712 $this->lines[$i]->product_label = $obj->product_label;
2713 $this->lines[$i]->product_desc = $obj->product_desc;
2714 $this->lines[$i]->fk_product_type = $obj->fk_product_type; // deprecated
2715 $this->lines[$i]->product_type = $obj->product_type;
2716 $this->lines[$i]->qty = $obj->qty;
2717 $this->lines[$i]->subprice = $obj->subprice;
2718 $this->lines[$i]->fk_remise_except = $obj->fk_remise_except;
2719 $this->lines[$i]->remise_percent = $obj->remise_percent;
2720 $this->lines[$i]->tva_tx = $obj->tva_tx;
2721 $this->lines[$i]->vat_src_code = $obj->vat_src_code;
2722 $this->lines[$i]->info_bits = $obj->info_bits;
2723 $this->lines[$i]->total_ht = $obj->total_ht;
2724 $this->lines[$i]->total_tva = $obj->total_tva;
2725 $this->lines[$i]->total_ttc = $obj->total_ttc;
2726 $this->lines[$i]->fk_fournprice = $obj->fk_fournprice;
2727 $marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->lines[$i]->fk_fournprice, $obj->pa_ht);
2728 $this->lines[$i]->pa_ht = $marginInfos[0];
2729 $this->lines[$i]->marge_tx = $marginInfos[1];
2730 $this->lines[$i]->marque_tx = $marginInfos[2];
2731 $this->lines[$i]->fk_parent_line = $obj->fk_parent_line;
2732 $this->lines[$i]->special_code = $obj->special_code;
2733 $this->lines[$i]->rang = $obj->rang;
2734
2735 $this->lines[$i]->ref_fourn = $obj->ref_supplier; // deprecated
2736 $this->lines[$i]->ref_supplier = $obj->ref_supplier;
2737
2738 $this->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
2739
2740 // Multicurrency
2741 $this->lines[$i]->fk_multicurrency = $obj->fk_multicurrency;
2742 $this->lines[$i]->multicurrency_code = $obj->multicurrency_code;
2743 $this->lines[$i]->multicurrency_subprice = $obj->multicurrency_subprice;
2744 $this->lines[$i]->multicurrency_total_ht = $obj->multicurrency_total_ht;
2745 $this->lines[$i]->multicurrency_total_tva = $obj->multicurrency_total_tva;
2746 $this->lines[$i]->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
2747 $this->lines[$i]->fk_unit = $obj->fk_unit;
2748
2749 $i++;
2750 }
2751 $this->db->free($resql);
2752
2753 return 1;
2754 } else {
2755 $this->error = $this->db->error();
2756 return -1;
2757 }
2758 }
2759
2771 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2772 {
2773 global $langs;
2774
2775 $langs->load("supplier_proposal");
2776 $outputlangs->load("products");
2777
2778 if (!dol_strlen($modele)) {
2779 $modele = ''; // On supplier documents, template can be empty( no doc generated in this case)
2780
2781 if ($this->model_pdf) {
2782 $modele = $this->model_pdf;
2783 } elseif (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF')) {
2784 $modele = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF');
2785 }
2786 }
2787
2788 if (empty($modele)) {
2789 return 1;
2790 }
2791
2792 $modelpath = "core/modules/supplier_proposal/doc/";
2793
2794 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2795 }
2796
2797
2806 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2807 {
2808 $tables = array(
2809 'supplier_proposal'
2810 );
2811
2812 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2813 }
2814
2823 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
2824 {
2825 $tables = array(
2826 'supplier_proposaldet'
2827 );
2828
2829 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
2830 }
2831
2832
2840 public function getKanbanView($option = '', $arraydata = null)
2841 {
2842 global $langs;
2843
2844 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2845
2846 $return = '<div class="box-flex-item box-flex-grow-zero">';
2847 $return .= '<div class="info-box info-box-sm">';
2848 $return .= '<span class="info-box-icon bg-infobox-action">';
2849 $return .= img_picto('', $this->picto);
2850 //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2851 $return .= '</span>';
2852 $return .= '<div class="info-box-content">';
2853 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
2854 if ($selected >= 0) {
2855 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2856 }
2857 if (property_exists($this, 'socid')) {
2858 $return .= '<span class="info-box-ref"> | '.$this->socid.'</span>';
2859 }
2860 if (property_exists($this, 'delivery_date')) {
2861 $return .= '<br><span class="opacitymedium">'.$langs->trans("DateEnd").'</span> : <span class="info-box-label">'.dol_print_date($this->delivery_date).'</span>';
2862 }
2863 if (property_exists($this, 'total_ttc')) {
2864 $return .= '<br><span class="opacitymedium" >'.$langs->trans("AmountHT").' : </span><span class="info-box-label amount">'.price($this->total_ttc).'</span>';
2865 }
2866 if (method_exists($this, 'getLibStatut')) {
2867 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
2868 }
2869 $return .= '</div>';
2870 $return .= '</div>';
2871 $return .= '</div>';
2872 return $return;
2873 }
2874}
2875
2876
2881{
2885 public $db;
2886
2890 public $error = '';
2891
2895 public $element = 'supplier_proposaldet';
2896
2900 public $table_element = 'supplier_proposaldet';
2901
2905 public $parent_element = 'supplier_proposal';
2906
2910 public $fk_parent_attribute = 'fk_supplier_proposal';
2911
2915 public $oldline;
2916
2920 public $id;
2921
2925 public $fk_supplier_proposal;
2926
2930 public $fk_parent_line;
2931
2935 public $desc; // Description ligne
2936
2940 public $fk_product; // Id produit predefini
2941
2947 public $fk_product_type;
2953 public $product_type = Product::TYPE_PRODUCT;
2954
2958 public $qty;
2962 public $tva_tx;
2966 public $vat_src_code;
2967
2972 public $subprice;
2976 public $remise_percent;
2977
2981 public $fk_remise_except;
2982
2986 public $rang = 0;
2987
2991 public $fk_fournprice;
2992
2996 public $pa_ht;
3000 public $marge_tx;
3004 public $marque_tx;
3005
3009 public $special_code; // Tag for special lines (exclusive tags)
3010 // 1: frais de port
3011 // 2: ecotaxe
3012 // 3: option line (when qty = 0)
3013
3017 public $info_bits = 0; // Liste d'options cumulables:
3018 // Bit 0: 0 si TVA normal - 1 if TVA NPR
3019 // Bit 1: 0 ligne normal - 1 if fixed reduction
3020
3024 public $total_ht; // Total HT de la ligne toute quantite et incluant la remise ligne
3028 public $total_tva; // Total TVA de la ligne toute quantite et incluant la remise ligne
3032 public $total_ttc; // Total TTC de la ligne toute quantite et incluant la remise ligne
3033
3037 public $date_start;
3041 public $date_end;
3042
3043 // From llx_product
3049 public $ref;
3050
3055 public $product_ref;
3056
3062 public $libelle;
3063
3068 public $product_label;
3069
3074 public $label;
3075
3080 public $product_desc;
3081
3085 public $localtax1_tx; // Local tax 1
3089 public $localtax2_tx; // Local tax 2
3093 public $localtax1_type; // Local tax 1 type
3097 public $localtax2_type; // Local tax 2 type
3101 public $total_localtax1; // Line total local tax 1
3105 public $total_localtax2; // Line total local tax 2
3106
3110 public $skip_update_total; // Skip update price total for special lines
3111
3115 public $ref_fourn;
3119 public $ref_supplier;
3120
3121 // Multicurrency
3125 public $fk_multicurrency;
3126
3130 public $multicurrency_code;
3134 public $multicurrency_subprice;
3138 public $multicurrency_total_ht;
3142 public $multicurrency_total_tva;
3146 public $multicurrency_total_ttc;
3147
3153 public function __construct($db)
3154 {
3155 $this->db = $db;
3156 }
3157
3164 public function fetch($rowid)
3165 {
3166 $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,';
3167 $sql .= ' pd.date_start, pd.date_end,';
3168 $sql .= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice,';
3169 $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,';
3170 $sql .= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
3171 $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
3172 $sql .= ' pd.product_type, pd.ref_fourn as ref_produit_fourn, pd.extraparams,';
3173 $sql .= ' pd.fk_multicurrency, pd.multicurrency_code, pd.multicurrency_subprice, pd.multicurrency_total_ht, pd.multicurrency_total_tva, pd.multicurrency_total_ttc, pd.fk_unit';
3174 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pd';
3175 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
3176 $sql .= ' WHERE pd.rowid = '.((int) $rowid);
3177
3178 $result = $this->db->query($sql);
3179 if ($result) {
3180 if ($objp = $this->db->fetch_object($result)) {
3181 $this->id = $objp->rowid;
3182 $this->fk_supplier_proposal = $objp->fk_supplier_proposal;
3183 $this->fk_parent_line = $objp->fk_parent_line;
3184 $this->label = $objp->custom_label;
3185 $this->desc = $objp->description;
3186 $this->qty = $objp->qty;
3187 $this->subprice = $objp->subprice;
3188 $this->tva_tx = $objp->tva_tx;
3189 $this->remise_percent = $objp->remise_percent;
3190 $this->fk_remise_except = $objp->fk_remise_except;
3191 $this->fk_product = $objp->fk_product;
3192 $this->info_bits = $objp->info_bits;
3193 $this->date_start = $this->db->jdate($objp->date_start);
3194 $this->date_end = $this->db->jdate($objp->date_end);
3195
3196 $this->total_ht = $objp->total_ht;
3197 $this->total_tva = $objp->total_tva;
3198 $this->total_ttc = $objp->total_ttc;
3199
3200 $this->fk_fournprice = $objp->fk_fournprice;
3201
3202 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
3203 $this->pa_ht = $marginInfos[0];
3204 $this->marge_tx = $marginInfos[1];
3205 $this->marque_tx = $marginInfos[2];
3206
3207 $this->special_code = $objp->special_code;
3208 $this->product_type = $objp->product_type;
3209 $this->rang = $objp->rang;
3210
3211 $this->ref = $objp->product_ref; // deprecated
3212 $this->product_ref = $objp->product_ref;
3213 $this->libelle = $objp->product_label; // deprecated
3214 $this->product_label = $objp->product_label;
3215 $this->product_desc = $objp->product_desc;
3216
3217 $this->ref_fourn = $objp->ref_produit_fourn;
3218
3219 $this->extraparams = !empty($objp->extraparams) ? (array) json_decode($objp->extraparams, true) : array();
3220
3221 // Multicurrency
3222 $this->fk_multicurrency = $objp->fk_multicurrency;
3223 $this->multicurrency_code = $objp->multicurrency_code;
3224 $this->multicurrency_subprice = $objp->multicurrency_subprice;
3225 $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3226 $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3227 $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3228 $this->fk_unit = $objp->fk_unit;
3229
3230 $this->db->free($result);
3231 return 1;
3232 }
3233 return 0;
3234 } else {
3235 dol_print_error($this->db);
3236 return -1;
3237 }
3238 }
3239
3246 public function insert($notrigger = 0)
3247 {
3248 global $conf, $langs, $user;
3249
3250 $error = 0;
3251
3252 dol_syslog(get_class($this)."::insert rang=".$this->rang);
3253
3254 // Clean parameters
3255 if (empty($this->tva_tx)) {
3256 $this->tva_tx = 0;
3257 }
3258 if (empty($this->vat_src_code)) {
3259 $this->vat_src_code = '';
3260 }
3261 if (empty($this->localtax1_tx)) {
3262 $this->localtax1_tx = 0;
3263 }
3264 if (empty($this->localtax2_tx)) {
3265 $this->localtax2_tx = 0;
3266 }
3267 if (empty($this->localtax1_type)) {
3268 $this->localtax1_type = '';
3269 }
3270 if (empty($this->localtax2_type)) {
3271 $this->localtax2_type = '';
3272 }
3273 if (empty($this->total_localtax1)) {
3274 $this->total_localtax1 = 0;
3275 }
3276 if (empty($this->total_localtax2)) {
3277 $this->total_localtax2 = 0;
3278 }
3279 if (empty($this->rang)) {
3280 $this->rang = 0;
3281 }
3282 if (empty($this->remise_percent)) {
3283 $this->remise_percent = 0;
3284 }
3285 if (empty($this->info_bits)) {
3286 $this->info_bits = 0;
3287 }
3288 if (empty($this->special_code)) {
3289 $this->special_code = 0;
3290 }
3291 if (empty($this->fk_parent_line)) {
3292 $this->fk_parent_line = 0;
3293 }
3294 if (empty($this->fk_fournprice)) {
3295 $this->fk_fournprice = 0;
3296 }
3297 if (empty($this->fk_unit)) {
3298 $this->fk_unit = 0;
3299 }
3300 if (empty($this->subprice)) {
3301 $this->subprice = 0;
3302 }
3303
3304 if (empty($this->pa_ht)) {
3305 $this->pa_ht = 0;
3306 }
3307
3308 // if buy price not defined, define buyprice as configured in margin admin
3309 if ($this->pa_ht == 0) {
3310 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3311 if ($result < 0) {
3312 return $result;
3313 } else {
3314 $this->pa_ht = $result;
3315 }
3316 }
3317
3318 // Check parameters
3319 if ($this->product_type < 0) {
3320 return -1;
3321 }
3322
3323 $this->db->begin();
3324
3325 // Insert line into database
3326 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'supplier_proposaldet';
3327 $sql .= ' (fk_supplier_proposal, fk_parent_line, label, description, fk_product, product_type,';
3328 $sql .= ' date_start, date_end,';
3329 $sql .= ' fk_remise_except, qty, tva_tx, vat_src_code, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3330 $sql .= ' subprice, remise_percent, ';
3331 $sql .= ' info_bits, ';
3332 $sql .= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
3333 $sql .= ' ref_fourn,';
3334 $sql .= ' fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc, fk_unit)';
3335 $sql .= " VALUES (".$this->fk_supplier_proposal.",";
3336 $sql .= " ".($this->fk_parent_line > 0 ? ((int) $this->fk_parent_line) : "null").",";
3337 $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
3338 $sql .= " '".$this->db->escape($this->desc)."',";
3339 $sql .= " ".($this->fk_product ? ((int) $this->fk_product) : "null").",";
3340 $sql .= " '".$this->db->escape((string) $this->product_type)."',";
3341 $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3342 $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3343 $sql .= " ".($this->fk_remise_except ? ((int) $this->fk_remise_except) : "null").",";
3344 $sql .= " ".price2num($this->qty, 'MS').",";
3345 $sql .= " ".price2num($this->tva_tx).",";
3346 $sql .= " '".$this->db->escape($this->vat_src_code)."',";
3347 $sql .= " ".price2num($this->localtax1_tx).",";
3348 $sql .= " ".price2num($this->localtax2_tx).",";
3349 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3350 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3351 $sql .= " ".price2num($this->subprice, 'MU') .",";
3352 $sql .= " ".((float) $this->remise_percent).",";
3353 $sql .= " ".(isset($this->info_bits) ? ((int) $this->info_bits) : "null").",";
3354 $sql .= " ".price2num($this->total_ht, 'MT').",";
3355 $sql .= " ".price2num($this->total_tva, 'MT').",";
3356 $sql .= " ".price2num($this->total_localtax1, 'MT').",";
3357 $sql .= " ".price2num($this->total_localtax2, 'MT').",";
3358 $sql .= " ".price2num($this->total_ttc, 'MT').",";
3359 $sql .= " ".(!empty($this->fk_fournprice) ? ((int) $this->fk_fournprice) : "null").",";
3360 $sql .= " ".(isset($this->pa_ht) ? price2num($this->pa_ht, 'MU') : "null").",";
3361 $sql .= ' '.((int) $this->special_code).',';
3362 $sql .= ' '.((int) $this->rang).',';
3363 $sql .= " '".$this->db->escape($this->ref_fourn)."'";
3364 $sql .= ", ".($this->fk_multicurrency > 0 ? ((int) $this->fk_multicurrency) : 'null');
3365 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3366 $sql .= ", ".price2num($this->multicurrency_subprice, 'CU');
3367 $sql .= ", ".price2num($this->multicurrency_total_ht, 'CT');
3368 $sql .= ", ".price2num($this->multicurrency_total_tva, 'CT');
3369 $sql .= ", ".price2num($this->multicurrency_total_ttc, 'CT');
3370 $sql .= ", ".($this->fk_unit ? ((int) $this->fk_unit) : 'null');
3371 $sql .= ')';
3372
3373 dol_syslog(get_class($this).'::insert', LOG_DEBUG);
3374 $resql = $this->db->query($sql);
3375 if ($resql) {
3376 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'supplier_proposaldet');
3377
3378 if (!$error) {
3379 $result = $this->insertExtraFields();
3380 if ($result < 0) {
3381 $error++;
3382 }
3383 }
3384
3385 if (!$error && !$notrigger) {
3386 // Call trigger
3387 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_INSERT', $user);
3388 if ($result < 0) {
3389 $this->db->rollback();
3390 return -1;
3391 }
3392 // End call triggers
3393 }
3394
3395 $this->db->commit();
3396 return 1;
3397 } else {
3398 $this->error = $this->db->error()." sql=".$sql;
3399 $this->db->rollback();
3400 return -1;
3401 }
3402 }
3403
3410 public function delete($user)
3411 {
3412 $error = 0;
3413
3414 $this->db->begin();
3415
3416 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet";
3417 $sql .= " WHERE rowid = ".((int) $this->id);
3418
3419 if ($this->db->query($sql)) {
3420 // Remove extrafields
3421 if (!$error) {
3422 $result = $this->deleteExtraFields();
3423 if ($result < 0) {
3424 $error++;
3425 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3426 }
3427 }
3428
3429 // Call trigger
3430 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_DELETE', $user);
3431 if ($result < 0) {
3432 $this->db->rollback();
3433 return -1;
3434 }
3435 // End call triggers
3436
3437 $this->db->commit();
3438
3439 return 1;
3440 } else {
3441 $this->error = $this->db->error()." sql=".$sql;
3442 $this->db->rollback();
3443 return -1;
3444 }
3445 }
3446
3453 public function update($notrigger = 0)
3454 {
3455 global $conf, $langs, $user;
3456
3457 $error = 0;
3458
3459 // Clean parameters
3460 if (empty($this->tva_tx)) {
3461 $this->tva_tx = 0;
3462 }
3463 if (empty($this->localtax1_tx)) {
3464 $this->localtax1_tx = 0;
3465 }
3466 if (empty($this->localtax2_tx)) {
3467 $this->localtax2_tx = 0;
3468 }
3469 if (empty($this->total_localtax1)) {
3470 $this->total_localtax1 = 0;
3471 }
3472 if (empty($this->total_localtax2)) {
3473 $this->total_localtax2 = 0;
3474 }
3475 if (empty($this->localtax1_type)) {
3476 $this->localtax1_type = '';
3477 }
3478 if (empty($this->localtax2_type)) {
3479 $this->localtax2_type = '';
3480 }
3481 if (empty($this->marque_tx)) {
3482 $this->marque_tx = 0;
3483 }
3484 if (empty($this->marge_tx)) {
3485 $this->marge_tx = 0;
3486 }
3487 if (empty($this->remise_percent)) {
3488 $this->remise_percent = 0;
3489 }
3490 if (empty($this->info_bits)) {
3491 $this->info_bits = 0;
3492 }
3493 if (empty($this->special_code)) {
3494 $this->special_code = 0;
3495 }
3496 if (empty($this->fk_parent_line)) {
3497 $this->fk_parent_line = 0;
3498 }
3499 if (empty($this->fk_fournprice)) {
3500 $this->fk_fournprice = 0;
3501 }
3502 if (empty($this->fk_unit)) {
3503 $this->fk_unit = 0;
3504 }
3505 if (empty($this->subprice)) {
3506 $this->subprice = 0;
3507 }
3508
3509 if (empty($this->pa_ht)) {
3510 $this->pa_ht = 0;
3511 }
3512
3513 // if buy price not defined, define buyprice as configured in margin admin
3514 if ($this->pa_ht == 0) {
3515 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3516 if ($result < 0) {
3517 return $result;
3518 } else {
3519 $this->pa_ht = $result;
3520 }
3521 }
3522
3523 $this->db->begin();
3524
3525 // Mise a jour ligne en base
3526 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3527 $sql .= " description='".$this->db->escape($this->desc)."'";
3528 $sql .= " , label=".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
3529 $sql .= " , product_type=".((int) $this->product_type);
3530 $sql .= " , date_start=".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null");
3531 $sql .= " , date_end=".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null");
3532 $sql .= " , tva_tx='".price2num($this->tva_tx)."'";
3533 $sql .= " , localtax1_tx=".price2num($this->localtax1_tx);
3534 $sql .= " , localtax2_tx=".price2num($this->localtax2_tx);
3535 $sql .= " , localtax1_type='".$this->db->escape($this->localtax1_type)."'";
3536 $sql .= " , localtax2_type='".$this->db->escape($this->localtax2_type)."'";
3537 $sql .= " , qty='".price2num($this->qty)."'";
3538 $sql .= " , subprice=".price2num($this->subprice);
3539 $sql .= " , remise_percent=".price2num($this->remise_percent);
3540 $sql .= " , info_bits='".$this->db->escape((string) $this->info_bits)."'";
3541 if (empty($this->skip_update_total)) {
3542 $sql .= " , total_ht=".price2num($this->total_ht);
3543 $sql .= " , total_tva=".price2num($this->total_tva);
3544 $sql .= " , total_ttc=".price2num($this->total_ttc);
3545 $sql .= " , total_localtax1=".price2num($this->total_localtax1);
3546 $sql .= " , total_localtax2=".price2num($this->total_localtax2);
3547 }
3548 $sql .= " , fk_product_fournisseur_price=".(!empty($this->fk_fournprice) ? "'".$this->db->escape((string) $this->fk_fournprice)."'" : "null");
3549 $sql .= " , buy_price_ht=".price2num($this->pa_ht);
3550 $sql .= " , special_code=".((int) $this->special_code);
3551 $sql .= " , fk_parent_line=".($this->fk_parent_line > 0 ? $this->fk_parent_line : "null");
3552 if (!empty($this->rang)) {
3553 $sql .= ", rang=".((int) $this->rang);
3554 }
3555 $sql .= " , ref_fourn=".(!empty($this->ref_fourn) ? "'".$this->db->escape($this->ref_fourn)."'" : "null");
3556 $sql .= " , fk_unit=".($this->fk_unit ? $this->fk_unit : 'null');
3557
3558 // Multicurrency
3559 $sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice);
3560 $sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
3561 $sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
3562 $sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
3563
3564 $sql .= " WHERE rowid = ".((int) $this->id);
3565
3566 dol_syslog(get_class($this)."::update", LOG_DEBUG);
3567 $resql = $this->db->query($sql);
3568 if ($resql) {
3569 if (!$error) {
3570 $result = $this->insertExtraFields();
3571 if ($result < 0) {
3572 $error++;
3573 }
3574 }
3575
3576 if (!$error && !$notrigger) {
3577 // Call trigger
3578 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_MODIFY', $user);
3579 if ($result < 0) {
3580 $this->db->rollback();
3581 return -1;
3582 }
3583 // End call triggers
3584 }
3585
3586 $this->db->commit();
3587 return 1;
3588 } else {
3589 $this->error = $this->db->error();
3590 $this->db->rollback();
3591 return -2;
3592 }
3593 }
3594
3595 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3602 public function update_total()
3603 {
3604 // phpcs:enable
3605 $this->db->begin();
3606
3607 // Mise a jour ligne en base
3608 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3609 $sql .= " total_ht=".price2num($this->total_ht, 'MT');
3610 $sql .= ",total_tva=".price2num($this->total_tva, 'MT');
3611 $sql .= ",total_ttc=".price2num($this->total_ttc, 'MT');
3612 $sql .= " WHERE rowid = ".((int) $this->id);
3613
3614 dol_syslog("SupplierProposalLine::update_total", LOG_DEBUG);
3615
3616 $resql = $this->db->query($sql);
3617 if ($resql) {
3618 $this->db->commit();
3619 return 1;
3620 } else {
3621 $this->error = $this->db->error();
3622 $this->db->rollback();
3623 return -2;
3624 }
3625 }
3626}
$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)
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.
call_trigger($triggerName, $user)
Call trigger based on this instance.
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.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template module.
addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1=0, $txlocaltax2=0, $fk_product=0, $remise_percent=0, $price_base_type='HT', $pu_ttc=0, $info_bits=0, $type=0, $rang=-1, $special_code=0, $fk_parent_line=0, $fk_fournprice=0, $pa_ht=0, $label='', $array_options=[], $ref_supplier='', $fk_unit=0, $origin='', $origin_id=0, $pu_ht_devise=0, $date_start=0, $date_end=0)
Add a proposal line into database (linked to product/service or not) Les parameters sont deja cense e...
info($id)
Object SupplierProposal Information.
create($user, $notrigger=0)
Create commercial proposal into database this->ref can be set or empty.
insert_discount($idremise)
Adding line of fixed discount in the proposal in DB.
reopen($user, $status, $note='', $notrigger=0)
Set an overall discount on the proposal.
static replaceProduct(DoliDB $db, $origin_id, $dest_id)
Function used to replace a product id with another one.
getNextNumRef($soc)
Returns the reference to the following non used Proposal used depending on the active numbering modul...
fetch($rowid, $ref='')
Load a proposal from database and its ligne array.
load_board($user, $mode)
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
createFromClone(User $user, $fromid=0)
Load an object from its id and create a new one in database.
initAsSpecimen()
Initialise an instance with random values.
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, ...)
static replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
updateOrCreatePriceFournisseur($user)
Add or update supplier price according to result of proposal.
loadStateBoard()
Load indicator this->nb of global stats widget.
deleteLine($lineid)
Delete detail line.
const STATUS_VALIDATED
Validated status.
createPriceFournisseur($product, $user)
Create Price Fournisseur.
getLibStatut($mode=0)
Return label of status of proposal (draft, validated, ...)
add_product($idproduct, $qty, $remise_percent=0)
Add line into array ->lines.
set_date_livraison($user, $delivery_date)
Set delivery date.
const STATUS_SIGNED
Signed quote.
getNomUrl($withpicto=0, $option='', $get_params='', $notooltip=0, $save_lastsearch_value=-1, $addlinktonotes=0)
Return clickable link of object (with eventually picto)
getKanbanView($option='', $arraydata=null)
Return clickable link of object (with eventually picto)
getTooltipContentArray($params)
getTooltipContentArray
valid($user, $notrigger=0)
Set status to validated.
const STATUS_CLOSE
Billed or closed/processed quote.
getLinesArray()
Retrieve an array of supplier proposal lines.
setDraft($user)
Set draft status.
Class to manage supplier_proposal lines.
fetch($rowid)
Retrieve the propal line object.
insert($notrigger=0)
Insert object line propal in database.
update_total()
Update DB line fields total_xxx Used by migration.
update($notrigger=0)
Update propal line object into DB.
__construct($db)
Class line Constructor.
Class to manage translations.
Class to manage Dolibarr users.
print $langs trans("Ref").' m titre as m m statut as status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition index.php:171
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:63
dol_delete_preview($object)
Delete all preview files linked to object instance.
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_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.
dol_now($mode='auto')
Return date for now.
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.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
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)
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0)
Clean a string to use it as a file name.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
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, when selling a product with vat $vatrate,...
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
getMarginInfos($pv_ht, $remise_percent, $tva_tx, $localtax1_tx, $localtax2_tx, $fk_pa, $pa_ht)
Return an array with margins information of a line.
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
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