dolibarr 24.0.2
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-2025 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-2026 MDW <mdeweerd@users.noreply.github.com>
20 * Copyright (C) 2026 Vincent de Grandpré <vincent@de-grandpre.quebec>
21 *
22 * This program is free software; you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License as published by
24 * the Free Software Foundation; either version 3 of the License, or
25 * (at your option) any later version.
26 *
27 * This program is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU General Public License for more details.
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this program. If not, see <https://www.gnu.org/licenses/>.
34 */
35
41require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
42require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
43require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
44require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
45require_once DOL_DOCUMENT_ROOT.'/margin/lib/margins.lib.php';
46require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
47require_once DOL_DOCUMENT_ROOT.'/core/class/commonincoterm.class.php';
48require_once DOL_DOCUMENT_ROOT.'/subtotals/class/commonsubtotal.class.php';
53{
54 use CommonIncoterm;
55 use CommonSubtotal;
56
60 public $element = 'supplier_proposal';
61
65 public $table_element = 'supplier_proposal';
66
70 public $table_element_line = 'supplier_proposaldet';
71
75 public $class_element_line = 'SupplierProposalLine';
79 public $fk_element = 'fk_supplier_proposal';
80
84 public $picto = 'supplier_proposal';
85
90 public $restrictiononfksoc = 1;
91
95 protected $table_ref_field = 'ref';
96
100 public $socid; // Id client
101
107 public $author;
108
112 public $ref_fourn;
116 public $ref_supplier;
117
122 public $statut; // 0 (draft), 1 (validated), 2 (signed), 3 (not signed), 4 (processed/billed)
123
127 public $date;
128
132 public $delivery_date;
133
139 public $datec;
140
146 public $datev;
147
151 public $user_author_id;
152
158 public $price;
159
165 public $tva;
166
172 public $total;
173
177 public $cond_reglement_code;
181 public $cond_reglement_doc; // label doc
182
186 public $mode_reglement_code;
191 public $mode_reglement;
192
198 public $deposit_percent;
199
200
204 public $extraparams = array();
205
209 public $lines = array();
210
214 public $line;
215
216 public $labelStatus = array();
217 public $labelStatusShort = array();
218
222 public $nbtodo;
226 public $nbtodolate;
227
228 // Multicurrency
232 public $fk_multicurrency;
233
237 public $multicurrency_code;
241 public $multicurrency_tx;
245 public $multicurrency_total_ht;
249 public $multicurrency_total_tva;
253 public $multicurrency_total_ttc;
254
255 public $fields = array(
256 'ref' => array('type' => 'varchar(255)', 'label' => 'Ref', 'enabled' => 1, 'visible' => 1, 'showoncombobox' => 1, 'position' => 25, 'searchall' => 1),
257 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 750, 'searchall' => 1),
258 );
259
263 const STATUS_DRAFT = 0;
264
269
273 const STATUS_SIGNED = 2;
274
279
283 const STATUS_CLOSE = 4;
284
285
286
294 public function __construct($db, $socid = 0, $supplier_proposalid = 0)
295 {
296 $this->db = $db;
297
298 $this->ismultientitymanaged = 1;
299 $this->socid = $socid;
300 $this->id = $supplier_proposalid;
301 }
302
303
304 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
315 public function add_product($idproduct, $qty, $remise_percent = 0)
316 {
317 // phpcs:enable
318 global $mysoc;
319
320 if (!$qty) {
321 $qty = 1;
322 }
323
324 dol_syslog(get_class($this)."::add_product $idproduct, $qty, $remise_percent");
325 if ($idproduct > 0) {
326 $prod = new Product($this->db);
327 $prod->fetch($idproduct);
328
329 $productdesc = $prod->description;
330
331 $tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
332 $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
333 if (empty($tva_tx)) {
334 $tva_npr = 0;
335 }
336 $localtax1_tx = get_localtax($tva_tx, 1, $mysoc, $this->thirdparty, $tva_npr);
337 $localtax2_tx = get_localtax($tva_tx, 2, $mysoc, $this->thirdparty, $tva_npr);
338
339 // multiprix
340 if (getDolGlobalString('PRODUIT_MULTIPRICES') && $this->thirdparty->price_level) {
341 $price = $prod->multiprices[$this->thirdparty->price_level];
342 } else {
343 $price = $prod->price;
344 }
345
346 $line = new SupplierProposalLine($this->db);
347
348 $line->fk_product = $idproduct;
349 $line->desc = $productdesc;
350 $line->qty = $qty;
351 $line->subprice = $price;
352 $line->remise_percent = $remise_percent;
353 $line->tva_tx = $tva_tx;
354
355 $this->lines[] = $line;
356 return 1;
357 }
358 return -1;
359 }
360
361 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
368 public function insert_discount($idremise)
369 {
370 // phpcs:enable
371 global $langs;
372
373 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
374 include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
375
376 $this->db->begin();
377
378 $remise = new DiscountAbsolute($this->db);
379 $result = $remise->fetch($idremise);
380
381 if ($result > 0) {
382 if ($remise->fk_facture) { // Protection against multiple submission
383 $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
384 $this->db->rollback();
385 return -5;
386 }
387
388 $supplier_proposalligne = new SupplierProposalLine($this->db);
389 $supplier_proposalligne->fk_supplier_proposal = $this->id;
390 $supplier_proposalligne->fk_remise_except = $remise->id;
391 $supplier_proposalligne->desc = $remise->description; // Description of the proposal line
392 $supplier_proposalligne->tva_tx = $remise->tva_tx;
393 $supplier_proposalligne->localtax1_tx = $remise->localtax1_tx;
394 $supplier_proposalligne->localtax1_type = $remise->localtax1_type;
395 $supplier_proposalligne->localtax2_tx = $remise->localtax1_tx;
396 $supplier_proposalligne->localtax2_type = $remise->localtax1_type;
397 $supplier_proposalligne->subprice = -(float) $remise->amount_ht;
398 $supplier_proposalligne->fk_product = 0; // Predefined Product ID
399 $supplier_proposalligne->qty = 1;
400 $supplier_proposalligne->remise_percent = 0;
401 $supplier_proposalligne->rang = -1;
402 $supplier_proposalligne->info_bits = 2;
403
404 $supplier_proposalligne->total_ht = -(float) $remise->amount_ht;
405 $supplier_proposalligne->total_tva = -(float) $remise->amount_tva;
406 $supplier_proposalligne->total_ttc = -(float) $remise->amount_ttc;
407 $supplier_proposalligne->total_localtax1 = -(float) $remise->total_localtax1;
408 $supplier_proposalligne->total_localtax2 = -(float) $remise->total_localtax2;
409
410 $result = $supplier_proposalligne->insert();
411 if ($result > 0) {
412 $result = $this->update_price(1);
413 if ($result > 0) {
414 $this->db->commit();
415 return 1;
416 } else {
417 $this->db->rollback();
418 return -1;
419 }
420 } else {
421 $this->setErrorsFromObject($supplier_proposalligne);
422 $this->db->rollback();
423 return -2;
424 }
425 } else {
426 $this->db->rollback();
427 return -2;
428 }
429 }
430
467 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)
468 {
469 global $mysoc, $langs;
470
471 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");
472 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
473
474 // Clean parameters
475 if (empty($remise_percent)) {
476 $remise_percent = 0;
477 }
478 if (empty($qty)) {
479 $qty = 0;
480 }
481 if (empty($info_bits)) {
482 $info_bits = 0;
483 }
484 if (empty($rang)) {
485 $rang = 0;
486 }
487 if (empty($fk_parent_line) || $fk_parent_line < 0) {
488 $fk_parent_line = 0;
489 }
490 if (empty($pu_ht)) {
491 $pu_ht = 0;
492 }
493
494 $remise_percent = price2num($remise_percent);
495 $qty = (float) price2num($qty);
496 $pu_ht = price2num($pu_ht);
497 $pu_ttc = price2num($pu_ttc);
498 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
499 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
500 }
501 $txlocaltax1 = price2num($txlocaltax1);
502 $txlocaltax2 = price2num($txlocaltax2);
503 $pa_ht = price2num($pa_ht);
504 if ($price_base_type == 'HT') {
505 $pu = $pu_ht;
506 } else {
507 $pu = $pu_ttc;
508 }
509
510 // Check parameters
511 if ($type < 0) {
512 return -1;
513 }
514
515 if ($this->status == self::STATUS_DRAFT) {
516 $this->db->begin();
517
518 if ($fk_product > 0) {
519 if (getDolGlobalInt('SUPPLIER_PROPOSAL_WITH_PREDEFINED_PRICES_ONLY') == 1) { // Not the common case
520 // Check quantity is enough
521 dol_syslog(get_class($this)."::addline we check supplier prices fk_product=".$fk_product." fk_fournprice=".$fk_fournprice." qty=".$qty." ref_supplier=".$ref_supplier);
522 $productsupplier = new ProductFournisseur($this->db);
523 if ($productsupplier->fetch($fk_product) > 0) {
524 $product_type = $productsupplier->type;
525 $label = $productsupplier->label;
526 $fk_prod_fourn_price = $fk_fournprice;
527
528 // We use 'none' instead of $ref_supplier, because fourn_ref may not exists anymore. So we will take the first supplier price ok.
529 // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
530 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
531 $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
532 if ($result > 0) {
533 $pu = $productsupplier->fourn_pu; // Unit price supplier price set by get_buyprice
534 $ref_supplier = $productsupplier->ref_supplier; // Ref supplier price set by get_buyprice
535 // is remise percent not keyed but present for the product we add it
536 if ($remise_percent == 0 && $productsupplier->remise_percent != 0) {
537 $remise_percent = $productsupplier->remise_percent;
538 }
539 }
540 if ($result == 0) { // If result == 0, we failed to found the supplier reference price
541 $langs->load("errors");
542 $this->error = "Ref ".$productsupplier->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
543 $this->db->rollback();
544 dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
545 //$pu = $productsupplier->fourn_pu; // We do not overwrite unit price
546 //$ref = $productsupplier_fourn; // We do not overwrite ref supplier price
547 return -1;
548 }
549 if ($result == -1) {
550 $langs->load("errors");
551 $this->error = "Ref ".$productsupplier->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
552 $this->db->rollback();
553 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
554 return -1;
555 }
556 if ($result < -1) {
557 $this->setErrorsFromObject($productsupplier);
558 $this->db->rollback();
559 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
560 return -1;
561 }
562 } else {
563 $this->setErrorsFromObject($productsupplier);
564 $this->db->rollback();
565 return -1;
566 }
567 }
568 } else {
569 $product_type = $type;
570 }
571
572 // Calculation of the gross total (TTC) and VAT for the line from qty, pu, remise_percent and txtva
573 // VERY IMPORTANT: It's at the time of line insertion that we must store the net, VAT, and gross amounts,
574 // and this is done at the line level, which has its own VAT rate
575
576 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
577
578 // Clean vat code
579 $reg = array();
580 $vat_src_code = '';
581 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
582 $vat_src_code = $reg[1];
583 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
584 }
585
586 if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
587 $pu = 0;
588 }
589
590 $tabprice = calcul_price_total(
591 $qty,
592 $pu,
593 (float) $remise_percent,
594 $txtva,
595 (float) $txlocaltax1,
596 (float) $txlocaltax2,
597 0,
598 $price_base_type,
599 $info_bits,
600 $type,
601 $this->thirdparty,
602 $localtaxes_type,
603 100,
604 (float) $this->multicurrency_tx,
605 $pu_ht_devise
606 );
607 $total_ht = $tabprice[0];
608 $total_tva = $tabprice[1];
609 $total_ttc = $tabprice[2];
610 $total_localtax1 = $tabprice[9];
611 $total_localtax2 = $tabprice[10];
612 $pu = $pu_ht = $tabprice[3];
613
614 // MultiCurrency
615 $multicurrency_total_ht = $tabprice[16];
616 $multicurrency_total_tva = $tabprice[17];
617 $multicurrency_total_ttc = $tabprice[18];
618 $pu_ht_devise = $tabprice[19];
619
620 // Rang to use
621 $ranktouse = $rang;
622 if ($ranktouse == -1) {
623 $rangmax = $this->line_max($fk_parent_line);
624 $ranktouse = $rangmax + 1;
625 }
626
627 // Insert line
628 $this->line = new SupplierProposalLine($this->db);
629
630 $this->line->fk_supplier_proposal = $this->id;
631 $this->line->label = $label;
632 $this->line->desc = $desc;
633 $this->line->qty = $qty;
634
635 $this->line->vat_src_code = $vat_src_code;
636 $this->line->tva_tx = $txtva;
637 $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
638 $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
639 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
640 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
641 $this->line->fk_product = $fk_product;
642 $this->line->remise_percent = $remise_percent;
643 $this->line->subprice = (float) $pu_ht;
644 $this->line->rang = $ranktouse;
645 $this->line->info_bits = $info_bits;
646 $this->line->total_ht = (float) $total_ht;
647 $this->line->total_tva = (float) $total_tva;
648 $this->line->total_localtax1 = (float) $total_localtax1;
649 $this->line->total_localtax2 = (float) $total_localtax2;
650 $this->line->total_ttc = (float) $total_ttc;
651 $this->line->product_type = $type;
652 $this->line->special_code = $special_code;
653 $this->line->fk_parent_line = $fk_parent_line;
654 $this->line->fk_unit = $fk_unit;
655 $this->line->origin = $origin;
656 $this->line->origin_type = $origin;
657 $this->line->origin_id = $origin_id;
658 $this->line->ref_fourn = $this->db->escape($ref_supplier);
659 $this->line->date_start = $date_start;
660 $this->line->date_end = $date_end;
661
662 // infos merge
663 if (!empty($fk_product) && $fk_product > 0 && empty($fk_fournprice) && empty($pa_ht)) {
664 // When fk_fournprice is 0, we take the lowest buying price
665 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
666 $productFournisseur = new ProductFournisseur($this->db);
667 $productFournisseur->find_min_price_product_fournisseur($fk_product);
668 $this->line->fk_fournprice = $productFournisseur->product_fourn_price_id;
669 } else {
670 $this->line->fk_fournprice = ($fk_fournprice > 0 ? $fk_fournprice : 0); // If fk_fournprice is -1, we will not use fk_fournprice
671 }
672 $this->line->pa_ht = $pa_ht;
673 //var_dump($this->line->fk_fournprice);exit;
674
675 // Multicurrency
676 $this->line->fk_multicurrency = $this->fk_multicurrency;
677 $this->line->multicurrency_code = $this->multicurrency_code;
678 $this->line->multicurrency_subprice = (float) $pu_ht_devise;
679 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
680 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
681 $this->line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
682
683 // Set the proposal line as optional
684 if (empty($qty) && empty($special_code)) {
685 $this->line->special_code = 3;
686 }
687
688 if (is_array($array_options) && count($array_options) > 0) {
689 $this->line->array_options = $array_options;
690 }
691
692 $result = $this->line->insert();
693 if ($result > 0) {
694 // Reorder if child line
695 if (!empty($fk_parent_line)) {
696 $this->line_order(true, 'DESC');
697 } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) { // Update all rank of all other lines
698 $linecount = count($this->lines);
699 for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
700 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
701 }
702 }
703
704 // Update denormalized information at the present proposal level
705 $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.
706 if ($result > 0) {
707 $this->db->commit();
708 return $this->line->id;
709 } else {
710 $this->db->rollback();
711 return -1;
712 }
713 } else {
714 $this->setErrorsFromObject($this->line);
715 $this->db->rollback();
716 return -2;
717 }
718 } else {
719 $this->error = 'BadStatusOfObjectToAddLine';
720 return -5;
721 }
722 }
723
724
751 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)
752 {
753 global $mysoc;
754
755 if (!$this->isLineOfObject($rowid)) {
756 $this->error = 'ErrorLineIDDoesNotMatchWithObjectID';
757 return -1;
758 }
759
760 dol_syslog(get_class($this)."::updateLine $rowid, $pu, $qty, $remise_percent, $txtva, $desc, $price_base_type, $info_bits");
761 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
762
763 // Clean parameters
764 $remise_percent = price2num($remise_percent);
765 $qty = (float) price2num($qty);
766 $pu = price2num($pu);
767 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
768 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
769 }
770 $txlocaltax1 = price2num($txlocaltax1);
771 $txlocaltax2 = price2num($txlocaltax2);
772 $pa_ht = price2num($pa_ht);
773 if (empty($qty) && empty($special_code)) {
774 $special_code = 3; // Set option tag
775 }
776 if (!empty($qty) && $special_code == 3) {
777 $special_code = 0; // Remove option tag
778 }
779
780 if ($this->status == 0) {
781 $this->db->begin();
782
783 // Calculation of the gross total (TTC) and VAT for the line from qty, pu, remise_percent and txtva
784 // VERY IMPORTANT: It's at the time of line insertion that we must store the net, VAT, and gross amounts,
785 // and this is done at the line level, which has its own VAT rate
786
787 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
788
789 // Clean vat code
790 $reg = array();
791 $vat_src_code = '';
792 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
793 $vat_src_code = $reg[1];
794 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
795 }
796
797 if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
798 $pu = 0;
799 }
800
801 $tabprice = calcul_price_total(
802 $qty,
803 $pu,
804 (float) $remise_percent,
805 $txtva,
806 (float) $txlocaltax1,
807 (float) $txlocaltax2,
808 0,
809 $price_base_type,
810 $info_bits,
811 $type,
812 $this->thirdparty,
813 $localtaxes_type,
814 100,
815 (float) $this->multicurrency_tx,
816 $pu_ht_devise
817 );
818 $total_ht = $tabprice[0];
819 $total_tva = $tabprice[1];
820 $total_ttc = $tabprice[2];
821 $total_localtax1 = $tabprice[9];
822 $total_localtax2 = $tabprice[10];
823 $pu_ht = $tabprice[3];
824 $pu_tva = $tabprice[4];
825 $pu_ttc = $tabprice[5];
826
827 // MultiCurrency
828 $multicurrency_total_ht = $tabprice[16];
829 $multicurrency_total_tva = $tabprice[17];
830 $multicurrency_total_ttc = $tabprice[18];
831 $pu_ht_devise = $tabprice[19];
832
833 $pu = $pu_ht;
834 if ($price_base_type == 'TTC') {
835 $pu = $pu_ttc;
836 }
837
838 // Fetch current line from the database and then clone the object and set it in $oldline property
839 $line = new SupplierProposalLine($this->db);
840 $line->fetch($rowid);
841 $line->fetch_optionals();
842
843 $fk_product = $line->fk_product;
844
845 // Stock previous line records
846 $staticline = clone $line;
847
848 $line->oldline = $staticline;
849 $this->line = $line;
850 $this->line->context = $this->context;
851
852 // Reorder if fk_parent_line change
853 if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
854 $rangmax = $this->line_max($fk_parent_line);
855 $this->line->rang = $rangmax + 1;
856 }
857
858 $this->line->id = $rowid;
859 $this->line->label = $label;
860 $this->line->desc = $desc;
861 $this->line->qty = $qty;
862 $this->line->product_type = $type;
863
864 $this->line->vat_src_code = $vat_src_code;
865 $this->line->tva_tx = $txtva;
866 $this->line->localtax1_tx = $txlocaltax1;
867 $this->line->localtax2_tx = $txlocaltax2;
868 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
869 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
870 $this->line->remise_percent = $remise_percent;
871 $this->line->subprice = (float) $pu;
872 $this->line->info_bits = $info_bits;
873 $this->line->total_ht = (float) $total_ht;
874 $this->line->total_tva = (float) $total_tva;
875 $this->line->total_localtax1 = (float) $total_localtax1;
876 $this->line->total_localtax2 = (float) $total_localtax2;
877 $this->line->total_ttc = (float) $total_ttc;
878 $this->line->special_code = $special_code;
879 $this->line->fk_parent_line = $fk_parent_line;
880 $this->line->skip_update_total = $skip_update_total;
881 $this->line->ref_fourn = $ref_supplier;
882 $this->line->fk_unit = $fk_unit;
883
884 // infos merge
885 if (!empty($fk_product) && $fk_product > 0 && empty($fk_fournprice) && empty($pa_ht)) {
886 // by external module, take lowest buying price
887 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
888 $productFournisseur = new ProductFournisseur($this->db);
889 $productFournisseur->find_min_price_product_fournisseur($fk_product);
890 $this->line->fk_fournprice = $productFournisseur->product_fourn_price_id;
891 } else {
892 $this->line->fk_fournprice = $fk_fournprice;
893 }
894 $this->line->pa_ht = $pa_ht;
895
896 if (is_array($array_options) && count($array_options) > 0) {
897 // We replace values in this->line->array_options only for entries defined into $array_options
898 foreach ($array_options as $key => $value) {
899 $this->line->array_options[$key] = $array_options[$key];
900 }
901 }
902
903 // Multicurrency
904 $this->line->multicurrency_subprice = (float) $pu_ht_devise;
905 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
906 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
907 $this->line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
908
909 $result = $this->line->update();
910 if ($result > 0) {
911 // Reorder if child line
912 if (!empty($fk_parent_line)) {
913 $this->line_order(true, 'DESC');
914 }
915
916 $this->update_price(1);
917
918 $this->db->commit();
919 return $result;
920 } else {
921 $this->error = $this->db->error();
922 $this->db->rollback();
923 return -1;
924 }
925 } else {
926 dol_syslog(get_class($this)."::updateline Erreur -2 SupplierProposal en mode incompatible pour cette action");
927 return -2;
928 }
929 }
930
931
938 public function deleteLine($lineid)
939 {
940 global $user;
941
942 if ($this->status == 0) {
943 $line = new SupplierProposalLine($this->db);
944
945 // For triggers
946 $line->fetch($lineid);
947
948 if ($this->id > 0 && (int) $line->fk_supplier_proposal !== (int) $this->id) {
949 $this->error = 'ErrorLineIDDoesNotMatchWithObjectID';
950 return -1;
951 }
952
953 if ($line->delete($user) > 0) {
954 $this->update_price(1);
955
956 return 1;
957 } else {
958 return -1;
959 }
960 } else {
961 return -2;
962 }
963 }
964
965
974 public function create($user, $notrigger = 0)
975 {
976 global $conf, $mysoc, $hookmanager;
977 $error = 0;
978
979 $now = dol_now();
980
981 dol_syslog(get_class($this)."::create");
982
983 // Check parameters
984 $result = $this->fetch_thirdparty();
985 if ($result < 0) {
986 $this->error = "Failed to fetch company";
987 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
988 return -3;
989 }
990 if (!empty($this->ref)) { // We check that ref is not already used
991 $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
992 if ($result > 0) {
993 $this->error = 'ErrorRefAlreadyExists';
994 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
995 $this->db->rollback();
996 return -1;
997 }
998 }
999
1000 // Set tmp vars
1001 $delivery_date = $this->delivery_date;
1002
1003 // Multicurrency
1004 if (!empty($this->multicurrency_code)) {
1005 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $now);
1006 }
1007 if (empty($this->fk_multicurrency)) {
1008 $this->multicurrency_code = getDolCurrency();
1009 $this->fk_multicurrency = 0;
1010 $this->multicurrency_tx = 1;
1011 }
1012
1013 $this->db->begin();
1014
1015 // Insert into database
1016 $sql = "INSERT INTO ".MAIN_DB_PREFIX."supplier_proposal (";
1017 $sql .= "fk_soc";
1018 $sql .= ", price";
1019 $sql .= ", total_tva";
1020 $sql .= ", total_ttc";
1021 $sql .= ", datec";
1022 $sql .= ", ref";
1023 $sql .= ", fk_user_author";
1024 $sql .= ", note_private";
1025 $sql .= ", note_public";
1026 $sql .= ", model_pdf";
1027 $sql .= ", fk_cond_reglement";
1028 $sql .= ", deposit_percent";
1029 $sql .= ", fk_mode_reglement";
1030 $sql .= ", fk_account";
1031 $sql .= ", date_livraison";
1032 $sql .= ", fk_shipping_method";
1033 $sql .= ", fk_projet";
1034 $sql .= ", entity";
1035 $sql .= ", fk_multicurrency";
1036 $sql .= ", multicurrency_code";
1037 $sql .= ", multicurrency_tx";
1038 $sql .= ") ";
1039 $sql .= " VALUES (";
1040 $sql .= ((int) $this->socid);
1041 $sql .= ", 0";
1042 $sql .= ", 0";
1043 $sql .= ", 0";
1044 $sql .= ", '".$this->db->idate($now)."'";
1045 $sql .= ", '(PROV)'";
1046 $sql .= ", ".($user->id > 0 ? ((int) $user->id) : "null");
1047 $sql .= ", '".$this->db->escape($this->note_private)."'";
1048 $sql .= ", '".$this->db->escape($this->note_public)."'";
1049 $sql .= ", '".$this->db->escape($this->model_pdf)."'";
1050 $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : 'NULL');
1051 $sql .= ", ".(!empty($this->deposit_percent) ? "'" . $this->db->escape($this->deposit_percent) . "'" : 'NULL');
1052 $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : 'NULL');
1053 $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
1054 $sql .= ", ".(isDolTms($delivery_date) ? "'".$this->db->idate($delivery_date)."'" : "null");
1055 $sql .= ", ".($this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : 'NULL');
1056 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
1057 $sql .= ", ".((int) $conf->entity);
1058 $sql .= ", ".((int) $this->fk_multicurrency);
1059 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1060 $sql .= ", ".((float) $this->multicurrency_tx);
1061 $sql .= ")";
1062
1063 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1064 $resql = $this->db->query($sql);
1065 if ($resql) {
1066 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."supplier_proposal");
1067
1068 if ($this->id) {
1069 $this->ref = '(PROV'.$this->id.')';
1070 $sql = 'UPDATE '.MAIN_DB_PREFIX."supplier_proposal SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
1071
1072 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1073 $resql = $this->db->query($sql);
1074 if (!$resql) {
1075 $error++;
1076 }
1077
1078 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1079 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1080 }
1081
1082 // Add object linked
1083 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1084 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1085 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, ...))
1086 foreach ($tmp_origin_id as $origin_id) {
1087 $ret = $this->add_object_linked($origin, $origin_id);
1088 if (!$ret) {
1089 dol_print_error($this->db);
1090 $error++;
1091 }
1092 }
1093 }
1094 }
1095 }
1096
1097 /*
1098 * Insert products details into database
1099 */
1100 if (!$error) {
1101 $fk_parent_line = 0;
1102 $num = count($this->lines);
1103
1104 for ($i = 0; $i < $num; $i++) {
1105 // Reset fk_parent_line for no child products and special product
1106 if (($this->lines[$i]->product_type != 9 && empty($this->lines[$i]->fk_parent_line)) || $this->lines[$i]->product_type == 9) {
1107 $fk_parent_line = 0;
1108 }
1109
1110 $result = $this->addline(
1111 $this->lines[$i]->desc,
1112 $this->lines[$i]->subprice,
1113 $this->lines[$i]->qty,
1114 $this->lines[$i]->tva_tx,
1115 $this->lines[$i]->localtax1_tx,
1116 $this->lines[$i]->localtax2_tx,
1117 $this->lines[$i]->fk_product,
1118 $this->lines[$i]->remise_percent,
1119 'HT',
1120 0,
1121 0,
1122 $this->lines[$i]->product_type,
1123 $this->lines[$i]->rang,
1124 $this->lines[$i]->special_code,
1125 $fk_parent_line,
1126 $this->lines[$i]->fk_fournprice,
1127 $this->lines[$i]->pa_ht,
1128 empty($this->lines[$i]->label) ? '' : $this->lines[$i]->label, // deprecated
1129 $this->lines[$i]->array_options,
1130 $this->lines[$i]->ref_fourn,
1131 $this->lines[$i]->fk_unit,
1132 'supplier_proposal',
1133 $this->lines[$i]->rowid
1134 );
1135
1136 if ($result < 0) {
1137 $error++;
1138 $this->error = $this->db->error;
1139 dol_print_error($this->db);
1140 break;
1141 }
1142 // Defined the new fk_parent_line
1143 if ($result > 0 && $this->lines[$i]->product_type == 9) {
1144 $fk_parent_line = $result;
1145 }
1146 }
1147 }
1148
1149 if (!$error) {
1150 // Update denormalized data
1151 $resql = $this->update_price(1);
1152 if ($resql) {
1153 $action = 'update';
1154
1155 // Actions on extra fields
1156 if (!$error) {
1157 $result = $this->insertExtraFields();
1158 if ($result < 0) {
1159 $error++;
1160 }
1161 }
1162
1163 if (!$error && !$notrigger) {
1164 // Call trigger
1165 $result = $this->call_trigger('PROPOSAL_SUPPLIER_CREATE', $user);
1166 if ($result < 0) {
1167 $error++;
1168 }
1169 // End call triggers
1170 }
1171 } else {
1172 $this->error = $this->db->lasterror();
1173 $error++;
1174 }
1175 }
1176 } else {
1177 $this->error = $this->db->lasterror();
1178 $error++;
1179 }
1180
1181 if (!$error) {
1182 $this->db->commit();
1183 dol_syslog(get_class($this)."::create done id=".$this->id);
1184 return $this->id;
1185 } else {
1186 $this->db->rollback();
1187 return -2;
1188 }
1189 } else {
1190 $this->error = $this->db->lasterror();
1191 $this->db->rollback();
1192 return -1;
1193 }
1194 }
1195
1203 public function createFromClone(User $user, $fromid = 0)
1204 {
1205 global $hookmanager;
1206
1207 $error = 0;
1208 $now = dol_now();
1209
1210 $this->db->begin();
1211
1212 // get extrafields so they will be clone
1213 foreach ($this->lines as $line) {
1214 $line->fetch_optionals();
1215 }
1216
1217 // Load source object
1218 $objFrom = clone $this;
1219
1220 $objsoc = new Societe($this->db);
1221
1222 // Change socid if needed
1223 if (!empty($fromid) && $fromid != $this->socid) {
1224 if ($objsoc->fetch($fromid) > 0) {
1225 $this->socid = $objsoc->id;
1226 $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1227 $this->deposit_percent = (!empty($objsoc->deposit_percent) ? $objsoc->deposit_percent : 0);
1228 $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1229 unset($this->fk_project);
1230 }
1231
1232 // TODO Change product price if multi-prices
1233 } else {
1234 $objsoc->fetch($this->socid);
1235 }
1236
1237 $this->id = 0;
1238 $this->status = 0;
1239 $this->statut = 0;
1240
1241 if (!getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') || !is_readable(DOL_DOCUMENT_ROOT."/core/modules/supplier_proposal/" . getDolGlobalString('SUPPLIER_PROPOSAL_ADDON').".php")) {
1242 $this->error = 'ErrorSetupNotComplete';
1243 return -1;
1244 }
1245
1246 // Clear fields
1247 $this->user_author_id = $user->id;
1248 $this->user_validation_id = 0;
1249 $this->date = $now;
1250
1251 // Set ref
1252 require_once DOL_DOCUMENT_ROOT."/core/modules/supplier_proposal/" . getDolGlobalString('SUPPLIER_PROPOSAL_ADDON').'.php';
1253 $obj = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
1254 $modSupplierProposal = new $obj();
1255 '@phan-var-force ModeleNumRefSupplierProposal $modSupplierProposal';
1256 $this->ref = $modSupplierProposal->getNextValue($objsoc, $this);
1257
1258 // Create clone
1259 $this->context['createfromclone'] = 'createfromclone';
1260 $result = $this->create($user);
1261 if ($result < 0) {
1262 $error++;
1263 }
1264
1265 if (!$error) {
1266 // Hook of thirdparty module
1267 if (is_object($hookmanager)) {
1268 $parameters = array('objFrom' => $objFrom);
1269 $action = '';
1270 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1271 if ($reshook < 0) {
1272 $this->setErrorsFromObject($hookmanager);
1273 $error++;
1274 }
1275 }
1276 }
1277
1278 unset($this->context['createfromclone']);
1279
1280 // End
1281 if (!$error) {
1282 $this->db->commit();
1283 return $this->id;
1284 } else {
1285 $this->db->rollback();
1286 return -1;
1287 }
1288 }
1289
1297 public function fetch($rowid, $ref = '')
1298 {
1299 $sql = "SELECT p.rowid, p.entity, p.ref, p.fk_soc as socid";
1300 $sql .= ", p.total_ttc, p.total_tva, p.localtax1, p.localtax2, p.total_ht";
1301 $sql .= ", p.datec, GREATEST(p.tms, COALESCE(pef.tms, p.tms)) as date_modification";
1302 $sql .= ", p.date_valid as datev";
1303 $sql .= ", p.date_livraison as delivery_date";
1304 $sql .= ", p.model_pdf, p.extraparams";
1305 $sql .= ", p.note_private, p.note_public";
1306 $sql .= ", p.fk_projet as fk_project, p.fk_statut as status";
1307 $sql .= ", p.fk_user_author, p.fk_user_modif, p.fk_user_valid, p.fk_user_cloture";
1308 $sql .= ", p.fk_cond_reglement";
1309 $sql .= ", p.fk_mode_reglement";
1310 $sql .= ', p.fk_account';
1311 $sql .= ", p.fk_shipping_method";
1312 $sql .= ", p.last_main_doc";
1313 $sql .= ", p.fk_multicurrency, p.multicurrency_code, p.multicurrency_tx, p.multicurrency_total_ht, p.multicurrency_total_tva, p.multicurrency_total_ttc";
1314 $sql .= ", c.label as statut_label";
1315 $sql .= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc, p.deposit_percent";
1316 $sql .= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement";
1317 $sql .= " FROM ".MAIN_DB_PREFIX."c_propalst as c, ".MAIN_DB_PREFIX."supplier_proposal as p";
1318 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."supplier_proposal_extrafields as pef ON pef.fk_object=p.rowid";
1319 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id';
1320 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON p.fk_cond_reglement = cr.rowid';
1321 $sql .= " WHERE p.fk_statut = c.id";
1322 $sql .= " AND p.entity IN (".getEntity('supplier_proposal').")";
1323 if ($ref) {
1324 $sql .= " AND p.ref = '".$this->db->escape($ref)."'";
1325 } else {
1326 $sql .= " AND p.rowid = ".((int) $rowid);
1327 }
1328
1329 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1330 $resql = $this->db->query($sql);
1331 if ($resql) {
1332 if ($this->db->num_rows($resql)) {
1333 $obj = $this->db->fetch_object($resql);
1334
1335 $this->id = $obj->rowid;
1336 $this->entity = $obj->entity;
1337
1338 $this->ref = $obj->ref;
1339 $this->total_ht = $obj->total_ht;
1340 $this->total_tva = $obj->total_tva;
1341 $this->total_localtax1 = $obj->localtax1;
1342 $this->total_localtax2 = $obj->localtax2;
1343 $this->total_ttc = $obj->total_ttc;
1344 $this->socid = $obj->socid;
1345 $this->fk_project = $obj->fk_project;
1346 $this->model_pdf = $obj->model_pdf;
1347 $this->note = $obj->note_private; // TODO deprecated
1348 $this->note_private = $obj->note_private;
1349 $this->note_public = $obj->note_public;
1350 $this->statut = (int) $obj->status;
1351 $this->status = (int) $obj->status;
1352 $this->datec = $this->db->jdate($obj->datec); // TODO deprecated
1353 $this->datev = $this->db->jdate($obj->datev); // TODO deprecated
1354 $this->date_creation = $this->db->jdate($obj->datec); // Creation date
1355 $this->date_modification = $this->db->jdate($obj->date_modification); // Modification date
1356 $this->date = $this->date_creation;
1357 $this->date_validation = $this->db->jdate($obj->datev); // Validation date
1358 $this->delivery_date = $this->db->jdate($obj->delivery_date);
1359 $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1360
1361 $this->last_main_doc = $obj->last_main_doc;
1362 $this->mode_reglement_id = $obj->fk_mode_reglement;
1363 $this->mode_reglement_code = $obj->mode_reglement_code;
1364 $this->mode_reglement = $obj->mode_reglement;
1365 $this->deposit_percent = $obj->deposit_percent;
1366 $this->fk_account = ($obj->fk_account > 0) ? $obj->fk_account : null;
1367 $this->cond_reglement_id = $obj->fk_cond_reglement;
1368 $this->cond_reglement_code = $obj->cond_reglement_code;
1369 $this->cond_reglement = $obj->cond_reglement;
1370 $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1371
1372 $this->extraparams = (array) (!empty($obj->extraparams) ? json_decode($obj->extraparams, true) : array());
1373
1374 $this->user_author_id = $obj->fk_user_author;
1375 $this->user_modification_id = $obj->fk_user_modif;
1376 $this->user_validation_id = $obj->fk_user_valid;
1377 $this->user_closing_id = $obj->fk_user_cloture;
1378
1379 // Multicurrency
1380 $this->fk_multicurrency = $obj->fk_multicurrency;
1381 $this->multicurrency_code = $obj->multicurrency_code;
1382 $this->multicurrency_tx = $obj->multicurrency_tx;
1383 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1384 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1385 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1386
1387 $this->db->free($resql);
1388
1389 // Retrieve all extrafield
1390 // fetch optionals attributes and labels
1391 $this->fetch_optionals();
1392
1393 $this->lines = array();
1394
1395 // Lines of supplier proposals
1396 $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,";
1397 $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,";
1398 $sql .= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label,';
1399 $sql .= ' d.ref_fourn as ref_produit_fourn, d.extraparams,';
1400 $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';
1401 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposaldet as d";
1402 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON d.fk_product = p.rowid";
1403 $sql .= " WHERE d.fk_supplier_proposal = ".((int) $this->id);
1404 $sql .= " ORDER by d.rang";
1405
1406 $result = $this->db->query($sql);
1407 if ($result) {
1408 $num = $this->db->num_rows($result);
1409 $i = 0;
1410
1411 while ($i < $num) {
1412 $objp = $this->db->fetch_object($result);
1413
1414 $line = new SupplierProposalLine($this->db);
1415
1416 $line->rowid = $objp->rowid; // deprecated
1417 $line->id = $objp->rowid;
1418 $line->fk_supplier_proposal = $objp->fk_supplier_proposal;
1419 $line->fk_parent_line = $objp->fk_parent_line;
1420 $line->product_type = $objp->product_type;
1421 $line->label = $objp->custom_label;
1422 $line->desc = $objp->description; // Description of the line
1423 $line->qty = $objp->qty;
1424 $line->tva_tx = $objp->tva_tx;
1425 $line->localtax1_tx = $objp->localtax1_tx;
1426 $line->localtax2_tx = $objp->localtax2_tx;
1427 $line->subprice = $objp->subprice;
1428 $line->fk_remise_except = $objp->fk_remise_except;
1429 $line->remise_percent = $objp->remise_percent;
1430
1431 $line->info_bits = $objp->info_bits;
1432 $line->total_ht = $objp->total_ht;
1433 $line->total_tva = $objp->total_tva;
1434 $line->total_localtax1 = $objp->total_localtax1;
1435 $line->total_localtax2 = $objp->total_localtax2;
1436 $line->total_ttc = $objp->total_ttc;
1437 $line->fk_fournprice = $objp->fk_fournprice;
1438 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1439 $line->pa_ht = $marginInfos[0];
1440 $line->marge_tx = $marginInfos[1];
1441 $line->marque_tx = $marginInfos[2];
1442 $line->special_code = $objp->special_code;
1443 $line->rang = $objp->rang;
1444
1445 $line->fk_product = $objp->fk_product;
1446
1447 $line->ref = $objp->product_ref; // deprecated
1448 $line->product_ref = $objp->product_ref;
1449 $line->libelle = $objp->product_label; // deprecated
1450 $line->product_label = $objp->product_label;
1451 $line->product_desc = $objp->product_desc; // Description produit
1452 $line->fk_product_type = $objp->fk_product_type;
1453
1454 $line->ref_fourn = $objp->ref_produit_fourn;
1455
1456 $line->extraparams = !empty($objp->extraparams) ? (array) json_decode($objp->extraparams, true) : array();
1457
1458 // Multicurrency
1459 $line->fk_multicurrency = $objp->fk_multicurrency;
1460 $line->multicurrency_code = $objp->multicurrency_code;
1461 $line->multicurrency_subprice = $objp->multicurrency_subprice;
1462 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
1463 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
1464 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
1465 $line->fk_unit = $objp->fk_unit;
1466
1467 $line->fetch_optionals();
1468
1469 $this->lines[$i] = $line;
1470
1471 $i++;
1472 }
1473 $this->db->free($result);
1474 } else {
1475 $this->error = $this->db->error();
1476 return -1;
1477 }
1478
1479 return 1;
1480 }
1481
1482 $this->error = "Record Not Found";
1483 return 0;
1484 } else {
1485 $this->error = $this->db->error();
1486 return -1;
1487 }
1488 }
1489
1497 public function valid($user, $notrigger = 0)
1498 {
1499 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1500
1501 global $conf;
1502
1503 $error = 0;
1504 $now = dol_now();
1505
1506 if ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'creer'))
1507 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'validate_advance'))) {
1508 $this->db->begin();
1509
1510 // Numbering module definition
1511 $soc = new Societe($this->db);
1512 $result = $soc->fetch($this->socid);
1513
1514 if ($result < 0) {
1515 return -1;
1516 }
1517 if (!getDolGlobalBool('SUPPLIER_PROPOSAL_NOCHECK_ONBUY_PRODUCTS_ONVALID') && !$this->checkActiveProductInLines('onbuy')) {
1518 dol_syslog(get_class($this)."::valid checkActiveProductInLines ".$this->error, LOG_INFO);
1519 return -1;
1520 }
1521 // Define new ref
1522 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1523 $num = $this->getNextNumRef($soc);
1524 } else {
1525 $num = (string) $this->ref;
1526 }
1527 $this->newref = dol_sanitizeFileName($num);
1528
1529 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1530 $sql .= " SET ref = '".$this->db->escape($num)."',";
1531 $sql .= " fk_statut = 1, date_valid='".$this->db->idate($now)."', fk_user_valid=".((int) $user->id);
1532 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1533
1534 dol_syslog(get_class($this)."::valid", LOG_DEBUG);
1535 $resql = $this->db->query($sql);
1536 if (!$resql) {
1537 dol_print_error($this->db);
1538 $error++;
1539 }
1540
1541 // Trigger calls
1542 if (!$error && !$notrigger) {
1543 // Call trigger
1544 $result = $this->call_trigger('PROPOSAL_SUPPLIER_VALIDATE', $user);
1545 if ($result < 0) {
1546 $error++;
1547 }
1548 // End call triggers
1549 }
1550
1551 if (!$error) {
1552 $this->oldref = $this->ref;
1553
1554 // Rename directory if dir was a temporary ref
1555 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1556 // Now we rename also files into index
1557 $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)."'";
1558 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
1559 $resql = $this->db->query($sql);
1560 if (!$resql) {
1561 $error++;
1562 $this->error = $this->db->lasterror();
1563 }
1564 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'supplier_proposal/".$this->db->escape($this->newref)."'";
1565 $sql .= " WHERE filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
1566 $resql = $this->db->query($sql);
1567 if (!$resql) {
1568 $error++;
1569 $this->error = $this->db->lasterror();
1570 }
1571
1572 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1573 $oldref = dol_sanitizeFileName($this->ref);
1574 $newref = dol_sanitizeFileName($num);
1575 $dirsource = $conf->supplier_proposal->dir_output.'/'.$oldref;
1576 $dirdest = $conf->supplier_proposal->dir_output.'/'.$newref;
1577 if (!$error && file_exists($dirsource)) {
1578 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
1579 if (@rename($dirsource, $dirdest)) {
1580 dol_syslog("Rename ok");
1581 // Rename docs starting with $oldref with $newref
1582 $listoffiles = dol_dir_list($conf->supplier_proposal->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1583 foreach ($listoffiles as $fileentry) {
1584 $dirsource = $fileentry['name'];
1585 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1586 $dirsource = $fileentry['path'].'/'.$dirsource;
1587 $dirdest = $fileentry['path'].'/'.$dirdest;
1588 @rename($dirsource, $dirdest);
1589 }
1590 }
1591 }
1592 }
1593
1594 $this->ref = $num;
1595 $this->statut = self::STATUS_VALIDATED;
1597 $this->user_validation_id = $user->id;
1598 $this->datev = $now;
1599 $this->date_validation = $now;
1600
1601 if (getDolGlobalString('SUPPLIER_PROPOSAL_AUTOADD_USER_CONTACT')) {
1602 $result = $this->add_contact($user->id, 'SALESREPFOLL', 'internal', 1);
1603 if ($result < 0 && $result != -2) { // -2 means already exists
1604 $error++;
1605 }
1606 }
1607
1608 $this->db->commit();
1609 return 1;
1610 } else {
1611 $this->db->rollback();
1612 return -1;
1613 }
1614 } else {
1615 dol_syslog("You don't have permission to validate supplier proposal", LOG_WARNING);
1616 return -2;
1617 }
1618 }
1619
1620 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1629 public function set_date_livraison($user, $delivery_date)
1630 {
1631 // phpcs:enable
1632 return $this->setDeliveryDate($user, $delivery_date);
1633 }
1634
1642 public function setDeliveryDate($user, $delivery_date)
1643 {
1644 if ($user->hasRight('supplier_proposal', 'creer')) {
1645 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1646 $sql .= " SET date_livraison = ".(isDolTms($delivery_date) ? "'".$this->db->idate($delivery_date)."'" : 'null');
1647 $sql .= " WHERE rowid = ".((int) $this->id);
1648
1649 if ($this->db->query($sql)) {
1650 $this->delivery_date = $delivery_date;
1651 return 1;
1652 } else {
1653 $this->error = $this->db->error();
1654 dol_syslog(get_class($this)."::setDeliveryDate Erreur SQL");
1655 return -1;
1656 }
1657 }
1658 return 0;
1659 }
1660
1661
1671 public function reopen($user, $status, $note = '', $notrigger = 0)
1672 {
1673 $error = 0;
1674
1675 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1676 $sql .= " SET fk_statut = ".((int) $status).",";
1677 if (!empty($note)) {
1678 $sql .= " note_private = '".$this->db->escape($note)."',";
1679 }
1680 $sql .= " date_cloture = NULL, fk_user_cloture = NULL";
1681 $sql .= " WHERE rowid = ".((int) $this->id);
1682
1683 $this->db->begin();
1684
1685 dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
1686 $resql = $this->db->query($sql);
1687 if (!$resql) {
1688 $error++;
1689 $this->errors[] = "Error ".$this->db->lasterror();
1690 }
1691 if (!$error) {
1692 if (!$notrigger) {
1693 // Call trigger
1694 $result = $this->call_trigger('PROPOSAL_SUPPLIER_REOPEN', $user);
1695 if ($result < 0) {
1696 $error++;
1697 }
1698 // End call triggers
1699 }
1700 }
1701
1702 // Commit or rollback
1703 if ($error) {
1704 if (!empty($this->errors)) {
1705 foreach ($this->errors as $errmsg) {
1706 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1707 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1708 }
1709 }
1710 $this->db->rollback();
1711 return -1 * $error;
1712 } else {
1713 $this->statut = $status;
1714 $this->status = $status;
1715
1716 $this->db->commit();
1717 return 1;
1718 }
1719 }
1720
1721
1730 public function cloture($user, $status, $note)
1731 {
1732 global $langs, $conf;
1733 $hidedetails = 0;
1734 $hidedesc = 0;
1735 $hideref = 0;
1736
1737 $error = 0;
1738 $now = dol_now();
1739
1740 $this->db->begin();
1741
1742 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1743 $sql .= " SET fk_statut = ".((int) $status).", note_private = '".$this->db->escape($note)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".((int) $user->id);
1744 $sql .= " WHERE rowid = ".((int) $this->id);
1745
1746 $resql = $this->db->query($sql);
1747 if ($resql) {
1748 $this->statut = $status;
1749 $this->status = $status;
1750
1751 $modelpdf = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED', (empty($this->model_pdf) ? '' : $this->model_pdf));
1752 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_REFUSED';
1753
1754 if ($status == 2) {
1755 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_SIGNED';
1756 $modelpdf = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL', (empty($this->model_pdf) ? '' : $this->model_pdf));
1757
1758 if (getDolGlobalString('SUPPLIER_PROPOSAL_UPDATE_PRICE_ON_SUPPlIER_PROPOSAL')) { // TODO This option was not tested correctly. Error if product ref does not exists
1759 $result = $this->updateOrCreatePriceFournisseur($user);
1760 }
1761 }
1762 if ($status == 4) {
1763 $triggerName = 'PROPOSAL_SUPPLIER_CLASSIFY_BILLED';
1764 }
1765
1766 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1767 // Define output language
1768 $outputlangs = $langs;
1769 if (getDolGlobalInt('MAIN_MULTILANGS')) {
1770 $outputlangs = new Translate("", $conf);
1771 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
1772 $outputlangs->setDefaultLang($newlang);
1773 }
1774
1775 $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1776 }
1777
1778 // Call trigger
1779 $result = $this->call_trigger($triggerName, $user);
1780 if ($result < 0) {
1781 $error++;
1782 }
1783 // End call triggers
1784
1785 if (!$error) {
1786 $this->db->commit();
1787 return 1;
1788 } else {
1789 $this->db->rollback();
1790 return -1;
1791 }
1792 } else {
1793 $this->error = $this->db->lasterror();
1794 $this->errors[] = $this->db->lasterror();
1795 $this->db->rollback();
1796 return -1;
1797 }
1798 }
1799
1806 public function updateOrCreatePriceFournisseur($user)
1807 {
1808 dol_syslog(get_class($this)."::updateOrCreatePriceFournisseur", LOG_DEBUG);
1809
1810 foreach ($this->lines as $product) {
1811 if ($product->subprice <= 0) {
1812 continue;
1813 }
1814 $productsupplier = new ProductFournisseur($this->db);
1815
1816 $multicurrency_tx = 1;
1817 $fk_multicurrency = 0;
1818
1819 if (empty($this->thirdparty)) {
1820 $this->fetch_thirdparty();
1821 }
1822
1823 $ref_fourn = $product->ref_fourn;
1824 if (empty($ref_fourn)) {
1825 $ref_fourn = $product->ref_supplier;
1826 }
1827 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1828 list($fk_multicurrency, $multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $product->multicurrency_code);
1829 }
1830 $productsupplier->id = $product->fk_product;
1831
1832 $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);
1833 }
1834
1835 return 1;
1836 }
1837
1846 public function updatePriceFournisseur($idProductFournPrice, $product, $user)
1847 {
1848 $price = price2num($product->subprice * $product->qty, 'MU');
1849 $unitPrice = price2num($product->subprice, 'MU');
1850
1851 $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);
1852
1853 $resql = $this->db->query($sql);
1854 if (!$resql) {
1855 $this->error = $this->db->error();
1856 $this->db->rollback();
1857 return -1;
1858 }
1859 return 1;
1860 }
1861
1869 public function createPriceFournisseur($product, $user)
1870 {
1871 $price = price2num($product->subprice * $product->qty, 'MU');
1872 $qty = price2num($product->qty);
1873 $unitPrice = price2num($product->subprice, 'MU');
1874
1875 $now = dol_now();
1876
1877 $sanitized_values = array(
1878 "'".$this->db->idate($now)."'",
1879 (int) $product->fk_product,
1880 (int) $this->thirdparty->id,
1881 "'".$this->db->escape($product->ref_fourn)."'",
1882 (float) $price,
1883 (float) $qty,
1884 (float) $unitPrice,
1885 (float) $product->tva_tx,
1886 (int) $user->id
1887 );
1888 if (isModEnabled("multicurrency")) {
1889 if (!empty($product->multicurrency_code)) {
1890 include_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
1891 $multicurrency = new MultiCurrency($this->db); //need to fetch because empty fk_multicurrency and rate
1892 $multicurrency->fetch(0, $product->multicurrency_code);
1893 if (!empty($multicurrency->id)) {
1894 $sanitized_values[] = (int) $multicurrency->id;
1895 $sanitized_values[] = "'".$this->db->escape($product->multicurrency_code)."'";
1896 $sanitized_values[] = (float) $product->multicurrency_subprice;
1897 $sanitized_values[] = (float) $product->multicurrency_total_ht;
1898 $sanitized_values[] = (float) $multicurrency->rate->rate;
1899 } else {
1900 for ($i = 0; $i < 5; $i++) {
1901 $sanitized_values[] = 'NULL';
1902 }
1903 }
1904 }
1905 }
1906
1907 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'product_fournisseur_price ';
1908 $sql .= '(datec, fk_product, fk_soc, ref_fourn, price, quantity, unitprice, tva_tx, fk_user';
1909 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1910 $sql .= ',fk_multicurrency, multicurrency_code, multicurrency_unitprice, multicurrency_price, multicurrency_tx';
1911 }
1912 $sql .= ') VALUES ('.implode(',', $sanitized_values).')';
1913
1914 $resql = $this->db->query($sql);
1915 if (!$resql) {
1916 $this->error = $this->db->error();
1917 $this->db->rollback();
1918 return -1;
1919 }
1920 return 1;
1921 }
1922
1923 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1930 public function setDraft($user)
1931 {
1932 // phpcs:enable
1933 $error = 0;
1934
1935 if ($this->status == self::STATUS_DRAFT) {
1936 dol_syslog(get_class($this)."::setDraft already draft status", LOG_WARNING);
1937 return 0;
1938 }
1939
1940 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1941 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1942 $sql .= " WHERE rowid = ".((int) $this->id);
1943
1944 if ($this->db->query($sql)) {
1945 if (!$error) {
1946 $this->oldcopy = clone $this;
1947 }
1948
1949 if (!$error) {
1950 // Call trigger
1951 $result = $this->call_trigger('PROPOSAL_SUPPLIER_UNVALIDATE', $user);
1952 if ($result < 0) {
1953 $error++;
1954 }
1955 }
1956
1957 if (!$error) {
1958 $this->status = self::STATUS_DRAFT;
1959 $this->statut = self::STATUS_DRAFT; // deprecated
1960 $this->db->commit();
1961 return 1;
1962 } else {
1963 $this->db->rollback();
1964 return -1;
1965 }
1966 } else {
1967 return -1;
1968 }
1969 }
1970
1971
1972 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1986 public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datec', $sortorder = 'DESC')
1987 {
1988 // phpcs:enable
1989 global $user;
1990
1991 $ga = array();
1992
1993 $search_sale = 0;
1994 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
1995 $search_sale = $user->id;
1996 }
1997
1998 $sql = "SELECT s.rowid, s.nom as name, s.client,";
1999 $sql .= " p.rowid as supplier_proposalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
2000 $sql .= " p.datep as dp, p.fin_validite as datelimite";
2001 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."supplier_proposal as p, ".MAIN_DB_PREFIX."c_propalst as c";
2002 $sql .= " WHERE p.entity IN (".getEntity('supplier_proposal').")";
2003 $sql .= " AND p.fk_soc = s.rowid";
2004 $sql .= " AND p.fk_statut = c.id";
2005 if ($socid) {
2006 $sql .= " AND s.rowid = ".((int) $socid);
2007 }
2008 if ($draft) {
2009 $sql .= " AND p.fk_statut = 0";
2010 }
2011 if ($notcurrentuser > 0) {
2012 $sql .= " AND p.fk_user_author <> ".((int) $user->id);
2013 }
2014 // Search on sale representative
2015 if ($search_sale && $search_sale != '-1') {
2016 if ($search_sale == -2) {
2017 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
2018 } elseif ($search_sale > 0) {
2019 $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).")";
2020 }
2021 }
2022 $sql .= $this->db->order($sortfield, $sortorder);
2023 $sql .= $this->db->plimit($limit, $offset);
2024
2025 $result = $this->db->query($sql);
2026 if ($result) {
2027 $num = $this->db->num_rows($result);
2028 if ($num) {
2029 $i = 0;
2030 while ($i < $num) {
2031 $obj = $this->db->fetch_object($result);
2032
2033 if ($shortlist == 1) {
2034 $ga[$obj->supplier_proposalid] = $obj->ref;
2035 } elseif ($shortlist == 2) {
2036 $ga[$obj->supplier_proposalid] = $obj->ref.' ('.$obj->name.')';
2037 } else {
2038 $ga[$i]['id'] = $obj->supplier_proposalid;
2039 $ga[$i]['ref'] = $obj->ref;
2040 $ga[$i]['name'] = $obj->name;
2041 }
2042
2043 $i++;
2044 }
2045 }
2046 return $ga;
2047 } else {
2048 dol_print_error($this->db);
2049 return -1;
2050 }
2051 }
2052
2060 public function delete($user, $notrigger = 0)
2061 {
2062 global $conf;
2063 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2064
2065 $error = 0;
2066
2067 $this->db->begin();
2068
2069 if (!$notrigger) {
2070 // Call trigger
2071 $result = $this->call_trigger('PROPOSAL_SUPPLIER_DELETE', $user);
2072 if ($result < 0) {
2073 $error++;
2074 }
2075 // End call triggers
2076 }
2077
2078 // Remove linked categories.
2079 if (!$error) {
2080 $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_supplier_proposal";
2081 $sql .= " WHERE fk_supplier_proposal = ".((int) $this->id);
2082
2083 $result = $this->db->query($sql);
2084 if (!$result) {
2085 $error++;
2086 $this->error = $this->db->lasterror();
2087 $this->errors[] = $this->error;
2088 }
2089 }
2090
2091 if (!$error) {
2092 $sanitized_main = MAIN_DB_PREFIX.'supplier_proposaldet';
2093 $sanitized_ef = $sanitized_main."_extrafields";
2094 $sqlef = "DELETE FROM $sanitized_ef WHERE fk_object IN (SELECT rowid FROM $sanitized_main WHERE fk_supplier_proposal = ".((int) $this->id).")";
2095 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet WHERE fk_supplier_proposal = ".((int) $this->id);
2096 if ($this->db->query($sql)) {
2097 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposal WHERE rowid = ".((int) $this->id);
2098 if ($this->db->query($sqlef) && $this->db->query($sql)) {
2099 // Delete linked object
2100 $res = $this->deleteObjectLinked();
2101 if ($res < 0) {
2102 $error++;
2103 }
2104
2105 if (!$error) {
2106 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2107 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
2108 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
2109
2110 // We remove directory
2111 $ref = dol_sanitizeFileName($this->ref);
2112 if ($conf->supplier_proposal->dir_output && !empty($this->ref)) {
2113 $dir = $conf->supplier_proposal->dir_output."/".$ref;
2114 $file = $dir."/".$ref.".pdf";
2115 if (file_exists($file)) {
2116 dol_delete_preview($this);
2117
2118 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2119 $this->error = 'ErrorFailToDeleteFile';
2120 $this->errors = array('ErrorFailToDeleteFile');
2121 $this->db->rollback();
2122 return 0;
2123 }
2124 }
2125 if (file_exists($dir)) {
2126 $res = @dol_delete_dir_recursive($dir);
2127 if (!$res) {
2128 $this->error = 'ErrorFailToDeleteDir';
2129 $this->errors = array('ErrorFailToDeleteDir');
2130 $this->db->rollback();
2131 return 0;
2132 }
2133 }
2134 }
2135 }
2136
2137 // Removed extrafields
2138 if (!$error) {
2139 $result = $this->deleteExtraFields();
2140 if ($result < 0) {
2141 $error++;
2142 $errorflag = -4;
2143 dol_syslog(get_class($this)."::delete erreur ".$errorflag." ".$this->error, LOG_ERR);
2144 }
2145 }
2146
2147 if (!$error) {
2148 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
2149 $this->db->commit();
2150 return 1;
2151 } else {
2152 $this->error = $this->db->lasterror();
2153 $this->db->rollback();
2154 return 0;
2155 }
2156 } else {
2157 $this->error = $this->db->lasterror();
2158 $this->db->rollback();
2159 return -3;
2160 }
2161 } else {
2162 $this->error = $this->db->lasterror();
2163 $this->db->rollback();
2164 return -2;
2165 }
2166 } else {
2167 $this->db->rollback();
2168 return -1;
2169 }
2170 }
2171
2178 public function info($id)
2179 {
2180 $sql = "SELECT c.rowid, GREATEST(c.tms, COALESCE(cef.tms, c.tms)) as date_modification,";
2181 $sql .= " c.datec as date_creation, c.date_valid as date_validation, c.date_cloture as date_closure,";
2182 $sql .= " c.fk_user_author, c.fk_user_modif, c.fk_user_valid, c.fk_user_cloture";
2183 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as c";
2184 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."supplier_proposal_extrafields as cef ON cef.fk_object=c.rowid";
2185 $sql .= " WHERE c.rowid = ".((int) $id);
2186
2187 $result = $this->db->query($sql);
2188
2189 if ($result) {
2190 if ($this->db->num_rows($result)) {
2191 $obj = $this->db->fetch_object($result);
2192
2193 $this->id = $obj->rowid;
2194
2195 $this->date_creation = $this->db->jdate($obj->date_creation);
2196 $this->date_modification = empty($obj->date_modification) ? '' : $this->db->jdate($obj->date_modification);
2197 $this->date_validation = $this->db->jdate($obj->date_validation);
2198 $this->date_cloture = $this->db->jdate($obj->date_closure);
2199
2200 $this->user_creation_id = $obj->fk_user_author;
2201 $this->user_modification_id = $obj->fk_user_modif;
2202 $this->user_validation_id = $obj->fk_user_valid;
2203 $this->user_closing_id = $obj->fk_user_cloture;
2204 }
2205 $this->db->free($result);
2206 } else {
2207 dol_print_error($this->db);
2208 }
2209 }
2210
2211
2218 public function getLibStatut($mode = 0)
2219 {
2220 return $this->LibStatut((isset($this->status) ? $this->status : $this->statut), $mode);
2221 }
2222
2223 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2231 public function LibStatut($status, $mode = 1)
2232 {
2233 // phpcs:enable
2234
2235 // Init/load array of translation of status
2236 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
2237 global $langs;
2238 $langs->load("supplier_proposal");
2239 $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraft");
2240 $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidated");
2241 $this->labelStatus[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSigned");
2242 $this->labelStatus[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSigned");
2243 $this->labelStatus[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosed");
2244 $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraftShort");
2245 $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidatedShort");
2246 $this->labelStatusShort[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSignedShort");
2247 $this->labelStatusShort[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSignedShort");
2248 $this->labelStatusShort[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosedShort");
2249 }
2250
2251 $statusnew = '';
2252 if ($status == self::STATUS_DRAFT) {
2253 $statusnew = 'status0';
2254 } elseif ($status == self::STATUS_VALIDATED) {
2255 $statusnew = 'status1';
2256 } elseif ($status == self::STATUS_SIGNED) {
2257 $statusnew = 'status4';
2258 } elseif ($status == self::STATUS_NOTSIGNED) {
2259 $statusnew = 'status9';
2260 } elseif ($status == self::STATUS_CLOSE) {
2261 $statusnew = 'status6';
2262 }
2263
2264 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusnew, $mode);
2265 }
2266
2267
2268 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2276 public function load_board($user, $mode)
2277 {
2278 // phpcs:enable
2279 global $conf, $langs;
2280
2281 $now = dol_now();
2282
2283 $clause = " WHERE";
2284
2285 $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.date_cloture as datefin";
2286 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2287 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2288 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
2289 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2290 $clause = " AND";
2291 }
2292 $sql .= $clause." p.entity IN (".getEntity('supplier_proposal').")";
2293 if ($mode == 'opened') {
2294 $sql .= " AND p.fk_statut = 1";
2295 }
2296 if ($mode == 'signed') {
2297 $sql .= " AND p.fk_statut = 2";
2298 }
2299 if ($user->socid) {
2300 $sql .= " AND p.fk_soc = ".((int) $user->socid);
2301 }
2302
2303 $resql = $this->db->query($sql);
2304 if ($resql) {
2305 $label = $labelShort = '';
2306 $status = '';
2307 $delay_warning = 0;
2308 if ($mode == 'opened') {
2309 $delay_warning = !empty($conf->supplier_proposal->cloture->warning_delay) ? $conf->supplier_proposal->cloture->warning_delay : 0;
2310 $status = self::STATUS_VALIDATED;
2311 $label = $langs->trans("SupplierProposalsToClose");
2312 $labelShort = $langs->trans("ToAcceptRefuse");
2313 }
2314 if ($mode == 'signed') {
2315 $delay_warning = !empty($conf->supplier_proposal->facturation->warning_delay) ? $conf->supplier_proposal->facturation->warning_delay : 0;
2316 $status = self::STATUS_SIGNED;
2317 $label = $langs->trans("SupplierProposalsToProcess"); // May be billed or ordered
2318 $labelShort = $langs->trans("ToClose");
2319 }
2320
2321 $response = new WorkboardResponse();
2322 $response->warning_delay = $delay_warning / 60 / 60 / 24;
2323 $response->label = $label;
2324 $response->labelShort = $labelShort;
2325 $response->url = DOL_URL_ROOT.'/supplier_proposal/list.php?search_status='.$status;
2326 $response->img = img_object('', "propal");
2327
2328 // This assignment in condition is not a bug. It allows walking the results.
2329 while ($obj = $this->db->fetch_object($resql)) {
2330 $response->nbtodo++;
2331 if ($mode == 'opened') {
2332 $datelimit = $this->db->jdate($obj->datefin);
2333 if ($datelimit < ($now - $delay_warning)) {
2334 $response->nbtodolate++;
2335 }
2336 }
2337 // TODO Definir regle des propales a facturer en retard
2338 // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
2339 }
2340 return $response;
2341 } else {
2342 $this->error = $this->db->lasterror();
2343 return -1;
2344 }
2345 }
2346
2347
2356 public function initAsSpecimen($param = array())
2357 {
2358 global $conf, $langs;
2359
2360 // Load array of products prodids
2361 $num_prods = 0;
2362 $prodids = array();
2363 $sql = "SELECT rowid";
2364 $sql .= " FROM ".MAIN_DB_PREFIX."product";
2365 $sql .= " WHERE entity IN (".getEntity('product').")";
2366 if (array_key_exists('tobuy', $param)) {
2367 $sql .= " AND tobuy = ".((int) $param['tobuy']);
2368 }
2369 $sql .= $this->db->plimit(100);
2370
2371 $resql = $this->db->query($sql);
2372 if ($resql) {
2373 $num_prods = $this->db->num_rows($resql);
2374 $i = 0;
2375 while ($i < $num_prods) {
2376 $i++;
2377 $row = $this->db->fetch_row($resql);
2378 $prodids[$i] = $row[0];
2379 }
2380 }
2381
2382 // Initialise parameters
2383 $this->id = 0;
2384 $this->ref = 'SPECIMEN';
2385 $this->ref_supplier = 'NEMICEPS';
2386 $this->specimen = 1;
2387 $this->socid = 1;
2388 $this->date = time();
2389 $this->cond_reglement_id = 1;
2390 $this->cond_reglement_code = 'RECEP';
2391 $this->mode_reglement_id = 7;
2392 $this->mode_reglement_code = 'CHQ';
2393 $this->note_public = 'This is a comment (public)';
2394 $this->note_private = 'This is a comment (private)';
2395
2396 $this->multicurrency_tx = 1;
2397 $this->multicurrency_code = getDolCurrency();
2398
2399 // Lines
2400 $nbp = min(1000, GETPOSTINT('nblines') ? GETPOSTINT('nblines') : 5); // We can force the nb of lines to test from command line (but not more than 1000)
2401 $xnbp = 0;
2402 while ($xnbp < $nbp) {
2403 $line = new SupplierProposalLine($this->db);
2404 $line->desc = $langs->trans("Description")." ".$xnbp;
2405 $line->qty = 1;
2406 $line->subprice = 100;
2407 $line->tva_tx = 19.6;
2408 $line->localtax1_tx = 0;
2409 $line->localtax2_tx = 0;
2410 if ($xnbp == 2) {
2411 $line->total_ht = 50;
2412 $line->total_ttc = 59.8;
2413 $line->total_tva = 9.8;
2414 $line->remise_percent = 50;
2415 } else {
2416 $line->total_ht = 100;
2417 $line->total_ttc = 119.6;
2418 $line->total_tva = 19.6;
2419 $line->remise_percent = 00;
2420 }
2421
2422 if ($num_prods > 0) {
2423 $prodid = mt_rand(1, $num_prods);
2424 $line->fk_product = $prodids[$prodid];
2425 }
2426
2427 $this->lines[$xnbp] = $line;
2428
2429 $this->total_ht += $line->total_ht;
2430 $this->total_tva += $line->total_tva;
2431 $this->total_ttc += $line->total_ttc;
2432
2433 $xnbp++;
2434 }
2435
2436 return 1;
2437 }
2438
2444 public function loadStateBoard()
2445 {
2446 global $user;
2447
2448 $this->nb = array();
2449 $clause = "WHERE";
2450
2451 $sql = "SELECT count(p.rowid) as nb";
2452 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2453 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
2454 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2455 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2456 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2457 $clause = "AND";
2458 }
2459 $sql .= " ".$clause." p.entity IN (".getEntity('supplier_proposal').")";
2460
2461 $resql = $this->db->query($sql);
2462 if ($resql) {
2463 // This assignment in condition is not a bug. It allows walking the results.
2464 while ($obj = $this->db->fetch_object($resql)) {
2465 $this->nb["supplier_proposals"] = $obj->nb;
2466 }
2467 $this->db->free($resql);
2468 return 1;
2469 } else {
2470 dol_print_error($this->db);
2471 $this->error = $this->db->lasterror();
2472 return -1;
2473 }
2474 }
2475
2476
2484 public function getNextNumRef($soc)
2485 {
2486 global $conf, $db, $langs;
2487 $langs->load("supplier_proposal");
2488
2489 if (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON')) {
2490 $mybool = false;
2491
2492 $file = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') . ".php";
2493 $classname = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
2494
2495 // Include file with class
2496 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2497 foreach ($dirmodels as $reldir) {
2498 $dir = dol_buildpath($reldir."core/modules/supplier_proposal/");
2499
2500 // Load file with numbering class (if found)
2501 $mybool = ((bool) @include_once $dir.$file) || $mybool;
2502 }
2503
2504 if (!$mybool) {
2505 dol_print_error(null, "Failed to include file ".$file);
2506 return '';
2507 }
2508
2509 $obj = new $classname();
2510 '@phan-var-force ModeleNumRefSupplierProposal $obj';
2512 $numref = "";
2513 $numref = $obj->getNextValue($soc, $this);
2514
2515 if ($numref != "") {
2516 return $numref;
2517 } else {
2518 $this->error = $obj->error;
2519 return "";
2520 }
2521 } else {
2522 $langs->load("errors");
2523 print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("SupplierProposal"));
2524 return "";
2525 }
2526 }
2527
2534 public function getTooltipContentArray($params)
2535 {
2536 global $conf, $langs;
2537
2538 $langs->load('supplier_proposal');
2539
2540 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2541 return ['optimize' => $langs->trans("ShowSupplierProposal")];
2542 }
2543
2544 //$option = $params['option'] ?? '';
2545 $datas = [];
2546
2547 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("SupplierProposal").'</u>';
2548 if (isset($this->status)) {
2549 $datas['picto'] .= ' '.$this->getLibStatut(5);
2550 }
2551 if (!empty($this->ref)) {
2552 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2553 }
2554 if (!empty($this->ref_fourn)) {
2555 $datas['ref_supplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_fourn;
2556 }
2557 if (!empty($this->total_ht)) {
2558 $datas['amount_ht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, getDolCurrency());
2559 }
2560 if (!empty($this->total_tva)) {
2561 $datas['amount_vat'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, getDolCurrency());
2562 }
2563 if (!empty($this->total_ttc)) {
2564 $datas['amount_ttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, getDolCurrency());
2565 }
2566
2567 return $datas;
2568 }
2569
2581 public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2582 {
2583 global $langs, $conf, $user, $hookmanager;
2584
2585 if (!empty($conf->dol_no_mouse_hover)) {
2586 $notooltip = 1; // Force disable tooltips
2587 }
2588
2589 $url = '';
2590 $result = '';
2591 $params = [
2592 'id' => $this->id,
2593 'objecttype' => $this->element,
2594 'option' => $option,
2595 ];
2596 $classfortooltip = 'classfortooltip';
2597 $dataparams = '';
2598 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2599 $classfortooltip = 'classforajaxtooltip';
2600 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
2601 $label = '';
2602 } else {
2603 $label = implode($this->getTooltipContentArray($params));
2604 }
2605
2606 if ($option == '') {
2607 $url = DOL_URL_ROOT.'/supplier_proposal/card.php?id='.$this->id.$get_params;
2608 }
2609 if ($option == 'document') {
2610 $url = DOL_URL_ROOT.'/supplier_proposal/document.php?id='.$this->id.$get_params;
2611 }
2612
2613 if ($option !== 'nolink') {
2614 // Add param to save lastsearch_values or not
2615 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2616 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2617 $add_save_lastsearch_values = 1;
2618 }
2619 if ($add_save_lastsearch_values) {
2620 $url .= '&save_lastsearch_values=1';
2621 }
2622 }
2623
2624 $linkclose = '';
2625 if (empty($notooltip) && $user->hasRight('propal', 'lire')) {
2626 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2627 $label = $langs->trans("ShowSupplierProposal");
2628 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
2629 }
2630 $linkclose .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
2631 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
2632 }
2633
2634 $linkstart = '<a href="'.$url.'"';
2635 $linkstart .= $linkclose.'>';
2636 $linkend = '</a>';
2637
2638 $result .= $linkstart;
2639 if ($withpicto) {
2640 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
2641 }
2642 if ($withpicto != 2) {
2643 $result .= $this->ref;
2644 }
2645 $result .= $linkend;
2646
2647 if ($addlinktonotes) {
2648 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2649 if ($txttoshow) {
2650 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2651 $result .= ' <span class="note inline-block">';
2652 $result .= '<a href="'.DOL_URL_ROOT.'/supplier_proposal/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2653 $result .= img_picto('', 'note');
2654 $result .= '</a>';
2655 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
2656 //$result.='</a>';
2657 $result .= '</span>';
2658 }
2659 }
2660 global $action;
2661 $hookmanager->initHooks(array($this->element . 'dao'));
2662 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
2663 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2664 if ($reshook > 0) {
2665 $result = $hookmanager->resPrint;
2666 } else {
2667 $result .= $hookmanager->resPrint;
2668 }
2669 return $result;
2670 }
2671
2677 public function getLinesArray()
2678 {
2679 // For other object, here we call fetch_lines. But fetch_lines does not exists on supplier proposal
2680
2681 $sql = 'SELECT pt.rowid, pt.label as custom_label, pt.description, pt.fk_product, pt.fk_remise_except,';
2682 $sql .= ' pt.qty, pt.tva_tx, pt.vat_src_code, pt.remise_percent, pt.subprice, pt.info_bits,';
2683 $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,';
2684 $sql .= ' pt.product_type, pt.rang, pt.fk_parent_line, pt.extraparams,';
2685 $sql .= ' p.label as product_label, p.ref, p.fk_product_type, p.rowid as prodid,';
2686 $sql .= ' p.description as product_desc, pt.ref_fourn as ref_supplier,';
2687 $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';
2688 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pt';
2689 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pt.fk_product=p.rowid';
2690 $sql .= ' WHERE pt.fk_supplier_proposal = '.((int) $this->id);
2691 $sql .= ' ORDER BY pt.rang ASC, pt.rowid';
2692
2693 dol_syslog(get_class($this).'::getLinesArray', LOG_DEBUG);
2694 $resql = $this->db->query($sql);
2695 if ($resql) {
2696 $num = $this->db->num_rows($resql);
2697 $i = 0;
2698
2699 while ($i < $num) {
2700 $obj = $this->db->fetch_object($resql);
2701
2702 $this->lines[$i] = new SupplierProposalLine($this->db);
2703 $this->lines[$i]->id = $obj->rowid; // for backward compatibility
2704 $this->lines[$i]->rowid = $obj->rowid;
2705
2706 $this->lines[$i]->label = $obj->custom_label;
2707 $this->lines[$i]->description = $obj->description; // deprecated
2708 $this->lines[$i]->desc = $obj->description;
2709
2710 $this->lines[$i]->fk_product = $obj->fk_product;
2711 $this->lines[$i]->ref = $obj->ref;
2712 $this->lines[$i]->product_label = $obj->product_label;
2713 $this->lines[$i]->product_desc = $obj->product_desc;
2714 $this->lines[$i]->fk_product_type = $obj->fk_product_type; // deprecated
2715 $this->lines[$i]->product_type = $obj->product_type;
2716 $this->lines[$i]->qty = $obj->qty;
2717 $this->lines[$i]->subprice = $obj->subprice;
2718 $this->lines[$i]->fk_remise_except = $obj->fk_remise_except;
2719 $this->lines[$i]->remise_percent = $obj->remise_percent;
2720 $this->lines[$i]->tva_tx = $obj->tva_tx;
2721 $this->lines[$i]->vat_src_code = $obj->vat_src_code;
2722 $this->lines[$i]->info_bits = $obj->info_bits;
2723 $this->lines[$i]->total_ht = $obj->total_ht;
2724 $this->lines[$i]->total_tva = $obj->total_tva;
2725 $this->lines[$i]->total_ttc = $obj->total_ttc;
2726 $this->lines[$i]->fk_fournprice = $obj->fk_fournprice;
2727 $marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->lines[$i]->fk_fournprice, $obj->pa_ht);
2728 $this->lines[$i]->pa_ht = $marginInfos[0];
2729 $this->lines[$i]->marge_tx = $marginInfos[1];
2730 $this->lines[$i]->marque_tx = $marginInfos[2];
2731 $this->lines[$i]->fk_parent_line = $obj->fk_parent_line;
2732 $this->lines[$i]->special_code = $obj->special_code;
2733 $this->lines[$i]->rang = $obj->rang;
2734
2735 $this->lines[$i]->ref_fourn = $obj->ref_supplier; // deprecated
2736 $this->lines[$i]->ref_supplier = $obj->ref_supplier;
2737
2738 // Multicurrency
2739 $this->lines[$i]->fk_multicurrency = $obj->fk_multicurrency;
2740 $this->lines[$i]->multicurrency_code = $obj->multicurrency_code;
2741 $this->lines[$i]->multicurrency_subprice = $obj->multicurrency_subprice;
2742 $this->lines[$i]->multicurrency_total_ht = $obj->multicurrency_total_ht;
2743 $this->lines[$i]->multicurrency_total_tva = $obj->multicurrency_total_tva;
2744 $this->lines[$i]->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
2745 $this->lines[$i]->fk_unit = $obj->fk_unit;
2746 $this->lines[$i]->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
2747
2748 $this->lines[$i]->fetch_optionals();
2749
2750 $i++;
2751 }
2752 $this->db->free($resql);
2753
2754 return 1;
2755 } else {
2756 $this->error = $this->db->error();
2757 return -1;
2758 }
2759 }
2760
2772 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2773 {
2774 global $langs;
2775
2776 $langs->load("supplier_proposal");
2777 $outputlangs->load("products");
2778
2779 if (!dol_strlen($modele)) {
2780 $modele = ''; // On supplier documents, template can be empty( no doc generated in this case)
2781
2782 if ($this->model_pdf) {
2783 $modele = $this->model_pdf;
2784 } elseif (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF')) {
2785 $modele = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF');
2786 }
2787 }
2788
2789 if (empty($modele)) {
2790 return 1;
2791 }
2792
2793 $modelpath = "core/modules/supplier_proposal/doc/";
2794
2795 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2796 }
2797
2798
2807 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2808 {
2809 $tables = array(
2810 'supplier_proposal'
2811 );
2812
2813 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2814 }
2815
2824 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
2825 {
2826 $tables = array(
2827 'supplier_proposaldet'
2828 );
2829
2830 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
2831 }
2832
2833
2841 public function getKanbanView($option = '', $arraydata = null)
2842 {
2843 global $langs;
2844
2845 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2846
2847 $return = '<div class="box-flex-item box-flex-grow-zero">';
2848 $return .= '<div class="info-box info-box-sm">';
2849 $return .= '<span class="info-box-icon bg-infobox-action">';
2850 $return .= img_picto('', $this->picto);
2851 //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2852 $return .= '</span>';
2853 $return .= '<div class="info-box-content">';
2854 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
2855 if ($selected >= 0) {
2856 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2857 }
2858 if (property_exists($this, 'socid')) {
2859 $return .= '<span class="info-box-ref"> | '.$this->socid.'</span>';
2860 }
2861 if (property_exists($this, 'delivery_date')) {
2862 $return .= '<br><span class="opacitymedium">'.$langs->trans("DateEnd").'</span> : <span class="info-box-label">'.dol_print_date($this->delivery_date).'</span>';
2863 }
2864 if (property_exists($this, 'total_ttc')) {
2865 $return .= '<br><span class="opacitymedium" >'.$langs->trans("AmountHT").' : </span><span class="info-box-label amount">'.price($this->total_ttc).'</span>';
2866 }
2867 if (method_exists($this, 'getLibStatut')) {
2868 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
2869 }
2870 $return .= '</div>';
2871 $return .= '</div>';
2872 $return .= '</div>';
2873 return $return;
2874 }
2875
2886 public function setCategories($categories)
2887 {
2888 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2889 return parent::setCategoriesCommon($categories, Categorie::TYPE_SUPPLIER_PROPOSAL);
2890 }
2891}
2892
2893
2898{
2902 public $db;
2903
2907 public $error = '';
2908
2912 public $element = 'supplier_proposaldet';
2913
2917 public $table_element = 'supplier_proposaldet';
2918
2922 public $parent_element = 'supplier_proposal';
2923
2927 public $fk_parent_attribute = 'fk_supplier_proposal';
2928
2932 public $oldline;
2933
2937 public $id;
2938
2942 public $fk_supplier_proposal;
2943
2947 public $fk_parent_line;
2948
2952 public $desc; // Description of the line
2953
2957 public $fk_product; // Predefined Product ID
2958
2964 public $fk_product_type;
2970 public $product_type = Product::TYPE_PRODUCT;
2971
2975 public $qty;
2979 public $tva_tx;
2983 public $vat_src_code;
2984
2988 public $remise_percent;
2989
2993 public $fk_remise_except;
2994
2998 public $rang = 0;
2999
3003 public $fk_fournprice;
3004
3008 public $pa_ht;
3012 public $marge_tx;
3016 public $marque_tx;
3017
3021 public $special_code; // Tag for special lines (exclusive tags)
3022 // 1: frais de port
3023 // 2: ecotaxe
3024 // 3: option line (when qty = 0)
3025
3029 public $info_bits = 0; // Liste d'options cumulables:
3030 // Bit 0: 0 if TVA normal - 1 if TVA NPR
3031 // Bit 1: 0 normal line - 1 if fixed reduction
3032
3036 public $total_ht; // Line Net Total (HT), including all quantity and the line discount
3040 public $total_tva; // Line Net Total (VAT), including all quantity and the line discount
3044 public $total_ttc; // Line Net Total (TTC), including all quantity and the line discount
3045
3049 public $date_start;
3053 public $date_end;
3054
3055 // From llx_product
3061 public $ref;
3062
3067 public $product_ref;
3068
3074 public $libelle;
3075
3080 public $product_label;
3081
3086 public $label;
3087
3092 public $product_desc;
3093
3097 public $localtax1_tx; // Local tax 1
3101 public $localtax2_tx; // Local tax 2
3105 public $localtax1_type; // Local tax 1 type
3109 public $localtax2_type; // Local tax 2 type
3113 public $total_localtax1; // Line total local tax 1
3117 public $total_localtax2; // Line total local tax 2
3118
3122 public $skip_update_total; // Skip update price total for special lines
3123
3127 public $ref_fourn;
3131 public $ref_supplier;
3132
3133 // Multicurrency
3137 public $fk_multicurrency;
3138
3142 public $multicurrency_code;
3146 public $multicurrency_subprice;
3150 public $multicurrency_total_ht;
3154 public $multicurrency_total_tva;
3158 public $multicurrency_total_ttc;
3159
3165 public function __construct($db)
3166 {
3167 $this->db = $db;
3168 }
3169
3176 public function fetch($rowid)
3177 {
3178 $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,';
3179 $sql .= ' pd.date_start, pd.date_end,';
3180 $sql .= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice, pd.subprice_ttc,';
3181 $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,';
3182 $sql .= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
3183 $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
3184 $sql .= ' pd.product_type, pd.ref_fourn as ref_produit_fourn, pd.extraparams,';
3185 $sql .= ' pd.fk_multicurrency, pd.multicurrency_code, pd.multicurrency_subprice, pd.multicurrency_subprice_ttc, pd.multicurrency_total_ht, pd.multicurrency_total_tva, pd.multicurrency_total_ttc, pd.fk_unit';
3186 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pd';
3187 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
3188 $sql .= ' WHERE pd.rowid = '.((int) $rowid);
3189
3190 $result = $this->db->query($sql);
3191 if ($result) {
3192 if ($objp = $this->db->fetch_object($result)) {
3193 $this->id = $objp->rowid;
3194 $this->fk_supplier_proposal = $objp->fk_supplier_proposal;
3195 $this->fk_parent_line = $objp->fk_parent_line;
3196 $this->label = $objp->custom_label;
3197 $this->desc = $objp->description;
3198 $this->qty = $objp->qty;
3199
3200 $this->subprice = $objp->subprice;
3201 $this->subprice_ttc = $objp->subprice_ttc;
3202
3203 $this->tva_tx = $objp->tva_tx;
3204 $this->remise_percent = $objp->remise_percent;
3205 $this->fk_remise_except = $objp->fk_remise_except;
3206 $this->fk_product = $objp->fk_product;
3207 $this->info_bits = $objp->info_bits;
3208 $this->date_start = $this->db->jdate($objp->date_start);
3209 $this->date_end = $this->db->jdate($objp->date_end);
3210
3211 $this->total_ht = $objp->total_ht;
3212 $this->total_tva = $objp->total_tva;
3213 $this->total_ttc = $objp->total_ttc;
3214
3215 $this->fk_fournprice = $objp->fk_fournprice;
3216
3217 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
3218 $this->pa_ht = $marginInfos[0];
3219 $this->marge_tx = $marginInfos[1];
3220 $this->marque_tx = $marginInfos[2];
3221
3222 $this->special_code = $objp->special_code;
3223 $this->product_type = $objp->product_type;
3224 $this->rang = $objp->rang;
3225
3226 $this->product_ref = $objp->product_ref;
3227 $this->product_label = $objp->product_label;
3228 $this->product_desc = $objp->product_desc;
3229
3230 $this->ref_fourn = $objp->ref_produit_fourn;
3231
3232 $this->extraparams = !empty($objp->extraparams) ? (array) json_decode($objp->extraparams, true) : array();
3233
3234 // Multicurrency
3235 $this->fk_multicurrency = $objp->fk_multicurrency;
3236 $this->multicurrency_code = $objp->multicurrency_code;
3237 $this->multicurrency_subprice = $objp->multicurrency_subprice;
3238 $this->multicurrency_subprice_ttc = $objp->multicurrency_subprice_ttc;
3239 $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3240 $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3241 $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3242 $this->fk_unit = $objp->fk_unit;
3243
3244 $this->db->free($result);
3245 return 1;
3246 }
3247 return 0;
3248 } else {
3249 dol_print_error($this->db);
3250 return -1;
3251 }
3252 }
3253
3260 public function insert($notrigger = 0)
3261 {
3262 global $user;
3263
3264 $error = 0;
3265
3266 dol_syslog(get_class($this)."::insert rang=".$this->rang);
3267
3268 // Clean parameters
3269 if (empty($this->tva_tx)) {
3270 $this->tva_tx = 0;
3271 }
3272 if (empty($this->vat_src_code)) {
3273 $this->vat_src_code = '';
3274 }
3275 if (empty($this->localtax1_tx)) {
3276 $this->localtax1_tx = 0;
3277 }
3278 if (empty($this->localtax2_tx)) {
3279 $this->localtax2_tx = 0;
3280 }
3281 if (empty($this->localtax1_type)) {
3282 $this->localtax1_type = '';
3283 }
3284 if (empty($this->localtax2_type)) {
3285 $this->localtax2_type = '';
3286 }
3287 if (empty($this->total_localtax1)) {
3288 $this->total_localtax1 = 0;
3289 }
3290 if (empty($this->total_localtax2)) {
3291 $this->total_localtax2 = 0;
3292 }
3293 if (empty($this->rang)) {
3294 $this->rang = 0;
3295 }
3296 if (empty($this->remise_percent)) {
3297 $this->remise_percent = 0;
3298 }
3299 if (empty($this->info_bits)) {
3300 $this->info_bits = 0;
3301 }
3302 if (empty($this->special_code)) {
3303 $this->special_code = 0;
3304 }
3305 if (empty($this->fk_parent_line)) {
3306 $this->fk_parent_line = 0;
3307 }
3308 if (empty($this->fk_fournprice)) {
3309 $this->fk_fournprice = 0;
3310 }
3311 if (empty($this->fk_unit)) {
3312 $this->fk_unit = 0;
3313 }
3314 if (empty($this->subprice)) {
3315 $this->subprice = 0;
3316 }
3317
3318 if (empty($this->pa_ht)) {
3319 $this->pa_ht = 0;
3320 }
3321
3322 // if buy price not defined, define buyprice as configured in margin admin
3323 if ($this->pa_ht == 0) {
3324 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product, $this->qty);
3325 if ($result < 0) {
3326 return $result;
3327 } else {
3328 $this->pa_ht = $result;
3329 }
3330 }
3331
3332 // Check parameters
3333 if ($this->product_type < 0) {
3334 return -1;
3335 }
3336
3337 $this->db->begin();
3338
3339 // Insert line into database
3340 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'supplier_proposaldet';
3341 $sql .= ' (fk_supplier_proposal, fk_parent_line, label, description, fk_product, product_type,';
3342 $sql .= ' date_start, date_end,';
3343 $sql .= ' fk_remise_except, qty, tva_tx, vat_src_code, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3344 $sql .= ' subprice, subprice_ttc, remise_percent, ';
3345 $sql .= ' info_bits, ';
3346 $sql .= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
3347 $sql .= ' ref_fourn,';
3348 $sql .= ' fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_subprice_ttc, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc, fk_unit)';
3349 $sql .= " VALUES (".((int) $this->fk_supplier_proposal).",";
3350 $sql .= " ".($this->fk_parent_line > 0 ? ((int) $this->fk_parent_line) : "null").",";
3351 $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
3352 $sql .= " '".$this->db->escape($this->desc)."',";
3353 $sql .= " ".($this->fk_product ? ((int) $this->fk_product) : "null").",";
3354 $sql .= " ".((int) $this->product_type).",";
3355 $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3356 $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3357 $sql .= " ".($this->fk_remise_except ? ((int) $this->fk_remise_except) : "null").",";
3358 $sql .= " ".(float) price2num($this->qty, 'MS').",";
3359 $sql .= " ".(float) price2num($this->tva_tx).",";
3360 $sql .= " '".$this->db->escape($this->vat_src_code)."',";
3361 $sql .= " ".(float) price2num($this->localtax1_tx).",";
3362 $sql .= " ".(float) price2num($this->localtax2_tx).",";
3363 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3364 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3365 $sql .= " ".(float) price2num($this->subprice, 'MU') .",";
3366 $sql .= " ".(float) price2num($this->subprice_ttc, 'MU') .",";
3367 $sql .= " ".((float) $this->remise_percent).",";
3368 $sql .= " ".(isset($this->info_bits) ? ((int) $this->info_bits) : "null").",";
3369 $sql .= " ".(float) price2num($this->total_ht, 'MT').",";
3370 $sql .= " ".(float) price2num($this->total_tva, 'MT').",";
3371 $sql .= " ".(float) price2num($this->total_localtax1, 'MT').",";
3372 $sql .= " ".(float) price2num($this->total_localtax2, 'MT').",";
3373 $sql .= " ".(float) price2num($this->total_ttc, 'MT').",";
3374 $sql .= " ".(!empty($this->fk_fournprice) ? ((int) $this->fk_fournprice) : "null").",";
3375 $sql .= " ".(isset($this->pa_ht) ? (float) price2num($this->pa_ht, 'MU') : "null").",";
3376 $sql .= ' '.((int) $this->special_code).',';
3377 $sql .= ' '.((int) $this->rang).',';
3378 $sql .= " '".$this->db->escape($this->ref_fourn)."'";
3379 $sql .= ", ".($this->fk_multicurrency > 0 ? ((int) $this->fk_multicurrency) : 'null');
3380 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3381 $sql .= ", ".(float) price2num($this->multicurrency_subprice, 'CU');
3382 $sql .= ", ".(float) price2num($this->multicurrency_subprice_ttc, 'CU');
3383 $sql .= ", ".(float) price2num($this->multicurrency_total_ht, 'CT');
3384 $sql .= ", ".(float) price2num($this->multicurrency_total_tva, 'CT');
3385 $sql .= ", ".(float) price2num($this->multicurrency_total_ttc, 'CT');
3386 $sql .= ", ".($this->fk_unit ? ((int) $this->fk_unit) : 'null');
3387 $sql .= ')';
3388
3389 dol_syslog(get_class($this).'::insert', LOG_DEBUG);
3390 $resql = $this->db->query($sql);
3391 if ($resql) {
3392 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'supplier_proposaldet');
3393
3394 if (!$error) {
3395 $result = $this->insertExtraFields();
3396 if ($result < 0) {
3397 $error++;
3398 }
3399 }
3400
3401 if (!$error && !$notrigger) {
3402 // Call trigger
3403 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_INSERT', $user);
3404 if ($result < 0) {
3405 $this->db->rollback();
3406 return -1;
3407 }
3408 // End call triggers
3409 }
3410
3411 $this->db->commit();
3412 return 1;
3413 } else {
3414 $this->error = $this->db->error()." sql=".$sql;
3415 $this->db->rollback();
3416 return -1;
3417 }
3418 }
3419
3426 public function delete($user)
3427 {
3428 $error = 0;
3429
3430 $this->db->begin();
3431
3432 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet";
3433 $sql .= " WHERE rowid = ".((int) $this->id);
3434
3435 if ($this->db->query($sql)) {
3436 // Remove extrafields
3437 if (!$error) {
3438 $result = $this->deleteExtraFields();
3439 if ($result < 0) {
3440 $error++;
3441 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3442 }
3443 }
3444
3445 // Call trigger
3446 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_DELETE', $user);
3447 if ($result < 0) {
3448 $this->db->rollback();
3449 return -1;
3450 }
3451 // End call triggers
3452
3453 $this->db->commit();
3454
3455 return 1;
3456 } else {
3457 $this->error = $this->db->error()." sql=".$sql;
3458 $this->db->rollback();
3459 return -1;
3460 }
3461 }
3462
3469 public function update($notrigger = 0)
3470 {
3471 global $user;
3472
3473 $error = 0;
3474
3475 // Clean parameters
3476 if (empty($this->tva_tx)) {
3477 $this->tva_tx = 0;
3478 }
3479 if (empty($this->localtax1_tx)) {
3480 $this->localtax1_tx = 0;
3481 }
3482 if (empty($this->localtax2_tx)) {
3483 $this->localtax2_tx = 0;
3484 }
3485 if (empty($this->total_localtax1)) {
3486 $this->total_localtax1 = 0;
3487 }
3488 if (empty($this->total_localtax2)) {
3489 $this->total_localtax2 = 0;
3490 }
3491 if (empty($this->localtax1_type)) {
3492 $this->localtax1_type = '';
3493 }
3494 if (empty($this->localtax2_type)) {
3495 $this->localtax2_type = '';
3496 }
3497 if (empty($this->marque_tx)) {
3498 $this->marque_tx = 0;
3499 }
3500 if (empty($this->marge_tx)) {
3501 $this->marge_tx = 0;
3502 }
3503 if (empty($this->remise_percent)) {
3504 $this->remise_percent = 0;
3505 }
3506 if (empty($this->info_bits)) {
3507 $this->info_bits = 0;
3508 }
3509 if (empty($this->special_code)) {
3510 $this->special_code = 0;
3511 }
3512 if (empty($this->fk_parent_line)) {
3513 $this->fk_parent_line = 0;
3514 }
3515 if (empty($this->fk_fournprice)) {
3516 $this->fk_fournprice = 0;
3517 }
3518 if (empty($this->fk_unit)) {
3519 $this->fk_unit = 0;
3520 }
3521 if (empty($this->subprice)) {
3522 $this->subprice = 0;
3523 }
3524
3525 if (empty($this->pa_ht)) {
3526 $this->pa_ht = 0;
3527 }
3528
3529 // if buy price not defined, define buyprice as configured in margin admin
3530 if ($this->pa_ht == 0) {
3531 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product, $this->qty);
3532 if ($result < 0) {
3533 return $result;
3534 } else {
3535 $this->pa_ht = $result;
3536 }
3537 }
3538
3539 $this->db->begin();
3540
3541 // Update line in database
3542 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3543 $sql .= " description = '".$this->db->escape($this->desc)."'";
3544 $sql .= " , label = ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
3545 $sql .= " , product_type = ".((int) $this->product_type);
3546 $sql .= " , date_start = ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null");
3547 $sql .= " , date_end = ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null");
3548 $sql .= " , tva_tx = '".price2num($this->tva_tx)."'";
3549 $sql .= " , localtax1_tx = ".(float) price2num($this->localtax1_tx);
3550 $sql .= " , localtax2_tx = ".(float) price2num($this->localtax2_tx);
3551 $sql .= " , localtax1_type = '".$this->db->escape($this->localtax1_type)."'";
3552 $sql .= " , localtax2_type = '".$this->db->escape($this->localtax2_type)."'";
3553 $sql .= " , qty = ".(float) price2num($this->qty);
3554 $sql .= " , subprice = ".(float) price2num($this->subprice);
3555 $sql .= " , subprice_ttc = ".(float) price2num($this->subprice_ttc);
3556 $sql .= " , remise_percent = ".(float) price2num($this->remise_percent);
3557 $sql .= " , info_bits = ".((int) $this->info_bits);
3558 if (empty($this->skip_update_total)) {
3559 $sql .= " , total_ht = ".(float) price2num($this->total_ht);
3560 $sql .= " , total_tva = ".(float) price2num($this->total_tva);
3561 $sql .= " , total_ttc = ".(float) price2num($this->total_ttc);
3562 $sql .= " , total_localtax1 = ".(float) price2num($this->total_localtax1);
3563 $sql .= " , total_localtax2 = ".(float) price2num($this->total_localtax2);
3564 }
3565 $sql .= " , fk_product_fournisseur_price = ".(!empty($this->fk_fournprice) ? "'".$this->db->escape((string) $this->fk_fournprice)."'" : "null");
3566 $sql .= " , buy_price_ht = ".(float) price2num($this->pa_ht);
3567 $sql .= " , special_code = ".((int) $this->special_code);
3568 $sql .= " , fk_parent_line = ".($this->fk_parent_line > 0 ? ((int) $this->fk_parent_line) : "null");
3569 if (!empty($this->rang)) {
3570 $sql .= ", rang = ".((int) $this->rang);
3571 }
3572 $sql .= " , ref_fourn = ".(!empty($this->ref_fourn) ? "'".$this->db->escape($this->ref_fourn)."'" : "null");
3573 $sql .= " , fk_unit = ".($this->fk_unit ? (int) $this->fk_unit : 'null');
3574
3575 // Multicurrency
3576 $sql .= " , multicurrency_subprice = ".(float) price2num($this->multicurrency_subprice);
3577 $sql .= " , multicurrency_subprice_ttc = ".(float) price2num($this->multicurrency_subprice_ttc);
3578 $sql .= " , multicurrency_total_ht = ".(float) price2num($this->multicurrency_total_ht);
3579 $sql .= " , multicurrency_total_tva = ".(float) price2num($this->multicurrency_total_tva);
3580 $sql .= " , multicurrency_total_ttc = ".(float) price2num($this->multicurrency_total_ttc);
3581
3582 $sql .= " WHERE rowid = ".((int) $this->id);
3583
3584 dol_syslog(get_class($this)."::update", LOG_DEBUG);
3585 $resql = $this->db->query($sql);
3586 if ($resql) {
3587 if (!$error) {
3588 $result = $this->insertExtraFields();
3589 if ($result < 0) {
3590 $error++;
3591 }
3592 }
3593
3594 if (!$error && !$notrigger) {
3595 // Call trigger
3596 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_MODIFY', $user);
3597 if ($result < 0) {
3598 $this->db->rollback();
3599 return -1;
3600 }
3601 // End call triggers
3602 }
3603
3604 $this->db->commit();
3605 return 1;
3606 } else {
3607 $this->error = $this->db->error();
3608 $this->db->rollback();
3609 return -2;
3610 }
3611 }
3612
3613 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3620 public function update_total()
3621 {
3622 // phpcs:enable
3623 $this->db->begin();
3624
3625 // Update line in database
3626 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3627 $sql .= " total_ht = ".(float) price2num($this->total_ht, 'MT');
3628 $sql .= ",total_tva = ".(float) price2num($this->total_tva, 'MT');
3629 $sql .= ",total_ttc = ".(float) price2num($this->total_ttc, 'MT');
3630 $sql .= " WHERE rowid = ".((int) $this->id);
3631
3632 dol_syslog("SupplierProposalLine::update_total", LOG_DEBUG);
3633
3634 $resql = $this->db->query($sql);
3635 if ($resql) {
3636 $this->db->commit();
3637 return 1;
3638 } else {
3639 $this->error = $this->db->error();
3640 $this->db->rollback();
3641 return -2;
3642 }
3643 }
3644}
$object ref
Definition info.php:90
Parent class of all other business classes (invoices, contracts, proposals, orders,...
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
Save a new position (field rang) for details lines.
deleteEcmFiles($mode=0)
Delete related files of object in database.
update_price($exclspec=0, $roundingadjust='auto', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
add_object_linked($origin=null, $origin_id=null, $f_user=null, $notrigger=0)
Add an object link into llx_element_element.
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
isLineOfObject($lineid)
Check that a line belongs to this object, using $this->table_element_line and $this->fk_element.
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)
checkActiveProductInLines($status='onsale')
Check if all products have the right status (on sale, on buy) called during validation of propal,...
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.
defineBuyPrice($unitPrice=0.0, $discountPercent=0.0, $fk_product=0, $qty=0)
Get buy price to use for margin calculation.
add_contact($fk_socpeople, $type_contact, $source='external', $notrigger=0)
Add a link between element $this->element and a contact.
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
Class to manage absolute discounts.
Class to manage Dolibarr database access.
Class Currency.
static getIdAndTxFromCode($dbs, $code, $date_document=0)
Get id and rate of currency from code.
Class to manage predefined suppliers products.
Class to manage products or services.
const TYPE_PRODUCT
Regular product.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage price ask supplier.
updatePriceFournisseur($idProductFournPrice, $product, $user)
Update ProductFournisseur.
updateline($rowid, $pu, $qty, $remise_percent, $txtva, $txlocaltax1=0, $txlocaltax2=0, $desc='', $price_base_type='HT', $info_bits=0, $special_code=0, $fk_parent_line=0, $skip_update_total=0, $fk_fournprice=0, $pa_ht=0, $label='', $type=0, $array_options=[], $ref_supplier='', $fk_unit=0, $pu_ht_devise=0)
Update a proposal line.
cloture($user, $status, $note)
Close the askprice.
initAsSpecimen($param=array())
Initialise an instance with random values.
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) The parameters are already suppo...
info($id)
Object SupplierProposal Information.
create($user, $notrigger=0)
Create commercial proposal into database this->ref can be set or empty.
insert_discount($idremise)
Adding line of fixed discount in the proposal in DB.
reopen($user, $status, $note='', $notrigger=0)
Reopen the commercial proposal.
static replaceProduct(DoliDB $db, $origin_id, $dest_id)
Function used to replace a product id with another one.
fetch($rowid, $ref='')
Load a proposal from database and its lines 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.
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, ...)
setCategories($categories)
Sets object to supplied categories.
static replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
updateOrCreatePriceFournisseur($user)
Add or update supplier price according to result of proposal.
loadStateBoard()
Load indicator this->nb of global stats widget.
deleteLine($lineid)
Delete detail line.
const STATUS_VALIDATED
Validated status.
createPriceFournisseur($product, $user)
Create Price Fournisseur.
getLibStatut($mode=0)
Return label of status of proposal (draft, validated, ...)
add_product($idproduct, $qty, $remise_percent=0)
Add line into array ->lines.
set_date_livraison($user, $delivery_date)
Set delivery date.
const STATUS_SIGNED
Signed quote.
getNomUrl($withpicto=0, $option='', $get_params='', $notooltip=0, $save_lastsearch_value=-1, $addlinktonotes=0)
Return clickable link of object (with eventually picto)
getKanbanView($option='', $arraydata=null)
Return clickable link of object (with eventually picto)
getTooltipContentArray($params)
getTooltipContentArray
valid($user, $notrigger=0)
Set status to validated.
const STATUS_CLOSE
Billed or closed/processed quote.
getLinesArray()
Retrieve an array of supplier proposal lines.
setDraft($user)
Set draft status.
Class to manage supplier_proposal lines.
fetch($rowid)
Retrieve the propal line object.
insert($notrigger=0)
Insert object line propal in database.
update_total()
Update DB line fields total_xxx Used by migration.
update($notrigger=0)
Update propal line object into DB.
__construct($db)
Class line Constructor.
Class to manage translations.
Class to manage Dolibarr users.
print $langs trans("Ref").' m titre as m m statut as status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition index.php:169
global $mysoc
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
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:64
dol_delete_preview($object)
Delete all preview files linked to object instance.
$date_start
Variables from include:
dol_now($mode='gmt')
Return date for now.
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)
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_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
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.
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.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0, $nodefault=0)
Return value of a param into GET or POST supervariable.
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)
getDolCurrency()
Return the main currency ('EUR', 'USD', ...)
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
GETPOSTINT($paramname, $method=0, $nodefault=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
getDolGlobalBool($key, $default=false)
Return a Dolibarr global constant boolean value.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
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 rate, 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...
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
buildzip.php
getMarginInfos($pv_ht, $remise_percent, $tva_tx, $localtax1_tx, $localtax2_tx, $fk_pa, $pa_ht)
Return an array with margins information of a line.
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $notused, $price_base_type, $info_bits, $type, $seller=null, $localtaxes_array=[], $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition price.lib.php:90
print $langs trans('Date')." left Ref Label right Qty right Price right TotalHT right TotalTTC right right right right right right right right right centpercent right TotalHT right n right VAT right n right TotalVAT right n No sujeto a RE IRPF right TotalLT1 right n right TotalLT2 right n right TotalTTC right n takeposcustomercurrency takeposcustomercurrency takeposcustomercurrency takeposcustomercurrency right TotalTTC takeposcustomercurrency right takeposcustomercurrency n right Paid right PaymentTypeShortLIQ right SELECT p pos_change as p datep as date
Definition receipt.php:492