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