dolibarr 20.0.0
supplier_proposal.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2002-2004 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2004 Eric Seigne <eric.seigne@ryxeo.com>
4 * Copyright (C) 2004-2011 Laurent Destailleur <eldy@users.sourceforge.net>
5 * Copyright (C) 2005 Marc Barilley <marc@ocebo.com>
6 * Copyright (C) 2005-2013 Regis Houssin <regis.houssin@inodbox.com>
7 * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
8 * Copyright (C) 2008 Raphael Bertrand <raphael.bertrand@resultic.fr>
9 * Copyright (C) 2010-2020 Juanjo Menent <jmenent@2byte.es>
10 * Copyright (C) 2010-2018 Philippe Grand <philippe.grand@atoo-net.com>
11 * Copyright (C) 2012-2014 Christophe Battarel <christophe.battarel@altairis.fr>
12 * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
13 * Copyright (C) 2014 Marcos García <marcosgdf@gmail.com>
14 * Copyright (C) 2016 Ferran Marcet <fmarcet@2byte.es>
15 * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
16 * Copyright (C) 2019-2024 Frédéric France <frederic.france@free.fr>
17 * Copyright (C) 2020 Tobias Sekan <tobias.sekan@startmail.com>
18 * Copyright (C) 2022 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
19 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
20 *
21 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 3 of the License, or
24 * (at your option) any later version.
25 *
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
30 *
31 * You should have received a copy of the GNU General Public License
32 * along with this program. If not, see <https://www.gnu.org/licenses/>.
33 */
34
40require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
41require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
42require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
43require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
44require_once DOL_DOCUMENT_ROOT.'/margin/lib/margins.lib.php';
45require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
46require_once DOL_DOCUMENT_ROOT.'/core/class/commonincoterm.class.php';
47
52{
54
58 public $element = 'supplier_proposal';
59
63 public $table_element = 'supplier_proposal';
64
68 public $table_element_line = 'supplier_proposaldet';
69
73 public $fk_element = 'fk_supplier_proposal';
74
78 public $picto = 'supplier_proposal';
79
84 public $restrictiononfksoc = 1;
85
89 protected $table_ref_field = 'ref';
90
91 public $socid; // Id client
92
97 public $author;
98
99 public $ref_fourn; //Reference saisie lors de l'ajout d'une ligne à la demande
100 public $ref_supplier; //Reference saisie lors de l'ajout d'une ligne à la demande
101
105 public $statut; // 0 (draft), 1 (validated), 2 (signed), 3 (not signed), 4 (processed/billed)
106
110 public $date;
111
115 public $delivery_date;
116
121 public $datec;
122
126 public $date_creation;
127
132 public $datev;
133
137 public $date_validation;
138
139
140 public $user_author_id;
141
146 public $price;
147
152 public $tva;
153
158 public $total;
159
160 public $cond_reglement_code;
161 public $cond_reglement_doc; // label doc
162
163 public $mode_reglement_code;
168 public $mode_reglement;
169
170 public $extraparams = array();
171 public $lines = array();
172 public $line;
173
174 public $labelStatus = array();
175 public $labelStatusShort = array();
176
177 public $nbtodo;
178 public $nbtodolate;
179
180 // Multicurrency
184 public $fk_multicurrency;
185
186 public $multicurrency_code;
187 public $multicurrency_tx;
188 public $multicurrency_total_ht;
189 public $multicurrency_total_tva;
190 public $multicurrency_total_ttc;
191
195 const STATUS_DRAFT = 0;
196
201
205 const STATUS_SIGNED = 2;
206
211
215 const STATUS_CLOSE = 4;
216
217
218
226 public function __construct($db, $socid = 0, $supplier_proposalid = 0)
227 {
228 global $conf, $langs;
229
230 $this->db = $db;
231
232 $this->ismultientitymanaged = 1;
233 $this->socid = $socid;
234 $this->id = $supplier_proposalid;
235 }
236
237
238 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
249 public function add_product($idproduct, $qty, $remise_percent = 0)
250 {
251 // phpcs:enable
252 global $conf, $mysoc;
253
254 if (!$qty) {
255 $qty = 1;
256 }
257
258 dol_syslog(get_class($this)."::add_product $idproduct, $qty, $remise_percent");
259 if ($idproduct > 0) {
260 $prod = new Product($this->db);
261 $prod->fetch($idproduct);
262
263 $productdesc = $prod->description;
264
265 $tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
266 $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
267 if (empty($tva_tx)) {
268 $tva_npr = 0;
269 }
270 $localtax1_tx = get_localtax($tva_tx, 1, $mysoc, $this->thirdparty, $tva_npr);
271 $localtax2_tx = get_localtax($tva_tx, 2, $mysoc, $this->thirdparty, $tva_npr);
272
273 // multiprix
274 if ($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level) {
275 $price = $prod->multiprices[$this->thirdparty->price_level];
276 } else {
277 $price = $prod->price;
278 }
279
280 $line = new SupplierProposalLine($this->db);
281
282 $line->fk_product = $idproduct;
283 $line->desc = $productdesc;
284 $line->qty = $qty;
285 $line->subprice = $price;
286 $line->remise_percent = $remise_percent;
287 $line->tva_tx = $tva_tx;
288
289 $this->lines[] = $line;
290 return 1;
291 }
292 return -1;
293 }
294
295 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
302 public function insert_discount($idremise)
303 {
304 // phpcs:enable
305 global $langs;
306
307 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
308 include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
309
310 $this->db->begin();
311
312 $remise = new DiscountAbsolute($this->db);
313 $result = $remise->fetch($idremise);
314
315 if ($result > 0) {
316 if ($remise->fk_facture) { // Protection against multiple submission
317 $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
318 $this->db->rollback();
319 return -5;
320 }
321
322 $supplier_proposalligne = new SupplierProposalLine($this->db);
323 $supplier_proposalligne->fk_supplier_proposal = $this->id;
324 $supplier_proposalligne->fk_remise_except = $remise->id;
325 $supplier_proposalligne->desc = $remise->description; // Description ligne
326 $supplier_proposalligne->tva_tx = $remise->tva_tx;
327 $supplier_proposalligne->subprice = -$remise->amount_ht;
328 $supplier_proposalligne->fk_product = 0; // Id produit predefini
329 $supplier_proposalligne->qty = 1;
330 $supplier_proposalligne->remise_percent = 0;
331 $supplier_proposalligne->rang = -1;
332 $supplier_proposalligne->info_bits = 2;
333
334 $supplier_proposalligne->total_ht = -$remise->amount_ht;
335 $supplier_proposalligne->total_tva = -$remise->amount_tva;
336 $supplier_proposalligne->total_ttc = -$remise->amount_ttc;
337
338 $result = $supplier_proposalligne->insert();
339 if ($result > 0) {
340 $result = $this->update_price(1);
341 if ($result > 0) {
342 $this->db->commit();
343 return 1;
344 } else {
345 $this->db->rollback();
346 return -1;
347 }
348 } else {
349 $this->error = $supplier_proposalligne->error;
350 $this->db->rollback();
351 return -2;
352 }
353 } else {
354 $this->db->rollback();
355 return -2;
356 }
357 }
358
396 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)
397 {
398 global $mysoc, $conf, $langs;
399
400 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");
401 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
402
403 // Clean parameters
404 if (empty($remise_percent)) {
405 $remise_percent = 0;
406 }
407 if (empty($qty)) {
408 $qty = 0;
409 }
410 if (empty($info_bits)) {
411 $info_bits = 0;
412 }
413 if (empty($rang)) {
414 $rang = 0;
415 }
416 if (empty($fk_parent_line) || $fk_parent_line < 0) {
417 $fk_parent_line = 0;
418 }
419 if (empty($pu_ht)) {
420 $pu_ht = 0;
421 }
422
423 $remise_percent = price2num($remise_percent);
424 $qty = (float) price2num($qty);
425 $pu_ht = price2num($pu_ht);
426 $pu_ttc = price2num($pu_ttc);
427 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
428 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
429 }
430 $txlocaltax1 = price2num($txlocaltax1);
431 $txlocaltax2 = price2num($txlocaltax2);
432 $pa_ht = price2num($pa_ht);
433 if ($price_base_type == 'HT') {
434 $pu = $pu_ht;
435 } else {
436 $pu = $pu_ttc;
437 }
438
439 // Check parameters
440 if ($type < 0) {
441 return -1;
442 }
443
444 if ($this->statut == self::STATUS_DRAFT) {
445 $this->db->begin();
446
447 if ($fk_product > 0) {
448 if (getDolGlobalString('SUPPLIER_PROPOSAL_WITH_PREDEFINED_PRICES_ONLY')) {
449 // Check quantity is enough
450 dol_syslog(get_class($this)."::addline we check supplier prices fk_product=".$fk_product." fk_fournprice=".$fk_fournprice." qty=".$qty." ref_supplier=".$ref_supplier);
451 $productsupplier = new ProductFournisseur($this->db);
452 if ($productsupplier->fetch($fk_product) > 0) {
453 $product_type = $productsupplier->type;
454 $label = $productsupplier->label;
455 $fk_prod_fourn_price = $fk_fournprice;
456
457 // We use 'none' instead of $ref_supplier, because fourn_ref may not exists anymore. So we will take the first supplier price ok.
458 // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
459 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
460 $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
461 if ($result > 0) {
462 $pu = $productsupplier->fourn_pu; // Unit price supplier price set by get_buyprice
463 $ref_supplier = $productsupplier->ref_supplier; // Ref supplier price set by get_buyprice
464 // is remise percent not keyed but present for the product we add it
465 if ($remise_percent == 0 && $productsupplier->remise_percent != 0) {
466 $remise_percent = $productsupplier->remise_percent;
467 }
468 }
469 if ($result == 0) { // If result == 0, we failed to found the supplier reference price
470 $langs->load("errors");
471 $this->error = "Ref ".$productsupplier->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
472 $this->db->rollback();
473 dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
474 //$pu = $productsupplier->fourn_pu; // We do not overwrite unit price
475 //$ref = $productsupplier_fourn; // We do not overwrite ref supplier price
476 return -1;
477 }
478 if ($result == -1) {
479 $langs->load("errors");
480 $this->error = "Ref ".$productsupplier->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
481 $this->db->rollback();
482 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
483 return -1;
484 }
485 if ($result < -1) {
486 $this->error = $productsupplier->error;
487 $this->errors = $productsupplier->errors;
488 $this->db->rollback();
489 dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
490 return -1;
491 }
492 } else {
493 $this->error = $productsupplier->error;
494 $this->errors = $productsupplier->errors;
495 $this->db->rollback();
496 return -1;
497 }
498 }
499 } else {
500 $product_type = $type;
501 }
502
503 // Calcul du total TTC et de la TVA pour la ligne a partir de
504 // qty, pu, remise_percent et txtva
505 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
506 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
507
508 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
509
510 // Clean vat code
511 $reg = array();
512 $vat_src_code = '';
513 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
514 $vat_src_code = $reg[1];
515 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
516 }
517
518 if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
519 $pu = 0;
520 }
521
522 $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
523 $total_ht = $tabprice[0];
524 $total_tva = $tabprice[1];
525 $total_ttc = $tabprice[2];
526 $total_localtax1 = $tabprice[9];
527 $total_localtax2 = $tabprice[10];
528 $pu = $pu_ht = $tabprice[3];
529
530 // MultiCurrency
531 $multicurrency_total_ht = $tabprice[16];
532 $multicurrency_total_tva = $tabprice[17];
533 $multicurrency_total_ttc = $tabprice[18];
534 $pu_ht_devise = $tabprice[19];
535
536 // Rang to use
537 $ranktouse = $rang;
538 if ($ranktouse == -1) {
539 $rangmax = $this->line_max($fk_parent_line);
540 $ranktouse = $rangmax + 1;
541 }
542
543 // TODO A virer
544 // Anciens indicateurs: $price, $remise (a ne plus utiliser)
545 $price = $pu;
546 $remise = 0;
547 if ($remise_percent > 0) {
548 $remise = round(($pu * (float) $remise_percent / 100), 2);
549 $price = $pu - $remise;
550 }
551
552 // Insert line
553 $this->line = new SupplierProposalLine($this->db);
554
555 $this->line->fk_supplier_proposal = $this->id;
556 $this->line->label = $label;
557 $this->line->desc = $desc;
558 $this->line->qty = $qty;
559
560 $this->line->vat_src_code = $vat_src_code;
561 $this->line->tva_tx = $txtva;
562 $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
563 $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
564 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
565 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
566 $this->line->fk_product = $fk_product;
567 $this->line->remise_percent = $remise_percent;
568 $this->line->subprice = $pu_ht;
569 $this->line->rang = $ranktouse;
570 $this->line->info_bits = $info_bits;
571 $this->line->total_ht = $total_ht;
572 $this->line->total_tva = $total_tva;
573 $this->line->total_localtax1 = $total_localtax1;
574 $this->line->total_localtax2 = $total_localtax2;
575 $this->line->total_ttc = $total_ttc;
576 $this->line->product_type = $type;
577 $this->line->special_code = $special_code;
578 $this->line->fk_parent_line = $fk_parent_line;
579 $this->line->fk_unit = $fk_unit;
580 $this->line->origin = $origin;
581 $this->line->origin_id = $origin_id;
582 $this->line->ref_fourn = $this->db->escape($ref_supplier);
583 $this->line->date_start = $date_start;
584 $this->line->date_end = $date_end;
585
586 // infos merge
587 if (!empty($fk_product) && $fk_product > 0 && empty($fk_fournprice) && empty($pa_ht)) {
588 // When fk_fournprice is 0, we take the lowest buying price
589 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
590 $productFournisseur = new ProductFournisseur($this->db);
591 $productFournisseur->find_min_price_product_fournisseur($fk_product);
592 $this->line->fk_fournprice = $productFournisseur->product_fourn_price_id;
593 } else {
594 $this->line->fk_fournprice = ($fk_fournprice > 0 ? $fk_fournprice : 0); // If fk_fournprice is -1, we will not use fk_fournprice
595 }
596 $this->line->pa_ht = $pa_ht;
597 //var_dump($this->line->fk_fournprice);exit;
598
599 // Multicurrency
600 $this->line->fk_multicurrency = $this->fk_multicurrency;
601 $this->line->multicurrency_code = $this->multicurrency_code;
602 $this->line->multicurrency_subprice = $pu_ht_devise;
603 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
604 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
605 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
606
607 // Mise en option de la ligne
608 if (empty($qty) && empty($special_code)) {
609 $this->line->special_code = 3;
610 }
611
612 if (is_array($array_options) && count($array_options) > 0) {
613 $this->line->array_options = $array_options;
614 }
615
616 $result = $this->line->insert();
617 if ($result > 0) {
618 // Reorder if child line
619 if (!empty($fk_parent_line)) {
620 $this->line_order(true, 'DESC');
621 } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) { // Update all rank of all other lines
622 $linecount = count($this->lines);
623 for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
624 $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
625 }
626 }
627
628 // Mise a jour information denormalisees au niveau de la propale meme
629 $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.
630 if ($result > 0) {
631 $this->db->commit();
632 return $this->line->id;
633 } else {
634 $this->db->rollback();
635 return -1;
636 }
637 } else {
638 $this->error = $this->line->error;
639 $this->errors = $this->line->errors;
640 $this->db->rollback();
641 return -2;
642 }
643 } else {
644 $this->error = 'BadStatusOfObjectToAddLine';
645 return -5;
646 }
647 }
648
649
676 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)
677 {
678 global $conf, $user, $langs, $mysoc;
679
680 dol_syslog(get_class($this)."::updateLine $rowid, $pu, $qty, $remise_percent, $txtva, $desc, $price_base_type, $info_bits");
681 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
682
683 // Clean parameters
684 $remise_percent = price2num($remise_percent);
685 $qty = (float) price2num($qty);
686 $pu = price2num($pu);
687 if (!preg_match('/\‍((.*)\‍)/', (string) $txtva)) {
688 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
689 }
690 $txlocaltax1 = price2num($txlocaltax1);
691 $txlocaltax2 = price2num($txlocaltax2);
692 $pa_ht = price2num($pa_ht);
693 if (empty($qty) && empty($special_code)) {
694 $special_code = 3; // Set option tag
695 }
696 if (!empty($qty) && $special_code == 3) {
697 $special_code = 0; // Remove option tag
698 }
699
700 if ($this->status == 0) {
701 $this->db->begin();
702
703 // Calcul du total TTC et de la TVA pour la ligne a partir de
704 // qty, pu, remise_percent et txtva
705 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
706 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
707
708 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
709
710 // Clean vat code
711 $reg = array();
712 $vat_src_code = '';
713 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
714 $vat_src_code = $reg[1];
715 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
716 }
717
718 if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
719 $pu = 0;
720 }
721
722 $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
723 $total_ht = $tabprice[0];
724 $total_tva = $tabprice[1];
725 $total_ttc = $tabprice[2];
726 $total_localtax1 = $tabprice[9];
727 $total_localtax2 = $tabprice[10];
728 $pu_ht = $tabprice[3];
729 $pu_tva = $tabprice[4];
730 $pu_ttc = $tabprice[5];
731
732 // MultiCurrency
733 $multicurrency_total_ht = $tabprice[16];
734 $multicurrency_total_tva = $tabprice[17];
735 $multicurrency_total_ttc = $tabprice[18];
736 $pu_ht_devise = $tabprice[19];
737
738 $pu = $pu_ht;
739 if ($price_base_type == 'TTC') {
740 $pu = $pu_ttc;
741 }
742
743 //Fetch current line from the database and then clone the object and set it in $oldline property
744 $line = new SupplierProposalLine($this->db);
745 $line->fetch($rowid);
746 $line->fetch_optionals();
747
748 $fk_product = $line->fk_product;
749
750 // Stock previous line records
751 $staticline = clone $line;
752
753 $line->oldline = $staticline;
754 $this->line = $line;
755 $this->line->context = $this->context;
756
757 // Reorder if fk_parent_line change
758 if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
759 $rangmax = $this->line_max($fk_parent_line);
760 $this->line->rang = $rangmax + 1;
761 }
762
763 $this->line->id = $rowid;
764 $this->line->label = $label;
765 $this->line->desc = $desc;
766 $this->line->qty = $qty;
767 $this->line->product_type = $type;
768
769 $this->line->vat_src_code = $vat_src_code;
770 $this->line->tva_tx = $txtva;
771 $this->line->localtax1_tx = $txlocaltax1;
772 $this->line->localtax2_tx = $txlocaltax2;
773 $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
774 $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
775 $this->line->remise_percent = $remise_percent;
776 $this->line->subprice = $pu;
777 $this->line->info_bits = $info_bits;
778 $this->line->total_ht = $total_ht;
779 $this->line->total_tva = $total_tva;
780 $this->line->total_localtax1 = $total_localtax1;
781 $this->line->total_localtax2 = $total_localtax2;
782 $this->line->total_ttc = $total_ttc;
783 $this->line->special_code = $special_code;
784 $this->line->fk_parent_line = $fk_parent_line;
785 $this->line->skip_update_total = $skip_update_total;
786 $this->line->ref_fourn = $ref_supplier;
787 $this->line->fk_unit = $fk_unit;
788
789 // infos merge
790 if (!empty($fk_product) && $fk_product > 0 && empty($fk_fournprice) && empty($pa_ht)) {
791 // by external module, take lowest buying price
792 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
793 $productFournisseur = new ProductFournisseur($this->db);
794 $productFournisseur->find_min_price_product_fournisseur($fk_product);
795 $this->line->fk_fournprice = $productFournisseur->product_fourn_price_id;
796 } else {
797 $this->line->fk_fournprice = $fk_fournprice;
798 }
799 $this->line->pa_ht = $pa_ht;
800
801 if (is_array($array_options) && count($array_options) > 0) {
802 // We replace values in this->line->array_options only for entries defined into $array_options
803 foreach ($array_options as $key => $value) {
804 $this->line->array_options[$key] = $array_options[$key];
805 }
806 }
807
808 // Multicurrency
809 $this->line->multicurrency_subprice = $pu_ht_devise;
810 $this->line->multicurrency_total_ht = $multicurrency_total_ht;
811 $this->line->multicurrency_total_tva = $multicurrency_total_tva;
812 $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
813
814 $result = $this->line->update();
815 if ($result > 0) {
816 // Reorder if child line
817 if (!empty($fk_parent_line)) {
818 $this->line_order(true, 'DESC');
819 }
820
821 $this->update_price(1);
822
823 $this->db->commit();
824 return $result;
825 } else {
826 $this->error = $this->db->error();
827 $this->db->rollback();
828 return -1;
829 }
830 } else {
831 dol_syslog(get_class($this)."::updateline Erreur -2 SupplierProposal en mode incompatible pour cette action");
832 return -2;
833 }
834 }
835
836
843 public function deleteLine($lineid)
844 {
845 global $user;
846
847 if ($this->statut == 0) {
848 $line = new SupplierProposalLine($this->db);
849
850 // For triggers
851 $line->fetch($lineid);
852
853 if ($line->delete($user) > 0) {
854 $this->update_price(1);
855
856 return 1;
857 } else {
858 return -1;
859 }
860 } else {
861 return -2;
862 }
863 }
864
865
874 public function create($user, $notrigger = 0)
875 {
876 global $langs, $conf, $mysoc, $hookmanager;
877 $error = 0;
878
879 $now = dol_now();
880
881 dol_syslog(get_class($this)."::create");
882
883 // Check parameters
884 $result = $this->fetch_thirdparty();
885 if ($result < 0) {
886 $this->error = "Failed to fetch company";
887 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
888 return -3;
889 }
890 if (!empty($this->ref)) { // We check that ref is not already used
891 $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
892 if ($result > 0) {
893 $this->error = 'ErrorRefAlreadyExists';
894 dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
895 $this->db->rollback();
896 return -1;
897 }
898 }
899
900 // Set tmp vars
901 $delivery_date = $this->delivery_date;
902
903 // Multicurrency
904 if (!empty($this->multicurrency_code)) {
905 list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $now);
906 }
907 if (empty($this->fk_multicurrency)) {
908 $this->multicurrency_code = $conf->currency;
909 $this->fk_multicurrency = 0;
910 $this->multicurrency_tx = 1;
911 }
912
913 $this->db->begin();
914
915 // Insert into database
916 $sql = "INSERT INTO ".MAIN_DB_PREFIX."supplier_proposal (";
917 $sql .= "fk_soc";
918 $sql .= ", price";
919 $sql .= ", total_tva";
920 $sql .= ", total_ttc";
921 $sql .= ", datec";
922 $sql .= ", ref";
923 $sql .= ", fk_user_author";
924 $sql .= ", note_private";
925 $sql .= ", note_public";
926 $sql .= ", model_pdf";
927 $sql .= ", fk_cond_reglement";
928 $sql .= ", fk_mode_reglement";
929 $sql .= ", fk_account";
930 $sql .= ", date_livraison";
931 $sql .= ", fk_shipping_method";
932 $sql .= ", fk_projet";
933 $sql .= ", entity";
934 $sql .= ", fk_multicurrency";
935 $sql .= ", multicurrency_code";
936 $sql .= ", multicurrency_tx";
937 $sql .= ") ";
938 $sql .= " VALUES (";
939 $sql .= ((int) $this->socid);
940 $sql .= ", 0";
941 $sql .= ", 0";
942 $sql .= ", 0";
943 $sql .= ", '".$this->db->idate($now)."'";
944 $sql .= ", '(PROV)'";
945 $sql .= ", ".($user->id > 0 ? ((int) $user->id) : "null");
946 $sql .= ", '".$this->db->escape($this->note_private)."'";
947 $sql .= ", '".$this->db->escape($this->note_public)."'";
948 $sql .= ", '".$this->db->escape($this->model_pdf)."'";
949 $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : 'NULL');
950 $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : 'NULL');
951 $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
952 $sql .= ", ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : "null");
953 $sql .= ", ".($this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : 'NULL');
954 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
955 $sql .= ", ".((int) $conf->entity);
956 $sql .= ", ".((int) $this->fk_multicurrency);
957 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
958 $sql .= ", ".((float) $this->multicurrency_tx);
959 $sql .= ")";
960
961 dol_syslog(get_class($this)."::create", LOG_DEBUG);
962 $resql = $this->db->query($sql);
963 if ($resql) {
964 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."supplier_proposal");
965
966 if ($this->id) {
967 $this->ref = '(PROV'.$this->id.')';
968 $sql = 'UPDATE '.MAIN_DB_PREFIX."supplier_proposal SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
969
970 dol_syslog(get_class($this)."::create", LOG_DEBUG);
971 $resql = $this->db->query($sql);
972 if (!$resql) {
973 $error++;
974 }
975
976 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
977 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
978 }
979
980 // Add object linked
981 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
982 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
983 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, ...))
984 foreach ($tmp_origin_id as $origin_id) {
985 $ret = $this->add_object_linked($origin, $origin_id);
986 if (!$ret) {
987 dol_print_error($this->db);
988 $error++;
989 }
990 }
991 }
992 }
993 }
994
995 /*
996 * Insertion du detail des produits dans la base
997 */
998 if (!$error) {
999 $fk_parent_line = 0;
1000 $num = count($this->lines);
1001
1002 for ($i = 0; $i < $num; $i++) {
1003 // Reset fk_parent_line for no child products and special product
1004 if (($this->lines[$i]->product_type != 9 && empty($this->lines[$i]->fk_parent_line)) || $this->lines[$i]->product_type == 9) {
1005 $fk_parent_line = 0;
1006 }
1007
1008 $result = $this->addline(
1009 $this->lines[$i]->desc,
1010 $this->lines[$i]->subprice,
1011 $this->lines[$i]->qty,
1012 $this->lines[$i]->tva_tx,
1013 $this->lines[$i]->localtax1_tx,
1014 $this->lines[$i]->localtax2_tx,
1015 $this->lines[$i]->fk_product,
1016 $this->lines[$i]->remise_percent,
1017 'HT',
1018 0,
1019 0,
1020 $this->lines[$i]->product_type,
1021 $this->lines[$i]->rang,
1022 $this->lines[$i]->special_code,
1023 $fk_parent_line,
1024 $this->lines[$i]->fk_fournprice,
1025 $this->lines[$i]->pa_ht,
1026 empty($this->lines[$i]->label) ? '' : $this->lines[$i]->label, // deprecated
1027 $this->lines[$i]->array_options,
1028 $this->lines[$i]->ref_fourn,
1029 $this->lines[$i]->fk_unit,
1030 'supplier_proposal',
1031 $this->lines[$i]->rowid
1032 );
1033
1034 if ($result < 0) {
1035 $error++;
1036 $this->error = $this->db->error;
1037 dol_print_error($this->db);
1038 break;
1039 }
1040 // Defined the new fk_parent_line
1041 if ($result > 0 && $this->lines[$i]->product_type == 9) {
1042 $fk_parent_line = $result;
1043 }
1044 }
1045 }
1046
1047 if (!$error) {
1048 // Mise a jour infos denormalisees
1049 $resql = $this->update_price(1);
1050 if ($resql) {
1051 $action = 'update';
1052
1053 // Actions on extra fields
1054 if (!$error) {
1055 $result = $this->insertExtraFields();
1056 if ($result < 0) {
1057 $error++;
1058 }
1059 }
1060
1061 if (!$error && !$notrigger) {
1062 // Call trigger
1063 $result = $this->call_trigger('PROPOSAL_SUPPLIER_CREATE', $user);
1064 if ($result < 0) {
1065 $error++;
1066 }
1067 // End call triggers
1068 }
1069 } else {
1070 $this->error = $this->db->lasterror();
1071 $error++;
1072 }
1073 }
1074 } else {
1075 $this->error = $this->db->lasterror();
1076 $error++;
1077 }
1078
1079 if (!$error) {
1080 $this->db->commit();
1081 dol_syslog(get_class($this)."::create done id=".$this->id);
1082 return $this->id;
1083 } else {
1084 $this->db->rollback();
1085 return -2;
1086 }
1087 } else {
1088 $this->error = $this->db->lasterror();
1089 $this->db->rollback();
1090 return -1;
1091 }
1092 }
1093
1101 public function createFromClone(User $user, $fromid = 0)
1102 {
1103 global $conf, $hookmanager;
1104
1105 $error = 0;
1106 $now = dol_now();
1107
1108 $this->db->begin();
1109
1110 // get extrafields so they will be clone
1111 foreach ($this->lines as $line) {
1112 $line->fetch_optionals();
1113 }
1114
1115 // Load source object
1116 $objFrom = clone $this;
1117
1118 $objsoc = new Societe($this->db);
1119
1120 // Change socid if needed
1121 if (!empty($fromid) && $fromid != $this->socid) {
1122 if ($objsoc->fetch($fromid) > 0) {
1123 $this->socid = $objsoc->id;
1124 $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1125 $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1126 unset($this->fk_project);
1127 }
1128
1129 // TODO Change product price if multi-prices
1130 } else {
1131 $objsoc->fetch($this->socid);
1132 }
1133
1134 $this->id = 0;
1135 $this->statut = 0;
1136
1137 if (!getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') || !is_readable(DOL_DOCUMENT_ROOT."/core/modules/supplier_proposal/" . getDolGlobalString('SUPPLIER_PROPOSAL_ADDON').".php")) {
1138 $this->error = 'ErrorSetupNotComplete';
1139 return -1;
1140 }
1141
1142 // Clear fields
1143 $this->user_author_id = $user->id;
1144 $this->user_validation_id = 0;
1145 $this->date = $now;
1146
1147 // Set ref
1148 require_once DOL_DOCUMENT_ROOT."/core/modules/supplier_proposal/" . getDolGlobalString('SUPPLIER_PROPOSAL_ADDON').'.php';
1149 $obj = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
1150 $modSupplierProposal = new $obj();
1151 $this->ref = $modSupplierProposal->getNextValue($objsoc, $this);
1152
1153 // Create clone
1154 $this->context['createfromclone'] = 'createfromclone';
1155 $result = $this->create($user);
1156 if ($result < 0) {
1157 $error++;
1158 }
1159
1160 if (!$error) {
1161 // Hook of thirdparty module
1162 if (is_object($hookmanager)) {
1163 $parameters = array('objFrom' => $objFrom);
1164 $action = '';
1165 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1166 if ($reshook < 0) {
1167 $this->setErrorsFromObject($hookmanager);
1168 $error++;
1169 }
1170 }
1171 }
1172
1173 unset($this->context['createfromclone']);
1174
1175 // End
1176 if (!$error) {
1177 $this->db->commit();
1178 return $this->id;
1179 } else {
1180 $this->db->rollback();
1181 return -1;
1182 }
1183 }
1184
1192 public function fetch($rowid, $ref = '')
1193 {
1194 global $conf;
1195
1196 $sql = "SELECT p.rowid, p.entity, p.ref, p.fk_soc as socid";
1197 $sql .= ", p.total_ttc, p.total_tva, p.localtax1, p.localtax2, p.total_ht";
1198 $sql .= ", p.datec";
1199 $sql .= ", p.date_valid as datev";
1200 $sql .= ", p.date_livraison as delivery_date";
1201 $sql .= ", p.model_pdf, p.extraparams";
1202 $sql .= ", p.note_private, p.note_public";
1203 $sql .= ", p.fk_projet as fk_project, p.fk_statut";
1204 $sql .= ", p.fk_user_author, p.fk_user_valid, p.fk_user_cloture";
1205 $sql .= ", p.fk_cond_reglement";
1206 $sql .= ", p.fk_mode_reglement";
1207 $sql .= ', p.fk_account';
1208 $sql .= ", p.fk_shipping_method";
1209 $sql .= ", p.fk_multicurrency, p.multicurrency_code, p.multicurrency_tx, p.multicurrency_total_ht, p.multicurrency_total_tva, p.multicurrency_total_ttc";
1210 $sql .= ", c.label as statut_label";
1211 $sql .= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc";
1212 $sql .= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement";
1213 $sql .= " FROM ".MAIN_DB_PREFIX."c_propalst as c, ".MAIN_DB_PREFIX."supplier_proposal as p";
1214 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id';
1215 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON p.fk_cond_reglement = cr.rowid';
1216 $sql .= " WHERE p.fk_statut = c.id";
1217 $sql .= " AND p.entity IN (".getEntity('supplier_proposal').")";
1218 if ($ref) {
1219 $sql .= " AND p.ref = '".$this->db->escape($ref)."'";
1220 } else {
1221 $sql .= " AND p.rowid = ".((int) $rowid);
1222 }
1223
1224 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1225 $resql = $this->db->query($sql);
1226 if ($resql) {
1227 if ($this->db->num_rows($resql)) {
1228 $obj = $this->db->fetch_object($resql);
1229
1230 $this->id = $obj->rowid;
1231 $this->entity = $obj->entity;
1232
1233 $this->ref = $obj->ref;
1234 $this->total_ht = $obj->total_ht;
1235 $this->total_tva = $obj->total_tva;
1236 $this->total_localtax1 = $obj->localtax1;
1237 $this->total_localtax2 = $obj->localtax2;
1238 $this->total_ttc = $obj->total_ttc;
1239 $this->socid = $obj->socid;
1240 $this->fk_project = $obj->fk_project;
1241 $this->model_pdf = $obj->model_pdf;
1242 $this->note = $obj->note_private; // TODO deprecated
1243 $this->note_private = $obj->note_private;
1244 $this->note_public = $obj->note_public;
1245 $this->statut = (int) $obj->fk_statut;
1246 $this->status = (int) $obj->fk_statut;
1247 $this->datec = $this->db->jdate($obj->datec); // TODO deprecated
1248 $this->datev = $this->db->jdate($obj->datev); // TODO deprecated
1249 $this->date_creation = $this->db->jdate($obj->datec); // Creation date
1250 $this->date = $this->date_creation;
1251 $this->date_validation = $this->db->jdate($obj->datev); // Validation date
1252 $this->delivery_date = $this->db->jdate($obj->delivery_date);
1253 $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1254
1255 $this->mode_reglement_id = $obj->fk_mode_reglement;
1256 $this->mode_reglement_code = $obj->mode_reglement_code;
1257 $this->mode_reglement = $obj->mode_reglement;
1258 $this->fk_account = ($obj->fk_account > 0) ? $obj->fk_account : null;
1259 $this->cond_reglement_id = $obj->fk_cond_reglement;
1260 $this->cond_reglement_code = $obj->cond_reglement_code;
1261 $this->cond_reglement = $obj->cond_reglement;
1262 $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1263
1264 $this->extraparams = (array) json_decode($obj->extraparams, true);
1265
1266 $this->user_author_id = $obj->fk_user_author;
1267 $this->user_validation_id = $obj->fk_user_valid;
1268 $this->user_closing_id = $obj->fk_user_cloture;
1269
1270 // Multicurrency
1271 $this->fk_multicurrency = $obj->fk_multicurrency;
1272 $this->multicurrency_code = $obj->multicurrency_code;
1273 $this->multicurrency_tx = $obj->multicurrency_tx;
1274 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1275 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1276 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1277
1278 // Retrieve all extrafield
1279 // fetch optionals attributes and labels
1280 $this->fetch_optionals();
1281
1282 $this->db->free($resql);
1283
1284 $this->lines = array();
1285
1286 // Lines of supplier proposals
1287 $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,";
1288 $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,";
1289 $sql .= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label,';
1290 $sql .= ' d.ref_fourn as ref_produit_fourn,';
1291 $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';
1292 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposaldet as d";
1293 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON d.fk_product = p.rowid";
1294 $sql .= " WHERE d.fk_supplier_proposal = ".((int) $this->id);
1295 $sql .= " ORDER by d.rang";
1296
1297 $result = $this->db->query($sql);
1298 if ($result) {
1299 $num = $this->db->num_rows($result);
1300 $i = 0;
1301
1302 while ($i < $num) {
1303 $objp = $this->db->fetch_object($result);
1304
1305 $line = new SupplierProposalLine($this->db);
1306
1307 $line->rowid = $objp->rowid; // deprecated
1308 $line->id = $objp->rowid;
1309 $line->fk_supplier_proposal = $objp->fk_supplier_proposal;
1310 $line->fk_parent_line = $objp->fk_parent_line;
1311 $line->product_type = $objp->product_type;
1312 $line->label = $objp->custom_label;
1313 $line->desc = $objp->description; // Description ligne
1314 $line->qty = $objp->qty;
1315 $line->tva_tx = $objp->tva_tx;
1316 $line->localtax1_tx = $objp->localtax1_tx;
1317 $line->localtax2_tx = $objp->localtax2_tx;
1318 $line->subprice = $objp->subprice;
1319 $line->fk_remise_except = $objp->fk_remise_except;
1320 $line->remise_percent = $objp->remise_percent;
1321
1322 $line->info_bits = $objp->info_bits;
1323 $line->total_ht = $objp->total_ht;
1324 $line->total_tva = $objp->total_tva;
1325 $line->total_localtax1 = $objp->total_localtax1;
1326 $line->total_localtax2 = $objp->total_localtax2;
1327 $line->total_ttc = $objp->total_ttc;
1328 $line->fk_fournprice = $objp->fk_fournprice;
1329 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1330 $line->pa_ht = $marginInfos[0];
1331 $line->marge_tx = $marginInfos[1];
1332 $line->marque_tx = $marginInfos[2];
1333 $line->special_code = $objp->special_code;
1334 $line->rang = $objp->rang;
1335
1336 $line->fk_product = $objp->fk_product;
1337
1338 $line->ref = $objp->product_ref; // deprecated
1339 $line->product_ref = $objp->product_ref;
1340 $line->libelle = $objp->product_label; // deprecated
1341 $line->product_label = $objp->product_label;
1342 $line->product_desc = $objp->product_desc; // Description produit
1343 $line->fk_product_type = $objp->fk_product_type;
1344
1345 $line->ref_fourn = $objp->ref_produit_fourn;
1346
1347 // Multicurrency
1348 $line->fk_multicurrency = $objp->fk_multicurrency;
1349 $line->multicurrency_code = $objp->multicurrency_code;
1350 $line->multicurrency_subprice = $objp->multicurrency_subprice;
1351 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
1352 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
1353 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
1354 $line->fk_unit = $objp->fk_unit;
1355
1356 $this->lines[$i] = $line;
1357
1358 $i++;
1359 }
1360 $this->db->free($result);
1361 } else {
1362 $this->error = $this->db->error();
1363 return -1;
1364 }
1365
1366 // Retrieve all extrafield
1367 // fetch optionals attributes and labels
1368 $this->fetch_optionals();
1369
1370 return 1;
1371 }
1372
1373 $this->error = "Record Not Found";
1374 return 0;
1375 } else {
1376 $this->error = $this->db->error();
1377 return -1;
1378 }
1379 }
1380
1388 public function valid($user, $notrigger = 0)
1389 {
1390 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1391
1392 global $conf, $langs;
1393
1394 $error = 0;
1395 $now = dol_now();
1396
1397 if ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'creer'))
1398 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'validate_advance'))) {
1399 $this->db->begin();
1400
1401 // Numbering module definition
1402 $soc = new Societe($this->db);
1403 $result = $soc->fetch($this->socid);
1404
1405 if ($result < 0) {
1406 return -1;
1407 }
1408
1409 // Define new ref
1410 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1411 $num = $this->getNextNumRef($soc);
1412 } else {
1413 $num = $this->ref;
1414 }
1415 $this->newref = dol_sanitizeFileName($num);
1416
1417 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1418 $sql .= " SET ref = '".$this->db->escape($num)."',";
1419 $sql .= " fk_statut = 1, date_valid='".$this->db->idate($now)."', fk_user_valid=".((int) $user->id);
1420 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1421
1422 dol_syslog(get_class($this)."::valid", LOG_DEBUG);
1423 $resql = $this->db->query($sql);
1424 if (!$resql) {
1425 dol_print_error($this->db);
1426 $error++;
1427 }
1428
1429 // Trigger calls
1430 if (!$error && !$notrigger) {
1431 // Call trigger
1432 $result = $this->call_trigger('PROPOSAL_SUPPLIER_VALIDATE', $user);
1433 if ($result < 0) {
1434 $error++;
1435 }
1436 // End call triggers
1437 }
1438
1439 if (!$error) {
1440 $this->oldref = $this->ref;
1441
1442 // Rename directory if dir was a temporary ref
1443 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1444 // Now we rename also files into index
1445 $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)."'";
1446 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1447 $resql = $this->db->query($sql);
1448 if (!$resql) {
1449 $error++;
1450 $this->error = $this->db->lasterror();
1451 }
1452 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'supplier_proposal/".$this->db->escape($this->newref)."'";
1453 $sql .= " WHERE filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1454 $resql = $this->db->query($sql);
1455 if (!$resql) {
1456 $error++;
1457 $this->error = $this->db->lasterror();
1458 }
1459
1460 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1461 $oldref = dol_sanitizeFileName($this->ref);
1462 $newref = dol_sanitizeFileName($num);
1463 $dirsource = $conf->supplier_proposal->dir_output.'/'.$oldref;
1464 $dirdest = $conf->supplier_proposal->dir_output.'/'.$newref;
1465 if (!$error && file_exists($dirsource)) {
1466 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
1467 if (@rename($dirsource, $dirdest)) {
1468 dol_syslog("Rename ok");
1469 // Rename docs starting with $oldref with $newref
1470 $listoffiles = dol_dir_list($conf->supplier_proposal->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1471 foreach ($listoffiles as $fileentry) {
1472 $dirsource = $fileentry['name'];
1473 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1474 $dirsource = $fileentry['path'].'/'.$dirsource;
1475 $dirdest = $fileentry['path'].'/'.$dirdest;
1476 @rename($dirsource, $dirdest);
1477 }
1478 }
1479 }
1480 }
1481
1482 $this->ref = $num;
1485 $this->user_validation_id = $user->id;
1486 $this->datev = $now;
1487 $this->date_validation = $now;
1488
1489 $this->db->commit();
1490 return 1;
1491 } else {
1492 $this->db->rollback();
1493 return -1;
1494 }
1495 } else {
1496 dol_syslog("You don't have permission to validate supplier proposal", LOG_WARNING);
1497 return -2;
1498 }
1499 }
1500
1501 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1510 public function set_date_livraison($user, $delivery_date)
1511 {
1512 // phpcs:enable
1513 return $this->setDeliveryDate($user, $delivery_date);
1514 }
1515
1523 public function setDeliveryDate($user, $delivery_date)
1524 {
1525 if ($user->hasRight('supplier_proposal', 'creer')) {
1526 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1527 $sql .= " SET date_livraison = ".($delivery_date != '' ? "'".$this->db->idate($delivery_date)."'" : 'null');
1528 $sql .= " WHERE rowid = ".((int) $this->id);
1529
1530 if ($this->db->query($sql)) {
1531 $this->delivery_date = $delivery_date;
1532 return 1;
1533 } else {
1534 $this->error = $this->db->error();
1535 dol_syslog(get_class($this)."::setDeliveryDate Erreur SQL");
1536 return -1;
1537 }
1538 }
1539 return 0;
1540 }
1541
1542 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1550 /*
1551 public function set_remise_percent($user, $remise)
1552 {
1553 // phpcs:enable
1554 $remise = trim($remise) ?trim($remise) : 0;
1555
1556 if ($user->hasRight('supplier_proposal', 'creer')) {
1557 $remise = price2num($remise, 2);
1558
1559 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal SET remise_percent = ".((float) $remise);
1560 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1561
1562 if ($this->db->query($sql)) {
1563 $this->remise_percent = ((float) $remise);
1564 $this->update_price(1);
1565 return 1;
1566 } else {
1567 $this->error = $this->db->error();
1568 return -1;
1569 }
1570 }
1571 return 0;
1572 }
1573 */
1574
1575 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1583 /*
1584 public function set_remise_absolue($user, $remise)
1585 {
1586 // phpcs:enable
1587 if (empty($remise)) {
1588 $remise = 0;
1589 }
1590
1591 $remise = price2num($remise);
1592
1593 if ($user->hasRight('supplier_proposal', 'creer')) {
1594 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1595 $sql .= " SET remise_absolue = ".((float) $remise);
1596 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1597
1598 if ($this->db->query($sql)) {
1599 $this->remise_absolue = $remise;
1600 $this->update_price(1);
1601 return 1;
1602 } else {
1603 $this->error = $this->db->error();
1604 return -1;
1605 }
1606 }
1607 return 0;
1608 }
1609 */
1610
1611
1621 public function reopen($user, $statut, $note = '', $notrigger = 0)
1622 {
1623 global $langs, $conf;
1624
1625 $this->statut = $statut;
1626 $error = 0;
1627
1628 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1629 $sql .= " SET fk_statut = ".((int) $this->statut).",";
1630 if (!empty($note)) {
1631 $sql .= " note_private = '".$this->db->escape($note)."',";
1632 }
1633 $sql .= " date_cloture = NULL, fk_user_cloture = NULL";
1634 $sql .= " WHERE rowid = ".((int) $this->id);
1635
1636 $this->db->begin();
1637
1638 dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
1639 $resql = $this->db->query($sql);
1640 if (!$resql) {
1641 $error++;
1642 $this->errors[] = "Error ".$this->db->lasterror();
1643 }
1644 if (!$error) {
1645 if (!$notrigger) {
1646 // Call trigger
1647 $result = $this->call_trigger('PROPOSAL_SUPPLIER_REOPEN', $user);
1648 if ($result < 0) {
1649 $error++;
1650 }
1651 // End call triggers
1652 }
1653 }
1654
1655 // Commit or rollback
1656 if ($error) {
1657 if (!empty($this->errors)) {
1658 foreach ($this->errors as $errmsg) {
1659 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1660 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1661 }
1662 }
1663 $this->db->rollback();
1664 return -1 * $error;
1665 } else {
1666 $this->db->commit();
1667 return 1;
1668 }
1669 }
1670
1671
1680 public function cloture($user, $status, $note)
1681 {
1682 global $langs, $conf;
1683 $hidedetails = 0;
1684 $hidedesc = 0;
1685 $hideref = 0;
1686 $this->statut = $status;
1687 $error = 0;
1688 $now = dol_now();
1689
1690 $this->db->begin();
1691
1692 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1693 $sql .= " SET fk_statut = ".((int) $status).", note_private = '".$this->db->escape($note)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".$user->id;
1694 $sql .= " WHERE rowid = ".((int) $this->id);
1695
1696 $resql = $this->db->query($sql);
1697 if ($resql) {
1698 $modelpdf = $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED ? $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED : (empty($this->model_pdf) ? '' : $this->model_pdf);
1699 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_REFUSED';
1700
1701 if ($status == 2) {
1702 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_SIGNED';
1703 $modelpdf = $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL ? $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL : (empty($this->model_pdf) ? '' : $this->model_pdf);
1704
1705 if (getDolGlobalString('SUPPLIER_PROPOSAL_UPDATE_PRICE_ON_SUPPlIER_PROPOSAL')) { // TODO This option was not tested correctly. Error if product ref does not exists
1706 $result = $this->updateOrCreatePriceFournisseur($user);
1707 }
1708 }
1709 if ($status == 4) {
1710 $triggerName = 'PROPOSAL_SUPPLIER_CLASSIFY_BILLED';
1711 }
1712
1713 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1714 // Define output language
1715 $outputlangs = $langs;
1716 if (getDolGlobalInt('MAIN_MULTILANGS')) {
1717 $outputlangs = new Translate("", $conf);
1718 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
1719 $outputlangs->setDefaultLang($newlang);
1720 }
1721 //$ret=$object->fetch($id); // Reload to get new records
1722 $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1723 }
1724
1725 // Call trigger
1726 $result = $this->call_trigger($triggerName, $user);
1727 if ($result < 0) {
1728 $error++;
1729 }
1730 // End call triggers
1731
1732 if (!$error) {
1733 $this->db->commit();
1734 return 1;
1735 } else {
1736 $this->db->rollback();
1737 return -1;
1738 }
1739 } else {
1740 $this->error = $this->db->lasterror();
1741 $this->errors[] = $this->db->lasterror();
1742 $this->db->rollback();
1743 return -1;
1744 }
1745 }
1746
1753 public function updateOrCreatePriceFournisseur($user)
1754 {
1755 global $conf;
1756
1757 dol_syslog(get_class($this)."::updateOrCreatePriceFournisseur", LOG_DEBUG);
1758 foreach ($this->lines as $product) {
1759 if ($product->subprice <= 0) {
1760 continue;
1761 }
1762 $productsupplier = new ProductFournisseur($this->db);
1763
1764 $multicurrency_tx = 1;
1765 $fk_multicurrency = 0;
1766
1767 if (empty($this->thirdparty)) {
1768 $this->fetch_thirdparty();
1769 }
1770
1771 $ref_fourn = $product->ref_fourn;
1772 if (empty($ref_fourn)) {
1773 $ref_fourn = $product->ref_supplier;
1774 }
1775 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1776 list($fk_multicurrency, $multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $product->multicurrency_code);
1777 }
1778 $productsupplier->id = $product->fk_product;
1779
1780 $productsupplier->update_buyprice($product->qty, $product->total_ht, $user, 'HT', $this->thirdparty, '', $ref_fourn, $product->tva_tx, 0, 0, 0, $product->info_bits, '', '', array(), '', $product->multicurrency_total_ht, 'HT', $multicurrency_tx, $product->multicurrency_code, '', '', '');
1781 }
1782
1783 return 1;
1784 }
1785
1794 public function updatePriceFournisseur($idProductFournPrice, $product, $user)
1795 {
1796 $price = price2num($product->subprice * $product->qty, 'MU');
1797 $unitPrice = price2num($product->subprice, 'MU');
1798
1799 $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);
1800
1801 $resql = $this->db->query($sql);
1802 if (!$resql) {
1803 $this->error = $this->db->error();
1804 $this->db->rollback();
1805 return -1;
1806 }
1807 return 1;
1808 }
1809
1817 public function createPriceFournisseur($product, $user)
1818 {
1819 global $conf;
1820
1821 $price = price2num($product->subprice * $product->qty, 'MU');
1822 $qty = price2num($product->qty);
1823 $unitPrice = price2num($product->subprice, 'MU');
1824
1825 $now = dol_now();
1826
1827 $values = array(
1828 "'".$this->db->idate($now)."'",
1829 $product->fk_product,
1830 $this->thirdparty->id,
1831 "'".$product->ref_fourn."'",
1832 $price,
1833 $qty,
1834 $unitPrice,
1835 $product->tva_tx,
1836 $user->id
1837 );
1838 if (isModEnabled("multicurrency")) {
1839 if (!empty($product->multicurrency_code)) {
1840 include_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
1841 $multicurrency = new MultiCurrency($this->db); //need to fetch because empty fk_multicurrency and rate
1842 $multicurrency->fetch(0, $product->multicurrency_code);
1843 if (!empty($multicurrency->id)) {
1844 $values[] = $multicurrency->id;
1845 $values[] = "'".$product->multicurrency_code."'";
1846 $values[] = $product->multicurrency_subprice;
1847 $values[] = $product->multicurrency_total_ht;
1848 $values[] = $multicurrency->rate->rate;
1849 } else {
1850 for ($i = 0; $i < 5; $i++) {
1851 $values[] = 'NULL';
1852 }
1853 }
1854 }
1855 }
1856
1857 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'product_fournisseur_price ';
1858 $sql .= '(datec, fk_product, fk_soc, ref_fourn, price, quantity, unitprice, tva_tx, fk_user';
1859 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1860 $sql .= ',fk_multicurrency, multicurrency_code, multicurrency_unitprice, multicurrency_price, multicurrency_tx';
1861 }
1862 $sql .= ') VALUES ('.implode(',', $values).')';
1863
1864 $resql = $this->db->query($sql);
1865 if (!$resql) {
1866 $this->error = $this->db->error();
1867 $this->db->rollback();
1868 return -1;
1869 }
1870 return 1;
1871 }
1872
1873 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1880 public function setDraft($user)
1881 {
1882 // phpcs:enable
1883 global $conf, $langs;
1884
1885 $error = 0;
1886
1887 if ($this->statut == self::STATUS_DRAFT) {
1888 dol_syslog(get_class($this)."::setDraft already draft status", LOG_WARNING);
1889 return 0;
1890 }
1891
1892 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1893 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1894 $sql .= " WHERE rowid = ".((int) $this->id);
1895
1896 if ($this->db->query($sql)) {
1897 if (!$error) {
1898 $this->oldcopy = clone $this;
1899 }
1900
1901 if (!$error) {
1902 // Call trigger
1903 $result = $this->call_trigger('PROPOSAL_SUPPLIER_UNVALIDATE', $user);
1904 if ($result < 0) {
1905 $error++;
1906 }
1907 }
1908
1909 if (!$error) {
1910 $this->status = self::STATUS_DRAFT;
1911 $this->statut = self::STATUS_DRAFT; // deprecated
1912 $this->db->commit();
1913 return 1;
1914 } else {
1915 $this->db->rollback();
1916 return -1;
1917 }
1918 } else {
1919 return -1;
1920 }
1921 }
1922
1923
1924 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1938 public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datec', $sortorder = 'DESC')
1939 {
1940 // phpcs:enable
1941 global $user;
1942
1943 $ga = array();
1944
1945 $search_sale = 0;
1946 if (!$user->hasRight('societe', 'client', 'voir')) {
1947 $search_sale = $user->id;
1948 }
1949
1950 $sql = "SELECT s.rowid, s.nom as name, s.client,";
1951 $sql .= " p.rowid as supplier_proposalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
1952 $sql .= " p.datep as dp, p.fin_validite as datelimite";
1953 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."supplier_proposal as p, ".MAIN_DB_PREFIX."c_propalst as c";
1954 $sql .= " WHERE p.entity IN (".getEntity('supplier_proposal').")";
1955 $sql .= " AND p.fk_soc = s.rowid";
1956 $sql .= " AND p.fk_statut = c.id";
1957 if ($socid) {
1958 $sql .= " AND s.rowid = ".((int) $socid);
1959 }
1960 if ($draft) {
1961 $sql .= " AND p.fk_statut = 0";
1962 }
1963 if ($notcurrentuser > 0) {
1964 $sql .= " AND p.fk_user_author <> ".((int) $user->id);
1965 }
1966 // Search on sale representative
1967 if ($search_sale && $search_sale != '-1') {
1968 if ($search_sale == -2) {
1969 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
1970 } elseif ($search_sale > 0) {
1971 $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).")";
1972 }
1973 }
1974 $sql .= $this->db->order($sortfield, $sortorder);
1975 $sql .= $this->db->plimit($limit, $offset);
1976
1977 $result = $this->db->query($sql);
1978 if ($result) {
1979 $num = $this->db->num_rows($result);
1980 if ($num) {
1981 $i = 0;
1982 while ($i < $num) {
1983 $obj = $this->db->fetch_object($result);
1984
1985 if ($shortlist == 1) {
1986 $ga[$obj->supplier_proposalid] = $obj->ref;
1987 } elseif ($shortlist == 2) {
1988 $ga[$obj->supplier_proposalid] = $obj->ref.' ('.$obj->name.')';
1989 } else {
1990 $ga[$i]['id'] = $obj->supplier_proposalid;
1991 $ga[$i]['ref'] = $obj->ref;
1992 $ga[$i]['name'] = $obj->name;
1993 }
1994
1995 $i++;
1996 }
1997 }
1998 return $ga;
1999 } else {
2000 dol_print_error($this->db);
2001 return -1;
2002 }
2003 }
2004
2012 public function delete($user, $notrigger = 0)
2013 {
2014 global $conf, $langs;
2015 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2016
2017 $error = 0;
2018
2019 $this->db->begin();
2020
2021 if (!$notrigger) {
2022 // Call trigger
2023 $result = $this->call_trigger('PROPOSAL_SUPPLIER_DELETE', $user);
2024 if ($result < 0) {
2025 $error++;
2026 }
2027 // End call triggers
2028 }
2029
2030 if (!$error) {
2031 $main = MAIN_DB_PREFIX.'supplier_proposaldet';
2032 $ef = $main."_extrafields";
2033 $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_supplier_proposal = ".((int) $this->id).")";
2034 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet WHERE fk_supplier_proposal = ".((int) $this->id);
2035 if ($this->db->query($sql)) {
2036 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposal WHERE rowid = ".((int) $this->id);
2037 if ($this->db->query($sqlef) && $this->db->query($sql)) {
2038 // Delete linked object
2039 $res = $this->deleteObjectLinked();
2040 if ($res < 0) {
2041 $error++;
2042 }
2043
2044 if (!$error) {
2045 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2046 $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
2047 $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
2048
2049 // We remove directory
2050 $ref = dol_sanitizeFileName($this->ref);
2051 if ($conf->supplier_proposal->dir_output && !empty($this->ref)) {
2052 $dir = $conf->supplier_proposal->dir_output."/".$ref;
2053 $file = $dir."/".$ref.".pdf";
2054 if (file_exists($file)) {
2055 dol_delete_preview($this);
2056
2057 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2058 $this->error = 'ErrorFailToDeleteFile';
2059 $this->errors = array('ErrorFailToDeleteFile');
2060 $this->db->rollback();
2061 return 0;
2062 }
2063 }
2064 if (file_exists($dir)) {
2065 $res = @dol_delete_dir_recursive($dir);
2066 if (!$res) {
2067 $this->error = 'ErrorFailToDeleteDir';
2068 $this->errors = array('ErrorFailToDeleteDir');
2069 $this->db->rollback();
2070 return 0;
2071 }
2072 }
2073 }
2074 }
2075
2076 // Removed extrafields
2077 if (!$error) {
2078 $result = $this->deleteExtraFields();
2079 if ($result < 0) {
2080 $error++;
2081 $errorflag = -4;
2082 dol_syslog(get_class($this)."::delete erreur ".$errorflag." ".$this->error, LOG_ERR);
2083 }
2084 }
2085
2086 if (!$error) {
2087 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
2088 $this->db->commit();
2089 return 1;
2090 } else {
2091 $this->error = $this->db->lasterror();
2092 $this->db->rollback();
2093 return 0;
2094 }
2095 } else {
2096 $this->error = $this->db->lasterror();
2097 $this->db->rollback();
2098 return -3;
2099 }
2100 } else {
2101 $this->error = $this->db->lasterror();
2102 $this->db->rollback();
2103 return -2;
2104 }
2105 } else {
2106 $this->db->rollback();
2107 return -1;
2108 }
2109 }
2110
2117 public function info($id)
2118 {
2119 $sql = "SELECT c.rowid, ";
2120 $sql .= " c.datec as date_creation, c.date_valid as date_validation, c.date_cloture as date_closure,";
2121 $sql .= " c.fk_user_author, c.fk_user_valid, c.fk_user_cloture";
2122 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as c";
2123 $sql .= " WHERE c.rowid = ".((int) $id);
2124
2125 $result = $this->db->query($sql);
2126
2127 if ($result) {
2128 if ($this->db->num_rows($result)) {
2129 $obj = $this->db->fetch_object($result);
2130
2131 $this->id = $obj->rowid;
2132
2133 $this->date_creation = $this->db->jdate($obj->date_creation);
2134 $this->date_validation = $this->db->jdate($obj->date_validation);
2135 $this->date_cloture = $this->db->jdate($obj->date_closure);
2136
2137 $this->user_creation_id = $obj->fk_user_author;
2138 $this->user_validation_id = $obj->fk_user_valid;
2139 $this->user_closing_id = $obj->fk_user_cloture;
2140 }
2141 $this->db->free($result);
2142 } else {
2143 dol_print_error($this->db);
2144 }
2145 }
2146
2147
2154 public function getLibStatut($mode = 0)
2155 {
2156 return $this->LibStatut((isset($this->statut) ? $this->statut : $this->status), $mode);
2157 }
2158
2159 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2167 public function LibStatut($status, $mode = 1)
2168 {
2169 // phpcs:enable
2170
2171 // Init/load array of translation of status
2172 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
2173 global $langs;
2174 $langs->load("supplier_proposal");
2175 $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraft");
2176 $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidated");
2177 $this->labelStatus[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSigned");
2178 $this->labelStatus[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSigned");
2179 $this->labelStatus[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosed");
2180 $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraftShort");
2181 $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidatedShort");
2182 $this->labelStatusShort[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSignedShort");
2183 $this->labelStatusShort[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSignedShort");
2184 $this->labelStatusShort[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosedShort");
2185 }
2186
2187 $statusnew = '';
2188 if ($status == self::STATUS_DRAFT) {
2189 $statusnew = 'status0';
2190 } elseif ($status == self::STATUS_VALIDATED) {
2191 $statusnew = 'status1';
2192 } elseif ($status == self::STATUS_SIGNED) {
2193 $statusnew = 'status4';
2194 } elseif ($status == self::STATUS_NOTSIGNED) {
2195 $statusnew = 'status9';
2196 } elseif ($status == self::STATUS_CLOSE) {
2197 $statusnew = 'status6';
2198 }
2199
2200 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusnew, $mode);
2201 }
2202
2203
2204 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2212 public function load_board($user, $mode)
2213 {
2214 // phpcs:enable
2215 global $conf, $user, $langs;
2216
2217 $now = dol_now();
2218
2219 $clause = " WHERE";
2220
2221 $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.date_cloture as datefin";
2222 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2223 if (!$user->hasRight('societe', 'client', 'voir')) {
2224 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
2225 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2226 $clause = " AND";
2227 }
2228 $sql .= $clause." p.entity IN (".getEntity('supplier_proposal').")";
2229 if ($mode == 'opened') {
2230 $sql .= " AND p.fk_statut = 1";
2231 }
2232 if ($mode == 'signed') {
2233 $sql .= " AND p.fk_statut = 2";
2234 }
2235 if ($user->socid) {
2236 $sql .= " AND p.fk_soc = ".((int) $user->socid);
2237 }
2238
2239 $resql = $this->db->query($sql);
2240 if ($resql) {
2241 $label = $labelShort = '';
2242 $status = '';
2243 if ($mode == 'opened') {
2244 $delay_warning = !empty($conf->supplier_proposal->cloture->warning_delay) ? $conf->supplier_proposal->cloture->warning_delay : 0;
2245 $status = self::STATUS_VALIDATED;
2246 $label = $langs->trans("SupplierProposalsToClose");
2247 $labelShort = $langs->trans("ToAcceptRefuse");
2248 }
2249 if ($mode == 'signed') {
2250 $delay_warning = !empty($conf->supplier_proposal->facturation->warning_delay) ? $conf->supplier_proposal->facturation->warning_delay : 0;
2251 $status = self::STATUS_SIGNED;
2252 $label = $langs->trans("SupplierProposalsToProcess"); // May be billed or ordered
2253 $labelShort = $langs->trans("ToClose");
2254 }
2255
2256 $response = new WorkboardResponse();
2257 $response->warning_delay = $delay_warning / 60 / 60 / 24;
2258 $response->label = $label;
2259 $response->labelShort = $labelShort;
2260 $response->url = DOL_URL_ROOT.'/supplier_proposal/list.php?search_status='.$status;
2261 $response->img = img_object('', "propal");
2262
2263 // This assignment in condition is not a bug. It allows walking the results.
2264 while ($obj = $this->db->fetch_object($resql)) {
2265 $response->nbtodo++;
2266 if ($mode == 'opened') {
2267 $datelimit = $this->db->jdate($obj->datefin);
2268 if ($datelimit < ($now - $delay_warning)) {
2269 $response->nbtodolate++;
2270 }
2271 }
2272 // TODO Definir regle des propales a facturer en retard
2273 // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
2274 }
2275 return $response;
2276 } else {
2277 $this->error = $this->db->lasterror();
2278 return -1;
2279 }
2280 }
2281
2282
2290 public function initAsSpecimen()
2291 {
2292 global $user, $langs, $conf;
2293
2294 // Load array of products prodids
2295 $num_prods = 0;
2296 $prodids = array();
2297 $sql = "SELECT rowid";
2298 $sql .= " FROM ".MAIN_DB_PREFIX."product";
2299 $sql .= " WHERE entity IN (".getEntity('product').")";
2300 $sql .= $this->db->plimit(100);
2301
2302 $resql = $this->db->query($sql);
2303 if ($resql) {
2304 $num_prods = $this->db->num_rows($resql);
2305 $i = 0;
2306 while ($i < $num_prods) {
2307 $i++;
2308 $row = $this->db->fetch_row($resql);
2309 $prodids[$i] = $row[0];
2310 }
2311 }
2312
2313 // Initialise parameters
2314 $this->id = 0;
2315 $this->ref = 'SPECIMEN';
2316 $this->specimen = 1;
2317 $this->socid = 1;
2318 $this->date = time();
2319 $this->cond_reglement_id = 1;
2320 $this->cond_reglement_code = 'RECEP';
2321 $this->mode_reglement_id = 7;
2322 $this->mode_reglement_code = 'CHQ';
2323 $this->note_public = 'This is a comment (public)';
2324 $this->note_private = 'This is a comment (private)';
2325 // Lines
2326 $nbp = 5;
2327 $xnbp = 0;
2328 while ($xnbp < $nbp) {
2329 $line = new SupplierProposalLine($this->db);
2330 $line->desc = $langs->trans("Description")." ".$xnbp;
2331 $line->qty = 1;
2332 $line->subprice = 100;
2333 $line->tva_tx = 19.6;
2334 $line->localtax1_tx = 0;
2335 $line->localtax2_tx = 0;
2336 if ($xnbp == 2) {
2337 $line->total_ht = 50;
2338 $line->total_ttc = 59.8;
2339 $line->total_tva = 9.8;
2340 $line->remise_percent = 50;
2341 } else {
2342 $line->total_ht = 100;
2343 $line->total_ttc = 119.6;
2344 $line->total_tva = 19.6;
2345 $line->remise_percent = 00;
2346 }
2347
2348 if ($num_prods > 0) {
2349 $prodid = mt_rand(1, $num_prods);
2350 $line->fk_product = $prodids[$prodid];
2351 }
2352
2353 $this->lines[$xnbp] = $line;
2354
2355 $this->total_ht += $line->total_ht;
2356 $this->total_tva += $line->total_tva;
2357 $this->total_ttc += $line->total_ttc;
2358
2359 $xnbp++;
2360 }
2361
2362 return 1;
2363 }
2364
2370 public function loadStateBoard()
2371 {
2372 global $conf, $user;
2373
2374 $this->nb = array();
2375 $clause = "WHERE";
2376
2377 $sql = "SELECT count(p.rowid) as nb";
2378 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2379 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
2380 if (!$user->hasRight('societe', 'client', 'voir')) {
2381 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2382 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2383 $clause = "AND";
2384 }
2385 $sql .= " ".$clause." p.entity IN (".getEntity('supplier_proposal').")";
2386
2387 $resql = $this->db->query($sql);
2388 if ($resql) {
2389 // This assignment in condition is not a bug. It allows walking the results.
2390 while ($obj = $this->db->fetch_object($resql)) {
2391 $this->nb["supplier_proposals"] = $obj->nb;
2392 }
2393 $this->db->free($resql);
2394 return 1;
2395 } else {
2396 dol_print_error($this->db);
2397 $this->error = $this->db->lasterror();
2398 return -1;
2399 }
2400 }
2401
2402
2410 public function getNextNumRef($soc)
2411 {
2412 global $conf, $db, $langs;
2413 $langs->load("supplier_proposal");
2414
2415 if (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON')) {
2416 $mybool = false;
2417
2418 $file = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') . ".php";
2419 $classname = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
2420
2421 // Include file with class
2422 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2423 foreach ($dirmodels as $reldir) {
2424 $dir = dol_buildpath($reldir."core/modules/supplier_proposal/");
2425
2426 // Load file with numbering class (if found)
2427 $mybool = ((bool) @include_once $dir.$file) || $mybool;
2428 }
2429
2430 if (!$mybool) {
2431 dol_print_error(null, "Failed to include file ".$file);
2432 return '';
2433 }
2434
2435 $obj = new $classname();
2436 $numref = "";
2437 $numref = $obj->getNextValue($soc, $this);
2438
2439 if ($numref != "") {
2440 return $numref;
2441 } else {
2442 $this->error = $obj->error;
2443 return "";
2444 }
2445 } else {
2446 $langs->load("errors");
2447 print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("SupplierProposal"));
2448 return "";
2449 }
2450 }
2451
2459 public function getTooltipContentArray($params)
2460 {
2461 global $conf, $langs, $menumanager;
2462
2463 $langs->load('supplier_proposal');
2464
2465 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2466 return ['optimize' => $langs->trans("ShowSupplierProposal")];
2467 }
2468
2469 $option = $params['option'] ?? '';
2470 $datas = [];
2471
2472 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("SupplierProposal").'</u>';
2473 if (isset($this->status)) {
2474 $datas['picto'] .= ' '.$this->getLibStatut(5);
2475 }
2476 if (!empty($this->ref)) {
2477 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2478 }
2479 if (!empty($this->ref_fourn)) {
2480 $datas['ref_supplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_fourn;
2481 }
2482 if (!empty($this->total_ht)) {
2483 $datas['amount_ht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2484 }
2485 if (!empty($this->total_tva)) {
2486 $datas['amount_vat'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2487 }
2488 if (!empty($this->total_ttc)) {
2489 $datas['amount_ttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2490 }
2491
2492 return $datas;
2493 }
2494
2506 public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2507 {
2508 global $langs, $conf, $user, $hookmanager;
2509
2510 if (!empty($conf->dol_no_mouse_hover)) {
2511 $notooltip = 1; // Force disable tooltips
2512 }
2513
2514 $url = '';
2515 $result = '';
2516 $params = [
2517 'id' => $this->id,
2518 'objecttype' => $this->element,
2519 'option' => $option,
2520 ];
2521 $classfortooltip = 'classfortooltip';
2522 $dataparams = '';
2523 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2524 $classfortooltip = 'classforajaxtooltip';
2525 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
2526 $label = '';
2527 } else {
2528 $label = implode($this->getTooltipContentArray($params));
2529 }
2530
2531 if ($option == '') {
2532 $url = DOL_URL_ROOT.'/supplier_proposal/card.php?id='.$this->id.$get_params;
2533 }
2534 if ($option == 'document') {
2535 $url = DOL_URL_ROOT.'/supplier_proposal/document.php?id='.$this->id.$get_params;
2536 }
2537
2538 if ($option !== 'nolink') {
2539 // Add param to save lastsearch_values or not
2540 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2541 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2542 $add_save_lastsearch_values = 1;
2543 }
2544 if ($add_save_lastsearch_values) {
2545 $url .= '&save_lastsearch_values=1';
2546 }
2547 }
2548
2549 $linkclose = '';
2550 if (empty($notooltip) && $user->hasRight('propal', 'lire')) {
2551 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2552 $label = $langs->trans("ShowSupplierProposal");
2553 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
2554 }
2555 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
2556 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
2557 }
2558
2559 $linkstart = '<a href="'.$url.'"';
2560 $linkstart .= $linkclose.'>';
2561 $linkend = '</a>';
2562
2563 $result .= $linkstart;
2564 if ($withpicto) {
2565 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
2566 }
2567 if ($withpicto != 2) {
2568 $result .= $this->ref;
2569 }
2570 $result .= $linkend;
2571
2572 if ($addlinktonotes) {
2573 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2574 if ($txttoshow) {
2575 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2576 $result .= ' <span class="note inline-block">';
2577 $result .= '<a href="'.DOL_URL_ROOT.'/supplier_proposal/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2578 $result .= img_picto('', 'note');
2579 $result .= '</a>';
2580 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
2581 //$result.='</a>';
2582 $result .= '</span>';
2583 }
2584 }
2585 global $action;
2586 $hookmanager->initHooks(array($this->element . 'dao'));
2587 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
2588 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2589 if ($reshook > 0) {
2590 $result = $hookmanager->resPrint;
2591 } else {
2592 $result .= $hookmanager->resPrint;
2593 }
2594 return $result;
2595 }
2596
2602 public function getLinesArray()
2603 {
2604 // For other object, here we call fetch_lines. But fetch_lines does not exists on supplier proposal
2605
2606 $sql = 'SELECT pt.rowid, pt.label as custom_label, pt.description, pt.fk_product, pt.fk_remise_except,';
2607 $sql .= ' pt.qty, pt.tva_tx, pt.vat_src_code, pt.remise_percent, pt.subprice, pt.info_bits,';
2608 $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,';
2609 $sql .= ' pt.product_type, pt.rang, pt.fk_parent_line,';
2610 $sql .= ' p.label as product_label, p.ref, p.fk_product_type, p.rowid as prodid,';
2611 $sql .= ' p.description as product_desc, pt.ref_fourn as ref_supplier,';
2612 $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';
2613 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pt';
2614 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pt.fk_product=p.rowid';
2615 $sql .= ' WHERE pt.fk_supplier_proposal = '.((int) $this->id);
2616 $sql .= ' ORDER BY pt.rang ASC, pt.rowid';
2617
2618 dol_syslog(get_class($this).'::getLinesArray', LOG_DEBUG);
2619 $resql = $this->db->query($sql);
2620 if ($resql) {
2621 $num = $this->db->num_rows($resql);
2622 $i = 0;
2623
2624 while ($i < $num) {
2625 $obj = $this->db->fetch_object($resql);
2626
2627 $this->lines[$i] = new SupplierProposalLine($this->db);
2628 $this->lines[$i]->id = $obj->rowid; // for backward compatibility
2629 $this->lines[$i]->rowid = $obj->rowid;
2630 $this->lines[$i]->label = $obj->custom_label;
2631 $this->lines[$i]->description = $obj->description;
2632 $this->lines[$i]->fk_product = $obj->fk_product;
2633 $this->lines[$i]->ref = $obj->ref;
2634 $this->lines[$i]->product_label = $obj->product_label;
2635 $this->lines[$i]->product_desc = $obj->product_desc;
2636 $this->lines[$i]->fk_product_type = $obj->fk_product_type; // deprecated
2637 $this->lines[$i]->product_type = $obj->product_type;
2638 $this->lines[$i]->qty = $obj->qty;
2639 $this->lines[$i]->subprice = $obj->subprice;
2640 $this->lines[$i]->fk_remise_except = $obj->fk_remise_except;
2641 $this->lines[$i]->remise_percent = $obj->remise_percent;
2642 $this->lines[$i]->tva_tx = $obj->tva_tx;
2643 $this->lines[$i]->vat_src_code = $obj->vat_src_code;
2644 $this->lines[$i]->info_bits = $obj->info_bits;
2645 $this->lines[$i]->total_ht = $obj->total_ht;
2646 $this->lines[$i]->total_tva = $obj->total_tva;
2647 $this->lines[$i]->total_ttc = $obj->total_ttc;
2648 $this->lines[$i]->fk_fournprice = $obj->fk_fournprice;
2649 $marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->lines[$i]->fk_fournprice, $obj->pa_ht);
2650 $this->lines[$i]->pa_ht = $marginInfos[0];
2651 $this->lines[$i]->marge_tx = $marginInfos[1];
2652 $this->lines[$i]->marque_tx = $marginInfos[2];
2653 $this->lines[$i]->fk_parent_line = $obj->fk_parent_line;
2654 $this->lines[$i]->special_code = $obj->special_code;
2655 $this->lines[$i]->rang = $obj->rang;
2656
2657 $this->lines[$i]->ref_fourn = $obj->ref_supplier; // deprecated
2658 $this->lines[$i]->ref_supplier = $obj->ref_supplier;
2659
2660 // Multicurrency
2661 $this->lines[$i]->fk_multicurrency = $obj->fk_multicurrency;
2662 $this->lines[$i]->multicurrency_code = $obj->multicurrency_code;
2663 $this->lines[$i]->multicurrency_subprice = $obj->multicurrency_subprice;
2664 $this->lines[$i]->multicurrency_total_ht = $obj->multicurrency_total_ht;
2665 $this->lines[$i]->multicurrency_total_tva = $obj->multicurrency_total_tva;
2666 $this->lines[$i]->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
2667 $this->lines[$i]->fk_unit = $obj->fk_unit;
2668
2669 $i++;
2670 }
2671 $this->db->free($resql);
2672
2673 return 1;
2674 } else {
2675 $this->error = $this->db->error();
2676 return -1;
2677 }
2678 }
2679
2691 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2692 {
2693 global $conf, $langs;
2694
2695 $langs->load("supplier_proposal");
2696 $outputlangs->load("products");
2697
2698 if (!dol_strlen($modele)) {
2699 $modele = 'aurore';
2700
2701 if ($this->model_pdf) {
2702 $modele = $this->model_pdf;
2703 } elseif (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF')) {
2704 $modele = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF');
2705 }
2706 }
2707
2708 $modelpath = "core/modules/supplier_proposal/doc/";
2709
2710 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2711 }
2712
2713
2722 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2723 {
2724 $tables = array(
2725 'supplier_proposal'
2726 );
2727
2728 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2729 }
2730
2739 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
2740 {
2741 $tables = array(
2742 'supplier_proposaldet'
2743 );
2744
2745 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
2746 }
2747
2748
2756 public function getKanbanView($option = '', $arraydata = null)
2757 {
2758 global $langs;
2759
2760 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2761
2762 $return = '<div class="box-flex-item box-flex-grow-zero">';
2763 $return .= '<div class="info-box info-box-sm">';
2764 $return .= '<span class="info-box-icon bg-infobox-action">';
2765 $return .= img_picto('', $this->picto);
2766 //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2767 $return .= '</span>';
2768 $return .= '<div class="info-box-content">';
2769 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
2770 if ($selected >= 0) {
2771 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2772 }
2773 if (property_exists($this, 'socid')) {
2774 $return .= '<span class="info-box-ref"> | '.$this->socid.'</span>';
2775 }
2776 if (property_exists($this, 'delivery_date')) {
2777 $return .= '<br><span class="opacitymedium">'.$langs->trans("DateEnd").'</span> : <span class="info-box-label">'.dol_print_date($this->delivery_date).'</span>';
2778 }
2779 if (property_exists($this, 'total_ttc')) {
2780 $return .= '<br><span class="opacitymedium" >'.$langs->trans("AmountHT").' : </span><span class="info-box-label amount">'.price($this->total_ttc).'</span>';
2781 }
2782 if (method_exists($this, 'getLibStatut')) {
2783 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
2784 }
2785 $return .= '</div>';
2786 $return .= '</div>';
2787 $return .= '</div>';
2788 return $return;
2789 }
2790}
2791
2792
2797{
2801 public $db;
2802
2806 public $error = '';
2807
2811 public $element = 'supplier_proposaldet';
2812
2816 public $table_element = 'supplier_proposaldet';
2817
2821 public $parent_element = 'supplier_proposal';
2822
2826 public $fk_parent_attribute = 'fk_supplier_proposal';
2827
2828 public $oldline;
2829
2833 public $id;
2834
2838 public $fk_supplier_proposal;
2839
2843 public $fk_parent_line;
2844
2845 public $desc; // Description ligne
2846
2850 public $fk_product; // Id produit predefini
2851
2862 public $product_type = Product::TYPE_PRODUCT;
2863
2867 public $qty;
2868 public $tva_tx;
2869 public $vat_src_code;
2870
2875 public $subprice;
2876 public $remise_percent;
2877
2881 public $fk_remise_except;
2882
2883 public $rang = 0;
2884
2888 public $fk_fournprice;
2889
2890 public $pa_ht;
2891 public $marge_tx;
2892 public $marque_tx;
2893
2897 public $special_code; // Tag for special lines (exclusive tags)
2898 // 1: frais de port
2899 // 2: ecotaxe
2900 // 3: option line (when qty = 0)
2901
2902 public $info_bits = 0; // Liste d'options cumulables:
2903 // Bit 0: 0 si TVA normal - 1 if TVA NPR
2904 // Bit 1: 0 ligne normal - 1 if fixed reduction
2905
2906 public $total_ht; // Total HT de la ligne toute quantite et incluant la remise ligne
2907 public $total_tva; // Total TVA de la ligne toute quantite et incluant la remise ligne
2908 public $total_ttc; // Total TTC de la ligne toute quantite et incluant la remise ligne
2909
2910 public $date_start;
2911 public $date_end;
2912
2913 // From llx_product
2918 public $ref;
2919
2924 public $product_ref;
2925
2930 public $libelle;
2931
2936 public $product_label;
2937
2942 public $label;
2943
2948 public $product_desc;
2949
2950 public $localtax1_tx; // Local tax 1
2951 public $localtax2_tx; // Local tax 2
2952 public $localtax1_type; // Local tax 1 type
2953 public $localtax2_type; // Local tax 2 type
2954 public $total_localtax1; // Line total local tax 1
2955 public $total_localtax2; // Line total local tax 2
2956
2957 public $skip_update_total; // Skip update price total for special lines
2958
2959 public $ref_fourn;
2960 public $ref_supplier;
2961
2962 // Multicurrency
2966 public $fk_multicurrency;
2967
2968 public $multicurrency_code;
2969 public $multicurrency_subprice;
2970 public $multicurrency_total_ht;
2971 public $multicurrency_total_tva;
2972 public $multicurrency_total_ttc;
2973
2979 public function __construct($db)
2980 {
2981 $this->db = $db;
2982 }
2983
2990 public function fetch($rowid)
2991 {
2992 $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,';
2993 $sql .= ' pd.date_start, pd.date_end,';
2994 $sql .= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice,';
2995 $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,';
2996 $sql .= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
2997 $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
2998 $sql .= ' pd.product_type, pd.ref_fourn as ref_produit_fourn,';
2999 $sql .= ' pd.fk_multicurrency, pd.multicurrency_code, pd.multicurrency_subprice, pd.multicurrency_total_ht, pd.multicurrency_total_tva, pd.multicurrency_total_ttc, pd.fk_unit';
3000 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pd';
3001 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
3002 $sql .= ' WHERE pd.rowid = '.((int) $rowid);
3003
3004 $result = $this->db->query($sql);
3005 if ($result) {
3006 $objp = $this->db->fetch_object($result);
3007
3008 $this->id = $objp->rowid;
3009 $this->fk_supplier_proposal = $objp->fk_supplier_proposal;
3010 $this->fk_parent_line = $objp->fk_parent_line;
3011 $this->label = $objp->custom_label;
3012 $this->desc = $objp->description;
3013 $this->qty = $objp->qty;
3014 $this->subprice = $objp->subprice;
3015 $this->tva_tx = $objp->tva_tx;
3016 $this->remise_percent = $objp->remise_percent;
3017 $this->fk_remise_except = $objp->fk_remise_except;
3018 $this->fk_product = $objp->fk_product;
3019 $this->info_bits = $objp->info_bits;
3020 $this->date_start = $this->db->jdate($objp->date_start);
3021 $this->date_end = $this->db->jdate($objp->date_end);
3022
3023 $this->total_ht = $objp->total_ht;
3024 $this->total_tva = $objp->total_tva;
3025 $this->total_ttc = $objp->total_ttc;
3026
3027 $this->fk_fournprice = $objp->fk_fournprice;
3028
3029 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
3030 $this->pa_ht = $marginInfos[0];
3031 $this->marge_tx = $marginInfos[1];
3032 $this->marque_tx = $marginInfos[2];
3033
3034 $this->special_code = $objp->special_code;
3035 $this->product_type = $objp->product_type;
3036 $this->rang = $objp->rang;
3037
3038 $this->ref = $objp->product_ref; // deprecated
3039 $this->product_ref = $objp->product_ref;
3040 $this->libelle = $objp->product_label; // deprecated
3041 $this->product_label = $objp->product_label;
3042 $this->product_desc = $objp->product_desc;
3043
3044 $this->ref_fourn = $objp->ref_produit_fourn;
3045
3046 // Multicurrency
3047 $this->fk_multicurrency = $objp->fk_multicurrency;
3048 $this->multicurrency_code = $objp->multicurrency_code;
3049 $this->multicurrency_subprice = $objp->multicurrency_subprice;
3050 $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3051 $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3052 $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3053 $this->fk_unit = $objp->fk_unit;
3054
3055 $this->db->free($result);
3056 return 1;
3057 } else {
3058 dol_print_error($this->db);
3059 return -1;
3060 }
3061 }
3062
3069 public function insert($notrigger = 0)
3070 {
3071 global $conf, $langs, $user;
3072
3073 $error = 0;
3074
3075 dol_syslog(get_class($this)."::insert rang=".$this->rang);
3076
3077 // Clean parameters
3078 if (empty($this->tva_tx)) {
3079 $this->tva_tx = 0;
3080 }
3081 if (empty($this->vat_src_code)) {
3082 $this->vat_src_code = '';
3083 }
3084 if (empty($this->localtax1_tx)) {
3085 $this->localtax1_tx = 0;
3086 }
3087 if (empty($this->localtax2_tx)) {
3088 $this->localtax2_tx = 0;
3089 }
3090 if (empty($this->localtax1_type)) {
3091 $this->localtax1_type = 0;
3092 }
3093 if (empty($this->localtax2_type)) {
3094 $this->localtax2_type = 0;
3095 }
3096 if (empty($this->total_localtax1)) {
3097 $this->total_localtax1 = 0;
3098 }
3099 if (empty($this->total_localtax2)) {
3100 $this->total_localtax2 = 0;
3101 }
3102 if (empty($this->rang)) {
3103 $this->rang = 0;
3104 }
3105 if (empty($this->remise_percent)) {
3106 $this->remise_percent = 0;
3107 }
3108 if (empty($this->info_bits)) {
3109 $this->info_bits = 0;
3110 }
3111 if (empty($this->special_code)) {
3112 $this->special_code = 0;
3113 }
3114 if (empty($this->fk_parent_line)) {
3115 $this->fk_parent_line = 0;
3116 }
3117 if (empty($this->fk_fournprice)) {
3118 $this->fk_fournprice = 0;
3119 }
3120 if (empty($this->fk_unit)) {
3121 $this->fk_unit = 0;
3122 }
3123 if (empty($this->subprice)) {
3124 $this->subprice = 0;
3125 }
3126
3127 if (empty($this->pa_ht)) {
3128 $this->pa_ht = 0;
3129 }
3130
3131 // if buy price not defined, define buyprice as configured in margin admin
3132 if ($this->pa_ht == 0) {
3133 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3134 if ($result < 0) {
3135 return $result;
3136 } else {
3137 $this->pa_ht = $result;
3138 }
3139 }
3140
3141 // Check parameters
3142 if ($this->product_type < 0) {
3143 return -1;
3144 }
3145
3146 $this->db->begin();
3147
3148 // Insert line into database
3149 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'supplier_proposaldet';
3150 $sql .= ' (fk_supplier_proposal, fk_parent_line, label, description, fk_product, product_type,';
3151 $sql .= ' date_start, date_end,';
3152 $sql .= ' fk_remise_except, qty, tva_tx, vat_src_code, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3153 $sql .= ' subprice, remise_percent, ';
3154 $sql .= ' info_bits, ';
3155 $sql .= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
3156 $sql .= ' ref_fourn,';
3157 $sql .= ' fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc, fk_unit)';
3158 $sql .= " VALUES (".$this->fk_supplier_proposal.",";
3159 $sql .= " ".($this->fk_parent_line > 0 ? ((int) $this->fk_parent_line) : "null").",";
3160 $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
3161 $sql .= " '".$this->db->escape($this->desc)."',";
3162 $sql .= " ".($this->fk_product ? ((int) $this->fk_product) : "null").",";
3163 $sql .= " '".$this->db->escape($this->product_type)."',";
3164 $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3165 $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3166 $sql .= " ".($this->fk_remise_except ? ((int) $this->fk_remise_except) : "null").",";
3167 $sql .= " ".price2num($this->qty, 'MS').",";
3168 $sql .= " ".price2num($this->tva_tx).",";
3169 $sql .= " '".$this->db->escape($this->vat_src_code)."',";
3170 $sql .= " ".price2num($this->localtax1_tx).",";
3171 $sql .= " ".price2num($this->localtax2_tx).",";
3172 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3173 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3174 $sql .= " ".price2num($this->subprice, 'MU') .",";
3175 $sql .= " ".((float) $this->remise_percent).",";
3176 $sql .= " ".(isset($this->info_bits) ? ((int) $this->info_bits) : "null").",";
3177 $sql .= " ".price2num($this->total_ht, 'MT').",";
3178 $sql .= " ".price2num($this->total_tva, 'MT').",";
3179 $sql .= " ".price2num($this->total_localtax1, 'MT').",";
3180 $sql .= " ".price2num($this->total_localtax2, 'MT').",";
3181 $sql .= " ".price2num($this->total_ttc, 'MT').",";
3182 $sql .= " ".(!empty($this->fk_fournprice) ? ((int) $this->fk_fournprice) : "null").",";
3183 $sql .= " ".(isset($this->pa_ht) ? price2num($this->pa_ht, 'MU') : "null").",";
3184 $sql .= ' '.((int) $this->special_code).',';
3185 $sql .= ' '.((int) $this->rang).',';
3186 $sql .= " '".$this->db->escape($this->ref_fourn)."'";
3187 $sql .= ", ".($this->fk_multicurrency > 0 ? ((int) $this->fk_multicurrency) : 'null');
3188 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3189 $sql .= ", ".price2num($this->multicurrency_subprice, 'CU');
3190 $sql .= ", ".price2num($this->multicurrency_total_ht, 'CT');
3191 $sql .= ", ".price2num($this->multicurrency_total_tva, 'CT');
3192 $sql .= ", ".price2num($this->multicurrency_total_ttc, 'CT');
3193 $sql .= ", ".($this->fk_unit ? ((int) $this->fk_unit) : 'null');
3194 $sql .= ')';
3195
3196 dol_syslog(get_class($this).'::insert', LOG_DEBUG);
3197 $resql = $this->db->query($sql);
3198 if ($resql) {
3199 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'supplier_proposaldet');
3200
3201 if (!$error) {
3202 $result = $this->insertExtraFields();
3203 if ($result < 0) {
3204 $error++;
3205 }
3206 }
3207
3208 if (!$error && !$notrigger) {
3209 // Call trigger
3210 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_INSERT', $user);
3211 if ($result < 0) {
3212 $this->db->rollback();
3213 return -1;
3214 }
3215 // End call triggers
3216 }
3217
3218 $this->db->commit();
3219 return 1;
3220 } else {
3221 $this->error = $this->db->error()." sql=".$sql;
3222 $this->db->rollback();
3223 return -1;
3224 }
3225 }
3226
3233 public function delete($user)
3234 {
3235 $error = 0;
3236
3237 $this->db->begin();
3238
3239 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet";
3240 $sql .= " WHERE rowid = ".((int) $this->id);
3241
3242 if ($this->db->query($sql)) {
3243 // Remove extrafields
3244 if (!$error) {
3245 $result = $this->deleteExtraFields();
3246 if ($result < 0) {
3247 $error++;
3248 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3249 }
3250 }
3251
3252 // Call trigger
3253 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_DELETE', $user);
3254 if ($result < 0) {
3255 $this->db->rollback();
3256 return -1;
3257 }
3258 // End call triggers
3259
3260 $this->db->commit();
3261
3262 return 1;
3263 } else {
3264 $this->error = $this->db->error()." sql=".$sql;
3265 $this->db->rollback();
3266 return -1;
3267 }
3268 }
3269
3276 public function update($notrigger = 0)
3277 {
3278 global $conf, $langs, $user;
3279
3280 $error = 0;
3281
3282 // Clean parameters
3283 if (empty($this->tva_tx)) {
3284 $this->tva_tx = 0;
3285 }
3286 if (empty($this->localtax1_tx)) {
3287 $this->localtax1_tx = 0;
3288 }
3289 if (empty($this->localtax2_tx)) {
3290 $this->localtax2_tx = 0;
3291 }
3292 if (empty($this->total_localtax1)) {
3293 $this->total_localtax1 = 0;
3294 }
3295 if (empty($this->total_localtax2)) {
3296 $this->total_localtax2 = 0;
3297 }
3298 if (empty($this->localtax1_type)) {
3299 $this->localtax1_type = 0;
3300 }
3301 if (empty($this->localtax2_type)) {
3302 $this->localtax2_type = 0;
3303 }
3304 if (empty($this->marque_tx)) {
3305 $this->marque_tx = 0;
3306 }
3307 if (empty($this->marge_tx)) {
3308 $this->marge_tx = 0;
3309 }
3310 if (empty($this->remise_percent)) {
3311 $this->remise_percent = 0;
3312 }
3313 if (empty($this->info_bits)) {
3314 $this->info_bits = 0;
3315 }
3316 if (empty($this->special_code)) {
3317 $this->special_code = 0;
3318 }
3319 if (empty($this->fk_parent_line)) {
3320 $this->fk_parent_line = 0;
3321 }
3322 if (empty($this->fk_fournprice)) {
3323 $this->fk_fournprice = 0;
3324 }
3325 if (empty($this->fk_unit)) {
3326 $this->fk_unit = 0;
3327 }
3328 if (empty($this->subprice)) {
3329 $this->subprice = 0;
3330 }
3331
3332 if (empty($this->pa_ht)) {
3333 $this->pa_ht = 0;
3334 }
3335
3336 // if buy price not defined, define buyprice as configured in margin admin
3337 if ($this->pa_ht == 0) {
3338 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3339 if ($result < 0) {
3340 return $result;
3341 } else {
3342 $this->pa_ht = $result;
3343 }
3344 }
3345
3346 $this->db->begin();
3347
3348 // Mise a jour ligne en base
3349 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3350 $sql .= " description='".$this->db->escape($this->desc)."'";
3351 $sql .= " , label=".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
3352 $sql .= " , product_type=".((int) $this->product_type);
3353 $sql .= " , date_start=".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null");
3354 $sql .= " , date_end=".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null");
3355 $sql .= " , tva_tx='".price2num($this->tva_tx)."'";
3356 $sql .= " , localtax1_tx=".price2num($this->localtax1_tx);
3357 $sql .= " , localtax2_tx=".price2num($this->localtax2_tx);
3358 $sql .= " , localtax1_type='".$this->db->escape($this->localtax1_type)."'";
3359 $sql .= " , localtax2_type='".$this->db->escape($this->localtax2_type)."'";
3360 $sql .= " , qty='".price2num($this->qty)."'";
3361 $sql .= " , subprice=".price2num($this->subprice);
3362 $sql .= " , remise_percent=".price2num($this->remise_percent);
3363 $sql .= " , info_bits='".$this->db->escape($this->info_bits)."'";
3364 if (empty($this->skip_update_total)) {
3365 $sql .= " , total_ht=".price2num($this->total_ht);
3366 $sql .= " , total_tva=".price2num($this->total_tva);
3367 $sql .= " , total_ttc=".price2num($this->total_ttc);
3368 $sql .= " , total_localtax1=".price2num($this->total_localtax1);
3369 $sql .= " , total_localtax2=".price2num($this->total_localtax2);
3370 }
3371 $sql .= " , fk_product_fournisseur_price=".(!empty($this->fk_fournprice) ? "'".$this->db->escape($this->fk_fournprice)."'" : "null");
3372 $sql .= " , buy_price_ht=".price2num($this->pa_ht);
3373 $sql .= " , special_code=".((int) $this->special_code);
3374 $sql .= " , fk_parent_line=".($this->fk_parent_line > 0 ? $this->fk_parent_line : "null");
3375 if (!empty($this->rang)) {
3376 $sql .= ", rang=".((int) $this->rang);
3377 }
3378 $sql .= " , ref_fourn=".(!empty($this->ref_fourn) ? "'".$this->db->escape($this->ref_fourn)."'" : "null");
3379 $sql .= " , fk_unit=".($this->fk_unit ? $this->fk_unit : 'null');
3380
3381 // Multicurrency
3382 $sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice);
3383 $sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
3384 $sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
3385 $sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
3386
3387 $sql .= " WHERE rowid = ".((int) $this->id);
3388
3389 dol_syslog(get_class($this)."::update", LOG_DEBUG);
3390 $resql = $this->db->query($sql);
3391 if ($resql) {
3392 if (!$error) {
3393 $result = $this->insertExtraFields();
3394 if ($result < 0) {
3395 $error++;
3396 }
3397 }
3398
3399 if (!$error && !$notrigger) {
3400 // Call trigger
3401 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_MODIFY', $user);
3402 if ($result < 0) {
3403 $this->db->rollback();
3404 return -1;
3405 }
3406 // End call triggers
3407 }
3408
3409 $this->db->commit();
3410 return 1;
3411 } else {
3412 $this->error = $this->db->error();
3413 $this->db->rollback();
3414 return -2;
3415 }
3416 }
3417
3418 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3425 public function update_total()
3426 {
3427 // phpcs:enable
3428 $this->db->begin();
3429
3430 // Mise a jour ligne en base
3431 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3432 $sql .= " total_ht=".price2num($this->total_ht, 'MT');
3433 $sql .= ",total_tva=".price2num($this->total_tva, 'MT');
3434 $sql .= ",total_ttc=".price2num($this->total_ttc, 'MT');
3435 $sql .= " WHERE rowid = ".((int) $this->id);
3436
3437 dol_syslog("SupplierProposalLine::update_total", LOG_DEBUG);
3438
3439 $resql = $this->db->query($sql);
3440 if ($resql) {
3441 $this->db->commit();
3442 return 1;
3443 } else {
3444 $this->error = $this->db->error();
3445 $this->db->rollback();
3446 return -2;
3447 }
3448 }
3449}
print $langs trans("AuditedSecurityEvents").'</strong >< span class="opacitymedium"></span >< br > status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition security.php:636
$object ref
Definition info.php:79
Parent class of all other business classes (invoices, contracts, proposals, orders,...
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
Save a new position (field rang) for details lines.
deleteEcmFiles($mode=0)
Delete related files of object in database.
update_price($exclspec=0, $roundingadjust='auto', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
add_object_linked($origin=null, $origin_id=null, $f_user=null, $notrigger=0)
Add an object link into llx_element_element.
defineBuyPrice($unitPrice=0.0, $discountPercent=0.0, $fk_product=0)
Get buy price to use for margin calculation.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty.
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid=0, $f_user=null, $notrigger=0)
Delete all links between an object $this.
setErrorsFromObject($object)
setErrorsFromObject
static isExistingObject($element, $id, $ref='', $ref_ext='')
Check if an object id or ref exists If you don't need or want to instantiate the object and just need...
updateRangOfLine($rowid, $rang)
Update position of line (rang)
deleteExtraFields()
Delete all extra fields values for the current object.
static commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
static commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product id with another one.
line_max($fk_parent_line=0)
Get max value used for position of line (rang)
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
call_trigger($triggerName, $user)
Call trigger based on this instance.
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
Class to manage absolute discounts.
Class to manage Dolibarr database access.
Class Currency.
static getIdAndTxFromCode($dbs, $code, $date_document='')
Get id and rate of currency from code.
Class to manage predefined suppliers products.
Class to manage products or services.
const TYPE_PRODUCT
Regular product.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage price ask supplier.
updatePriceFournisseur($idProductFournPrice, $product, $user)
Update ProductFournisseur.
updateline($rowid, $pu, $qty, $remise_percent, $txtva, $txlocaltax1=0, $txlocaltax2=0, $desc='', $price_base_type='HT', $info_bits=0, $special_code=0, $fk_parent_line=0, $skip_update_total=0, $fk_fournprice=0, $pa_ht=0, $label='', $type=0, $array_options=[], $ref_supplier='', $fk_unit=0, $pu_ht_devise=0)
Update a proposal line.
cloture($user, $status, $note)
Close the askprice.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template module.
addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1=0, $txlocaltax2=0, $fk_product=0, $remise_percent=0, $price_base_type='HT', $pu_ttc=0, $info_bits=0, $type=0, $rang=-1, $special_code=0, $fk_parent_line=0, $fk_fournprice=0, $pa_ht=0, $label='', $array_options=[], $ref_supplier='', $fk_unit=0, $origin='', $origin_id=0, $pu_ht_devise=0, $date_start=0, $date_end=0)
Add a proposal line into database (linked to product/service or not) Les parameters sont deja cense e...
info($id)
Object SupplierProposal Information.
create($user, $notrigger=0)
Create commercial proposal into database this->ref can be set or empty.
insert_discount($idremise)
Adding line of fixed discount in the proposal in DB.
static replaceProduct(DoliDB $db, $origin_id, $dest_id)
Function used to replace a product id with another one.
getNextNumRef($soc)
Returns the reference to the following non used Proposal used depending on the active numbering modul...
fetch($rowid, $ref='')
Load a proposal from database and its ligne array.
load_board($user, $mode)
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
createFromClone(User $user, $fromid=0)
Load an object from its id and create a new one in database.
initAsSpecimen()
Initialise an instance with random values.
const STATUS_NOTSIGNED
Not signed quote, canceled.
const STATUS_DRAFT
Draft status.
liste_array($shortlist=0, $draft=0, $notcurrentuser=0, $socid=0, $limit=0, $offset=0, $sortfield='p.datec', $sortorder='DESC')
Return list of askprice (eventually filtered on user) into an array.
setDeliveryDate($user, $delivery_date)
Set delivery date.
__construct($db, $socid=0, $supplier_proposalid=0)
Constructor.
LibStatut($status, $mode=1)
Return label of a status (draft, validated, ...)
static replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
updateOrCreatePriceFournisseur($user)
Add or update supplier price according to result of proposal.
reopen($user, $statut, $note='', $notrigger=0)
Set an overall discount on the proposal.
loadStateBoard()
Load indicator this->nb of global stats widget.
deleteLine($lineid)
Delete detail line.
const STATUS_VALIDATED
Validated status.
createPriceFournisseur($product, $user)
Create ProductFournisseur.
getLibStatut($mode=0)
Return label of status of proposal (draft, validated, ...)
add_product($idproduct, $qty, $remise_percent=0)
Add line into array ->lines.
set_date_livraison($user, $delivery_date)
Set delivery date.
const STATUS_SIGNED
Signed quote.
getNomUrl($withpicto=0, $option='', $get_params='', $notooltip=0, $save_lastsearch_value=-1, $addlinktonotes=0)
Return clicable link of object (with eventually picto)
getKanbanView($option='', $arraydata=null)
Return clicable 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.
trait CommonIncoterm
Superclass for incoterm classes.
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1, $nolog=0)
Remove a file or several files with a mask.
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:63
dol_delete_preview($object)
Delete all preview files linked to object instance.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that returns whether VAT must be recoverable collected VAT (e.g.: VAT NPR in France)
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that return vat rate of a product line (according to seller, buyer and product vat rate) VAT...
get_localtax($vatrate, $local, $thirdparty_buyer=null, $thirdparty_seller=null, $vatnpr=0)
Return localtax rate for a particular vat, when selling a product with vat $vatrate,...
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
getMarginInfos($pv_ht, $remise_percent, $tva_tx, $localtax1_tx, $localtax2_tx, $fk_pa, $pa_ht)
Return an array with margins information of a line.
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller='', $localtaxes_array=[], $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition price.lib.php:88
publicphonebutton2 phonegreen basiclayout basiclayout TotalHT VATCode TotalVAT TotalLT1 TotalLT2 TotalTTC TotalHT clearboth nowraponall TAKEPOS_SHOW_SUBPRICE right right right takeposterminal SELECT e e e e e statut
Definition invoice.php:1991