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