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 ($line->delete($user) > 0) {
912 $this->update_price(1);
913
914 return 1;
915 } else {
916 return -1;
917 }
918 } else {
919 return -2;
920 }
921 }
922
923
932 public function create($user, $notrigger = 0)
933 {
934 global $langs, $conf, $mysoc, $hookmanager;
935 $error = 0;
936
937 $now = dol_now();
938
939 dol_syslog(get_class($this)."::create");
940
941 // Check parameters
942 $result = $this->fetch_thirdparty();
943 if ($result < 0) {
944 $this->error = "Failed to fetch company";
945 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
946 return -3;
947 }
948 if (!empty($this->ref)) { // We check that ref is not already used
949 $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
950 if ($result > 0) {
951 $this->error = 'ErrorRefAlreadyExists';
952 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
953 $this->db->rollback();
954 return -1;
955 }
956 }
957
958 // Set tmp vars
959 $delivery_date = $this->delivery_date;
960
961 // Multicurrency
962 if (!empty($this->multicurrency_code)) {
963 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $now);
964 }
965 if (empty($this->fk_multicurrency)) {
966 $this->multicurrency_code = $conf->currency;
967 $this->fk_multicurrency = 0;
968 $this->multicurrency_tx = 1;
969 }
970
971 $this->db->begin();
972
973 // Insert into database
974 $sql = "INSERT INTO ".MAIN_DB_PREFIX."supplier_proposal (";
975 $sql .= "fk_soc";
976 $sql .= ", price";
977 $sql .= ", total_tva";
978 $sql .= ", total_ttc";
979 $sql .= ", datec";
980 $sql .= ", ref";
981 $sql .= ", fk_user_author";
982 $sql .= ", note_private";
983 $sql .= ", note_public";
984 $sql .= ", model_pdf";
985 $sql .= ", fk_cond_reglement";
986 $sql .= ", fk_mode_reglement";
987 $sql .= ", fk_account";
988 $sql .= ", date_livraison";
989 $sql .= ", fk_shipping_method";
990 $sql .= ", fk_projet";
991 $sql .= ", entity";
992 $sql .= ", fk_multicurrency";
993 $sql .= ", multicurrency_code";
994 $sql .= ", multicurrency_tx";
995 $sql .= ") ";
996 $sql .= " VALUES (";
997 $sql .= ((int) $this->socid);
998 $sql .= ", 0";
999 $sql .= ", 0";
1000 $sql .= ", 0";
1001 $sql .= ", '".$this->db->idate($now)."'";
1002 $sql .= ", '(PROV)'";
1003 $sql .= ", ".($user->id > 0 ? ((int) $user->id) : "null");
1004 $sql .= ", '".$this->db->escape($this->note_private)."'";
1005 $sql .= ", '".$this->db->escape($this->note_public)."'";
1006 $sql .= ", '".$this->db->escape($this->model_pdf)."'";
1007 $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : 'NULL');
1008 $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : 'NULL');
1009 $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
1010 $sql .= ", ".(isDolTms($delivery_date) ? "'".$this->db->idate($delivery_date)."'" : "null");
1011 $sql .= ", ".($this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : 'NULL');
1012 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
1013 $sql .= ", ".((int) $conf->entity);
1014 $sql .= ", ".((int) $this->fk_multicurrency);
1015 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1016 $sql .= ", ".((float) $this->multicurrency_tx);
1017 $sql .= ")";
1018
1019 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1020 $resql = $this->db->query($sql);
1021 if ($resql) {
1022 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."supplier_proposal");
1023
1024 if ($this->id) {
1025 $this->ref = '(PROV'.$this->id.')';
1026 $sql = 'UPDATE '.MAIN_DB_PREFIX."supplier_proposal SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
1027
1028 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1029 $resql = $this->db->query($sql);
1030 if (!$resql) {
1031 $error++;
1032 }
1033
1034 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1035 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1036 }
1037
1038 // Add object linked
1039 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1040 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1041 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, ...))
1042 foreach ($tmp_origin_id as $origin_id) {
1043 $ret = $this->add_object_linked($origin, $origin_id);
1044 if (!$ret) {
1045 dol_print_error($this->db);
1046 $error++;
1047 }
1048 }
1049 }
1050 }
1051 }
1052
1053 /*
1054 * Insertion du detail des produits dans la base
1055 */
1056 if (!$error) {
1057 $fk_parent_line = 0;
1058 $num = count($this->lines);
1059
1060 for ($i = 0; $i < $num; $i++) {
1061 // Reset fk_parent_line for no child products and special product
1062 if (($this->lines[$i]->product_type != 9 && empty($this->lines[$i]->fk_parent_line)) || $this->lines[$i]->product_type == 9) {
1063 $fk_parent_line = 0;
1064 }
1065
1066 $result = $this->addline(
1067 $this->lines[$i]->desc,
1068 $this->lines[$i]->subprice,
1069 $this->lines[$i]->qty,
1070 $this->lines[$i]->tva_tx,
1071 $this->lines[$i]->localtax1_tx,
1072 $this->lines[$i]->localtax2_tx,
1073 $this->lines[$i]->fk_product,
1074 $this->lines[$i]->remise_percent,
1075 'HT',
1076 0,
1077 0,
1078 $this->lines[$i]->product_type,
1079 $this->lines[$i]->rang,
1080 $this->lines[$i]->special_code,
1081 $fk_parent_line,
1082 $this->lines[$i]->fk_fournprice,
1083 $this->lines[$i]->pa_ht,
1084 empty($this->lines[$i]->label) ? '' : $this->lines[$i]->label, // deprecated
1085 $this->lines[$i]->array_options,
1086 $this->lines[$i]->ref_fourn,
1087 $this->lines[$i]->fk_unit,
1088 'supplier_proposal',
1089 $this->lines[$i]->rowid
1090 );
1091
1092 if ($result < 0) {
1093 $error++;
1094 $this->error = $this->db->error;
1095 dol_print_error($this->db);
1096 break;
1097 }
1098 // Defined the new fk_parent_line
1099 if ($result > 0 && $this->lines[$i]->product_type == 9) {
1100 $fk_parent_line = $result;
1101 }
1102 }
1103 }
1104
1105 if (!$error) {
1106 // Mise a jour infos denormalisees
1107 $resql = $this->update_price(1);
1108 if ($resql) {
1109 $action = 'update';
1110
1111 // Actions on extra fields
1112 if (!$error) {
1113 $result = $this->insertExtraFields();
1114 if ($result < 0) {
1115 $error++;
1116 }
1117 }
1118
1119 if (!$error && !$notrigger) {
1120 // Call trigger
1121 $result = $this->call_trigger('PROPOSAL_SUPPLIER_CREATE', $user);
1122 if ($result < 0) {
1123 $error++;
1124 }
1125 // End call triggers
1126 }
1127 } else {
1128 $this->error = $this->db->lasterror();
1129 $error++;
1130 }
1131 }
1132 } else {
1133 $this->error = $this->db->lasterror();
1134 $error++;
1135 }
1136
1137 if (!$error) {
1138 $this->db->commit();
1139 dol_syslog(get_class($this)."::create done id=".$this->id);
1140 return $this->id;
1141 } else {
1142 $this->db->rollback();
1143 return -2;
1144 }
1145 } else {
1146 $this->error = $this->db->lasterror();
1147 $this->db->rollback();
1148 return -1;
1149 }
1150 }
1151
1159 public function createFromClone(User $user, $fromid = 0)
1160 {
1161 global $conf, $hookmanager;
1162
1163 $error = 0;
1164 $now = dol_now();
1165
1166 $this->db->begin();
1167
1168 // get extrafields so they will be clone
1169 foreach ($this->lines as $line) {
1170 $line->fetch_optionals();
1171 }
1172
1173 // Load source object
1174 $objFrom = clone $this;
1175
1176 $objsoc = new Societe($this->db);
1177
1178 // Change socid if needed
1179 if (!empty($fromid) && $fromid != $this->socid) {
1180 if ($objsoc->fetch($fromid) > 0) {
1181 $this->socid = $objsoc->id;
1182 $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1183 $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1184 unset($this->fk_project);
1185 }
1186
1187 // TODO Change product price if multi-prices
1188 } else {
1189 $objsoc->fetch($this->socid);
1190 }
1191
1192 $this->id = 0;
1193 $this->statut = 0;
1194
1195 if (!getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') || !is_readable(DOL_DOCUMENT_ROOT."/core/modules/supplier_proposal/" . getDolGlobalString('SUPPLIER_PROPOSAL_ADDON').".php")) {
1196 $this->error = 'ErrorSetupNotComplete';
1197 return -1;
1198 }
1199
1200 // Clear fields
1201 $this->user_author_id = $user->id;
1202 $this->user_validation_id = 0;
1203 $this->date = $now;
1204
1205 // Set ref
1206 require_once DOL_DOCUMENT_ROOT."/core/modules/supplier_proposal/" . getDolGlobalString('SUPPLIER_PROPOSAL_ADDON').'.php';
1207 $obj = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
1208 $modSupplierProposal = new $obj();
1209 '@phan-var-force ModeleNumRefSupplierProposal $modSupplierProposal';
1210 $this->ref = $modSupplierProposal->getNextValue($objsoc, $this);
1211
1212 // Create clone
1213 $this->context['createfromclone'] = 'createfromclone';
1214 $result = $this->create($user);
1215 if ($result < 0) {
1216 $error++;
1217 }
1218
1219 if (!$error) {
1220 // Hook of thirdparty module
1221 if (is_object($hookmanager)) {
1222 $parameters = array('objFrom' => $objFrom);
1223 $action = '';
1224 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1225 if ($reshook < 0) {
1226 $this->setErrorsFromObject($hookmanager);
1227 $error++;
1228 }
1229 }
1230 }
1231
1232 unset($this->context['createfromclone']);
1233
1234 // End
1235 if (!$error) {
1236 $this->db->commit();
1237 return $this->id;
1238 } else {
1239 $this->db->rollback();
1240 return -1;
1241 }
1242 }
1243
1251 public function fetch($rowid, $ref = '')
1252 {
1253 global $conf;
1254
1255 $sql = "SELECT p.rowid, p.entity, p.ref, p.fk_soc as socid";
1256 $sql .= ", p.total_ttc, p.total_tva, p.localtax1, p.localtax2, p.total_ht";
1257 $sql .= ", p.datec";
1258 $sql .= ", p.date_valid as datev";
1259 $sql .= ", p.date_livraison as delivery_date";
1260 $sql .= ", p.model_pdf, p.extraparams";
1261 $sql .= ", p.note_private, p.note_public";
1262 $sql .= ", p.fk_projet as fk_project, p.fk_statut";
1263 $sql .= ", p.fk_user_author, p.fk_user_valid, p.fk_user_cloture";
1264 $sql .= ", p.fk_cond_reglement";
1265 $sql .= ", p.fk_mode_reglement";
1266 $sql .= ', p.fk_account';
1267 $sql .= ", p.fk_shipping_method";
1268 $sql .= ", p.last_main_doc";
1269 $sql .= ", p.fk_multicurrency, p.multicurrency_code, p.multicurrency_tx, p.multicurrency_total_ht, p.multicurrency_total_tva, p.multicurrency_total_ttc";
1270 $sql .= ", c.label as statut_label";
1271 $sql .= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc";
1272 $sql .= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement";
1273 $sql .= " FROM ".MAIN_DB_PREFIX."c_propalst as c, ".MAIN_DB_PREFIX."supplier_proposal as p";
1274 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id';
1275 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON p.fk_cond_reglement = cr.rowid';
1276 $sql .= " WHERE p.fk_statut = c.id";
1277 $sql .= " AND p.entity IN (".getEntity('supplier_proposal').")";
1278 if ($ref) {
1279 $sql .= " AND p.ref = '".$this->db->escape($ref)."'";
1280 } else {
1281 $sql .= " AND p.rowid = ".((int) $rowid);
1282 }
1283
1284 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1285 $resql = $this->db->query($sql);
1286 if ($resql) {
1287 if ($this->db->num_rows($resql)) {
1288 $obj = $this->db->fetch_object($resql);
1289
1290 $this->id = $obj->rowid;
1291 $this->entity = $obj->entity;
1292
1293 $this->ref = $obj->ref;
1294 $this->total_ht = $obj->total_ht;
1295 $this->total_tva = $obj->total_tva;
1296 $this->total_localtax1 = $obj->localtax1;
1297 $this->total_localtax2 = $obj->localtax2;
1298 $this->total_ttc = $obj->total_ttc;
1299 $this->socid = $obj->socid;
1300 $this->fk_project = $obj->fk_project;
1301 $this->model_pdf = $obj->model_pdf;
1302 $this->note = $obj->note_private; // TODO deprecated
1303 $this->note_private = $obj->note_private;
1304 $this->note_public = $obj->note_public;
1305 $this->statut = (int) $obj->fk_statut;
1306 $this->status = (int) $obj->fk_statut;
1307 $this->datec = $this->db->jdate($obj->datec); // TODO deprecated
1308 $this->datev = $this->db->jdate($obj->datev); // TODO deprecated
1309 $this->date_creation = $this->db->jdate($obj->datec); // Creation date
1310 $this->date = $this->date_creation;
1311 $this->date_validation = $this->db->jdate($obj->datev); // Validation date
1312 $this->delivery_date = $this->db->jdate($obj->delivery_date);
1313 $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1314
1315 $this->last_main_doc = $obj->last_main_doc;
1316 $this->mode_reglement_id = $obj->fk_mode_reglement;
1317 $this->mode_reglement_code = $obj->mode_reglement_code;
1318 $this->mode_reglement = $obj->mode_reglement;
1319 $this->fk_account = ($obj->fk_account > 0) ? $obj->fk_account : null;
1320 $this->cond_reglement_id = $obj->fk_cond_reglement;
1321 $this->cond_reglement_code = $obj->cond_reglement_code;
1322 $this->cond_reglement = $obj->cond_reglement;
1323 $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1324
1325 $this->extraparams = (array) (!empty($obj->extraparams) ? json_decode($obj->extraparams, true) : array());
1326
1327 $this->user_author_id = $obj->fk_user_author;
1328 $this->user_validation_id = $obj->fk_user_valid;
1329 $this->user_closing_id = $obj->fk_user_cloture;
1330
1331 // Multicurrency
1332 $this->fk_multicurrency = $obj->fk_multicurrency;
1333 $this->multicurrency_code = $obj->multicurrency_code;
1334 $this->multicurrency_tx = $obj->multicurrency_tx;
1335 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1336 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1337 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1338
1339 $this->db->free($resql);
1340
1341 // Retrieve all extrafield
1342 // fetch optionals attributes and labels
1343 $this->fetch_optionals();
1344
1345 $this->lines = array();
1346
1347 // Lines of supplier proposals
1348 $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,";
1349 $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,";
1350 $sql .= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label,';
1351 $sql .= ' d.ref_fourn as ref_produit_fourn, d.extraparams,';
1352 $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';
1353 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposaldet as d";
1354 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON d.fk_product = p.rowid";
1355 $sql .= " WHERE d.fk_supplier_proposal = ".((int) $this->id);
1356 $sql .= " ORDER by d.rang";
1357
1358 $result = $this->db->query($sql);
1359 if ($result) {
1360 $num = $this->db->num_rows($result);
1361 $i = 0;
1362
1363 while ($i < $num) {
1364 $objp = $this->db->fetch_object($result);
1365
1366 $line = new SupplierProposalLine($this->db);
1367
1368 $line->rowid = $objp->rowid; // deprecated
1369 $line->id = $objp->rowid;
1370 $line->fk_supplier_proposal = $objp->fk_supplier_proposal;
1371 $line->fk_parent_line = $objp->fk_parent_line;
1372 $line->product_type = $objp->product_type;
1373 $line->label = $objp->custom_label;
1374 $line->desc = $objp->description; // Description ligne
1375 $line->qty = $objp->qty;
1376 $line->tva_tx = $objp->tva_tx;
1377 $line->localtax1_tx = $objp->localtax1_tx;
1378 $line->localtax2_tx = $objp->localtax2_tx;
1379 $line->subprice = $objp->subprice;
1380 $line->fk_remise_except = $objp->fk_remise_except;
1381 $line->remise_percent = $objp->remise_percent;
1382
1383 $line->info_bits = $objp->info_bits;
1384 $line->total_ht = $objp->total_ht;
1385 $line->total_tva = $objp->total_tva;
1386 $line->total_localtax1 = $objp->total_localtax1;
1387 $line->total_localtax2 = $objp->total_localtax2;
1388 $line->total_ttc = $objp->total_ttc;
1389 $line->fk_fournprice = $objp->fk_fournprice;
1390 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1391 $line->pa_ht = $marginInfos[0];
1392 $line->marge_tx = $marginInfos[1];
1393 $line->marque_tx = $marginInfos[2];
1394 $line->special_code = $objp->special_code;
1395 $line->rang = $objp->rang;
1396
1397 $line->fk_product = $objp->fk_product;
1398
1399 $line->ref = $objp->product_ref; // deprecated
1400 $line->product_ref = $objp->product_ref;
1401 $line->libelle = $objp->product_label; // deprecated
1402 $line->product_label = $objp->product_label;
1403 $line->product_desc = $objp->product_desc; // Description produit
1404 $line->fk_product_type = $objp->fk_product_type;
1405
1406 $line->ref_fourn = $objp->ref_produit_fourn;
1407
1408 $line->extraparams = !empty($objp->extraparams) ? (array) json_decode($objp->extraparams, true) : array();
1409
1410 // Multicurrency
1411 $line->fk_multicurrency = $objp->fk_multicurrency;
1412 $line->multicurrency_code = $objp->multicurrency_code;
1413 $line->multicurrency_subprice = $objp->multicurrency_subprice;
1414 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
1415 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
1416 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
1417 $line->fk_unit = $objp->fk_unit;
1418
1419 $line->fetch_optionals();
1420
1421 $this->lines[$i] = $line;
1422
1423 $i++;
1424 }
1425 $this->db->free($result);
1426 } else {
1427 $this->error = $this->db->error();
1428 return -1;
1429 }
1430
1431 return 1;
1432 }
1433
1434 $this->error = "Record Not Found";
1435 return 0;
1436 } else {
1437 $this->error = $this->db->error();
1438 return -1;
1439 }
1440 }
1441
1449 public function valid($user, $notrigger = 0)
1450 {
1451 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1452
1453 global $conf, $langs;
1454
1455 $error = 0;
1456 $now = dol_now();
1457
1458 if ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'creer'))
1459 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'validate_advance'))) {
1460 $this->db->begin();
1461
1462 // Numbering module definition
1463 $soc = new Societe($this->db);
1464 $result = $soc->fetch($this->socid);
1465
1466 if ($result < 0) {
1467 return -1;
1468 }
1469
1470 // Define new ref
1471 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1472 $num = $this->getNextNumRef($soc);
1473 } else {
1474 $num = $this->ref;
1475 }
1476 $this->newref = dol_sanitizeFileName($num);
1477
1478 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1479 $sql .= " SET ref = '".$this->db->escape($num)."',";
1480 $sql .= " fk_statut = 1, date_valid='".$this->db->idate($now)."', fk_user_valid=".((int) $user->id);
1481 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1482
1483 dol_syslog(get_class($this)."::valid", LOG_DEBUG);
1484 $resql = $this->db->query($sql);
1485 if (!$resql) {
1486 dol_print_error($this->db);
1487 $error++;
1488 }
1489
1490 // Trigger calls
1491 if (!$error && !$notrigger) {
1492 // Call trigger
1493 $result = $this->call_trigger('PROPOSAL_SUPPLIER_VALIDATE', $user);
1494 if ($result < 0) {
1495 $error++;
1496 }
1497 // End call triggers
1498 }
1499
1500 if (!$error) {
1501 $this->oldref = $this->ref;
1502
1503 // Rename directory if dir was a temporary ref
1504 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1505 // Now we rename also files into index
1506 $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)."'";
1507 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1508 $resql = $this->db->query($sql);
1509 if (!$resql) {
1510 $error++;
1511 $this->error = $this->db->lasterror();
1512 }
1513 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'supplier_proposal/".$this->db->escape($this->newref)."'";
1514 $sql .= " WHERE filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1515 $resql = $this->db->query($sql);
1516 if (!$resql) {
1517 $error++;
1518 $this->error = $this->db->lasterror();
1519 }
1520
1521 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1522 $oldref = dol_sanitizeFileName($this->ref);
1523 $newref = dol_sanitizeFileName($num);
1524 $dirsource = $conf->supplier_proposal->dir_output.'/'.$oldref;
1525 $dirdest = $conf->supplier_proposal->dir_output.'/'.$newref;
1526 if (!$error && file_exists($dirsource)) {
1527 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
1528 if (@rename($dirsource, $dirdest)) {
1529 dol_syslog("Rename ok");
1530 // Rename docs starting with $oldref with $newref
1531 $listoffiles = dol_dir_list($conf->supplier_proposal->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1532 foreach ($listoffiles as $fileentry) {
1533 $dirsource = $fileentry['name'];
1534 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1535 $dirsource = $fileentry['path'].'/'.$dirsource;
1536 $dirdest = $fileentry['path'].'/'.$dirdest;
1537 @rename($dirsource, $dirdest);
1538 }
1539 }
1540 }
1541 }
1542
1543 $this->ref = $num;
1544 $this->statut = self::STATUS_VALIDATED;
1546 $this->user_validation_id = $user->id;
1547 $this->datev = $now;
1548 $this->date_validation = $now;
1549
1550 if (getDolGlobalString('SUPPLIER_PROPOSAL_AUTOADD_USER_CONTACT')) {
1551 $result = $this->add_contact($user->id, 'SALESREPFOLL', 'internal', 1);
1552 if ($result < 0 && $result != -2) { // -2 means already exists
1553 $error++;
1554 }
1555 }
1556
1557 $this->db->commit();
1558 return 1;
1559 } else {
1560 $this->db->rollback();
1561 return -1;
1562 }
1563 } else {
1564 dol_syslog("You don't have permission to validate supplier proposal", LOG_WARNING);
1565 return -2;
1566 }
1567 }
1568
1569 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1578 public function set_date_livraison($user, $delivery_date)
1579 {
1580 // phpcs:enable
1581 return $this->setDeliveryDate($user, $delivery_date);
1582 }
1583
1591 public function setDeliveryDate($user, $delivery_date)
1592 {
1593 if ($user->hasRight('supplier_proposal', 'creer')) {
1594 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1595 $sql .= " SET date_livraison = ".(isDolTms($delivery_date) ? "'".$this->db->idate($delivery_date)."'" : 'null');
1596 $sql .= " WHERE rowid = ".((int) $this->id);
1597
1598 if ($this->db->query($sql)) {
1599 $this->delivery_date = $delivery_date;
1600 return 1;
1601 } else {
1602 $this->error = $this->db->error();
1603 dol_syslog(get_class($this)."::setDeliveryDate Erreur SQL");
1604 return -1;
1605 }
1606 }
1607 return 0;
1608 }
1609
1610 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1618 /*
1619 public function set_remise_percent($user, $remise)
1620 {
1621 // phpcs:enable
1622 $remise = trim($remise) ?trim($remise) : 0;
1623
1624 if ($user->hasRight('supplier_proposal', 'creer')) {
1625 $remise = price2num($remise, 2);
1626
1627 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal SET remise_percent = ".((float) $remise);
1628 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1629
1630 if ($this->db->query($sql)) {
1631 $this->remise_percent = ((float) $remise);
1632 $this->update_price(1);
1633 return 1;
1634 } else {
1635 $this->error = $this->db->error();
1636 return -1;
1637 }
1638 }
1639 return 0;
1640 }
1641 */
1642
1643 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1651 /*
1652 public function set_remise_absolue($user, $remise)
1653 {
1654 // phpcs:enable
1655 if (empty($remise)) {
1656 $remise = 0;
1657 }
1658
1659 $remise = price2num($remise);
1660
1661 if ($user->hasRight('supplier_proposal', 'creer')) {
1662 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1663 $sql .= " SET remise_absolue = ".((float) $remise);
1664 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1665
1666 if ($this->db->query($sql)) {
1667 $this->remise_absolue = $remise;
1668 $this->update_price(1);
1669 return 1;
1670 } else {
1671 $this->error = $this->db->error();
1672 return -1;
1673 }
1674 }
1675 return 0;
1676 }
1677 */
1678
1679
1689 public function reopen($user, $status, $note = '', $notrigger = 0)
1690 {
1691 $error = 0;
1692
1693 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1694 $sql .= " SET fk_statut = ".((int) $status).",";
1695 if (!empty($note)) {
1696 $sql .= " note_private = '".$this->db->escape($note)."',";
1697 }
1698 $sql .= " date_cloture = NULL, fk_user_cloture = NULL";
1699 $sql .= " WHERE rowid = ".((int) $this->id);
1700
1701 $this->db->begin();
1702
1703 dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
1704 $resql = $this->db->query($sql);
1705 if (!$resql) {
1706 $error++;
1707 $this->errors[] = "Error ".$this->db->lasterror();
1708 }
1709 if (!$error) {
1710 if (!$notrigger) {
1711 // Call trigger
1712 $result = $this->call_trigger('PROPOSAL_SUPPLIER_REOPEN', $user);
1713 if ($result < 0) {
1714 $error++;
1715 }
1716 // End call triggers
1717 }
1718 }
1719
1720 // Commit or rollback
1721 if ($error) {
1722 if (!empty($this->errors)) {
1723 foreach ($this->errors as $errmsg) {
1724 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1725 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1726 }
1727 }
1728 $this->db->rollback();
1729 return -1 * $error;
1730 } else {
1731 $this->statut = $status;
1732 $this->status = $status;
1733
1734 $this->db->commit();
1735 return 1;
1736 }
1737 }
1738
1739
1748 public function cloture($user, $status, $note)
1749 {
1750 global $langs, $conf;
1751 $hidedetails = 0;
1752 $hidedesc = 0;
1753 $hideref = 0;
1754
1755 $error = 0;
1756 $now = dol_now();
1757
1758 $this->db->begin();
1759
1760 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1761 $sql .= " SET fk_statut = ".((int) $status).", note_private = '".$this->db->escape($note)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".((int) $user->id);
1762 $sql .= " WHERE rowid = ".((int) $this->id);
1763
1764 $resql = $this->db->query($sql);
1765 if ($resql) {
1766 $this->statut = $status;
1767 $this->status = $status;
1768
1769 $modelpdf = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED', (empty($this->model_pdf) ? '' : $this->model_pdf));
1770 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_REFUSED';
1771
1772 if ($status == 2) {
1773 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_SIGNED';
1774 $modelpdf = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL', (empty($this->model_pdf) ? '' : $this->model_pdf));
1775
1776 if (getDolGlobalString('SUPPLIER_PROPOSAL_UPDATE_PRICE_ON_SUPPlIER_PROPOSAL')) { // TODO This option was not tested correctly. Error if product ref does not exists
1777 $result = $this->updateOrCreatePriceFournisseur($user);
1778 }
1779 }
1780 if ($status == 4) {
1781 $triggerName = 'PROPOSAL_SUPPLIER_CLASSIFY_BILLED';
1782 }
1783
1784 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1785 // Define output language
1786 $outputlangs = $langs;
1787 if (getDolGlobalInt('MAIN_MULTILANGS')) {
1788 $outputlangs = new Translate("", $conf);
1789 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
1790 $outputlangs->setDefaultLang($newlang);
1791 }
1792
1793 $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1794 }
1795
1796 // Call trigger
1797 $result = $this->call_trigger($triggerName, $user);
1798 if ($result < 0) {
1799 $error++;
1800 }
1801 // End call triggers
1802
1803 if (!$error) {
1804 $this->db->commit();
1805 return 1;
1806 } else {
1807 $this->db->rollback();
1808 return -1;
1809 }
1810 } else {
1811 $this->error = $this->db->lasterror();
1812 $this->errors[] = $this->db->lasterror();
1813 $this->db->rollback();
1814 return -1;
1815 }
1816 }
1817
1824 public function updateOrCreatePriceFournisseur($user)
1825 {
1826 global $conf;
1827
1828 dol_syslog(get_class($this)."::updateOrCreatePriceFournisseur", LOG_DEBUG);
1829 foreach ($this->lines as $product) {
1830 if ($product->subprice <= 0) {
1831 continue;
1832 }
1833 $productsupplier = new ProductFournisseur($this->db);
1834
1835 $multicurrency_tx = 1;
1836 $fk_multicurrency = 0;
1837
1838 if (empty($this->thirdparty)) {
1839 $this->fetch_thirdparty();
1840 }
1841
1842 $ref_fourn = $product->ref_fourn;
1843 if (empty($ref_fourn)) {
1844 $ref_fourn = $product->ref_supplier;
1845 }
1846 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1847 list($fk_multicurrency, $multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $product->multicurrency_code);
1848 }
1849 $productsupplier->id = $product->fk_product;
1850
1851 $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);
1852 }
1853
1854 return 1;
1855 }
1856
1865 public function updatePriceFournisseur($idProductFournPrice, $product, $user)
1866 {
1867 $price = price2num($product->subprice * $product->qty, 'MU');
1868 $unitPrice = price2num($product->subprice, 'MU');
1869
1870 $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);
1871
1872 $resql = $this->db->query($sql);
1873 if (!$resql) {
1874 $this->error = $this->db->error();
1875 $this->db->rollback();
1876 return -1;
1877 }
1878 return 1;
1879 }
1880
1888 public function createPriceFournisseur($product, $user)
1889 {
1890 global $conf;
1891
1892 $price = price2num($product->subprice * $product->qty, 'MU');
1893 $qty = price2num($product->qty);
1894 $unitPrice = price2num($product->subprice, 'MU');
1895
1896 $now = dol_now();
1897
1898 $values = array(
1899 "'".$this->db->idate($now)."'",
1900 (int) $product->fk_product,
1901 (int) $this->thirdparty->id,
1902 "'".$this->db->escape($product->ref_fourn)."'",
1903 (float) $price,
1904 (float) $qty,
1905 (float) $unitPrice,
1906 (float) $product->tva_tx,
1907 (int) $user->id
1908 );
1909 if (isModEnabled("multicurrency")) {
1910 if (!empty($product->multicurrency_code)) {
1911 include_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
1912 $multicurrency = new MultiCurrency($this->db); //need to fetch because empty fk_multicurrency and rate
1913 $multicurrency->fetch(0, $product->multicurrency_code);
1914 if (!empty($multicurrency->id)) {
1915 $values[] = (int) $multicurrency->id;
1916 $values[] = "'".$this->db->escape($product->multicurrency_code)."'";
1917 $values[] = (float) $product->multicurrency_subprice;
1918 $values[] = (float) $product->multicurrency_total_ht;
1919 $values[] = (float) $multicurrency->rate->rate;
1920 } else {
1921 for ($i = 0; $i < 5; $i++) {
1922 $values[] = 'NULL';
1923 }
1924 }
1925 }
1926 }
1927
1928 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'product_fournisseur_price ';
1929 $sql .= '(datec, fk_product, fk_soc, ref_fourn, price, quantity, unitprice, tva_tx, fk_user';
1930 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1931 $sql .= ',fk_multicurrency, multicurrency_code, multicurrency_unitprice, multicurrency_price, multicurrency_tx';
1932 }
1933 $sql .= ') VALUES ('.implode(',', $values).')';
1934
1935 $resql = $this->db->query($sql);
1936 if (!$resql) {
1937 $this->error = $this->db->error();
1938 $this->db->rollback();
1939 return -1;
1940 }
1941 return 1;
1942 }
1943
1944 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1951 public function setDraft($user)
1952 {
1953 // phpcs:enable
1954 global $conf, $langs;
1955
1956 $error = 0;
1957
1958 if ($this->status == self::STATUS_DRAFT) {
1959 dol_syslog(get_class($this)."::setDraft already draft status", LOG_WARNING);
1960 return 0;
1961 }
1962
1963 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1964 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1965 $sql .= " WHERE rowid = ".((int) $this->id);
1966
1967 if ($this->db->query($sql)) {
1968 if (!$error) {
1969 $this->oldcopy = clone $this;
1970 }
1971
1972 if (!$error) {
1973 // Call trigger
1974 $result = $this->call_trigger('PROPOSAL_SUPPLIER_UNVALIDATE', $user);
1975 if ($result < 0) {
1976 $error++;
1977 }
1978 }
1979
1980 if (!$error) {
1981 $this->status = self::STATUS_DRAFT;
1982 $this->statut = self::STATUS_DRAFT; // deprecated
1983 $this->db->commit();
1984 return 1;
1985 } else {
1986 $this->db->rollback();
1987 return -1;
1988 }
1989 } else {
1990 return -1;
1991 }
1992 }
1993
1994
1995 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2009 public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datec', $sortorder = 'DESC')
2010 {
2011 // phpcs:enable
2012 global $user;
2013
2014 $ga = array();
2015
2016 $search_sale = 0;
2017 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2018 $search_sale = $user->id;
2019 }
2020
2021 $sql = "SELECT s.rowid, s.nom as name, s.client,";
2022 $sql .= " p.rowid as supplier_proposalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
2023 $sql .= " p.datep as dp, p.fin_validite as datelimite";
2024 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."supplier_proposal as p, ".MAIN_DB_PREFIX."c_propalst as c";
2025 $sql .= " WHERE p.entity IN (".getEntity('supplier_proposal').")";
2026 $sql .= " AND p.fk_soc = s.rowid";
2027 $sql .= " AND p.fk_statut = c.id";
2028 if ($socid) {
2029 $sql .= " AND s.rowid = ".((int) $socid);
2030 }
2031 if ($draft) {
2032 $sql .= " AND p.fk_statut = 0";
2033 }
2034 if ($notcurrentuser > 0) {
2035 $sql .= " AND p.fk_user_author <> ".((int) $user->id);
2036 }
2037 // Search on sale representative
2038 if ($search_sale && $search_sale != '-1') {
2039 if ($search_sale == -2) {
2040 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
2041 } elseif ($search_sale > 0) {
2042 $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).")";
2043 }
2044 }
2045 $sql .= $this->db->order($sortfield, $sortorder);
2046 $sql .= $this->db->plimit($limit, $offset);
2047
2048 $result = $this->db->query($sql);
2049 if ($result) {
2050 $num = $this->db->num_rows($result);
2051 if ($num) {
2052 $i = 0;
2053 while ($i < $num) {
2054 $obj = $this->db->fetch_object($result);
2055
2056 if ($shortlist == 1) {
2057 $ga[$obj->supplier_proposalid] = $obj->ref;
2058 } elseif ($shortlist == 2) {
2059 $ga[$obj->supplier_proposalid] = $obj->ref.' ('.$obj->name.')';
2060 } else {
2061 $ga[$i]['id'] = $obj->supplier_proposalid;
2062 $ga[$i]['ref'] = $obj->ref;
2063 $ga[$i]['name'] = $obj->name;
2064 }
2065
2066 $i++;
2067 }
2068 }
2069 return $ga;
2070 } else {
2071 dol_print_error($this->db);
2072 return -1;
2073 }
2074 }
2075
2083 public function delete($user, $notrigger = 0)
2084 {
2085 global $conf, $langs;
2086 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2087
2088 $error = 0;
2089
2090 $this->db->begin();
2091
2092 if (!$notrigger) {
2093 // Call trigger
2094 $result = $this->call_trigger('PROPOSAL_SUPPLIER_DELETE', $user);
2095 if ($result < 0) {
2096 $error++;
2097 }
2098 // End call triggers
2099 }
2100
2101 if (!$error) {
2102 $main = MAIN_DB_PREFIX.'supplier_proposaldet';
2103 $ef = $main."_extrafields";
2104 $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_supplier_proposal = ".((int) $this->id).")";
2105 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet WHERE fk_supplier_proposal = ".((int) $this->id);
2106 if ($this->db->query($sql)) {
2107 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposal WHERE rowid = ".((int) $this->id);
2108 if ($this->db->query($sqlef) && $this->db->query($sql)) {
2109 // Delete linked object
2110 $res = $this->deleteObjectLinked();
2111 if ($res < 0) {
2112 $error++;
2113 }
2114
2115 if (!$error) {
2116 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2117 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
2118 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
2119
2120 // We remove directory
2121 $ref = dol_sanitizeFileName($this->ref);
2122 if ($conf->supplier_proposal->dir_output && !empty($this->ref)) {
2123 $dir = $conf->supplier_proposal->dir_output."/".$ref;
2124 $file = $dir."/".$ref.".pdf";
2125 if (file_exists($file)) {
2126 dol_delete_preview($this);
2127
2128 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2129 $this->error = 'ErrorFailToDeleteFile';
2130 $this->errors = array('ErrorFailToDeleteFile');
2131 $this->db->rollback();
2132 return 0;
2133 }
2134 }
2135 if (file_exists($dir)) {
2136 $res = @dol_delete_dir_recursive($dir);
2137 if (!$res) {
2138 $this->error = 'ErrorFailToDeleteDir';
2139 $this->errors = array('ErrorFailToDeleteDir');
2140 $this->db->rollback();
2141 return 0;
2142 }
2143 }
2144 }
2145 }
2146
2147 // Removed extrafields
2148 if (!$error) {
2149 $result = $this->deleteExtraFields();
2150 if ($result < 0) {
2151 $error++;
2152 $errorflag = -4;
2153 dol_syslog(get_class($this)."::delete erreur ".$errorflag." ".$this->error, LOG_ERR);
2154 }
2155 }
2156
2157 if (!$error) {
2158 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
2159 $this->db->commit();
2160 return 1;
2161 } else {
2162 $this->error = $this->db->lasterror();
2163 $this->db->rollback();
2164 return 0;
2165 }
2166 } else {
2167 $this->error = $this->db->lasterror();
2168 $this->db->rollback();
2169 return -3;
2170 }
2171 } else {
2172 $this->error = $this->db->lasterror();
2173 $this->db->rollback();
2174 return -2;
2175 }
2176 } else {
2177 $this->db->rollback();
2178 return -1;
2179 }
2180 }
2181
2188 public function info($id)
2189 {
2190 $sql = "SELECT c.rowid, ";
2191 $sql .= " c.datec as date_creation, c.date_valid as date_validation, c.date_cloture as date_closure,";
2192 $sql .= " c.fk_user_author, c.fk_user_valid, c.fk_user_cloture";
2193 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as c";
2194 $sql .= " WHERE c.rowid = ".((int) $id);
2195
2196 $result = $this->db->query($sql);
2197
2198 if ($result) {
2199 if ($this->db->num_rows($result)) {
2200 $obj = $this->db->fetch_object($result);
2201
2202 $this->id = $obj->rowid;
2203
2204 $this->date_creation = $this->db->jdate($obj->date_creation);
2205 $this->date_validation = $this->db->jdate($obj->date_validation);
2206 $this->date_cloture = $this->db->jdate($obj->date_closure);
2207
2208 $this->user_creation_id = $obj->fk_user_author;
2209 $this->user_validation_id = $obj->fk_user_valid;
2210 $this->user_closing_id = $obj->fk_user_cloture;
2211 }
2212 $this->db->free($result);
2213 } else {
2214 dol_print_error($this->db);
2215 }
2216 }
2217
2218
2225 public function getLibStatut($mode = 0)
2226 {
2227 return $this->LibStatut((isset($this->status) ? $this->status : $this->statut), $mode);
2228 }
2229
2230 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2238 public function LibStatut($status, $mode = 1)
2239 {
2240 // phpcs:enable
2241
2242 // Init/load array of translation of status
2243 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
2244 global $langs;
2245 $langs->load("supplier_proposal");
2246 $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraft");
2247 $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidated");
2248 $this->labelStatus[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSigned");
2249 $this->labelStatus[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSigned");
2250 $this->labelStatus[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosed");
2251 $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraftShort");
2252 $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidatedShort");
2253 $this->labelStatusShort[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSignedShort");
2254 $this->labelStatusShort[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSignedShort");
2255 $this->labelStatusShort[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosedShort");
2256 }
2257
2258 $statusnew = '';
2259 if ($status == self::STATUS_DRAFT) {
2260 $statusnew = 'status0';
2261 } elseif ($status == self::STATUS_VALIDATED) {
2262 $statusnew = 'status1';
2263 } elseif ($status == self::STATUS_SIGNED) {
2264 $statusnew = 'status4';
2265 } elseif ($status == self::STATUS_NOTSIGNED) {
2266 $statusnew = 'status9';
2267 } elseif ($status == self::STATUS_CLOSE) {
2268 $statusnew = 'status6';
2269 }
2270
2271 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusnew, $mode);
2272 }
2273
2274
2275 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2283 public function load_board($user, $mode)
2284 {
2285 // phpcs:enable
2286 global $conf, $langs;
2287
2288 $now = dol_now();
2289
2290 $clause = " WHERE";
2291
2292 $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.date_cloture as datefin";
2293 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2294 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2295 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
2296 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2297 $clause = " AND";
2298 }
2299 $sql .= $clause." p.entity IN (".getEntity('supplier_proposal').")";
2300 if ($mode == 'opened') {
2301 $sql .= " AND p.fk_statut = 1";
2302 }
2303 if ($mode == 'signed') {
2304 $sql .= " AND p.fk_statut = 2";
2305 }
2306 if ($user->socid) {
2307 $sql .= " AND p.fk_soc = ".((int) $user->socid);
2308 }
2309
2310 $resql = $this->db->query($sql);
2311 if ($resql) {
2312 $label = $labelShort = '';
2313 $status = '';
2314 $delay_warning = 0;
2315 if ($mode == 'opened') {
2316 $delay_warning = !empty($conf->supplier_proposal->cloture->warning_delay) ? $conf->supplier_proposal->cloture->warning_delay : 0;
2317 $status = self::STATUS_VALIDATED;
2318 $label = $langs->trans("SupplierProposalsToClose");
2319 $labelShort = $langs->trans("ToAcceptRefuse");
2320 }
2321 if ($mode == 'signed') {
2322 $delay_warning = !empty($conf->supplier_proposal->facturation->warning_delay) ? $conf->supplier_proposal->facturation->warning_delay : 0;
2323 $status = self::STATUS_SIGNED;
2324 $label = $langs->trans("SupplierProposalsToProcess"); // May be billed or ordered
2325 $labelShort = $langs->trans("ToClose");
2326 }
2327
2328 $response = new WorkboardResponse();
2329 $response->warning_delay = $delay_warning / 60 / 60 / 24;
2330 $response->label = $label;
2331 $response->labelShort = $labelShort;
2332 $response->url = DOL_URL_ROOT.'/supplier_proposal/list.php?search_status='.$status;
2333 $response->img = img_object('', "propal");
2334
2335 // This assignment in condition is not a bug. It allows walking the results.
2336 while ($obj = $this->db->fetch_object($resql)) {
2337 $response->nbtodo++;
2338 if ($mode == 'opened') {
2339 $datelimit = $this->db->jdate($obj->datefin);
2340 if ($datelimit < ($now - $delay_warning)) {
2341 $response->nbtodolate++;
2342 }
2343 }
2344 // TODO Definir regle des propales a facturer en retard
2345 // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
2346 }
2347 return $response;
2348 } else {
2349 $this->error = $this->db->lasterror();
2350 return -1;
2351 }
2352 }
2353
2354
2362 public function initAsSpecimen()
2363 {
2364 global $user, $langs, $conf;
2365
2366 // Load array of products prodids
2367 $num_prods = 0;
2368 $prodids = array();
2369 $sql = "SELECT rowid";
2370 $sql .= " FROM ".MAIN_DB_PREFIX."product";
2371 $sql .= " WHERE entity IN (".getEntity('product').")";
2372 $sql .= $this->db->plimit(100);
2373
2374 $resql = $this->db->query($sql);
2375 if ($resql) {
2376 $num_prods = $this->db->num_rows($resql);
2377 $i = 0;
2378 while ($i < $num_prods) {
2379 $i++;
2380 $row = $this->db->fetch_row($resql);
2381 $prodids[$i] = $row[0];
2382 }
2383 }
2384
2385 // Initialise parameters
2386 $this->id = 0;
2387 $this->ref = 'SPECIMEN';
2388 $this->specimen = 1;
2389 $this->socid = 1;
2390 $this->date = time();
2391 $this->cond_reglement_id = 1;
2392 $this->cond_reglement_code = 'RECEP';
2393 $this->mode_reglement_id = 7;
2394 $this->mode_reglement_code = 'CHQ';
2395 $this->note_public = 'This is a comment (public)';
2396 $this->note_private = 'This is a comment (private)';
2397 // Lines
2398 $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)
2399 $xnbp = 0;
2400 while ($xnbp < $nbp) {
2401 $line = new SupplierProposalLine($this->db);
2402 $line->desc = $langs->trans("Description")." ".$xnbp;
2403 $line->qty = 1;
2404 $line->subprice = 100;
2405 $line->tva_tx = 19.6;
2406 $line->localtax1_tx = 0;
2407 $line->localtax2_tx = 0;
2408 if ($xnbp == 2) {
2409 $line->total_ht = 50;
2410 $line->total_ttc = 59.8;
2411 $line->total_tva = 9.8;
2412 $line->remise_percent = 50;
2413 } else {
2414 $line->total_ht = 100;
2415 $line->total_ttc = 119.6;
2416 $line->total_tva = 19.6;
2417 $line->remise_percent = 00;
2418 }
2419
2420 if ($num_prods > 0) {
2421 $prodid = mt_rand(1, $num_prods);
2422 $line->fk_product = $prodids[$prodid];
2423 }
2424
2425 $this->lines[$xnbp] = $line;
2426
2427 $this->total_ht += $line->total_ht;
2428 $this->total_tva += $line->total_tva;
2429 $this->total_ttc += $line->total_ttc;
2430
2431 $xnbp++;
2432 }
2433
2434 return 1;
2435 }
2436
2442 public function loadStateBoard()
2443 {
2444 global $conf, $user;
2445
2446 $this->nb = array();
2447 $clause = "WHERE";
2448
2449 $sql = "SELECT count(p.rowid) as nb";
2450 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2451 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
2452 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2453 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2454 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2455 $clause = "AND";
2456 }
2457 $sql .= " ".$clause." p.entity IN (".getEntity('supplier_proposal').")";
2458
2459 $resql = $this->db->query($sql);
2460 if ($resql) {
2461 // This assignment in condition is not a bug. It allows walking the results.
2462 while ($obj = $this->db->fetch_object($resql)) {
2463 $this->nb["supplier_proposals"] = $obj->nb;
2464 }
2465 $this->db->free($resql);
2466 return 1;
2467 } else {
2468 dol_print_error($this->db);
2469 $this->error = $this->db->lasterror();
2470 return -1;
2471 }
2472 }
2473
2474
2482 public function getNextNumRef($soc)
2483 {
2484 global $conf, $db, $langs;
2485 $langs->load("supplier_proposal");
2486
2487 if (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON')) {
2488 $mybool = false;
2489
2490 $file = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') . ".php";
2491 $classname = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
2492
2493 // Include file with class
2494 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2495 foreach ($dirmodels as $reldir) {
2496 $dir = dol_buildpath($reldir."core/modules/supplier_proposal/");
2497
2498 // Load file with numbering class (if found)
2499 $mybool = ((bool) @include_once $dir.$file) || $mybool;
2500 }
2501
2502 if (!$mybool) {
2503 dol_print_error(null, "Failed to include file ".$file);
2504 return '';
2505 }
2506
2507 $obj = new $classname();
2508 '@phan-var-force ModeleNumRefSupplierProposal $obj';
2509 $numref = "";
2510 $numref = $obj->getNextValue($soc, $this);
2511
2512 if ($numref != "") {
2513 return $numref;
2514 } else {
2515 $this->error = $obj->error;
2516 return "";
2517 }
2518 } else {
2519 $langs->load("errors");
2520 print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("SupplierProposal"));
2521 return "";
2522 }
2523 }
2524
2531 public function getTooltipContentArray($params)
2532 {
2533 global $conf, $langs, $menumanager;
2534
2535 $langs->load('supplier_proposal');
2536
2537 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2538 return ['optimize' => $langs->trans("ShowSupplierProposal")];
2539 }
2540
2541 $option = $params['option'] ?? '';
2542 $datas = [];
2543
2544 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("SupplierProposal").'</u>';
2545 if (isset($this->status)) {
2546 $datas['picto'] .= ' '.$this->getLibStatut(5);
2547 }
2548 if (!empty($this->ref)) {
2549 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2550 }
2551 if (!empty($this->ref_fourn)) {
2552 $datas['ref_supplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_fourn;
2553 }
2554 if (!empty($this->total_ht)) {
2555 $datas['amount_ht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2556 }
2557 if (!empty($this->total_tva)) {
2558 $datas['amount_vat'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2559 }
2560 if (!empty($this->total_ttc)) {
2561 $datas['amount_ttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2562 }
2563
2564 return $datas;
2565 }
2566
2578 public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2579 {
2580 global $langs, $conf, $user, $hookmanager;
2581
2582 if (!empty($conf->dol_no_mouse_hover)) {
2583 $notooltip = 1; // Force disable tooltips
2584 }
2585
2586 $url = '';
2587 $result = '';
2588 $params = [
2589 'id' => $this->id,
2590 'objecttype' => $this->element,
2591 'option' => $option,
2592 ];
2593 $classfortooltip = 'classfortooltip';
2594 $dataparams = '';
2595 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2596 $classfortooltip = 'classforajaxtooltip';
2597 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
2598 $label = '';
2599 } else {
2600 $label = implode($this->getTooltipContentArray($params));
2601 }
2602
2603 if ($option == '') {
2604 $url = DOL_URL_ROOT.'/supplier_proposal/card.php?id='.$this->id.$get_params;
2605 }
2606 if ($option == 'document') {
2607 $url = DOL_URL_ROOT.'/supplier_proposal/document.php?id='.$this->id.$get_params;
2608 }
2609
2610 if ($option !== 'nolink') {
2611 // Add param to save lastsearch_values or not
2612 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2613 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2614 $add_save_lastsearch_values = 1;
2615 }
2616 if ($add_save_lastsearch_values) {
2617 $url .= '&save_lastsearch_values=1';
2618 }
2619 }
2620
2621 $linkclose = '';
2622 if (empty($notooltip) && $user->hasRight('propal', 'lire')) {
2623 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2624 $label = $langs->trans("ShowSupplierProposal");
2625 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
2626 }
2627 $linkclose .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
2628 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
2629 }
2630
2631 $linkstart = '<a href="'.$url.'"';
2632 $linkstart .= $linkclose.'>';
2633 $linkend = '</a>';
2634
2635 $result .= $linkstart;
2636 if ($withpicto) {
2637 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
2638 }
2639 if ($withpicto != 2) {
2640 $result .= $this->ref;
2641 }
2642 $result .= $linkend;
2643
2644 if ($addlinktonotes) {
2645 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2646 if ($txttoshow) {
2647 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2648 $result .= ' <span class="note inline-block">';
2649 $result .= '<a href="'.DOL_URL_ROOT.'/supplier_proposal/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2650 $result .= img_picto('', 'note');
2651 $result .= '</a>';
2652 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
2653 //$result.='</a>';
2654 $result .= '</span>';
2655 }
2656 }
2657 global $action;
2658 $hookmanager->initHooks(array($this->element . 'dao'));
2659 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
2660 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2661 if ($reshook > 0) {
2662 $result = $hookmanager->resPrint;
2663 } else {
2664 $result .= $hookmanager->resPrint;
2665 }
2666 return $result;
2667 }
2668
2674 public function getLinesArray()
2675 {
2676 // For other object, here we call fetch_lines. But fetch_lines does not exists on supplier proposal
2677
2678 $sql = 'SELECT pt.rowid, pt.label as custom_label, pt.description, pt.fk_product, pt.fk_remise_except,';
2679 $sql .= ' pt.qty, pt.tva_tx, pt.vat_src_code, pt.remise_percent, pt.subprice, pt.info_bits,';
2680 $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,';
2681 $sql .= ' pt.product_type, pt.rang, pt.fk_parent_line, pt.extraparams,';
2682 $sql .= ' p.label as product_label, p.ref, p.fk_product_type, p.rowid as prodid,';
2683 $sql .= ' p.description as product_desc, pt.ref_fourn as ref_supplier,';
2684 $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';
2685 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pt';
2686 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pt.fk_product=p.rowid';
2687 $sql .= ' WHERE pt.fk_supplier_proposal = '.((int) $this->id);
2688 $sql .= ' ORDER BY pt.rang ASC, pt.rowid';
2689
2690 dol_syslog(get_class($this).'::getLinesArray', LOG_DEBUG);
2691 $resql = $this->db->query($sql);
2692 if ($resql) {
2693 $num = $this->db->num_rows($resql);
2694 $i = 0;
2695
2696 while ($i < $num) {
2697 $obj = $this->db->fetch_object($resql);
2698
2699 $this->lines[$i] = new SupplierProposalLine($this->db);
2700 $this->lines[$i]->id = $obj->rowid; // for backward compatibility
2701 $this->lines[$i]->rowid = $obj->rowid;
2702 $this->lines[$i]->fetch_optionals();
2703 $this->lines[$i]->label = $obj->custom_label;
2704 $this->lines[$i]->description = $obj->description;
2705 $this->lines[$i]->fk_product = $obj->fk_product;
2706 $this->lines[$i]->ref = $obj->ref;
2707 $this->lines[$i]->product_label = $obj->product_label;
2708 $this->lines[$i]->product_desc = $obj->product_desc;
2709 $this->lines[$i]->fk_product_type = $obj->fk_product_type; // deprecated
2710 $this->lines[$i]->product_type = $obj->product_type;
2711 $this->lines[$i]->qty = $obj->qty;
2712 $this->lines[$i]->subprice = $obj->subprice;
2713 $this->lines[$i]->fk_remise_except = $obj->fk_remise_except;
2714 $this->lines[$i]->remise_percent = $obj->remise_percent;
2715 $this->lines[$i]->tva_tx = $obj->tva_tx;
2716 $this->lines[$i]->vat_src_code = $obj->vat_src_code;
2717 $this->lines[$i]->info_bits = $obj->info_bits;
2718 $this->lines[$i]->total_ht = $obj->total_ht;
2719 $this->lines[$i]->total_tva = $obj->total_tva;
2720 $this->lines[$i]->total_ttc = $obj->total_ttc;
2721 $this->lines[$i]->fk_fournprice = $obj->fk_fournprice;
2722 $marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->lines[$i]->fk_fournprice, $obj->pa_ht);
2723 $this->lines[$i]->pa_ht = $marginInfos[0];
2724 $this->lines[$i]->marge_tx = $marginInfos[1];
2725 $this->lines[$i]->marque_tx = $marginInfos[2];
2726 $this->lines[$i]->fk_parent_line = $obj->fk_parent_line;
2727 $this->lines[$i]->special_code = $obj->special_code;
2728 $this->lines[$i]->rang = $obj->rang;
2729
2730 $this->lines[$i]->ref_fourn = $obj->ref_supplier; // deprecated
2731 $this->lines[$i]->ref_supplier = $obj->ref_supplier;
2732
2733 $this->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
2734
2735 // Multicurrency
2736 $this->lines[$i]->fk_multicurrency = $obj->fk_multicurrency;
2737 $this->lines[$i]->multicurrency_code = $obj->multicurrency_code;
2738 $this->lines[$i]->multicurrency_subprice = $obj->multicurrency_subprice;
2739 $this->lines[$i]->multicurrency_total_ht = $obj->multicurrency_total_ht;
2740 $this->lines[$i]->multicurrency_total_tva = $obj->multicurrency_total_tva;
2741 $this->lines[$i]->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
2742 $this->lines[$i]->fk_unit = $obj->fk_unit;
2743
2744 $i++;
2745 }
2746 $this->db->free($resql);
2747
2748 return 1;
2749 } else {
2750 $this->error = $this->db->error();
2751 return -1;
2752 }
2753 }
2754
2766 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2767 {
2768 global $langs;
2769
2770 $langs->load("supplier_proposal");
2771 $outputlangs->load("products");
2772
2773 if (!dol_strlen($modele)) {
2774 $modele = ''; // On supplier documents, template can be empty( no doc generated in this case)
2775
2776 if ($this->model_pdf) {
2777 $modele = $this->model_pdf;
2778 } elseif (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF')) {
2779 $modele = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF');
2780 }
2781 }
2782
2783 if (empty($modele)) {
2784 return 1;
2785 }
2786
2787 $modelpath = "core/modules/supplier_proposal/doc/";
2788
2789 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2790 }
2791
2792
2801 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2802 {
2803 $tables = array(
2804 'supplier_proposal'
2805 );
2806
2807 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2808 }
2809
2818 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
2819 {
2820 $tables = array(
2821 'supplier_proposaldet'
2822 );
2823
2824 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
2825 }
2826
2827
2835 public function getKanbanView($option = '', $arraydata = null)
2836 {
2837 global $langs;
2838
2839 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2840
2841 $return = '<div class="box-flex-item box-flex-grow-zero">';
2842 $return .= '<div class="info-box info-box-sm">';
2843 $return .= '<span class="info-box-icon bg-infobox-action">';
2844 $return .= img_picto('', $this->picto);
2845 //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2846 $return .= '</span>';
2847 $return .= '<div class="info-box-content">';
2848 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
2849 if ($selected >= 0) {
2850 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2851 }
2852 if (property_exists($this, 'socid')) {
2853 $return .= '<span class="info-box-ref"> | '.$this->socid.'</span>';
2854 }
2855 if (property_exists($this, 'delivery_date')) {
2856 $return .= '<br><span class="opacitymedium">'.$langs->trans("DateEnd").'</span> : <span class="info-box-label">'.dol_print_date($this->delivery_date).'</span>';
2857 }
2858 if (property_exists($this, 'total_ttc')) {
2859 $return .= '<br><span class="opacitymedium" >'.$langs->trans("AmountHT").' : </span><span class="info-box-label amount">'.price($this->total_ttc).'</span>';
2860 }
2861 if (method_exists($this, 'getLibStatut')) {
2862 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
2863 }
2864 $return .= '</div>';
2865 $return .= '</div>';
2866 $return .= '</div>';
2867 return $return;
2868 }
2869}
2870
2871
2876{
2880 public $db;
2881
2885 public $error = '';
2886
2890 public $element = 'supplier_proposaldet';
2891
2895 public $table_element = 'supplier_proposaldet';
2896
2900 public $parent_element = 'supplier_proposal';
2901
2905 public $fk_parent_attribute = 'fk_supplier_proposal';
2906
2910 public $oldline;
2911
2915 public $id;
2916
2920 public $fk_supplier_proposal;
2921
2925 public $fk_parent_line;
2926
2930 public $desc; // Description ligne
2931
2935 public $fk_product; // Id produit predefini
2936
2942 public $fk_product_type;
2948 public $product_type = Product::TYPE_PRODUCT;
2949
2953 public $qty;
2957 public $tva_tx;
2961 public $vat_src_code;
2962
2967 public $subprice;
2971 public $remise_percent;
2972
2976 public $fk_remise_except;
2977
2981 public $rang = 0;
2982
2986 public $fk_fournprice;
2987
2991 public $pa_ht;
2995 public $marge_tx;
2999 public $marque_tx;
3000
3004 public $special_code; // Tag for special lines (exclusive tags)
3005 // 1: frais de port
3006 // 2: ecotaxe
3007 // 3: option line (when qty = 0)
3008
3012 public $info_bits = 0; // Liste d'options cumulables:
3013 // Bit 0: 0 si TVA normal - 1 if TVA NPR
3014 // Bit 1: 0 ligne normal - 1 if fixed reduction
3015
3019 public $total_ht; // Total HT de la ligne toute quantite et incluant la remise ligne
3023 public $total_tva; // Total TVA de la ligne toute quantite et incluant la remise ligne
3027 public $total_ttc; // Total TTC de la ligne toute quantite et incluant la remise ligne
3028
3032 public $date_start;
3036 public $date_end;
3037
3038 // From llx_product
3044 public $ref;
3045
3050 public $product_ref;
3051
3057 public $libelle;
3058
3063 public $product_label;
3064
3069 public $label;
3070
3075 public $product_desc;
3076
3080 public $localtax1_tx; // Local tax 1
3084 public $localtax2_tx; // Local tax 2
3088 public $localtax1_type; // Local tax 1 type
3092 public $localtax2_type; // Local tax 2 type
3096 public $total_localtax1; // Line total local tax 1
3100 public $total_localtax2; // Line total local tax 2
3101
3105 public $skip_update_total; // Skip update price total for special lines
3106
3110 public $ref_fourn;
3114 public $ref_supplier;
3115
3116 // Multicurrency
3120 public $fk_multicurrency;
3121
3125 public $multicurrency_code;
3129 public $multicurrency_subprice;
3133 public $multicurrency_total_ht;
3137 public $multicurrency_total_tva;
3141 public $multicurrency_total_ttc;
3142
3148 public function __construct($db)
3149 {
3150 $this->db = $db;
3151 }
3152
3159 public function fetch($rowid)
3160 {
3161 $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,';
3162 $sql .= ' pd.date_start, pd.date_end,';
3163 $sql .= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice,';
3164 $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,';
3165 $sql .= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
3166 $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
3167 $sql .= ' pd.product_type, pd.ref_fourn as ref_produit_fourn, pd.extraparams,';
3168 $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';
3169 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pd';
3170 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
3171 $sql .= ' WHERE pd.rowid = '.((int) $rowid);
3172
3173 $result = $this->db->query($sql);
3174 if ($result) {
3175 if ($objp = $this->db->fetch_object($result)) {
3176 $this->id = $objp->rowid;
3177 $this->fk_supplier_proposal = $objp->fk_supplier_proposal;
3178 $this->fk_parent_line = $objp->fk_parent_line;
3179 $this->label = $objp->custom_label;
3180 $this->desc = $objp->description;
3181 $this->qty = $objp->qty;
3182 $this->subprice = $objp->subprice;
3183 $this->tva_tx = $objp->tva_tx;
3184 $this->remise_percent = $objp->remise_percent;
3185 $this->fk_remise_except = $objp->fk_remise_except;
3186 $this->fk_product = $objp->fk_product;
3187 $this->info_bits = $objp->info_bits;
3188 $this->date_start = $this->db->jdate($objp->date_start);
3189 $this->date_end = $this->db->jdate($objp->date_end);
3190
3191 $this->total_ht = $objp->total_ht;
3192 $this->total_tva = $objp->total_tva;
3193 $this->total_ttc = $objp->total_ttc;
3194
3195 $this->fk_fournprice = $objp->fk_fournprice;
3196
3197 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
3198 $this->pa_ht = $marginInfos[0];
3199 $this->marge_tx = $marginInfos[1];
3200 $this->marque_tx = $marginInfos[2];
3201
3202 $this->special_code = $objp->special_code;
3203 $this->product_type = $objp->product_type;
3204 $this->rang = $objp->rang;
3205
3206 $this->ref = $objp->product_ref; // deprecated
3207 $this->product_ref = $objp->product_ref;
3208 $this->libelle = $objp->product_label; // deprecated
3209 $this->product_label = $objp->product_label;
3210 $this->product_desc = $objp->product_desc;
3211
3212 $this->ref_fourn = $objp->ref_produit_fourn;
3213
3214 $this->extraparams = !empty($objp->extraparams) ? (array) json_decode($objp->extraparams, true) : array();
3215
3216 // Multicurrency
3217 $this->fk_multicurrency = $objp->fk_multicurrency;
3218 $this->multicurrency_code = $objp->multicurrency_code;
3219 $this->multicurrency_subprice = $objp->multicurrency_subprice;
3220 $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3221 $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3222 $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3223 $this->fk_unit = $objp->fk_unit;
3224
3225 $this->db->free($result);
3226 return 1;
3227 }
3228 return 0;
3229 } else {
3230 dol_print_error($this->db);
3231 return -1;
3232 }
3233 }
3234
3241 public function insert($notrigger = 0)
3242 {
3243 global $conf, $langs, $user;
3244
3245 $error = 0;
3246
3247 dol_syslog(get_class($this)."::insert rang=".$this->rang);
3248
3249 // Clean parameters
3250 if (empty($this->tva_tx)) {
3251 $this->tva_tx = 0;
3252 }
3253 if (empty($this->vat_src_code)) {
3254 $this->vat_src_code = '';
3255 }
3256 if (empty($this->localtax1_tx)) {
3257 $this->localtax1_tx = 0;
3258 }
3259 if (empty($this->localtax2_tx)) {
3260 $this->localtax2_tx = 0;
3261 }
3262 if (empty($this->localtax1_type)) {
3263 $this->localtax1_type = '';
3264 }
3265 if (empty($this->localtax2_type)) {
3266 $this->localtax2_type = '';
3267 }
3268 if (empty($this->total_localtax1)) {
3269 $this->total_localtax1 = 0;
3270 }
3271 if (empty($this->total_localtax2)) {
3272 $this->total_localtax2 = 0;
3273 }
3274 if (empty($this->rang)) {
3275 $this->rang = 0;
3276 }
3277 if (empty($this->remise_percent)) {
3278 $this->remise_percent = 0;
3279 }
3280 if (empty($this->info_bits)) {
3281 $this->info_bits = 0;
3282 }
3283 if (empty($this->special_code)) {
3284 $this->special_code = 0;
3285 }
3286 if (empty($this->fk_parent_line)) {
3287 $this->fk_parent_line = 0;
3288 }
3289 if (empty($this->fk_fournprice)) {
3290 $this->fk_fournprice = 0;
3291 }
3292 if (empty($this->fk_unit)) {
3293 $this->fk_unit = 0;
3294 }
3295 if (empty($this->subprice)) {
3296 $this->subprice = 0;
3297 }
3298
3299 if (empty($this->pa_ht)) {
3300 $this->pa_ht = 0;
3301 }
3302
3303 // if buy price not defined, define buyprice as configured in margin admin
3304 if ($this->pa_ht == 0) {
3305 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3306 if ($result < 0) {
3307 return $result;
3308 } else {
3309 $this->pa_ht = $result;
3310 }
3311 }
3312
3313 // Check parameters
3314 if ($this->product_type < 0) {
3315 return -1;
3316 }
3317
3318 $this->db->begin();
3319
3320 // Insert line into database
3321 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'supplier_proposaldet';
3322 $sql .= ' (fk_supplier_proposal, fk_parent_line, label, description, fk_product, product_type,';
3323 $sql .= ' date_start, date_end,';
3324 $sql .= ' fk_remise_except, qty, tva_tx, vat_src_code, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3325 $sql .= ' subprice, remise_percent, ';
3326 $sql .= ' info_bits, ';
3327 $sql .= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
3328 $sql .= ' ref_fourn,';
3329 $sql .= ' fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc, fk_unit)';
3330 $sql .= " VALUES (".$this->fk_supplier_proposal.",";
3331 $sql .= " ".($this->fk_parent_line > 0 ? ((int) $this->fk_parent_line) : "null").",";
3332 $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
3333 $sql .= " '".$this->db->escape($this->desc)."',";
3334 $sql .= " ".($this->fk_product ? ((int) $this->fk_product) : "null").",";
3335 $sql .= " '".$this->db->escape((string) $this->product_type)."',";
3336 $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3337 $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3338 $sql .= " ".($this->fk_remise_except ? ((int) $this->fk_remise_except) : "null").",";
3339 $sql .= " ".price2num($this->qty, 'MS').",";
3340 $sql .= " ".price2num($this->tva_tx).",";
3341 $sql .= " '".$this->db->escape($this->vat_src_code)."',";
3342 $sql .= " ".price2num($this->localtax1_tx).",";
3343 $sql .= " ".price2num($this->localtax2_tx).",";
3344 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3345 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3346 $sql .= " ".price2num($this->subprice, 'MU') .",";
3347 $sql .= " ".((float) $this->remise_percent).",";
3348 $sql .= " ".(isset($this->info_bits) ? ((int) $this->info_bits) : "null").",";
3349 $sql .= " ".price2num($this->total_ht, 'MT').",";
3350 $sql .= " ".price2num($this->total_tva, 'MT').",";
3351 $sql .= " ".price2num($this->total_localtax1, 'MT').",";
3352 $sql .= " ".price2num($this->total_localtax2, 'MT').",";
3353 $sql .= " ".price2num($this->total_ttc, 'MT').",";
3354 $sql .= " ".(!empty($this->fk_fournprice) ? ((int) $this->fk_fournprice) : "null").",";
3355 $sql .= " ".(isset($this->pa_ht) ? price2num($this->pa_ht, 'MU') : "null").",";
3356 $sql .= ' '.((int) $this->special_code).',';
3357 $sql .= ' '.((int) $this->rang).',';
3358 $sql .= " '".$this->db->escape($this->ref_fourn)."'";
3359 $sql .= ", ".($this->fk_multicurrency > 0 ? ((int) $this->fk_multicurrency) : 'null');
3360 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3361 $sql .= ", ".price2num($this->multicurrency_subprice, 'CU');
3362 $sql .= ", ".price2num($this->multicurrency_total_ht, 'CT');
3363 $sql .= ", ".price2num($this->multicurrency_total_tva, 'CT');
3364 $sql .= ", ".price2num($this->multicurrency_total_ttc, 'CT');
3365 $sql .= ", ".($this->fk_unit ? ((int) $this->fk_unit) : 'null');
3366 $sql .= ')';
3367
3368 dol_syslog(get_class($this).'::insert', LOG_DEBUG);
3369 $resql = $this->db->query($sql);
3370 if ($resql) {
3371 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'supplier_proposaldet');
3372
3373 if (!$error) {
3374 $result = $this->insertExtraFields();
3375 if ($result < 0) {
3376 $error++;
3377 }
3378 }
3379
3380 if (!$error && !$notrigger) {
3381 // Call trigger
3382 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_INSERT', $user);
3383 if ($result < 0) {
3384 $this->db->rollback();
3385 return -1;
3386 }
3387 // End call triggers
3388 }
3389
3390 $this->db->commit();
3391 return 1;
3392 } else {
3393 $this->error = $this->db->error()." sql=".$sql;
3394 $this->db->rollback();
3395 return -1;
3396 }
3397 }
3398
3405 public function delete($user)
3406 {
3407 $error = 0;
3408
3409 $this->db->begin();
3410
3411 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet";
3412 $sql .= " WHERE rowid = ".((int) $this->id);
3413
3414 if ($this->db->query($sql)) {
3415 // Remove extrafields
3416 if (!$error) {
3417 $result = $this->deleteExtraFields();
3418 if ($result < 0) {
3419 $error++;
3420 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3421 }
3422 }
3423
3424 // Call trigger
3425 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_DELETE', $user);
3426 if ($result < 0) {
3427 $this->db->rollback();
3428 return -1;
3429 }
3430 // End call triggers
3431
3432 $this->db->commit();
3433
3434 return 1;
3435 } else {
3436 $this->error = $this->db->error()." sql=".$sql;
3437 $this->db->rollback();
3438 return -1;
3439 }
3440 }
3441
3448 public function update($notrigger = 0)
3449 {
3450 global $conf, $langs, $user;
3451
3452 $error = 0;
3453
3454 // Clean parameters
3455 if (empty($this->tva_tx)) {
3456 $this->tva_tx = 0;
3457 }
3458 if (empty($this->localtax1_tx)) {
3459 $this->localtax1_tx = 0;
3460 }
3461 if (empty($this->localtax2_tx)) {
3462 $this->localtax2_tx = 0;
3463 }
3464 if (empty($this->total_localtax1)) {
3465 $this->total_localtax1 = 0;
3466 }
3467 if (empty($this->total_localtax2)) {
3468 $this->total_localtax2 = 0;
3469 }
3470 if (empty($this->localtax1_type)) {
3471 $this->localtax1_type = '';
3472 }
3473 if (empty($this->localtax2_type)) {
3474 $this->localtax2_type = '';
3475 }
3476 if (empty($this->marque_tx)) {
3477 $this->marque_tx = 0;
3478 }
3479 if (empty($this->marge_tx)) {
3480 $this->marge_tx = 0;
3481 }
3482 if (empty($this->remise_percent)) {
3483 $this->remise_percent = 0;
3484 }
3485 if (empty($this->info_bits)) {
3486 $this->info_bits = 0;
3487 }
3488 if (empty($this->special_code)) {
3489 $this->special_code = 0;
3490 }
3491 if (empty($this->fk_parent_line)) {
3492 $this->fk_parent_line = 0;
3493 }
3494 if (empty($this->fk_fournprice)) {
3495 $this->fk_fournprice = 0;
3496 }
3497 if (empty($this->fk_unit)) {
3498 $this->fk_unit = 0;
3499 }
3500 if (empty($this->subprice)) {
3501 $this->subprice = 0;
3502 }
3503
3504 if (empty($this->pa_ht)) {
3505 $this->pa_ht = 0;
3506 }
3507
3508 // if buy price not defined, define buyprice as configured in margin admin
3509 if ($this->pa_ht == 0) {
3510 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3511 if ($result < 0) {
3512 return $result;
3513 } else {
3514 $this->pa_ht = $result;
3515 }
3516 }
3517
3518 $this->db->begin();
3519
3520 // Mise a jour ligne en base
3521 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3522 $sql .= " description='".$this->db->escape($this->desc)."'";
3523 $sql .= " , label=".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
3524 $sql .= " , product_type=".((int) $this->product_type);
3525 $sql .= " , date_start=".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null");
3526 $sql .= " , date_end=".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null");
3527 $sql .= " , tva_tx='".price2num($this->tva_tx)."'";
3528 $sql .= " , localtax1_tx=".price2num($this->localtax1_tx);
3529 $sql .= " , localtax2_tx=".price2num($this->localtax2_tx);
3530 $sql .= " , localtax1_type='".$this->db->escape($this->localtax1_type)."'";
3531 $sql .= " , localtax2_type='".$this->db->escape($this->localtax2_type)."'";
3532 $sql .= " , qty='".price2num($this->qty)."'";
3533 $sql .= " , subprice=".price2num($this->subprice);
3534 $sql .= " , remise_percent=".price2num($this->remise_percent);
3535 $sql .= " , info_bits='".$this->db->escape((string) $this->info_bits)."'";
3536 if (empty($this->skip_update_total)) {
3537 $sql .= " , total_ht=".price2num($this->total_ht);
3538 $sql .= " , total_tva=".price2num($this->total_tva);
3539 $sql .= " , total_ttc=".price2num($this->total_ttc);
3540 $sql .= " , total_localtax1=".price2num($this->total_localtax1);
3541 $sql .= " , total_localtax2=".price2num($this->total_localtax2);
3542 }
3543 $sql .= " , fk_product_fournisseur_price=".(!empty($this->fk_fournprice) ? "'".$this->db->escape((string) $this->fk_fournprice)."'" : "null");
3544 $sql .= " , buy_price_ht=".price2num($this->pa_ht);
3545 $sql .= " , special_code=".((int) $this->special_code);
3546 $sql .= " , fk_parent_line=".($this->fk_parent_line > 0 ? $this->fk_parent_line : "null");
3547 if (!empty($this->rang)) {
3548 $sql .= ", rang=".((int) $this->rang);
3549 }
3550 $sql .= " , ref_fourn=".(!empty($this->ref_fourn) ? "'".$this->db->escape($this->ref_fourn)."'" : "null");
3551 $sql .= " , fk_unit=".($this->fk_unit ? $this->fk_unit : 'null');
3552
3553 // Multicurrency
3554 $sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice);
3555 $sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
3556 $sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
3557 $sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
3558
3559 $sql .= " WHERE rowid = ".((int) $this->id);
3560
3561 dol_syslog(get_class($this)."::update", LOG_DEBUG);
3562 $resql = $this->db->query($sql);
3563 if ($resql) {
3564 if (!$error) {
3565 $result = $this->insertExtraFields();
3566 if ($result < 0) {
3567 $error++;
3568 }
3569 }
3570
3571 if (!$error && !$notrigger) {
3572 // Call trigger
3573 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_MODIFY', $user);
3574 if ($result < 0) {
3575 $this->db->rollback();
3576 return -1;
3577 }
3578 // End call triggers
3579 }
3580
3581 $this->db->commit();
3582 return 1;
3583 } else {
3584 $this->error = $this->db->error();
3585 $this->db->rollback();
3586 return -2;
3587 }
3588 }
3589
3590 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3597 public function update_total()
3598 {
3599 // phpcs:enable
3600 $this->db->begin();
3601
3602 // Mise a jour ligne en base
3603 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3604 $sql .= " total_ht=".price2num($this->total_ht, 'MT');
3605 $sql .= ",total_tva=".price2num($this->total_tva, 'MT');
3606 $sql .= ",total_ttc=".price2num($this->total_ttc, 'MT');
3607 $sql .= " WHERE rowid = ".((int) $this->id);
3608
3609 dol_syslog("SupplierProposalLine::update_total", LOG_DEBUG);
3610
3611 $resql = $this->db->query($sql);
3612 if ($resql) {
3613 $this->db->commit();
3614 return 1;
3615 } else {
3616 $this->error = $this->db->error();
3617 $this->db->rollback();
3618 return -2;
3619 }
3620 }
3621}
$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