dolibarr 21.0.3
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 (getDolGlobalInt('SUPPLIER_PROPOSAL_WITH_PREDEFINED_PRICES_ONLY') == 1) { // Not the common case
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(((float) $pu * (float) $remise_percent / 100), 2);
597 $price = (float) $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 = (float) $pu_ht;
617 $this->line->rang = $ranktouse;
618 $this->line->info_bits = $info_bits;
619 $this->line->total_ht = (float) $total_ht;
620 $this->line->total_tva = (float) $total_tva;
621 $this->line->total_localtax1 = (float) $total_localtax1;
622 $this->line->total_localtax2 = (float) $total_localtax2;
623 $this->line->total_ttc = (float) $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 = (float) $pu_ht_devise;
651 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
652 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
653 $this->line->multicurrency_total_ttc = (float) $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 = (float) $pu;
825 $this->line->info_bits = $info_bits;
826 $this->line->total_ht = (float) $total_ht;
827 $this->line->total_tva = (float) $total_tva;
828 $this->line->total_localtax1 = (float) $total_localtax1;
829 $this->line->total_localtax2 = (float) $total_localtax2;
830 $this->line->total_ttc = (float) $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 = (float) $pu_ht_devise;
858 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
859 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
860 $this->line->multicurrency_total_ttc = (float) $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.last_main_doc";
1259 $sql .= ", p.fk_multicurrency, p.multicurrency_code, p.multicurrency_tx, p.multicurrency_total_ht, p.multicurrency_total_tva, p.multicurrency_total_ttc";
1260 $sql .= ", c.label as statut_label";
1261 $sql .= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc";
1262 $sql .= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement";
1263 $sql .= " FROM ".MAIN_DB_PREFIX."c_propalst as c, ".MAIN_DB_PREFIX."supplier_proposal as p";
1264 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id';
1265 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON p.fk_cond_reglement = cr.rowid';
1266 $sql .= " WHERE p.fk_statut = c.id";
1267 $sql .= " AND p.entity IN (".getEntity('supplier_proposal').")";
1268 if ($ref) {
1269 $sql .= " AND p.ref = '".$this->db->escape($ref)."'";
1270 } else {
1271 $sql .= " AND p.rowid = ".((int) $rowid);
1272 }
1273
1274 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1275 $resql = $this->db->query($sql);
1276 if ($resql) {
1277 if ($this->db->num_rows($resql)) {
1278 $obj = $this->db->fetch_object($resql);
1279
1280 $this->id = $obj->rowid;
1281 $this->entity = $obj->entity;
1282
1283 $this->ref = $obj->ref;
1284 $this->total_ht = $obj->total_ht;
1285 $this->total_tva = $obj->total_tva;
1286 $this->total_localtax1 = $obj->localtax1;
1287 $this->total_localtax2 = $obj->localtax2;
1288 $this->total_ttc = $obj->total_ttc;
1289 $this->socid = $obj->socid;
1290 $this->fk_project = $obj->fk_project;
1291 $this->model_pdf = $obj->model_pdf;
1292 $this->note = $obj->note_private; // TODO deprecated
1293 $this->note_private = $obj->note_private;
1294 $this->note_public = $obj->note_public;
1295 $this->statut = (int) $obj->fk_statut;
1296 $this->status = (int) $obj->fk_statut;
1297 $this->datec = $this->db->jdate($obj->datec); // TODO deprecated
1298 $this->datev = $this->db->jdate($obj->datev); // TODO deprecated
1299 $this->date_creation = $this->db->jdate($obj->datec); // Creation date
1300 $this->date = $this->date_creation;
1301 $this->date_validation = $this->db->jdate($obj->datev); // Validation date
1302 $this->delivery_date = $this->db->jdate($obj->delivery_date);
1303 $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1304
1305 $this->last_main_doc = $obj->last_main_doc;
1306 $this->mode_reglement_id = $obj->fk_mode_reglement;
1307 $this->mode_reglement_code = $obj->mode_reglement_code;
1308 $this->mode_reglement = $obj->mode_reglement;
1309 $this->fk_account = ($obj->fk_account > 0) ? $obj->fk_account : null;
1310 $this->cond_reglement_id = $obj->fk_cond_reglement;
1311 $this->cond_reglement_code = $obj->cond_reglement_code;
1312 $this->cond_reglement = $obj->cond_reglement;
1313 $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1314
1315 $this->extraparams = (array) (!empty($obj->extraparams) ? json_decode($obj->extraparams, true) : array());
1316
1317 $this->user_author_id = $obj->fk_user_author;
1318 $this->user_validation_id = $obj->fk_user_valid;
1319 $this->user_closing_id = $obj->fk_user_cloture;
1320
1321 // Multicurrency
1322 $this->fk_multicurrency = $obj->fk_multicurrency;
1323 $this->multicurrency_code = $obj->multicurrency_code;
1324 $this->multicurrency_tx = $obj->multicurrency_tx;
1325 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1326 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1327 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1328
1329 // Retrieve all extrafield
1330 // fetch optionals attributes and labels
1331 $this->fetch_optionals();
1332
1333 $this->db->free($resql);
1334
1335 $this->lines = array();
1336
1337 // Lines of supplier proposals
1338 $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,";
1339 $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,";
1340 $sql .= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label,';
1341 $sql .= ' d.ref_fourn as ref_produit_fourn,';
1342 $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';
1343 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposaldet as d";
1344 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON d.fk_product = p.rowid";
1345 $sql .= " WHERE d.fk_supplier_proposal = ".((int) $this->id);
1346 $sql .= " ORDER by d.rang";
1347
1348 $result = $this->db->query($sql);
1349 if ($result) {
1350 $num = $this->db->num_rows($result);
1351 $i = 0;
1352
1353 while ($i < $num) {
1354 $objp = $this->db->fetch_object($result);
1355
1356 $line = new SupplierProposalLine($this->db);
1357
1358 $line->rowid = $objp->rowid; // deprecated
1359 $line->id = $objp->rowid;
1360 $line->fk_supplier_proposal = $objp->fk_supplier_proposal;
1361 $line->fk_parent_line = $objp->fk_parent_line;
1362 $line->product_type = $objp->product_type;
1363 $line->label = $objp->custom_label;
1364 $line->desc = $objp->description; // Description ligne
1365 $line->qty = $objp->qty;
1366 $line->tva_tx = $objp->tva_tx;
1367 $line->localtax1_tx = $objp->localtax1_tx;
1368 $line->localtax2_tx = $objp->localtax2_tx;
1369 $line->subprice = $objp->subprice;
1370 $line->fk_remise_except = $objp->fk_remise_except;
1371 $line->remise_percent = $objp->remise_percent;
1372
1373 $line->info_bits = $objp->info_bits;
1374 $line->total_ht = $objp->total_ht;
1375 $line->total_tva = $objp->total_tva;
1376 $line->total_localtax1 = $objp->total_localtax1;
1377 $line->total_localtax2 = $objp->total_localtax2;
1378 $line->total_ttc = $objp->total_ttc;
1379 $line->fk_fournprice = $objp->fk_fournprice;
1380 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1381 $line->pa_ht = $marginInfos[0];
1382 $line->marge_tx = $marginInfos[1];
1383 $line->marque_tx = $marginInfos[2];
1384 $line->special_code = $objp->special_code;
1385 $line->rang = $objp->rang;
1386
1387 $line->fk_product = $objp->fk_product;
1388
1389 $line->ref = $objp->product_ref; // deprecated
1390 $line->product_ref = $objp->product_ref;
1391 $line->libelle = $objp->product_label; // deprecated
1392 $line->product_label = $objp->product_label;
1393 $line->product_desc = $objp->product_desc; // Description produit
1394 $line->fk_product_type = $objp->fk_product_type;
1395
1396 $line->ref_fourn = $objp->ref_produit_fourn;
1397
1398 // Multicurrency
1399 $line->fk_multicurrency = $objp->fk_multicurrency;
1400 $line->multicurrency_code = $objp->multicurrency_code;
1401 $line->multicurrency_subprice = $objp->multicurrency_subprice;
1402 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
1403 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
1404 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
1405 $line->fk_unit = $objp->fk_unit;
1406
1407 $this->lines[$i] = $line;
1408
1409 $i++;
1410 }
1411 $this->db->free($result);
1412 } else {
1413 $this->error = $this->db->error();
1414 return -1;
1415 }
1416
1417 // Retrieve all extrafield
1418 // fetch optionals attributes and labels
1419 $this->fetch_optionals();
1420
1421 return 1;
1422 }
1423
1424 $this->error = "Record Not Found";
1425 return 0;
1426 } else {
1427 $this->error = $this->db->error();
1428 return -1;
1429 }
1430 }
1431
1439 public function valid($user, $notrigger = 0)
1440 {
1441 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1442
1443 global $conf, $langs;
1444
1445 $error = 0;
1446 $now = dol_now();
1447
1448 if ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'creer'))
1449 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'validate_advance'))) {
1450 $this->db->begin();
1451
1452 // Numbering module definition
1453 $soc = new Societe($this->db);
1454 $result = $soc->fetch($this->socid);
1455
1456 if ($result < 0) {
1457 return -1;
1458 }
1459
1460 // Define new ref
1461 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1462 $num = $this->getNextNumRef($soc);
1463 } else {
1464 $num = $this->ref;
1465 }
1466 $this->newref = dol_sanitizeFileName($num);
1467
1468 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1469 $sql .= " SET ref = '".$this->db->escape($num)."',";
1470 $sql .= " fk_statut = 1, date_valid='".$this->db->idate($now)."', fk_user_valid=".((int) $user->id);
1471 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1472
1473 dol_syslog(get_class($this)."::valid", LOG_DEBUG);
1474 $resql = $this->db->query($sql);
1475 if (!$resql) {
1476 dol_print_error($this->db);
1477 $error++;
1478 }
1479
1480 // Trigger calls
1481 if (!$error && !$notrigger) {
1482 // Call trigger
1483 $result = $this->call_trigger('PROPOSAL_SUPPLIER_VALIDATE', $user);
1484 if ($result < 0) {
1485 $error++;
1486 }
1487 // End call triggers
1488 }
1489
1490 if (!$error) {
1491 $this->oldref = $this->ref;
1492
1493 // Rename directory if dir was a temporary ref
1494 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1495 // Now we rename also files into index
1496 $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)."'";
1497 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1498 $resql = $this->db->query($sql);
1499 if (!$resql) {
1500 $error++;
1501 $this->error = $this->db->lasterror();
1502 }
1503 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'supplier_proposal/".$this->db->escape($this->newref)."'";
1504 $sql .= " WHERE filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1505 $resql = $this->db->query($sql);
1506 if (!$resql) {
1507 $error++;
1508 $this->error = $this->db->lasterror();
1509 }
1510
1511 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1512 $oldref = dol_sanitizeFileName($this->ref);
1513 $newref = dol_sanitizeFileName($num);
1514 $dirsource = $conf->supplier_proposal->dir_output.'/'.$oldref;
1515 $dirdest = $conf->supplier_proposal->dir_output.'/'.$newref;
1516 if (!$error && file_exists($dirsource)) {
1517 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
1518 if (@rename($dirsource, $dirdest)) {
1519 dol_syslog("Rename ok");
1520 // Rename docs starting with $oldref with $newref
1521 $listoffiles = dol_dir_list($conf->supplier_proposal->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1522 foreach ($listoffiles as $fileentry) {
1523 $dirsource = $fileentry['name'];
1524 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1525 $dirsource = $fileentry['path'].'/'.$dirsource;
1526 $dirdest = $fileentry['path'].'/'.$dirdest;
1527 @rename($dirsource, $dirdest);
1528 }
1529 }
1530 }
1531 }
1532
1533 $this->ref = $num;
1534 $this->statut = self::STATUS_VALIDATED;
1536 $this->user_validation_id = $user->id;
1537 $this->datev = $now;
1538 $this->date_validation = $now;
1539
1540 $this->db->commit();
1541 return 1;
1542 } else {
1543 $this->db->rollback();
1544 return -1;
1545 }
1546 } else {
1547 dol_syslog("You don't have permission to validate supplier proposal", LOG_WARNING);
1548 return -2;
1549 }
1550 }
1551
1552 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1561 public function set_date_livraison($user, $delivery_date)
1562 {
1563 // phpcs:enable
1564 return $this->setDeliveryDate($user, $delivery_date);
1565 }
1566
1574 public function setDeliveryDate($user, $delivery_date)
1575 {
1576 if ($user->hasRight('supplier_proposal', 'creer')) {
1577 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1578 $sql .= " SET date_livraison = ".(isDolTms($delivery_date) ? "'".$this->db->idate($delivery_date)."'" : 'null');
1579 $sql .= " WHERE rowid = ".((int) $this->id);
1580
1581 if ($this->db->query($sql)) {
1582 $this->delivery_date = $delivery_date;
1583 return 1;
1584 } else {
1585 $this->error = $this->db->error();
1586 dol_syslog(get_class($this)."::setDeliveryDate Erreur SQL");
1587 return -1;
1588 }
1589 }
1590 return 0;
1591 }
1592
1593 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1601 /*
1602 public function set_remise_percent($user, $remise)
1603 {
1604 // phpcs:enable
1605 $remise = trim($remise) ?trim($remise) : 0;
1606
1607 if ($user->hasRight('supplier_proposal', 'creer')) {
1608 $remise = price2num($remise, 2);
1609
1610 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal SET remise_percent = ".((float) $remise);
1611 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1612
1613 if ($this->db->query($sql)) {
1614 $this->remise_percent = ((float) $remise);
1615 $this->update_price(1);
1616 return 1;
1617 } else {
1618 $this->error = $this->db->error();
1619 return -1;
1620 }
1621 }
1622 return 0;
1623 }
1624 */
1625
1626 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1634 /*
1635 public function set_remise_absolue($user, $remise)
1636 {
1637 // phpcs:enable
1638 if (empty($remise)) {
1639 $remise = 0;
1640 }
1641
1642 $remise = price2num($remise);
1643
1644 if ($user->hasRight('supplier_proposal', 'creer')) {
1645 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1646 $sql .= " SET remise_absolue = ".((float) $remise);
1647 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1648
1649 if ($this->db->query($sql)) {
1650 $this->remise_absolue = $remise;
1651 $this->update_price(1);
1652 return 1;
1653 } else {
1654 $this->error = $this->db->error();
1655 return -1;
1656 }
1657 }
1658 return 0;
1659 }
1660 */
1661
1662
1672 public function reopen($user, $statut, $note = '', $notrigger = 0)
1673 {
1674 global $langs, $conf;
1675
1676 $this->statut = $statut;
1677 $error = 0;
1678
1679 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1680 $sql .= " SET fk_statut = ".((int) $this->statut).",";
1681 if (!empty($note)) {
1682 $sql .= " note_private = '".$this->db->escape($note)."',";
1683 }
1684 $sql .= " date_cloture = NULL, fk_user_cloture = NULL";
1685 $sql .= " WHERE rowid = ".((int) $this->id);
1686
1687 $this->db->begin();
1688
1689 dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
1690 $resql = $this->db->query($sql);
1691 if (!$resql) {
1692 $error++;
1693 $this->errors[] = "Error ".$this->db->lasterror();
1694 }
1695 if (!$error) {
1696 if (!$notrigger) {
1697 // Call trigger
1698 $result = $this->call_trigger('PROPOSAL_SUPPLIER_REOPEN', $user);
1699 if ($result < 0) {
1700 $error++;
1701 }
1702 // End call triggers
1703 }
1704 }
1705
1706 // Commit or rollback
1707 if ($error) {
1708 if (!empty($this->errors)) {
1709 foreach ($this->errors as $errmsg) {
1710 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1711 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1712 }
1713 }
1714 $this->db->rollback();
1715 return -1 * $error;
1716 } else {
1717 $this->db->commit();
1718 return 1;
1719 }
1720 }
1721
1722
1731 public function cloture($user, $status, $note)
1732 {
1733 global $langs, $conf;
1734 $hidedetails = 0;
1735 $hidedesc = 0;
1736 $hideref = 0;
1737 $this->statut = $status;
1738 $error = 0;
1739 $now = dol_now();
1740
1741 $this->db->begin();
1742
1743 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1744 $sql .= " SET fk_statut = ".((int) $status).", note_private = '".$this->db->escape($note)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".$user->id;
1745 $sql .= " WHERE rowid = ".((int) $this->id);
1746
1747 $resql = $this->db->query($sql);
1748 if ($resql) {
1749 $modelpdf = $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED ? $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED : (empty($this->model_pdf) ? '' : $this->model_pdf);
1750 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_REFUSED';
1751
1752 if ($status == 2) {
1753 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_SIGNED';
1754 $modelpdf = $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL ? $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL : (empty($this->model_pdf) ? '' : $this->model_pdf);
1755
1756 if (getDolGlobalString('SUPPLIER_PROPOSAL_UPDATE_PRICE_ON_SUPPlIER_PROPOSAL')) { // TODO This option was not tested correctly. Error if product ref does not exists
1757 $result = $this->updateOrCreatePriceFournisseur($user);
1758 }
1759 }
1760 if ($status == 4) {
1761 $triggerName = 'PROPOSAL_SUPPLIER_CLASSIFY_BILLED';
1762 }
1763
1764 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1765 // Define output language
1766 $outputlangs = $langs;
1767 if (getDolGlobalInt('MAIN_MULTILANGS')) {
1768 $outputlangs = new Translate("", $conf);
1769 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
1770 $outputlangs->setDefaultLang($newlang);
1771 }
1772 //$ret=$object->fetch($id); // Reload to get new records
1773 $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1774 }
1775
1776 // Call trigger
1777 $result = $this->call_trigger($triggerName, $user);
1778 if ($result < 0) {
1779 $error++;
1780 }
1781 // End call triggers
1782
1783 if (!$error) {
1784 $this->db->commit();
1785 return 1;
1786 } else {
1787 $this->db->rollback();
1788 return -1;
1789 }
1790 } else {
1791 $this->error = $this->db->lasterror();
1792 $this->errors[] = $this->db->lasterror();
1793 $this->db->rollback();
1794 return -1;
1795 }
1796 }
1797
1804 public function updateOrCreatePriceFournisseur($user)
1805 {
1806 global $conf;
1807
1808 dol_syslog(get_class($this)."::updateOrCreatePriceFournisseur", LOG_DEBUG);
1809 foreach ($this->lines as $product) {
1810 if ($product->subprice <= 0) {
1811 continue;
1812 }
1813 $productsupplier = new ProductFournisseur($this->db);
1814
1815 $multicurrency_tx = 1;
1816 $fk_multicurrency = 0;
1817
1818 if (empty($this->thirdparty)) {
1819 $this->fetch_thirdparty();
1820 }
1821
1822 $ref_fourn = $product->ref_fourn;
1823 if (empty($ref_fourn)) {
1824 $ref_fourn = $product->ref_supplier;
1825 }
1826 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1827 list($fk_multicurrency, $multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $product->multicurrency_code);
1828 }
1829 $productsupplier->id = $product->fk_product;
1830
1831 $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);
1832 }
1833
1834 return 1;
1835 }
1836
1845 public function updatePriceFournisseur($idProductFournPrice, $product, $user)
1846 {
1847 $price = price2num($product->subprice * $product->qty, 'MU');
1848 $unitPrice = price2num($product->subprice, 'MU');
1849
1850 $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);
1851
1852 $resql = $this->db->query($sql);
1853 if (!$resql) {
1854 $this->error = $this->db->error();
1855 $this->db->rollback();
1856 return -1;
1857 }
1858 return 1;
1859 }
1860
1868 public function createPriceFournisseur($product, $user)
1869 {
1870 global $conf;
1871
1872 $price = price2num($product->subprice * $product->qty, 'MU');
1873 $qty = price2num($product->qty);
1874 $unitPrice = price2num($product->subprice, 'MU');
1875
1876 $now = dol_now();
1877
1878 $values = array(
1879 "'".$this->db->idate($now)."'",
1880 $product->fk_product,
1881 $this->thirdparty->id,
1882 "'".$product->ref_fourn."'",
1883 $price,
1884 $qty,
1885 $unitPrice,
1886 $product->tva_tx,
1887 $user->id
1888 );
1889 if (isModEnabled("multicurrency")) {
1890 if (!empty($product->multicurrency_code)) {
1891 include_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
1892 $multicurrency = new MultiCurrency($this->db); //need to fetch because empty fk_multicurrency and rate
1893 $multicurrency->fetch(0, $product->multicurrency_code);
1894 if (!empty($multicurrency->id)) {
1895 $values[] = $multicurrency->id;
1896 $values[] = "'".$product->multicurrency_code."'";
1897 $values[] = $product->multicurrency_subprice;
1898 $values[] = $product->multicurrency_total_ht;
1899 $values[] = $multicurrency->rate->rate;
1900 } else {
1901 for ($i = 0; $i < 5; $i++) {
1902 $values[] = 'NULL';
1903 }
1904 }
1905 }
1906 }
1907
1908 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'product_fournisseur_price ';
1909 $sql .= '(datec, fk_product, fk_soc, ref_fourn, price, quantity, unitprice, tva_tx, fk_user';
1910 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1911 $sql .= ',fk_multicurrency, multicurrency_code, multicurrency_unitprice, multicurrency_price, multicurrency_tx';
1912 }
1913 $sql .= ') VALUES ('.implode(',', $values).')';
1914
1915 $resql = $this->db->query($sql);
1916 if (!$resql) {
1917 $this->error = $this->db->error();
1918 $this->db->rollback();
1919 return -1;
1920 }
1921 return 1;
1922 }
1923
1924 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1931 public function setDraft($user)
1932 {
1933 // phpcs:enable
1934 global $conf, $langs;
1935
1936 $error = 0;
1937
1938 if ($this->statut == self::STATUS_DRAFT) {
1939 dol_syslog(get_class($this)."::setDraft already draft status", LOG_WARNING);
1940 return 0;
1941 }
1942
1943 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1944 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1945 $sql .= " WHERE rowid = ".((int) $this->id);
1946
1947 if ($this->db->query($sql)) {
1948 if (!$error) {
1949 $this->oldcopy = clone $this;
1950 }
1951
1952 if (!$error) {
1953 // Call trigger
1954 $result = $this->call_trigger('PROPOSAL_SUPPLIER_UNVALIDATE', $user);
1955 if ($result < 0) {
1956 $error++;
1957 }
1958 }
1959
1960 if (!$error) {
1961 $this->status = self::STATUS_DRAFT;
1962 $this->statut = self::STATUS_DRAFT; // deprecated
1963 $this->db->commit();
1964 return 1;
1965 } else {
1966 $this->db->rollback();
1967 return -1;
1968 }
1969 } else {
1970 return -1;
1971 }
1972 }
1973
1974
1975 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1989 public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datec', $sortorder = 'DESC')
1990 {
1991 // phpcs:enable
1992 global $user;
1993
1994 $ga = array();
1995
1996 $search_sale = 0;
1997 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
1998 $search_sale = $user->id;
1999 }
2000
2001 $sql = "SELECT s.rowid, s.nom as name, s.client,";
2002 $sql .= " p.rowid as supplier_proposalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
2003 $sql .= " p.datep as dp, p.fin_validite as datelimite";
2004 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."supplier_proposal as p, ".MAIN_DB_PREFIX."c_propalst as c";
2005 $sql .= " WHERE p.entity IN (".getEntity('supplier_proposal').")";
2006 $sql .= " AND p.fk_soc = s.rowid";
2007 $sql .= " AND p.fk_statut = c.id";
2008 if ($socid) {
2009 $sql .= " AND s.rowid = ".((int) $socid);
2010 }
2011 if ($draft) {
2012 $sql .= " AND p.fk_statut = 0";
2013 }
2014 if ($notcurrentuser > 0) {
2015 $sql .= " AND p.fk_user_author <> ".((int) $user->id);
2016 }
2017 // Search on sale representative
2018 if ($search_sale && $search_sale != '-1') {
2019 if ($search_sale == -2) {
2020 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
2021 } elseif ($search_sale > 0) {
2022 $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).")";
2023 }
2024 }
2025 $sql .= $this->db->order($sortfield, $sortorder);
2026 $sql .= $this->db->plimit($limit, $offset);
2027
2028 $result = $this->db->query($sql);
2029 if ($result) {
2030 $num = $this->db->num_rows($result);
2031 if ($num) {
2032 $i = 0;
2033 while ($i < $num) {
2034 $obj = $this->db->fetch_object($result);
2035
2036 if ($shortlist == 1) {
2037 $ga[$obj->supplier_proposalid] = $obj->ref;
2038 } elseif ($shortlist == 2) {
2039 $ga[$obj->supplier_proposalid] = $obj->ref.' ('.$obj->name.')';
2040 } else {
2041 $ga[$i]['id'] = $obj->supplier_proposalid;
2042 $ga[$i]['ref'] = $obj->ref;
2043 $ga[$i]['name'] = $obj->name;
2044 }
2045
2046 $i++;
2047 }
2048 }
2049 return $ga;
2050 } else {
2051 dol_print_error($this->db);
2052 return -1;
2053 }
2054 }
2055
2063 public function delete($user, $notrigger = 0)
2064 {
2065 global $conf, $langs;
2066 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2067
2068 $error = 0;
2069
2070 $this->db->begin();
2071
2072 if (!$notrigger) {
2073 // Call trigger
2074 $result = $this->call_trigger('PROPOSAL_SUPPLIER_DELETE', $user);
2075 if ($result < 0) {
2076 $error++;
2077 }
2078 // End call triggers
2079 }
2080
2081 if (!$error) {
2082 $main = MAIN_DB_PREFIX.'supplier_proposaldet';
2083 $ef = $main."_extrafields";
2084 $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_supplier_proposal = ".((int) $this->id).")";
2085 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet WHERE fk_supplier_proposal = ".((int) $this->id);
2086 if ($this->db->query($sql)) {
2087 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposal WHERE rowid = ".((int) $this->id);
2088 if ($this->db->query($sqlef) && $this->db->query($sql)) {
2089 // Delete linked object
2090 $res = $this->deleteObjectLinked();
2091 if ($res < 0) {
2092 $error++;
2093 }
2094
2095 if (!$error) {
2096 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2097 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
2098 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
2099
2100 // We remove directory
2101 $ref = dol_sanitizeFileName($this->ref);
2102 if ($conf->supplier_proposal->dir_output && !empty($this->ref)) {
2103 $dir = $conf->supplier_proposal->dir_output."/".$ref;
2104 $file = $dir."/".$ref.".pdf";
2105 if (file_exists($file)) {
2106 dol_delete_preview($this);
2107
2108 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2109 $this->error = 'ErrorFailToDeleteFile';
2110 $this->errors = array('ErrorFailToDeleteFile');
2111 $this->db->rollback();
2112 return 0;
2113 }
2114 }
2115 if (file_exists($dir)) {
2116 $res = @dol_delete_dir_recursive($dir);
2117 if (!$res) {
2118 $this->error = 'ErrorFailToDeleteDir';
2119 $this->errors = array('ErrorFailToDeleteDir');
2120 $this->db->rollback();
2121 return 0;
2122 }
2123 }
2124 }
2125 }
2126
2127 // Removed extrafields
2128 if (!$error) {
2129 $result = $this->deleteExtraFields();
2130 if ($result < 0) {
2131 $error++;
2132 $errorflag = -4;
2133 dol_syslog(get_class($this)."::delete erreur ".$errorflag." ".$this->error, LOG_ERR);
2134 }
2135 }
2136
2137 if (!$error) {
2138 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
2139 $this->db->commit();
2140 return 1;
2141 } else {
2142 $this->error = $this->db->lasterror();
2143 $this->db->rollback();
2144 return 0;
2145 }
2146 } else {
2147 $this->error = $this->db->lasterror();
2148 $this->db->rollback();
2149 return -3;
2150 }
2151 } else {
2152 $this->error = $this->db->lasterror();
2153 $this->db->rollback();
2154 return -2;
2155 }
2156 } else {
2157 $this->db->rollback();
2158 return -1;
2159 }
2160 }
2161
2168 public function info($id)
2169 {
2170 $sql = "SELECT c.rowid, ";
2171 $sql .= " c.datec as date_creation, c.date_valid as date_validation, c.date_cloture as date_closure,";
2172 $sql .= " c.fk_user_author, c.fk_user_valid, c.fk_user_cloture";
2173 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as c";
2174 $sql .= " WHERE c.rowid = ".((int) $id);
2175
2176 $result = $this->db->query($sql);
2177
2178 if ($result) {
2179 if ($this->db->num_rows($result)) {
2180 $obj = $this->db->fetch_object($result);
2181
2182 $this->id = $obj->rowid;
2183
2184 $this->date_creation = $this->db->jdate($obj->date_creation);
2185 $this->date_validation = $this->db->jdate($obj->date_validation);
2186 $this->date_cloture = $this->db->jdate($obj->date_closure);
2187
2188 $this->user_creation_id = $obj->fk_user_author;
2189 $this->user_validation_id = $obj->fk_user_valid;
2190 $this->user_closing_id = $obj->fk_user_cloture;
2191 }
2192 $this->db->free($result);
2193 } else {
2194 dol_print_error($this->db);
2195 }
2196 }
2197
2198
2205 public function getLibStatut($mode = 0)
2206 {
2207 return $this->LibStatut((isset($this->statut) ? $this->statut : $this->status), $mode);
2208 }
2209
2210 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2218 public function LibStatut($status, $mode = 1)
2219 {
2220 // phpcs:enable
2221
2222 // Init/load array of translation of status
2223 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
2224 global $langs;
2225 $langs->load("supplier_proposal");
2226 $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraft");
2227 $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidated");
2228 $this->labelStatus[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSigned");
2229 $this->labelStatus[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSigned");
2230 $this->labelStatus[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosed");
2231 $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraftShort");
2232 $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidatedShort");
2233 $this->labelStatusShort[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSignedShort");
2234 $this->labelStatusShort[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSignedShort");
2235 $this->labelStatusShort[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosedShort");
2236 }
2237
2238 $statusnew = '';
2239 if ($status == self::STATUS_DRAFT) {
2240 $statusnew = 'status0';
2241 } elseif ($status == self::STATUS_VALIDATED) {
2242 $statusnew = 'status1';
2243 } elseif ($status == self::STATUS_SIGNED) {
2244 $statusnew = 'status4';
2245 } elseif ($status == self::STATUS_NOTSIGNED) {
2246 $statusnew = 'status9';
2247 } elseif ($status == self::STATUS_CLOSE) {
2248 $statusnew = 'status6';
2249 }
2250
2251 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusnew, $mode);
2252 }
2253
2254
2255 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2263 public function load_board($user, $mode)
2264 {
2265 // phpcs:enable
2266 global $conf, $langs;
2267
2268 $now = dol_now();
2269
2270 $clause = " WHERE";
2271
2272 $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.date_cloture as datefin";
2273 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2274 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2275 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
2276 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2277 $clause = " AND";
2278 }
2279 $sql .= $clause." p.entity IN (".getEntity('supplier_proposal').")";
2280 if ($mode == 'opened') {
2281 $sql .= " AND p.fk_statut = 1";
2282 }
2283 if ($mode == 'signed') {
2284 $sql .= " AND p.fk_statut = 2";
2285 }
2286 if ($user->socid) {
2287 $sql .= " AND p.fk_soc = ".((int) $user->socid);
2288 }
2289
2290 $resql = $this->db->query($sql);
2291 if ($resql) {
2292 $label = $labelShort = '';
2293 $status = '';
2294 $delay_warning = 0;
2295 if ($mode == 'opened') {
2296 $delay_warning = !empty($conf->supplier_proposal->cloture->warning_delay) ? $conf->supplier_proposal->cloture->warning_delay : 0;
2297 $status = self::STATUS_VALIDATED;
2298 $label = $langs->trans("SupplierProposalsToClose");
2299 $labelShort = $langs->trans("ToAcceptRefuse");
2300 }
2301 if ($mode == 'signed') {
2302 $delay_warning = !empty($conf->supplier_proposal->facturation->warning_delay) ? $conf->supplier_proposal->facturation->warning_delay : 0;
2303 $status = self::STATUS_SIGNED;
2304 $label = $langs->trans("SupplierProposalsToProcess"); // May be billed or ordered
2305 $labelShort = $langs->trans("ToClose");
2306 }
2307
2308 $response = new WorkboardResponse();
2309 $response->warning_delay = $delay_warning / 60 / 60 / 24;
2310 $response->label = $label;
2311 $response->labelShort = $labelShort;
2312 $response->url = DOL_URL_ROOT.'/supplier_proposal/list.php?search_status='.$status;
2313 $response->img = img_object('', "propal");
2314
2315 // This assignment in condition is not a bug. It allows walking the results.
2316 while ($obj = $this->db->fetch_object($resql)) {
2317 $response->nbtodo++;
2318 if ($mode == 'opened') {
2319 $datelimit = $this->db->jdate($obj->datefin);
2320 if ($datelimit < ($now - $delay_warning)) {
2321 $response->nbtodolate++;
2322 }
2323 }
2324 // TODO Definir regle des propales a facturer en retard
2325 // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
2326 }
2327 return $response;
2328 } else {
2329 $this->error = $this->db->lasterror();
2330 return -1;
2331 }
2332 }
2333
2334
2342 public function initAsSpecimen()
2343 {
2344 global $user, $langs, $conf;
2345
2346 // Load array of products prodids
2347 $num_prods = 0;
2348 $prodids = array();
2349 $sql = "SELECT rowid";
2350 $sql .= " FROM ".MAIN_DB_PREFIX."product";
2351 $sql .= " WHERE entity IN (".getEntity('product').")";
2352 $sql .= $this->db->plimit(100);
2353
2354 $resql = $this->db->query($sql);
2355 if ($resql) {
2356 $num_prods = $this->db->num_rows($resql);
2357 $i = 0;
2358 while ($i < $num_prods) {
2359 $i++;
2360 $row = $this->db->fetch_row($resql);
2361 $prodids[$i] = $row[0];
2362 }
2363 }
2364
2365 // Initialise parameters
2366 $this->id = 0;
2367 $this->ref = 'SPECIMEN';
2368 $this->specimen = 1;
2369 $this->socid = 1;
2370 $this->date = time();
2371 $this->cond_reglement_id = 1;
2372 $this->cond_reglement_code = 'RECEP';
2373 $this->mode_reglement_id = 7;
2374 $this->mode_reglement_code = 'CHQ';
2375 $this->note_public = 'This is a comment (public)';
2376 $this->note_private = 'This is a comment (private)';
2377 // Lines
2378 $nbp = min(1000, GETPOSTINT('nblines') ? GETPOSTINT('nblines') : 5); // We can force the nb of lines to test from command line (but not more than 1000)
2379 $xnbp = 0;
2380 while ($xnbp < $nbp) {
2381 $line = new SupplierProposalLine($this->db);
2382 $line->desc = $langs->trans("Description")." ".$xnbp;
2383 $line->qty = 1;
2384 $line->subprice = 100;
2385 $line->tva_tx = 19.6;
2386 $line->localtax1_tx = 0;
2387 $line->localtax2_tx = 0;
2388 if ($xnbp == 2) {
2389 $line->total_ht = 50;
2390 $line->total_ttc = 59.8;
2391 $line->total_tva = 9.8;
2392 $line->remise_percent = 50;
2393 } else {
2394 $line->total_ht = 100;
2395 $line->total_ttc = 119.6;
2396 $line->total_tva = 19.6;
2397 $line->remise_percent = 00;
2398 }
2399
2400 if ($num_prods > 0) {
2401 $prodid = mt_rand(1, $num_prods);
2402 $line->fk_product = $prodids[$prodid];
2403 }
2404
2405 $this->lines[$xnbp] = $line;
2406
2407 $this->total_ht += $line->total_ht;
2408 $this->total_tva += $line->total_tva;
2409 $this->total_ttc += $line->total_ttc;
2410
2411 $xnbp++;
2412 }
2413
2414 return 1;
2415 }
2416
2422 public function loadStateBoard()
2423 {
2424 global $conf, $user;
2425
2426 $this->nb = array();
2427 $clause = "WHERE";
2428
2429 $sql = "SELECT count(p.rowid) as nb";
2430 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2431 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
2432 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2433 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2434 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2435 $clause = "AND";
2436 }
2437 $sql .= " ".$clause." p.entity IN (".getEntity('supplier_proposal').")";
2438
2439 $resql = $this->db->query($sql);
2440 if ($resql) {
2441 // This assignment in condition is not a bug. It allows walking the results.
2442 while ($obj = $this->db->fetch_object($resql)) {
2443 $this->nb["supplier_proposals"] = $obj->nb;
2444 }
2445 $this->db->free($resql);
2446 return 1;
2447 } else {
2448 dol_print_error($this->db);
2449 $this->error = $this->db->lasterror();
2450 return -1;
2451 }
2452 }
2453
2454
2462 public function getNextNumRef($soc)
2463 {
2464 global $conf, $db, $langs;
2465 $langs->load("supplier_proposal");
2466
2467 if (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON')) {
2468 $mybool = false;
2469
2470 $file = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') . ".php";
2471 $classname = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
2472
2473 // Include file with class
2474 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2475 foreach ($dirmodels as $reldir) {
2476 $dir = dol_buildpath($reldir."core/modules/supplier_proposal/");
2477
2478 // Load file with numbering class (if found)
2479 $mybool = ((bool) @include_once $dir.$file) || $mybool;
2480 }
2481
2482 if (!$mybool) {
2483 dol_print_error(null, "Failed to include file ".$file);
2484 return '';
2485 }
2486
2487 $obj = new $classname();
2488 '@phan-var-force ModeleNumRefSupplierProposal $obj';
2489 $numref = "";
2490 $numref = $obj->getNextValue($soc, $this);
2491
2492 if ($numref != "") {
2493 return $numref;
2494 } else {
2495 $this->error = $obj->error;
2496 return "";
2497 }
2498 } else {
2499 $langs->load("errors");
2500 print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("SupplierProposal"));
2501 return "";
2502 }
2503 }
2504
2511 public function getTooltipContentArray($params)
2512 {
2513 global $conf, $langs, $menumanager;
2514
2515 $langs->load('supplier_proposal');
2516
2517 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2518 return ['optimize' => $langs->trans("ShowSupplierProposal")];
2519 }
2520
2521 $option = $params['option'] ?? '';
2522 $datas = [];
2523
2524 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("SupplierProposal").'</u>';
2525 if (isset($this->status)) {
2526 $datas['picto'] .= ' '.$this->getLibStatut(5);
2527 }
2528 if (!empty($this->ref)) {
2529 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2530 }
2531 if (!empty($this->ref_fourn)) {
2532 $datas['ref_supplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_fourn;
2533 }
2534 if (!empty($this->total_ht)) {
2535 $datas['amount_ht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2536 }
2537 if (!empty($this->total_tva)) {
2538 $datas['amount_vat'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2539 }
2540 if (!empty($this->total_ttc)) {
2541 $datas['amount_ttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2542 }
2543
2544 return $datas;
2545 }
2546
2558 public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2559 {
2560 global $langs, $conf, $user, $hookmanager;
2561
2562 if (!empty($conf->dol_no_mouse_hover)) {
2563 $notooltip = 1; // Force disable tooltips
2564 }
2565
2566 $url = '';
2567 $result = '';
2568 $params = [
2569 'id' => $this->id,
2570 'objecttype' => $this->element,
2571 'option' => $option,
2572 ];
2573 $classfortooltip = 'classfortooltip';
2574 $dataparams = '';
2575 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2576 $classfortooltip = 'classforajaxtooltip';
2577 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
2578 $label = '';
2579 } else {
2580 $label = implode($this->getTooltipContentArray($params));
2581 }
2582
2583 if ($option == '') {
2584 $url = DOL_URL_ROOT.'/supplier_proposal/card.php?id='.$this->id.$get_params;
2585 }
2586 if ($option == 'document') {
2587 $url = DOL_URL_ROOT.'/supplier_proposal/document.php?id='.$this->id.$get_params;
2588 }
2589
2590 if ($option !== 'nolink') {
2591 // Add param to save lastsearch_values or not
2592 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2593 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2594 $add_save_lastsearch_values = 1;
2595 }
2596 if ($add_save_lastsearch_values) {
2597 $url .= '&save_lastsearch_values=1';
2598 }
2599 }
2600
2601 $linkclose = '';
2602 if (empty($notooltip) && $user->hasRight('propal', 'lire')) {
2603 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2604 $label = $langs->trans("ShowSupplierProposal");
2605 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
2606 }
2607 $linkclose .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
2608 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
2609 }
2610
2611 $linkstart = '<a href="'.$url.'"';
2612 $linkstart .= $linkclose.'>';
2613 $linkend = '</a>';
2614
2615 $result .= $linkstart;
2616 if ($withpicto) {
2617 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
2618 }
2619 if ($withpicto != 2) {
2620 $result .= $this->ref;
2621 }
2622 $result .= $linkend;
2623
2624 if ($addlinktonotes) {
2625 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2626 if ($txttoshow) {
2627 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2628 $result .= ' <span class="note inline-block">';
2629 $result .= '<a href="'.DOL_URL_ROOT.'/supplier_proposal/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2630 $result .= img_picto('', 'note');
2631 $result .= '</a>';
2632 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
2633 //$result.='</a>';
2634 $result .= '</span>';
2635 }
2636 }
2637 global $action;
2638 $hookmanager->initHooks(array($this->element . 'dao'));
2639 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
2640 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2641 if ($reshook > 0) {
2642 $result = $hookmanager->resPrint;
2643 } else {
2644 $result .= $hookmanager->resPrint;
2645 }
2646 return $result;
2647 }
2648
2654 public function getLinesArray()
2655 {
2656 // For other object, here we call fetch_lines. But fetch_lines does not exists on supplier proposal
2657
2658 $sql = 'SELECT pt.rowid, pt.label as custom_label, pt.description, pt.fk_product, pt.fk_remise_except,';
2659 $sql .= ' pt.qty, pt.tva_tx, pt.vat_src_code, pt.remise_percent, pt.subprice, pt.info_bits,';
2660 $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,';
2661 $sql .= ' pt.product_type, pt.rang, pt.fk_parent_line,';
2662 $sql .= ' p.label as product_label, p.ref, p.fk_product_type, p.rowid as prodid,';
2663 $sql .= ' p.description as product_desc, pt.ref_fourn as ref_supplier,';
2664 $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';
2665 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pt';
2666 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pt.fk_product=p.rowid';
2667 $sql .= ' WHERE pt.fk_supplier_proposal = '.((int) $this->id);
2668 $sql .= ' ORDER BY pt.rang ASC, pt.rowid';
2669
2670 dol_syslog(get_class($this).'::getLinesArray', LOG_DEBUG);
2671 $resql = $this->db->query($sql);
2672 if ($resql) {
2673 $num = $this->db->num_rows($resql);
2674 $i = 0;
2675
2676 while ($i < $num) {
2677 $obj = $this->db->fetch_object($resql);
2678
2679 $this->lines[$i] = new SupplierProposalLine($this->db);
2680 $this->lines[$i]->id = $obj->rowid; // for backward compatibility
2681 $this->lines[$i]->rowid = $obj->rowid;
2682 $this->lines[$i]->label = $obj->custom_label;
2683 $this->lines[$i]->description = $obj->description;
2684 $this->lines[$i]->fk_product = $obj->fk_product;
2685 $this->lines[$i]->ref = $obj->ref;
2686 $this->lines[$i]->product_label = $obj->product_label;
2687 $this->lines[$i]->product_desc = $obj->product_desc;
2688 $this->lines[$i]->fk_product_type = $obj->fk_product_type; // deprecated
2689 $this->lines[$i]->product_type = $obj->product_type;
2690 $this->lines[$i]->qty = $obj->qty;
2691 $this->lines[$i]->subprice = $obj->subprice;
2692 $this->lines[$i]->fk_remise_except = $obj->fk_remise_except;
2693 $this->lines[$i]->remise_percent = $obj->remise_percent;
2694 $this->lines[$i]->tva_tx = $obj->tva_tx;
2695 $this->lines[$i]->vat_src_code = $obj->vat_src_code;
2696 $this->lines[$i]->info_bits = $obj->info_bits;
2697 $this->lines[$i]->total_ht = $obj->total_ht;
2698 $this->lines[$i]->total_tva = $obj->total_tva;
2699 $this->lines[$i]->total_ttc = $obj->total_ttc;
2700 $this->lines[$i]->fk_fournprice = $obj->fk_fournprice;
2701 $marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->lines[$i]->fk_fournprice, $obj->pa_ht);
2702 $this->lines[$i]->pa_ht = $marginInfos[0];
2703 $this->lines[$i]->marge_tx = $marginInfos[1];
2704 $this->lines[$i]->marque_tx = $marginInfos[2];
2705 $this->lines[$i]->fk_parent_line = $obj->fk_parent_line;
2706 $this->lines[$i]->special_code = $obj->special_code;
2707 $this->lines[$i]->rang = $obj->rang;
2708
2709 $this->lines[$i]->ref_fourn = $obj->ref_supplier; // deprecated
2710 $this->lines[$i]->ref_supplier = $obj->ref_supplier;
2711
2712 // Multicurrency
2713 $this->lines[$i]->fk_multicurrency = $obj->fk_multicurrency;
2714 $this->lines[$i]->multicurrency_code = $obj->multicurrency_code;
2715 $this->lines[$i]->multicurrency_subprice = $obj->multicurrency_subprice;
2716 $this->lines[$i]->multicurrency_total_ht = $obj->multicurrency_total_ht;
2717 $this->lines[$i]->multicurrency_total_tva = $obj->multicurrency_total_tva;
2718 $this->lines[$i]->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
2719 $this->lines[$i]->fk_unit = $obj->fk_unit;
2720
2721 $i++;
2722 }
2723 $this->db->free($resql);
2724
2725 return 1;
2726 } else {
2727 $this->error = $this->db->error();
2728 return -1;
2729 }
2730 }
2731
2743 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2744 {
2745 global $conf, $langs;
2746
2747 $langs->load("supplier_proposal");
2748 $outputlangs->load("products");
2749
2750 if (!dol_strlen($modele)) {
2751 $modele = 'aurore';
2752
2753 if ($this->model_pdf) {
2754 $modele = $this->model_pdf;
2755 } elseif (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF')) {
2756 $modele = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF');
2757 }
2758 }
2759
2760 $modelpath = "core/modules/supplier_proposal/doc/";
2761
2762 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2763 }
2764
2765
2774 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2775 {
2776 $tables = array(
2777 'supplier_proposal'
2778 );
2779
2780 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2781 }
2782
2791 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
2792 {
2793 $tables = array(
2794 'supplier_proposaldet'
2795 );
2796
2797 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
2798 }
2799
2800
2808 public function getKanbanView($option = '', $arraydata = null)
2809 {
2810 global $langs;
2811
2812 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2813
2814 $return = '<div class="box-flex-item box-flex-grow-zero">';
2815 $return .= '<div class="info-box info-box-sm">';
2816 $return .= '<span class="info-box-icon bg-infobox-action">';
2817 $return .= img_picto('', $this->picto);
2818 //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2819 $return .= '</span>';
2820 $return .= '<div class="info-box-content">';
2821 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
2822 if ($selected >= 0) {
2823 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2824 }
2825 if (property_exists($this, 'socid')) {
2826 $return .= '<span class="info-box-ref"> | '.$this->socid.'</span>';
2827 }
2828 if (property_exists($this, 'delivery_date')) {
2829 $return .= '<br><span class="opacitymedium">'.$langs->trans("DateEnd").'</span> : <span class="info-box-label">'.dol_print_date($this->delivery_date).'</span>';
2830 }
2831 if (property_exists($this, 'total_ttc')) {
2832 $return .= '<br><span class="opacitymedium" >'.$langs->trans("AmountHT").' : </span><span class="info-box-label amount">'.price($this->total_ttc).'</span>';
2833 }
2834 if (method_exists($this, 'getLibStatut')) {
2835 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
2836 }
2837 $return .= '</div>';
2838 $return .= '</div>';
2839 $return .= '</div>';
2840 return $return;
2841 }
2842}
2843
2844
2849{
2853 public $db;
2854
2858 public $error = '';
2859
2863 public $element = 'supplier_proposaldet';
2864
2868 public $table_element = 'supplier_proposaldet';
2869
2873 public $parent_element = 'supplier_proposal';
2874
2878 public $fk_parent_attribute = 'fk_supplier_proposal';
2879
2883 public $oldline;
2884
2888 public $id;
2889
2893 public $fk_supplier_proposal;
2894
2898 public $fk_parent_line;
2899
2903 public $desc; // Description ligne
2904
2908 public $fk_product; // Id produit predefini
2909
2915 public $fk_product_type;
2921 public $product_type = Product::TYPE_PRODUCT;
2922
2926 public $qty;
2930 public $tva_tx;
2934 public $vat_src_code;
2935
2940 public $subprice;
2944 public $remise_percent;
2945
2949 public $fk_remise_except;
2950
2954 public $rang = 0;
2955
2959 public $fk_fournprice;
2960
2964 public $pa_ht;
2968 public $marge_tx;
2972 public $marque_tx;
2973
2977 public $special_code; // Tag for special lines (exclusive tags)
2978 // 1: frais de port
2979 // 2: ecotaxe
2980 // 3: option line (when qty = 0)
2981
2985 public $info_bits = 0; // Liste d'options cumulables:
2986 // Bit 0: 0 si TVA normal - 1 if TVA NPR
2987 // Bit 1: 0 ligne normal - 1 if fixed reduction
2988
2992 public $total_ht; // Total HT de la ligne toute quantite et incluant la remise ligne
2996 public $total_tva; // Total TVA de la ligne toute quantite et incluant la remise ligne
3000 public $total_ttc; // Total TTC de la ligne toute quantite et incluant la remise ligne
3001
3005 public $date_start;
3009 public $date_end;
3010
3011 // From llx_product
3017 public $ref;
3018
3023 public $product_ref;
3024
3030 public $libelle;
3031
3036 public $product_label;
3037
3042 public $label;
3043
3048 public $product_desc;
3049
3053 public $localtax1_tx; // Local tax 1
3057 public $localtax2_tx; // Local tax 2
3061 public $localtax1_type; // Local tax 1 type
3065 public $localtax2_type; // Local tax 2 type
3069 public $total_localtax1; // Line total local tax 1
3073 public $total_localtax2; // Line total local tax 2
3074
3078 public $skip_update_total; // Skip update price total for special lines
3079
3083 public $ref_fourn;
3087 public $ref_supplier;
3088
3089 // Multicurrency
3093 public $fk_multicurrency;
3094
3098 public $multicurrency_code;
3102 public $multicurrency_subprice;
3106 public $multicurrency_total_ht;
3110 public $multicurrency_total_tva;
3114 public $multicurrency_total_ttc;
3115
3121 public function __construct($db)
3122 {
3123 $this->db = $db;
3124 }
3125
3132 public function fetch($rowid)
3133 {
3134 $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,';
3135 $sql .= ' pd.date_start, pd.date_end,';
3136 $sql .= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice,';
3137 $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,';
3138 $sql .= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
3139 $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
3140 $sql .= ' pd.product_type, pd.ref_fourn as ref_produit_fourn,';
3141 $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';
3142 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pd';
3143 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
3144 $sql .= ' WHERE pd.rowid = '.((int) $rowid);
3145
3146 $result = $this->db->query($sql);
3147 if ($result) {
3148 if ($objp = $this->db->fetch_object($result)) {
3149 $this->id = $objp->rowid;
3150 $this->fk_supplier_proposal = $objp->fk_supplier_proposal;
3151 $this->fk_parent_line = $objp->fk_parent_line;
3152 $this->label = $objp->custom_label;
3153 $this->desc = $objp->description;
3154 $this->qty = $objp->qty;
3155 $this->subprice = $objp->subprice;
3156 $this->tva_tx = $objp->tva_tx;
3157 $this->remise_percent = $objp->remise_percent;
3158 $this->fk_remise_except = $objp->fk_remise_except;
3159 $this->fk_product = $objp->fk_product;
3160 $this->info_bits = $objp->info_bits;
3161 $this->date_start = $this->db->jdate($objp->date_start);
3162 $this->date_end = $this->db->jdate($objp->date_end);
3163
3164 $this->total_ht = $objp->total_ht;
3165 $this->total_tva = $objp->total_tva;
3166 $this->total_ttc = $objp->total_ttc;
3167
3168 $this->fk_fournprice = $objp->fk_fournprice;
3169
3170 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
3171 $this->pa_ht = $marginInfos[0];
3172 $this->marge_tx = $marginInfos[1];
3173 $this->marque_tx = $marginInfos[2];
3174
3175 $this->special_code = $objp->special_code;
3176 $this->product_type = $objp->product_type;
3177 $this->rang = $objp->rang;
3178
3179 $this->ref = $objp->product_ref; // deprecated
3180 $this->product_ref = $objp->product_ref;
3181 $this->libelle = $objp->product_label; // deprecated
3182 $this->product_label = $objp->product_label;
3183 $this->product_desc = $objp->product_desc;
3184
3185 $this->ref_fourn = $objp->ref_produit_fourn;
3186
3187 // Multicurrency
3188 $this->fk_multicurrency = $objp->fk_multicurrency;
3189 $this->multicurrency_code = $objp->multicurrency_code;
3190 $this->multicurrency_subprice = $objp->multicurrency_subprice;
3191 $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3192 $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3193 $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3194 $this->fk_unit = $objp->fk_unit;
3195
3196 $this->db->free($result);
3197 return 1;
3198 }
3199 return 0;
3200 } else {
3201 dol_print_error($this->db);
3202 return -1;
3203 }
3204 }
3205
3212 public function insert($notrigger = 0)
3213 {
3214 global $conf, $langs, $user;
3215
3216 $error = 0;
3217
3218 dol_syslog(get_class($this)."::insert rang=".$this->rang);
3219
3220 // Clean parameters
3221 if (empty($this->tva_tx)) {
3222 $this->tva_tx = 0;
3223 }
3224 if (empty($this->vat_src_code)) {
3225 $this->vat_src_code = '';
3226 }
3227 if (empty($this->localtax1_tx)) {
3228 $this->localtax1_tx = 0;
3229 }
3230 if (empty($this->localtax2_tx)) {
3231 $this->localtax2_tx = 0;
3232 }
3233 if (empty($this->localtax1_type)) {
3234 $this->localtax1_type = '';
3235 }
3236 if (empty($this->localtax2_type)) {
3237 $this->localtax2_type = '';
3238 }
3239 if (empty($this->total_localtax1)) {
3240 $this->total_localtax1 = 0;
3241 }
3242 if (empty($this->total_localtax2)) {
3243 $this->total_localtax2 = 0;
3244 }
3245 if (empty($this->rang)) {
3246 $this->rang = 0;
3247 }
3248 if (empty($this->remise_percent)) {
3249 $this->remise_percent = 0;
3250 }
3251 if (empty($this->info_bits)) {
3252 $this->info_bits = 0;
3253 }
3254 if (empty($this->special_code)) {
3255 $this->special_code = 0;
3256 }
3257 if (empty($this->fk_parent_line)) {
3258 $this->fk_parent_line = 0;
3259 }
3260 if (empty($this->fk_fournprice)) {
3261 $this->fk_fournprice = 0;
3262 }
3263 if (empty($this->fk_unit)) {
3264 $this->fk_unit = 0;
3265 }
3266 if (empty($this->subprice)) {
3267 $this->subprice = 0;
3268 }
3269
3270 if (empty($this->pa_ht)) {
3271 $this->pa_ht = 0;
3272 }
3273
3274 // if buy price not defined, define buyprice as configured in margin admin
3275 if ($this->pa_ht == 0) {
3276 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3277 if ($result < 0) {
3278 return $result;
3279 } else {
3280 $this->pa_ht = $result;
3281 }
3282 }
3283
3284 // Check parameters
3285 if ($this->product_type < 0) {
3286 return -1;
3287 }
3288
3289 $this->db->begin();
3290
3291 // Insert line into database
3292 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'supplier_proposaldet';
3293 $sql .= ' (fk_supplier_proposal, fk_parent_line, label, description, fk_product, product_type,';
3294 $sql .= ' date_start, date_end,';
3295 $sql .= ' fk_remise_except, qty, tva_tx, vat_src_code, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3296 $sql .= ' subprice, remise_percent, ';
3297 $sql .= ' info_bits, ';
3298 $sql .= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
3299 $sql .= ' ref_fourn,';
3300 $sql .= ' fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc, fk_unit)';
3301 $sql .= " VALUES (".$this->fk_supplier_proposal.",";
3302 $sql .= " ".($this->fk_parent_line > 0 ? ((int) $this->fk_parent_line) : "null").",";
3303 $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
3304 $sql .= " '".$this->db->escape($this->desc)."',";
3305 $sql .= " ".($this->fk_product ? ((int) $this->fk_product) : "null").",";
3306 $sql .= " '".$this->db->escape($this->product_type)."',";
3307 $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3308 $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3309 $sql .= " ".($this->fk_remise_except ? ((int) $this->fk_remise_except) : "null").",";
3310 $sql .= " ".price2num($this->qty, 'MS').",";
3311 $sql .= " ".price2num($this->tva_tx).",";
3312 $sql .= " '".$this->db->escape($this->vat_src_code)."',";
3313 $sql .= " ".price2num($this->localtax1_tx).",";
3314 $sql .= " ".price2num($this->localtax2_tx).",";
3315 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3316 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3317 $sql .= " ".price2num($this->subprice, 'MU') .",";
3318 $sql .= " ".((float) $this->remise_percent).",";
3319 $sql .= " ".(isset($this->info_bits) ? ((int) $this->info_bits) : "null").",";
3320 $sql .= " ".price2num($this->total_ht, 'MT').",";
3321 $sql .= " ".price2num($this->total_tva, 'MT').",";
3322 $sql .= " ".price2num($this->total_localtax1, 'MT').",";
3323 $sql .= " ".price2num($this->total_localtax2, 'MT').",";
3324 $sql .= " ".price2num($this->total_ttc, 'MT').",";
3325 $sql .= " ".(!empty($this->fk_fournprice) ? ((int) $this->fk_fournprice) : "null").",";
3326 $sql .= " ".(isset($this->pa_ht) ? price2num($this->pa_ht, 'MU') : "null").",";
3327 $sql .= ' '.((int) $this->special_code).',';
3328 $sql .= ' '.((int) $this->rang).',';
3329 $sql .= " '".$this->db->escape($this->ref_fourn)."'";
3330 $sql .= ", ".($this->fk_multicurrency > 0 ? ((int) $this->fk_multicurrency) : 'null');
3331 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3332 $sql .= ", ".price2num($this->multicurrency_subprice, 'CU');
3333 $sql .= ", ".price2num($this->multicurrency_total_ht, 'CT');
3334 $sql .= ", ".price2num($this->multicurrency_total_tva, 'CT');
3335 $sql .= ", ".price2num($this->multicurrency_total_ttc, 'CT');
3336 $sql .= ", ".($this->fk_unit ? ((int) $this->fk_unit) : 'null');
3337 $sql .= ')';
3338
3339 dol_syslog(get_class($this).'::insert', LOG_DEBUG);
3340 $resql = $this->db->query($sql);
3341 if ($resql) {
3342 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'supplier_proposaldet');
3343
3344 if (!$error) {
3345 $result = $this->insertExtraFields();
3346 if ($result < 0) {
3347 $error++;
3348 }
3349 }
3350
3351 if (!$error && !$notrigger) {
3352 // Call trigger
3353 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_INSERT', $user);
3354 if ($result < 0) {
3355 $this->db->rollback();
3356 return -1;
3357 }
3358 // End call triggers
3359 }
3360
3361 $this->db->commit();
3362 return 1;
3363 } else {
3364 $this->error = $this->db->error()." sql=".$sql;
3365 $this->db->rollback();
3366 return -1;
3367 }
3368 }
3369
3376 public function delete($user)
3377 {
3378 $error = 0;
3379
3380 $this->db->begin();
3381
3382 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet";
3383 $sql .= " WHERE rowid = ".((int) $this->id);
3384
3385 if ($this->db->query($sql)) {
3386 // Remove extrafields
3387 if (!$error) {
3388 $result = $this->deleteExtraFields();
3389 if ($result < 0) {
3390 $error++;
3391 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3392 }
3393 }
3394
3395 // Call trigger
3396 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_DELETE', $user);
3397 if ($result < 0) {
3398 $this->db->rollback();
3399 return -1;
3400 }
3401 // End call triggers
3402
3403 $this->db->commit();
3404
3405 return 1;
3406 } else {
3407 $this->error = $this->db->error()." sql=".$sql;
3408 $this->db->rollback();
3409 return -1;
3410 }
3411 }
3412
3419 public function update($notrigger = 0)
3420 {
3421 global $conf, $langs, $user;
3422
3423 $error = 0;
3424
3425 // Clean parameters
3426 if (empty($this->tva_tx)) {
3427 $this->tva_tx = 0;
3428 }
3429 if (empty($this->localtax1_tx)) {
3430 $this->localtax1_tx = 0;
3431 }
3432 if (empty($this->localtax2_tx)) {
3433 $this->localtax2_tx = 0;
3434 }
3435 if (empty($this->total_localtax1)) {
3436 $this->total_localtax1 = 0;
3437 }
3438 if (empty($this->total_localtax2)) {
3439 $this->total_localtax2 = 0;
3440 }
3441 if (empty($this->localtax1_type)) {
3442 $this->localtax1_type = '';
3443 }
3444 if (empty($this->localtax2_type)) {
3445 $this->localtax2_type = '';
3446 }
3447 if (empty($this->marque_tx)) {
3448 $this->marque_tx = 0;
3449 }
3450 if (empty($this->marge_tx)) {
3451 $this->marge_tx = 0;
3452 }
3453 if (empty($this->remise_percent)) {
3454 $this->remise_percent = 0;
3455 }
3456 if (empty($this->info_bits)) {
3457 $this->info_bits = 0;
3458 }
3459 if (empty($this->special_code)) {
3460 $this->special_code = 0;
3461 }
3462 if (empty($this->fk_parent_line)) {
3463 $this->fk_parent_line = 0;
3464 }
3465 if (empty($this->fk_fournprice)) {
3466 $this->fk_fournprice = 0;
3467 }
3468 if (empty($this->fk_unit)) {
3469 $this->fk_unit = 0;
3470 }
3471 if (empty($this->subprice)) {
3472 $this->subprice = 0;
3473 }
3474
3475 if (empty($this->pa_ht)) {
3476 $this->pa_ht = 0;
3477 }
3478
3479 // if buy price not defined, define buyprice as configured in margin admin
3480 if ($this->pa_ht == 0) {
3481 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3482 if ($result < 0) {
3483 return $result;
3484 } else {
3485 $this->pa_ht = $result;
3486 }
3487 }
3488
3489 $this->db->begin();
3490
3491 // Mise a jour ligne en base
3492 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3493 $sql .= " description='".$this->db->escape($this->desc)."'";
3494 $sql .= " , label=".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
3495 $sql .= " , product_type=".((int) $this->product_type);
3496 $sql .= " , date_start=".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null");
3497 $sql .= " , date_end=".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null");
3498 $sql .= " , tva_tx='".price2num($this->tva_tx)."'";
3499 $sql .= " , localtax1_tx=".price2num($this->localtax1_tx);
3500 $sql .= " , localtax2_tx=".price2num($this->localtax2_tx);
3501 $sql .= " , localtax1_type='".$this->db->escape($this->localtax1_type)."'";
3502 $sql .= " , localtax2_type='".$this->db->escape($this->localtax2_type)."'";
3503 $sql .= " , qty='".price2num($this->qty)."'";
3504 $sql .= " , subprice=".price2num($this->subprice);
3505 $sql .= " , remise_percent=".price2num($this->remise_percent);
3506 $sql .= " , info_bits='".$this->db->escape($this->info_bits)."'";
3507 if (empty($this->skip_update_total)) {
3508 $sql .= " , total_ht=".price2num($this->total_ht);
3509 $sql .= " , total_tva=".price2num($this->total_tva);
3510 $sql .= " , total_ttc=".price2num($this->total_ttc);
3511 $sql .= " , total_localtax1=".price2num($this->total_localtax1);
3512 $sql .= " , total_localtax2=".price2num($this->total_localtax2);
3513 }
3514 $sql .= " , fk_product_fournisseur_price=".(!empty($this->fk_fournprice) ? "'".$this->db->escape($this->fk_fournprice)."'" : "null");
3515 $sql .= " , buy_price_ht=".price2num($this->pa_ht);
3516 $sql .= " , special_code=".((int) $this->special_code);
3517 $sql .= " , fk_parent_line=".($this->fk_parent_line > 0 ? $this->fk_parent_line : "null");
3518 if (!empty($this->rang)) {
3519 $sql .= ", rang=".((int) $this->rang);
3520 }
3521 $sql .= " , ref_fourn=".(!empty($this->ref_fourn) ? "'".$this->db->escape($this->ref_fourn)."'" : "null");
3522 $sql .= " , fk_unit=".($this->fk_unit ? $this->fk_unit : 'null');
3523
3524 // Multicurrency
3525 $sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice);
3526 $sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
3527 $sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
3528 $sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
3529
3530 $sql .= " WHERE rowid = ".((int) $this->id);
3531
3532 dol_syslog(get_class($this)."::update", LOG_DEBUG);
3533 $resql = $this->db->query($sql);
3534 if ($resql) {
3535 if (!$error) {
3536 $result = $this->insertExtraFields();
3537 if ($result < 0) {
3538 $error++;
3539 }
3540 }
3541
3542 if (!$error && !$notrigger) {
3543 // Call trigger
3544 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_MODIFY', $user);
3545 if ($result < 0) {
3546 $this->db->rollback();
3547 return -1;
3548 }
3549 // End call triggers
3550 }
3551
3552 $this->db->commit();
3553 return 1;
3554 } else {
3555 $this->error = $this->db->error();
3556 $this->db->rollback();
3557 return -2;
3558 }
3559 }
3560
3561 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3568 public function update_total()
3569 {
3570 // phpcs:enable
3571 $this->db->begin();
3572
3573 // Mise a jour ligne en base
3574 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3575 $sql .= " total_ht=".price2num($this->total_ht, 'MT');
3576 $sql .= ",total_tva=".price2num($this->total_tva, 'MT');
3577 $sql .= ",total_ttc=".price2num($this->total_ttc, 'MT');
3578 $sql .= " WHERE rowid = ".((int) $this->id);
3579
3580 dol_syslog("SupplierProposalLine::update_total", LOG_DEBUG);
3581
3582 $resql = $this->db->query($sql);
3583 if ($resql) {
3584 $this->db->commit();
3585 return 1;
3586 } else {
3587 $this->error = $this->db->error();
3588 $this->db->rollback();
3589 return -2;
3590 }
3591 }
3592}
$object ref
Definition info.php:89
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:171
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1, $nolog=0)
Remove a file or several files with a mask.
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0, $level=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:63
dol_delete_preview($object)
Delete all preview files linked to object instance.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that returns whether VAT must be recoverable collected VAT (e.g.: VAT NPR in France)
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0)
Clean a string to use it as a file name.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that return vat rate of a product line (according to seller, buyer and product vat rate) VAT...
get_localtax($vatrate, $local, $thirdparty_buyer=null, $thirdparty_seller=null, $vatnpr=0)
Return localtax rate for a particular vat, when selling a product with vat $vatrate,...
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
getMarginInfos($pv_ht, $remise_percent, $tva_tx, $localtax1_tx, $localtax2_tx, $fk_pa, $pa_ht)
Return an array with margins information of a line.
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller='', $localtaxes_array=[], $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition price.lib.php:90