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