dolibarr 18.0.6
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 public $statut; // 0 (draft), 1 (validated), 2 (signed), 3 (not signed), 4 (processed/billed)
107
111 public $date;
112
117 public $date_livraison;
118
122 public $delivery_date;
123
128 public $datec;
129
133 public $date_creation;
134
139 public $datev;
140
144 public $date_validation;
145
146
147 public $user_author_id;
148 public $user_valid_id;
149 public $user_close_id;
150
155 public $price;
156
161 public $tva;
162
167 public $total;
168
169 public $cond_reglement_code;
170 public $mode_reglement_code;
171 public $remise = 0;
172 public $remise_percent = 0;
173 public $remise_absolue = 0;
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 (!empty($conf->global->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->fk_soc ? $this->fk_soc : $this->socid)); // Search on couple $fk_prod_fourn_price/$qty first, then on triplet $qty/$fk_product/$ref_supplier/$this->fk_soc
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->error = $this->error();
638 $this->errors = $this->errors();
639 $this->db->rollback();
640 return -1;
641 }
642 } else {
643 $this->error = $this->line->error;
644 $this->errors = $this->line->errors;
645 $this->db->rollback();
646 return -2;
647 }
648 } else {
649 $this->error = 'BadStatusOfObjectToAddLine';
650 return -5;
651 }
652 }
653
654
681 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)
682 {
683 global $conf, $user, $langs, $mysoc;
684
685 dol_syslog(get_class($this)."::updateLine $rowid, $pu, $qty, $remise_percent, $txtva, $desc, $price_base_type, $info_bits");
686 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
687
688 // Clean parameters
689 $remise_percent = price2num($remise_percent);
690 $qty = price2num($qty);
691 $pu = price2num($pu);
692 if (!preg_match('/\‍((.*)\‍)/', $txtva)) {
693 $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
694 }
695 $txlocaltax1 = price2num($txlocaltax1);
696 $txlocaltax2 = price2num($txlocaltax2);
697 $pa_ht = price2num($pa_ht);
698 if (empty($qty) && empty($special_code)) {
699 $special_code = 3; // Set option tag
700 }
701 if (!empty($qty) && $special_code == 3) {
702 $special_code = 0; // Remove option tag
703 }
704
705 if ($this->statut == 0) {
706 $this->db->begin();
707
708 // Calcul du total TTC et de la TVA pour la ligne a partir de
709 // qty, pu, remise_percent et txtva
710 // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
711 // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
712
713 $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
714
715 // Clean vat code
716 $reg = array();
717 $vat_src_code = '';
718 if (preg_match('/\‍((.*)\‍)/', $txtva, $reg)) {
719 $vat_src_code = $reg[1];
720 $txtva = preg_replace('/\s*\‍(.*\‍)/', '', $txtva); // Remove code into vatrate.
721 }
722
723 if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
724 $pu = 0;
725 }
726
727 $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);
728 $total_ht = $tabprice[0];
729 $total_tva = $tabprice[1];
730 $total_ttc = $tabprice[2];
731 $total_localtax1 = $tabprice[9];
732 $total_localtax2 = $tabprice[10];
733 $pu_ht = $tabprice[3];
734 $pu_tva = $tabprice[4];
735 $pu_ttc = $tabprice[5];
736
737 // MultiCurrency
738 $multicurrency_total_ht = $tabprice[16];
739 $multicurrency_total_tva = $tabprice[17];
740 $multicurrency_total_ttc = $tabprice[18];
741 $pu_ht_devise = $tabprice[19];
742
743 $pu = $pu_ht;
744 if ($price_base_type == 'TTC') {
745 $pu = $pu_ttc;
746 }
747
748 //Fetch current line from the database and then clone the object and set it in $oldline property
749 $line = new SupplierProposalLine($this->db);
750 $line->fetch($rowid);
751 $line->fetch_optionals();
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->fk_supplier_proposal = $this->id;
827
828 $this->db->commit();
829 return $result;
830 } else {
831 $this->error = $this->db->error();
832 $this->db->rollback();
833 return -1;
834 }
835 } else {
836 dol_syslog(get_class($this)."::updateline Erreur -2 SupplierProposal en mode incompatible pour cette action");
837 return -2;
838 }
839 }
840
841
848 public function deleteline($lineid)
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() > 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 = empty($this->delivery_date) ? $this->date_livraison : $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 .= ", remise";
923 $sql .= ", remise_percent";
924 $sql .= ", remise_absolue";
925 $sql .= ", total_tva";
926 $sql .= ", total_ttc";
927 $sql .= ", datec";
928 $sql .= ", ref";
929 $sql .= ", fk_user_author";
930 $sql .= ", note_private";
931 $sql .= ", note_public";
932 $sql .= ", model_pdf";
933 $sql .= ", fk_cond_reglement";
934 $sql .= ", fk_mode_reglement";
935 $sql .= ", fk_account";
936 $sql .= ", date_livraison";
937 $sql .= ", fk_shipping_method";
938 $sql .= ", fk_projet";
939 $sql .= ", entity";
940 $sql .= ", fk_multicurrency";
941 $sql .= ", multicurrency_code";
942 $sql .= ", multicurrency_tx";
943 $sql .= ") ";
944 $sql .= " VALUES (";
945 $sql .= ((int) $this->socid);
946 $sql .= ", 0";
947 $sql .= ", ".((double) $this->remise);
948 $sql .= ", ".($this->remise_percent ? ((double) $this->remise_percent) : 'null');
949 $sql .= ", ".($this->remise_absolue ? ((double) $this->remise_absolue) : 'null');
950 $sql .= ", 0";
951 $sql .= ", 0";
952 $sql .= ", '".$this->db->idate($now)."'";
953 $sql .= ", '(PROV)'";
954 $sql .= ", ".($user->id > 0 ? ((int) $user->id) : "null");
955 $sql .= ", '".$this->db->escape($this->note_private)."'";
956 $sql .= ", '".$this->db->escape($this->note_public)."'";
957 $sql .= ", '".$this->db->escape($this->model_pdf)."'";
958 $sql .= ", ".($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : 'NULL');
959 $sql .= ", ".($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : 'NULL');
960 $sql .= ", ".($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
961 $sql .= ", ".($delivery_date ? "'".$this->db->idate($delivery_date)."'" : "null");
962 $sql .= ", ".($this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : 'NULL');
963 $sql .= ", ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
964 $sql .= ", ".((int) $conf->entity);
965 $sql .= ", ".((int) $this->fk_multicurrency);
966 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
967 $sql .= ", ".((double) $this->multicurrency_tx);
968 $sql .= ")";
969
970 dol_syslog(get_class($this)."::create", LOG_DEBUG);
971 $resql = $this->db->query($sql);
972 if ($resql) {
973 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."supplier_proposal");
974
975 if ($this->id) {
976 $this->ref = '(PROV'.$this->id.')';
977 $sql = 'UPDATE '.MAIN_DB_PREFIX."supplier_proposal SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
978
979 dol_syslog(get_class($this)."::create", LOG_DEBUG);
980 $resql = $this->db->query($sql);
981 if (!$resql) {
982 $error++;
983 }
984
985 if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
986 $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
987 }
988
989 // Add object linked
990 if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
991 foreach ($this->linked_objects as $origin => $tmp_origin_id) {
992 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, ...))
993 foreach ($tmp_origin_id as $origin_id) {
994 $ret = $this->add_object_linked($origin, $origin_id);
995 if (!$ret) {
996 dol_print_error($this->db);
997 $error++;
998 }
999 }
1000 }
1001 }
1002 }
1003
1004 /*
1005 * Insertion du detail des produits dans la base
1006 */
1007 if (!$error) {
1008 $fk_parent_line = 0;
1009 $num = count($this->lines);
1010
1011 for ($i = 0; $i < $num; $i++) {
1012 // Reset fk_parent_line for no child products and special product
1013 if (($this->lines[$i]->product_type != 9 && empty($this->lines[$i]->fk_parent_line)) || $this->lines[$i]->product_type == 9) {
1014 $fk_parent_line = 0;
1015 }
1016
1017 $result = $this->addline(
1018 $this->lines[$i]->desc,
1019 $this->lines[$i]->subprice,
1020 $this->lines[$i]->qty,
1021 $this->lines[$i]->tva_tx,
1022 $this->lines[$i]->localtax1_tx,
1023 $this->lines[$i]->localtax2_tx,
1024 $this->lines[$i]->fk_product,
1025 $this->lines[$i]->remise_percent,
1026 'HT',
1027 0,
1028 0,
1029 $this->lines[$i]->product_type,
1030 $this->lines[$i]->rang,
1031 $this->lines[$i]->special_code,
1032 $fk_parent_line,
1033 $this->lines[$i]->fk_fournprice,
1034 $this->lines[$i]->pa_ht,
1035 empty($this->lines[$i]->label) ? '' : $this->lines[$i]->label, // deprecated
1036 $this->lines[$i]->array_options,
1037 $this->lines[$i]->ref_fourn,
1038 $this->lines[$i]->fk_unit,
1039 'supplier_proposal',
1040 $this->lines[$i]->rowid
1041 );
1042
1043 if ($result < 0) {
1044 $error++;
1045 $this->error = $this->db->error;
1046 dol_print_error($this->db);
1047 break;
1048 }
1049 // Defined the new fk_parent_line
1050 if ($result > 0 && $this->lines[$i]->product_type == 9) {
1051 $fk_parent_line = $result;
1052 }
1053 }
1054 }
1055
1056 if (!$error) {
1057 // Mise a jour infos denormalisees
1058 $resql = $this->update_price(1);
1059 if ($resql) {
1060 $action = 'update';
1061
1062 // Actions on extra fields
1063 if (!$error) {
1064 $result = $this->insertExtraFields();
1065 if ($result < 0) {
1066 $error++;
1067 }
1068 }
1069
1070 if (!$error && !$notrigger) {
1071 // Call trigger
1072 $result = $this->call_trigger('PROPOSAL_SUPPLIER_CREATE', $user);
1073 if ($result < 0) {
1074 $error++;
1075 }
1076 // End call triggers
1077 }
1078 } else {
1079 $this->error = $this->db->lasterror();
1080 $error++;
1081 }
1082 }
1083 } else {
1084 $this->error = $this->db->lasterror();
1085 $error++;
1086 }
1087
1088 if (!$error) {
1089 $this->db->commit();
1090 dol_syslog(get_class($this)."::create done id=".$this->id);
1091 return $this->id;
1092 } else {
1093 $this->db->rollback();
1094 return -2;
1095 }
1096 } else {
1097 $this->error = $this->db->lasterror();
1098 $this->db->rollback();
1099 return -1;
1100 }
1101 }
1102
1110 public function createFromClone(User $user, $fromid = 0)
1111 {
1112 global $conf, $hookmanager;
1113
1114 $error = 0;
1115 $now = dol_now();
1116
1117 $this->db->begin();
1118
1119 // get extrafields so they will be clone
1120 foreach ($this->lines as $line) {
1121 $line->fetch_optionals();
1122 }
1123
1124 // Load source object
1125 $objFrom = clone $this;
1126
1127 $objsoc = new Societe($this->db);
1128
1129 // Change socid if needed
1130 if (!empty($fromid) && $fromid != $this->socid) {
1131 if ($objsoc->fetch($fromid) > 0) {
1132 $this->socid = $objsoc->id;
1133 $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1134 $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1135 $this->fk_project = '';
1136 }
1137
1138 // TODO Change product price if multi-prices
1139 } else {
1140 $objsoc->fetch($this->socid);
1141 }
1142
1143 $this->id = 0;
1144 $this->statut = 0;
1145
1146 if (empty($conf->global->SUPPLIER_PROPOSAL_ADDON) || !is_readable(DOL_DOCUMENT_ROOT."/core/modules/supplier_proposal/".$conf->global->SUPPLIER_PROPOSAL_ADDON.".php")) {
1147 $this->error = 'ErrorSetupNotComplete';
1148 return -1;
1149 }
1150
1151 // Clear fields
1152 $this->user_author = $user->id; // deprecated
1153 $this->user_author_id = $user->id;
1154 $this->user_valid = 0; // deprecated
1155 $this->user_valid_id = 0;
1156 $this->date = $now;
1157
1158 // Set ref
1159 require_once DOL_DOCUMENT_ROOT."/core/modules/supplier_proposal/".$conf->global->SUPPLIER_PROPOSAL_ADDON.'.php';
1160 $obj = $conf->global->SUPPLIER_PROPOSAL_ADDON;
1161 $modSupplierProposal = new $obj;
1162 $this->ref = $modSupplierProposal->getNextValue($objsoc, $this);
1163
1164 // Create clone
1165 $this->context['createfromclone'] = 'createfromclone';
1166 $result = $this->create($user);
1167 if ($result < 0) {
1168 $error++;
1169 }
1170
1171 if (!$error) {
1172 // Hook of thirdparty module
1173 if (is_object($hookmanager)) {
1174 $parameters = array('objFrom'=>$objFrom);
1175 $action = '';
1176 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1177 if ($reshook < 0) {
1178 $this->setErrorsFromObject($hookmanager);
1179 $error++;
1180 }
1181 }
1182 }
1183
1184 unset($this->context['createfromclone']);
1185
1186 // End
1187 if (!$error) {
1188 $this->db->commit();
1189 return $this->id;
1190 } else {
1191 $this->db->rollback();
1192 return -1;
1193 }
1194 }
1195
1203 public function fetch($rowid, $ref = '')
1204 {
1205 global $conf;
1206
1207 $sql = "SELECT p.rowid, p.entity, p.ref, p.remise, p.remise_percent, p.remise_absolue, p.fk_soc";
1208 $sql .= ", p.total_ttc, p.total_tva, p.localtax1, p.localtax2, p.total_ht";
1209 $sql .= ", p.datec";
1210 $sql .= ", p.date_valid as datev";
1211 $sql .= ", p.date_livraison as delivery_date";
1212 $sql .= ", p.model_pdf, p.extraparams";
1213 $sql .= ", p.note_private, p.note_public";
1214 $sql .= ", p.fk_projet as fk_project, p.fk_statut";
1215 $sql .= ", p.fk_user_author, p.fk_user_valid, p.fk_user_cloture";
1216 $sql .= ", p.fk_cond_reglement";
1217 $sql .= ", p.fk_mode_reglement";
1218 $sql .= ', p.fk_account';
1219 $sql .= ", p.fk_shipping_method";
1220 $sql .= ", p.fk_multicurrency, p.multicurrency_code, p.multicurrency_tx, p.multicurrency_total_ht, p.multicurrency_total_tva, p.multicurrency_total_ttc";
1221 $sql .= ", c.label as statut_label";
1222 $sql .= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc";
1223 $sql .= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement";
1224 $sql .= " FROM ".MAIN_DB_PREFIX."c_propalst as c, ".MAIN_DB_PREFIX."supplier_proposal as p";
1225 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id';
1226 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON p.fk_cond_reglement = cr.rowid';
1227 $sql .= " WHERE p.fk_statut = c.id";
1228 $sql .= " AND p.entity IN (".getEntity('supplier_proposal').")";
1229 if ($ref) {
1230 $sql .= " AND p.ref = '".$this->db->escape($ref)."'";
1231 } else {
1232 $sql .= " AND p.rowid = ".((int) $rowid);
1233 }
1234
1235 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1236 $resql = $this->db->query($sql);
1237 if ($resql) {
1238 if ($this->db->num_rows($resql)) {
1239 $obj = $this->db->fetch_object($resql);
1240
1241 $this->id = $obj->rowid;
1242 $this->entity = $obj->entity;
1243
1244 $this->ref = $obj->ref;
1245 $this->remise = $obj->remise;
1246 $this->remise_percent = $obj->remise_percent;
1247 $this->remise_absolue = $obj->remise_absolue;
1248 $this->total_ht = $obj->total_ht;
1249 $this->total_tva = $obj->total_tva;
1250 $this->total_localtax1 = $obj->localtax1;
1251 $this->total_localtax2 = $obj->localtax2;
1252 $this->total_ttc = $obj->total_ttc;
1253 $this->socid = $obj->fk_soc;
1254 $this->fk_project = $obj->fk_project;
1255 $this->model_pdf = $obj->model_pdf;
1256 $this->modelpdf = $obj->model_pdf; // deprecated
1257 $this->note = $obj->note_private; // TODO deprecated
1258 $this->note_private = $obj->note_private;
1259 $this->note_public = $obj->note_public;
1260 $this->statut = (int) $obj->fk_statut;
1261 $this->status = (int) $obj->fk_statut;
1262 $this->datec = $this->db->jdate($obj->datec); // TODO deprecated
1263 $this->datev = $this->db->jdate($obj->datev); // TODO deprecated
1264 $this->date_creation = $this->db->jdate($obj->datec); // Creation date
1265 $this->date = $this->date_creation;
1266 $this->date_validation = $this->db->jdate($obj->datev); // Validation date
1267 $this->date_livraison = $this->db->jdate($obj->delivery_date); // deprecated
1268 $this->delivery_date = $this->db->jdate($obj->delivery_date);
1269 $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1270
1271 $this->mode_reglement_id = $obj->fk_mode_reglement;
1272 $this->mode_reglement_code = $obj->mode_reglement_code;
1273 $this->mode_reglement = $obj->mode_reglement;
1274 $this->fk_account = ($obj->fk_account > 0) ? $obj->fk_account : null;
1275 $this->cond_reglement_id = $obj->fk_cond_reglement;
1276 $this->cond_reglement_code = $obj->cond_reglement_code;
1277 $this->cond_reglement = $obj->cond_reglement;
1278 $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1279
1280 $this->extraparams = (array) json_decode($obj->extraparams, true);
1281
1282 $this->user_author_id = $obj->fk_user_author;
1283 $this->user_valid_id = $obj->fk_user_valid;
1284 $this->user_close_id = $obj->fk_user_cloture;
1285
1286 // Multicurrency
1287 $this->fk_multicurrency = $obj->fk_multicurrency;
1288 $this->multicurrency_code = $obj->multicurrency_code;
1289 $this->multicurrency_tx = $obj->multicurrency_tx;
1290 $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1291 $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
1292 $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
1293
1294 if ($obj->fk_statut == 0) {
1295 $this->brouillon = 1;
1296 }
1297
1298 // Retrieve all extrafield
1299 // fetch optionals attributes and labels
1300 $this->fetch_optionals();
1301
1302 $this->db->free($resql);
1303
1304 $this->lines = array();
1305
1306 // Lines of supplier proposals
1307 $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,";
1308 $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,";
1309 $sql .= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label,';
1310 $sql .= ' d.ref_fourn as ref_produit_fourn,';
1311 $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';
1312 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposaldet as d";
1313 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON d.fk_product = p.rowid";
1314 $sql .= " WHERE d.fk_supplier_proposal = ".((int) $this->id);
1315 $sql .= " ORDER by d.rang";
1316
1317 $result = $this->db->query($sql);
1318 if ($result) {
1319 $num = $this->db->num_rows($result);
1320 $i = 0;
1321
1322 while ($i < $num) {
1323 $objp = $this->db->fetch_object($result);
1324
1325 $line = new SupplierProposalLine($this->db);
1326
1327 $line->rowid = $objp->rowid; // deprecated
1328 $line->id = $objp->rowid;
1329 $line->fk_supplier_proposal = $objp->fk_supplier_proposal;
1330 $line->fk_parent_line = $objp->fk_parent_line;
1331 $line->product_type = $objp->product_type;
1332 $line->label = $objp->custom_label;
1333 $line->desc = $objp->description; // Description ligne
1334 $line->qty = $objp->qty;
1335 $line->tva_tx = $objp->tva_tx;
1336 $line->localtax1_tx = $objp->localtax1_tx;
1337 $line->localtax2_tx = $objp->localtax2_tx;
1338 $line->subprice = $objp->subprice;
1339 $line->fk_remise_except = $objp->fk_remise_except;
1340 $line->remise_percent = $objp->remise_percent;
1341
1342 $line->info_bits = $objp->info_bits;
1343 $line->total_ht = $objp->total_ht;
1344 $line->total_tva = $objp->total_tva;
1345 $line->total_localtax1 = $objp->total_localtax1;
1346 $line->total_localtax2 = $objp->total_localtax2;
1347 $line->total_ttc = $objp->total_ttc;
1348 $line->fk_fournprice = $objp->fk_fournprice;
1349 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1350 $line->pa_ht = $marginInfos[0];
1351 $line->marge_tx = $marginInfos[1];
1352 $line->marque_tx = $marginInfos[2];
1353 $line->special_code = $objp->special_code;
1354 $line->rang = $objp->rang;
1355
1356 $line->fk_product = $objp->fk_product;
1357
1358 $line->ref = $objp->product_ref; // deprecated
1359 $line->product_ref = $objp->product_ref;
1360 $line->libelle = $objp->product_label; // deprecated
1361 $line->product_label = $objp->product_label;
1362 $line->product_desc = $objp->product_desc; // Description produit
1363 $line->fk_product_type = $objp->fk_product_type;
1364
1365 $line->ref_fourn = $objp->ref_produit_fourn;
1366
1367 // Multicurrency
1368 $line->fk_multicurrency = $objp->fk_multicurrency;
1369 $line->multicurrency_code = $objp->multicurrency_code;
1370 $line->multicurrency_subprice = $objp->multicurrency_subprice;
1371 $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
1372 $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
1373 $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
1374 $line->fk_unit = $objp->fk_unit;
1375
1376 $this->lines[$i] = $line;
1377
1378 $i++;
1379 }
1380 $this->db->free($result);
1381 } else {
1382 $this->error = $this->db->error();
1383 return -1;
1384 }
1385
1386 // Retrieve all extrafield
1387 // fetch optionals attributes and labels
1388 $this->fetch_optionals();
1389
1390 return 1;
1391 }
1392
1393 $this->error = "Record Not Found";
1394 return 0;
1395 } else {
1396 $this->error = $this->db->error();
1397 return -1;
1398 }
1399 }
1400
1408 public function valid($user, $notrigger = 0)
1409 {
1410 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1411
1412 global $conf, $langs;
1413
1414 $error = 0;
1415 $now = dol_now();
1416
1417 if ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->supplier_proposal->creer))
1418 || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->supplier_proposal->validate_advance))) {
1419 $this->db->begin();
1420
1421 // Numbering module definition
1422 $soc = new Societe($this->db);
1423 $result = $soc->fetch($this->socid);
1424
1425 if ($result < 0) {
1426 return -1;
1427 }
1428
1429 // Define new ref
1430 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1431 $num = $this->getNextNumRef($soc);
1432 } else {
1433 $num = $this->ref;
1434 }
1435 $this->newref = dol_sanitizeFileName($num);
1436
1437 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1438 $sql .= " SET ref = '".$this->db->escape($num)."',";
1439 $sql .= " fk_statut = 1, date_valid='".$this->db->idate($now)."', fk_user_valid=".((int) $user->id);
1440 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1441
1442 dol_syslog(get_class($this)."::valid", LOG_DEBUG);
1443 $resql = $this->db->query($sql);
1444 if (!$resql) {
1445 dol_print_error($this->db);
1446 $error++;
1447 }
1448
1449 // Trigger calls
1450 if (!$error && !$notrigger) {
1451 // Call trigger
1452 $result = $this->call_trigger('PROPOSAL_SUPPLIER_VALIDATE', $user);
1453 if ($result < 0) {
1454 $error++;
1455 }
1456 // End call triggers
1457 }
1458
1459 if (!$error) {
1460 $this->oldref = $this->ref;
1461
1462 // Rename directory if dir was a temporary ref
1463 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1464 // Now we rename also files into index
1465 $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)."'";
1466 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1467 $resql = $this->db->query($sql);
1468 if (!$resql) {
1469 $error++; $this->error = $this->db->lasterror();
1470 }
1471 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'supplier_proposal/".$this->db->escape($this->newref)."'";
1472 $sql .= " WHERE filepath = 'supplier_proposal/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1473 $resql = $this->db->query($sql);
1474 if (!$resql) {
1475 $error++; $this->error = $this->db->lasterror();
1476 }
1477
1478 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1479 $oldref = dol_sanitizeFileName($this->ref);
1480 $newref = dol_sanitizeFileName($num);
1481 $dirsource = $conf->supplier_proposal->dir_output.'/'.$oldref;
1482 $dirdest = $conf->supplier_proposal->dir_output.'/'.$newref;
1483 if (!$error && file_exists($dirsource)) {
1484 dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
1485 if (@rename($dirsource, $dirdest)) {
1486 dol_syslog("Rename ok");
1487 // Rename docs starting with $oldref with $newref
1488 $listoffiles = dol_dir_list($conf->supplier_proposal->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1489 foreach ($listoffiles as $fileentry) {
1490 $dirsource = $fileentry['name'];
1491 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1492 $dirsource = $fileentry['path'].'/'.$dirsource;
1493 $dirdest = $fileentry['path'].'/'.$dirdest;
1494 @rename($dirsource, $dirdest);
1495 }
1496 }
1497 }
1498 }
1499
1500 $this->ref = $num;
1501 $this->brouillon = 0;
1502 $this->statut = 1;
1503 $this->user_valid_id = $user->id;
1504 $this->datev = $now;
1505
1506 $this->db->commit();
1507 return 1;
1508 } else {
1509 $this->db->rollback();
1510 return -1;
1511 }
1512 } else {
1513 dol_syslog("You don't have permission to validate supplier proposal", LOG_WARNING);
1514 return -2;
1515 }
1516 }
1517
1518 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1527 public function set_date_livraison($user, $delivery_date)
1528 {
1529 // phpcs:enable
1530 return $this->setDeliveryDate($user, $delivery_date);
1531 }
1532
1540 public function setDeliveryDate($user, $delivery_date)
1541 {
1542 if (!empty($user->rights->supplier_proposal->creer)) {
1543 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1544 $sql .= " SET date_livraison = ".($delivery_date != '' ? "'".$this->db->idate($delivery_date)."'" : 'null');
1545 $sql .= " WHERE rowid = ".((int) $this->id);
1546
1547 if ($this->db->query($sql)) {
1548 $this->date_livraison = $delivery_date;
1549 $this->delivery_date = $delivery_date;
1550 return 1;
1551 } else {
1552 $this->error = $this->db->error();
1553 dol_syslog(get_class($this)."::setDeliveryDate Erreur SQL");
1554 return -1;
1555 }
1556 }
1557 return 0;
1558 }
1559
1560 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1568 public function set_remise_percent($user, $remise)
1569 {
1570 // phpcs:enable
1571 $remise = trim($remise) ?trim($remise) : 0;
1572
1573 if (!empty($user->rights->supplier_proposal->creer)) {
1574 $remise = price2num($remise, 2);
1575
1576 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal SET remise_percent = ".((float) $remise);
1577 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1578
1579 if ($this->db->query($sql)) {
1580 $this->remise_percent = ((float) $remise);
1581 $this->update_price(1);
1582 return 1;
1583 } else {
1584 $this->error = $this->db->error();
1585 return -1;
1586 }
1587 }
1588 return 0;
1589 }
1590
1591
1592 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1600 public function set_remise_absolue($user, $remise)
1601 {
1602 // phpcs:enable
1603 if (empty($remise)) {
1604 $remise = 0;
1605 }
1606
1607 $remise = price2num($remise);
1608
1609 if (!empty($user->rights->supplier_proposal->creer)) {
1610 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1611 $sql .= " SET remise_absolue = ".((float) $remise);
1612 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1613
1614 if ($this->db->query($sql)) {
1615 $this->remise_absolue = $remise;
1616 $this->update_price(1);
1617 return 1;
1618 } else {
1619 $this->error = $this->db->error();
1620 return -1;
1621 }
1622 }
1623 return 0;
1624 }
1625
1626
1627
1637 public function reopen($user, $statut, $note = '', $notrigger = 0)
1638 {
1639 global $langs, $conf;
1640
1641 $this->statut = $statut;
1642 $error = 0;
1643
1644 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1645 $sql .= " SET fk_statut = ".((int) $this->statut).",";
1646 if (!empty($note)) {
1647 $sql .= " note_private = '".$this->db->escape($note)."',";
1648 }
1649 $sql .= " date_cloture=NULL, fk_user_cloture=NULL";
1650 $sql .= " WHERE rowid = ".((int) $this->id);
1651
1652 $this->db->begin();
1653
1654 dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
1655 $resql = $this->db->query($sql);
1656 if (!$resql) {
1657 $error++; $this->errors[] = "Error ".$this->db->lasterror();
1658 }
1659 if (!$error) {
1660 if (!$notrigger) {
1661 // Call trigger
1662 $result = $this->call_trigger('PROPOSAL_SUPPLIER_REOPEN', $user);
1663 if ($result < 0) {
1664 $error++;
1665 }
1666 // End call triggers
1667 }
1668 }
1669
1670 // Commit or rollback
1671 if ($error) {
1672 if (!empty($this->errors)) {
1673 foreach ($this->errors as $errmsg) {
1674 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1675 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1676 }
1677 }
1678 $this->db->rollback();
1679 return -1 * $error;
1680 } else {
1681 $this->db->commit();
1682 return 1;
1683 }
1684 }
1685
1686
1695 public function cloture($user, $status, $note)
1696 {
1697 global $langs, $conf;
1698 $hidedetails = 0;
1699 $hidedesc = 0;
1700 $hideref = 0;
1701 $this->statut = $status;
1702 $error = 0;
1703 $now = dol_now();
1704
1705 $this->db->begin();
1706
1707 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1708 $sql .= " SET fk_statut = ".((int) $status).", note_private = '".$this->db->escape($note)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".$user->id;
1709 $sql .= " WHERE rowid = ".((int) $this->id);
1710
1711 $resql = $this->db->query($sql);
1712 if ($resql) {
1713 $modelpdf = $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED ? $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED : (empty($this->model_pdf) ? '' : $this->model_pdf);
1714 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_REFUSED';
1715
1716 if ($status == 2) {
1717 $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_SIGNED';
1718 $modelpdf = $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL ? $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL : (empty($this->model_pdf) ? '' : $this->model_pdf);
1719
1720 if (!empty($conf->global->SUPPLIER_PROPOSAL_UPDATE_PRICE_ON_SUPPlIER_PROPOSAL)) { // TODO This option was not tested correctly. Error if product ref does not exists
1721 $result = $this->updateOrCreatePriceFournisseur($user);
1722 }
1723 }
1724 if ($status == 4) {
1725 $triggerName = 'PROPOSAL_SUPPLIER_CLASSIFY_BILLED';
1726 }
1727
1728 if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE)) {
1729 // Define output language
1730 $outputlangs = $langs;
1731 if (getDolGlobalInt('MAIN_MULTILANGS')) {
1732 $outputlangs = new Translate("", $conf);
1733 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
1734 $outputlangs->setDefaultLang($newlang);
1735 }
1736 //$ret=$object->fetch($id); // Reload to get new records
1737 $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1738 }
1739
1740 // Call trigger
1741 $result = $this->call_trigger($triggerName, $user);
1742 if ($result < 0) {
1743 $error++;
1744 }
1745 // End call triggers
1746
1747 if (!$error) {
1748 $this->db->commit();
1749 return 1;
1750 } else {
1751 $this->db->rollback();
1752 return -1;
1753 }
1754 } else {
1755 $this->error = $this->db->lasterror();
1756 $this->errors[] = $this->db->lasterror();
1757 $this->db->rollback();
1758 return -1;
1759 }
1760 }
1761
1768 public function updateOrCreatePriceFournisseur($user)
1769 {
1770 global $conf;
1771
1772 dol_syslog(get_class($this)."::updateOrCreatePriceFournisseur", LOG_DEBUG);
1773 foreach ($this->lines as $product) {
1774 if ($product->subprice <= 0) {
1775 continue;
1776 }
1777 $productsupplier = new ProductFournisseur($this->db);
1778
1779 $multicurrency_tx = 1;
1780 $fk_multicurrency = 0;
1781
1782 if (empty($this->thirdparty)) {
1783 $this->fetch_thirdparty();
1784 }
1785
1786 $ref_fourn = $product->ref_fourn;
1787 if (empty($ref_fourn)) {
1788 $ref_fourn = $product->ref_supplier;
1789 }
1790 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1791 list($fk_multicurrency, $multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $product->multicurrency_code);
1792 }
1793 $productsupplier->id = $product->fk_product;
1794
1795 $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, '', '', '');
1796 }
1797
1798 return 1;
1799 }
1800
1809 public function updatePriceFournisseur($idProductFournPrice, $product, $user)
1810 {
1811 $price = price2num($product->subprice * $product->qty, 'MU');
1812 $unitPrice = price2num($product->subprice, 'MU');
1813
1814 $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);
1815
1816 $resql = $this->db->query($sql);
1817 if (!$resql) {
1818 $this->error = $this->db->error();
1819 $this->db->rollback();
1820 return -1;
1821 }
1822 return 1;
1823 }
1824
1832 public function createPriceFournisseur($product, $user)
1833 {
1834 global $conf;
1835
1836 $price = price2num($product->subprice * $product->qty, 'MU');
1837 $qty = price2num($product->qty);
1838 $unitPrice = price2num($product->subprice, 'MU');
1839
1840 $now = dol_now();
1841
1842 $values = array(
1843 "'".$this->db->idate($now)."'",
1844 $product->fk_product,
1845 $this->thirdparty->id,
1846 "'".$product->ref_fourn."'",
1847 $price,
1848 $qty,
1849 $unitPrice,
1850 $product->tva_tx,
1851 $user->id
1852 );
1853 if (isModEnabled("multicurrency")) {
1854 if (!empty($product->multicurrency_code)) {
1855 include_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
1856 $multicurrency = new MultiCurrency($this->db); //need to fetch because empty fk_multicurrency and rate
1857 $multicurrency->fetch(0, $product->multicurrency_code);
1858 if (!empty($multicurrency->id)) {
1859 $values[] = $multicurrency->id;
1860 $values[] = "'".$product->multicurrency_code."'";
1861 $values[] = $product->multicurrency_subprice;
1862 $values[] = $product->multicurrency_total_ht;
1863 $values[] = $multicurrency->rate->rate;
1864 } else {
1865 for ($i = 0; $i < 5; $i++) {
1866 $values[] = 'NULL';
1867 }
1868 }
1869 }
1870 }
1871
1872 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'product_fournisseur_price ';
1873 $sql .= '(datec, fk_product, fk_soc, ref_fourn, price, quantity, unitprice, tva_tx, fk_user';
1874 if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1875 $sql .= ',fk_multicurrency, multicurrency_code, multicurrency_unitprice, multicurrency_price, multicurrency_tx';
1876 }
1877 $sql .= ') VALUES ('.implode(',', $values).')';
1878
1879 $resql = $this->db->query($sql);
1880 if (!$resql) {
1881 $this->error = $this->db->error();
1882 $this->db->rollback();
1883 return -1;
1884 }
1885 return 1;
1886 }
1887
1888 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1895 public function setDraft($user)
1896 {
1897 // phpcs:enable
1898 global $conf, $langs;
1899
1900 $error = 0;
1901
1902 if ($this->statut == self::STATUS_DRAFT) {
1903 dol_syslog(get_class($this)."::setDraft already draft status", LOG_WARNING);
1904 return 0;
1905 }
1906
1907 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal";
1908 $sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1909 $sql .= " WHERE rowid = ".((int) $this->id);
1910
1911 if ($this->db->query($sql)) {
1912 if (!$error) {
1913 $this->oldcopy = clone $this;
1914 }
1915
1916 if (!$error) {
1917 // Call trigger
1918 $result = $this->call_trigger('PROPOSAL_SUPPLIER_UNVALIDATE', $user);
1919 if ($result < 0) {
1920 $error++;
1921 }
1922 }
1923
1924 if (!$error) {
1925 $this->statut = self::STATUS_DRAFT;
1926 $this->brouillon = 1;
1927 $this->db->commit();
1928 return 1;
1929 } else {
1930 $this->db->rollback();
1931 return -1;
1932 }
1933 } else {
1934 return -1;
1935 }
1936 }
1937
1938
1939 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1953 public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datec', $sortorder = 'DESC')
1954 {
1955 // phpcs:enable
1956 global $conf, $user;
1957
1958 $ga = array();
1959
1960 $sql = "SELECT s.rowid, s.nom as name, s.client,";
1961 $sql .= " p.rowid as supplier_proposalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
1962 $sql .= " p.datep as dp, p.fin_validite as datelimite";
1963 if (empty($user->rights->societe->client->voir) && !$socid) {
1964 $sql .= ", sc.fk_soc, sc.fk_user";
1965 }
1966 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."supplier_proposal as p, ".MAIN_DB_PREFIX."c_propalst as c";
1967 if (empty($user->rights->societe->client->voir) && !$socid) {
1968 $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
1969 }
1970 $sql .= " WHERE p.entity IN (".getEntity('supplier_proposal').")";
1971 $sql .= " AND p.fk_soc = s.rowid";
1972 $sql .= " AND p.fk_statut = c.id";
1973 if (empty($user->rights->societe->client->voir) && !$socid) { //restriction
1974 $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
1975 }
1976 if ($socid) {
1977 $sql .= " AND s.rowid = ".((int) $socid);
1978 }
1979 if ($draft) {
1980 $sql .= " AND p.fk_statut = 0";
1981 }
1982 if ($notcurrentuser > 0) {
1983 $sql .= " AND p.fk_user_author <> ".$user->id;
1984 }
1985 $sql .= $this->db->order($sortfield, $sortorder);
1986 $sql .= $this->db->plimit($limit, $offset);
1987
1988 $result = $this->db->query($sql);
1989 if ($result) {
1990 $num = $this->db->num_rows($result);
1991 if ($num) {
1992 $i = 0;
1993 while ($i < $num) {
1994 $obj = $this->db->fetch_object($result);
1995
1996 if ($shortlist == 1) {
1997 $ga[$obj->supplier_proposalid] = $obj->ref;
1998 } elseif ($shortlist == 2) {
1999 $ga[$obj->supplier_proposalid] = $obj->ref.' ('.$obj->name.')';
2000 } else {
2001 $ga[$i]['id'] = $obj->supplier_proposalid;
2002 $ga[$i]['ref'] = $obj->ref;
2003 $ga[$i]['name'] = $obj->name;
2004 }
2005
2006 $i++;
2007 }
2008 }
2009 return $ga;
2010 } else {
2011 dol_print_error($this->db);
2012 return -1;
2013 }
2014 }
2015
2023 public function delete($user, $notrigger = 0)
2024 {
2025 global $conf, $langs;
2026 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2027
2028 $error = 0;
2029
2030 $this->db->begin();
2031
2032 if (!$notrigger) {
2033 // Call trigger
2034 $result = $this->call_trigger('PROPOSAL_SUPPLIER_DELETE', $user);
2035 if ($result < 0) {
2036 $error++;
2037 }
2038 // End call triggers
2039 }
2040
2041 if (!$error) {
2042 $main = MAIN_DB_PREFIX.'supplier_proposaldet';
2043 $ef = $main."_extrafields";
2044 $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_supplier_proposal = ".((int) $this->id).")";
2045 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet WHERE fk_supplier_proposal = ".((int) $this->id);
2046 if ($this->db->query($sql)) {
2047 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposal WHERE rowid = ".((int) $this->id);
2048 if ($this->db->query($sqlef) && $this->db->query($sql)) {
2049 // Delete linked object
2050 $res = $this->deleteObjectLinked();
2051 if ($res < 0) {
2052 $error++;
2053 }
2054
2055 if (!$error) {
2056 // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2057 $this->deleteEcmFiles();
2058
2059 // We remove directory
2060 $ref = dol_sanitizeFileName($this->ref);
2061 if ($conf->supplier_proposal->dir_output && !empty($this->ref)) {
2062 $dir = $conf->supplier_proposal->dir_output."/".$ref;
2063 $file = $dir."/".$ref.".pdf";
2064 if (file_exists($file)) {
2065 dol_delete_preview($this);
2066
2067 if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2068 $this->error = 'ErrorFailToDeleteFile';
2069 $this->errors = array('ErrorFailToDeleteFile');
2070 $this->db->rollback();
2071 return 0;
2072 }
2073 }
2074 if (file_exists($dir)) {
2075 $res = @dol_delete_dir_recursive($dir);
2076 if (!$res) {
2077 $this->error = 'ErrorFailToDeleteDir';
2078 $this->errors = array('ErrorFailToDeleteDir');
2079 $this->db->rollback();
2080 return 0;
2081 }
2082 }
2083 }
2084 }
2085
2086 // Removed extrafields
2087 if (!$error) {
2088 $result = $this->deleteExtraFields();
2089 if ($result < 0) {
2090 $error++;
2091 $errorflag = -4;
2092 dol_syslog(get_class($this)."::delete erreur ".$errorflag." ".$this->error, LOG_ERR);
2093 }
2094 }
2095
2096 if (!$error) {
2097 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
2098 $this->db->commit();
2099 return 1;
2100 } else {
2101 $this->error = $this->db->lasterror();
2102 $this->db->rollback();
2103 return 0;
2104 }
2105 } else {
2106 $this->error = $this->db->lasterror();
2107 $this->db->rollback();
2108 return -3;
2109 }
2110 } else {
2111 $this->error = $this->db->lasterror();
2112 $this->db->rollback();
2113 return -2;
2114 }
2115 } else {
2116 $this->db->rollback();
2117 return -1;
2118 }
2119 }
2120
2127 public function info($id)
2128 {
2129 $sql = "SELECT c.rowid, ";
2130 $sql .= " c.datec, c.date_valid as datev, c.date_cloture as dateo,";
2131 $sql .= " c.fk_user_author, c.fk_user_valid, c.fk_user_cloture";
2132 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as c";
2133 $sql .= " WHERE c.rowid = ".((int) $id);
2134
2135 $result = $this->db->query($sql);
2136
2137 if ($result) {
2138 if ($this->db->num_rows($result)) {
2139 $obj = $this->db->fetch_object($result);
2140
2141 $this->id = $obj->rowid;
2142
2143 $this->date_creation = $this->db->jdate($obj->datec);
2144 $this->date_validation = $this->db->jdate($obj->datev);
2145 $this->date_cloture = $this->db->jdate($obj->dateo);
2146
2147 $cuser = new User($this->db);
2148 $cuser->fetch($obj->fk_user_author);
2149 $this->user_creation = $cuser;
2150
2151 if ($obj->fk_user_valid) {
2152 $vuser = new User($this->db);
2153 $vuser->fetch($obj->fk_user_valid);
2154 $this->user_validation = $vuser;
2155 }
2156
2157 if ($obj->fk_user_cloture) {
2158 $cluser = new User($this->db);
2159 $cluser->fetch($obj->fk_user_cloture);
2160 $this->user_cloture = $cluser;
2161 }
2162 }
2163 $this->db->free($result);
2164 } else {
2165 dol_print_error($this->db);
2166 }
2167 }
2168
2169
2176 public function getLibStatut($mode = 0)
2177 {
2178 return $this->LibStatut((isset($this->statut) ? $this->statut : $this->status), $mode);
2179 }
2180
2181 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2189 public function LibStatut($status, $mode = 1)
2190 {
2191 // phpcs:enable
2192
2193 // Init/load array of translation of status
2194 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
2195 global $langs;
2196 $langs->load("supplier_proposal");
2197 $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraft");
2198 $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidated");
2199 $this->labelStatus[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSigned");
2200 $this->labelStatus[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSigned");
2201 $this->labelStatus[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosed");
2202 $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraftShort");
2203 $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidatedShort");
2204 $this->labelStatusShort[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSignedShort");
2205 $this->labelStatusShort[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSignedShort");
2206 $this->labelStatusShort[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosedShort");
2207 }
2208
2209 $statusnew = '';
2210 if ($status == self::STATUS_DRAFT) {
2211 $statusnew = 'status0';
2212 } elseif ($status == self::STATUS_VALIDATED) {
2213 $statusnew = 'status1';
2214 } elseif ($status == self::STATUS_SIGNED) {
2215 $statusnew = 'status4';
2216 } elseif ($status == self::STATUS_NOTSIGNED) {
2217 $statusnew = 'status9';
2218 } elseif ($status == self::STATUS_CLOSE) {
2219 $statusnew = 'status6';
2220 }
2221
2222 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusnew, $mode);
2223 }
2224
2225
2226 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2234 public function load_board($user, $mode)
2235 {
2236 // phpcs:enable
2237 global $conf, $user, $langs;
2238
2239 $now = dol_now();
2240
2241 $this->nbtodo = $this->nbtodolate = 0;
2242 $clause = " WHERE";
2243
2244 $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.date_cloture as datefin";
2245 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2246 if (empty($user->rights->societe->client->voir) && !$user->socid) {
2247 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
2248 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2249 $clause = " AND";
2250 }
2251 $sql .= $clause." p.entity IN (".getEntity('supplier_proposal').")";
2252 if ($mode == 'opened') {
2253 $sql .= " AND p.fk_statut = 1";
2254 }
2255 if ($mode == 'signed') {
2256 $sql .= " AND p.fk_statut = 2";
2257 }
2258 if ($user->socid) {
2259 $sql .= " AND p.fk_soc = ".((int) $user->socid);
2260 }
2261
2262 $resql = $this->db->query($sql);
2263 if ($resql) {
2264 $label = $labelShort = '';
2265 $status = '';
2266 if ($mode == 'opened') {
2267 $delay_warning = !empty($conf->supplier_proposal->cloture->warning_delay) ? $conf->supplier_proposal->cloture->warning_delay : 0;
2268 $status = self::STATUS_VALIDATED;
2269 $label = $langs->trans("SupplierProposalsToClose");
2270 $labelShort = $langs->trans("ToAcceptRefuse");
2271 }
2272 if ($mode == 'signed') {
2273 $delay_warning = !empty($conf->supplier_proposal->facturation->warning_delay) ? $conf->supplier_proposal->facturation->warning_delay : 0;
2274 $status = self::STATUS_SIGNED;
2275 $label = $langs->trans("SupplierProposalsToProcess"); // May be billed or ordered
2276 $labelShort = $langs->trans("ToClose");
2277 }
2278
2279 $response = new WorkboardResponse();
2280 $response->warning_delay = $delay_warning / 60 / 60 / 24;
2281 $response->label = $label;
2282 $response->labelShort = $labelShort;
2283 $response->url = DOL_URL_ROOT.'/supplier_proposal/list.php?search_status='.$status;
2284 $response->img = img_object('', "propal");
2285
2286 // This assignment in condition is not a bug. It allows walking the results.
2287 while ($obj = $this->db->fetch_object($resql)) {
2288 $response->nbtodo++;
2289 if ($mode == 'opened') {
2290 $datelimit = $this->db->jdate($obj->datefin);
2291 if ($datelimit < ($now - $delay_warning)) {
2292 $response->nbtodolate++;
2293 }
2294 }
2295 // TODO Definir regle des propales a facturer en retard
2296 // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
2297 }
2298 return $response;
2299 } else {
2300 $this->error = $this->db->lasterror();
2301 return -1;
2302 }
2303 }
2304
2305
2313 public function initAsSpecimen()
2314 {
2315 global $user, $langs, $conf;
2316
2317 // Load array of products prodids
2318 $num_prods = 0;
2319 $prodids = array();
2320 $sql = "SELECT rowid";
2321 $sql .= " FROM ".MAIN_DB_PREFIX."product";
2322 $sql .= " WHERE entity IN (".getEntity('product').")";
2323 $sql .= $this->db->plimit(100);
2324
2325 $resql = $this->db->query($sql);
2326 if ($resql) {
2327 $num_prods = $this->db->num_rows($resql);
2328 $i = 0;
2329 while ($i < $num_prods) {
2330 $i++;
2331 $row = $this->db->fetch_row($resql);
2332 $prodids[$i] = $row[0];
2333 }
2334 }
2335
2336 // Initialise parametres
2337 $this->id = 0;
2338 $this->ref = 'SPECIMEN';
2339 $this->specimen = 1;
2340 $this->socid = 1;
2341 $this->date = time();
2342 $this->cond_reglement_id = 1;
2343 $this->cond_reglement_code = 'RECEP';
2344 $this->mode_reglement_id = 7;
2345 $this->mode_reglement_code = 'CHQ';
2346 $this->note_public = 'This is a comment (public)';
2347 $this->note_private = 'This is a comment (private)';
2348 // Lines
2349 $nbp = 5;
2350 $xnbp = 0;
2351 while ($xnbp < $nbp) {
2352 $line = new SupplierProposalLine($this->db);
2353 $line->desc = $langs->trans("Description")." ".$xnbp;
2354 $line->qty = 1;
2355 $line->subprice = 100;
2356 $line->tva_tx = 19.6;
2357 $line->localtax1_tx = 0;
2358 $line->localtax2_tx = 0;
2359 if ($xnbp == 2) {
2360 $line->total_ht = 50;
2361 $line->total_ttc = 59.8;
2362 $line->total_tva = 9.8;
2363 $line->remise_percent = 50;
2364 } else {
2365 $line->total_ht = 100;
2366 $line->total_ttc = 119.6;
2367 $line->total_tva = 19.6;
2368 $line->remise_percent = 00;
2369 }
2370
2371 if ($num_prods > 0) {
2372 $prodid = mt_rand(1, $num_prods);
2373 $line->fk_product = $prodids[$prodid];
2374 }
2375
2376 $this->lines[$xnbp] = $line;
2377
2378 $this->total_ht += $line->total_ht;
2379 $this->total_tva += $line->total_tva;
2380 $this->total_ttc += $line->total_ttc;
2381
2382 $xnbp++;
2383 }
2384 }
2385
2386 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2392 public function load_state_board()
2393 {
2394 // phpcs:enable
2395 global $conf, $user;
2396
2397 $this->nb = array();
2398 $clause = "WHERE";
2399
2400 $sql = "SELECT count(p.rowid) as nb";
2401 $sql .= " FROM ".MAIN_DB_PREFIX."supplier_proposal as p";
2402 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
2403 if (empty($user->rights->societe->client->voir) && !$user->socid) {
2404 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2405 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2406 $clause = "AND";
2407 }
2408 $sql .= " ".$clause." p.entity IN (".getEntity('supplier_proposal').")";
2409
2410 $resql = $this->db->query($sql);
2411 if ($resql) {
2412 // This assignment in condition is not a bug. It allows walking the results.
2413 while ($obj = $this->db->fetch_object($resql)) {
2414 $this->nb["supplier_proposals"] = $obj->nb;
2415 }
2416 $this->db->free($resql);
2417 return 1;
2418 } else {
2419 dol_print_error($this->db);
2420 $this->error = $this->db->lasterror();
2421 return -1;
2422 }
2423 }
2424
2425
2433 public function getNextNumRef($soc)
2434 {
2435 global $conf, $db, $langs;
2436 $langs->load("supplier_proposal");
2437
2438 if (!empty($conf->global->SUPPLIER_PROPOSAL_ADDON)) {
2439 $mybool = false;
2440
2441 $file = $conf->global->SUPPLIER_PROPOSAL_ADDON.".php";
2442 $classname = $conf->global->SUPPLIER_PROPOSAL_ADDON;
2443
2444 // Include file with class
2445 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2446 foreach ($dirmodels as $reldir) {
2447 $dir = dol_buildpath($reldir."core/modules/supplier_proposal/");
2448
2449 // Load file with numbering class (if found)
2450 $mybool |= @include_once $dir.$file;
2451 }
2452
2453 if (!$mybool) {
2454 dol_print_error('', "Failed to include file ".$file);
2455 return '';
2456 }
2457
2458 $obj = new $classname();
2459 $numref = "";
2460 $numref = $obj->getNextValue($soc, $this);
2461
2462 if ($numref != "") {
2463 return $numref;
2464 } else {
2465 $this->error = $obj->error;
2466 return "";
2467 }
2468 } else {
2469 $langs->load("errors");
2470 print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("SupplierProposal"));
2471 return "";
2472 }
2473 }
2474
2482 public function getTooltipContentArray($params)
2483 {
2484 global $conf, $langs, $menumanager;
2485
2486 $langs->load('supplier_proposal');
2487
2488 if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
2489 return ['optimize' => $langs->trans("ShowSupplierProposal")];
2490 }
2491
2492 $option = $params['option'] ?? '';
2493 $datas = [];
2494
2495 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("SupplierProposal").'</u>';
2496 if (isset($this->status)) {
2497 $datas['picto'] .= ' '.$this->getLibStatut(5);
2498 }
2499 if (!empty($this->ref)) {
2500 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2501 }
2502 if (!empty($this->ref_fourn)) {
2503 $datas['ref_supplier'] = '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_fourn;
2504 }
2505 if (!empty($this->total_ht)) {
2506 $datas['amount_ht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2507 }
2508 if (!empty($this->total_tva)) {
2509 $datas['amount_vat'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2510 }
2511 if (!empty($this->total_ttc)) {
2512 $datas['amount_ttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2513 }
2514
2515 return $datas;
2516 }
2517
2529 public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2530 {
2531 global $langs, $conf, $user, $hookmanager;
2532
2533 if (!empty($conf->dol_no_mouse_hover)) {
2534 $notooltip = 1; // Force disable tooltips
2535 }
2536
2537 $url = '';
2538 $result = '';
2539 $params = [
2540 'id' => $this->id,
2541 'objecttype' => $this->element,
2542 'option' => $option,
2543 ];
2544 $classfortooltip = 'classfortooltip';
2545 $dataparams = '';
2546 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2547 $classfortooltip = 'classforajaxtooltip';
2548 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
2549 $label = '';
2550 } else {
2551 $label = implode($this->getTooltipContentArray($params));
2552 }
2553
2554 if ($option == '') {
2555 $url = DOL_URL_ROOT.'/supplier_proposal/card.php?id='.$this->id.$get_params;
2556 }
2557 if ($option == 'document') {
2558 $url = DOL_URL_ROOT.'/supplier_proposal/document.php?id='.$this->id.$get_params;
2559 }
2560
2561 if ($option !== 'nolink') {
2562 // Add param to save lastsearch_values or not
2563 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2564 if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2565 $add_save_lastsearch_values = 1;
2566 }
2567 if ($add_save_lastsearch_values) {
2568 $url .= '&save_lastsearch_values=1';
2569 }
2570 }
2571
2572 $linkclose = '';
2573 if (empty($notooltip) && $user->hasRight('propal', 'lire')) {
2574 if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
2575 $label = $langs->trans("ShowSupplierProposal");
2576 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
2577 }
2578 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
2579 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
2580 }
2581
2582 $linkstart = '<a href="'.$url.'"';
2583 $linkstart .= $linkclose.'>';
2584 $linkend = '</a>';
2585
2586 $result .= $linkstart;
2587 if ($withpicto) {
2588 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
2589 }
2590 if ($withpicto != 2) {
2591 $result .= $this->ref;
2592 }
2593 $result .= $linkend;
2594
2595 if ($addlinktonotes) {
2596 $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2597 if ($txttoshow) {
2598 $notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2599 $result .= ' <span class="note inline-block">';
2600 $result .= '<a href="'.DOL_URL_ROOT.'/supplier_proposal/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2601 $result .= img_picto('', 'note');
2602 $result .= '</a>';
2603 //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
2604 //$result.='</a>';
2605 $result .= '</span>';
2606 }
2607 }
2608 global $action;
2609 $hookmanager->initHooks(array($this->element . 'dao'));
2610 $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
2611 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2612 if ($reshook > 0) {
2613 $result = $hookmanager->resPrint;
2614 } else {
2615 $result .= $hookmanager->resPrint;
2616 }
2617 return $result;
2618 }
2619
2625 public function getLinesArray()
2626 {
2627 // For other object, here we call fetch_lines. But fetch_lines does not exists on supplier proposal
2628
2629 $sql = 'SELECT pt.rowid, pt.label as custom_label, pt.description, pt.fk_product, pt.fk_remise_except,';
2630 $sql .= ' pt.qty, pt.tva_tx, pt.vat_src_code, pt.remise_percent, pt.subprice, pt.info_bits,';
2631 $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,';
2632 $sql .= ' pt.product_type, pt.rang, pt.fk_parent_line,';
2633 $sql .= ' p.label as product_label, p.ref, p.fk_product_type, p.rowid as prodid,';
2634 $sql .= ' p.description as product_desc, pt.ref_fourn as ref_supplier,';
2635 $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';
2636 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pt';
2637 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pt.fk_product=p.rowid';
2638 $sql .= ' WHERE pt.fk_supplier_proposal = '.((int) $this->id);
2639 $sql .= ' ORDER BY pt.rang ASC, pt.rowid';
2640
2641 dol_syslog(get_class($this).'::getLinesArray', LOG_DEBUG);
2642 $resql = $this->db->query($sql);
2643 if ($resql) {
2644 $num = $this->db->num_rows($resql);
2645 $i = 0;
2646
2647 while ($i < $num) {
2648 $obj = $this->db->fetch_object($resql);
2649
2650 $this->lines[$i] = new SupplierProposalLine($this->db);
2651 $this->lines[$i]->id = $obj->rowid; // for backward compatibility
2652 $this->lines[$i]->rowid = $obj->rowid;
2653 $this->lines[$i]->label = $obj->custom_label;
2654 $this->lines[$i]->description = $obj->description;
2655 $this->lines[$i]->fk_product = $obj->fk_product;
2656 $this->lines[$i]->ref = $obj->ref;
2657 $this->lines[$i]->product_label = $obj->product_label;
2658 $this->lines[$i]->product_desc = $obj->product_desc;
2659 $this->lines[$i]->fk_product_type = $obj->fk_product_type; // deprecated
2660 $this->lines[$i]->product_type = $obj->product_type;
2661 $this->lines[$i]->qty = $obj->qty;
2662 $this->lines[$i]->subprice = $obj->subprice;
2663 $this->lines[$i]->fk_remise_except = $obj->fk_remise_except;
2664 $this->lines[$i]->remise_percent = $obj->remise_percent;
2665 $this->lines[$i]->tva_tx = $obj->tva_tx;
2666 $this->lines[$i]->vat_src_code = $obj->vat_src_code;
2667 $this->lines[$i]->info_bits = $obj->info_bits;
2668 $this->lines[$i]->total_ht = $obj->total_ht;
2669 $this->lines[$i]->total_tva = $obj->total_tva;
2670 $this->lines[$i]->total_ttc = $obj->total_ttc;
2671 $this->lines[$i]->fk_fournprice = $obj->fk_fournprice;
2672 $marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->lines[$i]->fk_fournprice, $obj->pa_ht);
2673 $this->lines[$i]->pa_ht = $marginInfos[0];
2674 $this->lines[$i]->marge_tx = $marginInfos[1];
2675 $this->lines[$i]->marque_tx = $marginInfos[2];
2676 $this->lines[$i]->fk_parent_line = $obj->fk_parent_line;
2677 $this->lines[$i]->special_code = $obj->special_code;
2678 $this->lines[$i]->rang = $obj->rang;
2679
2680 $this->lines[$i]->ref_fourn = $obj->ref_supplier; // deprecated
2681 $this->lines[$i]->ref_supplier = $obj->ref_supplier;
2682
2683 // Multicurrency
2684 $this->lines[$i]->fk_multicurrency = $obj->fk_multicurrency;
2685 $this->lines[$i]->multicurrency_code = $obj->multicurrency_code;
2686 $this->lines[$i]->multicurrency_subprice = $obj->multicurrency_subprice;
2687 $this->lines[$i]->multicurrency_total_ht = $obj->multicurrency_total_ht;
2688 $this->lines[$i]->multicurrency_total_tva = $obj->multicurrency_total_tva;
2689 $this->lines[$i]->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
2690 $this->lines[$i]->fk_unit = $obj->fk_unit;
2691
2692 $i++;
2693 }
2694 $this->db->free($resql);
2695
2696 return 1;
2697 } else {
2698 $this->error = $this->db->error();
2699 return -1;
2700 }
2701 }
2702
2714 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2715 {
2716 global $conf, $langs;
2717
2718 $langs->load("supplier_proposal");
2719 $outputlangs->load("products");
2720
2721 if (!dol_strlen($modele)) {
2722 $modele = 'aurore';
2723
2724 if ($this->model_pdf) {
2725 $modele = $this->model_pdf;
2726 } elseif (!empty($conf->global->SUPPLIER_PROPOSAL_ADDON_PDF)) {
2727 $modele = $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF;
2728 }
2729 }
2730
2731 $modelpath = "core/modules/supplier_proposal/doc/";
2732
2733 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2734 }
2735
2736
2745 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2746 {
2747 $tables = array(
2748 'supplier_proposal'
2749 );
2750
2751 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2752 }
2753
2762 public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
2763 {
2764 $tables = array(
2765 'supplier_proposaldet'
2766 );
2767
2768 return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
2769 }
2770
2771
2779 public function getKanbanView($option = '', $arraydata = null)
2780 {
2781 global $langs;
2782
2783 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2784
2785 $return = '<div class="box-flex-item box-flex-grow-zero">';
2786 $return .= '<div class="info-box info-box-sm">';
2787 $return .= '<span class="info-box-icon bg-infobox-action">';
2788 $return .= img_picto('', $this->picto);
2789 //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2790 $return .= '</span>';
2791 $return .= '<div class="info-box-content">';
2792 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
2793 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2794 if (property_exists($this, 'socid')) {
2795 $return .= '<span class="info-box-ref"> | '.$this->socid.'</span>';
2796 }
2797 if (property_exists($this, 'delivery_date')) {
2798 $return .= '<br><span class="opacitymedium">'.$langs->trans("DateEnd").'</span> : <span class="info-box-label">'.dol_print_date($this->delivery_date).'</span>';
2799 }
2800 if (property_exists($this, 'total_ttc')) {
2801 $return .='<br><span class="opacitymedium" >'.$langs->trans("AmountHT").' : </span><span class="info-box-label amount">'.price($this->total_ttc).'</span>';
2802 }
2803 if (method_exists($this, 'getLibStatut')) {
2804 $return .= '<br><div class="info-box-status margintoponly">'.$this->getLibStatut(3).'</div>';
2805 }
2806 $return .= '</div>';
2807 $return .= '</div>';
2808 $return .= '</div>';
2809 return $return;
2810 }
2811}
2812
2813
2818{
2822 public $db;
2823
2827 public $error = '';
2828
2832 public $element = 'supplier_proposaldet';
2833
2837 public $table_element = 'supplier_proposaldet';
2838
2839 public $oldline;
2840
2844 public $id;
2845
2849 public $fk_supplier_proposal;
2850
2854 public $fk_parent_line;
2855
2856 public $desc; // Description ligne
2857
2861 public $fk_product; // Id produit predefini
2862
2873 public $product_type = Product::TYPE_PRODUCT;
2874
2875 public $qty;
2876 public $tva_tx;
2877 public $vat_src_code;
2878
2879 public $subprice;
2880 public $remise_percent;
2881
2885 public $fk_remise_except;
2886
2887 public $rang = 0;
2888
2892 public $fk_fournprice;
2893
2894 public $pa_ht;
2895 public $marge_tx;
2896 public $marque_tx;
2897
2898 public $special_code; // Tag for special lines (exlusive tags)
2899 // 1: frais de port
2900 // 2: ecotaxe
2901 // 3: option line (when qty = 0)
2902
2903 public $info_bits = 0; // Liste d'options cumulables:
2904 // Bit 0: 0 si TVA normal - 1 si TVA NPR
2905 // Bit 1: 0 ligne normale - 1 si ligne de remise fixe
2906
2907 public $total_ht; // Total HT de la ligne toute quantite et incluant la remise ligne
2908 public $total_tva; // Total TVA de la ligne toute quantite et incluant la remise ligne
2909 public $total_ttc; // Total TTC de la ligne toute quantite et incluant la remise ligne
2910
2911 public $date_start;
2912 public $date_end;
2913
2914 // From llx_product
2919 public $ref;
2920
2925 public $product_ref;
2926
2931 public $libelle;
2932
2937 public $product_label;
2938
2943 public $product_desc;
2944
2945 public $localtax1_tx; // Local tax 1
2946 public $localtax2_tx; // Local tax 2
2947 public $localtax1_type; // Local tax 1 type
2948 public $localtax2_type; // Local tax 2 type
2949 public $total_localtax1; // Line total local tax 1
2950 public $total_localtax2; // Line total local tax 2
2951
2952 public $skip_update_total; // Skip update price total for special lines
2953
2954 public $ref_fourn;
2955 public $ref_supplier;
2956
2957 // Multicurrency
2961 public $fk_multicurrency;
2962
2963 public $multicurrency_code;
2964 public $multicurrency_subprice;
2965 public $multicurrency_total_ht;
2966 public $multicurrency_total_tva;
2967 public $multicurrency_total_ttc;
2968
2974 public function __construct($db)
2975 {
2976 $this->db = $db;
2977 }
2978
2985 public function fetch($rowid)
2986 {
2987 $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,';
2988 $sql .= ' pd.date_start, pd.date_end,';
2989 $sql .= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice,';
2990 $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,';
2991 $sql .= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
2992 $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
2993 $sql .= ' pd.product_type, pd.ref_fourn as ref_produit_fourn,';
2994 $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';
2995 $sql .= ' FROM '.MAIN_DB_PREFIX.'supplier_proposaldet as pd';
2996 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
2997 $sql .= ' WHERE pd.rowid = '.((int) $rowid);
2998
2999 $result = $this->db->query($sql);
3000 if ($result) {
3001 $objp = $this->db->fetch_object($result);
3002
3003 $this->id = $objp->rowid;
3004 $this->fk_supplier_proposal = $objp->fk_supplier_proposal;
3005 $this->fk_parent_line = $objp->fk_parent_line;
3006 $this->label = $objp->custom_label;
3007 $this->desc = $objp->description;
3008 $this->qty = $objp->qty;
3009 $this->subprice = $objp->subprice;
3010 $this->tva_tx = $objp->tva_tx;
3011 $this->remise_percent = $objp->remise_percent;
3012 $this->fk_remise_except = $objp->fk_remise_except;
3013 $this->fk_product = $objp->fk_product;
3014 $this->info_bits = $objp->info_bits;
3015 $this->date_start = $this->db->jdate($objp->date_start);
3016 $this->date_end = $this->db->jdate($objp->date_end);
3017
3018 $this->total_ht = $objp->total_ht;
3019 $this->total_tva = $objp->total_tva;
3020 $this->total_ttc = $objp->total_ttc;
3021
3022 $this->fk_fournprice = $objp->fk_fournprice;
3023
3024 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
3025 $this->pa_ht = $marginInfos[0];
3026 $this->marge_tx = $marginInfos[1];
3027 $this->marque_tx = $marginInfos[2];
3028
3029 $this->special_code = $objp->special_code;
3030 $this->product_type = $objp->product_type;
3031 $this->rang = $objp->rang;
3032
3033 $this->ref = $objp->product_ref; // deprecated
3034 $this->product_ref = $objp->product_ref;
3035 $this->libelle = $objp->product_label; // deprecated
3036 $this->product_label = $objp->product_label;
3037 $this->product_desc = $objp->product_desc;
3038
3039 $this->ref_fourn = $objp->ref_produit_fourn;
3040
3041 // Multicurrency
3042 $this->fk_multicurrency = $objp->fk_multicurrency;
3043 $this->multicurrency_code = $objp->multicurrency_code;
3044 $this->multicurrency_subprice = $objp->multicurrency_subprice;
3045 $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
3046 $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
3047 $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
3048 $this->fk_unit = $objp->fk_unit;
3049
3050 $this->db->free($result);
3051 return 1;
3052 } else {
3053 dol_print_error($this->db);
3054 return -1;
3055 }
3056 }
3057
3064 public function insert($notrigger = 0)
3065 {
3066 global $conf, $langs, $user;
3067
3068 $error = 0;
3069
3070 dol_syslog(get_class($this)."::insert rang=".$this->rang);
3071
3072 // Clean parameters
3073 if (empty($this->tva_tx)) {
3074 $this->tva_tx = 0;
3075 }
3076 if (empty($this->vat_src_code)) {
3077 $this->vat_src_code = '';
3078 }
3079 if (empty($this->localtax1_tx)) {
3080 $this->localtax1_tx = 0;
3081 }
3082 if (empty($this->localtax2_tx)) {
3083 $this->localtax2_tx = 0;
3084 }
3085 if (empty($this->localtax1_type)) {
3086 $this->localtax1_type = 0;
3087 }
3088 if (empty($this->localtax2_type)) {
3089 $this->localtax2_type = 0;
3090 }
3091 if (empty($this->total_localtax1)) {
3092 $this->total_localtax1 = 0;
3093 }
3094 if (empty($this->total_localtax2)) {
3095 $this->total_localtax2 = 0;
3096 }
3097 if (empty($this->rang)) {
3098 $this->rang = 0;
3099 }
3100 if (empty($this->remise_percent)) {
3101 $this->remise_percent = 0;
3102 }
3103 if (empty($this->info_bits)) {
3104 $this->info_bits = 0;
3105 }
3106 if (empty($this->special_code)) {
3107 $this->special_code = 0;
3108 }
3109 if (empty($this->fk_parent_line)) {
3110 $this->fk_parent_line = 0;
3111 }
3112 if (empty($this->fk_fournprice)) {
3113 $this->fk_fournprice = 0;
3114 }
3115 if (empty($this->fk_unit)) {
3116 $this->fk_unit = 0;
3117 }
3118 if (empty($this->subprice)) {
3119 $this->subprice = 0;
3120 }
3121
3122 if (empty($this->pa_ht)) {
3123 $this->pa_ht = 0;
3124 }
3125
3126 // if buy price not defined, define buyprice as configured in margin admin
3127 if ($this->pa_ht == 0) {
3128 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3129 if ($result < 0) {
3130 return $result;
3131 } else {
3132 $this->pa_ht = $result;
3133 }
3134 }
3135
3136 // Check parameters
3137 if ($this->product_type < 0) {
3138 return -1;
3139 }
3140
3141 $this->db->begin();
3142
3143 // Insert line into database
3144 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'supplier_proposaldet';
3145 $sql .= ' (fk_supplier_proposal, fk_parent_line, label, description, fk_product, product_type,';
3146 $sql .= ' date_start, date_end,';
3147 $sql .= ' fk_remise_except, qty, tva_tx, vat_src_code, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3148 $sql .= ' subprice, remise_percent, ';
3149 $sql .= ' info_bits, ';
3150 $sql .= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
3151 $sql .= ' ref_fourn,';
3152 $sql .= ' fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc, fk_unit)';
3153 $sql .= " VALUES (".$this->fk_supplier_proposal.",";
3154 $sql .= " ".($this->fk_parent_line > 0 ? ((int) $this->fk_parent_line) : "null").",";
3155 $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
3156 $sql .= " '".$this->db->escape($this->desc)."',";
3157 $sql .= " ".($this->fk_product ? ((int) $this->fk_product) : "null").",";
3158 $sql .= " '".$this->db->escape($this->product_type)."',";
3159 $sql .= " ".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3160 $sql .= " ".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3161 $sql .= " ".($this->fk_remise_except ? ((int) $this->fk_remise_except) : "null").",";
3162 $sql .= " ".price2num($this->qty, 'MS').",";
3163 $sql .= " ".price2num($this->tva_tx).",";
3164 $sql .= " '".$this->db->escape($this->vat_src_code)."',";
3165 $sql .= " ".price2num($this->localtax1_tx).",";
3166 $sql .= " ".price2num($this->localtax2_tx).",";
3167 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3168 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3169 $sql .= " ".price2num($this->subprice, 'MU') .",";
3170 $sql .= " ".((float) $this->remise_percent).",";
3171 $sql .= " ".(isset($this->info_bits) ? ((int) $this->info_bits) : "null").",";
3172 $sql .= " ".price2num($this->total_ht, 'MT').",";
3173 $sql .= " ".price2num($this->total_tva, 'MT').",";
3174 $sql .= " ".price2num($this->total_localtax1, 'MT').",";
3175 $sql .= " ".price2num($this->total_localtax2, 'MT').",";
3176 $sql .= " ".price2num($this->total_ttc, 'MT').",";
3177 $sql .= " ".(!empty($this->fk_fournprice) ? ((int) $this->fk_fournprice) : "null").",";
3178 $sql .= " ".(isset($this->pa_ht) ? price2num($this->pa_ht, 'MU') : "null").",";
3179 $sql .= ' '.((int) $this->special_code).',';
3180 $sql .= ' '.((int) $this->rang).',';
3181 $sql .= " '".$this->db->escape($this->ref_fourn)."'";
3182 $sql .= ", ".($this->fk_multicurrency > 0 ? ((int) $this->fk_multicurrency) : 'null');
3183 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3184 $sql .= ", ".price2num($this->multicurrency_subprice, 'CU');
3185 $sql .= ", ".price2num($this->multicurrency_total_ht, 'CT');
3186 $sql .= ", ".price2num($this->multicurrency_total_tva, 'CT');
3187 $sql .= ", ".price2num($this->multicurrency_total_ttc, 'CT');
3188 $sql .= ", ".($this->fk_unit ? ((int) $this->fk_unit) : 'null');
3189 $sql .= ')';
3190
3191 dol_syslog(get_class($this).'::insert', LOG_DEBUG);
3192 $resql = $this->db->query($sql);
3193 if ($resql) {
3194 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'supplier_proposaldet');
3195
3196 if (!$error) {
3197 $result = $this->insertExtraFields();
3198 if ($result < 0) {
3199 $error++;
3200 }
3201 }
3202
3203 if (!$error && !$notrigger) {
3204 // Call trigger
3205 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_INSERT', $user);
3206 if ($result < 0) {
3207 $this->db->rollback();
3208 return -1;
3209 }
3210 // End call triggers
3211 }
3212
3213 $this->db->commit();
3214 return 1;
3215 } else {
3216 $this->error = $this->db->error()." sql=".$sql;
3217 $this->db->rollback();
3218 return -1;
3219 }
3220 }
3221
3227 public function delete()
3228 {
3229 global $conf, $langs, $user;
3230
3231 $error = 0;
3232 $this->db->begin();
3233
3234 $sql = "DELETE FROM ".MAIN_DB_PREFIX."supplier_proposaldet WHERE rowid = ".((int) $this->id);
3235 dol_syslog("SupplierProposalLine::delete", LOG_DEBUG);
3236 if ($this->db->query($sql)) {
3237 // Remove extrafields
3238 if (!$error) {
3239 $result = $this->deleteExtraFields();
3240 if ($result < 0) {
3241 $error++;
3242 dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3243 }
3244 }
3245
3246 // Call trigger
3247 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_DELETE', $user);
3248 if ($result < 0) {
3249 $this->db->rollback();
3250 return -1;
3251 }
3252 // End call triggers
3253
3254 $this->db->commit();
3255
3256 return 1;
3257 } else {
3258 $this->error = $this->db->error()." sql=".$sql;
3259 $this->db->rollback();
3260 return -1;
3261 }
3262 }
3263
3270 public function update($notrigger = 0)
3271 {
3272 global $conf, $langs, $user;
3273
3274 $error = 0;
3275
3276 // Clean parameters
3277 if (empty($this->tva_tx)) {
3278 $this->tva_tx = 0;
3279 }
3280 if (empty($this->localtax1_tx)) {
3281 $this->localtax1_tx = 0;
3282 }
3283 if (empty($this->localtax2_tx)) {
3284 $this->localtax2_tx = 0;
3285 }
3286 if (empty($this->total_localtax1)) {
3287 $this->total_localtax1 = 0;
3288 }
3289 if (empty($this->total_localtax2)) {
3290 $this->total_localtax2 = 0;
3291 }
3292 if (empty($this->localtax1_type)) {
3293 $this->localtax1_type = 0;
3294 }
3295 if (empty($this->localtax2_type)) {
3296 $this->localtax2_type = 0;
3297 }
3298 if (empty($this->marque_tx)) {
3299 $this->marque_tx = 0;
3300 }
3301 if (empty($this->marge_tx)) {
3302 $this->marge_tx = 0;
3303 }
3304 if (empty($this->remise_percent)) {
3305 $this->remise_percent = 0;
3306 }
3307 if (empty($this->info_bits)) {
3308 $this->info_bits = 0;
3309 }
3310 if (empty($this->special_code)) {
3311 $this->special_code = 0;
3312 }
3313 if (empty($this->fk_parent_line)) {
3314 $this->fk_parent_line = 0;
3315 }
3316 if (empty($this->fk_fournprice)) {
3317 $this->fk_fournprice = 0;
3318 }
3319 if (empty($this->fk_unit)) {
3320 $this->fk_unit = 0;
3321 }
3322 if (empty($this->subprice)) {
3323 $this->subprice = 0;
3324 }
3325
3326 if (empty($this->pa_ht)) {
3327 $this->pa_ht = 0;
3328 }
3329
3330 // if buy price not defined, define buyprice as configured in margin admin
3331 if ($this->pa_ht == 0) {
3332 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3333 if ($result < 0) {
3334 return $result;
3335 } else {
3336 $this->pa_ht = $result;
3337 }
3338 }
3339
3340 $this->db->begin();
3341
3342 // Mise a jour ligne en base
3343 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3344 $sql .= " description='".$this->db->escape($this->desc)."'";
3345 $sql .= " , label=".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
3346 $sql .= " , product_type=".((int) $this->product_type);
3347 $sql .= " , date_start=".($this->date_start ? "'".$this->db->idate($this->date_start)."'" : "null");
3348 $sql .= " , date_end=".($this->date_end ? "'".$this->db->idate($this->date_end)."'" : "null");
3349 $sql .= " , tva_tx='".price2num($this->tva_tx)."'";
3350 $sql .= " , localtax1_tx=".price2num($this->localtax1_tx);
3351 $sql .= " , localtax2_tx=".price2num($this->localtax2_tx);
3352 $sql .= " , localtax1_type='".$this->db->escape($this->localtax1_type)."'";
3353 $sql .= " , localtax2_type='".$this->db->escape($this->localtax2_type)."'";
3354 $sql .= " , qty='".price2num($this->qty)."'";
3355 $sql .= " , subprice=".price2num($this->subprice);
3356 $sql .= " , remise_percent=".price2num($this->remise_percent);
3357 $sql .= " , info_bits='".$this->db->escape($this->info_bits)."'";
3358 if (empty($this->skip_update_total)) {
3359 $sql .= " , total_ht=".price2num($this->total_ht);
3360 $sql .= " , total_tva=".price2num($this->total_tva);
3361 $sql .= " , total_ttc=".price2num($this->total_ttc);
3362 $sql .= " , total_localtax1=".price2num($this->total_localtax1);
3363 $sql .= " , total_localtax2=".price2num($this->total_localtax2);
3364 }
3365 $sql .= " , fk_product_fournisseur_price=".(!empty($this->fk_fournprice) ? "'".$this->db->escape($this->fk_fournprice)."'" : "null");
3366 $sql .= " , buy_price_ht=".price2num($this->pa_ht);
3367 if (strlen($this->special_code)) {
3368 $sql .= " , special_code=".((int) $this->special_code);
3369 }
3370 $sql .= " , fk_parent_line=".($this->fk_parent_line > 0 ? $this->fk_parent_line : "null");
3371 if (!empty($this->rang)) {
3372 $sql .= ", rang=".((int) $this->rang);
3373 }
3374 $sql .= " , ref_fourn=".(!empty($this->ref_fourn) ? "'".$this->db->escape($this->ref_fourn)."'" : "null");
3375 $sql .= " , fk_unit=".($this->fk_unit ? $this->fk_unit : 'null');
3376
3377 // Multicurrency
3378 $sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice);
3379 $sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
3380 $sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
3381 $sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
3382
3383 $sql .= " WHERE rowid = ".((int) $this->id);
3384
3385 dol_syslog(get_class($this)."::update", LOG_DEBUG);
3386 $resql = $this->db->query($sql);
3387 if ($resql) {
3388 if (!$error) {
3389 $result = $this->insertExtraFields();
3390 if ($result < 0) {
3391 $error++;
3392 }
3393 }
3394
3395 if (!$error && !$notrigger) {
3396 // Call trigger
3397 $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_MODIFY', $user);
3398 if ($result < 0) {
3399 $this->db->rollback();
3400 return -1;
3401 }
3402 // End call triggers
3403 }
3404
3405 $this->db->commit();
3406 return 1;
3407 } else {
3408 $this->error = $this->db->error();
3409 $this->db->rollback();
3410 return -2;
3411 }
3412 }
3413
3414 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3421 public function update_total()
3422 {
3423 // phpcs:enable
3424 $this->db->begin();
3425
3426 // Mise a jour ligne en base
3427 $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposaldet SET";
3428 $sql .= " total_ht=".price2num($this->total_ht, 'MT');
3429 $sql .= ",total_tva=".price2num($this->total_tva, 'MT');
3430 $sql .= ",total_ttc=".price2num($this->total_ttc, 'MT');
3431 $sql .= " WHERE rowid = ".((int) $this->id);
3432
3433 dol_syslog("SupplierProposalLine::update_total", LOG_DEBUG);
3434
3435 $resql = $this->db->query($sql);
3436 if ($resql) {
3437 $this->db->commit();
3438 return 1;
3439 } else {
3440 $this->error = $this->db->error();
3441 $this->db->rollback();
3442 return -2;
3443 }
3444 }
3445}
$object ref
Definition info.php:78
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.
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)
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid='', $f_user=null, $notrigger=0)
Delete all links between an object $this.
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.
set_remise_percent($user, $remise)
Set an overall discount on the proposal.
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)
Reopen the commercial proposal.
const STATUS_VALIDATED
Validated status.
createPriceFournisseur($product, $user)
Create ProductFournisseur.
getLibStatut($mode=0)
Return label of status of proposal (draft, validated, ...)
set_remise_absolue($user, $remise)
Set an absolute overall discount on the proposal.
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.
print $langs trans("Ref").' m m m statut
Definition index.php:152
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 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.
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...
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