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