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