dolibarr 21.0.0-alpha
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 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 public $lines = array();
199 public $line;
200
201 public $labelStatus = array();
202 public $labelStatusShort = array();
203
207 public $nbtodo;
211 public $nbtodolate;
212
213 // Multicurrency
217 public $fk_multicurrency;
218
222 public $multicurrency_code;
226 public $multicurrency_tx;
230 public $multicurrency_total_ht;
234 public $multicurrency_total_tva;
238 public $multicurrency_total_ttc;
239
243 const STATUS_DRAFT = 0;
244
249
253 const STATUS_SIGNED = 2;
254
259
263 const STATUS_CLOSE = 4;
264
265
266
274 public function __construct($db, $socid = 0, $supplier_proposalid = 0)
275 {
276 global $conf, $langs;
277
278 $this->db = $db;
279
280 $this->ismultientitymanaged = 1;
281 $this->socid = $socid;
282 $this->id = $supplier_proposalid;
283 }
284
285
286 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
297 public function add_product($idproduct, $qty, $remise_percent = 0)
298 {
299 // phpcs:enable
300 global $conf, $mysoc;
301
302 if (!$qty) {
303 $qty = 1;
304 }
305
306 dol_syslog(get_class($this)."::add_product $idproduct, $qty, $remise_percent");
307 if ($idproduct > 0) {
308 $prod = new Product($this->db);
309 $prod->fetch($idproduct);
310
311 $productdesc = $prod->description;
312
313 $tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
314 $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
315 if (empty($tva_tx)) {
316 $tva_npr = 0;
317 }
318 $localtax1_tx = get_localtax($tva_tx, 1, $mysoc, $this->thirdparty, $tva_npr);
319 $localtax2_tx = get_localtax($tva_tx, 2, $mysoc, $this->thirdparty, $tva_npr);
320
321 // multiprix
322 if ($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level) {
323 $price = $prod->multiprices[$this->thirdparty->price_level];
324 } else {
325 $price = $prod->price;
326 }
327
328 $line = new SupplierProposalLine($this->db);
329
330 $line->fk_product = $idproduct;
331 $line->desc = $productdesc;
332 $line->qty = $qty;
333 $line->subprice = $price;
334 $line->remise_percent = $remise_percent;
335 $line->tva_tx = $tva_tx;
336
337 $this->lines[] = $line;
338 return 1;
339 }
340 return -1;
341 }
342
343 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
350 public function insert_discount($idremise)
351 {
352 // phpcs:enable
353 global $langs;
354
355 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
356 include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
357
358 $this->db->begin();
359
360 $remise = new DiscountAbsolute($this->db);
361 $result = $remise->fetch($idremise);
362
363 if ($result > 0) {
364 if ($remise->fk_facture) { // Protection against multiple submission
365 $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
366 $this->db->rollback();
367 return -5;
368 }
369
370 $supplier_proposalligne = new SupplierProposalLine($this->db);
371 $supplier_proposalligne->fk_supplier_proposal = $this->id;
372 $supplier_proposalligne->fk_remise_except = $remise->id;
373 $supplier_proposalligne->desc = $remise->description; // Description ligne
374 $supplier_proposalligne->tva_tx = $remise->tva_tx;
375 $supplier_proposalligne->subprice = -(float) $remise->amount_ht;
376 $supplier_proposalligne->fk_product = 0; // Id produit predefini
377 $supplier_proposalligne->qty = 1;
378 $supplier_proposalligne->remise_percent = 0;
379 $supplier_proposalligne->rang = -1;
380 $supplier_proposalligne->info_bits = 2;
381
382 $supplier_proposalligne->total_ht = -(float) $remise->amount_ht;
383 $supplier_proposalligne->total_tva = -(float) $remise->amount_tva;
384 $supplier_proposalligne->total_ttc = -(float) $remise->amount_ttc;
385
386 $result = $supplier_proposalligne->insert();
387 if ($result > 0) {
388 $result = $this->update_price(1);
389 if ($result > 0) {
390 $this->db->commit();
391 return 1;
392 } else {
393 $this->db->rollback();
394 return -1;
395 }
396 } else {
397 $this->error = $supplier_proposalligne->error;
398 $this->db->rollback();
399 return -2;
400 }
401 } else {
402 $this->db->rollback();
403 return -2;
404 }
405 }
406
444 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)
445 {
446 global $mysoc, $conf, $langs;
447
448 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");
449 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
450
451 // Clean parameters
452 if (empty($remise_percent)) {
453 $remise_percent = 0;
454 }
455 if (empty($qty)) {
456 $qty = 0;
457 }
458 if (empty($info_bits)) {
459 $info_bits = 0;
460 }
461 if (empty($rang)) {
462 $rang = 0;
463 }
464 if (empty($fk_parent_line) || $fk_parent_line < 0) {
465 $fk_parent_line = 0;
466 }
467 if (empty($pu_ht)) {
468 $pu_ht = 0;
469 }
470
471 $remise_percent = price2num($remise_percent);
472 $qty = (float) price2num($qty);
473 $pu_ht = price2num($pu_ht);
474 $pu_ttc = price2num($pu_ttc);
475 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
476 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
477 }
478 $txlocaltax1 = price2num($txlocaltax1);
479 $txlocaltax2 = price2num($txlocaltax2);
480 $pa_ht = price2num($pa_ht);
481 if ($price_base_type == 'HT') {
482 $pu = $pu_ht;
483 } else {
484 $pu = $pu_ttc;
485 }
486
487 // Check parameters
488 if ($type < 0) {
489 return -1;
490 }
491
492 if ($this->statut == self::STATUS_DRAFT) {
493 $this->db->begin();
494
495 if ($fk_product > 0) {
496 if (getDolGlobalString('SUPPLIER_PROPOSAL_WITH_PREDEFINED_PRICES_ONLY')) {
497 // Check quantity is enough
498 dol_syslog(get_class($this)."::addline we check supplier prices fk_product=".$fk_product." fk_fournprice=".$fk_fournprice." qty=".$qty." ref_supplier=".$ref_supplier);
499 $productsupplier = new ProductFournisseur($this->db);
500 if ($productsupplier->fetch($fk_product) > 0) {
501 $product_type = $productsupplier->type;
502 $label = $productsupplier->label;
503 $fk_prod_fourn_price = $fk_fournprice;
504
505 // We use 'none' instead of $ref_supplier, because fourn_ref may not exists anymore. So we will take the first supplier price ok.
506 // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
507 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
508 $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
509 if ($result > 0) {
510 $pu = $productsupplier->fourn_pu; // Unit price supplier price set by get_buyprice
511 $ref_supplier = $productsupplier->ref_supplier; // Ref supplier price set by get_buyprice
512 // is remise percent not keyed but present for the product we add it
513 if ($remise_percent == 0 && $productsupplier->remise_percent != 0) {
514 $remise_percent = $productsupplier->remise_percent;
515 }
516 }
517 if ($result == 0) { // If result == 0, we failed to found the supplier reference price
518 $langs->load("errors");
519 $this->error = "Ref ".$productsupplier->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
520 $this->db->rollback();
521 dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
522 //$pu = $productsupplier->fourn_pu; // We do not overwrite unit price
523 //$ref = $productsupplier_fourn; // We do not overwrite ref supplier price
524 return -1;
525 }
526 if ($result == -1) {
527 $langs->load("errors");
528 $this->error = "Ref ".$productsupplier->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
529 $this->db->rollback();
530 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
531 return -1;
532 }
533 if ($result < -1) {
534 $this->error = $productsupplier->error;
535 $this->errors = $productsupplier->errors;
536 $this->db->rollback();
537 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
538 return -1;
539 }
540 } else {
541 $this->error = $productsupplier->error;
542 $this->errors = $productsupplier->errors;
543 $this->db->rollback();
544 return -1;
545 }
546 }
547 } else {
548 $product_type = $type;
549 }
550
551 // Calcul du total TTC et de la TVA pour la ligne a partir de
552 // qty, pu, remise_percent et txtva
553 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
554 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
555
556 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
557
558 // Clean vat code
559 $reg = array();
560 $vat_src_code = '';
561 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
562 $vat_src_code = $reg[1];
563 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
564 }
565
566 if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
567 $pu = 0;
568 }
569
570 $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
571 $total_ht = $tabprice[0];
572 $total_tva = $tabprice[1];
573 $total_ttc = $tabprice[2];
574 $total_localtax1 = $tabprice[9];
575 $total_localtax2 = $tabprice[10];
576 $pu = $pu_ht = $tabprice[3];
577
578 // MultiCurrency
579 $multicurrency_total_ht = $tabprice[16];
580 $multicurrency_total_tva = $tabprice[17];
581 $multicurrency_total_ttc = $tabprice[18];
582 $pu_ht_devise = $tabprice[19];
583
584 // Rang to use
585 $ranktouse = $rang;
586 if ($ranktouse == -1) {
587 $rangmax = $this->line_max($fk_parent_line);
588 $ranktouse = $rangmax + 1;
589 }
590
591 // TODO A virer
592 // Anciens indicateurs: $price, $remise (a ne plus utiliser)
593 $price = $pu;
594 $remise = 0;
595 if ($remise_percent > 0) {
596 $remise = round(($pu * (float) $remise_percent / 100), 2);
597 $price = $pu - $remise;
598 }
599
600 // Insert line
601 $this->line = new SupplierProposalLine($this->db);
602
603 $this->line->fk_supplier_proposal = $this->id;
604 $this->line->label = $label;
605 $this->line->desc = $desc;
606 $this->line->qty = $qty;
607
608 $this->line->vat_src_code = $vat_src_code;
609 $this->line->tva_tx = $txtva;
610 $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
611 $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
612 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
613 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
614 $this->line->fk_product = $fk_product;
615 $this->line->remise_percent = $remise_percent;
616 $this->line->subprice = $pu_ht;
617 $this->line->rang = $ranktouse;
618 $this->line->info_bits = $info_bits;
619 $this->line->total_ht = $total_ht;
620 $this->line->total_tva = $total_tva;
621 $this->line->total_localtax1 = $total_localtax1;
622 $this->line->total_localtax2 = $total_localtax2;
623 $this->line->total_ttc = $total_ttc;
624 $this->line->product_type = $type;
625 $this->line->special_code = $special_code;
626 $this->line->fk_parent_line = $fk_parent_line;
627 $this->line->fk_unit = $fk_unit;
628 $this->line->origin = $origin;
629 $this->line->origin_id = $origin_id;
630 $this->line->ref_fourn = $this->db->escape($ref_supplier);
631 $this->line->date_start = $date_start;
632 $this->line->date_end = $date_end;
633
634 // infos merge
635 if (!empty($fk_product) && $fk_product > 0 && empty($fk_fournprice) && empty($pa_ht)) {
636 // When fk_fournprice is 0, we take the lowest buying price
637 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
638 $productFournisseur = new ProductFournisseur($this->db);
639 $productFournisseur->find_min_price_product_fournisseur($fk_product);
640 $this->line->fk_fournprice = $productFournisseur->product_fourn_price_id;
641 } else {
642 $this->line->fk_fournprice = ($fk_fournprice > 0 ? $fk_fournprice : 0); // If fk_fournprice is -1, we will not use fk_fournprice
643 }
644 $this->line->pa_ht = $pa_ht;
645 //var_dump($this->line->fk_fournprice);exit;
646
647 // Multicurrency
648 $this->line->fk_multicurrency = $this->fk_multicurrency;
649 $this->line->multicurrency_code = $this->multicurrency_code;
650 $this->line->multicurrency_subprice = $pu_ht_devise;
651 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
652 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
653 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
654
655 // Mise en option de la ligne
656 if (empty($qty) && empty($special_code)) {
657 $this->line->special_code = 3;
658 }
659
660 if (is_array($array_options) && count($array_options) > 0) {
661 $this->line->array_options = $array_options;
662 }
663
664 $result = $this->line->insert();
665 if ($result > 0) {
666 // Reorder if child line
667 if (!empty($fk_parent_line)) {
668 $this->line_order(true, 'DESC');
669 } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) { // Update all rank of all other lines
670 $linecount = count($this->lines);
671 for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
672 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
673 }
674 }
675
676 // Mise a jour information denormalisees au niveau de la propale meme
677 $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.
678 if ($result > 0) {
679 $this->db->commit();
680 return $this->line->id;
681 } else {
682 $this->db->rollback();
683 return -1;
684 }
685 } else {
686 $this->error = $this->line->error;
687 $this->errors = $this->line->errors;
688 $this->db->rollback();
689 return -2;
690 }
691 } else {
692 $this->error = 'BadStatusOfObjectToAddLine';
693 return -5;
694 }
695 }
696
697
724 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)
725 {
726 global $conf, $user, $langs, $mysoc;
727
728 dol_syslog(get_class($this)."::updateLine $rowid, $pu, $qty, $remise_percent, $txtva, $desc, $price_base_type, $info_bits");
729 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
730
731 // Clean parameters
732 $remise_percent = price2num($remise_percent);
733 $qty = (float) price2num($qty);
734 $pu = price2num($pu);
735 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
736 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
737 }
738 $txlocaltax1 = price2num($txlocaltax1);
739 $txlocaltax2 = price2num($txlocaltax2);
740 $pa_ht = price2num($pa_ht);
741 if (empty($qty) && empty($special_code)) {
742 $special_code = 3; // Set option tag
743 }
744 if (!empty($qty) && $special_code == 3) {
745 $special_code = 0; // Remove option tag
746 }
747
748 if ($this->status == 0) {
749 $this->db->begin();
750
751 // Calcul du total TTC et de la TVA pour la ligne a partir de
752 // qty, pu, remise_percent et txtva
753 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
754 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
755
756 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
757
758 // Clean vat code
759 $reg = array();
760 $vat_src_code = '';
761 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
762 $vat_src_code = $reg[1];
763 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
764 }
765
766 if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
767 $pu = 0;
768 }
769
770 $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
771 $total_ht = $tabprice[0];
772 $total_tva = $tabprice[1];
773 $total_ttc = $tabprice[2];
774 $total_localtax1 = $tabprice[9];
775 $total_localtax2 = $tabprice[10];
776 $pu_ht = $tabprice[3];
777 $pu_tva = $tabprice[4];
778 $pu_ttc = $tabprice[5];
779
780 // MultiCurrency
781 $multicurrency_total_ht = $tabprice[16];
782 $multicurrency_total_tva = $tabprice[17];
783 $multicurrency_total_ttc = $tabprice[18];
784 $pu_ht_devise = $tabprice[19];
785
786 $pu = $pu_ht;
787 if ($price_base_type == 'TTC') {
788 $pu = $pu_ttc;
789 }
790
791 // Fetch current line from the database and then clone the object and set it in $oldline property
792 $line = new SupplierProposalLine($this->db);
793 $line->fetch($rowid);
794 $line->fetch_optionals();
795
796 $fk_product = $line->fk_product;
797
798 // Stock previous line records
799 $staticline = clone $line;
800
801 $line->oldline = $staticline;
802 $this->line = $line;
803 $this->line->context = $this->context;
804
805 // Reorder if fk_parent_line change
806 if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
807 $rangmax = $this->line_max($fk_parent_line);
808 $this->line->rang = $rangmax + 1;
809 }
810
811 $this->line->id = $rowid;
812 $this->line->label = $label;
813 $this->line->desc = $desc;
814 $this->line->qty = $qty;
815 $this->line->product_type = $type;
816
817 $this->line->vat_src_code = $vat_src_code;
818 $this->line->tva_tx = $txtva;
819 $this->line->localtax1_tx = $txlocaltax1;
820 $this->line->localtax2_tx = $txlocaltax2;
821 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
822 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
823 $this->line->remise_percent = $remise_percent;
824 $this->line->subprice = $pu;
825 $this->line->info_bits = $info_bits;
826 $this->line->total_ht = $total_ht;
827 $this->line->total_tva = $total_tva;
828 $this->line->total_localtax1 = $total_localtax1;
829 $this->line->total_localtax2 = $total_localtax2;
830 $this->line->total_ttc = $total_ttc;
831 $this->line->special_code = $special_code;
832 $this->line->fk_parent_line = $fk_parent_line;
833 $this->line->skip_update_total = $skip_update_total;
834 $this->line->ref_fourn = $ref_supplier;
835 $this->line->fk_unit = $fk_unit;
836
837 // infos merge
838 if (!empty($fk_product) && $fk_product > 0 && empty($fk_fournprice) && empty($pa_ht)) {
839 // by external module, take lowest buying price
840 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
841 $productFournisseur = new ProductFournisseur($this->db);
842 $productFournisseur->find_min_price_product_fournisseur($fk_product);
843 $this->line->fk_fournprice = $productFournisseur->product_fourn_price_id;
844 } else {
845 $this->line->fk_fournprice = $fk_fournprice;
846 }
847 $this->line->pa_ht = $pa_ht;
848
849 if (is_array($array_options) && count($array_options) > 0) {
850 // We replace values in this->line->array_options only for entries defined into $array_options
851 foreach ($array_options as $key => $value) {
852 $this->line->array_options[$key] = $array_options[$key];
853 }
854 }
855
856 // Multicurrency
857 $this->line->multicurrency_subprice = $pu_ht_devise;
858 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
859 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
860 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
861
862 $result = $this->line->update();
863 if ($result > 0) {
864 // Reorder if child line
865 if (!empty($fk_parent_line)) {
866 $this->line_order(true, 'DESC');
867 }
868
869 $this->update_price(1);
870
871 $this->db->commit();
872 return $result;
873 } else {
874 $this->error = $this->db->error();
875 $this->db->rollback();
876 return -1;
877 }
878 } else {
879 dol_syslog(get_class($this)."::updateline Erreur -2 SupplierProposal en mode incompatible pour cette action");
880 return -2;
881 }
882 }
883
884
891 public function deleteLine($lineid)
892 {
893 global $user;
894
895 if ($this->statut == 0) {
896 $line = new SupplierProposalLine($this->db);
897
898 // For triggers
899 $line->fetch($lineid);
900
901 if ($line->delete($user) > 0) {
902 $this->update_price(1);
903
904 return 1;
905 } else {
906 return -1;
907 }
908 } else {
909 return -2;
910 }
911 }
912
913
922 public function create($user, $notrigger = 0)
923 {
924 global $langs, $conf, $mysoc, $hookmanager;
925 $error = 0;
926
927 $now = dol_now();
928
929 dol_syslog(get_class($this)."::create");
930
931 // Check parameters
932 $result = $this->fetch_thirdparty();
933 if ($result < 0) {
934 $this->error = "Failed to fetch company";
935 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
936 return -3;
937 }
938 if (!empty($this->ref)) { // We check that ref is not already used
939 $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
940 if ($result > 0) {
941 $this->error = 'ErrorRefAlreadyExists';
942 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
943 $this->db->rollback();
944 return -1;
945 }
946 }
947
948 // Set tmp vars
949 $delivery_date = $this->delivery_date;
950
951 // Multicurrency
952 if (!empty($this->multicurrency_code)) {
953 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $now);
954 }
955 if (empty($this->fk_multicurrency)) {
956 $this->multicurrency_code = $conf->currency;
957 $this->fk_multicurrency = 0;
958 $this->multicurrency_tx = 1;
959 }
960
961 $this->db->begin();
962
963 // Insert into database
964 $sql = "INSERT INTO ".MAIN_DB_PREFIX."supplier_proposal (";
965 $sql .= "fk_soc";
966 $sql .= ", price";
967 $sql .= ", total_tva";
968 $sql .= ", total_ttc";
969 $sql .= ", datec";
970 $sql .= ", ref";
971 $sql .= ", fk_user_author";
972 $sql .= ", note_private";
973 $sql .= ", note_public";
974 $sql .= ", model_pdf";
975 $sql .= ", fk_cond_reglement";
976 $sql .= ", fk_mode_reglement";
977 $sql .= ", fk_account";
978 $sql .= ", date_livraison";
979 $sql .= ", fk_shipping_method";
980 $sql .= ", fk_projet";
981 $sql .= ", entity";
982 $sql .= ", fk_multicurrency";
983 $sql .= ", multicurrency_code";
984 $sql .= ", multicurrency_tx";
985 $sql .= ") ";
986 $sql .= " VALUES (";
987 $sql .= ((int) $this->socid);
988 $sql .= ", 0";
989 $sql .= ", 0";
990 $sql .= ", 0";
991 $sql .= ", '".$this->db->idate($now)."'";
992 $sql .= ", '(PROV)'";
993 $sql .= ", ".($user->id > 0 ? ((int) $user->id) : "null");
994 $sql .= ", '".$this->db->escape($this->note_private)."'";
995 $sql .= ", '".$this->db->escape($this->note_public)."'";
996 $sql .= ", '".$this->db->escape($this->model_pdf)."'";
997 $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : 'NULL');
998 $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : 'NULL');
999 $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
1000 $sql .= ", ".(isDolTms($delivery_date) ? "'".$this->db->idate($delivery_date)."'" : "null");
1001 $sql .= ", ".($this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : 'NULL');
1002 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
1003 $sql .= ", ".((int) $conf->entity);
1004 $sql .= ", ".((int) $this->fk_multicurrency);
1005 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1006 $sql .= ", ".((float) $this->multicurrency_tx);
1007 $sql .= ")";
1008
1009 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1010 $resql = $this->db->query($sql);
1011 if ($resql) {
1012 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."supplier_proposal");
1013
1014 if ($this->id) {
1015 $this->ref = '(PROV'.$this->id.')';
1016 $sql = 'UPDATE '.MAIN_DB_PREFIX."supplier_proposal SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
1017
1018 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1019 $resql = $this->db->query($sql);
1020 if (!$resql) {
1021 $error++;
1022 }
1023
1024 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1025 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1026 }
1027
1028 // Add object linked
1029 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1030 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1031 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, ...))
1032 foreach ($tmp_origin_id as $origin_id) {
1033 $ret = $this->add_object_linked($origin, $origin_id);
1034 if (!$ret) {
1035 dol_print_error($this->db);
1036 $error++;
1037 }
1038 }
1039 }
1040 }
1041 }
1042
1043 /*
1044 * Insertion du detail des produits dans la base
1045 */
1046 if (!$error) {
1047 $fk_parent_line = 0;
1048 $num = count($this->lines);
1049
1050 for ($i = 0; $i < $num; $i++) {
1051 // Reset fk_parent_line for no child products and special product
1052 if (($this->lines[$i]->product_type != 9 && empty($this->lines[$i]->fk_parent_line)) || $this->lines[$i]->product_type == 9) {
1053 $fk_parent_line = 0;
1054 }
1055
1056 $result = $this->addline(
1057 $this->lines[$i]->desc,
1058 $this->lines[$i]->subprice,
1059 $this->lines[$i]->qty,
1060 $this->lines[$i]->tva_tx,
1061 $this->lines[$i]->localtax1_tx,
1062 $this->lines[$i]->localtax2_tx,
1063 $this->lines[$i]->fk_product,
1064 $this->lines[$i]->remise_percent,
1065 'HT',
1066 0,
1067 0,
1068 $this->lines[$i]->product_type,
1069 $this->lines[$i]->rang,
1070 $this->lines[$i]->special_code,
1071 $fk_parent_line,
1072 $this->lines[$i]->fk_fournprice,
1073 $this->lines[$i]->pa_ht,
1074 empty($this->lines[$i]->label) ? '' : $this->lines[$i]->label, // deprecated
1075 $this->lines[$i]->array_options,
1076 $this->lines[$i]->ref_fourn,
1077 $this->lines[$i]->fk_unit,
1078 'supplier_proposal',
1079 $this->lines[$i]->rowid
1080 );
1081
1082 if ($result < 0) {
1083 $error++;
1084 $this->error = $this->db->error;
1085 dol_print_error($this->db);
1086 break;
1087 }
1088 // Defined the new fk_parent_line
1089 if ($result > 0 && $this->lines[$i]->product_type == 9) {
1090 $fk_parent_line = $result;
1091 }
1092 }
1093 }
1094
1095 if (!$error) {
1096 // Mise a jour infos denormalisees
1097 $resql = $this->update_price(1);
1098 if ($resql) {
1099 $action = 'update';
1100
1101 // Actions on extra fields
1102 if (!$error) {
1103 $result = $this->insertExtraFields();
1104 if ($result < 0) {
1105 $error++;
1106 }
1107 }
1108
1109 if (!$error && !$notrigger) {
1110 // Call trigger
1111 $result = $this->call_trigger('PROPOSAL_SUPPLIER_CREATE', $user);
1112 if ($result < 0) {
1113 $error++;
1114 }
1115 // End call triggers
1116 }
1117 } else {
1118 $this->error = $this->db->lasterror();
1119 $error++;
1120 }
1121 }
1122 } else {
1123 $this->error = $this->db->lasterror();
1124 $error++;
1125 }
1126
1127 if (!$error) {
1128 $this->db->commit();
1129 dol_syslog(get_class($this)."::create done id=".$this->id);
1130 return $this->id;
1131 } else {
1132 $this->db->rollback();
1133 return -2;
1134 }
1135 } else {
1136 $this->error = $this->db->lasterror();
1137 $this->db->rollback();
1138 return -1;
1139 }
1140 }
1141
1149 public function createFromClone(User $user, $fromid = 0)
1150 {
1151 global $conf, $hookmanager;
1152
1153 $error = 0;
1154 $now = dol_now();
1155
1156 $this->db->begin();
1157
1158 // get extrafields so they will be clone
1159 foreach ($this->lines as $line) {
1160 $line->fetch_optionals();
1161 }
1162
1163 // Load source object
1164 $objFrom = clone $this;
1165
1166 $objsoc = new Societe($this->db);
1167
1168 // Change socid if needed
1169 if (!empty($fromid) && $fromid != $this->socid) {
1170 if ($objsoc->fetch($fromid) > 0) {
1171 $this->socid = $objsoc->id;
1172 $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1173 $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1174 unset($this->fk_project);
1175 }
1176
1177 // TODO Change product price if multi-prices
1178 } else {
1179 $objsoc->fetch($this->socid);
1180 }
1181
1182 $this->id = 0;
1183 $this->statut = 0;
1184
1185 if (!getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') || !is_readable(DOL_DOCUMENT_ROOT."/core/modules/supplier_proposal/" . getDolGlobalString('SUPPLIER_PROPOSAL_ADDON').".php")) {
1186 $this->error = 'ErrorSetupNotComplete';
1187 return -1;
1188 }
1189
1190 // Clear fields
1191 $this->user_author_id = $user->id;
1192 $this->user_validation_id = 0;
1193 $this->date = $now;
1194
1195 // Set ref
1196 require_once DOL_DOCUMENT_ROOT."/core/modules/supplier_proposal/" . getDolGlobalString('SUPPLIER_PROPOSAL_ADDON').'.php';
1197 $obj = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
1198 $modSupplierProposal = new $obj();
1199 '@phan-var-force ModeleNumRefSupplierProposal $modSupplierProposal';
1200 $this->ref = $modSupplierProposal->getNextValue($objsoc, $this);
1201
1202 // Create clone
1203 $this->context['createfromclone'] = 'createfromclone';
1204 $result = $this->create($user);
1205 if ($result < 0) {
1206 $error++;
1207 }
1208
1209 if (!$error) {
1210 // Hook of thirdparty module
1211 if (is_object($hookmanager)) {
1212 $parameters = array('objFrom' => $objFrom);
1213 $action = '';
1214 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1215 if ($reshook < 0) {
1216 $this->setErrorsFromObject($hookmanager);
1217 $error++;
1218 }
1219 }
1220 }
1221
1222 unset($this->context['createfromclone']);
1223
1224 // End
1225 if (!$error) {
1226 $this->db->commit();
1227 return $this->id;
1228 } else {
1229 $this->db->rollback();
1230 return -1;
1231 }
1232 }
1233
1241 public function fetch($rowid, $ref = '')
1242 {
1243 global $conf;
1244
1245 $sql = "SELECT p.rowid, p.entity, p.ref, p.fk_soc as socid";
1246 $sql .= ", p.total_ttc, p.total_tva, p.localtax1, p.localtax2, p.total_ht";
1247 $sql .= ", p.datec";
1248 $sql .= ", p.date_valid as datev";
1249 $sql .= ", p.date_livraison as delivery_date";
1250 $sql .= ", p.model_pdf, p.extraparams";
1251 $sql .= ", p.note_private, p.note_public";
1252 $sql .= ", p.fk_projet as fk_project, p.fk_statut";
1253 $sql .= ", p.fk_user_author, p.fk_user_valid, p.fk_user_cloture";
1254 $sql .= ", p.fk_cond_reglement";
1255 $sql .= ", p.fk_mode_reglement";
1256 $sql .= ', p.fk_account';
1257 $sql .= ", p.fk_shipping_method";
1258 $sql .= ", p.fk_multicurrency, p.multicurrency_code, p.multicurrency_tx, p.multicurrency_total_ht, p.multicurrency_total_tva, p.multicurrency_total_ttc";
1259 $sql .= ", c.label as statut_label";
1260 $sql .= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc";
1261 $sql .= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement";
1262 $sql .= " FROM ".MAIN_DB_PREFIX."c_propalst as c, ".MAIN_DB_PREFIX."supplier_proposal as p";
1263 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id';
1264 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON p.fk_cond_reglement = cr.rowid';
1265 $sql .= " WHERE p.fk_statut = c.id";
1266 $sql .= " AND p.entity IN (".getEntity('supplier_proposal').")";
1267 if ($ref) {
1268 $sql .= " AND p.ref = '".$this->db->escape($ref)."'";
1269 } else {
1270 $sql .= " AND p.rowid = ".((int) $rowid);
1271 }
1272
1273 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1274 $resql = $this->db->query($sql);
1275 if ($resql) {
1276 if ($this->db->num_rows($resql)) {
1277 $obj = $this->db->fetch_object($resql);
1278
1279 $this->id = $obj->rowid;
1280 $this->entity = $obj->entity;
1281
1282 $this->ref = $obj->ref;
1283 $this->total_ht = $obj->total_ht;
1284 $this->total_tva = $obj->total_tva;
1285 $this->total_localtax1 = $obj->localtax1;
1286 $this->total_localtax2 = $obj->localtax2;
1287 $this->total_ttc = $obj->total_ttc;
1288 $this->socid = $obj->socid;
1289 $this->fk_project = $obj->fk_project;
1290 $this->model_pdf = $obj->model_pdf;
1291 $this->note = $obj->note_private; // TODO deprecated
1292 $this->note_private = $obj->note_private;
1293 $this->note_public = $obj->note_public;
1294 $this->statut = (int) $obj->fk_statut;
1295 $this->status = (int) $obj->fk_statut;
1296 $this->datec = $this->db->jdate($obj->datec); // TODO deprecated
1297 $this->datev = $this->db->jdate($obj->datev); // TODO deprecated
1298 $this->date_creation = $this->db->jdate($obj->datec); // Creation date
1299 $this->date = $this->date_creation;
1300 $this->date_validation = $this->db->jdate($obj->datev); // Validation date
1301 $this->delivery_date = $this->db->jdate($obj->delivery_date);
1302 $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1303
1304 $this->mode_reglement_id = $obj->fk_mode_reglement;
1305 $this->mode_reglement_code = $obj->mode_reglement_code;
1306 $this->mode_reglement = $obj->mode_reglement;
1307 $this->fk_account = ($obj->fk_account > 0) ? $obj->fk_account : null;
1308 $this->cond_reglement_id = $obj->fk_cond_reglement;
1309 $this->cond_reglement_code = $obj->cond_reglement_code;
1310 $this->cond_reglement = $obj->cond_reglement;
1311 $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1312
1313 $this->extraparams = (array) json_decode($obj->extraparams, true);
1314
1315 $this->user_author_id = $obj->fk_user_author;
1316 $this->user_validation_id = $obj->fk_user_valid;
1317 $this->user_closing_id = $obj->fk_user_cloture;
1318
1319 // Multicurrency
1320 $this->fk_multicurrency = $obj->fk_multicurrency;
1321 $this->multicurrency_code = $obj->multicurrency_code;
1322 $this->multicurrency_tx = $obj->multicurrency_tx;
1323 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1324 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1325 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1326
1327 // Retrieve all extrafield
1328 // fetch optionals attributes and labels
1329 $this->fetch_optionals();
1330
1331 $this->db->free($resql);
1332
1333 $this->lines = array();
1334
1335 // Lines of supplier proposals
1336 $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,";
1337 $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,";
1338 $sql .= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label,';
1339 $sql .= ' d.ref_fourn as ref_produit_fourn,';
1340 $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';
1341 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposaldet as d";
1342 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON d.fk_product = p.rowid";
1343 $sql .= " WHERE d.fk_supplier_proposal = ".((int) $this->id);
1344 $sql .= " ORDER by d.rang";
1345
1346 $result = $this->db->query($sql);
1347 if ($result) {
1348 $num = $this->db->num_rows($result);
1349 $i = 0;
1350
1351 while ($i < $num) {
1352 $objp = $this->db->fetch_object($result);
1353
1354 $line = new SupplierProposalLine($this->db);
1355
1356 $line->rowid = $objp->rowid; // deprecated
1357 $line->id = $objp->rowid;
1358 $line->fk_supplier_proposal = $objp->fk_supplier_proposal;
1359 $line->fk_parent_line = $objp->fk_parent_line;
1360 $line->product_type = $objp->product_type;
1361 $line->label = $objp->custom_label;
1362 $line->desc = $objp->description; // Description ligne
1363 $line->qty = $objp->qty;
1364 $line->tva_tx = $objp->tva_tx;
1365 $line->localtax1_tx = $objp->localtax1_tx;
1366 $line->localtax2_tx = $objp->localtax2_tx;
1367 $line->subprice = $objp->subprice;
1368 $line->fk_remise_except = $objp->fk_remise_except;
1369 $line->remise_percent = $objp->remise_percent;
1370
1371 $line->info_bits = $objp->info_bits;
1372 $line->total_ht = $objp->total_ht;
1373 $line->total_tva = $objp->total_tva;
1374 $line->total_localtax1 = $objp->total_localtax1;
1375 $line->total_localtax2 = $objp->total_localtax2;
1376 $line->total_ttc = $objp->total_ttc;
1377 $line->fk_fournprice = $objp->fk_fournprice;
1378 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1379 $line->pa_ht = $marginInfos[0];
1380 $line->marge_tx = $marginInfos[1];
1381 $line->marque_tx = $marginInfos[2];
1382 $line->special_code = $objp->special_code;
1383 $line->rang = $objp->rang;
1384
1385 $line->fk_product = $objp->fk_product;
1386
1387 $line->ref = $objp->product_ref; // deprecated
1388 $line->product_ref = $objp->product_ref;
1389 $line->libelle = $objp->product_label; // deprecated
1390 $line->product_label = $objp->product_label;
1391 $line->product_desc = $objp->product_desc; // Description produit
1392 $line->fk_product_type = $objp->fk_product_type;
1393
1394 $line->ref_fourn = $objp->ref_produit_fourn;
1395
1396 // Multicurrency
1397 $line->fk_multicurrency = $objp->fk_multicurrency;
1398 $line->multicurrency_code = $objp->multicurrency_code;
1399 $line->multicurrency_subprice = $objp->multicurrency_subprice;
1400 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
1401 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
1402 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
1403 $line->fk_unit = $objp->fk_unit;
1404
1405 $this->lines[$i] = $line;
1406
1407 $i++;
1408 }
1409 $this->db->free($result);
1410 } else {
1411 $this->error = $this->db->error();
1412 return -1;
1413 }
1414
1415 // Retrieve all extrafield
1416 // fetch optionals attributes and labels
1417 $this->fetch_optionals();
1418
1419 return 1;
1420 }
1421
1422 $this->error = "Record Not Found";
1423 return 0;
1424 } else {
1425 $this->error = $this->db->error();
1426 return -1;
1427 }
1428 }
1429
1437 public function valid($user, $notrigger = 0)
1438 {
1439 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1440
1441 global $conf, $langs;
1442
1443 $error = 0;
1444 $now = dol_now();
1445
1446 if ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'creer'))
1447 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'validate_advance'))) {
1448 $this->db->begin();
1449
1450 // Numbering module definition
1451 $soc = new Societe($this->db);
1452 $result = $soc->fetch($this->socid);
1453
1454 if ($result < 0) {
1455 return -1;
1456 }
1457
1458 // Define new ref
1459 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1460 $num = $this->getNextNumRef($soc);
1461 } else {
1462 $num = $this->ref;
1463 }
1464 $this->newref = dol_sanitizeFileName($num);
1465
1466 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1467 $sql .= " SET ref = '".$this->db->escape($num)."',";
1468 $sql .= " fk_statut = 1, date_valid='".$this->db->idate($now)."', fk_user_valid=".((int) $user->id);
1469 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1470
1471 dol_syslog(get_class($this)."::valid", LOG_DEBUG);
1472 $resql = $this->db->query($sql);
1473 if (!$resql) {
1474 dol_print_error($this->db);
1475 $error++;
1476 }
1477
1478 // Trigger calls
1479 if (!$error && !$notrigger) {
1480 // Call trigger
1481 $result = $this->call_trigger('PROPOSAL_SUPPLIER_VALIDATE', $user);
1482 if ($result < 0) {
1483 $error++;
1484 }
1485 // End call triggers
1486 }
1487
1488 if (!$error) {
1489 $this->oldref = $this->ref;
1490
1491 // Rename directory if dir was a temporary ref
1492 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1493 // Now we rename also files into index
1494 $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)."'";
1495 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1496 $resql = $this->db->query($sql);
1497 if (!$resql) {
1498 $error++;
1499 $this->error = $this->db->lasterror();
1500 }
1501 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'supplier_proposal/".$this->db->escape($this->newref)."'";
1502 $sql .= " WHERE filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1503 $resql = $this->db->query($sql);
1504 if (!$resql) {
1505 $error++;
1506 $this->error = $this->db->lasterror();
1507 }
1508
1509 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1510 $oldref = dol_sanitizeFileName($this->ref);
1511 $newref = dol_sanitizeFileName($num);
1512 $dirsource = $conf->supplier_proposal->dir_output.'/'.$oldref;
1513 $dirdest = $conf->supplier_proposal->dir_output.'/'.$newref;
1514 if (!$error && file_exists($dirsource)) {
1515 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
1516 if (@rename($dirsource, $dirdest)) {
1517 dol_syslog("Rename ok");
1518 // Rename docs starting with $oldref with $newref
1519 $listoffiles = dol_dir_list($conf->supplier_proposal->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1520 foreach ($listoffiles as $fileentry) {
1521 $dirsource = $fileentry['name'];
1522 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1523 $dirsource = $fileentry['path'].'/'.$dirsource;
1524 $dirdest = $fileentry['path'].'/'.$dirdest;
1525 @rename($dirsource, $dirdest);
1526 }
1527 }
1528 }
1529 }
1530
1531 $this->ref = $num;
1532 $this->statut = self::STATUS_VALIDATED;
1534 $this->user_validation_id = $user->id;
1535 $this->datev = $now;
1536 $this->date_validation = $now;
1537
1538 $this->db->commit();
1539 return 1;
1540 } else {
1541 $this->db->rollback();
1542 return -1;
1543 }
1544 } else {
1545 dol_syslog("You don't have permission to validate supplier proposal", LOG_WARNING);
1546 return -2;
1547 }
1548 }
1549
1550 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1559 public function set_date_livraison($user, $delivery_date)
1560 {
1561 // phpcs:enable
1562 return $this->setDeliveryDate($user, $delivery_date);
1563 }
1564
1572 public function setDeliveryDate($user, $delivery_date)
1573 {
1574 if ($user->hasRight('supplier_proposal', 'creer')) {
1575 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1576 $sql .= " SET date_livraison = ".(isDolTms($delivery_date) ? "'".$this->db->idate($delivery_date)."'" : 'null');
1577 $sql .= " WHERE rowid = ".((int) $this->id);
1578
1579 if ($this->db->query($sql)) {
1580 $this->delivery_date = $delivery_date;
1581 return 1;
1582 } else {
1583 $this->error = $this->db->error();
1584 dol_syslog(get_class($this)."::setDeliveryDate Erreur SQL");
1585 return -1;
1586 }
1587 }
1588 return 0;
1589 }
1590
1591 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1599 /*
1600 public function set_remise_percent($user, $remise)
1601 {
1602 // phpcs:enable
1603 $remise = trim($remise) ?trim($remise) : 0;
1604
1605 if ($user->hasRight('supplier_proposal', 'creer')) {
1606 $remise = price2num($remise, 2);
1607
1608 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal SET remise_percent = ".((float) $remise);
1609 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1610
1611 if ($this->db->query($sql)) {
1612 $this->remise_percent = ((float) $remise);
1613 $this->update_price(1);
1614 return 1;
1615 } else {
1616 $this->error = $this->db->error();
1617 return -1;
1618 }
1619 }
1620 return 0;
1621 }
1622 */
1623
1624 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1632 /*
1633 public function set_remise_absolue($user, $remise)
1634 {
1635 // phpcs:enable
1636 if (empty($remise)) {
1637 $remise = 0;
1638 }
1639
1640 $remise = price2num($remise);
1641
1642 if ($user->hasRight('supplier_proposal', 'creer')) {
1643 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1644 $sql .= " SET remise_absolue = ".((float) $remise);
1645 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1646
1647 if ($this->db->query($sql)) {
1648 $this->remise_absolue = $remise;
1649 $this->update_price(1);
1650 return 1;
1651 } else {
1652 $this->error = $this->db->error();
1653 return -1;
1654 }
1655 }
1656 return 0;
1657 }
1658 */
1659
1660
1670 public function reopen($user, $statut, $note = '', $notrigger = 0)
1671 {
1672 global $langs, $conf;
1673
1674 $this->statut = $statut;
1675 $error = 0;
1676
1677 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1678 $sql .= " SET fk_statut = ".((int) $this->statut).",";
1679 if (!empty($note)) {
1680 $sql .= " note_private = '".$this->db->escape($note)."',";
1681 }
1682 $sql .= " date_cloture = NULL, fk_user_cloture = NULL";
1683 $sql .= " WHERE rowid = ".((int) $this->id);
1684
1685 $this->db->begin();
1686
1687 dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
1688 $resql = $this->db->query($sql);
1689 if (!$resql) {
1690 $error++;
1691 $this->errors[] = "Error ".$this->db->lasterror();
1692 }
1693 if (!$error) {
1694 if (!$notrigger) {
1695 // Call trigger
1696 $result = $this->call_trigger('PROPOSAL_SUPPLIER_REOPEN', $user);
1697 if ($result < 0) {
1698 $error++;
1699 }
1700 // End call triggers
1701 }
1702 }
1703
1704 // Commit or rollback
1705 if ($error) {
1706 if (!empty($this->errors)) {
1707 foreach ($this->errors as $errmsg) {
1708 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1709 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1710 }
1711 }
1712 $this->db->rollback();
1713 return -1 * $error;
1714 } else {
1715 $this->db->commit();
1716 return 1;
1717 }
1718 }
1719
1720
1729 public function cloture($user, $status, $note)
1730 {
1731 global $langs, $conf;
1732 $hidedetails = 0;
1733 $hidedesc = 0;
1734 $hideref = 0;
1735 $this->statut = $status;
1736 $error = 0;
1737 $now = dol_now();
1738
1739 $this->db->begin();
1740
1741 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1742 $sql .= " SET fk_statut = ".((int) $status).", note_private = '".$this->db->escape($note)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".$user->id;
1743 $sql .= " WHERE rowid = ".((int) $this->id);
1744
1745 $resql = $this->db->query($sql);
1746 if ($resql) {
1747 $modelpdf = $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED ? $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED : (empty($this->model_pdf) ? '' : $this->model_pdf);
1748 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_REFUSED';
1749
1750 if ($status == 2) {
1751 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_SIGNED';
1752 $modelpdf = $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL ? $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL : (empty($this->model_pdf) ? '' : $this->model_pdf);
1753
1754 if (getDolGlobalString('SUPPLIER_PROPOSAL_UPDATE_PRICE_ON_SUPPlIER_PROPOSAL')) { // TODO This option was not tested correctly. Error if product ref does not exists
1755 $result = $this->updateOrCreatePriceFournisseur($user);
1756 }
1757 }
1758 if ($status == 4) {
1759 $triggerName = 'PROPOSAL_SUPPLIER_CLASSIFY_BILLED';
1760 }
1761
1762 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1763 // Define output language
1764 $outputlangs = $langs;
1765 if (getDolGlobalInt('MAIN_MULTILANGS')) {
1766 $outputlangs = new Translate("", $conf);
1767 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
1768 $outputlangs->setDefaultLang($newlang);
1769 }
1770 //$ret=$object->fetch($id); // Reload to get new records
1771 $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1772 }
1773
1774 // Call trigger
1775 $result = $this->call_trigger($triggerName, $user);
1776 if ($result < 0) {
1777 $error++;
1778 }
1779 // End call triggers
1780
1781 if (!$error) {
1782 $this->db->commit();
1783 return 1;
1784 } else {
1785 $this->db->rollback();
1786 return -1;
1787 }
1788 } else {
1789 $this->error = $this->db->lasterror();
1790 $this->errors[] = $this->db->lasterror();
1791 $this->db->rollback();
1792 return -1;
1793 }
1794 }
1795
1802 public function updateOrCreatePriceFournisseur($user)
1803 {
1804 global $conf;
1805
1806 dol_syslog(get_class($this)."::updateOrCreatePriceFournisseur", LOG_DEBUG);
1807 foreach ($this->lines as $product) {
1808 if ($product->subprice <= 0) {
1809 continue;
1810 }
1811 $productsupplier = new ProductFournisseur($this->db);
1812
1813 $multicurrency_tx = 1;
1814 $fk_multicurrency = 0;
1815
1816 if (empty($this->thirdparty)) {
1817 $this->fetch_thirdparty();
1818 }
1819
1820 $ref_fourn = $product->ref_fourn;
1821 if (empty($ref_fourn)) {
1822 $ref_fourn = $product->ref_supplier;
1823 }
1824 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1825 list($fk_multicurrency, $multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $product->multicurrency_code);
1826 }
1827 $productsupplier->id = $product->fk_product;
1828
1829 $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);
1830 }
1831
1832 return 1;
1833 }
1834
1843 public function updatePriceFournisseur($idProductFournPrice, $product, $user)
1844 {
1845 $price = price2num($product->subprice * $product->qty, 'MU');
1846 $unitPrice = price2num($product->subprice, 'MU');
1847
1848 $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);
1849
1850 $resql = $this->db->query($sql);
1851 if (!$resql) {
1852 $this->error = $this->db->error();
1853 $this->db->rollback();
1854 return -1;
1855 }
1856 return 1;
1857 }
1858
1866 public function createPriceFournisseur($product, $user)
1867 {
1868 global $conf;
1869
1870 $price = price2num($product->subprice * $product->qty, 'MU');
1871 $qty = price2num($product->qty);
1872 $unitPrice = price2num($product->subprice, 'MU');
1873
1874 $now = dol_now();
1875
1876 $values = array(
1877 "'".$this->db->idate($now)."'",
1878 $product->fk_product,
1879 $this->thirdparty->id,
1880 "'".$product->ref_fourn."'",
1881 $price,
1882 $qty,
1883 $unitPrice,
1884 $product->tva_tx,
1885 $user->id
1886 );
1887 if (isModEnabled("multicurrency")) {
1888 if (!empty($product->multicurrency_code)) {
1889 include_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
1890 $multicurrency = new MultiCurrency($this->db); //need to fetch because empty fk_multicurrency and rate
1891 $multicurrency->fetch(0, $product->multicurrency_code);
1892 if (!empty($multicurrency->id)) {
1893 $values[] = $multicurrency->id;
1894 $values[] = "'".$product->multicurrency_code."'";
1895 $values[] = $product->multicurrency_subprice;
1896 $values[] = $product->multicurrency_total_ht;
1897 $values[] = $multicurrency->rate->rate;
1898 } else {
1899 for ($i = 0; $i < 5; $i++) {
1900 $values[] = 'NULL';
1901 }
1902 }
1903 }
1904 }
1905
1906 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'product_fournisseur_price ';
1907 $sql .= '(datec, fk_product, fk_soc, ref_fourn, price, quantity, unitprice, tva_tx, fk_user';
1908 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1909 $sql .= ',fk_multicurrency, multicurrency_code, multicurrency_unitprice, multicurrency_price, multicurrency_tx';
1910 }
1911 $sql .= ') VALUES ('.implode(',', $values).')';
1912
1913 $resql = $this->db->query($sql);
1914 if (!$resql) {
1915 $this->error = $this->db->error();
1916 $this->db->rollback();
1917 return -1;
1918 }
1919 return 1;
1920 }
1921
1922 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1929 public function setDraft($user)
1930 {
1931 // phpcs:enable
1932 global $conf, $langs;
1933
1934 $error = 0;
1935
1936 if ($this->statut == self::STATUS_DRAFT) {
1937 dol_syslog(get_class($this)."::setDraft already draft status", LOG_WARNING);
1938 return 0;
1939 }
1940
1941 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1942 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1943 $sql .= " WHERE rowid = ".((int) $this->id);
1944
1945 if ($this->db->query($sql)) {
1946 if (!$error) {
1947 $this->oldcopy = clone $this;
1948 }
1949
1950 if (!$error) {
1951 // Call trigger
1952 $result = $this->call_trigger('PROPOSAL_SUPPLIER_UNVALIDATE', $user);
1953 if ($result < 0) {
1954 $error++;
1955 }
1956 }
1957
1958 if (!$error) {
1959 $this->status = self::STATUS_DRAFT;
1960 $this->statut = self::STATUS_DRAFT; // deprecated
1961 $this->db->commit();
1962 return 1;
1963 } else {
1964 $this->db->rollback();
1965 return -1;
1966 }
1967 } else {
1968 return -1;
1969 }
1970 }
1971
1972
1973 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1987 public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datec', $sortorder = 'DESC')
1988 {
1989 // phpcs:enable
1990 global $user;
1991
1992 $ga = array();
1993
1994 $search_sale = 0;
1995 if (!$user->hasRight('societe', 'client', 'voir')) {
1996 $search_sale = $user->id;
1997 }
1998
1999 $sql = "SELECT s.rowid, s.nom as name, s.client,";
2000 $sql .= " p.rowid as supplier_proposalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
2001 $sql .= " p.datep as dp, p.fin_validite as datelimite";
2002 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."supplier_proposal as p, ".MAIN_DB_PREFIX."c_propalst as c";
2003 $sql .= " WHERE p.entity IN (".getEntity('supplier_proposal').")";
2004 $sql .= " AND p.fk_soc = s.rowid";
2005 $sql .= " AND p.fk_statut = c.id";
2006 if ($socid) {
2007 $sql .= " AND s.rowid = ".((int) $socid);
2008 }
2009 if ($draft) {
2010 $sql .= " AND p.fk_statut = 0";
2011 }
2012 if ($notcurrentuser > 0) {
2013 $sql .= " AND p.fk_user_author <> ".((int) $user->id);
2014 }
2015 // Search on sale representative
2016 if ($search_sale && $search_sale != '-1') {
2017 if ($search_sale == -2) {
2018 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
2019 } elseif ($search_sale > 0) {
2020 $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).")";
2021 }
2022 }
2023 $sql .= $this->db->order($sortfield, $sortorder);
2024 $sql .= $this->db->plimit($limit, $offset);
2025
2026 $result = $this->db->query($sql);
2027 if ($result) {
2028 $num = $this->db->num_rows($result);
2029 if ($num) {
2030 $i = 0;
2031 while ($i < $num) {
2032 $obj = $this->db->fetch_object($result);
2033
2034 if ($shortlist == 1) {
2035 $ga[$obj->supplier_proposalid] = $obj->ref;
2036 } elseif ($shortlist == 2) {
2037 $ga[$obj->supplier_proposalid] = $obj->ref.' ('.$obj->name.')';
2038 } else {
2039 $ga[$i]['id'] = $obj->supplier_proposalid;
2040 $ga[$i]['ref'] = $obj->ref;
2041 $ga[$i]['name'] = $obj->name;
2042 }
2043
2044 $i++;
2045 }
2046 }
2047 return $ga;
2048 } else {
2049 dol_print_error($this->db);
2050 return -1;
2051 }
2052 }
2053
2061 public function delete($user, $notrigger = 0)
2062 {
2063 global $conf, $langs;
2064 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2065
2066 $error = 0;
2067
2068 $this->db->begin();
2069
2070 if (!$notrigger) {
2071 // Call trigger
2072 $result = $this->call_trigger('PROPOSAL_SUPPLIER_DELETE', $user);
2073 if ($result < 0) {
2074 $error++;
2075 }
2076 // End call triggers
2077 }
2078
2079 if (!$error) {
2080 $main = MAIN_DB_PREFIX.'supplier_proposaldet';
2081 $ef = $main."_extrafields";
2082 $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_supplier_proposal = ".((int) $this->id).")";
2083 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet WHERE fk_supplier_proposal = ".((int) $this->id);
2084 if ($this->db->query($sql)) {
2085 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposal WHERE rowid = ".((int) $this->id);
2086 if ($this->db->query($sqlef) && $this->db->query($sql)) {
2087 // Delete linked object
2088 $res = $this->deleteObjectLinked();
2089 if ($res < 0) {
2090 $error++;
2091 }
2092
2093 if (!$error) {
2094 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2095 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
2096 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
2097
2098 // We remove directory
2099 $ref = dol_sanitizeFileName($this->ref);
2100 if ($conf->supplier_proposal->dir_output && !empty($this->ref)) {
2101 $dir = $conf->supplier_proposal->dir_output."/".$ref;
2102 $file = $dir."/".$ref.".pdf";
2103 if (file_exists($file)) {
2104 dol_delete_preview($this);
2105
2106 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2107 $this->error = 'ErrorFailToDeleteFile';
2108 $this->errors = array('ErrorFailToDeleteFile');
2109 $this->db->rollback();
2110 return 0;
2111 }
2112 }
2113 if (file_exists($dir)) {
2114 $res = @dol_delete_dir_recursive($dir);
2115 if (!$res) {
2116 $this->error = 'ErrorFailToDeleteDir';
2117 $this->errors = array('ErrorFailToDeleteDir');
2118 $this->db->rollback();
2119 return 0;
2120 }
2121 }
2122 }
2123 }
2124
2125 // Removed extrafields
2126 if (!$error) {
2127 $result = $this->deleteExtraFields();
2128 if ($result < 0) {
2129 $error++;
2130 $errorflag = -4;
2131 dol_syslog(get_class($this)."::delete erreur ".$errorflag." ".$this->error, LOG_ERR);
2132 }
2133 }
2134
2135 if (!$error) {
2136 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
2137 $this->db->commit();
2138 return 1;
2139 } else {
2140 $this->error = $this->db->lasterror();
2141 $this->db->rollback();
2142 return 0;
2143 }
2144 } else {
2145 $this->error = $this->db->lasterror();
2146 $this->db->rollback();
2147 return -3;
2148 }
2149 } else {
2150 $this->error = $this->db->lasterror();
2151 $this->db->rollback();
2152 return -2;
2153 }
2154 } else {
2155 $this->db->rollback();
2156 return -1;
2157 }
2158 }
2159
2166 public function info($id)
2167 {
2168 $sql = "SELECT c.rowid, ";
2169 $sql .= " c.datec as date_creation, c.date_valid as date_validation, c.date_cloture as date_closure,";
2170 $sql .= " c.fk_user_author, c.fk_user_valid, c.fk_user_cloture";
2171 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as c";
2172 $sql .= " WHERE c.rowid = ".((int) $id);
2173
2174 $result = $this->db->query($sql);
2175
2176 if ($result) {
2177 if ($this->db->num_rows($result)) {
2178 $obj = $this->db->fetch_object($result);
2179
2180 $this->id = $obj->rowid;
2181
2182 $this->date_creation = $this->db->jdate($obj->date_creation);
2183 $this->date_validation = $this->db->jdate($obj->date_validation);
2184 $this->date_cloture = $this->db->jdate($obj->date_closure);
2185
2186 $this->user_creation_id = $obj->fk_user_author;
2187 $this->user_validation_id = $obj->fk_user_valid;
2188 $this->user_closing_id = $obj->fk_user_cloture;
2189 }
2190 $this->db->free($result);
2191 } else {
2192 dol_print_error($this->db);
2193 }
2194 }
2195
2196
2203 public function getLibStatut($mode = 0)
2204 {
2205 return $this->LibStatut((isset($this->statut) ? $this->statut : $this->status), $mode);
2206 }
2207
2208 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2216 public function LibStatut($status, $mode = 1)
2217 {
2218 // phpcs:enable
2219
2220 // Init/load array of translation of status
2221 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
2222 global $langs;
2223 $langs->load("supplier_proposal");
2224 $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraft");
2225 $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidated");
2226 $this->labelStatus[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSigned");
2227 $this->labelStatus[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSigned");
2228 $this->labelStatus[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosed");
2229 $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraftShort");
2230 $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidatedShort");
2231 $this->labelStatusShort[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSignedShort");
2232 $this->labelStatusShort[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSignedShort");
2233 $this->labelStatusShort[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosedShort");
2234 }
2235
2236 $statusnew = '';
2237 if ($status == self::STATUS_DRAFT) {
2238 $statusnew = 'status0';
2239 } elseif ($status == self::STATUS_VALIDATED) {
2240 $statusnew = 'status1';
2241 } elseif ($status == self::STATUS_SIGNED) {
2242 $statusnew = 'status4';
2243 } elseif ($status == self::STATUS_NOTSIGNED) {
2244 $statusnew = 'status9';
2245 } elseif ($status == self::STATUS_CLOSE) {
2246 $statusnew = 'status6';
2247 }
2248
2249 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusnew, $mode);
2250 }
2251
2252
2253 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2261 public function load_board($user, $mode)
2262 {
2263 // phpcs:enable
2264 global $conf, $user, $langs;
2265
2266 $now = dol_now();
2267
2268 $clause = " WHERE";
2269
2270 $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.date_cloture as datefin";
2271 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2272 if (!$user->hasRight('societe', 'client', 'voir')) {
2273 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
2274 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2275 $clause = " AND";
2276 }
2277 $sql .= $clause." p.entity IN (".getEntity('supplier_proposal').")";
2278 if ($mode == 'opened') {
2279 $sql .= " AND p.fk_statut = 1";
2280 }
2281 if ($mode == 'signed') {
2282 $sql .= " AND p.fk_statut = 2";
2283 }
2284 if ($user->socid) {
2285 $sql .= " AND p.fk_soc = ".((int) $user->socid);
2286 }
2287
2288 $resql = $this->db->query($sql);
2289 if ($resql) {
2290 $label = $labelShort = '';
2291 $status = '';
2292 $delay_warning = 0;
2293 if ($mode == 'opened') {
2294 $delay_warning = !empty($conf->supplier_proposal->cloture->warning_delay) ? $conf->supplier_proposal->cloture->warning_delay : 0;
2295 $status = self::STATUS_VALIDATED;
2296 $label = $langs->trans("SupplierProposalsToClose");
2297 $labelShort = $langs->trans("ToAcceptRefuse");
2298 }
2299 if ($mode == 'signed') {
2300 $delay_warning = !empty($conf->supplier_proposal->facturation->warning_delay) ? $conf->supplier_proposal->facturation->warning_delay : 0;
2301 $status = self::STATUS_SIGNED;
2302 $label = $langs->trans("SupplierProposalsToProcess"); // May be billed or ordered
2303 $labelShort = $langs->trans("ToClose");
2304 }
2305
2306 $response = new WorkboardResponse();
2307 $response->warning_delay = $delay_warning / 60 / 60 / 24;
2308 $response->label = $label;
2309 $response->labelShort = $labelShort;
2310 $response->url = DOL_URL_ROOT.'/supplier_proposal/list.php?search_status='.$status;
2311 $response->img = img_object('', "propal");
2312
2313 // This assignment in condition is not a bug. It allows walking the results.
2314 while ($obj = $this->db->fetch_object($resql)) {
2315 $response->nbtodo++;
2316 if ($mode == 'opened') {
2317 $datelimit = $this->db->jdate($obj->datefin);
2318 if ($datelimit < ($now - $delay_warning)) {
2319 $response->nbtodolate++;
2320 }
2321 }
2322 // TODO Definir regle des propales a facturer en retard
2323 // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
2324 }
2325 return $response;
2326 } else {
2327 $this->error = $this->db->lasterror();
2328 return -1;
2329 }
2330 }
2331
2332
2340 public function initAsSpecimen()
2341 {
2342 global $user, $langs, $conf;
2343
2344 // Load array of products prodids
2345 $num_prods = 0;
2346 $prodids = array();
2347 $sql = "SELECT rowid";
2348 $sql .= " FROM ".MAIN_DB_PREFIX."product";
2349 $sql .= " WHERE entity IN (".getEntity('product').")";
2350 $sql .= $this->db->plimit(100);
2351
2352 $resql = $this->db->query($sql);
2353 if ($resql) {
2354 $num_prods = $this->db->num_rows($resql);
2355 $i = 0;
2356 while ($i < $num_prods) {
2357 $i++;
2358 $row = $this->db->fetch_row($resql);
2359 $prodids[$i] = $row[0];
2360 }
2361 }
2362
2363 // Initialise parameters
2364 $this->id = 0;
2365 $this->ref = 'SPECIMEN';
2366 $this->specimen = 1;
2367 $this->socid = 1;
2368 $this->date = time();
2369 $this->cond_reglement_id = 1;
2370 $this->cond_reglement_code = 'RECEP';
2371 $this->mode_reglement_id = 7;
2372 $this->mode_reglement_code = 'CHQ';
2373 $this->note_public = 'This is a comment (public)';
2374 $this->note_private = 'This is a comment (private)';
2375 // Lines
2376 $nbp = 5;
2377 $xnbp = 0;
2378 while ($xnbp < $nbp) {
2379 $line = new SupplierProposalLine($this->db);
2380 $line->desc = $langs->trans("Description")." ".$xnbp;
2381 $line->qty = 1;
2382 $line->subprice = 100;
2383 $line->tva_tx = 19.6;
2384 $line->localtax1_tx = 0;
2385 $line->localtax2_tx = 0;
2386 if ($xnbp == 2) {
2387 $line->total_ht = 50;
2388 $line->total_ttc = 59.8;
2389 $line->total_tva = 9.8;
2390 $line->remise_percent = 50;
2391 } else {
2392 $line->total_ht = 100;
2393 $line->total_ttc = 119.6;
2394 $line->total_tva = 19.6;
2395 $line->remise_percent = 00;
2396 }
2397
2398 if ($num_prods > 0) {
2399 $prodid = mt_rand(1, $num_prods);
2400 $line->fk_product = $prodids[$prodid];
2401 }
2402
2403 $this->lines[$xnbp] = $line;
2404
2405 $this->total_ht += $line->total_ht;
2406 $this->total_tva += $line->total_tva;
2407 $this->total_ttc += $line->total_ttc;
2408
2409 $xnbp++;
2410 }
2411
2412 return 1;
2413 }
2414
2420 public function loadStateBoard()
2421 {
2422 global $conf, $user;
2423
2424 $this->nb = array();
2425 $clause = "WHERE";
2426
2427 $sql = "SELECT count(p.rowid) as nb";
2428 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2429 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
2430 if (!$user->hasRight('societe', 'client', 'voir')) {
2431 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2432 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2433 $clause = "AND";
2434 }
2435 $sql .= " ".$clause." p.entity IN (".getEntity('supplier_proposal').")";
2436
2437 $resql = $this->db->query($sql);
2438 if ($resql) {
2439 // This assignment in condition is not a bug. It allows walking the results.
2440 while ($obj = $this->db->fetch_object($resql)) {
2441 $this->nb["supplier_proposals"] = $obj->nb;
2442 }
2443 $this->db->free($resql);
2444 return 1;
2445 } else {
2446 dol_print_error($this->db);
2447 $this->error = $this->db->lasterror();
2448 return -1;
2449 }
2450 }
2451
2452
2460 public function getNextNumRef($soc)
2461 {
2462 global $conf, $db, $langs;
2463 $langs->load("supplier_proposal");
2464
2465 if (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON')) {
2466 $mybool = false;
2467
2468 $file = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') . ".php";
2469 $classname = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
2470
2471 // Include file with class
2472 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2473 foreach ($dirmodels as $reldir) {
2474 $dir = dol_buildpath($reldir."core/modules/supplier_proposal/");
2475
2476 // Load file with numbering class (if found)
2477 $mybool = ((bool) @include_once $dir.$file) || $mybool;
2478 }
2479
2480 if (!$mybool) {
2481 dol_print_error(null, "Failed to include file ".$file);
2482 return '';
2483 }
2484
2485 $obj = new $classname();
2486 '@phan-var-force ModeleNumRefSupplierProposal $obj';
2487 $numref = "";
2488 $numref = $obj->getNextValue($soc, $this);
2489
2490 if ($numref != "") {
2491 return $numref;
2492 } else {
2493 $this->error = $obj->error;
2494 return "";
2495 }
2496 } else {
2497 $langs->load("errors");
2498 print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("SupplierProposal"));
2499 return "";
2500 }
2501 }
2502
2509 public function getTooltipContentArray($params)
2510 {
2511 global $conf, $langs, $menumanager;
2512
2513 $langs->load('supplier_proposal');
2514
2515 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2516 return ['optimize' => $langs->trans("ShowSupplierProposal")];
2517 }
2518
2519 $option = $params['option'] ?? '';
2520 $datas = [];
2521
2522 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("SupplierProposal").'</u>';
2523 if (isset($this->status)) {
2524 $datas['picto'] .= ' '.$this->getLibStatut(5);
2525 }
2526 if (!empty($this->ref)) {
2527 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2528 }
2529 if (!empty($this->ref_fourn)) {
2530 $datas['ref_supplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_fourn;
2531 }
2532 if (!empty($this->total_ht)) {
2533 $datas['amount_ht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2534 }
2535 if (!empty($this->total_tva)) {
2536 $datas['amount_vat'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2537 }
2538 if (!empty($this->total_ttc)) {
2539 $datas['amount_ttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2540 }
2541
2542 return $datas;
2543 }
2544
2556 public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2557 {
2558 global $langs, $conf, $user, $hookmanager;
2559
2560 if (!empty($conf->dol_no_mouse_hover)) {
2561 $notooltip = 1; // Force disable tooltips
2562 }
2563
2564 $url = '';
2565 $result = '';
2566 $params = [
2567 'id' => $this->id,
2568 'objecttype' => $this->element,
2569 'option' => $option,
2570 ];
2571 $classfortooltip = 'classfortooltip';
2572 $dataparams = '';
2573 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2574 $classfortooltip = 'classforajaxtooltip';
2575 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
2576 $label = '';
2577 } else {
2578 $label = implode($this->getTooltipContentArray($params));
2579 }
2580
2581 if ($option == '') {
2582 $url = DOL_URL_ROOT.'/supplier_proposal/card.php?id='.$this->id.$get_params;
2583 }
2584 if ($option == 'document') {
2585 $url = DOL_URL_ROOT.'/supplier_proposal/document.php?id='.$this->id.$get_params;
2586 }
2587
2588 if ($option !== 'nolink') {
2589 // Add param to save lastsearch_values or not
2590 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2591 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2592 $add_save_lastsearch_values = 1;
2593 }
2594 if ($add_save_lastsearch_values) {
2595 $url .= '&save_lastsearch_values=1';
2596 }
2597 }
2598
2599 $linkclose = '';
2600 if (empty($notooltip) && $user->hasRight('propal', 'lire')) {
2601 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2602 $label = $langs->trans("ShowSupplierProposal");
2603 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
2604 }
2605 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
2606 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
2607 }
2608
2609 $linkstart = '<a href="'.$url.'"';
2610 $linkstart .= $linkclose.'>';
2611 $linkend = '</a>';
2612
2613 $result .= $linkstart;
2614 if ($withpicto) {
2615 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
2616 }
2617 if ($withpicto != 2) {
2618 $result .= $this->ref;
2619 }
2620 $result .= $linkend;
2621
2622 if ($addlinktonotes) {
2623 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2624 if ($txttoshow) {
2625 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2626 $result .= ' <span class="note inline-block">';
2627 $result .= '<a href="'.DOL_URL_ROOT.'/supplier_proposal/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2628 $result .= img_picto('', 'note');
2629 $result .= '</a>';
2630 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
2631 //$result.='</a>';
2632 $result .= '</span>';
2633 }
2634 }
2635 global $action;
2636 $hookmanager->initHooks(array($this->element . 'dao'));
2637 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
2638 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2639 if ($reshook > 0) {
2640 $result = $hookmanager->resPrint;
2641 } else {
2642 $result .= $hookmanager->resPrint;
2643 }
2644 return $result;
2645 }
2646
2652 public function getLinesArray()
2653 {
2654 // For other object, here we call fetch_lines. But fetch_lines does not exists on supplier proposal
2655
2656 $sql = 'SELECT pt.rowid, pt.label as custom_label, pt.description, pt.fk_product, pt.fk_remise_except,';
2657 $sql .= ' pt.qty, pt.tva_tx, pt.vat_src_code, pt.remise_percent, pt.subprice, pt.info_bits,';
2658 $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,';
2659 $sql .= ' pt.product_type, pt.rang, pt.fk_parent_line,';
2660 $sql .= ' p.label as product_label, p.ref, p.fk_product_type, p.rowid as prodid,';
2661 $sql .= ' p.description as product_desc, pt.ref_fourn as ref_supplier,';
2662 $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';
2663 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pt';
2664 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pt.fk_product=p.rowid';
2665 $sql .= ' WHERE pt.fk_supplier_proposal = '.((int) $this->id);
2666 $sql .= ' ORDER BY pt.rang ASC, pt.rowid';
2667
2668 dol_syslog(get_class($this).'::getLinesArray', LOG_DEBUG);
2669 $resql = $this->db->query($sql);
2670 if ($resql) {
2671 $num = $this->db->num_rows($resql);
2672 $i = 0;
2673
2674 while ($i < $num) {
2675 $obj = $this->db->fetch_object($resql);
2676
2677 $this->lines[$i] = new SupplierProposalLine($this->db);
2678 $this->lines[$i]->id = $obj->rowid; // for backward compatibility
2679 $this->lines[$i]->rowid = $obj->rowid;
2680 $this->lines[$i]->label = $obj->custom_label;
2681 $this->lines[$i]->description = $obj->description;
2682 $this->lines[$i]->fk_product = $obj->fk_product;
2683 $this->lines[$i]->ref = $obj->ref;
2684 $this->lines[$i]->product_label = $obj->product_label;
2685 $this->lines[$i]->product_desc = $obj->product_desc;
2686 $this->lines[$i]->fk_product_type = $obj->fk_product_type; // deprecated
2687 $this->lines[$i]->product_type = $obj->product_type;
2688 $this->lines[$i]->qty = $obj->qty;
2689 $this->lines[$i]->subprice = $obj->subprice;
2690 $this->lines[$i]->fk_remise_except = $obj->fk_remise_except;
2691 $this->lines[$i]->remise_percent = $obj->remise_percent;
2692 $this->lines[$i]->tva_tx = $obj->tva_tx;
2693 $this->lines[$i]->vat_src_code = $obj->vat_src_code;
2694 $this->lines[$i]->info_bits = $obj->info_bits;
2695 $this->lines[$i]->total_ht = $obj->total_ht;
2696 $this->lines[$i]->total_tva = $obj->total_tva;
2697 $this->lines[$i]->total_ttc = $obj->total_ttc;
2698 $this->lines[$i]->fk_fournprice = $obj->fk_fournprice;
2699 $marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->lines[$i]->fk_fournprice, $obj->pa_ht);
2700 $this->lines[$i]->pa_ht = $marginInfos[0];
2701 $this->lines[$i]->marge_tx = $marginInfos[1];
2702 $this->lines[$i]->marque_tx = $marginInfos[2];
2703 $this->lines[$i]->fk_parent_line = $obj->fk_parent_line;
2704 $this->lines[$i]->special_code = $obj->special_code;
2705 $this->lines[$i]->rang = $obj->rang;
2706
2707 $this->lines[$i]->ref_fourn = $obj->ref_supplier; // deprecated
2708 $this->lines[$i]->ref_supplier = $obj->ref_supplier;
2709
2710 // Multicurrency
2711 $this->lines[$i]->fk_multicurrency = $obj->fk_multicurrency;
2712 $this->lines[$i]->multicurrency_code = $obj->multicurrency_code;
2713 $this->lines[$i]->multicurrency_subprice = $obj->multicurrency_subprice;
2714 $this->lines[$i]->multicurrency_total_ht = $obj->multicurrency_total_ht;
2715 $this->lines[$i]->multicurrency_total_tva = $obj->multicurrency_total_tva;
2716 $this->lines[$i]->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
2717 $this->lines[$i]->fk_unit = $obj->fk_unit;
2718
2719 $i++;
2720 }
2721 $this->db->free($resql);
2722
2723 return 1;
2724 } else {
2725 $this->error = $this->db->error();
2726 return -1;
2727 }
2728 }
2729
2741 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2742 {
2743 global $conf, $langs;
2744
2745 $langs->load("supplier_proposal");
2746 $outputlangs->load("products");
2747
2748 if (!dol_strlen($modele)) {
2749 $modele = 'aurore';
2750
2751 if ($this->model_pdf) {
2752 $modele = $this->model_pdf;
2753 } elseif (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF')) {
2754 $modele = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF');
2755 }
2756 }
2757
2758 $modelpath = "core/modules/supplier_proposal/doc/";
2759
2760 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2761 }
2762
2763
2772 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2773 {
2774 $tables = array(
2775 'supplier_proposal'
2776 );
2777
2778 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2779 }
2780
2789 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
2790 {
2791 $tables = array(
2792 'supplier_proposaldet'
2793 );
2794
2795 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
2796 }
2797
2798
2806 public function getKanbanView($option = '', $arraydata = null)
2807 {
2808 global $langs;
2809
2810 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2811
2812 $return = '<div class="box-flex-item box-flex-grow-zero">';
2813 $return .= '<div class="info-box info-box-sm">';
2814 $return .= '<span class="info-box-icon bg-infobox-action">';
2815 $return .= img_picto('', $this->picto);
2816 //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2817 $return .= '</span>';
2818 $return .= '<div class="info-box-content">';
2819 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
2820 if ($selected >= 0) {
2821 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2822 }
2823 if (property_exists($this, 'socid')) {
2824 $return .= '<span class="info-box-ref"> | '.$this->socid.'</span>';
2825 }
2826 if (property_exists($this, 'delivery_date')) {
2827 $return .= '<br><span class="opacitymedium">'.$langs->trans("DateEnd").'</span> : <span class="info-box-label">'.dol_print_date($this->delivery_date).'</span>';
2828 }
2829 if (property_exists($this, 'total_ttc')) {
2830 $return .= '<br><span class="opacitymedium" >'.$langs->trans("AmountHT").' : </span><span class="info-box-label amount">'.price($this->total_ttc).'</span>';
2831 }
2832 if (method_exists($this, 'getLibStatut')) {
2833 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
2834 }
2835 $return .= '</div>';
2836 $return .= '</div>';
2837 $return .= '</div>';
2838 return $return;
2839 }
2840}
2841
2842
2847{
2851 public $db;
2852
2856 public $error = '';
2857
2861 public $element = 'supplier_proposaldet';
2862
2866 public $table_element = 'supplier_proposaldet';
2867
2871 public $parent_element = 'supplier_proposal';
2872
2876 public $fk_parent_attribute = 'fk_supplier_proposal';
2877
2881 public $oldline;
2882
2886 public $id;
2887
2891 public $fk_supplier_proposal;
2892
2896 public $fk_parent_line;
2897
2901 public $desc; // Description ligne
2902
2906 public $fk_product; // Id produit predefini
2907
2913 public $fk_product_type;
2919 public $product_type = Product::TYPE_PRODUCT;
2920
2924 public $qty;
2928 public $tva_tx;
2932 public $vat_src_code;
2933
2938 public $subprice;
2942 public $remise_percent;
2943
2947 public $fk_remise_except;
2948
2952 public $rang = 0;
2953
2957 public $fk_fournprice;
2958
2962 public $pa_ht;
2966 public $marge_tx;
2970 public $marque_tx;
2971
2975 public $special_code; // Tag for special lines (exclusive tags)
2976 // 1: frais de port
2977 // 2: ecotaxe
2978 // 3: option line (when qty = 0)
2979
2983 public $info_bits = 0; // Liste d'options cumulables:
2984 // Bit 0: 0 si TVA normal - 1 if TVA NPR
2985 // Bit 1: 0 ligne normal - 1 if fixed reduction
2986
2990 public $total_ht; // Total HT de la ligne toute quantite et incluant la remise ligne
2994 public $total_tva; // Total TVA de la ligne toute quantite et incluant la remise ligne
2998 public $total_ttc; // Total TTC de la ligne toute quantite et incluant la remise ligne
2999
3003 public $date_start;
3007 public $date_end;
3008
3009 // From llx_product
3015 public $ref;
3016
3021 public $product_ref;
3022
3028 public $libelle;
3029
3034 public $product_label;
3035
3040 public $label;
3041
3046 public $product_desc;
3047
3051 public $localtax1_tx; // Local tax 1
3055 public $localtax2_tx; // Local tax 2
3059 public $localtax1_type; // Local tax 1 type
3063 public $localtax2_type; // Local tax 2 type
3067 public $total_localtax1; // Line total local tax 1
3071 public $total_localtax2; // Line total local tax 2
3072
3076 public $skip_update_total; // Skip update price total for special lines
3077
3081 public $ref_fourn;
3085 public $ref_supplier;
3086
3087 // Multicurrency
3091 public $fk_multicurrency;
3092
3096 public $multicurrency_code;
3100 public $multicurrency_subprice;
3104 public $multicurrency_total_ht;
3108 public $multicurrency_total_tva;
3112 public $multicurrency_total_ttc;
3113
3119 public function __construct($db)
3120 {
3121 $this->db = $db;
3122 }
3123
3130 public function fetch($rowid)
3131 {
3132 $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,';
3133 $sql .= ' pd.date_start, pd.date_end,';
3134 $sql .= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice,';
3135 $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,';
3136 $sql .= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
3137 $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
3138 $sql .= ' pd.product_type, pd.ref_fourn as ref_produit_fourn,';
3139 $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';
3140 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pd';
3141 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
3142 $sql .= ' WHERE pd.rowid = '.((int) $rowid);
3143
3144 $result = $this->db->query($sql);
3145 if ($result) {
3146 if ($objp = $this->db->fetch_object($result)) {
3147 $this->id = $objp->rowid;
3148 $this->fk_supplier_proposal = $objp->fk_supplier_proposal;
3149 $this->fk_parent_line = $objp->fk_parent_line;
3150 $this->label = $objp->custom_label;
3151 $this->desc = $objp->description;
3152 $this->qty = $objp->qty;
3153 $this->subprice = $objp->subprice;
3154 $this->tva_tx = $objp->tva_tx;
3155 $this->remise_percent = $objp->remise_percent;
3156 $this->fk_remise_except = $objp->fk_remise_except;
3157 $this->fk_product = $objp->fk_product;
3158 $this->info_bits = $objp->info_bits;
3159 $this->date_start = $this->db->jdate($objp->date_start);
3160 $this->date_end = $this->db->jdate($objp->date_end);
3161
3162 $this->total_ht = $objp->total_ht;
3163 $this->total_tva = $objp->total_tva;
3164 $this->total_ttc = $objp->total_ttc;
3165
3166 $this->fk_fournprice = $objp->fk_fournprice;
3167
3168 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
3169 $this->pa_ht = $marginInfos[0];
3170 $this->marge_tx = $marginInfos[1];
3171 $this->marque_tx = $marginInfos[2];
3172
3173 $this->special_code = $objp->special_code;
3174 $this->product_type = $objp->product_type;
3175 $this->rang = $objp->rang;
3176
3177 $this->ref = $objp->product_ref; // deprecated
3178 $this->product_ref = $objp->product_ref;
3179 $this->libelle = $objp->product_label; // deprecated
3180 $this->product_label = $objp->product_label;
3181 $this->product_desc = $objp->product_desc;
3182
3183 $this->ref_fourn = $objp->ref_produit_fourn;
3184
3185 // Multicurrency
3186 $this->fk_multicurrency = $objp->fk_multicurrency;
3187 $this->multicurrency_code = $objp->multicurrency_code;
3188 $this->multicurrency_subprice = $objp->multicurrency_subprice;
3189 $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3190 $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3191 $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3192 $this->fk_unit = $objp->fk_unit;
3193
3194 $this->db->free($result);
3195 return 1;
3196 }
3197 return 0;
3198 } else {
3199 dol_print_error($this->db);
3200 return -1;
3201 }
3202 }
3203
3210 public function insert($notrigger = 0)
3211 {
3212 global $conf, $langs, $user;
3213
3214 $error = 0;
3215
3216 dol_syslog(get_class($this)."::insert rang=".$this->rang);
3217
3218 // Clean parameters
3219 if (empty($this->tva_tx)) {
3220 $this->tva_tx = 0;
3221 }
3222 if (empty($this->vat_src_code)) {
3223 $this->vat_src_code = '';
3224 }
3225 if (empty($this->localtax1_tx)) {
3226 $this->localtax1_tx = 0;
3227 }
3228 if (empty($this->localtax2_tx)) {
3229 $this->localtax2_tx = 0;
3230 }
3231 if (empty($this->localtax1_type)) {
3232 $this->localtax1_type = '';
3233 }
3234 if (empty($this->localtax2_type)) {
3235 $this->localtax2_type = '';
3236 }
3237 if (empty($this->total_localtax1)) {
3238 $this->total_localtax1 = 0;
3239 }
3240 if (empty($this->total_localtax2)) {
3241 $this->total_localtax2 = 0;
3242 }
3243 if (empty($this->rang)) {
3244 $this->rang = 0;
3245 }
3246 if (empty($this->remise_percent)) {
3247 $this->remise_percent = 0;
3248 }
3249 if (empty($this->info_bits)) {
3250 $this->info_bits = 0;
3251 }
3252 if (empty($this->special_code)) {
3253 $this->special_code = 0;
3254 }
3255 if (empty($this->fk_parent_line)) {
3256 $this->fk_parent_line = 0;
3257 }
3258 if (empty($this->fk_fournprice)) {
3259 $this->fk_fournprice = 0;
3260 }
3261 if (empty($this->fk_unit)) {
3262 $this->fk_unit = 0;
3263 }
3264 if (empty($this->subprice)) {
3265 $this->subprice = 0;
3266 }
3267
3268 if (empty($this->pa_ht)) {
3269 $this->pa_ht = 0;
3270 }
3271
3272 // if buy price not defined, define buyprice as configured in margin admin
3273 if ($this->pa_ht == 0) {
3274 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3275 if ($result < 0) {
3276 return $result;
3277 } else {
3278 $this->pa_ht = $result;
3279 }
3280 }
3281
3282 // Check parameters
3283 if ($this->product_type < 0) {
3284 return -1;
3285 }
3286
3287 $this->db->begin();
3288
3289 // Insert line into database
3290 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'supplier_proposaldet';
3291 $sql .= ' (fk_supplier_proposal, fk_parent_line, label, description, fk_product, product_type,';
3292 $sql .= ' date_start, date_end,';
3293 $sql .= ' fk_remise_except, qty, tva_tx, vat_src_code, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3294 $sql .= ' subprice, remise_percent, ';
3295 $sql .= ' info_bits, ';
3296 $sql .= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
3297 $sql .= ' ref_fourn,';
3298 $sql .= ' fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc, fk_unit)';
3299 $sql .= " VALUES (".$this->fk_supplier_proposal.",";
3300 $sql .= " ".($this->fk_parent_line > 0 ? ((int) $this->fk_parent_line) : "null").",";
3301 $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
3302 $sql .= " '".$this->db->escape($this->desc)."',";
3303 $sql .= " ".($this->fk_product ? ((int) $this->fk_product) : "null").",";
3304 $sql .= " '".$this->db->escape($this->product_type)."',";
3305 $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3306 $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3307 $sql .= " ".($this->fk_remise_except ? ((int) $this->fk_remise_except) : "null").",";
3308 $sql .= " ".price2num($this->qty, 'MS').",";
3309 $sql .= " ".price2num($this->tva_tx).",";
3310 $sql .= " '".$this->db->escape($this->vat_src_code)."',";
3311 $sql .= " ".price2num($this->localtax1_tx).",";
3312 $sql .= " ".price2num($this->localtax2_tx).",";
3313 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3314 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3315 $sql .= " ".price2num($this->subprice, 'MU') .",";
3316 $sql .= " ".((float) $this->remise_percent).",";
3317 $sql .= " ".(isset($this->info_bits) ? ((int) $this->info_bits) : "null").",";
3318 $sql .= " ".price2num($this->total_ht, 'MT').",";
3319 $sql .= " ".price2num($this->total_tva, 'MT').",";
3320 $sql .= " ".price2num($this->total_localtax1, 'MT').",";
3321 $sql .= " ".price2num($this->total_localtax2, 'MT').",";
3322 $sql .= " ".price2num($this->total_ttc, 'MT').",";
3323 $sql .= " ".(!empty($this->fk_fournprice) ? ((int) $this->fk_fournprice) : "null").",";
3324 $sql .= " ".(isset($this->pa_ht) ? price2num($this->pa_ht, 'MU') : "null").",";
3325 $sql .= ' '.((int) $this->special_code).',';
3326 $sql .= ' '.((int) $this->rang).',';
3327 $sql .= " '".$this->db->escape($this->ref_fourn)."'";
3328 $sql .= ", ".($this->fk_multicurrency > 0 ? ((int) $this->fk_multicurrency) : 'null');
3329 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3330 $sql .= ", ".price2num($this->multicurrency_subprice, 'CU');
3331 $sql .= ", ".price2num($this->multicurrency_total_ht, 'CT');
3332 $sql .= ", ".price2num($this->multicurrency_total_tva, 'CT');
3333 $sql .= ", ".price2num($this->multicurrency_total_ttc, 'CT');
3334 $sql .= ", ".($this->fk_unit ? ((int) $this->fk_unit) : 'null');
3335 $sql .= ')';
3336
3337 dol_syslog(get_class($this).'::insert', LOG_DEBUG);
3338 $resql = $this->db->query($sql);
3339 if ($resql) {
3340 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'supplier_proposaldet');
3341
3342 if (!$error) {
3343 $result = $this->insertExtraFields();
3344 if ($result < 0) {
3345 $error++;
3346 }
3347 }
3348
3349 if (!$error && !$notrigger) {
3350 // Call trigger
3351 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_INSERT', $user);
3352 if ($result < 0) {
3353 $this->db->rollback();
3354 return -1;
3355 }
3356 // End call triggers
3357 }
3358
3359 $this->db->commit();
3360 return 1;
3361 } else {
3362 $this->error = $this->db->error()." sql=".$sql;
3363 $this->db->rollback();
3364 return -1;
3365 }
3366 }
3367
3374 public function delete($user)
3375 {
3376 $error = 0;
3377
3378 $this->db->begin();
3379
3380 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet";
3381 $sql .= " WHERE rowid = ".((int) $this->id);
3382
3383 if ($this->db->query($sql)) {
3384 // Remove extrafields
3385 if (!$error) {
3386 $result = $this->deleteExtraFields();
3387 if ($result < 0) {
3388 $error++;
3389 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3390 }
3391 }
3392
3393 // Call trigger
3394 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_DELETE', $user);
3395 if ($result < 0) {
3396 $this->db->rollback();
3397 return -1;
3398 }
3399 // End call triggers
3400
3401 $this->db->commit();
3402
3403 return 1;
3404 } else {
3405 $this->error = $this->db->error()." sql=".$sql;
3406 $this->db->rollback();
3407 return -1;
3408 }
3409 }
3410
3417 public function update($notrigger = 0)
3418 {
3419 global $conf, $langs, $user;
3420
3421 $error = 0;
3422
3423 // Clean parameters
3424 if (empty($this->tva_tx)) {
3425 $this->tva_tx = 0;
3426 }
3427 if (empty($this->localtax1_tx)) {
3428 $this->localtax1_tx = 0;
3429 }
3430 if (empty($this->localtax2_tx)) {
3431 $this->localtax2_tx = 0;
3432 }
3433 if (empty($this->total_localtax1)) {
3434 $this->total_localtax1 = 0;
3435 }
3436 if (empty($this->total_localtax2)) {
3437 $this->total_localtax2 = 0;
3438 }
3439 if (empty($this->localtax1_type)) {
3440 $this->localtax1_type = '';
3441 }
3442 if (empty($this->localtax2_type)) {
3443 $this->localtax2_type = '';
3444 }
3445 if (empty($this->marque_tx)) {
3446 $this->marque_tx = 0;
3447 }
3448 if (empty($this->marge_tx)) {
3449 $this->marge_tx = 0;
3450 }
3451 if (empty($this->remise_percent)) {
3452 $this->remise_percent = 0;
3453 }
3454 if (empty($this->info_bits)) {
3455 $this->info_bits = 0;
3456 }
3457 if (empty($this->special_code)) {
3458 $this->special_code = 0;
3459 }
3460 if (empty($this->fk_parent_line)) {
3461 $this->fk_parent_line = 0;
3462 }
3463 if (empty($this->fk_fournprice)) {
3464 $this->fk_fournprice = 0;
3465 }
3466 if (empty($this->fk_unit)) {
3467 $this->fk_unit = 0;
3468 }
3469 if (empty($this->subprice)) {
3470 $this->subprice = 0;
3471 }
3472
3473 if (empty($this->pa_ht)) {
3474 $this->pa_ht = 0;
3475 }
3476
3477 // if buy price not defined, define buyprice as configured in margin admin
3478 if ($this->pa_ht == 0) {
3479 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3480 if ($result < 0) {
3481 return $result;
3482 } else {
3483 $this->pa_ht = $result;
3484 }
3485 }
3486
3487 $this->db->begin();
3488
3489 // Mise a jour ligne en base
3490 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3491 $sql .= " description='".$this->db->escape($this->desc)."'";
3492 $sql .= " , label=".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
3493 $sql .= " , product_type=".((int) $this->product_type);
3494 $sql .= " , date_start=".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null");
3495 $sql .= " , date_end=".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null");
3496 $sql .= " , tva_tx='".price2num($this->tva_tx)."'";
3497 $sql .= " , localtax1_tx=".price2num($this->localtax1_tx);
3498 $sql .= " , localtax2_tx=".price2num($this->localtax2_tx);
3499 $sql .= " , localtax1_type='".$this->db->escape($this->localtax1_type)."'";
3500 $sql .= " , localtax2_type='".$this->db->escape($this->localtax2_type)."'";
3501 $sql .= " , qty='".price2num($this->qty)."'";
3502 $sql .= " , subprice=".price2num($this->subprice);
3503 $sql .= " , remise_percent=".price2num($this->remise_percent);
3504 $sql .= " , info_bits='".$this->db->escape($this->info_bits)."'";
3505 if (empty($this->skip_update_total)) {
3506 $sql .= " , total_ht=".price2num($this->total_ht);
3507 $sql .= " , total_tva=".price2num($this->total_tva);
3508 $sql .= " , total_ttc=".price2num($this->total_ttc);
3509 $sql .= " , total_localtax1=".price2num($this->total_localtax1);
3510 $sql .= " , total_localtax2=".price2num($this->total_localtax2);
3511 }
3512 $sql .= " , fk_product_fournisseur_price=".(!empty($this->fk_fournprice) ? "'".$this->db->escape($this->fk_fournprice)."'" : "null");
3513 $sql .= " , buy_price_ht=".price2num($this->pa_ht);
3514 $sql .= " , special_code=".((int) $this->special_code);
3515 $sql .= " , fk_parent_line=".($this->fk_parent_line > 0 ? $this->fk_parent_line : "null");
3516 if (!empty($this->rang)) {
3517 $sql .= ", rang=".((int) $this->rang);
3518 }
3519 $sql .= " , ref_fourn=".(!empty($this->ref_fourn) ? "'".$this->db->escape($this->ref_fourn)."'" : "null");
3520 $sql .= " , fk_unit=".($this->fk_unit ? $this->fk_unit : 'null');
3521
3522 // Multicurrency
3523 $sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice);
3524 $sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
3525 $sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
3526 $sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
3527
3528 $sql .= " WHERE rowid = ".((int) $this->id);
3529
3530 dol_syslog(get_class($this)."::update", LOG_DEBUG);
3531 $resql = $this->db->query($sql);
3532 if ($resql) {
3533 if (!$error) {
3534 $result = $this->insertExtraFields();
3535 if ($result < 0) {
3536 $error++;
3537 }
3538 }
3539
3540 if (!$error && !$notrigger) {
3541 // Call trigger
3542 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_MODIFY', $user);
3543 if ($result < 0) {
3544 $this->db->rollback();
3545 return -1;
3546 }
3547 // End call triggers
3548 }
3549
3550 $this->db->commit();
3551 return 1;
3552 } else {
3553 $this->error = $this->db->error();
3554 $this->db->rollback();
3555 return -2;
3556 }
3557 }
3558
3559 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3566 public function update_total()
3567 {
3568 // phpcs:enable
3569 $this->db->begin();
3570
3571 // Mise a jour ligne en base
3572 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3573 $sql .= " total_ht=".price2num($this->total_ht, 'MT');
3574 $sql .= ",total_tva=".price2num($this->total_tva, 'MT');
3575 $sql .= ",total_ttc=".price2num($this->total_ttc, 'MT');
3576 $sql .= " WHERE rowid = ".((int) $this->id);
3577
3578 dol_syslog("SupplierProposalLine::update_total", LOG_DEBUG);
3579
3580 $resql = $this->db->query($sql);
3581 if ($resql) {
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}
$object ref
Definition info.php:79
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.
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='')
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.
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.
reopen($user, $statut, $note='', $notrigger=0)
Set an overall discount on the proposal.
loadStateBoard()
Load indicator this->nb of global stats widget.
deleteLine($lineid)
Delete detail line.
const STATUS_VALIDATED
Validated status.
createPriceFournisseur($product, $user)
Create ProductFournisseur.
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:162
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
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_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_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
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 '.
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)
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_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
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.
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller='', $localtaxes_array=[], $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition price.lib.php:88