dolibarr 25.0.0-alpha
supplier_proposal.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2002-2004 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2004 Eric Seigne <eric.seigne@ryxeo.com>
4 * Copyright (C) 2004-2011 Laurent Destailleur <eldy@users.sourceforge.net>
5 * Copyright (C) 2005 Marc Barilley <marc@ocebo.com>
6 * Copyright (C) 2005-2013 Regis Houssin <regis.houssin@inodbox.com>
7 * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
8 * Copyright (C) 2008 Raphael Bertrand <raphael.bertrand@resultic.fr>
9 * Copyright (C) 2010-2020 Juanjo Menent <jmenent@2byte.es>
10 * Copyright (C) 2010-2018 Philippe Grand <philippe.grand@atoo-net.com>
11 * Copyright (C) 2012-2014 Christophe Battarel <christophe.battarel@altairis.fr>
12 * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
13 * Copyright (C) 2014 Marcos García <marcosgdf@gmail.com>
14 * Copyright (C) 2016 Ferran Marcet <fmarcet@2byte.es>
15 * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
16 * Copyright (C) 2019-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 dol_syslog(get_class($this)."::updateLine $rowid, $pu, $qty, $remise_percent, $txtva, $desc, $price_base_type, $info_bits");
756 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
757
758 // Clean parameters
759 $remise_percent = price2num($remise_percent);
760 $qty = (float) price2num($qty);
761 $pu = price2num($pu);
762 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
763 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
764 }
765 $txlocaltax1 = price2num($txlocaltax1);
766 $txlocaltax2 = price2num($txlocaltax2);
767 $pa_ht = price2num($pa_ht);
768 if (empty($qty) && empty($special_code)) {
769 $special_code = 3; // Set option tag
770 }
771 if (!empty($qty) && $special_code == 3) {
772 $special_code = 0; // Remove option tag
773 }
774
775 if ($this->status == 0) {
776 $this->db->begin();
777
778 // Calculation of the gross total (TTC) and VAT for the line from qty, pu, remise_percent and txtva
779 // VERY IMPORTANT: It's at the time of line insertion that we must store the net, VAT, and gross amounts,
780 // and this is done at the line level, which has its own VAT rate
781
782 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
783
784 // Clean vat code
785 $reg = array();
786 $vat_src_code = '';
787 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
788 $vat_src_code = $reg[1];
789 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
790 }
791
792 if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
793 $pu = 0;
794 }
795
796 $tabprice = calcul_price_total(
797 $qty,
798 $pu,
799 (float) $remise_percent,
800 $txtva,
801 (float) $txlocaltax1,
802 (float) $txlocaltax2,
803 0,
804 $price_base_type,
805 $info_bits,
806 $type,
807 $this->thirdparty,
808 $localtaxes_type,
809 100,
810 (float) $this->multicurrency_tx,
811 $pu_ht_devise
812 );
813 $total_ht = $tabprice[0];
814 $total_tva = $tabprice[1];
815 $total_ttc = $tabprice[2];
816 $total_localtax1 = $tabprice[9];
817 $total_localtax2 = $tabprice[10];
818 $pu_ht = $tabprice[3];
819 $pu_tva = $tabprice[4];
820 $pu_ttc = $tabprice[5];
821
822 // MultiCurrency
823 $multicurrency_total_ht = $tabprice[16];
824 $multicurrency_total_tva = $tabprice[17];
825 $multicurrency_total_ttc = $tabprice[18];
826 $pu_ht_devise = $tabprice[19];
827
828 $pu = $pu_ht;
829 if ($price_base_type == 'TTC') {
830 $pu = $pu_ttc;
831 }
832
833 // Fetch current line from the database and then clone the object and set it in $oldline property
834 $line = new SupplierProposalLine($this->db);
835 $line->fetch($rowid);
836 $line->fetch_optionals();
837
838 $fk_product = $line->fk_product;
839
840 // Stock previous line records
841 $staticline = clone $line;
842
843 $line->oldline = $staticline;
844 $this->line = $line;
845 $this->line->context = $this->context;
846
847 // Reorder if fk_parent_line change
848 if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
849 $rangmax = $this->line_max($fk_parent_line);
850 $this->line->rang = $rangmax + 1;
851 }
852
853 $this->line->id = $rowid;
854 $this->line->label = $label;
855 $this->line->desc = $desc;
856 $this->line->qty = $qty;
857 $this->line->product_type = $type;
858
859 $this->line->vat_src_code = $vat_src_code;
860 $this->line->tva_tx = $txtva;
861 $this->line->localtax1_tx = $txlocaltax1;
862 $this->line->localtax2_tx = $txlocaltax2;
863 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
864 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
865 $this->line->remise_percent = $remise_percent;
866 $this->line->subprice = (float) $pu;
867 $this->line->info_bits = $info_bits;
868 $this->line->total_ht = (float) $total_ht;
869 $this->line->total_tva = (float) $total_tva;
870 $this->line->total_localtax1 = (float) $total_localtax1;
871 $this->line->total_localtax2 = (float) $total_localtax2;
872 $this->line->total_ttc = (float) $total_ttc;
873 $this->line->special_code = $special_code;
874 $this->line->fk_parent_line = $fk_parent_line;
875 $this->line->skip_update_total = $skip_update_total;
876 $this->line->ref_fourn = $ref_supplier;
877 $this->line->fk_unit = $fk_unit;
878
879 // infos merge
880 if (!empty($fk_product) && $fk_product > 0 && empty($fk_fournprice) && empty($pa_ht)) {
881 // by external module, take lowest buying price
882 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
883 $productFournisseur = new ProductFournisseur($this->db);
884 $productFournisseur->find_min_price_product_fournisseur($fk_product);
885 $this->line->fk_fournprice = $productFournisseur->product_fourn_price_id;
886 } else {
887 $this->line->fk_fournprice = $fk_fournprice;
888 }
889 $this->line->pa_ht = $pa_ht;
890
891 if (is_array($array_options) && count($array_options) > 0) {
892 // We replace values in this->line->array_options only for entries defined into $array_options
893 foreach ($array_options as $key => $value) {
894 $this->line->array_options[$key] = $array_options[$key];
895 }
896 }
897
898 // Multicurrency
899 $this->line->multicurrency_subprice = (float) $pu_ht_devise;
900 $this->line->multicurrency_total_ht = (float) $multicurrency_total_ht;
901 $this->line->multicurrency_total_tva = (float) $multicurrency_total_tva;
902 $this->line->multicurrency_total_ttc = (float) $multicurrency_total_ttc;
903
904 $result = $this->line->update();
905 if ($result > 0) {
906 // Reorder if child line
907 if (!empty($fk_parent_line)) {
908 $this->line_order(true, 'DESC');
909 }
910
911 $this->update_price(1);
912
913 $this->db->commit();
914 return $result;
915 } else {
916 $this->error = $this->db->error();
917 $this->db->rollback();
918 return -1;
919 }
920 } else {
921 dol_syslog(get_class($this)."::updateline Erreur -2 SupplierProposal en mode incompatible pour cette action");
922 return -2;
923 }
924 }
925
926
933 public function deleteLine($lineid)
934 {
935 global $user;
936
937 if ($this->status == 0) {
938 $line = new SupplierProposalLine($this->db);
939
940 // For triggers
941 $line->fetch($lineid);
942
943 if ($line->delete($user) > 0) {
944 $this->update_price(1);
945
946 return 1;
947 } else {
948 return -1;
949 }
950 } else {
951 return -2;
952 }
953 }
954
955
964 public function create($user, $notrigger = 0)
965 {
966 global $conf, $mysoc, $hookmanager;
967 $error = 0;
968
969 $now = dol_now();
970
971 dol_syslog(get_class($this)."::create");
972
973 // Check parameters
974 $result = $this->fetch_thirdparty();
975 if ($result < 0) {
976 $this->error = "Failed to fetch company";
977 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
978 return -3;
979 }
980 if (!empty($this->ref)) { // We check that ref is not already used
981 $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
982 if ($result > 0) {
983 $this->error = 'ErrorRefAlreadyExists';
984 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
985 $this->db->rollback();
986 return -1;
987 }
988 }
989
990 // Set tmp vars
991 $delivery_date = $this->delivery_date;
992
993 // Multicurrency
994 if (!empty($this->multicurrency_code)) {
995 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $now);
996 }
997 if (empty($this->fk_multicurrency)) {
998 $this->multicurrency_code = getDolCurrency();
999 $this->fk_multicurrency = 0;
1000 $this->multicurrency_tx = 1;
1001 }
1002
1003 $this->db->begin();
1004
1005 // Insert into database
1006 $sql = "INSERT INTO ".MAIN_DB_PREFIX."supplier_proposal (";
1007 $sql .= "fk_soc";
1008 $sql .= ", price";
1009 $sql .= ", total_tva";
1010 $sql .= ", total_ttc";
1011 $sql .= ", datec";
1012 $sql .= ", ref";
1013 $sql .= ", fk_user_author";
1014 $sql .= ", note_private";
1015 $sql .= ", note_public";
1016 $sql .= ", model_pdf";
1017 $sql .= ", fk_cond_reglement";
1018 $sql .= ", deposit_percent";
1019 $sql .= ", fk_mode_reglement";
1020 $sql .= ", fk_account";
1021 $sql .= ", date_livraison";
1022 $sql .= ", fk_shipping_method";
1023 $sql .= ", fk_projet";
1024 $sql .= ", entity";
1025 $sql .= ", fk_multicurrency";
1026 $sql .= ", multicurrency_code";
1027 $sql .= ", multicurrency_tx";
1028 $sql .= ") ";
1029 $sql .= " VALUES (";
1030 $sql .= ((int) $this->socid);
1031 $sql .= ", 0";
1032 $sql .= ", 0";
1033 $sql .= ", 0";
1034 $sql .= ", '".$this->db->idate($now)."'";
1035 $sql .= ", '(PROV)'";
1036 $sql .= ", ".($user->id > 0 ? ((int) $user->id) : "null");
1037 $sql .= ", '".$this->db->escape($this->note_private)."'";
1038 $sql .= ", '".$this->db->escape($this->note_public)."'";
1039 $sql .= ", '".$this->db->escape($this->model_pdf)."'";
1040 $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : 'NULL');
1041 $sql .= ", ".(!empty($this->deposit_percent) ? "'" . $this->db->escape($this->deposit_percent) . "'" : 'NULL');
1042 $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : 'NULL');
1043 $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
1044 $sql .= ", ".(isDolTms($delivery_date) ? "'".$this->db->idate($delivery_date)."'" : "null");
1045 $sql .= ", ".($this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : 'NULL');
1046 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
1047 $sql .= ", ".((int) $conf->entity);
1048 $sql .= ", ".((int) $this->fk_multicurrency);
1049 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
1050 $sql .= ", ".((float) $this->multicurrency_tx);
1051 $sql .= ")";
1052
1053 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1054 $resql = $this->db->query($sql);
1055 if ($resql) {
1056 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."supplier_proposal");
1057
1058 if ($this->id) {
1059 $this->ref = '(PROV'.$this->id.')';
1060 $sql = 'UPDATE '.MAIN_DB_PREFIX."supplier_proposal SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
1061
1062 dol_syslog(get_class($this)."::create", LOG_DEBUG);
1063 $resql = $this->db->query($sql);
1064 if (!$resql) {
1065 $error++;
1066 }
1067
1068 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1069 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1070 }
1071
1072 // Add object linked
1073 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1074 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1075 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, ...))
1076 foreach ($tmp_origin_id as $origin_id) {
1077 $ret = $this->add_object_linked($origin, $origin_id);
1078 if (!$ret) {
1079 dol_print_error($this->db);
1080 $error++;
1081 }
1082 }
1083 }
1084 }
1085 }
1086
1087 /*
1088 * Insert products details into database
1089 */
1090 if (!$error) {
1091 $fk_parent_line = 0;
1092 $num = count($this->lines);
1093
1094 for ($i = 0; $i < $num; $i++) {
1095 // Reset fk_parent_line for no child products and special product
1096 if (($this->lines[$i]->product_type != 9 && empty($this->lines[$i]->fk_parent_line)) || $this->lines[$i]->product_type == 9) {
1097 $fk_parent_line = 0;
1098 }
1099
1100 $result = $this->addline(
1101 $this->lines[$i]->desc,
1102 $this->lines[$i]->subprice,
1103 $this->lines[$i]->qty,
1104 $this->lines[$i]->tva_tx,
1105 $this->lines[$i]->localtax1_tx,
1106 $this->lines[$i]->localtax2_tx,
1107 $this->lines[$i]->fk_product,
1108 $this->lines[$i]->remise_percent,
1109 'HT',
1110 0,
1111 0,
1112 $this->lines[$i]->product_type,
1113 $this->lines[$i]->rang,
1114 $this->lines[$i]->special_code,
1115 $fk_parent_line,
1116 $this->lines[$i]->fk_fournprice,
1117 $this->lines[$i]->pa_ht,
1118 empty($this->lines[$i]->label) ? '' : $this->lines[$i]->label, // deprecated
1119 $this->lines[$i]->array_options,
1120 $this->lines[$i]->ref_fourn,
1121 $this->lines[$i]->fk_unit,
1122 'supplier_proposal',
1123 $this->lines[$i]->rowid
1124 );
1125
1126 if ($result < 0) {
1127 $error++;
1128 $this->error = $this->db->error;
1129 dol_print_error($this->db);
1130 break;
1131 }
1132 // Defined the new fk_parent_line
1133 if ($result > 0 && $this->lines[$i]->product_type == 9) {
1134 $fk_parent_line = $result;
1135 }
1136 }
1137 }
1138
1139 if (!$error) {
1140 // Update denormalized data
1141 $resql = $this->update_price(1);
1142 if ($resql) {
1143 $action = 'update';
1144
1145 // Actions on extra fields
1146 if (!$error) {
1147 $result = $this->insertExtraFields();
1148 if ($result < 0) {
1149 $error++;
1150 }
1151 }
1152
1153 if (!$error && !$notrigger) {
1154 // Call trigger
1155 $result = $this->call_trigger('PROPOSAL_SUPPLIER_CREATE', $user);
1156 if ($result < 0) {
1157 $error++;
1158 }
1159 // End call triggers
1160 }
1161 } else {
1162 $this->error = $this->db->lasterror();
1163 $error++;
1164 }
1165 }
1166 } else {
1167 $this->error = $this->db->lasterror();
1168 $error++;
1169 }
1170
1171 if (!$error) {
1172 $this->db->commit();
1173 dol_syslog(get_class($this)."::create done id=".$this->id);
1174 return $this->id;
1175 } else {
1176 $this->db->rollback();
1177 return -2;
1178 }
1179 } else {
1180 $this->error = $this->db->lasterror();
1181 $this->db->rollback();
1182 return -1;
1183 }
1184 }
1185
1193 public function createFromClone(User $user, $fromid = 0)
1194 {
1195 global $hookmanager;
1196
1197 $error = 0;
1198 $now = dol_now();
1199
1200 $this->db->begin();
1201
1202 // get extrafields so they will be clone
1203 foreach ($this->lines as $line) {
1204 $line->fetch_optionals();
1205 }
1206
1207 // Load source object
1208 $objFrom = clone $this;
1209
1210 $objsoc = new Societe($this->db);
1211
1212 // Change socid if needed
1213 if (!empty($fromid) && $fromid != $this->socid) {
1214 if ($objsoc->fetch($fromid) > 0) {
1215 $this->socid = $objsoc->id;
1216 $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1217 $this->deposit_percent = (!empty($objsoc->deposit_percent) ? $objsoc->deposit_percent : 0);
1218 $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1219 unset($this->fk_project);
1220 }
1221
1222 // TODO Change product price if multi-prices
1223 } else {
1224 $objsoc->fetch($this->socid);
1225 }
1226
1227 $this->id = 0;
1228 $this->status = 0;
1229 $this->statut = 0;
1230
1231 if (!getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') || !is_readable(DOL_DOCUMENT_ROOT."/core/modules/supplier_proposal/" . getDolGlobalString('SUPPLIER_PROPOSAL_ADDON').".php")) {
1232 $this->error = 'ErrorSetupNotComplete';
1233 return -1;
1234 }
1235
1236 // Clear fields
1237 $this->user_author_id = $user->id;
1238 $this->user_validation_id = 0;
1239 $this->date = $now;
1240
1241 // Set ref
1242 require_once DOL_DOCUMENT_ROOT."/core/modules/supplier_proposal/" . getDolGlobalString('SUPPLIER_PROPOSAL_ADDON').'.php';
1243 $obj = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
1244 $modSupplierProposal = new $obj();
1245 '@phan-var-force ModeleNumRefSupplierProposal $modSupplierProposal';
1246 $this->ref = $modSupplierProposal->getNextValue($objsoc, $this);
1247
1248 // Create clone
1249 $this->context['createfromclone'] = 'createfromclone';
1250 $result = $this->create($user);
1251 if ($result < 0) {
1252 $error++;
1253 }
1254
1255 if (!$error) {
1256 // Hook of thirdparty module
1257 if (is_object($hookmanager)) {
1258 $parameters = array('objFrom' => $objFrom);
1259 $action = '';
1260 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1261 if ($reshook < 0) {
1262 $this->setErrorsFromObject($hookmanager);
1263 $error++;
1264 }
1265 }
1266 }
1267
1268 unset($this->context['createfromclone']);
1269
1270 // End
1271 if (!$error) {
1272 $this->db->commit();
1273 return $this->id;
1274 } else {
1275 $this->db->rollback();
1276 return -1;
1277 }
1278 }
1279
1287 public function fetch($rowid, $ref = '')
1288 {
1289 $sql = "SELECT p.rowid, p.entity, p.ref, p.fk_soc as socid";
1290 $sql .= ", p.total_ttc, p.total_tva, p.localtax1, p.localtax2, p.total_ht";
1291 $sql .= ", p.datec, GREATEST(p.tms, pef.tms) as date_modification";
1292 $sql .= ", p.date_valid as datev";
1293 $sql .= ", p.date_livraison as delivery_date";
1294 $sql .= ", p.model_pdf, p.extraparams";
1295 $sql .= ", p.note_private, p.note_public";
1296 $sql .= ", p.fk_projet as fk_project, p.fk_statut as status";
1297 $sql .= ", p.fk_user_author, p.fk_user_modif, p.fk_user_valid, p.fk_user_cloture";
1298 $sql .= ", p.fk_cond_reglement";
1299 $sql .= ", p.fk_mode_reglement";
1300 $sql .= ', p.fk_account';
1301 $sql .= ", p.fk_shipping_method";
1302 $sql .= ", p.last_main_doc";
1303 $sql .= ", p.fk_multicurrency, p.multicurrency_code, p.multicurrency_tx, p.multicurrency_total_ht, p.multicurrency_total_tva, p.multicurrency_total_ttc";
1304 $sql .= ", c.label as statut_label";
1305 $sql .= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc, p.deposit_percent";
1306 $sql .= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement";
1307 $sql .= " FROM ".MAIN_DB_PREFIX."c_propalst as c, ".MAIN_DB_PREFIX."supplier_proposal as p";
1308 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."supplier_proposal_extrafields as pef ON pef.fk_object=p.rowid";
1309 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id';
1310 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON p.fk_cond_reglement = cr.rowid';
1311 $sql .= " WHERE p.fk_statut = c.id";
1312 $sql .= " AND p.entity IN (".getEntity('supplier_proposal').")";
1313 if ($ref) {
1314 $sql .= " AND p.ref = '".$this->db->escape($ref)."'";
1315 } else {
1316 $sql .= " AND p.rowid = ".((int) $rowid);
1317 }
1318
1319 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1320 $resql = $this->db->query($sql);
1321 if ($resql) {
1322 if ($this->db->num_rows($resql)) {
1323 $obj = $this->db->fetch_object($resql);
1324
1325 $this->id = $obj->rowid;
1326 $this->entity = $obj->entity;
1327
1328 $this->ref = $obj->ref;
1329 $this->total_ht = $obj->total_ht;
1330 $this->total_tva = $obj->total_tva;
1331 $this->total_localtax1 = $obj->localtax1;
1332 $this->total_localtax2 = $obj->localtax2;
1333 $this->total_ttc = $obj->total_ttc;
1334 $this->socid = $obj->socid;
1335 $this->fk_project = $obj->fk_project;
1336 $this->model_pdf = $obj->model_pdf;
1337 $this->note = $obj->note_private; // TODO deprecated
1338 $this->note_private = $obj->note_private;
1339 $this->note_public = $obj->note_public;
1340 $this->statut = (int) $obj->status;
1341 $this->status = (int) $obj->status;
1342 $this->datec = $this->db->jdate($obj->datec); // TODO deprecated
1343 $this->datev = $this->db->jdate($obj->datev); // TODO deprecated
1344 $this->date_creation = $this->db->jdate($obj->datec); // Creation date
1345 $this->date_modification = $this->db->jdate($obj->date_modification); // Modification date
1346 $this->date = $this->date_creation;
1347 $this->date_validation = $this->db->jdate($obj->datev); // Validation date
1348 $this->delivery_date = $this->db->jdate($obj->delivery_date);
1349 $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1350
1351 $this->last_main_doc = $obj->last_main_doc;
1352 $this->mode_reglement_id = $obj->fk_mode_reglement;
1353 $this->mode_reglement_code = $obj->mode_reglement_code;
1354 $this->mode_reglement = $obj->mode_reglement;
1355 $this->deposit_percent = $obj->deposit_percent;
1356 $this->fk_account = ($obj->fk_account > 0) ? $obj->fk_account : null;
1357 $this->cond_reglement_id = $obj->fk_cond_reglement;
1358 $this->cond_reglement_code = $obj->cond_reglement_code;
1359 $this->cond_reglement = $obj->cond_reglement;
1360 $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1361
1362 $this->extraparams = (array) (!empty($obj->extraparams) ? json_decode($obj->extraparams, true) : array());
1363
1364 $this->user_author_id = $obj->fk_user_author;
1365 $this->user_modification_id = $obj->fk_user_modif;
1366 $this->user_validation_id = $obj->fk_user_valid;
1367 $this->user_closing_id = $obj->fk_user_cloture;
1368
1369 // Multicurrency
1370 $this->fk_multicurrency = $obj->fk_multicurrency;
1371 $this->multicurrency_code = $obj->multicurrency_code;
1372 $this->multicurrency_tx = $obj->multicurrency_tx;
1373 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1374 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1375 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1376
1377 $this->db->free($resql);
1378
1379 // Retrieve all extrafield
1380 // fetch optionals attributes and labels
1381 $this->fetch_optionals();
1382
1383 $this->lines = array();
1384
1385 // Lines of supplier proposals
1386 $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,";
1387 $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,";
1388 $sql .= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label,';
1389 $sql .= ' d.ref_fourn as ref_produit_fourn, d.extraparams,';
1390 $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';
1391 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposaldet as d";
1392 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON d.fk_product = p.rowid";
1393 $sql .= " WHERE d.fk_supplier_proposal = ".((int) $this->id);
1394 $sql .= " ORDER by d.rang";
1395
1396 $result = $this->db->query($sql);
1397 if ($result) {
1398 $num = $this->db->num_rows($result);
1399 $i = 0;
1400
1401 while ($i < $num) {
1402 $objp = $this->db->fetch_object($result);
1403
1404 $line = new SupplierProposalLine($this->db);
1405
1406 $line->rowid = $objp->rowid; // deprecated
1407 $line->id = $objp->rowid;
1408 $line->fk_supplier_proposal = $objp->fk_supplier_proposal;
1409 $line->fk_parent_line = $objp->fk_parent_line;
1410 $line->product_type = $objp->product_type;
1411 $line->label = $objp->custom_label;
1412 $line->desc = $objp->description; // Description of the line
1413 $line->qty = $objp->qty;
1414 $line->tva_tx = $objp->tva_tx;
1415 $line->localtax1_tx = $objp->localtax1_tx;
1416 $line->localtax2_tx = $objp->localtax2_tx;
1417 $line->subprice = $objp->subprice;
1418 $line->fk_remise_except = $objp->fk_remise_except;
1419 $line->remise_percent = $objp->remise_percent;
1420
1421 $line->info_bits = $objp->info_bits;
1422 $line->total_ht = $objp->total_ht;
1423 $line->total_tva = $objp->total_tva;
1424 $line->total_localtax1 = $objp->total_localtax1;
1425 $line->total_localtax2 = $objp->total_localtax2;
1426 $line->total_ttc = $objp->total_ttc;
1427 $line->fk_fournprice = $objp->fk_fournprice;
1428 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1429 $line->pa_ht = $marginInfos[0];
1430 $line->marge_tx = $marginInfos[1];
1431 $line->marque_tx = $marginInfos[2];
1432 $line->special_code = $objp->special_code;
1433 $line->rang = $objp->rang;
1434
1435 $line->fk_product = $objp->fk_product;
1436
1437 $line->ref = $objp->product_ref; // deprecated
1438 $line->product_ref = $objp->product_ref;
1439 $line->libelle = $objp->product_label; // deprecated
1440 $line->product_label = $objp->product_label;
1441 $line->product_desc = $objp->product_desc; // Description produit
1442 $line->fk_product_type = $objp->fk_product_type;
1443
1444 $line->ref_fourn = $objp->ref_produit_fourn;
1445
1446 $line->extraparams = !empty($objp->extraparams) ? (array) json_decode($objp->extraparams, true) : array();
1447
1448 // Multicurrency
1449 $line->fk_multicurrency = $objp->fk_multicurrency;
1450 $line->multicurrency_code = $objp->multicurrency_code;
1451 $line->multicurrency_subprice = $objp->multicurrency_subprice;
1452 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
1453 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
1454 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
1455 $line->fk_unit = $objp->fk_unit;
1456
1457 $line->fetch_optionals();
1458
1459 $this->lines[$i] = $line;
1460
1461 $i++;
1462 }
1463 $this->db->free($result);
1464 } else {
1465 $this->error = $this->db->error();
1466 return -1;
1467 }
1468
1469 return 1;
1470 }
1471
1472 $this->error = "Record Not Found";
1473 return 0;
1474 } else {
1475 $this->error = $this->db->error();
1476 return -1;
1477 }
1478 }
1479
1487 public function valid($user, $notrigger = 0)
1488 {
1489 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1490
1491 global $conf;
1492
1493 $error = 0;
1494 $now = dol_now();
1495
1496 if ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'creer'))
1497 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'validate_advance'))) {
1498 $this->db->begin();
1499
1500 // Numbering module definition
1501 $soc = new Societe($this->db);
1502 $result = $soc->fetch($this->socid);
1503
1504 if ($result < 0) {
1505 return -1;
1506 }
1507 if (!getDolGlobalBool('SUPPLIER_PROPOSAL_NOCHECK_ONBUY_PRODUCTS_ONVALID') && !$this->checkActiveProductInLines('onbuy')) {
1508 dol_syslog(get_class($this)."::valid checkActiveProductInLines ".$this->error, LOG_INFO);
1509 return -1;
1510 }
1511 // Define new ref
1512 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1513 $num = $this->getNextNumRef($soc);
1514 } else {
1515 $num = (string) $this->ref;
1516 }
1517 $this->newref = dol_sanitizeFileName($num);
1518
1519 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1520 $sql .= " SET ref = '".$this->db->escape($num)."',";
1521 $sql .= " fk_statut = 1, date_valid='".$this->db->idate($now)."', fk_user_valid=".((int) $user->id);
1522 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1523
1524 dol_syslog(get_class($this)."::valid", LOG_DEBUG);
1525 $resql = $this->db->query($sql);
1526 if (!$resql) {
1527 dol_print_error($this->db);
1528 $error++;
1529 }
1530
1531 // Trigger calls
1532 if (!$error && !$notrigger) {
1533 // Call trigger
1534 $result = $this->call_trigger('PROPOSAL_SUPPLIER_VALIDATE', $user);
1535 if ($result < 0) {
1536 $error++;
1537 }
1538 // End call triggers
1539 }
1540
1541 if (!$error) {
1542 $this->oldref = $this->ref;
1543
1544 // Rename directory if dir was a temporary ref
1545 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1546 // Now we rename also files into index
1547 $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)."'";
1548 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
1549 $resql = $this->db->query($sql);
1550 if (!$resql) {
1551 $error++;
1552 $this->error = $this->db->lasterror();
1553 }
1554 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'supplier_proposal/".$this->db->escape($this->newref)."'";
1555 $sql .= " WHERE filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
1556 $resql = $this->db->query($sql);
1557 if (!$resql) {
1558 $error++;
1559 $this->error = $this->db->lasterror();
1560 }
1561
1562 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1563 $oldref = dol_sanitizeFileName($this->ref);
1564 $newref = dol_sanitizeFileName($num);
1565 $dirsource = $conf->supplier_proposal->dir_output.'/'.$oldref;
1566 $dirdest = $conf->supplier_proposal->dir_output.'/'.$newref;
1567 if (!$error && file_exists($dirsource)) {
1568 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
1569 if (@rename($dirsource, $dirdest)) {
1570 dol_syslog("Rename ok");
1571 // Rename docs starting with $oldref with $newref
1572 $listoffiles = dol_dir_list($conf->supplier_proposal->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1573 foreach ($listoffiles as $fileentry) {
1574 $dirsource = $fileentry['name'];
1575 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1576 $dirsource = $fileentry['path'].'/'.$dirsource;
1577 $dirdest = $fileentry['path'].'/'.$dirdest;
1578 @rename($dirsource, $dirdest);
1579 }
1580 }
1581 }
1582 }
1583
1584 $this->ref = $num;
1585 $this->statut = self::STATUS_VALIDATED;
1587 $this->user_validation_id = $user->id;
1588 $this->datev = $now;
1589 $this->date_validation = $now;
1590
1591 if (getDolGlobalString('SUPPLIER_PROPOSAL_AUTOADD_USER_CONTACT')) {
1592 $result = $this->add_contact($user->id, 'SALESREPFOLL', 'internal', 1);
1593 if ($result < 0 && $result != -2) { // -2 means already exists
1594 $error++;
1595 }
1596 }
1597
1598 $this->db->commit();
1599 return 1;
1600 } else {
1601 $this->db->rollback();
1602 return -1;
1603 }
1604 } else {
1605 dol_syslog("You don't have permission to validate supplier proposal", LOG_WARNING);
1606 return -2;
1607 }
1608 }
1609
1610 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1619 public function set_date_livraison($user, $delivery_date)
1620 {
1621 // phpcs:enable
1622 return $this->setDeliveryDate($user, $delivery_date);
1623 }
1624
1632 public function setDeliveryDate($user, $delivery_date)
1633 {
1634 if ($user->hasRight('supplier_proposal', 'creer')) {
1635 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1636 $sql .= " SET date_livraison = ".(isDolTms($delivery_date) ? "'".$this->db->idate($delivery_date)."'" : 'null');
1637 $sql .= " WHERE rowid = ".((int) $this->id);
1638
1639 if ($this->db->query($sql)) {
1640 $this->delivery_date = $delivery_date;
1641 return 1;
1642 } else {
1643 $this->error = $this->db->error();
1644 dol_syslog(get_class($this)."::setDeliveryDate Erreur SQL");
1645 return -1;
1646 }
1647 }
1648 return 0;
1649 }
1650
1651
1661 public function reopen($user, $status, $note = '', $notrigger = 0)
1662 {
1663 $error = 0;
1664
1665 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1666 $sql .= " SET fk_statut = ".((int) $status).",";
1667 if (!empty($note)) {
1668 $sql .= " note_private = '".$this->db->escape($note)."',";
1669 }
1670 $sql .= " date_cloture = NULL, fk_user_cloture = NULL";
1671 $sql .= " WHERE rowid = ".((int) $this->id);
1672
1673 $this->db->begin();
1674
1675 dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
1676 $resql = $this->db->query($sql);
1677 if (!$resql) {
1678 $error++;
1679 $this->errors[] = "Error ".$this->db->lasterror();
1680 }
1681 if (!$error) {
1682 if (!$notrigger) {
1683 // Call trigger
1684 $result = $this->call_trigger('PROPOSAL_SUPPLIER_REOPEN', $user);
1685 if ($result < 0) {
1686 $error++;
1687 }
1688 // End call triggers
1689 }
1690 }
1691
1692 // Commit or rollback
1693 if ($error) {
1694 if (!empty($this->errors)) {
1695 foreach ($this->errors as $errmsg) {
1696 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1697 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1698 }
1699 }
1700 $this->db->rollback();
1701 return -1 * $error;
1702 } else {
1703 $this->statut = $status;
1704 $this->status = $status;
1705
1706 $this->db->commit();
1707 return 1;
1708 }
1709 }
1710
1711
1720 public function cloture($user, $status, $note)
1721 {
1722 global $langs, $conf;
1723 $hidedetails = 0;
1724 $hidedesc = 0;
1725 $hideref = 0;
1726
1727 $error = 0;
1728 $now = dol_now();
1729
1730 $this->db->begin();
1731
1732 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1733 $sql .= " SET fk_statut = ".((int) $status).", note_private = '".$this->db->escape($note)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".((int) $user->id);
1734 $sql .= " WHERE rowid = ".((int) $this->id);
1735
1736 $resql = $this->db->query($sql);
1737 if ($resql) {
1738 $this->statut = $status;
1739 $this->status = $status;
1740
1741 $modelpdf = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED', (empty($this->model_pdf) ? '' : $this->model_pdf));
1742 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_REFUSED';
1743
1744 if ($status == 2) {
1745 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_SIGNED';
1746 $modelpdf = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL', (empty($this->model_pdf) ? '' : $this->model_pdf));
1747
1748 if (getDolGlobalString('SUPPLIER_PROPOSAL_UPDATE_PRICE_ON_SUPPlIER_PROPOSAL')) { // TODO This option was not tested correctly. Error if product ref does not exists
1749 $result = $this->updateOrCreatePriceFournisseur($user);
1750 }
1751 }
1752 if ($status == 4) {
1753 $triggerName = 'PROPOSAL_SUPPLIER_CLASSIFY_BILLED';
1754 }
1755
1756 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1757 // Define output language
1758 $outputlangs = $langs;
1759 if (getDolGlobalInt('MAIN_MULTILANGS')) {
1760 $outputlangs = new Translate("", $conf);
1761 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
1762 $outputlangs->setDefaultLang($newlang);
1763 }
1764
1765 $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1766 }
1767
1768 // Call trigger
1769 $result = $this->call_trigger($triggerName, $user);
1770 if ($result < 0) {
1771 $error++;
1772 }
1773 // End call triggers
1774
1775 if (!$error) {
1776 $this->db->commit();
1777 return 1;
1778 } else {
1779 $this->db->rollback();
1780 return -1;
1781 }
1782 } else {
1783 $this->error = $this->db->lasterror();
1784 $this->errors[] = $this->db->lasterror();
1785 $this->db->rollback();
1786 return -1;
1787 }
1788 }
1789
1796 public function updateOrCreatePriceFournisseur($user)
1797 {
1798 dol_syslog(get_class($this)."::updateOrCreatePriceFournisseur", LOG_DEBUG);
1799
1800 foreach ($this->lines as $product) {
1801 if ($product->subprice <= 0) {
1802 continue;
1803 }
1804 $productsupplier = new ProductFournisseur($this->db);
1805
1806 $multicurrency_tx = 1;
1807 $fk_multicurrency = 0;
1808
1809 if (empty($this->thirdparty)) {
1810 $this->fetch_thirdparty();
1811 }
1812
1813 $ref_fourn = $product->ref_fourn;
1814 if (empty($ref_fourn)) {
1815 $ref_fourn = $product->ref_supplier;
1816 }
1817 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1818 list($fk_multicurrency, $multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $product->multicurrency_code);
1819 }
1820 $productsupplier->id = $product->fk_product;
1821
1822 $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);
1823 }
1824
1825 return 1;
1826 }
1827
1836 public function updatePriceFournisseur($idProductFournPrice, $product, $user)
1837 {
1838 $price = price2num($product->subprice * $product->qty, 'MU');
1839 $unitPrice = price2num($product->subprice, 'MU');
1840
1841 $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);
1842
1843 $resql = $this->db->query($sql);
1844 if (!$resql) {
1845 $this->error = $this->db->error();
1846 $this->db->rollback();
1847 return -1;
1848 }
1849 return 1;
1850 }
1851
1859 public function createPriceFournisseur($product, $user)
1860 {
1861 $price = price2num($product->subprice * $product->qty, 'MU');
1862 $qty = price2num($product->qty);
1863 $unitPrice = price2num($product->subprice, 'MU');
1864
1865 $now = dol_now();
1866
1867 $sanitized_values = array(
1868 "'".$this->db->idate($now)."'",
1869 (int) $product->fk_product,
1870 (int) $this->thirdparty->id,
1871 "'".$this->db->escape($product->ref_fourn)."'",
1872 (float) $price,
1873 (float) $qty,
1874 (float) $unitPrice,
1875 (float) $product->tva_tx,
1876 (int) $user->id
1877 );
1878 if (isModEnabled("multicurrency")) {
1879 if (!empty($product->multicurrency_code)) {
1880 include_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
1881 $multicurrency = new MultiCurrency($this->db); //need to fetch because empty fk_multicurrency and rate
1882 $multicurrency->fetch(0, $product->multicurrency_code);
1883 if (!empty($multicurrency->id)) {
1884 $sanitized_values[] = (int) $multicurrency->id;
1885 $sanitized_values[] = "'".$this->db->escape($product->multicurrency_code)."'";
1886 $sanitized_values[] = (float) $product->multicurrency_subprice;
1887 $sanitized_values[] = (float) $product->multicurrency_total_ht;
1888 $sanitized_values[] = (float) $multicurrency->rate->rate;
1889 } else {
1890 for ($i = 0; $i < 5; $i++) {
1891 $sanitized_values[] = 'NULL';
1892 }
1893 }
1894 }
1895 }
1896
1897 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'product_fournisseur_price ';
1898 $sql .= '(datec, fk_product, fk_soc, ref_fourn, price, quantity, unitprice, tva_tx, fk_user';
1899 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1900 $sql .= ',fk_multicurrency, multicurrency_code, multicurrency_unitprice, multicurrency_price, multicurrency_tx';
1901 }
1902 $sql .= ') VALUES ('.implode(',', $sanitized_values).')';
1903
1904 $resql = $this->db->query($sql);
1905 if (!$resql) {
1906 $this->error = $this->db->error();
1907 $this->db->rollback();
1908 return -1;
1909 }
1910 return 1;
1911 }
1912
1913 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1920 public function setDraft($user)
1921 {
1922 // phpcs:enable
1923 $error = 0;
1924
1925 if ($this->status == self::STATUS_DRAFT) {
1926 dol_syslog(get_class($this)."::setDraft already draft status", LOG_WARNING);
1927 return 0;
1928 }
1929
1930 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1931 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1932 $sql .= " WHERE rowid = ".((int) $this->id);
1933
1934 if ($this->db->query($sql)) {
1935 if (!$error) {
1936 $this->oldcopy = clone $this;
1937 }
1938
1939 if (!$error) {
1940 // Call trigger
1941 $result = $this->call_trigger('PROPOSAL_SUPPLIER_UNVALIDATE', $user);
1942 if ($result < 0) {
1943 $error++;
1944 }
1945 }
1946
1947 if (!$error) {
1948 $this->status = self::STATUS_DRAFT;
1949 $this->statut = self::STATUS_DRAFT; // deprecated
1950 $this->db->commit();
1951 return 1;
1952 } else {
1953 $this->db->rollback();
1954 return -1;
1955 }
1956 } else {
1957 return -1;
1958 }
1959 }
1960
1961
1962 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1976 public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datec', $sortorder = 'DESC')
1977 {
1978 // phpcs:enable
1979 global $user;
1980
1981 $ga = array();
1982
1983 $search_sale = 0;
1984 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
1985 $search_sale = $user->id;
1986 }
1987
1988 $sql = "SELECT s.rowid, s.nom as name, s.client,";
1989 $sql .= " p.rowid as supplier_proposalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
1990 $sql .= " p.datep as dp, p.fin_validite as datelimite";
1991 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."supplier_proposal as p, ".MAIN_DB_PREFIX."c_propalst as c";
1992 $sql .= " WHERE p.entity IN (".getEntity('supplier_proposal').")";
1993 $sql .= " AND p.fk_soc = s.rowid";
1994 $sql .= " AND p.fk_statut = c.id";
1995 if ($socid) {
1996 $sql .= " AND s.rowid = ".((int) $socid);
1997 }
1998 if ($draft) {
1999 $sql .= " AND p.fk_statut = 0";
2000 }
2001 if ($notcurrentuser > 0) {
2002 $sql .= " AND p.fk_user_author <> ".((int) $user->id);
2003 }
2004 // Search on sale representative
2005 if ($search_sale && $search_sale != '-1') {
2006 if ($search_sale == -2) {
2007 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
2008 } elseif ($search_sale > 0) {
2009 $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).")";
2010 }
2011 }
2012 $sql .= $this->db->order($sortfield, $sortorder);
2013 $sql .= $this->db->plimit($limit, $offset);
2014
2015 $result = $this->db->query($sql);
2016 if ($result) {
2017 $num = $this->db->num_rows($result);
2018 if ($num) {
2019 $i = 0;
2020 while ($i < $num) {
2021 $obj = $this->db->fetch_object($result);
2022
2023 if ($shortlist == 1) {
2024 $ga[$obj->supplier_proposalid] = $obj->ref;
2025 } elseif ($shortlist == 2) {
2026 $ga[$obj->supplier_proposalid] = $obj->ref.' ('.$obj->name.')';
2027 } else {
2028 $ga[$i]['id'] = $obj->supplier_proposalid;
2029 $ga[$i]['ref'] = $obj->ref;
2030 $ga[$i]['name'] = $obj->name;
2031 }
2032
2033 $i++;
2034 }
2035 }
2036 return $ga;
2037 } else {
2038 dol_print_error($this->db);
2039 return -1;
2040 }
2041 }
2042
2050 public function delete($user, $notrigger = 0)
2051 {
2052 global $conf;
2053 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2054
2055 $error = 0;
2056
2057 $this->db->begin();
2058
2059 if (!$notrigger) {
2060 // Call trigger
2061 $result = $this->call_trigger('PROPOSAL_SUPPLIER_DELETE', $user);
2062 if ($result < 0) {
2063 $error++;
2064 }
2065 // End call triggers
2066 }
2067
2068 // Remove linked categories.
2069 if (!$error) {
2070 $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_supplier_proposal";
2071 $sql .= " WHERE fk_supplier_proposal = ".((int) $this->id);
2072
2073 $result = $this->db->query($sql);
2074 if (!$result) {
2075 $error++;
2076 $this->error = $this->db->lasterror();
2077 $this->errors[] = $this->error;
2078 }
2079 }
2080
2081 if (!$error) {
2082 $sanitized_main = MAIN_DB_PREFIX.'supplier_proposaldet';
2083 $sanitized_ef = $sanitized_main."_extrafields";
2084 $sqlef = "DELETE FROM $sanitized_ef WHERE fk_object IN (SELECT rowid FROM $sanitized_main WHERE fk_supplier_proposal = ".((int) $this->id).")";
2085 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet WHERE fk_supplier_proposal = ".((int) $this->id);
2086 if ($this->db->query($sql)) {
2087 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposal WHERE rowid = ".((int) $this->id);
2088 if ($this->db->query($sqlef) && $this->db->query($sql)) {
2089 // Delete linked object
2090 $res = $this->deleteObjectLinked();
2091 if ($res < 0) {
2092 $error++;
2093 }
2094
2095 if (!$error) {
2096 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2097 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
2098 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
2099
2100 // We remove directory
2101 $ref = dol_sanitizeFileName($this->ref);
2102 if ($conf->supplier_proposal->dir_output && !empty($this->ref)) {
2103 $dir = $conf->supplier_proposal->dir_output."/".$ref;
2104 $file = $dir."/".$ref.".pdf";
2105 if (file_exists($file)) {
2106 dol_delete_preview($this);
2107
2108 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2109 $this->error = 'ErrorFailToDeleteFile';
2110 $this->errors = array('ErrorFailToDeleteFile');
2111 $this->db->rollback();
2112 return 0;
2113 }
2114 }
2115 if (file_exists($dir)) {
2116 $res = @dol_delete_dir_recursive($dir);
2117 if (!$res) {
2118 $this->error = 'ErrorFailToDeleteDir';
2119 $this->errors = array('ErrorFailToDeleteDir');
2120 $this->db->rollback();
2121 return 0;
2122 }
2123 }
2124 }
2125 }
2126
2127 // Removed extrafields
2128 if (!$error) {
2129 $result = $this->deleteExtraFields();
2130 if ($result < 0) {
2131 $error++;
2132 $errorflag = -4;
2133 dol_syslog(get_class($this)."::delete erreur ".$errorflag." ".$this->error, LOG_ERR);
2134 }
2135 }
2136
2137 if (!$error) {
2138 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
2139 $this->db->commit();
2140 return 1;
2141 } else {
2142 $this->error = $this->db->lasterror();
2143 $this->db->rollback();
2144 return 0;
2145 }
2146 } else {
2147 $this->error = $this->db->lasterror();
2148 $this->db->rollback();
2149 return -3;
2150 }
2151 } else {
2152 $this->error = $this->db->lasterror();
2153 $this->db->rollback();
2154 return -2;
2155 }
2156 } else {
2157 $this->db->rollback();
2158 return -1;
2159 }
2160 }
2161
2168 public function info($id)
2169 {
2170 $sql = "SELECT c.rowid, GREATEST(c.tms, cef.tms) as date_modification,";
2171 $sql .= " c.datec as date_creation, c.date_valid as date_validation, c.date_cloture as date_closure,";
2172 $sql .= " c.fk_user_author, c.fk_user_modif, c.fk_user_valid, c.fk_user_cloture";
2173 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as c";
2174 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."supplier_proposal_extrafields as cef ON cef.fk_object=c.rowid";
2175 $sql .= " WHERE c.rowid = ".((int) $id);
2176
2177 $result = $this->db->query($sql);
2178
2179 if ($result) {
2180 if ($this->db->num_rows($result)) {
2181 $obj = $this->db->fetch_object($result);
2182
2183 $this->id = $obj->rowid;
2184
2185 $this->date_creation = $this->db->jdate($obj->date_creation);
2186 $this->date_modification = empty($obj->date_modification) ? '' : $this->db->jdate($obj->date_modification);
2187 $this->date_validation = $this->db->jdate($obj->date_validation);
2188 $this->date_cloture = $this->db->jdate($obj->date_closure);
2189
2190 $this->user_creation_id = $obj->fk_user_author;
2191 $this->user_modification_id = $obj->fk_user_modif;
2192 $this->user_validation_id = $obj->fk_user_valid;
2193 $this->user_closing_id = $obj->fk_user_cloture;
2194 }
2195 $this->db->free($result);
2196 } else {
2197 dol_print_error($this->db);
2198 }
2199 }
2200
2201
2208 public function getLibStatut($mode = 0)
2209 {
2210 return $this->LibStatut((isset($this->status) ? $this->status : $this->statut), $mode);
2211 }
2212
2213 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2221 public function LibStatut($status, $mode = 1)
2222 {
2223 // phpcs:enable
2224
2225 // Init/load array of translation of status
2226 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
2227 global $langs;
2228 $langs->load("supplier_proposal");
2229 $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraft");
2230 $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidated");
2231 $this->labelStatus[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSigned");
2232 $this->labelStatus[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSigned");
2233 $this->labelStatus[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosed");
2234 $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraftShort");
2235 $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidatedShort");
2236 $this->labelStatusShort[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSignedShort");
2237 $this->labelStatusShort[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSignedShort");
2238 $this->labelStatusShort[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosedShort");
2239 }
2240
2241 $statusnew = '';
2242 if ($status == self::STATUS_DRAFT) {
2243 $statusnew = 'status0';
2244 } elseif ($status == self::STATUS_VALIDATED) {
2245 $statusnew = 'status1';
2246 } elseif ($status == self::STATUS_SIGNED) {
2247 $statusnew = 'status4';
2248 } elseif ($status == self::STATUS_NOTSIGNED) {
2249 $statusnew = 'status9';
2250 } elseif ($status == self::STATUS_CLOSE) {
2251 $statusnew = 'status6';
2252 }
2253
2254 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusnew, $mode);
2255 }
2256
2257
2258 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2266 public function load_board($user, $mode)
2267 {
2268 // phpcs:enable
2269 global $conf, $langs;
2270
2271 $now = dol_now();
2272
2273 $clause = " WHERE";
2274
2275 $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.date_cloture as datefin";
2276 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2277 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2278 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
2279 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2280 $clause = " AND";
2281 }
2282 $sql .= $clause." p.entity IN (".getEntity('supplier_proposal').")";
2283 if ($mode == 'opened') {
2284 $sql .= " AND p.fk_statut = 1";
2285 }
2286 if ($mode == 'signed') {
2287 $sql .= " AND p.fk_statut = 2";
2288 }
2289 if ($user->socid) {
2290 $sql .= " AND p.fk_soc = ".((int) $user->socid);
2291 }
2292
2293 $resql = $this->db->query($sql);
2294 if ($resql) {
2295 $label = $labelShort = '';
2296 $status = '';
2297 $delay_warning = 0;
2298 if ($mode == 'opened') {
2299 $delay_warning = !empty($conf->supplier_proposal->cloture->warning_delay) ? $conf->supplier_proposal->cloture->warning_delay : 0;
2300 $status = self::STATUS_VALIDATED;
2301 $label = $langs->trans("SupplierProposalsToClose");
2302 $labelShort = $langs->trans("ToAcceptRefuse");
2303 }
2304 if ($mode == 'signed') {
2305 $delay_warning = !empty($conf->supplier_proposal->facturation->warning_delay) ? $conf->supplier_proposal->facturation->warning_delay : 0;
2306 $status = self::STATUS_SIGNED;
2307 $label = $langs->trans("SupplierProposalsToProcess"); // May be billed or ordered
2308 $labelShort = $langs->trans("ToClose");
2309 }
2310
2311 $response = new WorkboardResponse();
2312 $response->warning_delay = $delay_warning / 60 / 60 / 24;
2313 $response->label = $label;
2314 $response->labelShort = $labelShort;
2315 $response->url = DOL_URL_ROOT.'/supplier_proposal/list.php?search_status='.$status;
2316 $response->img = img_object('', "propal");
2317
2318 // This assignment in condition is not a bug. It allows walking the results.
2319 while ($obj = $this->db->fetch_object($resql)) {
2320 $response->nbtodo++;
2321 if ($mode == 'opened') {
2322 $datelimit = $this->db->jdate($obj->datefin);
2323 if ($datelimit < ($now - $delay_warning)) {
2324 $response->nbtodolate++;
2325 }
2326 }
2327 // TODO Definir regle des propales a facturer en retard
2328 // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
2329 }
2330 return $response;
2331 } else {
2332 $this->error = $this->db->lasterror();
2333 return -1;
2334 }
2335 }
2336
2337
2346 public function initAsSpecimen($param = array())
2347 {
2348 global $conf, $langs;
2349
2350 // Load array of products prodids
2351 $num_prods = 0;
2352 $prodids = array();
2353 $sql = "SELECT rowid";
2354 $sql .= " FROM ".MAIN_DB_PREFIX."product";
2355 $sql .= " WHERE entity IN (".getEntity('product').")";
2356 if (array_key_exists('tobuy', $param)) {
2357 $sql .= " AND tobuy = ".((int) $param['tobuy']);
2358 }
2359 $sql .= $this->db->plimit(100);
2360
2361 $resql = $this->db->query($sql);
2362 if ($resql) {
2363 $num_prods = $this->db->num_rows($resql);
2364 $i = 0;
2365 while ($i < $num_prods) {
2366 $i++;
2367 $row = $this->db->fetch_row($resql);
2368 $prodids[$i] = $row[0];
2369 }
2370 }
2371
2372 // Initialise parameters
2373 $this->id = 0;
2374 $this->ref = 'SPECIMEN';
2375 $this->ref_supplier = 'NEMICEPS';
2376 $this->specimen = 1;
2377 $this->socid = 1;
2378 $this->date = time();
2379 $this->cond_reglement_id = 1;
2380 $this->cond_reglement_code = 'RECEP';
2381 $this->mode_reglement_id = 7;
2382 $this->mode_reglement_code = 'CHQ';
2383 $this->note_public = 'This is a comment (public)';
2384 $this->note_private = 'This is a comment (private)';
2385
2386 $this->multicurrency_tx = 1;
2387 $this->multicurrency_code = getDolCurrency();
2388
2389 // Lines
2390 $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)
2391 $xnbp = 0;
2392 while ($xnbp < $nbp) {
2393 $line = new SupplierProposalLine($this->db);
2394 $line->desc = $langs->trans("Description")." ".$xnbp;
2395 $line->qty = 1;
2396 $line->subprice = 100;
2397 $line->tva_tx = 19.6;
2398 $line->localtax1_tx = 0;
2399 $line->localtax2_tx = 0;
2400 if ($xnbp == 2) {
2401 $line->total_ht = 50;
2402 $line->total_ttc = 59.8;
2403 $line->total_tva = 9.8;
2404 $line->remise_percent = 50;
2405 } else {
2406 $line->total_ht = 100;
2407 $line->total_ttc = 119.6;
2408 $line->total_tva = 19.6;
2409 $line->remise_percent = 00;
2410 }
2411
2412 if ($num_prods > 0) {
2413 $prodid = mt_rand(1, $num_prods);
2414 $line->fk_product = $prodids[$prodid];
2415 }
2416
2417 $this->lines[$xnbp] = $line;
2418
2419 $this->total_ht += $line->total_ht;
2420 $this->total_tva += $line->total_tva;
2421 $this->total_ttc += $line->total_ttc;
2422
2423 $xnbp++;
2424 }
2425
2426 return 1;
2427 }
2428
2434 public function loadStateBoard()
2435 {
2436 global $user;
2437
2438 $this->nb = array();
2439 $clause = "WHERE";
2440
2441 $sql = "SELECT count(p.rowid) as nb";
2442 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2443 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
2444 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
2445 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2446 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2447 $clause = "AND";
2448 }
2449 $sql .= " ".$clause." p.entity IN (".getEntity('supplier_proposal').")";
2450
2451 $resql = $this->db->query($sql);
2452 if ($resql) {
2453 // This assignment in condition is not a bug. It allows walking the results.
2454 while ($obj = $this->db->fetch_object($resql)) {
2455 $this->nb["supplier_proposals"] = $obj->nb;
2456 }
2457 $this->db->free($resql);
2458 return 1;
2459 } else {
2460 dol_print_error($this->db);
2461 $this->error = $this->db->lasterror();
2462 return -1;
2463 }
2464 }
2465
2466
2474 public function getNextNumRef($soc)
2475 {
2476 global $conf, $db, $langs;
2477 $langs->load("supplier_proposal");
2478
2479 if (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON')) {
2480 $mybool = false;
2481
2482 $file = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') . ".php";
2483 $classname = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
2484
2485 // Include file with class
2486 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2487 foreach ($dirmodels as $reldir) {
2488 $dir = dol_buildpath($reldir."core/modules/supplier_proposal/");
2489
2490 // Load file with numbering class (if found)
2491 $mybool = ((bool) @include_once $dir.$file) || $mybool;
2492 }
2493
2494 if (!$mybool) {
2495 dol_print_error(null, "Failed to include file ".$file);
2496 return '';
2497 }
2498
2499 $obj = new $classname();
2500 '@phan-var-force ModeleNumRefSupplierProposal $obj';
2502 $numref = "";
2503 $numref = $obj->getNextValue($soc, $this);
2504
2505 if ($numref != "") {
2506 return $numref;
2507 } else {
2508 $this->error = $obj->error;
2509 return "";
2510 }
2511 } else {
2512 $langs->load("errors");
2513 print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("SupplierProposal"));
2514 return "";
2515 }
2516 }
2517
2524 public function getTooltipContentArray($params)
2525 {
2526 global $conf, $langs;
2527
2528 $langs->load('supplier_proposal');
2529
2530 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2531 return ['optimize' => $langs->trans("ShowSupplierProposal")];
2532 }
2533
2534 //$option = $params['option'] ?? '';
2535 $datas = [];
2536
2537 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("SupplierProposal").'</u>';
2538 if (isset($this->status)) {
2539 $datas['picto'] .= ' '.$this->getLibStatut(5);
2540 }
2541 if (!empty($this->ref)) {
2542 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2543 }
2544 if (!empty($this->ref_fourn)) {
2545 $datas['ref_supplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_fourn;
2546 }
2547 if (!empty($this->total_ht)) {
2548 $datas['amount_ht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, getDolCurrency());
2549 }
2550 if (!empty($this->total_tva)) {
2551 $datas['amount_vat'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, getDolCurrency());
2552 }
2553 if (!empty($this->total_ttc)) {
2554 $datas['amount_ttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, getDolCurrency());
2555 }
2556
2557 return $datas;
2558 }
2559
2571 public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2572 {
2573 global $langs, $conf, $user, $hookmanager;
2574
2575 if (!empty($conf->dol_no_mouse_hover)) {
2576 $notooltip = 1; // Force disable tooltips
2577 }
2578
2579 $url = '';
2580 $result = '';
2581 $params = [
2582 'id' => $this->id,
2583 'objecttype' => $this->element,
2584 'option' => $option,
2585 ];
2586 $classfortooltip = 'classfortooltip';
2587 $dataparams = '';
2588 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2589 $classfortooltip = 'classforajaxtooltip';
2590 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
2591 $label = '';
2592 } else {
2593 $label = implode($this->getTooltipContentArray($params));
2594 }
2595
2596 if ($option == '') {
2597 $url = DOL_URL_ROOT.'/supplier_proposal/card.php?id='.$this->id.$get_params;
2598 }
2599 if ($option == 'document') {
2600 $url = DOL_URL_ROOT.'/supplier_proposal/document.php?id='.$this->id.$get_params;
2601 }
2602
2603 if ($option !== 'nolink') {
2604 // Add param to save lastsearch_values or not
2605 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2606 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2607 $add_save_lastsearch_values = 1;
2608 }
2609 if ($add_save_lastsearch_values) {
2610 $url .= '&save_lastsearch_values=1';
2611 }
2612 }
2613
2614 $linkclose = '';
2615 if (empty($notooltip) && $user->hasRight('propal', 'lire')) {
2616 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2617 $label = $langs->trans("ShowSupplierProposal");
2618 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
2619 }
2620 $linkclose .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
2621 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
2622 }
2623
2624 $linkstart = '<a href="'.$url.'"';
2625 $linkstart .= $linkclose.'>';
2626 $linkend = '</a>';
2627
2628 $result .= $linkstart;
2629 if ($withpicto) {
2630 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
2631 }
2632 if ($withpicto != 2) {
2633 $result .= $this->ref;
2634 }
2635 $result .= $linkend;
2636
2637 if ($addlinktonotes) {
2638 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2639 if ($txttoshow) {
2640 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2641 $result .= ' <span class="note inline-block">';
2642 $result .= '<a href="'.DOL_URL_ROOT.'/supplier_proposal/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2643 $result .= img_picto('', 'note');
2644 $result .= '</a>';
2645 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
2646 //$result.='</a>';
2647 $result .= '</span>';
2648 }
2649 }
2650 global $action;
2651 $hookmanager->initHooks(array($this->element . 'dao'));
2652 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
2653 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2654 if ($reshook > 0) {
2655 $result = $hookmanager->resPrint;
2656 } else {
2657 $result .= $hookmanager->resPrint;
2658 }
2659 return $result;
2660 }
2661
2667 public function getLinesArray()
2668 {
2669 // For other object, here we call fetch_lines. But fetch_lines does not exists on supplier proposal
2670
2671 $sql = 'SELECT pt.rowid, pt.label as custom_label, pt.description, pt.fk_product, pt.fk_remise_except,';
2672 $sql .= ' pt.qty, pt.tva_tx, pt.vat_src_code, pt.remise_percent, pt.subprice, pt.info_bits,';
2673 $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,';
2674 $sql .= ' pt.product_type, pt.rang, pt.fk_parent_line, pt.extraparams,';
2675 $sql .= ' p.label as product_label, p.ref, p.fk_product_type, p.rowid as prodid,';
2676 $sql .= ' p.description as product_desc, pt.ref_fourn as ref_supplier,';
2677 $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';
2678 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pt';
2679 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pt.fk_product=p.rowid';
2680 $sql .= ' WHERE pt.fk_supplier_proposal = '.((int) $this->id);
2681 $sql .= ' ORDER BY pt.rang ASC, pt.rowid';
2682
2683 dol_syslog(get_class($this).'::getLinesArray', LOG_DEBUG);
2684 $resql = $this->db->query($sql);
2685 if ($resql) {
2686 $num = $this->db->num_rows($resql);
2687 $i = 0;
2688
2689 while ($i < $num) {
2690 $obj = $this->db->fetch_object($resql);
2691
2692 $this->lines[$i] = new SupplierProposalLine($this->db);
2693 $this->lines[$i]->id = $obj->rowid; // for backward compatibility
2694 $this->lines[$i]->rowid = $obj->rowid;
2695
2696 $this->lines[$i]->label = $obj->custom_label;
2697 $this->lines[$i]->description = $obj->description; // deprecated
2698 $this->lines[$i]->desc = $obj->description;
2699
2700 $this->lines[$i]->fk_product = $obj->fk_product;
2701 $this->lines[$i]->ref = $obj->ref;
2702 $this->lines[$i]->product_label = $obj->product_label;
2703 $this->lines[$i]->product_desc = $obj->product_desc;
2704 $this->lines[$i]->fk_product_type = $obj->fk_product_type; // deprecated
2705 $this->lines[$i]->product_type = $obj->product_type;
2706 $this->lines[$i]->qty = $obj->qty;
2707 $this->lines[$i]->subprice = $obj->subprice;
2708 $this->lines[$i]->fk_remise_except = $obj->fk_remise_except;
2709 $this->lines[$i]->remise_percent = $obj->remise_percent;
2710 $this->lines[$i]->tva_tx = $obj->tva_tx;
2711 $this->lines[$i]->vat_src_code = $obj->vat_src_code;
2712 $this->lines[$i]->info_bits = $obj->info_bits;
2713 $this->lines[$i]->total_ht = $obj->total_ht;
2714 $this->lines[$i]->total_tva = $obj->total_tva;
2715 $this->lines[$i]->total_ttc = $obj->total_ttc;
2716 $this->lines[$i]->fk_fournprice = $obj->fk_fournprice;
2717 $marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->lines[$i]->fk_fournprice, $obj->pa_ht);
2718 $this->lines[$i]->pa_ht = $marginInfos[0];
2719 $this->lines[$i]->marge_tx = $marginInfos[1];
2720 $this->lines[$i]->marque_tx = $marginInfos[2];
2721 $this->lines[$i]->fk_parent_line = $obj->fk_parent_line;
2722 $this->lines[$i]->special_code = $obj->special_code;
2723 $this->lines[$i]->rang = $obj->rang;
2724
2725 $this->lines[$i]->ref_fourn = $obj->ref_supplier; // deprecated
2726 $this->lines[$i]->ref_supplier = $obj->ref_supplier;
2727
2728 // Multicurrency
2729 $this->lines[$i]->fk_multicurrency = $obj->fk_multicurrency;
2730 $this->lines[$i]->multicurrency_code = $obj->multicurrency_code;
2731 $this->lines[$i]->multicurrency_subprice = $obj->multicurrency_subprice;
2732 $this->lines[$i]->multicurrency_total_ht = $obj->multicurrency_total_ht;
2733 $this->lines[$i]->multicurrency_total_tva = $obj->multicurrency_total_tva;
2734 $this->lines[$i]->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
2735 $this->lines[$i]->fk_unit = $obj->fk_unit;
2736 $this->lines[$i]->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
2737
2738 $this->lines[$i]->fetch_optionals();
2739
2740 $i++;
2741 }
2742 $this->db->free($resql);
2743
2744 return 1;
2745 } else {
2746 $this->error = $this->db->error();
2747 return -1;
2748 }
2749 }
2750
2762 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2763 {
2764 global $langs;
2765
2766 $langs->load("supplier_proposal");
2767 $outputlangs->load("products");
2768
2769 if (!dol_strlen($modele)) {
2770 $modele = ''; // On supplier documents, template can be empty( no doc generated in this case)
2771
2772 if ($this->model_pdf) {
2773 $modele = $this->model_pdf;
2774 } elseif (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF')) {
2775 $modele = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF');
2776 }
2777 }
2778
2779 if (empty($modele)) {
2780 return 1;
2781 }
2782
2783 $modelpath = "core/modules/supplier_proposal/doc/";
2784
2785 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2786 }
2787
2788
2797 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2798 {
2799 $tables = array(
2800 'supplier_proposal'
2801 );
2802
2803 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2804 }
2805
2814 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
2815 {
2816 $tables = array(
2817 'supplier_proposaldet'
2818 );
2819
2820 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
2821 }
2822
2823
2831 public function getKanbanView($option = '', $arraydata = null)
2832 {
2833 global $langs;
2834
2835 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2836
2837 $return = '<div class="box-flex-item box-flex-grow-zero">';
2838 $return .= '<div class="info-box info-box-sm">';
2839 $return .= '<span class="info-box-icon bg-infobox-action">';
2840 $return .= img_picto('', $this->picto);
2841 //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2842 $return .= '</span>';
2843 $return .= '<div class="info-box-content">';
2844 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
2845 if ($selected >= 0) {
2846 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2847 }
2848 if (property_exists($this, 'socid')) {
2849 $return .= '<span class="info-box-ref"> | '.$this->socid.'</span>';
2850 }
2851 if (property_exists($this, 'delivery_date')) {
2852 $return .= '<br><span class="opacitymedium">'.$langs->trans("DateEnd").'</span> : <span class="info-box-label">'.dol_print_date($this->delivery_date).'</span>';
2853 }
2854 if (property_exists($this, 'total_ttc')) {
2855 $return .= '<br><span class="opacitymedium" >'.$langs->trans("AmountHT").' : </span><span class="info-box-label amount">'.price($this->total_ttc).'</span>';
2856 }
2857 if (method_exists($this, 'getLibStatut')) {
2858 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
2859 }
2860 $return .= '</div>';
2861 $return .= '</div>';
2862 $return .= '</div>';
2863 return $return;
2864 }
2865
2876 public function setCategories($categories)
2877 {
2878 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2879 return parent::setCategoriesCommon($categories, Categorie::TYPE_SUPPLIER_PROPOSAL);
2880 }
2881}
2882
2883
2888{
2892 public $db;
2893
2897 public $error = '';
2898
2902 public $element = 'supplier_proposaldet';
2903
2907 public $table_element = 'supplier_proposaldet';
2908
2912 public $parent_element = 'supplier_proposal';
2913
2917 public $fk_parent_attribute = 'fk_supplier_proposal';
2918
2922 public $oldline;
2923
2927 public $id;
2928
2932 public $fk_supplier_proposal;
2933
2937 public $fk_parent_line;
2938
2942 public $desc; // Description of the line
2943
2947 public $fk_product; // Predefined Product ID
2948
2954 public $fk_product_type;
2960 public $product_type = Product::TYPE_PRODUCT;
2961
2965 public $qty;
2969 public $tva_tx;
2973 public $vat_src_code;
2974
2978 public $remise_percent;
2979
2983 public $fk_remise_except;
2984
2988 public $rang = 0;
2989
2993 public $fk_fournprice;
2994
2998 public $pa_ht;
3002 public $marge_tx;
3006 public $marque_tx;
3007
3011 public $special_code; // Tag for special lines (exclusive tags)
3012 // 1: frais de port
3013 // 2: ecotaxe
3014 // 3: option line (when qty = 0)
3015
3019 public $info_bits = 0; // Liste d'options cumulables:
3020 // Bit 0: 0 if TVA normal - 1 if TVA NPR
3021 // Bit 1: 0 normal line - 1 if fixed reduction
3022
3026 public $total_ht; // Line Net Total (HT), including all quantity and the line discount
3030 public $total_tva; // Line Net Total (VAT), including all quantity and the line discount
3034 public $total_ttc; // Line Net Total (TTC), including all quantity and the line discount
3035
3039 public $date_start;
3043 public $date_end;
3044
3045 // From llx_product
3051 public $ref;
3052
3057 public $product_ref;
3058
3064 public $libelle;
3065
3070 public $product_label;
3071
3076 public $label;
3077
3082 public $product_desc;
3083
3087 public $localtax1_tx; // Local tax 1
3091 public $localtax2_tx; // Local tax 2
3095 public $localtax1_type; // Local tax 1 type
3099 public $localtax2_type; // Local tax 2 type
3103 public $total_localtax1; // Line total local tax 1
3107 public $total_localtax2; // Line total local tax 2
3108
3112 public $skip_update_total; // Skip update price total for special lines
3113
3117 public $ref_fourn;
3121 public $ref_supplier;
3122
3123 // Multicurrency
3127 public $fk_multicurrency;
3128
3132 public $multicurrency_code;
3136 public $multicurrency_subprice;
3140 public $multicurrency_total_ht;
3144 public $multicurrency_total_tva;
3148 public $multicurrency_total_ttc;
3149
3155 public function __construct($db)
3156 {
3157 $this->db = $db;
3158 }
3159
3166 public function fetch($rowid)
3167 {
3168 $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,';
3169 $sql .= ' pd.date_start, pd.date_end,';
3170 $sql .= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice, pd.subprice_ttc,';
3171 $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,';
3172 $sql .= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
3173 $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
3174 $sql .= ' pd.product_type, pd.ref_fourn as ref_produit_fourn, pd.extraparams,';
3175 $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';
3176 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pd';
3177 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
3178 $sql .= ' WHERE pd.rowid = '.((int) $rowid);
3179
3180 $result = $this->db->query($sql);
3181 if ($result) {
3182 if ($objp = $this->db->fetch_object($result)) {
3183 $this->id = $objp->rowid;
3184 $this->fk_supplier_proposal = $objp->fk_supplier_proposal;
3185 $this->fk_parent_line = $objp->fk_parent_line;
3186 $this->label = $objp->custom_label;
3187 $this->desc = $objp->description;
3188 $this->qty = $objp->qty;
3189
3190 $this->subprice = $objp->subprice;
3191 $this->subprice_ttc = $objp->subprice_ttc;
3192
3193 $this->tva_tx = $objp->tva_tx;
3194 $this->remise_percent = $objp->remise_percent;
3195 $this->fk_remise_except = $objp->fk_remise_except;
3196 $this->fk_product = $objp->fk_product;
3197 $this->info_bits = $objp->info_bits;
3198 $this->date_start = $this->db->jdate($objp->date_start);
3199 $this->date_end = $this->db->jdate($objp->date_end);
3200
3201 $this->total_ht = $objp->total_ht;
3202 $this->total_tva = $objp->total_tva;
3203 $this->total_ttc = $objp->total_ttc;
3204
3205 $this->fk_fournprice = $objp->fk_fournprice;
3206
3207 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
3208 $this->pa_ht = $marginInfos[0];
3209 $this->marge_tx = $marginInfos[1];
3210 $this->marque_tx = $marginInfos[2];
3211
3212 $this->special_code = $objp->special_code;
3213 $this->product_type = $objp->product_type;
3214 $this->rang = $objp->rang;
3215
3216 $this->product_ref = $objp->product_ref;
3217 $this->product_label = $objp->product_label;
3218 $this->product_desc = $objp->product_desc;
3219
3220 $this->ref_fourn = $objp->ref_produit_fourn;
3221
3222 $this->extraparams = !empty($objp->extraparams) ? (array) json_decode($objp->extraparams, true) : array();
3223
3224 // Multicurrency
3225 $this->fk_multicurrency = $objp->fk_multicurrency;
3226 $this->multicurrency_code = $objp->multicurrency_code;
3227 $this->multicurrency_subprice = $objp->multicurrency_subprice;
3228 $this->multicurrency_subprice_ttc = $objp->multicurrency_subprice_ttc;
3229 $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3230 $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3231 $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3232 $this->fk_unit = $objp->fk_unit;
3233
3234 $this->db->free($result);
3235 return 1;
3236 }
3237 return 0;
3238 } else {
3239 dol_print_error($this->db);
3240 return -1;
3241 }
3242 }
3243
3250 public function insert($notrigger = 0)
3251 {
3252 global $user;
3253
3254 $error = 0;
3255
3256 dol_syslog(get_class($this)."::insert rang=".$this->rang);
3257
3258 // Clean parameters
3259 if (empty($this->tva_tx)) {
3260 $this->tva_tx = 0;
3261 }
3262 if (empty($this->vat_src_code)) {
3263 $this->vat_src_code = '';
3264 }
3265 if (empty($this->localtax1_tx)) {
3266 $this->localtax1_tx = 0;
3267 }
3268 if (empty($this->localtax2_tx)) {
3269 $this->localtax2_tx = 0;
3270 }
3271 if (empty($this->localtax1_type)) {
3272 $this->localtax1_type = '';
3273 }
3274 if (empty($this->localtax2_type)) {
3275 $this->localtax2_type = '';
3276 }
3277 if (empty($this->total_localtax1)) {
3278 $this->total_localtax1 = 0;
3279 }
3280 if (empty($this->total_localtax2)) {
3281 $this->total_localtax2 = 0;
3282 }
3283 if (empty($this->rang)) {
3284 $this->rang = 0;
3285 }
3286 if (empty($this->remise_percent)) {
3287 $this->remise_percent = 0;
3288 }
3289 if (empty($this->info_bits)) {
3290 $this->info_bits = 0;
3291 }
3292 if (empty($this->special_code)) {
3293 $this->special_code = 0;
3294 }
3295 if (empty($this->fk_parent_line)) {
3296 $this->fk_parent_line = 0;
3297 }
3298 if (empty($this->fk_fournprice)) {
3299 $this->fk_fournprice = 0;
3300 }
3301 if (empty($this->fk_unit)) {
3302 $this->fk_unit = 0;
3303 }
3304 if (empty($this->subprice)) {
3305 $this->subprice = 0;
3306 }
3307
3308 if (empty($this->pa_ht)) {
3309 $this->pa_ht = 0;
3310 }
3311
3312 // if buy price not defined, define buyprice as configured in margin admin
3313 if ($this->pa_ht == 0) {
3314 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3315 if ($result < 0) {
3316 return $result;
3317 } else {
3318 $this->pa_ht = $result;
3319 }
3320 }
3321
3322 // Check parameters
3323 if ($this->product_type < 0) {
3324 return -1;
3325 }
3326
3327 $this->db->begin();
3328
3329 // Insert line into database
3330 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'supplier_proposaldet';
3331 $sql .= ' (fk_supplier_proposal, fk_parent_line, label, description, fk_product, product_type,';
3332 $sql .= ' date_start, date_end,';
3333 $sql .= ' fk_remise_except, qty, tva_tx, vat_src_code, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3334 $sql .= ' subprice, subprice_ttc, remise_percent, ';
3335 $sql .= ' info_bits, ';
3336 $sql .= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
3337 $sql .= ' ref_fourn,';
3338 $sql .= ' fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_subprice_ttc, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc, fk_unit)';
3339 $sql .= " VALUES (".((int) $this->fk_supplier_proposal).",";
3340 $sql .= " ".($this->fk_parent_line > 0 ? ((int) $this->fk_parent_line) : "null").",";
3341 $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
3342 $sql .= " '".$this->db->escape($this->desc)."',";
3343 $sql .= " ".($this->fk_product ? ((int) $this->fk_product) : "null").",";
3344 $sql .= " ".((int) $this->product_type).",";
3345 $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3346 $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3347 $sql .= " ".($this->fk_remise_except ? ((int) $this->fk_remise_except) : "null").",";
3348 $sql .= " ".(float) price2num($this->qty, 'MS').",";
3349 $sql .= " ".(float) price2num($this->tva_tx).",";
3350 $sql .= " '".$this->db->escape($this->vat_src_code)."',";
3351 $sql .= " ".(float) price2num($this->localtax1_tx).",";
3352 $sql .= " ".(float) price2num($this->localtax2_tx).",";
3353 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3354 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3355 $sql .= " ".(float) price2num($this->subprice, 'MU') .",";
3356 $sql .= " ".(float) price2num($this->subprice_ttc, 'MU') .",";
3357 $sql .= " ".((float) $this->remise_percent).",";
3358 $sql .= " ".(isset($this->info_bits) ? ((int) $this->info_bits) : "null").",";
3359 $sql .= " ".(float) price2num($this->total_ht, 'MT').",";
3360 $sql .= " ".(float) price2num($this->total_tva, 'MT').",";
3361 $sql .= " ".(float) price2num($this->total_localtax1, 'MT').",";
3362 $sql .= " ".(float) price2num($this->total_localtax2, 'MT').",";
3363 $sql .= " ".(float) price2num($this->total_ttc, 'MT').",";
3364 $sql .= " ".(!empty($this->fk_fournprice) ? ((int) $this->fk_fournprice) : "null").",";
3365 $sql .= " ".(isset($this->pa_ht) ? (float) price2num($this->pa_ht, 'MU') : "null").",";
3366 $sql .= ' '.((int) $this->special_code).',';
3367 $sql .= ' '.((int) $this->rang).',';
3368 $sql .= " '".$this->db->escape($this->ref_fourn)."'";
3369 $sql .= ", ".($this->fk_multicurrency > 0 ? ((int) $this->fk_multicurrency) : 'null');
3370 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3371 $sql .= ", ".(float) price2num($this->multicurrency_subprice, 'CU');
3372 $sql .= ", ".(float) price2num($this->multicurrency_subprice_ttc, 'CU');
3373 $sql .= ", ".(float) price2num($this->multicurrency_total_ht, 'CT');
3374 $sql .= ", ".(float) price2num($this->multicurrency_total_tva, 'CT');
3375 $sql .= ", ".(float) price2num($this->multicurrency_total_ttc, 'CT');
3376 $sql .= ", ".($this->fk_unit ? ((int) $this->fk_unit) : 'null');
3377 $sql .= ')';
3378
3379 dol_syslog(get_class($this).'::insert', LOG_DEBUG);
3380 $resql = $this->db->query($sql);
3381 if ($resql) {
3382 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'supplier_proposaldet');
3383
3384 if (!$error) {
3385 $result = $this->insertExtraFields();
3386 if ($result < 0) {
3387 $error++;
3388 }
3389 }
3390
3391 if (!$error && !$notrigger) {
3392 // Call trigger
3393 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_INSERT', $user);
3394 if ($result < 0) {
3395 $this->db->rollback();
3396 return -1;
3397 }
3398 // End call triggers
3399 }
3400
3401 $this->db->commit();
3402 return 1;
3403 } else {
3404 $this->error = $this->db->error()." sql=".$sql;
3405 $this->db->rollback();
3406 return -1;
3407 }
3408 }
3409
3416 public function delete($user)
3417 {
3418 $error = 0;
3419
3420 $this->db->begin();
3421
3422 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet";
3423 $sql .= " WHERE rowid = ".((int) $this->id);
3424
3425 if ($this->db->query($sql)) {
3426 // Remove extrafields
3427 if (!$error) {
3428 $result = $this->deleteExtraFields();
3429 if ($result < 0) {
3430 $error++;
3431 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3432 }
3433 }
3434
3435 // Call trigger
3436 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_DELETE', $user);
3437 if ($result < 0) {
3438 $this->db->rollback();
3439 return -1;
3440 }
3441 // End call triggers
3442
3443 $this->db->commit();
3444
3445 return 1;
3446 } else {
3447 $this->error = $this->db->error()." sql=".$sql;
3448 $this->db->rollback();
3449 return -1;
3450 }
3451 }
3452
3459 public function update($notrigger = 0)
3460 {
3461 global $user;
3462
3463 $error = 0;
3464
3465 // Clean parameters
3466 if (empty($this->tva_tx)) {
3467 $this->tva_tx = 0;
3468 }
3469 if (empty($this->localtax1_tx)) {
3470 $this->localtax1_tx = 0;
3471 }
3472 if (empty($this->localtax2_tx)) {
3473 $this->localtax2_tx = 0;
3474 }
3475 if (empty($this->total_localtax1)) {
3476 $this->total_localtax1 = 0;
3477 }
3478 if (empty($this->total_localtax2)) {
3479 $this->total_localtax2 = 0;
3480 }
3481 if (empty($this->localtax1_type)) {
3482 $this->localtax1_type = '';
3483 }
3484 if (empty($this->localtax2_type)) {
3485 $this->localtax2_type = '';
3486 }
3487 if (empty($this->marque_tx)) {
3488 $this->marque_tx = 0;
3489 }
3490 if (empty($this->marge_tx)) {
3491 $this->marge_tx = 0;
3492 }
3493 if (empty($this->remise_percent)) {
3494 $this->remise_percent = 0;
3495 }
3496 if (empty($this->info_bits)) {
3497 $this->info_bits = 0;
3498 }
3499 if (empty($this->special_code)) {
3500 $this->special_code = 0;
3501 }
3502 if (empty($this->fk_parent_line)) {
3503 $this->fk_parent_line = 0;
3504 }
3505 if (empty($this->fk_fournprice)) {
3506 $this->fk_fournprice = 0;
3507 }
3508 if (empty($this->fk_unit)) {
3509 $this->fk_unit = 0;
3510 }
3511 if (empty($this->subprice)) {
3512 $this->subprice = 0;
3513 }
3514
3515 if (empty($this->pa_ht)) {
3516 $this->pa_ht = 0;
3517 }
3518
3519 // if buy price not defined, define buyprice as configured in margin admin
3520 if ($this->pa_ht == 0) {
3521 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3522 if ($result < 0) {
3523 return $result;
3524 } else {
3525 $this->pa_ht = $result;
3526 }
3527 }
3528
3529 $this->db->begin();
3530
3531 // Update line in database
3532 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3533 $sql .= " description = '".$this->db->escape($this->desc)."'";
3534 $sql .= " , label = ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
3535 $sql .= " , product_type = ".((int) $this->product_type);
3536 $sql .= " , date_start = ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null");
3537 $sql .= " , date_end = ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null");
3538 $sql .= " , tva_tx = '".price2num($this->tva_tx)."'";
3539 $sql .= " , localtax1_tx = ".(float) price2num($this->localtax1_tx);
3540 $sql .= " , localtax2_tx = ".(float) price2num($this->localtax2_tx);
3541 $sql .= " , localtax1_type = '".$this->db->escape($this->localtax1_type)."'";
3542 $sql .= " , localtax2_type = '".$this->db->escape($this->localtax2_type)."'";
3543 $sql .= " , qty = ".(float) price2num($this->qty);
3544 $sql .= " , subprice = ".(float) price2num($this->subprice);
3545 $sql .= " , subprice_ttc = ".(float) price2num($this->subprice_ttc);
3546 $sql .= " , remise_percent = ".(float) price2num($this->remise_percent);
3547 $sql .= " , info_bits = ".((int) $this->info_bits);
3548 if (empty($this->skip_update_total)) {
3549 $sql .= " , total_ht = ".(float) price2num($this->total_ht);
3550 $sql .= " , total_tva = ".(float) price2num($this->total_tva);
3551 $sql .= " , total_ttc = ".(float) price2num($this->total_ttc);
3552 $sql .= " , total_localtax1 = ".(float) price2num($this->total_localtax1);
3553 $sql .= " , total_localtax2 = ".(float) price2num($this->total_localtax2);
3554 }
3555 $sql .= " , fk_product_fournisseur_price = ".(!empty($this->fk_fournprice) ? "'".$this->db->escape((string) $this->fk_fournprice)."'" : "null");
3556 $sql .= " , buy_price_ht = ".(float) price2num($this->pa_ht);
3557 $sql .= " , special_code = ".((int) $this->special_code);
3558 $sql .= " , fk_parent_line = ".($this->fk_parent_line > 0 ? ((int) $this->fk_parent_line) : "null");
3559 if (!empty($this->rang)) {
3560 $sql .= ", rang = ".((int) $this->rang);
3561 }
3562 $sql .= " , ref_fourn = ".(!empty($this->ref_fourn) ? "'".$this->db->escape($this->ref_fourn)."'" : "null");
3563 $sql .= " , fk_unit = ".($this->fk_unit ? (int) $this->fk_unit : 'null');
3564
3565 // Multicurrency
3566 $sql .= " , multicurrency_subprice = ".(float) price2num($this->multicurrency_subprice);
3567 $sql .= " , multicurrency_subprice_ttc = ".(float) price2num($this->multicurrency_subprice_ttc);
3568 $sql .= " , multicurrency_total_ht = ".(float) price2num($this->multicurrency_total_ht);
3569 $sql .= " , multicurrency_total_tva = ".(float) price2num($this->multicurrency_total_tva);
3570 $sql .= " , multicurrency_total_ttc = ".(float) price2num($this->multicurrency_total_ttc);
3571
3572 $sql .= " WHERE rowid = ".((int) $this->id);
3573
3574 dol_syslog(get_class($this)."::update", LOG_DEBUG);
3575 $resql = $this->db->query($sql);
3576 if ($resql) {
3577 if (!$error) {
3578 $result = $this->insertExtraFields();
3579 if ($result < 0) {
3580 $error++;
3581 }
3582 }
3583
3584 if (!$error && !$notrigger) {
3585 // Call trigger
3586 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_MODIFY', $user);
3587 if ($result < 0) {
3588 $this->db->rollback();
3589 return -1;
3590 }
3591 // End call triggers
3592 }
3593
3594 $this->db->commit();
3595 return 1;
3596 } else {
3597 $this->error = $this->db->error();
3598 $this->db->rollback();
3599 return -2;
3600 }
3601 }
3602
3603 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3610 public function update_total()
3611 {
3612 // phpcs:enable
3613 $this->db->begin();
3614
3615 // Update line in database
3616 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3617 $sql .= " total_ht = ".(float) price2num($this->total_ht, 'MT');
3618 $sql .= ",total_tva = ".(float) price2num($this->total_tva, 'MT');
3619 $sql .= ",total_ttc = ".(float) price2num($this->total_ttc, 'MT');
3620 $sql .= " WHERE rowid = ".((int) $this->id);
3621
3622 dol_syslog("SupplierProposalLine::update_total", LOG_DEBUG);
3623
3624 $resql = $this->db->query($sql);
3625 if ($resql) {
3626 $this->db->commit();
3627 return 1;
3628 } else {
3629 $this->error = $this->db->error();
3630 $this->db->rollback();
3631 return -2;
3632 }
3633 }
3634}
$object ref
Definition info.php:90
Parent class of all other business classes (invoices, contracts, proposals, orders,...
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
Save a new position (field rang) for details lines.
deleteEcmFiles($mode=0)
Delete related files of object in database.
update_price($exclspec=0, $roundingadjust='auto', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
add_object_linked($origin=null, $origin_id=null, $f_user=null, $notrigger=0)
Add an object link into llx_element_element.
defineBuyPrice($unitPrice=0.0, $discountPercent=0.0, $fk_product=0)
Get buy price to use for margin calculation.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty.
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid=0, $f_user=null, $notrigger=0)
Delete all links between an object $this.
setErrorsFromObject($object)
setErrorsFromObject
static isExistingObject($element, $id, $ref='', $ref_ext='')
Check if an object id or ref exists If you don't need or want to instantiate the object and just need...
updateRangOfLine($rowid, $rang)
Update position of line (rang)
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.
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:487