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 // Retrieve all extrafield
1340 // fetch optionals attributes and labels
1341 $this->fetch_optionals();
1342
1343 $this->db->free($resql);
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 $this->lines[$i] = $line;
1420
1421 $i++;
1422 }
1423 $this->db->free($result);
1424 } else {
1425 $this->error = $this->db->error();
1426 return -1;
1427 }
1428
1429 // Retrieve all extrafield
1430 // fetch optionals attributes and labels
1431 $this->fetch_optionals();
1432
1433 return 1;
1434 }
1435
1436 $this->error = "Record Not Found";
1437 return 0;
1438 } else {
1439 $this->error = $this->db->error();
1440 return -1;
1441 }
1442 }
1443
1451 public function valid($user, $notrigger = 0)
1452 {
1453 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1454
1455 global $conf, $langs;
1456
1457 $error = 0;
1458 $now = dol_now();
1459
1460 if ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'creer'))
1461 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'validate_advance'))) {
1462 $this->db->begin();
1463
1464 // Numbering module definition
1465 $soc = new Societe($this->db);
1466 $result = $soc->fetch($this->socid);
1467
1468 if ($result < 0) {
1469 return -1;
1470 }
1471
1472 // Define new ref
1473 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1474 $num = $this->getNextNumRef($soc);
1475 } else {
1476 $num = $this->ref;
1477 }
1478 $this->newref = dol_sanitizeFileName($num);
1479
1480 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1481 $sql .= " SET ref = '".$this->db->escape($num)."',";
1482 $sql .= " fk_statut = 1, date_valid='".$this->db->idate($now)."', fk_user_valid=".((int) $user->id);
1483 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1484
1485 dol_syslog(get_class($this)."::valid", LOG_DEBUG);
1486 $resql = $this->db->query($sql);
1487 if (!$resql) {
1488 dol_print_error($this->db);
1489 $error++;
1490 }
1491
1492 // Trigger calls
1493 if (!$error && !$notrigger) {
1494 // Call trigger
1495 $result = $this->call_trigger('PROPOSAL_SUPPLIER_VALIDATE', $user);
1496 if ($result < 0) {
1497 $error++;
1498 }
1499 // End call triggers
1500 }
1501
1502 if (!$error) {
1503 $this->oldref = $this->ref;
1504
1505 // Rename directory if dir was a temporary ref
1506 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1507 // Now we rename also files into index
1508 $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)."'";
1509 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1510 $resql = $this->db->query($sql);
1511 if (!$resql) {
1512 $error++;
1513 $this->error = $this->db->lasterror();
1514 }
1515 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'supplier_proposal/".$this->db->escape($this->newref)."'";
1516 $sql .= " WHERE filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1517 $resql = $this->db->query($sql);
1518 if (!$resql) {
1519 $error++;
1520 $this->error = $this->db->lasterror();
1521 }
1522
1523 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1524 $oldref = dol_sanitizeFileName($this->ref);
1525 $newref = dol_sanitizeFileName($num);
1526 $dirsource = $conf->supplier_proposal->dir_output.'/'.$oldref;
1527 $dirdest = $conf->supplier_proposal->dir_output.'/'.$newref;
1528 if (!$error && file_exists($dirsource)) {
1529 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
1530 if (@rename($dirsource, $dirdest)) {
1531 dol_syslog("Rename ok");
1532 // Rename docs starting with $oldref with $newref
1533 $listoffiles = dol_dir_list($conf->supplier_proposal->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1534 foreach ($listoffiles as $fileentry) {
1535 $dirsource = $fileentry['name'];
1536 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1537 $dirsource = $fileentry['path'].'/'.$dirsource;
1538 $dirdest = $fileentry['path'].'/'.$dirdest;
1539 @rename($dirsource, $dirdest);
1540 }
1541 }
1542 }
1543 }
1544
1545 $this->ref = $num;
1546 $this->statut = self::STATUS_VALIDATED;
1548 $this->user_validation_id = $user->id;
1549 $this->datev = $now;
1550 $this->date_validation = $now;
1551
1552 if (getDolGlobalString('SUPPLIER_PROPOSAL_AUTOADD_USER_CONTACT')) {
1553 $result = $this->add_contact($user->id, 'SALESREPFOLL', 'internal', 1);
1554 if ($result < 0 && $result != -2) { // -2 means already exists
1555 $error++;
1556 }
1557 }
1558
1559 $this->db->commit();
1560 return 1;
1561 } else {
1562 $this->db->rollback();
1563 return -1;
1564 }
1565 } else {
1566 dol_syslog("You don't have permission to validate supplier proposal", LOG_WARNING);
1567 return -2;
1568 }
1569 }
1570
1571 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1580 public function set_date_livraison($user, $delivery_date)
1581 {
1582 // phpcs:enable
1583 return $this->setDeliveryDate($user, $delivery_date);
1584 }
1585
1593 public function setDeliveryDate($user, $delivery_date)
1594 {
1595 if ($user->hasRight('supplier_proposal', 'creer')) {
1596 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1597 $sql .= " SET date_livraison = ".(isDolTms($delivery_date) ? "'".$this->db->idate($delivery_date)."'" : 'null');
1598 $sql .= " WHERE rowid = ".((int) $this->id);
1599
1600 if ($this->db->query($sql)) {
1601 $this->delivery_date = $delivery_date;
1602 return 1;
1603 } else {
1604 $this->error = $this->db->error();
1605 dol_syslog(get_class($this)."::setDeliveryDate Erreur SQL");
1606 return -1;
1607 }
1608 }
1609 return 0;
1610 }
1611
1612 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1620 /*
1621 public function set_remise_percent($user, $remise)
1622 {
1623 // phpcs:enable
1624 $remise = trim($remise) ?trim($remise) : 0;
1625
1626 if ($user->hasRight('supplier_proposal', 'creer')) {
1627 $remise = price2num($remise, 2);
1628
1629 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal SET remise_percent = ".((float) $remise);
1630 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1631
1632 if ($this->db->query($sql)) {
1633 $this->remise_percent = ((float) $remise);
1634 $this->update_price(1);
1635 return 1;
1636 } else {
1637 $this->error = $this->db->error();
1638 return -1;
1639 }
1640 }
1641 return 0;
1642 }
1643 */
1644
1645 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1653 /*
1654 public function set_remise_absolue($user, $remise)
1655 {
1656 // phpcs:enable
1657 if (empty($remise)) {
1658 $remise = 0;
1659 }
1660
1661 $remise = price2num($remise);
1662
1663 if ($user->hasRight('supplier_proposal', 'creer')) {
1664 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1665 $sql .= " SET remise_absolue = ".((float) $remise);
1666 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1667
1668 if ($this->db->query($sql)) {
1669 $this->remise_absolue = $remise;
1670 $this->update_price(1);
1671 return 1;
1672 } else {
1673 $this->error = $this->db->error();
1674 return -1;
1675 }
1676 }
1677 return 0;
1678 }
1679 */
1680
1681
1691 public function reopen($user, $status, $note = '', $notrigger = 0)
1692 {
1693 $error = 0;
1694
1695 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1696 $sql .= " SET fk_statut = ".((int) $status).",";
1697 if (!empty($note)) {
1698 $sql .= " note_private = '".$this->db->escape($note)."',";
1699 }
1700 $sql .= " date_cloture = NULL, fk_user_cloture = NULL";
1701 $sql .= " WHERE rowid = ".((int) $this->id);
1702
1703 $this->db->begin();
1704
1705 dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
1706 $resql = $this->db->query($sql);
1707 if (!$resql) {
1708 $error++;
1709 $this->errors[] = "Error ".$this->db->lasterror();
1710 }
1711 if (!$error) {
1712 if (!$notrigger) {
1713 // Call trigger
1714 $result = $this->call_trigger('PROPOSAL_SUPPLIER_REOPEN', $user);
1715 if ($result < 0) {
1716 $error++;
1717 }
1718 // End call triggers
1719 }
1720 }
1721
1722 // Commit or rollback
1723 if ($error) {
1724 if (!empty($this->errors)) {
1725 foreach ($this->errors as $errmsg) {
1726 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1727 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1728 }
1729 }
1730 $this->db->rollback();
1731 return -1 * $error;
1732 } else {
1733 $this->statut = $status;
1734 $this->status = $status;
1735
1736 $this->db->commit();
1737 return 1;
1738 }
1739 }
1740
1741
1750 public function cloture($user, $status, $note)
1751 {
1752 global $langs, $conf;
1753 $hidedetails = 0;
1754 $hidedesc = 0;
1755 $hideref = 0;
1756
1757 $error = 0;
1758 $now = dol_now();
1759
1760 $this->db->begin();
1761
1762 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1763 $sql .= " SET fk_statut = ".((int) $status).", note_private = '".$this->db->escape($note)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".((int) $user->id);
1764 $sql .= " WHERE rowid = ".((int) $this->id);
1765
1766 $resql = $this->db->query($sql);
1767 if ($resql) {
1768 $this->statut = $status;
1769 $this->status = $status;
1770
1771 $modelpdf = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED', (empty($this->model_pdf) ? '' : $this->model_pdf));
1772 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_REFUSED';
1773
1774 if ($status == 2) {
1775 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_SIGNED';
1776 $modelpdf = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL', (empty($this->model_pdf) ? '' : $this->model_pdf));
1777
1778 if (getDolGlobalString('SUPPLIER_PROPOSAL_UPDATE_PRICE_ON_SUPPlIER_PROPOSAL')) { // TODO This option was not tested correctly. Error if product ref does not exists
1779 $result = $this->updateOrCreatePriceFournisseur($user);
1780 }
1781 }
1782 if ($status == 4) {
1783 $triggerName = 'PROPOSAL_SUPPLIER_CLASSIFY_BILLED';
1784 }
1785
1786 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1787 // Define output language
1788 $outputlangs = $langs;
1789 if (getDolGlobalInt('MAIN_MULTILANGS')) {
1790 $outputlangs = new Translate("", $conf);
1791 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
1792 $outputlangs->setDefaultLang($newlang);
1793 }
1794
1795 $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1796 }
1797
1798 // Call trigger
1799 $result = $this->call_trigger($triggerName, $user);
1800 if ($result < 0) {
1801 $error++;
1802 }
1803 // End call triggers
1804
1805 if (!$error) {
1806 $this->db->commit();
1807 return 1;
1808 } else {
1809 $this->db->rollback();
1810 return -1;
1811 }
1812 } else {
1813 $this->error = $this->db->lasterror();
1814 $this->errors[] = $this->db->lasterror();
1815 $this->db->rollback();
1816 return -1;
1817 }
1818 }
1819
1826 public function updateOrCreatePriceFournisseur($user)
1827 {
1828 global $conf;
1829
1830 dol_syslog(get_class($this)."::updateOrCreatePriceFournisseur", LOG_DEBUG);
1831 foreach ($this->lines as $product) {
1832 if ($product->subprice <= 0) {
1833 continue;
1834 }
1835 $productsupplier = new ProductFournisseur($this->db);
1836
1837 $multicurrency_tx = 1;
1838 $fk_multicurrency = 0;
1839
1840 if (empty($this->thirdparty)) {
1841 $this->fetch_thirdparty();
1842 }
1843
1844 $ref_fourn = $product->ref_fourn;
1845 if (empty($ref_fourn)) {
1846 $ref_fourn = $product->ref_supplier;
1847 }
1848 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1849 list($fk_multicurrency, $multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $product->multicurrency_code);
1850 }
1851 $productsupplier->id = $product->fk_product;
1852
1853 $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);
1854 }
1855
1856 return 1;
1857 }
1858
1867 public function updatePriceFournisseur($idProductFournPrice, $product, $user)
1868 {
1869 $price = price2num($product->subprice * $product->qty, 'MU');
1870 $unitPrice = price2num($product->subprice, 'MU');
1871
1872 $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);
1873
1874 $resql = $this->db->query($sql);
1875 if (!$resql) {
1876 $this->error = $this->db->error();
1877 $this->db->rollback();
1878 return -1;
1879 }
1880 return 1;
1881 }
1882
1890 public function createPriceFournisseur($product, $user)
1891 {
1892 global $conf;
1893
1894 $price = price2num($product->subprice * $product->qty, 'MU');
1895 $qty = price2num($product->qty);
1896 $unitPrice = price2num($product->subprice, 'MU');
1897
1898 $now = dol_now();
1899
1900 $values = array(
1901 "'".$this->db->idate($now)."'",
1902 (int) $product->fk_product,
1903 (int) $this->thirdparty->id,
1904 "'".$this->db->escape($product->ref_fourn)."'",
1905 (float) $price,
1906 (float) $qty,
1907 (float) $unitPrice,
1908 (float) $product->tva_tx,
1909 (int) $user->id
1910 );
1911 if (isModEnabled("multicurrency")) {
1912 if (!empty($product->multicurrency_code)) {
1913 include_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
1914 $multicurrency = new MultiCurrency($this->db); //need to fetch because empty fk_multicurrency and rate
1915 $multicurrency->fetch(0, $product->multicurrency_code);
1916 if (!empty($multicurrency->id)) {
1917 $values[] = (int) $multicurrency->id;
1918 $values[] = "'".$this->db->escape($product->multicurrency_code)."'";
1919 $values[] = (float) $product->multicurrency_subprice;
1920 $values[] = (float) $product->multicurrency_total_ht;
1921 $values[] = (float) $multicurrency->rate->rate;
1922 } else {
1923 for ($i = 0; $i < 5; $i++) {
1924 $values[] = 'NULL';
1925 }
1926 }
1927 }
1928 }
1929
1930 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'product_fournisseur_price ';
1931 $sql .= '(datec, fk_product, fk_soc, ref_fourn, price, quantity, unitprice, tva_tx, fk_user';
1932 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1933 $sql .= ',fk_multicurrency, multicurrency_code, multicurrency_unitprice, multicurrency_price, multicurrency_tx';
1934 }
1935 $sql .= ') VALUES ('.implode(',', $values).')';
1936
1937 $resql = $this->db->query($sql);
1938 if (!$resql) {
1939 $this->error = $this->db->error();
1940 $this->db->rollback();
1941 return -1;
1942 }
1943 return 1;
1944 }
1945
1946 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1953 public function setDraft($user)
1954 {
1955 // phpcs:enable
1956 global $conf, $langs;
1957
1958 $error = 0;
1959
1960 if ($this->status == self::STATUS_DRAFT) {
1961 dol_syslog(get_class($this)."::setDraft already draft status", LOG_WARNING);
1962 return 0;
1963 }
1964
1965 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1966 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1967 $sql .= " WHERE rowid = ".((int) $this->id);
1968
1969 if ($this->db->query($sql)) {
1970 if (!$error) {
1971 $this->oldcopy = clone $this;
1972 }
1973
1974 if (!$error) {
1975 // Call trigger
1976 $result = $this->call_trigger('PROPOSAL_SUPPLIER_UNVALIDATE', $user);
1977 if ($result < 0) {
1978 $error++;
1979 }
1980 }
1981
1982 if (!$error) {
1983 $this->status = self::STATUS_DRAFT;
1984 $this->statut = self::STATUS_DRAFT; // deprecated
1985 $this->db->commit();
1986 return 1;
1987 } else {
1988 $this->db->rollback();
1989 return -1;
1990 }
1991 } else {
1992 return -1;
1993 }
1994 }
1995
1996
1997 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2011 public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datec', $sortorder = 'DESC')
2012 {
2013 // phpcs:enable
2014 global $user;
2015
2016 $ga = array();
2017
2018 $search_sale = 0;
2019 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2020 $search_sale = $user->id;
2021 }
2022
2023 $sql = "SELECT s.rowid, s.nom as name, s.client,";
2024 $sql .= " p.rowid as supplier_proposalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
2025 $sql .= " p.datep as dp, p.fin_validite as datelimite";
2026 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."supplier_proposal as p, ".MAIN_DB_PREFIX."c_propalst as c";
2027 $sql .= " WHERE p.entity IN (".getEntity('supplier_proposal').")";
2028 $sql .= " AND p.fk_soc = s.rowid";
2029 $sql .= " AND p.fk_statut = c.id";
2030 if ($socid) {
2031 $sql .= " AND s.rowid = ".((int) $socid);
2032 }
2033 if ($draft) {
2034 $sql .= " AND p.fk_statut = 0";
2035 }
2036 if ($notcurrentuser > 0) {
2037 $sql .= " AND p.fk_user_author <> ".((int) $user->id);
2038 }
2039 // Search on sale representative
2040 if ($search_sale && $search_sale != '-1') {
2041 if ($search_sale == -2) {
2042 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
2043 } elseif ($search_sale > 0) {
2044 $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).")";
2045 }
2046 }
2047 $sql .= $this->db->order($sortfield, $sortorder);
2048 $sql .= $this->db->plimit($limit, $offset);
2049
2050 $result = $this->db->query($sql);
2051 if ($result) {
2052 $num = $this->db->num_rows($result);
2053 if ($num) {
2054 $i = 0;
2055 while ($i < $num) {
2056 $obj = $this->db->fetch_object($result);
2057
2058 if ($shortlist == 1) {
2059 $ga[$obj->supplier_proposalid] = $obj->ref;
2060 } elseif ($shortlist == 2) {
2061 $ga[$obj->supplier_proposalid] = $obj->ref.' ('.$obj->name.')';
2062 } else {
2063 $ga[$i]['id'] = $obj->supplier_proposalid;
2064 $ga[$i]['ref'] = $obj->ref;
2065 $ga[$i]['name'] = $obj->name;
2066 }
2067
2068 $i++;
2069 }
2070 }
2071 return $ga;
2072 } else {
2073 dol_print_error($this->db);
2074 return -1;
2075 }
2076 }
2077
2085 public function delete($user, $notrigger = 0)
2086 {
2087 global $conf, $langs;
2088 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2089
2090 $error = 0;
2091
2092 $this->db->begin();
2093
2094 if (!$notrigger) {
2095 // Call trigger
2096 $result = $this->call_trigger('PROPOSAL_SUPPLIER_DELETE', $user);
2097 if ($result < 0) {
2098 $error++;
2099 }
2100 // End call triggers
2101 }
2102
2103 if (!$error) {
2104 $main = MAIN_DB_PREFIX.'supplier_proposaldet';
2105 $ef = $main."_extrafields";
2106 $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_supplier_proposal = ".((int) $this->id).")";
2107 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet WHERE fk_supplier_proposal = ".((int) $this->id);
2108 if ($this->db->query($sql)) {
2109 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposal WHERE rowid = ".((int) $this->id);
2110 if ($this->db->query($sqlef) && $this->db->query($sql)) {
2111 // Delete linked object
2112 $res = $this->deleteObjectLinked();
2113 if ($res < 0) {
2114 $error++;
2115 }
2116
2117 if (!$error) {
2118 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2119 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
2120 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
2121
2122 // We remove directory
2123 $ref = dol_sanitizeFileName($this->ref);
2124 if ($conf->supplier_proposal->dir_output && !empty($this->ref)) {
2125 $dir = $conf->supplier_proposal->dir_output."/".$ref;
2126 $file = $dir."/".$ref.".pdf";
2127 if (file_exists($file)) {
2128 dol_delete_preview($this);
2129
2130 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2131 $this->error = 'ErrorFailToDeleteFile';
2132 $this->errors = array('ErrorFailToDeleteFile');
2133 $this->db->rollback();
2134 return 0;
2135 }
2136 }
2137 if (file_exists($dir)) {
2138 $res = @dol_delete_dir_recursive($dir);
2139 if (!$res) {
2140 $this->error = 'ErrorFailToDeleteDir';
2141 $this->errors = array('ErrorFailToDeleteDir');
2142 $this->db->rollback();
2143 return 0;
2144 }
2145 }
2146 }
2147 }
2148
2149 // Removed extrafields
2150 if (!$error) {
2151 $result = $this->deleteExtraFields();
2152 if ($result < 0) {
2153 $error++;
2154 $errorflag = -4;
2155 dol_syslog(get_class($this)."::delete erreur ".$errorflag." ".$this->error, LOG_ERR);
2156 }
2157 }
2158
2159 if (!$error) {
2160 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
2161 $this->db->commit();
2162 return 1;
2163 } else {
2164 $this->error = $this->db->lasterror();
2165 $this->db->rollback();
2166 return 0;
2167 }
2168 } else {
2169 $this->error = $this->db->lasterror();
2170 $this->db->rollback();
2171 return -3;
2172 }
2173 } else {
2174 $this->error = $this->db->lasterror();
2175 $this->db->rollback();
2176 return -2;
2177 }
2178 } else {
2179 $this->db->rollback();
2180 return -1;
2181 }
2182 }
2183
2190 public function info($id)
2191 {
2192 $sql = "SELECT c.rowid, ";
2193 $sql .= " c.datec as date_creation, c.date_valid as date_validation, c.date_cloture as date_closure,";
2194 $sql .= " c.fk_user_author, c.fk_user_valid, c.fk_user_cloture";
2195 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as c";
2196 $sql .= " WHERE c.rowid = ".((int) $id);
2197
2198 $result = $this->db->query($sql);
2199
2200 if ($result) {
2201 if ($this->db->num_rows($result)) {
2202 $obj = $this->db->fetch_object($result);
2203
2204 $this->id = $obj->rowid;
2205
2206 $this->date_creation = $this->db->jdate($obj->date_creation);
2207 $this->date_validation = $this->db->jdate($obj->date_validation);
2208 $this->date_cloture = $this->db->jdate($obj->date_closure);
2209
2210 $this->user_creation_id = $obj->fk_user_author;
2211 $this->user_validation_id = $obj->fk_user_valid;
2212 $this->user_closing_id = $obj->fk_user_cloture;
2213 }
2214 $this->db->free($result);
2215 } else {
2216 dol_print_error($this->db);
2217 }
2218 }
2219
2220
2227 public function getLibStatut($mode = 0)
2228 {
2229 return $this->LibStatut((isset($this->status) ? $this->status : $this->statut), $mode);
2230 }
2231
2232 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2240 public function LibStatut($status, $mode = 1)
2241 {
2242 // phpcs:enable
2243
2244 // Init/load array of translation of status
2245 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
2246 global $langs;
2247 $langs->load("supplier_proposal");
2248 $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraft");
2249 $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidated");
2250 $this->labelStatus[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSigned");
2251 $this->labelStatus[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSigned");
2252 $this->labelStatus[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosed");
2253 $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraftShort");
2254 $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidatedShort");
2255 $this->labelStatusShort[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSignedShort");
2256 $this->labelStatusShort[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSignedShort");
2257 $this->labelStatusShort[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosedShort");
2258 }
2259
2260 $statusnew = '';
2261 if ($status == self::STATUS_DRAFT) {
2262 $statusnew = 'status0';
2263 } elseif ($status == self::STATUS_VALIDATED) {
2264 $statusnew = 'status1';
2265 } elseif ($status == self::STATUS_SIGNED) {
2266 $statusnew = 'status4';
2267 } elseif ($status == self::STATUS_NOTSIGNED) {
2268 $statusnew = 'status9';
2269 } elseif ($status == self::STATUS_CLOSE) {
2270 $statusnew = 'status6';
2271 }
2272
2273 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusnew, $mode);
2274 }
2275
2276
2277 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2285 public function load_board($user, $mode)
2286 {
2287 // phpcs:enable
2288 global $conf, $langs;
2289
2290 $now = dol_now();
2291
2292 $clause = " WHERE";
2293
2294 $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.date_cloture as datefin";
2295 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2296 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2297 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
2298 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2299 $clause = " AND";
2300 }
2301 $sql .= $clause." p.entity IN (".getEntity('supplier_proposal').")";
2302 if ($mode == 'opened') {
2303 $sql .= " AND p.fk_statut = 1";
2304 }
2305 if ($mode == 'signed') {
2306 $sql .= " AND p.fk_statut = 2";
2307 }
2308 if ($user->socid) {
2309 $sql .= " AND p.fk_soc = ".((int) $user->socid);
2310 }
2311
2312 $resql = $this->db->query($sql);
2313 if ($resql) {
2314 $label = $labelShort = '';
2315 $status = '';
2316 $delay_warning = 0;
2317 if ($mode == 'opened') {
2318 $delay_warning = !empty($conf->supplier_proposal->cloture->warning_delay) ? $conf->supplier_proposal->cloture->warning_delay : 0;
2319 $status = self::STATUS_VALIDATED;
2320 $label = $langs->trans("SupplierProposalsToClose");
2321 $labelShort = $langs->trans("ToAcceptRefuse");
2322 }
2323 if ($mode == 'signed') {
2324 $delay_warning = !empty($conf->supplier_proposal->facturation->warning_delay) ? $conf->supplier_proposal->facturation->warning_delay : 0;
2325 $status = self::STATUS_SIGNED;
2326 $label = $langs->trans("SupplierProposalsToProcess"); // May be billed or ordered
2327 $labelShort = $langs->trans("ToClose");
2328 }
2329
2330 $response = new WorkboardResponse();
2331 $response->warning_delay = $delay_warning / 60 / 60 / 24;
2332 $response->label = $label;
2333 $response->labelShort = $labelShort;
2334 $response->url = DOL_URL_ROOT.'/supplier_proposal/list.php?search_status='.$status;
2335 $response->img = img_object('', "propal");
2336
2337 // This assignment in condition is not a bug. It allows walking the results.
2338 while ($obj = $this->db->fetch_object($resql)) {
2339 $response->nbtodo++;
2340 if ($mode == 'opened') {
2341 $datelimit = $this->db->jdate($obj->datefin);
2342 if ($datelimit < ($now - $delay_warning)) {
2343 $response->nbtodolate++;
2344 }
2345 }
2346 // TODO Definir regle des propales a facturer en retard
2347 // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
2348 }
2349 return $response;
2350 } else {
2351 $this->error = $this->db->lasterror();
2352 return -1;
2353 }
2354 }
2355
2356
2364 public function initAsSpecimen()
2365 {
2366 global $user, $langs, $conf;
2367
2368 // Load array of products prodids
2369 $num_prods = 0;
2370 $prodids = array();
2371 $sql = "SELECT rowid";
2372 $sql .= " FROM ".MAIN_DB_PREFIX."product";
2373 $sql .= " WHERE entity IN (".getEntity('product').")";
2374 $sql .= $this->db->plimit(100);
2375
2376 $resql = $this->db->query($sql);
2377 if ($resql) {
2378 $num_prods = $this->db->num_rows($resql);
2379 $i = 0;
2380 while ($i < $num_prods) {
2381 $i++;
2382 $row = $this->db->fetch_row($resql);
2383 $prodids[$i] = $row[0];
2384 }
2385 }
2386
2387 // Initialise parameters
2388 $this->id = 0;
2389 $this->ref = 'SPECIMEN';
2390 $this->specimen = 1;
2391 $this->socid = 1;
2392 $this->date = time();
2393 $this->cond_reglement_id = 1;
2394 $this->cond_reglement_code = 'RECEP';
2395 $this->mode_reglement_id = 7;
2396 $this->mode_reglement_code = 'CHQ';
2397 $this->note_public = 'This is a comment (public)';
2398 $this->note_private = 'This is a comment (private)';
2399 // Lines
2400 $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)
2401 $xnbp = 0;
2402 while ($xnbp < $nbp) {
2403 $line = new SupplierProposalLine($this->db);
2404 $line->desc = $langs->trans("Description")." ".$xnbp;
2405 $line->qty = 1;
2406 $line->subprice = 100;
2407 $line->tva_tx = 19.6;
2408 $line->localtax1_tx = 0;
2409 $line->localtax2_tx = 0;
2410 if ($xnbp == 2) {
2411 $line->total_ht = 50;
2412 $line->total_ttc = 59.8;
2413 $line->total_tva = 9.8;
2414 $line->remise_percent = 50;
2415 } else {
2416 $line->total_ht = 100;
2417 $line->total_ttc = 119.6;
2418 $line->total_tva = 19.6;
2419 $line->remise_percent = 00;
2420 }
2421
2422 if ($num_prods > 0) {
2423 $prodid = mt_rand(1, $num_prods);
2424 $line->fk_product = $prodids[$prodid];
2425 }
2426
2427 $this->lines[$xnbp] = $line;
2428
2429 $this->total_ht += $line->total_ht;
2430 $this->total_tva += $line->total_tva;
2431 $this->total_ttc += $line->total_ttc;
2432
2433 $xnbp++;
2434 }
2435
2436 return 1;
2437 }
2438
2444 public function loadStateBoard()
2445 {
2446 global $conf, $user;
2447
2448 $this->nb = array();
2449 $clause = "WHERE";
2450
2451 $sql = "SELECT count(p.rowid) as nb";
2452 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2453 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
2454 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2455 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2456 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2457 $clause = "AND";
2458 }
2459 $sql .= " ".$clause." p.entity IN (".getEntity('supplier_proposal').")";
2460
2461 $resql = $this->db->query($sql);
2462 if ($resql) {
2463 // This assignment in condition is not a bug. It allows walking the results.
2464 while ($obj = $this->db->fetch_object($resql)) {
2465 $this->nb["supplier_proposals"] = $obj->nb;
2466 }
2467 $this->db->free($resql);
2468 return 1;
2469 } else {
2470 dol_print_error($this->db);
2471 $this->error = $this->db->lasterror();
2472 return -1;
2473 }
2474 }
2475
2476
2484 public function getNextNumRef($soc)
2485 {
2486 global $conf, $db, $langs;
2487 $langs->load("supplier_proposal");
2488
2489 if (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON')) {
2490 $mybool = false;
2491
2492 $file = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') . ".php";
2493 $classname = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
2494
2495 // Include file with class
2496 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2497 foreach ($dirmodels as $reldir) {
2498 $dir = dol_buildpath($reldir."core/modules/supplier_proposal/");
2499
2500 // Load file with numbering class (if found)
2501 $mybool = ((bool) @include_once $dir.$file) || $mybool;
2502 }
2503
2504 if (!$mybool) {
2505 dol_print_error(null, "Failed to include file ".$file);
2506 return '';
2507 }
2508
2509 $obj = new $classname();
2510 '@phan-var-force ModeleNumRefSupplierProposal $obj';
2511 $numref = "";
2512 $numref = $obj->getNextValue($soc, $this);
2513
2514 if ($numref != "") {
2515 return $numref;
2516 } else {
2517 $this->error = $obj->error;
2518 return "";
2519 }
2520 } else {
2521 $langs->load("errors");
2522 print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("SupplierProposal"));
2523 return "";
2524 }
2525 }
2526
2533 public function getTooltipContentArray($params)
2534 {
2535 global $conf, $langs, $menumanager;
2536
2537 $langs->load('supplier_proposal');
2538
2539 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2540 return ['optimize' => $langs->trans("ShowSupplierProposal")];
2541 }
2542
2543 $option = $params['option'] ?? '';
2544 $datas = [];
2545
2546 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("SupplierProposal").'</u>';
2547 if (isset($this->status)) {
2548 $datas['picto'] .= ' '.$this->getLibStatut(5);
2549 }
2550 if (!empty($this->ref)) {
2551 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2552 }
2553 if (!empty($this->ref_fourn)) {
2554 $datas['ref_supplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_fourn;
2555 }
2556 if (!empty($this->total_ht)) {
2557 $datas['amount_ht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2558 }
2559 if (!empty($this->total_tva)) {
2560 $datas['amount_vat'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2561 }
2562 if (!empty($this->total_ttc)) {
2563 $datas['amount_ttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2564 }
2565
2566 return $datas;
2567 }
2568
2580 public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2581 {
2582 global $langs, $conf, $user, $hookmanager;
2583
2584 if (!empty($conf->dol_no_mouse_hover)) {
2585 $notooltip = 1; // Force disable tooltips
2586 }
2587
2588 $url = '';
2589 $result = '';
2590 $params = [
2591 'id' => $this->id,
2592 'objecttype' => $this->element,
2593 'option' => $option,
2594 ];
2595 $classfortooltip = 'classfortooltip';
2596 $dataparams = '';
2597 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2598 $classfortooltip = 'classforajaxtooltip';
2599 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
2600 $label = '';
2601 } else {
2602 $label = implode($this->getTooltipContentArray($params));
2603 }
2604
2605 if ($option == '') {
2606 $url = DOL_URL_ROOT.'/supplier_proposal/card.php?id='.$this->id.$get_params;
2607 }
2608 if ($option == 'document') {
2609 $url = DOL_URL_ROOT.'/supplier_proposal/document.php?id='.$this->id.$get_params;
2610 }
2611
2612 if ($option !== 'nolink') {
2613 // Add param to save lastsearch_values or not
2614 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2615 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2616 $add_save_lastsearch_values = 1;
2617 }
2618 if ($add_save_lastsearch_values) {
2619 $url .= '&save_lastsearch_values=1';
2620 }
2621 }
2622
2623 $linkclose = '';
2624 if (empty($notooltip) && $user->hasRight('propal', 'lire')) {
2625 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2626 $label = $langs->trans("ShowSupplierProposal");
2627 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
2628 }
2629 $linkclose .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
2630 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
2631 }
2632
2633 $linkstart = '<a href="'.$url.'"';
2634 $linkstart .= $linkclose.'>';
2635 $linkend = '</a>';
2636
2637 $result .= $linkstart;
2638 if ($withpicto) {
2639 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
2640 }
2641 if ($withpicto != 2) {
2642 $result .= $this->ref;
2643 }
2644 $result .= $linkend;
2645
2646 if ($addlinktonotes) {
2647 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2648 if ($txttoshow) {
2649 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2650 $result .= ' <span class="note inline-block">';
2651 $result .= '<a href="'.DOL_URL_ROOT.'/supplier_proposal/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2652 $result .= img_picto('', 'note');
2653 $result .= '</a>';
2654 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
2655 //$result.='</a>';
2656 $result .= '</span>';
2657 }
2658 }
2659 global $action;
2660 $hookmanager->initHooks(array($this->element . 'dao'));
2661 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
2662 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2663 if ($reshook > 0) {
2664 $result = $hookmanager->resPrint;
2665 } else {
2666 $result .= $hookmanager->resPrint;
2667 }
2668 return $result;
2669 }
2670
2676 public function getLinesArray()
2677 {
2678 // For other object, here we call fetch_lines. But fetch_lines does not exists on supplier proposal
2679
2680 $sql = 'SELECT pt.rowid, pt.label as custom_label, pt.description, pt.fk_product, pt.fk_remise_except,';
2681 $sql .= ' pt.qty, pt.tva_tx, pt.vat_src_code, pt.remise_percent, pt.subprice, pt.info_bits,';
2682 $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,';
2683 $sql .= ' pt.product_type, pt.rang, pt.fk_parent_line, pt.extraparams,';
2684 $sql .= ' p.label as product_label, p.ref, p.fk_product_type, p.rowid as prodid,';
2685 $sql .= ' p.description as product_desc, pt.ref_fourn as ref_supplier,';
2686 $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';
2687 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pt';
2688 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pt.fk_product=p.rowid';
2689 $sql .= ' WHERE pt.fk_supplier_proposal = '.((int) $this->id);
2690 $sql .= ' ORDER BY pt.rang ASC, pt.rowid';
2691
2692 dol_syslog(get_class($this).'::getLinesArray', LOG_DEBUG);
2693 $resql = $this->db->query($sql);
2694 if ($resql) {
2695 $num = $this->db->num_rows($resql);
2696 $i = 0;
2697
2698 while ($i < $num) {
2699 $obj = $this->db->fetch_object($resql);
2700
2701 $this->lines[$i] = new SupplierProposalLine($this->db);
2702 $this->lines[$i]->id = $obj->rowid; // for backward compatibility
2703 $this->lines[$i]->rowid = $obj->rowid;
2704 $this->lines[$i]->label = $obj->custom_label;
2705 $this->lines[$i]->description = $obj->description;
2706 $this->lines[$i]->fk_product = $obj->fk_product;
2707 $this->lines[$i]->ref = $obj->ref;
2708 $this->lines[$i]->product_label = $obj->product_label;
2709 $this->lines[$i]->product_desc = $obj->product_desc;
2710 $this->lines[$i]->fk_product_type = $obj->fk_product_type; // deprecated
2711 $this->lines[$i]->product_type = $obj->product_type;
2712 $this->lines[$i]->qty = $obj->qty;
2713 $this->lines[$i]->subprice = $obj->subprice;
2714 $this->lines[$i]->fk_remise_except = $obj->fk_remise_except;
2715 $this->lines[$i]->remise_percent = $obj->remise_percent;
2716 $this->lines[$i]->tva_tx = $obj->tva_tx;
2717 $this->lines[$i]->vat_src_code = $obj->vat_src_code;
2718 $this->lines[$i]->info_bits = $obj->info_bits;
2719 $this->lines[$i]->total_ht = $obj->total_ht;
2720 $this->lines[$i]->total_tva = $obj->total_tva;
2721 $this->lines[$i]->total_ttc = $obj->total_ttc;
2722 $this->lines[$i]->fk_fournprice = $obj->fk_fournprice;
2723 $marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->lines[$i]->fk_fournprice, $obj->pa_ht);
2724 $this->lines[$i]->pa_ht = $marginInfos[0];
2725 $this->lines[$i]->marge_tx = $marginInfos[1];
2726 $this->lines[$i]->marque_tx = $marginInfos[2];
2727 $this->lines[$i]->fk_parent_line = $obj->fk_parent_line;
2728 $this->lines[$i]->special_code = $obj->special_code;
2729 $this->lines[$i]->rang = $obj->rang;
2730
2731 $this->lines[$i]->ref_fourn = $obj->ref_supplier; // deprecated
2732 $this->lines[$i]->ref_supplier = $obj->ref_supplier;
2733
2734 $this->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
2735
2736 // Multicurrency
2737 $this->lines[$i]->fk_multicurrency = $obj->fk_multicurrency;
2738 $this->lines[$i]->multicurrency_code = $obj->multicurrency_code;
2739 $this->lines[$i]->multicurrency_subprice = $obj->multicurrency_subprice;
2740 $this->lines[$i]->multicurrency_total_ht = $obj->multicurrency_total_ht;
2741 $this->lines[$i]->multicurrency_total_tva = $obj->multicurrency_total_tva;
2742 $this->lines[$i]->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
2743 $this->lines[$i]->fk_unit = $obj->fk_unit;
2744
2745 $i++;
2746 }
2747 $this->db->free($resql);
2748
2749 return 1;
2750 } else {
2751 $this->error = $this->db->error();
2752 return -1;
2753 }
2754 }
2755
2767 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2768 {
2769 global $langs;
2770
2771 $langs->load("supplier_proposal");
2772 $outputlangs->load("products");
2773
2774 if (!dol_strlen($modele)) {
2775 $modele = ''; // On supplier documents, template can be empty( no doc generated in this case)
2776
2777 if ($this->model_pdf) {
2778 $modele = $this->model_pdf;
2779 } elseif (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF')) {
2780 $modele = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF');
2781 }
2782 }
2783
2784 if (empty($modele)) {
2785 return 1;
2786 }
2787
2788 $modelpath = "core/modules/supplier_proposal/doc/";
2789
2790 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2791 }
2792
2793
2802 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2803 {
2804 $tables = array(
2805 'supplier_proposal'
2806 );
2807
2808 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2809 }
2810
2819 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
2820 {
2821 $tables = array(
2822 'supplier_proposaldet'
2823 );
2824
2825 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
2826 }
2827
2828
2836 public function getKanbanView($option = '', $arraydata = null)
2837 {
2838 global $langs;
2839
2840 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2841
2842 $return = '<div class="box-flex-item box-flex-grow-zero">';
2843 $return .= '<div class="info-box info-box-sm">';
2844 $return .= '<span class="info-box-icon bg-infobox-action">';
2845 $return .= img_picto('', $this->picto);
2846 //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2847 $return .= '</span>';
2848 $return .= '<div class="info-box-content">';
2849 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
2850 if ($selected >= 0) {
2851 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2852 }
2853 if (property_exists($this, 'socid')) {
2854 $return .= '<span class="info-box-ref"> | '.$this->socid.'</span>';
2855 }
2856 if (property_exists($this, 'delivery_date')) {
2857 $return .= '<br><span class="opacitymedium">'.$langs->trans("DateEnd").'</span> : <span class="info-box-label">'.dol_print_date($this->delivery_date).'</span>';
2858 }
2859 if (property_exists($this, 'total_ttc')) {
2860 $return .= '<br><span class="opacitymedium" >'.$langs->trans("AmountHT").' : </span><span class="info-box-label amount">'.price($this->total_ttc).'</span>';
2861 }
2862 if (method_exists($this, 'getLibStatut')) {
2863 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
2864 }
2865 $return .= '</div>';
2866 $return .= '</div>';
2867 $return .= '</div>';
2868 return $return;
2869 }
2870}
2871
2872
2877{
2881 public $db;
2882
2886 public $error = '';
2887
2891 public $element = 'supplier_proposaldet';
2892
2896 public $table_element = 'supplier_proposaldet';
2897
2901 public $parent_element = 'supplier_proposal';
2902
2906 public $fk_parent_attribute = 'fk_supplier_proposal';
2907
2911 public $oldline;
2912
2916 public $id;
2917
2921 public $fk_supplier_proposal;
2922
2926 public $fk_parent_line;
2927
2931 public $desc; // Description ligne
2932
2936 public $fk_product; // Id produit predefini
2937
2943 public $fk_product_type;
2949 public $product_type = Product::TYPE_PRODUCT;
2950
2954 public $qty;
2958 public $tva_tx;
2962 public $vat_src_code;
2963
2968 public $subprice;
2972 public $remise_percent;
2973
2977 public $fk_remise_except;
2978
2982 public $rang = 0;
2983
2987 public $fk_fournprice;
2988
2992 public $pa_ht;
2996 public $marge_tx;
3000 public $marque_tx;
3001
3005 public $special_code; // Tag for special lines (exclusive tags)
3006 // 1: frais de port
3007 // 2: ecotaxe
3008 // 3: option line (when qty = 0)
3009
3013 public $info_bits = 0; // Liste d'options cumulables:
3014 // Bit 0: 0 si TVA normal - 1 if TVA NPR
3015 // Bit 1: 0 ligne normal - 1 if fixed reduction
3016
3020 public $total_ht; // Total HT de la ligne toute quantite et incluant la remise ligne
3024 public $total_tva; // Total TVA de la ligne toute quantite et incluant la remise ligne
3028 public $total_ttc; // Total TTC de la ligne toute quantite et incluant la remise ligne
3029
3033 public $date_start;
3037 public $date_end;
3038
3039 // From llx_product
3045 public $ref;
3046
3051 public $product_ref;
3052
3058 public $libelle;
3059
3064 public $product_label;
3065
3070 public $label;
3071
3076 public $product_desc;
3077
3081 public $localtax1_tx; // Local tax 1
3085 public $localtax2_tx; // Local tax 2
3089 public $localtax1_type; // Local tax 1 type
3093 public $localtax2_type; // Local tax 2 type
3097 public $total_localtax1; // Line total local tax 1
3101 public $total_localtax2; // Line total local tax 2
3102
3106 public $skip_update_total; // Skip update price total for special lines
3107
3111 public $ref_fourn;
3115 public $ref_supplier;
3116
3117 // Multicurrency
3121 public $fk_multicurrency;
3122
3126 public $multicurrency_code;
3130 public $multicurrency_subprice;
3134 public $multicurrency_total_ht;
3138 public $multicurrency_total_tva;
3142 public $multicurrency_total_ttc;
3143
3149 public function __construct($db)
3150 {
3151 $this->db = $db;
3152 }
3153
3160 public function fetch($rowid)
3161 {
3162 $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,';
3163 $sql .= ' pd.date_start, pd.date_end,';
3164 $sql .= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice,';
3165 $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,';
3166 $sql .= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
3167 $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
3168 $sql .= ' pd.product_type, pd.ref_fourn as ref_produit_fourn, pd.extraparams,';
3169 $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';
3170 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pd';
3171 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
3172 $sql .= ' WHERE pd.rowid = '.((int) $rowid);
3173
3174 $result = $this->db->query($sql);
3175 if ($result) {
3176 if ($objp = $this->db->fetch_object($result)) {
3177 $this->id = $objp->rowid;
3178 $this->fk_supplier_proposal = $objp->fk_supplier_proposal;
3179 $this->fk_parent_line = $objp->fk_parent_line;
3180 $this->label = $objp->custom_label;
3181 $this->desc = $objp->description;
3182 $this->qty = $objp->qty;
3183 $this->subprice = $objp->subprice;
3184 $this->tva_tx = $objp->tva_tx;
3185 $this->remise_percent = $objp->remise_percent;
3186 $this->fk_remise_except = $objp->fk_remise_except;
3187 $this->fk_product = $objp->fk_product;
3188 $this->info_bits = $objp->info_bits;
3189 $this->date_start = $this->db->jdate($objp->date_start);
3190 $this->date_end = $this->db->jdate($objp->date_end);
3191
3192 $this->total_ht = $objp->total_ht;
3193 $this->total_tva = $objp->total_tva;
3194 $this->total_ttc = $objp->total_ttc;
3195
3196 $this->fk_fournprice = $objp->fk_fournprice;
3197
3198 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
3199 $this->pa_ht = $marginInfos[0];
3200 $this->marge_tx = $marginInfos[1];
3201 $this->marque_tx = $marginInfos[2];
3202
3203 $this->special_code = $objp->special_code;
3204 $this->product_type = $objp->product_type;
3205 $this->rang = $objp->rang;
3206
3207 $this->ref = $objp->product_ref; // deprecated
3208 $this->product_ref = $objp->product_ref;
3209 $this->libelle = $objp->product_label; // deprecated
3210 $this->product_label = $objp->product_label;
3211 $this->product_desc = $objp->product_desc;
3212
3213 $this->ref_fourn = $objp->ref_produit_fourn;
3214
3215 $this->extraparams = !empty($objp->extraparams) ? (array) json_decode($objp->extraparams, true) : array();
3216
3217 // Multicurrency
3218 $this->fk_multicurrency = $objp->fk_multicurrency;
3219 $this->multicurrency_code = $objp->multicurrency_code;
3220 $this->multicurrency_subprice = $objp->multicurrency_subprice;
3221 $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3222 $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3223 $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3224 $this->fk_unit = $objp->fk_unit;
3225
3226 $this->db->free($result);
3227 return 1;
3228 }
3229 return 0;
3230 } else {
3231 dol_print_error($this->db);
3232 return -1;
3233 }
3234 }
3235
3242 public function insert($notrigger = 0)
3243 {
3244 global $conf, $langs, $user;
3245
3246 $error = 0;
3247
3248 dol_syslog(get_class($this)."::insert rang=".$this->rang);
3249
3250 // Clean parameters
3251 if (empty($this->tva_tx)) {
3252 $this->tva_tx = 0;
3253 }
3254 if (empty($this->vat_src_code)) {
3255 $this->vat_src_code = '';
3256 }
3257 if (empty($this->localtax1_tx)) {
3258 $this->localtax1_tx = 0;
3259 }
3260 if (empty($this->localtax2_tx)) {
3261 $this->localtax2_tx = 0;
3262 }
3263 if (empty($this->localtax1_type)) {
3264 $this->localtax1_type = '';
3265 }
3266 if (empty($this->localtax2_type)) {
3267 $this->localtax2_type = '';
3268 }
3269 if (empty($this->total_localtax1)) {
3270 $this->total_localtax1 = 0;
3271 }
3272 if (empty($this->total_localtax2)) {
3273 $this->total_localtax2 = 0;
3274 }
3275 if (empty($this->rang)) {
3276 $this->rang = 0;
3277 }
3278 if (empty($this->remise_percent)) {
3279 $this->remise_percent = 0;
3280 }
3281 if (empty($this->info_bits)) {
3282 $this->info_bits = 0;
3283 }
3284 if (empty($this->special_code)) {
3285 $this->special_code = 0;
3286 }
3287 if (empty($this->fk_parent_line)) {
3288 $this->fk_parent_line = 0;
3289 }
3290 if (empty($this->fk_fournprice)) {
3291 $this->fk_fournprice = 0;
3292 }
3293 if (empty($this->fk_unit)) {
3294 $this->fk_unit = 0;
3295 }
3296 if (empty($this->subprice)) {
3297 $this->subprice = 0;
3298 }
3299
3300 if (empty($this->pa_ht)) {
3301 $this->pa_ht = 0;
3302 }
3303
3304 // if buy price not defined, define buyprice as configured in margin admin
3305 if ($this->pa_ht == 0) {
3306 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3307 if ($result < 0) {
3308 return $result;
3309 } else {
3310 $this->pa_ht = $result;
3311 }
3312 }
3313
3314 // Check parameters
3315 if ($this->product_type < 0) {
3316 return -1;
3317 }
3318
3319 $this->db->begin();
3320
3321 // Insert line into database
3322 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'supplier_proposaldet';
3323 $sql .= ' (fk_supplier_proposal, fk_parent_line, label, description, fk_product, product_type,';
3324 $sql .= ' date_start, date_end,';
3325 $sql .= ' fk_remise_except, qty, tva_tx, vat_src_code, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3326 $sql .= ' subprice, remise_percent, ';
3327 $sql .= ' info_bits, ';
3328 $sql .= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
3329 $sql .= ' ref_fourn,';
3330 $sql .= ' fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc, fk_unit)';
3331 $sql .= " VALUES (".$this->fk_supplier_proposal.",";
3332 $sql .= " ".($this->fk_parent_line > 0 ? ((int) $this->fk_parent_line) : "null").",";
3333 $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
3334 $sql .= " '".$this->db->escape($this->desc)."',";
3335 $sql .= " ".($this->fk_product ? ((int) $this->fk_product) : "null").",";
3336 $sql .= " '".$this->db->escape((string) $this->product_type)."',";
3337 $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3338 $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3339 $sql .= " ".($this->fk_remise_except ? ((int) $this->fk_remise_except) : "null").",";
3340 $sql .= " ".price2num($this->qty, 'MS').",";
3341 $sql .= " ".price2num($this->tva_tx).",";
3342 $sql .= " '".$this->db->escape($this->vat_src_code)."',";
3343 $sql .= " ".price2num($this->localtax1_tx).",";
3344 $sql .= " ".price2num($this->localtax2_tx).",";
3345 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3346 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3347 $sql .= " ".price2num($this->subprice, 'MU') .",";
3348 $sql .= " ".((float) $this->remise_percent).",";
3349 $sql .= " ".(isset($this->info_bits) ? ((int) $this->info_bits) : "null").",";
3350 $sql .= " ".price2num($this->total_ht, 'MT').",";
3351 $sql .= " ".price2num($this->total_tva, 'MT').",";
3352 $sql .= " ".price2num($this->total_localtax1, 'MT').",";
3353 $sql .= " ".price2num($this->total_localtax2, 'MT').",";
3354 $sql .= " ".price2num($this->total_ttc, 'MT').",";
3355 $sql .= " ".(!empty($this->fk_fournprice) ? ((int) $this->fk_fournprice) : "null").",";
3356 $sql .= " ".(isset($this->pa_ht) ? price2num($this->pa_ht, 'MU') : "null").",";
3357 $sql .= ' '.((int) $this->special_code).',';
3358 $sql .= ' '.((int) $this->rang).',';
3359 $sql .= " '".$this->db->escape($this->ref_fourn)."'";
3360 $sql .= ", ".($this->fk_multicurrency > 0 ? ((int) $this->fk_multicurrency) : 'null');
3361 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3362 $sql .= ", ".price2num($this->multicurrency_subprice, 'CU');
3363 $sql .= ", ".price2num($this->multicurrency_total_ht, 'CT');
3364 $sql .= ", ".price2num($this->multicurrency_total_tva, 'CT');
3365 $sql .= ", ".price2num($this->multicurrency_total_ttc, 'CT');
3366 $sql .= ", ".($this->fk_unit ? ((int) $this->fk_unit) : 'null');
3367 $sql .= ')';
3368
3369 dol_syslog(get_class($this).'::insert', LOG_DEBUG);
3370 $resql = $this->db->query($sql);
3371 if ($resql) {
3372 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'supplier_proposaldet');
3373
3374 if (!$error) {
3375 $result = $this->insertExtraFields();
3376 if ($result < 0) {
3377 $error++;
3378 }
3379 }
3380
3381 if (!$error && !$notrigger) {
3382 // Call trigger
3383 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_INSERT', $user);
3384 if ($result < 0) {
3385 $this->db->rollback();
3386 return -1;
3387 }
3388 // End call triggers
3389 }
3390
3391 $this->db->commit();
3392 return 1;
3393 } else {
3394 $this->error = $this->db->error()." sql=".$sql;
3395 $this->db->rollback();
3396 return -1;
3397 }
3398 }
3399
3406 public function delete($user)
3407 {
3408 $error = 0;
3409
3410 $this->db->begin();
3411
3412 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet";
3413 $sql .= " WHERE rowid = ".((int) $this->id);
3414
3415 if ($this->db->query($sql)) {
3416 // Remove extrafields
3417 if (!$error) {
3418 $result = $this->deleteExtraFields();
3419 if ($result < 0) {
3420 $error++;
3421 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3422 }
3423 }
3424
3425 // Call trigger
3426 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_DELETE', $user);
3427 if ($result < 0) {
3428 $this->db->rollback();
3429 return -1;
3430 }
3431 // End call triggers
3432
3433 $this->db->commit();
3434
3435 return 1;
3436 } else {
3437 $this->error = $this->db->error()." sql=".$sql;
3438 $this->db->rollback();
3439 return -1;
3440 }
3441 }
3442
3449 public function update($notrigger = 0)
3450 {
3451 global $conf, $langs, $user;
3452
3453 $error = 0;
3454
3455 // Clean parameters
3456 if (empty($this->tva_tx)) {
3457 $this->tva_tx = 0;
3458 }
3459 if (empty($this->localtax1_tx)) {
3460 $this->localtax1_tx = 0;
3461 }
3462 if (empty($this->localtax2_tx)) {
3463 $this->localtax2_tx = 0;
3464 }
3465 if (empty($this->total_localtax1)) {
3466 $this->total_localtax1 = 0;
3467 }
3468 if (empty($this->total_localtax2)) {
3469 $this->total_localtax2 = 0;
3470 }
3471 if (empty($this->localtax1_type)) {
3472 $this->localtax1_type = '';
3473 }
3474 if (empty($this->localtax2_type)) {
3475 $this->localtax2_type = '';
3476 }
3477 if (empty($this->marque_tx)) {
3478 $this->marque_tx = 0;
3479 }
3480 if (empty($this->marge_tx)) {
3481 $this->marge_tx = 0;
3482 }
3483 if (empty($this->remise_percent)) {
3484 $this->remise_percent = 0;
3485 }
3486 if (empty($this->info_bits)) {
3487 $this->info_bits = 0;
3488 }
3489 if (empty($this->special_code)) {
3490 $this->special_code = 0;
3491 }
3492 if (empty($this->fk_parent_line)) {
3493 $this->fk_parent_line = 0;
3494 }
3495 if (empty($this->fk_fournprice)) {
3496 $this->fk_fournprice = 0;
3497 }
3498 if (empty($this->fk_unit)) {
3499 $this->fk_unit = 0;
3500 }
3501 if (empty($this->subprice)) {
3502 $this->subprice = 0;
3503 }
3504
3505 if (empty($this->pa_ht)) {
3506 $this->pa_ht = 0;
3507 }
3508
3509 // if buy price not defined, define buyprice as configured in margin admin
3510 if ($this->pa_ht == 0) {
3511 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3512 if ($result < 0) {
3513 return $result;
3514 } else {
3515 $this->pa_ht = $result;
3516 }
3517 }
3518
3519 $this->db->begin();
3520
3521 // Mise a jour ligne en base
3522 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3523 $sql .= " description='".$this->db->escape($this->desc)."'";
3524 $sql .= " , label=".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
3525 $sql .= " , product_type=".((int) $this->product_type);
3526 $sql .= " , date_start=".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null");
3527 $sql .= " , date_end=".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null");
3528 $sql .= " , tva_tx='".price2num($this->tva_tx)."'";
3529 $sql .= " , localtax1_tx=".price2num($this->localtax1_tx);
3530 $sql .= " , localtax2_tx=".price2num($this->localtax2_tx);
3531 $sql .= " , localtax1_type='".$this->db->escape($this->localtax1_type)."'";
3532 $sql .= " , localtax2_type='".$this->db->escape($this->localtax2_type)."'";
3533 $sql .= " , qty='".price2num($this->qty)."'";
3534 $sql .= " , subprice=".price2num($this->subprice);
3535 $sql .= " , remise_percent=".price2num($this->remise_percent);
3536 $sql .= " , info_bits='".$this->db->escape((string) $this->info_bits)."'";
3537 if (empty($this->skip_update_total)) {
3538 $sql .= " , total_ht=".price2num($this->total_ht);
3539 $sql .= " , total_tva=".price2num($this->total_tva);
3540 $sql .= " , total_ttc=".price2num($this->total_ttc);
3541 $sql .= " , total_localtax1=".price2num($this->total_localtax1);
3542 $sql .= " , total_localtax2=".price2num($this->total_localtax2);
3543 }
3544 $sql .= " , fk_product_fournisseur_price=".(!empty($this->fk_fournprice) ? "'".$this->db->escape((string) $this->fk_fournprice)."'" : "null");
3545 $sql .= " , buy_price_ht=".price2num($this->pa_ht);
3546 $sql .= " , special_code=".((int) $this->special_code);
3547 $sql .= " , fk_parent_line=".($this->fk_parent_line > 0 ? $this->fk_parent_line : "null");
3548 if (!empty($this->rang)) {
3549 $sql .= ", rang=".((int) $this->rang);
3550 }
3551 $sql .= " , ref_fourn=".(!empty($this->ref_fourn) ? "'".$this->db->escape($this->ref_fourn)."'" : "null");
3552 $sql .= " , fk_unit=".($this->fk_unit ? $this->fk_unit : 'null');
3553
3554 // Multicurrency
3555 $sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice);
3556 $sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
3557 $sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
3558 $sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
3559
3560 $sql .= " WHERE rowid = ".((int) $this->id);
3561
3562 dol_syslog(get_class($this)."::update", LOG_DEBUG);
3563 $resql = $this->db->query($sql);
3564 if ($resql) {
3565 if (!$error) {
3566 $result = $this->insertExtraFields();
3567 if ($result < 0) {
3568 $error++;
3569 }
3570 }
3571
3572 if (!$error && !$notrigger) {
3573 // Call trigger
3574 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_MODIFY', $user);
3575 if ($result < 0) {
3576 $this->db->rollback();
3577 return -1;
3578 }
3579 // End call triggers
3580 }
3581
3582 $this->db->commit();
3583 return 1;
3584 } else {
3585 $this->error = $this->db->error();
3586 $this->db->rollback();
3587 return -2;
3588 }
3589 }
3590
3591 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3598 public function update_total()
3599 {
3600 // phpcs:enable
3601 $this->db->begin();
3602
3603 // Mise a jour ligne en base
3604 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3605 $sql .= " total_ht=".price2num($this->total_ht, 'MT');
3606 $sql .= ",total_tva=".price2num($this->total_tva, 'MT');
3607 $sql .= ",total_ttc=".price2num($this->total_ttc, 'MT');
3608 $sql .= " WHERE rowid = ".((int) $this->id);
3609
3610 dol_syslog("SupplierProposalLine::update_total", LOG_DEBUG);
3611
3612 $resql = $this->db->query($sql);
3613 if ($resql) {
3614 $this->db->commit();
3615 return 1;
3616 } else {
3617 $this->error = $this->db->error();
3618 $this->db->rollback();
3619 return -2;
3620 }
3621 }
3622}
$object ref
Definition info.php:90
Parent class of all other business classes (invoices, contracts, proposals, orders,...
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
Save a new position (field rang) for details lines.
deleteEcmFiles($mode=0)
Delete related files of object in database.
update_price($exclspec=0, $roundingadjust='auto', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
add_object_linked($origin=null, $origin_id=null, $f_user=null, $notrigger=0)
Add an object link into llx_element_element.
defineBuyPrice($unitPrice=0.0, $discountPercent=0.0, $fk_product=0)
Get buy price to use for margin calculation.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty.
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid=0, $f_user=null, $notrigger=0)
Delete all links between an object $this.
setErrorsFromObject($object)
setErrorsFromObject
static isExistingObject($element, $id, $ref='', $ref_ext='')
Check if an object id or ref exists If you don't need or want to instantiate the object and just need...
updateRangOfLine($rowid, $rang)
Update position of line (rang)
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