Passed
Push — EXTRACT_CLASSES ( ae6b5c...83d77a )
by Rafael
60:14 queued 23:58
created

SupplierProposal   F

Complexity

Total Complexity 349

Size/Duplication

Total Lines 2743
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 1490
dl 0
loc 2743
rs 0.8
c 0
b 0
f 0
wmc 349

34 Methods

Rating   Name   Duplication   Size   Complexity  
F delete() 0 96 18
A updatePriceFournisseur() 0 14 3
C liste_array() 0 64 14
F create() 0 217 43
B add_product() 0 44 6
F addline() 0 250 45
C createFromClone() 0 81 14
B LibStatut() 0 34 8
A info() 0 27 3
A setDeliveryDate() 0 17 4
A replaceProduct() 0 7 1
A replaceThirdparty() 0 7 1
C fetch() 0 186 8
F valid() 0 112 20
B getKanbanView() 0 33 9
B initAsSpecimen() 0 73 6
F load_board() 0 67 13
B getLinesArray() 0 75 3
F updateline() 0 157 25
B getTooltipContentArray() 0 34 8
B insert_discount() 0 54 5
B getNextNumRef() 0 39 6
A __construct() 0 9 1
B reopen() 0 47 10
F getNomUrl() 0 89 25
A set_date_livraison() 0 4 1
B updateOrCreatePriceFournisseur() 0 31 7
B setDraft() 0 40 7
A generateDocument() 0 20 4
A getLibStatut() 0 3 2
A loadStateBoard() 0 29 4
F cloture() 0 64 14
A deleteLine() 0 19 3
B createPriceFournisseur() 0 53 8

How to fix   Complexity   

Complex Class

Complex classes like SupplierProposal often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SupplierProposal, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/* Copyright (C) 2002-2004  Rodolphe Quiedeville        <[email protected]>
4
 * Copyright (C) 2004       Eric Seigne				    <[email protected]>
5
 * Copyright (C) 2004-2011  Laurent Destailleur		    <[email protected]>
6
 * Copyright (C) 2005       Marc Barilley			    <[email protected]>
7
 * Copyright (C) 2005-2013  Regis Houssin			    <[email protected]>
8
 * Copyright (C) 2006       Andre Cianfarani			<[email protected]>
9
 * Copyright (C) 2008       Raphael Bertrand			<[email protected]>
10
 * Copyright (C) 2010-2020  Juanjo Menent			    <[email protected]>
11
 * Copyright (C) 2010-2018  Philippe Grand			    <[email protected]>
12
 * Copyright (C) 2012-2014  Christophe Battarel  	    <[email protected]>
13
 * Copyright (C) 2013       Florian Henry		  	    <[email protected]>
14
 * Copyright (C) 2014       Marcos García               <[email protected]>
15
 * Copyright (C) 2016       Ferran Marcet               <[email protected]>
16
 * Copyright (C) 2018       Nicolas ZABOURI			    <[email protected]>
17
 * Copyright (C) 2019-2024  Frédéric France             <[email protected]>
18
 * Copyright (C) 2020		Tobias Sekan			    <[email protected]>
19
 * Copyright (C) 2022       Gauthier VERDOL     		<[email protected]>
20
 * Copyright (C) 2024		MDW							<[email protected]>
21
 * Copyright (C) 2024       Rafael San José             <[email protected]>
22
 *
23
 * This program is free software; you can redistribute it and/or modify
24
 * it under the terms of the GNU General Public License as published by
25
 * the Free Software Foundation; either version 3 of the License, or
26
 * (at your option) any later version.
27
 *
28
 * This program is distributed in the hope that it will be useful,
29
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
30
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
31
 * GNU General Public License for more details.
32
 *
33
 * You should have received a copy of the GNU General Public License
34
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
35
 */
36
37
namespace Dolibarr\Code\SupplierProposal\Classes;
38
39
use Dolibarr\Code\MultiCurrency\Classes\MultiCurrency;
40
use Dolibarr\Core\Base\CommonObject;
41
42
/**
43
 *  \file       htdocs/supplier_proposal/class/supplier_proposal.class.php
44
 *  \brief      File of class to manage supplier proposals
45
 */
46
47
require_once constant('DOL_DOCUMENT_ROOT') . '/fourn/class/fournisseur.product.class.php';
48
require_once constant('DOL_DOCUMENT_ROOT') . '/product/class/product.class.php';
49
require_once constant('DOL_DOCUMENT_ROOT') . '/contact/class/contact.class.php';
50
require_once constant('DOL_DOCUMENT_ROOT') . '/margin/lib/margins.lib.php';
51
52
/**
53
 *  Class to manage price ask supplier
54
 */
55
class SupplierProposal extends CommonObject
56
{
57
    use CommonIncoterm;
58
59
    /**
60
     * @var string ID to identify managed object
61
     */
62
    public $element = 'supplier_proposal';
63
64
    /**
65
     * @var string Name of table without prefix where object is stored
66
     */
67
    public $table_element = 'supplier_proposal';
68
69
    /**
70
     * @var string    Name of subtable line
71
     */
72
    public $table_element_line = 'supplier_proposaldet';
73
74
    /**
75
     * @var string Name of class line
76
     */
77
    public $class_element_line = 'SupplierProposalLine';
78
    /**
79
     * @var string Field with ID of parent key if this field has a parent
80
     */
81
    public $fk_element = 'fk_supplier_proposal';
82
83
    /**
84
     * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png
85
     */
86
    public $picto = 'supplier_proposal';
87
88
    /**
89
     * 0=Default, 1=View may be restricted to sales representative only if no permission to see all or to company of external user if external user
90
     * @var integer
91
     */
92
    public $restrictiononfksoc = 1;
93
94
    /**
95
     * {@inheritdoc}
96
     */
97
    protected $table_ref_field = 'ref';
98
99
    public $socid; // Id client
100
101
    /**
102
     * @deprecated
103
     * @see $user_author_id
104
     */
105
    public $author;
106
107
    public $ref_fourn; //Reference saisie lors de l'ajout d'une ligne à la demande
108
    public $ref_supplier; //Reference saisie lors de l'ajout d'une ligne à la demande
109
110
    /**
111
     * @deprecated
112
     */
113
    public $statut; // 0 (draft), 1 (validated), 2 (signed), 3 (not signed), 4 (processed/billed)
114
115
    /**
116
     * @var integer|string Date of proposal
117
     */
118
    public $date;
119
120
    /**
121
     * @var integer|string date_livraison
122
     */
123
    public $delivery_date;
124
125
    /**
126
     * @deprecated
127
     * @see $date_creation
128
     */
129
    public $datec;
130
131
    /**
132
     * @var integer|string date_creation
133
     */
134
    public $date_creation;
135
136
    /**
137
     * @deprecated
138
     * @see $date_validation
139
     */
140
    public $datev;
141
142
    /**
143
     * @var integer|string date_validation
144
     */
145
    public $date_validation;
146
147
148
    public $user_author_id;
149
150
    /**
151
     * @deprecated
152
     * @see $price_ht
153
     */
154
    public $price;
155
156
    /**
157
     * @deprecated
158
     * @see $total_tva
159
     */
160
    public $tva;
161
162
    /**
163
     * @deprecated
164
     * @see $total_ttc
165
     */
166
    public $total;
167
168
    public $cond_reglement_code;
169
    public $cond_reglement_doc;     // label doc
170
171
    public $mode_reglement_code;
172
    /**
173
     * @deprecated
174
     * @var string  Mode reglement
175
     */
176
    public $mode_reglement;
177
178
    public $extraparams = array();
179
    public $lines = array();
180
    public $line;
181
182
    public $labelStatus = array();
183
    public $labelStatusShort = array();
184
185
    public $nbtodo;
186
    public $nbtodolate;
187
188
    // Multicurrency
189
    /**
190
     * @var int ID
191
     */
192
    public $fk_multicurrency;
193
194
    public $multicurrency_code;
195
    public $multicurrency_tx;
196
    public $multicurrency_total_ht;
197
    public $multicurrency_total_tva;
198
    public $multicurrency_total_ttc;
199
200
    /**
201
     * Draft status
202
     */
203
    const STATUS_DRAFT = 0;
204
205
    /**
206
     * Validated status
207
     */
208
    const STATUS_VALIDATED = 1;
209
210
    /**
211
     * Signed quote
212
     */
213
    const STATUS_SIGNED = 2;
214
215
    /**
216
     * Not signed quote, canceled
217
     */
218
    const STATUS_NOTSIGNED = 3;
219
220
    /**
221
     * Billed or closed/processed quote
222
     */
223
    const STATUS_CLOSE = 4;
224
225
226
227
    /**
228
     *  Constructor
229
     *
230
     *  @param      DoliDB  $db         Database handler
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\SupplierProposal\Classes\DoliDB was not found. Did you mean DoliDB? If so, make sure to prefix the type with \.
Loading history...
231
     *  @param      int     $socid      Id third party
232
     *  @param      int     $supplier_proposalid   Id supplier_proposal
233
     */
234
    public function __construct($db, $socid = 0, $supplier_proposalid = 0)
235
    {
236
        global $conf, $langs;
237
238
        $this->db = $db;
239
240
        $this->ismultientitymanaged = 1;
241
        $this->socid = $socid;
242
        $this->id = $supplier_proposalid;
243
    }
244
245
246
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
247
    /**
248
     *  Add line into array ->lines
249
     *
250
     *  @param  int     $idproduct          Product Id to add
251
     *  @param  float   $qty                Quantity
252
     *  @param  int     $remise_percent     Discount effected on Product
253
     *  @return int                         Return integer <0 if KO, >0 if OK
254
     *
255
     *  TODO    Remplacer les appels a cette fonction par generation object Ligne
256
     */
257
    public function add_product($idproduct, $qty, $remise_percent = 0)
258
    {
259
		// phpcs:enable
260
        global $conf, $mysoc;
261
262
        if (!$qty) {
263
            $qty = 1;
264
        }
265
266
        dol_syslog(get_class($this) . "::add_product $idproduct, $qty, $remise_percent");
267
        if ($idproduct > 0) {
268
            $prod = new Product($this->db);
269
            $prod->fetch($idproduct);
270
271
            $productdesc = $prod->description;
272
273
            $tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
274
            $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
275
            if (empty($tva_tx)) {
276
                $tva_npr = 0;
277
            }
278
            $localtax1_tx = get_localtax($tva_tx, 1, $mysoc, $this->thirdparty, $tva_npr);
279
            $localtax2_tx = get_localtax($tva_tx, 2, $mysoc, $this->thirdparty, $tva_npr);
280
281
            // multiprix
282
            if ($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level) {
283
                $price = $prod->multiprices[$this->thirdparty->price_level];
284
            } else {
285
                $price = $prod->price;
286
            }
287
288
            $line = new SupplierProposalLine($this->db);
289
290
            $line->fk_product = $idproduct;
291
            $line->desc = $productdesc;
292
            $line->qty = $qty;
293
            $line->subprice = $price;
294
            $line->remise_percent = $remise_percent;
295
            $line->tva_tx = $tva_tx;
296
297
            $this->lines[] = $line;
298
            return 1;
299
        }
300
        return -1;
301
    }
302
303
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
304
    /**
305
     *  Adding line of fixed discount in the proposal in DB
306
     *
307
     *  @param     int      $idremise           Id of fixed discount
308
     *  @return    int                          >0 if OK, <0 if KO
309
     */
310
    public function insert_discount($idremise)
311
    {
312
		// phpcs:enable
313
        global $langs;
314
315
        include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php';
316
        include_once DOL_DOCUMENT_ROOT . '/core/class/discount.class.php';
317
318
        $this->db->begin();
319
320
        $remise = new DiscountAbsolute($this->db);
321
        $result = $remise->fetch($idremise);
322
323
        if ($result > 0) {
324
            if ($remise->fk_facture) {  // Protection against multiple submission
325
                $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
326
                $this->db->rollback();
327
                return -5;
328
            }
329
330
            $supplier_proposalligne = new SupplierProposalLine($this->db);
331
            $supplier_proposalligne->fk_supplier_proposal = $this->id;
332
            $supplier_proposalligne->fk_remise_except = $remise->id;
333
            $supplier_proposalligne->desc = $remise->description; // Description ligne
334
            $supplier_proposalligne->tva_tx = $remise->tva_tx;
335
            $supplier_proposalligne->subprice = -$remise->amount_ht;
336
            $supplier_proposalligne->fk_product = 0; // Id produit predefini
337
            $supplier_proposalligne->qty = 1;
338
            $supplier_proposalligne->remise_percent = 0;
339
            $supplier_proposalligne->rang = -1;
340
            $supplier_proposalligne->info_bits = 2;
341
342
            $supplier_proposalligne->total_ht  = -$remise->amount_ht;
343
            $supplier_proposalligne->total_tva = -$remise->amount_tva;
344
            $supplier_proposalligne->total_ttc = -$remise->amount_ttc;
345
346
            $result = $supplier_proposalligne->insert();
347
            if ($result > 0) {
348
                $result = $this->update_price(1);
349
                if ($result > 0) {
350
                    $this->db->commit();
351
                    return 1;
352
                } else {
353
                    $this->db->rollback();
354
                    return -1;
355
                }
356
            } else {
357
                $this->error = $supplier_proposalligne->error;
358
                $this->db->rollback();
359
                return -2;
360
            }
361
        } else {
362
            $this->db->rollback();
363
            return -2;
364
        }
365
    }
366
367
    /**
368
     *      Add a proposal line into database (linked to product/service or not)
369
     *      Les parameters sont deja cense etre juste et avec valeurs finales a l'appel
370
     *      de cette methode. Aussi, pour le taux tva, il doit deja avoir ete defini
371
     *      par l'appelant par la methode get_default_tva(societe_vendeuse,societe_acheteuse,'',produit)
372
     *      et le desc doit deja avoir la bonne valeur (a l'appelant de gerer le multilangue)
373
     *
374
     *      @param      string      $desc               Description de la ligne
375
     *      @param      double      $pu_ht              Prix unitaire
376
     *      @param      double      $qty                Quantite
377
     *      @param      double      $txtva              Taux de tva
378
     *      @param      double      $txlocaltax1        Local tax 1 rate
379
     *      @param      double      $txlocaltax2        Local tax 2 rate
380
     *      @param      int         $fk_product         Product/Service ID predefined
381
     *      @param      double      $remise_percent     Percentage discount of the line
382
     *      @param      string      $price_base_type    HT or TTC
383
     *      @param      double      $pu_ttc             Prix unitaire TTC
384
     *      @param      int         $info_bits          Bits of type of lines
385
     *      @param      int         $type               Type of line (product, service)
386
     *      @param      int         $rang               Position of line
387
     *      @param      int         $special_code       Special code (also used by externals modules!)
388
     *      @param      int         $fk_parent_line     Id of parent line
389
     *      @param      int         $fk_fournprice      Id supplier price. If 0, we will take best price. If -1 we keep it empty.
390
     *      @param      int         $pa_ht              Buying price without tax
391
     *      @param      string      $label              ???
392
     *      @param      array       $array_options      extrafields array
393
     *      @param      string      $ref_supplier           Supplier price reference
394
     *      @param      int         $fk_unit            Id of the unit to use.
395
     *      @param      string      $origin             'order', 'supplier_proposal', ...
396
     *      @param      int         $origin_id          Id of origin line
397
     *      @param      double      $pu_ht_devise       Amount in currency
398
     *      @param      int         $date_start         Date start
399
     *      @param      int         $date_end           Date end
400
     *      @return     int                             >0 if OK, <0 if KO
401
     *
402
     *      @see        add_product()
403
     */
404
    public function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1 = 0, $txlocaltax2 = 0, $fk_product = 0, $remise_percent = 0, $price_base_type = 'HT', $pu_ttc = 0, $info_bits = 0, $type = 0, $rang = -1, $special_code = 0, $fk_parent_line = 0, $fk_fournprice = 0, $pa_ht = 0, $label = '', $array_options = [], $ref_supplier = '', $fk_unit = 0, $origin = '', $origin_id = 0, $pu_ht_devise = 0, $date_start = 0, $date_end = 0)
405
    {
406
        global $mysoc, $conf, $langs;
407
408
        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");
409
        include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php';
410
411
        // Clean parameters
412
        if (empty($remise_percent)) {
413
            $remise_percent = 0;
414
        }
415
        if (empty($qty)) {
416
            $qty = 0;
417
        }
418
        if (empty($info_bits)) {
419
            $info_bits = 0;
420
        }
421
        if (empty($rang)) {
422
            $rang = 0;
423
        }
424
        if (empty($fk_parent_line) || $fk_parent_line < 0) {
425
            $fk_parent_line = 0;
426
        }
427
        if (empty($pu_ht)) {
428
            $pu_ht = 0;
429
        }
430
431
        $remise_percent = price2num($remise_percent);
432
        $qty = (float) price2num($qty);
433
        $pu_ht = price2num($pu_ht);
434
        $pu_ttc = price2num($pu_ttc);
435
        if (!preg_match('/\((.*)\)/', (string) $txtva)) {
436
            $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
437
        }
438
        $txlocaltax1 = price2num($txlocaltax1);
439
        $txlocaltax2 = price2num($txlocaltax2);
440
        $pa_ht = price2num($pa_ht);
441
        if ($price_base_type == 'HT') {
442
            $pu = $pu_ht;
443
        } else {
444
            $pu = $pu_ttc;
445
        }
446
447
        // Check parameters
448
        if ($type < 0) {
449
            return -1;
450
        }
451
452
        if ($this->statut == self::STATUS_DRAFT) {
453
            $this->db->begin();
454
455
            if ($fk_product > 0) {
456
                if (getDolGlobalString('SUPPLIER_PROPOSAL_WITH_PREDEFINED_PRICES_ONLY')) {
457
                    // Check quantity is enough
458
                    dol_syslog(get_class($this) . "::addline we check supplier prices fk_product=" . $fk_product . " fk_fournprice=" . $fk_fournprice . " qty=" . $qty . " ref_supplier=" . $ref_supplier);
459
                    $productsupplier = new ProductFournisseur($this->db);
460
                    if ($productsupplier->fetch($fk_product) > 0) {
461
                        $product_type = $productsupplier->type;
462
                        $label = $productsupplier->label;
463
                        $fk_prod_fourn_price = $fk_fournprice;
464
465
                        // We use 'none' instead of $ref_supplier, because fourn_ref may not exists anymore. So we will take the first supplier price ok.
466
                        // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
467
                        // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
468
                        $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
469
                        if ($result > 0) {
470
                            $pu = $productsupplier->fourn_pu; // Unit price supplier price set by get_buyprice
471
                            $ref_supplier = $productsupplier->ref_supplier; // Ref supplier price set by get_buyprice
472
                            // is remise percent not keyed but present for the product we add it
473
                            if ($remise_percent == 0 && $productsupplier->remise_percent != 0) {
474
                                $remise_percent = $productsupplier->remise_percent;
475
                            }
476
                        }
477
                        if ($result == 0) {                   // If result == 0, we failed to found the supplier reference price
478
                            $langs->load("errors");
479
                            $this->error = "Ref " . $productsupplier->ref . " " . $langs->trans("ErrorQtyTooLowForThisSupplier");
480
                            $this->db->rollback();
481
                            dol_syslog(get_class($this) . "::addline we did not found supplier price, so we can't guess unit price");
482
                            //$pu    = $productsupplier->fourn_pu;     // We do not overwrite unit price
483
                            //$ref   = $productsupplier_fourn;    // We do not overwrite ref supplier price
484
                            return -1;
485
                        }
486
                        if ($result == -1) {
487
                            $langs->load("errors");
488
                            $this->error = "Ref " . $productsupplier->ref . " " . $langs->trans("ErrorQtyTooLowForThisSupplier");
489
                            $this->db->rollback();
490
                            dol_syslog(get_class($this) . "::addline result=" . $result . " - " . $this->error, LOG_DEBUG);
491
                            return -1;
492
                        }
493
                        if ($result < -1) {
494
                            $this->error = $productsupplier->error;
495
                            $this->errors = $productsupplier->errors;
496
                            $this->db->rollback();
497
                            dol_syslog(get_class($this) . "::addline result=" . $result . " - " . $this->error, LOG_ERR);
498
                            return -1;
499
                        }
500
                    } else {
501
                        $this->error = $productsupplier->error;
502
                        $this->errors = $productsupplier->errors;
503
                        $this->db->rollback();
504
                        return -1;
505
                    }
506
                }
507
            } else {
508
                $product_type = $type;
509
            }
510
511
            // Calcul du total TTC et de la TVA pour la ligne a partir de
512
            // qty, pu, remise_percent et txtva
513
            // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
514
            // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
515
516
            $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
517
518
            // Clean vat code
519
            $reg = array();
520
            $vat_src_code = '';
521
            if (preg_match('/\((.*)\)/', $txtva, $reg)) {
522
                $vat_src_code = $reg[1];
523
                $txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
524
            }
525
526
            if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
527
                $pu = 0;
528
            }
529
530
            $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);
531
            $total_ht  = $tabprice[0];
532
            $total_tva = $tabprice[1];
533
            $total_ttc = $tabprice[2];
534
            $total_localtax1 = $tabprice[9];
535
            $total_localtax2 = $tabprice[10];
536
            $pu = $pu_ht = $tabprice[3];
537
538
            // MultiCurrency
539
            $multicurrency_total_ht  = $tabprice[16];
540
            $multicurrency_total_tva = $tabprice[17];
541
            $multicurrency_total_ttc = $tabprice[18];
542
            $pu_ht_devise = $tabprice[19];
543
544
            // Rang to use
545
            $ranktouse = $rang;
546
            if ($ranktouse == -1) {
547
                $rangmax = $this->line_max($fk_parent_line);
548
                $ranktouse = $rangmax + 1;
549
            }
550
551
            // TODO A virer
552
            // Anciens indicateurs: $price, $remise (a ne plus utiliser)
553
            $price = $pu;
554
            $remise = 0;
555
            if ($remise_percent > 0) {
556
                $remise = round(($pu * (float) $remise_percent / 100), 2);
557
                $price = $pu - $remise;
558
            }
559
560
            // Insert line
561
            $this->line = new SupplierProposalLine($this->db);
562
563
            $this->line->fk_supplier_proposal = $this->id;
564
            $this->line->label = $label;
565
            $this->line->desc = $desc;
566
            $this->line->qty = $qty;
567
568
            $this->line->vat_src_code = $vat_src_code;
569
            $this->line->tva_tx = $txtva;
570
            $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
571
            $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
572
            $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
573
            $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
574
            $this->line->fk_product = $fk_product;
575
            $this->line->remise_percent = $remise_percent;
576
            $this->line->subprice = $pu_ht;
577
            $this->line->rang = $ranktouse;
578
            $this->line->info_bits = $info_bits;
579
            $this->line->total_ht = $total_ht;
580
            $this->line->total_tva = $total_tva;
581
            $this->line->total_localtax1 = $total_localtax1;
582
            $this->line->total_localtax2 = $total_localtax2;
583
            $this->line->total_ttc = $total_ttc;
584
            $this->line->product_type = $type;
585
            $this->line->special_code = $special_code;
586
            $this->line->fk_parent_line = $fk_parent_line;
587
            $this->line->fk_unit = $fk_unit;
588
            $this->line->origin = $origin;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$origin has been deprecated: Use $origin_type and $origin_id instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

588
            /** @scrutinizer ignore-deprecated */ $this->line->origin = $origin;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
589
            $this->line->origin_id = $origin_id;
590
            $this->line->ref_fourn = $this->db->escape($ref_supplier);
591
            $this->line->date_start = $date_start;
592
            $this->line->date_end = $date_end;
593
594
            // infos merge
595
            if (!empty($fk_product) && $fk_product > 0 && empty($fk_fournprice) && empty($pa_ht)) {
596
                // When fk_fournprice is 0, we take the lowest buying price
597
                include_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
598
                $productFournisseur = new ProductFournisseur($this->db);
599
                $productFournisseur->find_min_price_product_fournisseur($fk_product);
600
                $this->line->fk_fournprice = $productFournisseur->product_fourn_price_id;
601
            } else {
602
                $this->line->fk_fournprice = ($fk_fournprice > 0 ? $fk_fournprice : 0); // If fk_fournprice is -1, we will not use fk_fournprice
603
            }
604
            $this->line->pa_ht = $pa_ht;
605
            //var_dump($this->line->fk_fournprice);exit;
606
607
            // Multicurrency
608
            $this->line->fk_multicurrency = $this->fk_multicurrency;
609
            $this->line->multicurrency_code = $this->multicurrency_code;
610
            $this->line->multicurrency_subprice     = $pu_ht_devise;
611
            $this->line->multicurrency_total_ht     = $multicurrency_total_ht;
612
            $this->line->multicurrency_total_tva    = $multicurrency_total_tva;
613
            $this->line->multicurrency_total_ttc    = $multicurrency_total_ttc;
614
615
            // Mise en option de la ligne
616
            if (empty($qty) && empty($special_code)) {
617
                $this->line->special_code = 3;
618
            }
619
620
            if (is_array($array_options) && count($array_options) > 0) {
621
                $this->line->array_options = $array_options;
622
            }
623
624
            $result = $this->line->insert();
625
            if ($result > 0) {
626
                // Reorder if child line
627
                if (!empty($fk_parent_line)) {
628
                    $this->line_order(true, 'DESC');
629
                } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) { // Update all rank of all other lines
630
                    $linecount = count($this->lines);
631
                    for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
632
                        $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
633
                    }
634
                }
635
636
                // Mise a jour information denormalisees au niveau de la propale meme
637
                $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.
638
                if ($result > 0) {
639
                    $this->db->commit();
640
                    return $this->line->id;
641
                } else {
642
                    $this->db->rollback();
643
                    return -1;
644
                }
645
            } else {
646
                $this->error = $this->line->error;
647
                $this->errors = $this->line->errors;
648
                $this->db->rollback();
649
                return -2;
650
            }
651
        } else {
652
            $this->error = 'BadStatusOfObjectToAddLine';
653
            return -5;
654
        }
655
    }
656
657
658
    /**
659
     *  Update a proposal line
660
     *
661
     *  @param      int         $rowid              Id de la ligne
662
     *  @param      double      $pu                 Unit price (HT or TTC depending on price_base_type)
663
     *  @param      double      $qty                Quantity
664
     *  @param      double      $remise_percent     Discount on line
665
     *  @param      double      $txtva              VAT rate
666
     *  @param      double      $txlocaltax1        Local tax 1 rate
667
     *  @param      double      $txlocaltax2        Local tax 2 rate
668
     *  @param      string      $desc               Description
669
     *  @param      string      $price_base_type    HT or TTC
670
     *  @param      int         $info_bits          Miscellaneous information
671
     *  @param      int         $special_code       Special code (also used by externals modules!)
672
     *  @param      int         $fk_parent_line     Id of parent line (0 in most cases, used by modules adding sublevels into lines).
673
     *  @param      int         $skip_update_total  Keep fields total_xxx to 0 (used for special lines by some modules)
674
     *  @param      int         $fk_fournprice      Id of origin supplier price
675
     *  @param      int         $pa_ht              Price (without tax) of product when it was bought
676
     *  @param      string      $label              ???
677
     *  @param      int         $type               0/1=Product/service
678
     *  @param      array       $array_options      extrafields array
679
     *  @param      string      $ref_supplier       Supplier price reference
680
     *  @param      int         $fk_unit            Id of the unit to use.
681
     *  @param      double      $pu_ht_devise       Unit price in currency
682
     *  @return     int                             0 if OK, <0 if KO
683
     */
684
    public function updateline($rowid, $pu, $qty, $remise_percent, $txtva, $txlocaltax1 = 0, $txlocaltax2 = 0, $desc = '', $price_base_type = 'HT', $info_bits = 0, $special_code = 0, $fk_parent_line = 0, $skip_update_total = 0, $fk_fournprice = 0, $pa_ht = 0, $label = '', $type = 0, $array_options = [], $ref_supplier = '', $fk_unit = 0, $pu_ht_devise = 0)
685
    {
686
        global $conf, $user, $langs, $mysoc;
687
688
        dol_syslog(get_class($this) . "::updateLine $rowid, $pu, $qty, $remise_percent, $txtva, $desc, $price_base_type, $info_bits");
689
        include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php';
690
691
        // Clean parameters
692
        $remise_percent = price2num($remise_percent);
693
        $qty = (float) price2num($qty);
694
        $pu = price2num($pu);
695
        if (!preg_match('/\((.*)\)/', (string) $txtva)) {
696
            $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
697
        }
698
        $txlocaltax1 = price2num($txlocaltax1);
699
        $txlocaltax2 = price2num($txlocaltax2);
700
        $pa_ht = price2num($pa_ht);
701
        if (empty($qty) && empty($special_code)) {
702
            $special_code = 3; // Set option tag
703
        }
704
        if (!empty($qty) && $special_code == 3) {
705
            $special_code = 0; // Remove option tag
706
        }
707
708
        if ($this->status == 0) {
709
            $this->db->begin();
710
711
            // Calcul du total TTC et de la TVA pour la ligne a partir de
712
            // qty, pu, remise_percent et txtva
713
            // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
714
            // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
715
716
            $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
717
718
            // Clean vat code
719
            $reg = array();
720
            $vat_src_code = '';
721
            if (preg_match('/\((.*)\)/', $txtva, $reg)) {
722
                $vat_src_code = $reg[1];
723
                $txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
724
            }
725
726
            if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
727
                $pu = 0;
728
            }
729
730
            $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);
731
            $total_ht  = $tabprice[0];
732
            $total_tva = $tabprice[1];
733
            $total_ttc = $tabprice[2];
734
            $total_localtax1 = $tabprice[9];
735
            $total_localtax2 = $tabprice[10];
736
            $pu_ht = $tabprice[3];
737
            $pu_tva = $tabprice[4];
738
            $pu_ttc = $tabprice[5];
739
740
            // MultiCurrency
741
            $multicurrency_total_ht  = $tabprice[16];
742
            $multicurrency_total_tva = $tabprice[17];
743
            $multicurrency_total_ttc = $tabprice[18];
744
            $pu_ht_devise = $tabprice[19];
745
746
            $pu = $pu_ht;
747
            if ($price_base_type == 'TTC') {
748
                $pu = $pu_ttc;
749
            }
750
751
            //Fetch current line from the database and then clone the object and set it in $oldline property
752
            $line = new SupplierProposalLine($this->db);
753
            $line->fetch($rowid);
754
            $line->fetch_optionals();
755
756
            $fk_product = $line->fk_product;
757
758
            // Stock previous line records
759
            $staticline = clone $line;
760
761
            $line->oldline = $staticline;
762
            $this->line = $line;
763
            $this->line->context = $this->context;
764
765
            // Reorder if fk_parent_line change
766
            if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
767
                $rangmax = $this->line_max($fk_parent_line);
768
                $this->line->rang = $rangmax + 1;
769
            }
770
771
            $this->line->id                 = $rowid;
772
            $this->line->label = $label;
773
            $this->line->desc = $desc;
774
            $this->line->qty                = $qty;
775
            $this->line->product_type = $type;
776
777
            $this->line->vat_src_code = $vat_src_code;
778
            $this->line->tva_tx = $txtva;
779
            $this->line->localtax1_tx       = $txlocaltax1;
780
            $this->line->localtax2_tx       = $txlocaltax2;
781
            $this->line->localtax1_type     = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
782
            $this->line->localtax2_type     = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
783
            $this->line->remise_percent     = $remise_percent;
784
            $this->line->subprice           = $pu;
785
            $this->line->info_bits          = $info_bits;
786
            $this->line->total_ht           = $total_ht;
787
            $this->line->total_tva          = $total_tva;
788
            $this->line->total_localtax1    = $total_localtax1;
789
            $this->line->total_localtax2    = $total_localtax2;
790
            $this->line->total_ttc          = $total_ttc;
791
            $this->line->special_code = $special_code;
792
            $this->line->fk_parent_line     = $fk_parent_line;
793
            $this->line->skip_update_total = $skip_update_total;
794
            $this->line->ref_fourn = $ref_supplier;
795
            $this->line->fk_unit = $fk_unit;
796
797
            // infos merge
798
            if (!empty($fk_product) && $fk_product > 0 && empty($fk_fournprice) && empty($pa_ht)) {
799
                // by external module, take lowest buying price
800
                include_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
801
                $productFournisseur = new ProductFournisseur($this->db);
802
                $productFournisseur->find_min_price_product_fournisseur($fk_product);
803
                $this->line->fk_fournprice = $productFournisseur->product_fourn_price_id;
804
            } else {
805
                $this->line->fk_fournprice = $fk_fournprice;
806
            }
807
            $this->line->pa_ht = $pa_ht;
808
809
            if (is_array($array_options) && count($array_options) > 0) {
810
                // We replace values in this->line->array_options only for entries defined into $array_options
811
                foreach ($array_options as $key => $value) {
812
                    $this->line->array_options[$key] = $array_options[$key];
813
                }
814
            }
815
816
            // Multicurrency
817
            $this->line->multicurrency_subprice     = $pu_ht_devise;
818
            $this->line->multicurrency_total_ht     = $multicurrency_total_ht;
819
            $this->line->multicurrency_total_tva    = $multicurrency_total_tva;
820
            $this->line->multicurrency_total_ttc    = $multicurrency_total_ttc;
821
822
            $result = $this->line->update();
823
            if ($result > 0) {
824
                // Reorder if child line
825
                if (!empty($fk_parent_line)) {
826
                    $this->line_order(true, 'DESC');
827
                }
828
829
                $this->update_price(1);
830
831
                $this->db->commit();
832
                return $result;
833
            } else {
834
                $this->error = $this->db->error();
835
                $this->db->rollback();
836
                return -1;
837
            }
838
        } else {
839
            dol_syslog(get_class($this) . "::updateline Erreur -2 SupplierProposal en mode incompatible pour cette action");
840
            return -2;
841
        }
842
    }
843
844
845
    /**
846
     *  Delete detail line
847
     *
848
     *  @param      int     $lineid         Id of line to delete
849
     *  @return     int                     >0 if OK, <0 if KO
850
     */
851
    public function deleteLine($lineid)
852
    {
853
        global $user;
854
855
        if ($this->statut == 0) {
856
            $line = new SupplierProposalLine($this->db);
857
858
            // For triggers
859
            $line->fetch($lineid);
860
861
            if ($line->delete($user) > 0) {
862
                $this->update_price(1);
863
864
                return 1;
865
            } else {
866
                return -1;
867
            }
868
        } else {
869
            return -2;
870
        }
871
    }
872
873
874
    /**
875
     *  Create commercial proposal into database
876
     *  this->ref can be set or empty. If empty, we will use "(PROVid)"
877
     *
878
     *  @param      User    $user       User that create
879
     *  @param      int     $notrigger  1=Does not execute triggers, 0= execute triggers
880
     *  @return     int                 Return integer <0 if KO, >=0 if OK
881
     */
882
    public function create($user, $notrigger = 0)
883
    {
884
        global $langs, $conf, $mysoc, $hookmanager;
885
        $error = 0;
886
887
        $now = dol_now();
888
889
        dol_syslog(get_class($this) . "::create");
890
891
        // Check parameters
892
        $result = $this->fetch_thirdparty();
893
        if ($result < 0) {
894
            $this->error = "Failed to fetch company";
895
            dol_syslog(get_class($this) . "::create " . $this->error, LOG_ERR);
896
            return -3;
897
        }
898
        if (!empty($this->ref)) {   // We check that ref is not already used
899
            $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
900
            if ($result > 0) {
901
                $this->error = 'ErrorRefAlreadyExists';
902
                dol_syslog(get_class($this) . "::create " . $this->error, LOG_WARNING);
903
                $this->db->rollback();
904
                return -1;
905
            }
906
        }
907
908
        // Set tmp vars
909
        $delivery_date = $this->delivery_date;
910
911
        // Multicurrency
912
        if (!empty($this->multicurrency_code)) {
913
            list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $now);
914
        }
915
        if (empty($this->fk_multicurrency)) {
916
            $this->multicurrency_code = $conf->currency;
917
            $this->fk_multicurrency = 0;
918
            $this->multicurrency_tx = 1;
919
        }
920
921
        $this->db->begin();
922
923
        // Insert into database
924
        $sql = "INSERT INTO " . MAIN_DB_PREFIX . "supplier_proposal (";
925
        $sql .= "fk_soc";
926
        $sql .= ", price";
927
        $sql .= ", total_tva";
928
        $sql .= ", total_ttc";
929
        $sql .= ", datec";
930
        $sql .= ", ref";
931
        $sql .= ", fk_user_author";
932
        $sql .= ", note_private";
933
        $sql .= ", note_public";
934
        $sql .= ", model_pdf";
935
        $sql .= ", fk_cond_reglement";
936
        $sql .= ", fk_mode_reglement";
937
        $sql .= ", fk_account";
938
        $sql .= ", date_livraison";
939
        $sql .= ", fk_shipping_method";
940
        $sql .= ", fk_projet";
941
        $sql .= ", entity";
942
        $sql .= ", fk_multicurrency";
943
        $sql .= ", multicurrency_code";
944
        $sql .= ", multicurrency_tx";
945
        $sql .= ") ";
946
        $sql .= " VALUES (";
947
        $sql .= ((int) $this->socid);
948
        $sql .= ", 0";
949
        $sql .= ", 0";
950
        $sql .= ", 0";
951
        $sql .= ", '" . $this->db->idate($now) . "'";
952
        $sql .= ", '(PROV)'";
953
        $sql .= ", " . ($user->id > 0 ? ((int) $user->id) : "null");
954
        $sql .= ", '" . $this->db->escape($this->note_private) . "'";
955
        $sql .= ", '" . $this->db->escape($this->note_public) . "'";
956
        $sql .= ", '" . $this->db->escape($this->model_pdf) . "'";
957
        $sql .= ", " . ($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : 'NULL');
958
        $sql .= ", " . ($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : 'NULL');
959
        $sql .= ", " . ($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL');
960
        $sql .= ", " . ($delivery_date ? "'" . $this->db->idate($delivery_date) . "'" : "null");
961
        $sql .= ", " . ($this->shipping_method_id > 0 ? ((int) $this->shipping_method_id) : 'NULL');
962
        $sql .= ", " . ($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
963
        $sql .= ", " . ((int) $conf->entity);
964
        $sql .= ", " . ((int) $this->fk_multicurrency);
965
        $sql .= ", '" . $this->db->escape($this->multicurrency_code) . "'";
966
        $sql .= ", " . ((float) $this->multicurrency_tx);
967
        $sql .= ")";
968
969
        dol_syslog(get_class($this) . "::create", LOG_DEBUG);
970
        $resql = $this->db->query($sql);
971
        if ($resql) {
972
            $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . "supplier_proposal");
973
974
            if ($this->id) {
975
                $this->ref = '(PROV' . $this->id . ')';
976
                $sql = 'UPDATE ' . MAIN_DB_PREFIX . "supplier_proposal SET ref='" . $this->db->escape($this->ref) . "' WHERE rowid=" . ((int) $this->id);
977
978
                dol_syslog(get_class($this) . "::create", LOG_DEBUG);
979
                $resql = $this->db->query($sql);
980
                if (!$resql) {
981
                    $error++;
982
                }
983
984
                if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) {  // To use new linkedObjectsIds instead of old linked_objects
985
                    $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
986
                }
987
988
                // Add object linked
989
                if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
990
                    foreach ($this->linked_objects as $origin => $tmp_origin_id) {
991
                        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, ...))
992
                            foreach ($tmp_origin_id as $origin_id) {
993
                                $ret = $this->add_object_linked($origin, $origin_id);
994
                                if (!$ret) {
995
                                    dol_print_error($this->db);
996
                                    $error++;
997
                                }
998
                            }
999
                        }
1000
                    }
1001
                }
1002
1003
                /*
1004
                 *  Insertion du detail des produits dans la base
1005
                 */
1006
                if (!$error) {
1007
                    $fk_parent_line = 0;
1008
                    $num = count($this->lines);
1009
1010
                    for ($i = 0; $i < $num; $i++) {
1011
                        // Reset fk_parent_line for no child products and special product
1012
                        if (($this->lines[$i]->product_type != 9 && empty($this->lines[$i]->fk_parent_line)) || $this->lines[$i]->product_type == 9) {
1013
                            $fk_parent_line = 0;
1014
                        }
1015
1016
                        $result = $this->addline(
1017
                            $this->lines[$i]->desc,
1018
                            $this->lines[$i]->subprice,
1019
                            $this->lines[$i]->qty,
1020
                            $this->lines[$i]->tva_tx,
1021
                            $this->lines[$i]->localtax1_tx,
1022
                            $this->lines[$i]->localtax2_tx,
1023
                            $this->lines[$i]->fk_product,
1024
                            $this->lines[$i]->remise_percent,
1025
                            'HT',
1026
                            0,
1027
                            0,
1028
                            $this->lines[$i]->product_type,
1029
                            $this->lines[$i]->rang,
1030
                            $this->lines[$i]->special_code,
1031
                            $fk_parent_line,
1032
                            $this->lines[$i]->fk_fournprice,
1033
                            $this->lines[$i]->pa_ht,
1034
                            empty($this->lines[$i]->label) ? '' : $this->lines[$i]->label, // deprecated
1035
                            $this->lines[$i]->array_options,
1036
                            $this->lines[$i]->ref_fourn,
1037
                            $this->lines[$i]->fk_unit,
1038
                            'supplier_proposal',
1039
                            $this->lines[$i]->rowid
1040
                        );
1041
1042
                        if ($result < 0) {
1043
                            $error++;
1044
                            $this->error = $this->db->error;
1045
                            dol_print_error($this->db);
1046
                            break;
1047
                        }
1048
                        // Defined the new fk_parent_line
1049
                        if ($result > 0 && $this->lines[$i]->product_type == 9) {
1050
                            $fk_parent_line = $result;
1051
                        }
1052
                    }
1053
                }
1054
1055
                if (!$error) {
1056
                    // Mise a jour infos denormalisees
1057
                    $resql = $this->update_price(1);
1058
                    if ($resql) {
1059
                        $action = 'update';
1060
1061
                        // Actions on extra fields
1062
                        if (!$error) {
1063
                            $result = $this->insertExtraFields();
1064
                            if ($result < 0) {
1065
                                $error++;
1066
                            }
1067
                        }
1068
1069
                        if (!$error && !$notrigger) {
1070
                            // Call trigger
1071
                            $result = $this->call_trigger('PROPOSAL_SUPPLIER_CREATE', $user);
1072
                            if ($result < 0) {
1073
                                $error++;
1074
                            }
1075
                            // End call triggers
1076
                        }
1077
                    } else {
1078
                        $this->error = $this->db->lasterror();
1079
                        $error++;
1080
                    }
1081
                }
1082
            } else {
1083
                $this->error = $this->db->lasterror();
1084
                $error++;
1085
            }
1086
1087
            if (!$error) {
1088
                $this->db->commit();
1089
                dol_syslog(get_class($this) . "::create done id=" . $this->id);
1090
                return $this->id;
1091
            } else {
1092
                $this->db->rollback();
1093
                return -2;
1094
            }
1095
        } else {
1096
            $this->error = $this->db->lasterror();
1097
            $this->db->rollback();
1098
            return -1;
1099
        }
1100
    }
1101
1102
    /**
1103
     *      Load an object from its id and create a new one in database
1104
     *
1105
     *      @param      User    $user           User making the clone
1106
     *      @param      int     $fromid         Id of thirdparty
1107
     *      @return     int                     New id of clone
1108
     */
1109
    public function createFromClone(User $user, $fromid = 0)
1110
    {
1111
        global $conf, $hookmanager;
1112
1113
        $error = 0;
1114
        $now = dol_now();
1115
1116
        $this->db->begin();
1117
1118
        // get extrafields so they will be clone
1119
        foreach ($this->lines as $line) {
1120
            $line->fetch_optionals();
1121
        }
1122
1123
        // Load source object
1124
        $objFrom = clone $this;
1125
1126
        $objsoc = new Societe($this->db);
1127
1128
        // Change socid if needed
1129
        if (!empty($fromid) && $fromid != $this->socid) {
1130
            if ($objsoc->fetch($fromid) > 0) {
1131
                $this->socid = $objsoc->id;
1132
                $this->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1133
                $this->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1134
                unset($this->fk_project);
1135
            }
1136
1137
            // TODO Change product price if multi-prices
1138
        } else {
1139
            $objsoc->fetch($this->socid);
1140
        }
1141
1142
        $this->id = 0;
1143
        $this->statut = 0;
1144
1145
        if (!getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') || !is_readable(DOL_DOCUMENT_ROOT . "/core/modules/supplier_proposal/" . getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') . ".php")) {
1146
            $this->error = 'ErrorSetupNotComplete';
1147
            return -1;
1148
        }
1149
1150
        // Clear fields
1151
        $this->user_author_id = $user->id;
1152
        $this->user_validation_id = 0;
1153
        $this->date = $now;
1154
1155
        // Set ref
1156
        require_once DOL_DOCUMENT_ROOT . "/core/modules/supplier_proposal/" . getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') . '.php';
1157
        $obj = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
1158
        $modSupplierProposal = new $obj();
1159
        $this->ref = $modSupplierProposal->getNextValue($objsoc, $this);
1160
1161
        // Create clone
1162
        $this->context['createfromclone'] = 'createfromclone';
1163
        $result = $this->create($user);
1164
        if ($result < 0) {
1165
            $error++;
1166
        }
1167
1168
        if (!$error) {
1169
            // Hook of thirdparty module
1170
            if (is_object($hookmanager)) {
1171
                $parameters = array('objFrom' => $objFrom);
1172
                $action = '';
1173
                $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1174
                if ($reshook < 0) {
1175
                    $this->setErrorsFromObject($hookmanager);
1176
                    $error++;
1177
                }
1178
            }
1179
        }
1180
1181
        unset($this->context['createfromclone']);
1182
1183
        // End
1184
        if (!$error) {
1185
            $this->db->commit();
1186
            return $this->id;
1187
        } else {
1188
            $this->db->rollback();
1189
            return -1;
1190
        }
1191
    }
1192
1193
    /**
1194
     *  Load a proposal from database and its ligne array
1195
     *
1196
     *  @param      int         $rowid      id of object to load
1197
     *  @param      string      $ref        Ref of proposal
1198
     *  @return     int                     >0 if OK, <0 if KO
1199
     */
1200
    public function fetch($rowid, $ref = '')
1201
    {
1202
        global $conf;
1203
1204
        $sql = "SELECT p.rowid, p.entity, p.ref, p.fk_soc as socid";
1205
        $sql .= ", p.total_ttc, p.total_tva, p.localtax1, p.localtax2, p.total_ht";
1206
        $sql .= ", p.datec";
1207
        $sql .= ", p.date_valid as datev";
1208
        $sql .= ", p.date_livraison as delivery_date";
1209
        $sql .= ", p.model_pdf, p.extraparams";
1210
        $sql .= ", p.note_private, p.note_public";
1211
        $sql .= ", p.fk_projet as fk_project, p.fk_statut";
1212
        $sql .= ", p.fk_user_author, p.fk_user_valid, p.fk_user_cloture";
1213
        $sql .= ", p.fk_cond_reglement";
1214
        $sql .= ", p.fk_mode_reglement";
1215
        $sql .= ', p.fk_account';
1216
        $sql .= ", p.fk_shipping_method";
1217
        $sql .= ", p.fk_multicurrency, p.multicurrency_code, p.multicurrency_tx, p.multicurrency_total_ht, p.multicurrency_total_tva, p.multicurrency_total_ttc";
1218
        $sql .= ", c.label as statut_label";
1219
        $sql .= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc";
1220
        $sql .= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement";
1221
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_propalst as c, " . MAIN_DB_PREFIX . "supplier_proposal as p";
1222
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_paiement as cp ON p.fk_mode_reglement = cp.id';
1223
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_payment_term as cr ON p.fk_cond_reglement = cr.rowid';
1224
        $sql .= " WHERE p.fk_statut = c.id";
1225
        $sql .= " AND p.entity IN (" . getEntity('supplier_proposal') . ")";
1226
        if ($ref) {
1227
            $sql .= " AND p.ref = '" . $this->db->escape($ref) . "'";
1228
        } else {
1229
            $sql .= " AND p.rowid = " . ((int) $rowid);
1230
        }
1231
1232
        dol_syslog(get_class($this) . "::fetch", LOG_DEBUG);
1233
        $resql = $this->db->query($sql);
1234
        if ($resql) {
1235
            if ($this->db->num_rows($resql)) {
1236
                $obj = $this->db->fetch_object($resql);
1237
1238
                $this->id                   = $obj->rowid;
1239
                $this->entity               = $obj->entity;
1240
1241
                $this->ref                  = $obj->ref;
1242
                $this->total_ht             = $obj->total_ht;
1243
                $this->total_tva            = $obj->total_tva;
1244
                $this->total_localtax1      = $obj->localtax1;
1245
                $this->total_localtax2      = $obj->localtax2;
1246
                $this->total_ttc            = $obj->total_ttc;
1247
                $this->socid                = $obj->socid;
1248
                $this->fk_project           = $obj->fk_project;
1249
                $this->model_pdf            = $obj->model_pdf;
1250
                $this->note                 = $obj->note_private; // TODO deprecated
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$note has been deprecated: Use $note_private instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1250
                /** @scrutinizer ignore-deprecated */ $this->note                 = $obj->note_private; // TODO deprecated

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1251
                $this->note_private         = $obj->note_private;
1252
                $this->note_public          = $obj->note_public;
1253
                $this->statut               = (int) $obj->fk_statut;
1254
                $this->status               = (int) $obj->fk_statut;
1255
                $this->datec                = $this->db->jdate($obj->datec); // TODO deprecated
1256
                $this->datev                = $this->db->jdate($obj->datev); // TODO deprecated
1257
                $this->date_creation = $this->db->jdate($obj->datec);   // Creation date
1258
                $this->date                 = $this->date_creation;
1259
                $this->date_validation = $this->db->jdate($obj->datev); // Validation date
1260
                $this->delivery_date        = $this->db->jdate($obj->delivery_date);
1261
                $this->shipping_method_id   = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1262
1263
                $this->mode_reglement_id    = $obj->fk_mode_reglement;
1264
                $this->mode_reglement_code  = $obj->mode_reglement_code;
1265
                $this->mode_reglement       = $obj->mode_reglement;
1266
                $this->fk_account           = ($obj->fk_account > 0) ? $obj->fk_account : null;
1267
                $this->cond_reglement_id    = $obj->fk_cond_reglement;
1268
                $this->cond_reglement_code  = $obj->cond_reglement_code;
1269
                $this->cond_reglement       = $obj->cond_reglement;
0 ignored issues
show
Bug Best Practice introduced by
The property $cond_reglement is declared private in Dolibarr\Core\Base\CommonObject. Since you implement __set, consider adding a @property or @property-write.
Loading history...
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$cond_reglement has been deprecated: Use $cond_reglement_id instead - Kept for compatibility ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1269
                /** @scrutinizer ignore-deprecated */ $this->cond_reglement       = $obj->cond_reglement;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1270
                $this->cond_reglement_doc   = $obj->cond_reglement_libelle_doc;
1271
1272
                $this->extraparams = (array) json_decode($obj->extraparams, true);
1273
1274
                $this->user_author_id = $obj->fk_user_author;
1275
                $this->user_validation_id = $obj->fk_user_valid;
1276
                $this->user_closing_id = $obj->fk_user_cloture;
1277
1278
                // Multicurrency
1279
                $this->fk_multicurrency         = $obj->fk_multicurrency;
1280
                $this->multicurrency_code = $obj->multicurrency_code;
1281
                $this->multicurrency_tx         = $obj->multicurrency_tx;
1282
                $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1283
                $this->multicurrency_total_tva  = $obj->multicurrency_total_tva;
1284
                $this->multicurrency_total_ttc  = $obj->multicurrency_total_ttc;
1285
1286
                // Retrieve all extrafield
1287
                // fetch optionals attributes and labels
1288
                $this->fetch_optionals();
1289
1290
                $this->db->free($resql);
1291
1292
                $this->lines = array();
1293
1294
                // Lines of supplier proposals
1295
                $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,";
1296
                $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,";
1297
                $sql .= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label,';
1298
                $sql .= ' d.ref_fourn as ref_produit_fourn,';
1299
                $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';
1300
                $sql .= " FROM " . MAIN_DB_PREFIX . "supplier_proposaldet as d";
1301
                $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "product as p ON d.fk_product = p.rowid";
1302
                $sql .= " WHERE d.fk_supplier_proposal = " . ((int) $this->id);
1303
                $sql .= " ORDER by d.rang";
1304
1305
                $result = $this->db->query($sql);
1306
                if ($result) {
1307
                    $num = $this->db->num_rows($result);
1308
                    $i = 0;
1309
1310
                    while ($i < $num) {
1311
                        $objp                   = $this->db->fetch_object($result);
1312
1313
                        $line                   = new SupplierProposalLine($this->db);
1314
1315
                        $line->rowid = $objp->rowid; // deprecated
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObjectLine::$rowid has been deprecated: Try to use id property as possible (even if field into database is still rowid) ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1315
                        /** @scrutinizer ignore-deprecated */ $line->rowid = $objp->rowid; // deprecated

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1316
                        $line->id = $objp->rowid;
1317
                        $line->fk_supplier_proposal = $objp->fk_supplier_proposal;
1318
                        $line->fk_parent_line = $objp->fk_parent_line;
1319
                        $line->product_type     = $objp->product_type;
1320
                        $line->label            = $objp->custom_label;
1321
                        $line->desc             = $objp->description; // Description ligne
1322
                        $line->qty              = $objp->qty;
1323
                        $line->tva_tx           = $objp->tva_tx;
1324
                        $line->localtax1_tx     = $objp->localtax1_tx;
1325
                        $line->localtax2_tx     = $objp->localtax2_tx;
1326
                        $line->subprice         = $objp->subprice;
1327
                        $line->fk_remise_except = $objp->fk_remise_except;
1328
                        $line->remise_percent   = $objp->remise_percent;
1329
1330
                        $line->info_bits        = $objp->info_bits;
1331
                        $line->total_ht         = $objp->total_ht;
1332
                        $line->total_tva        = $objp->total_tva;
1333
                        $line->total_localtax1  = $objp->total_localtax1;
1334
                        $line->total_localtax2  = $objp->total_localtax2;
1335
                        $line->total_ttc        = $objp->total_ttc;
1336
                        $line->fk_fournprice    = $objp->fk_fournprice;
1337
                        $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1338
                        $line->pa_ht = $marginInfos[0];
1339
                        $line->marge_tx         = $marginInfos[1];
1340
                        $line->marque_tx        = $marginInfos[2];
1341
                        $line->special_code     = $objp->special_code;
1342
                        $line->rang             = $objp->rang;
1343
1344
                        $line->fk_product       = $objp->fk_product;
1345
1346
                        $line->ref = $objp->product_ref; // deprecated
1347
                        $line->product_ref = $objp->product_ref;
1348
                        $line->libelle = $objp->product_label; // deprecated
1349
                        $line->product_label = $objp->product_label;
1350
                        $line->product_desc     = $objp->product_desc; // Description produit
1351
                        $line->fk_product_type  = $objp->fk_product_type;
1352
1353
                        $line->ref_fourn = $objp->ref_produit_fourn;
1354
1355
                        // Multicurrency
1356
                        $line->fk_multicurrency = $objp->fk_multicurrency;
1357
                        $line->multicurrency_code = $objp->multicurrency_code;
1358
                        $line->multicurrency_subprice   = $objp->multicurrency_subprice;
1359
                        $line->multicurrency_total_ht   = $objp->multicurrency_total_ht;
1360
                        $line->multicurrency_total_tva  = $objp->multicurrency_total_tva;
1361
                        $line->multicurrency_total_ttc  = $objp->multicurrency_total_ttc;
1362
                        $line->fk_unit = $objp->fk_unit;
1363
1364
                        $this->lines[$i] = $line;
1365
1366
                        $i++;
1367
                    }
1368
                    $this->db->free($result);
1369
                } else {
1370
                    $this->error = $this->db->error();
1371
                    return -1;
1372
                }
1373
1374
                // Retrieve all extrafield
1375
                // fetch optionals attributes and labels
1376
                $this->fetch_optionals();
1377
1378
                return 1;
1379
            }
1380
1381
            $this->error = "Record Not Found";
1382
            return 0;
1383
        } else {
1384
            $this->error = $this->db->error();
1385
            return -1;
1386
        }
1387
    }
1388
1389
    /**
1390
     *  Set status to validated
1391
     *
1392
     *  @param  User    $user       Object user that validate
1393
     *  @param  int     $notrigger  1=Does not execute triggers, 0= execute triggers
1394
     *  @return int                 Return integer <0 if KO, >=0 if OK
1395
     */
1396
    public function valid($user, $notrigger = 0)
1397
    {
1398
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
1399
1400
        global $conf, $langs;
1401
1402
        $error = 0;
1403
        $now = dol_now();
1404
1405
        if (
1406
            (!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'creer'))
1407
            || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('supplier_proposal', 'validate_advance'))
1408
        ) {
1409
            $this->db->begin();
1410
1411
            // Numbering module definition
1412
            $soc = new Societe($this->db);
1413
            $result = $soc->fetch($this->socid);
1414
1415
            if ($result < 0) {
1416
                return -1;
1417
            }
1418
1419
            // Define new ref
1420
            if (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1421
                $num = $this->getNextNumRef($soc);
1422
            } else {
1423
                $num = $this->ref;
1424
            }
1425
            $this->newref = dol_sanitizeFileName($num);
1426
1427
            $sql = "UPDATE " . MAIN_DB_PREFIX . "supplier_proposal";
1428
            $sql .= " SET ref = '" . $this->db->escape($num) . "',";
1429
            $sql .= " fk_statut = 1, date_valid='" . $this->db->idate($now) . "', fk_user_valid=" . ((int) $user->id);
1430
            $sql .= " WHERE rowid = " . ((int) $this->id) . " AND fk_statut = 0";
1431
1432
            dol_syslog(get_class($this) . "::valid", LOG_DEBUG);
1433
            $resql = $this->db->query($sql);
1434
            if (!$resql) {
1435
                dol_print_error($this->db);
1436
                $error++;
1437
            }
1438
1439
            // Trigger calls
1440
            if (!$error && !$notrigger) {
1441
                // Call trigger
1442
                $result = $this->call_trigger('PROPOSAL_SUPPLIER_VALIDATE', $user);
1443
                if ($result < 0) {
1444
                    $error++;
1445
                }
1446
                // End call triggers
1447
            }
1448
1449
            if (!$error) {
1450
                $this->oldref = $this->ref;
1451
1452
                // Rename directory if dir was a temporary ref
1453
                if (preg_match('/^[\(]?PROV/i', $this->ref)) {
1454
                    // Now we rename also files into index
1455
                    $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) . "'";
1456
                    $sql .= " WHERE filename LIKE '" . $this->db->escape($this->ref) . "%' AND filepath = 'supplier_proposal/" . $this->db->escape($this->ref) . "' and entity = " . $conf->entity;
1457
                    $resql = $this->db->query($sql);
1458
                    if (!$resql) {
1459
                        $error++;
1460
                        $this->error = $this->db->lasterror();
1461
                    }
1462
                    $sql = 'UPDATE ' . MAIN_DB_PREFIX . "ecm_files set filepath = 'supplier_proposal/" . $this->db->escape($this->newref) . "'";
1463
                    $sql .= " WHERE filepath = 'supplier_proposal/" . $this->db->escape($this->ref) . "' and entity = " . $conf->entity;
1464
                    $resql = $this->db->query($sql);
1465
                    if (!$resql) {
1466
                        $error++;
1467
                        $this->error = $this->db->lasterror();
1468
                    }
1469
1470
                    // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1471
                    $oldref = dol_sanitizeFileName($this->ref);
1472
                    $newref = dol_sanitizeFileName($num);
1473
                    $dirsource = $conf->supplier_proposal->dir_output . '/' . $oldref;
1474
                    $dirdest = $conf->supplier_proposal->dir_output . '/' . $newref;
1475
                    if (!$error && file_exists($dirsource)) {
1476
                        dol_syslog(get_class($this) . "::valid rename dir " . $dirsource . " into " . $dirdest);
1477
                        if (@rename($dirsource, $dirdest)) {
1478
                            dol_syslog("Rename ok");
1479
                            // Rename docs starting with $oldref with $newref
1480
                            $listoffiles = dol_dir_list($conf->supplier_proposal->dir_output . '/' . $newref, 'files', 1, '^' . preg_quote($oldref, '/'));
1481
                            foreach ($listoffiles as $fileentry) {
1482
                                $dirsource = $fileentry['name'];
1483
                                $dirdest = preg_replace('/^' . preg_quote($oldref, '/') . '/', $newref, $dirsource);
1484
                                $dirsource = $fileentry['path'] . '/' . $dirsource;
1485
                                $dirdest = $fileentry['path'] . '/' . $dirdest;
1486
                                @rename($dirsource, $dirdest);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for rename(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

1486
                                /** @scrutinizer ignore-unhandled */ @rename($dirsource, $dirdest);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1487
                            }
1488
                        }
1489
                    }
1490
                }
1491
1492
                $this->ref = $num;
1493
                $this->statut = self::STATUS_VALIDATED;
1494
                $this->status = self::STATUS_VALIDATED;
1495
                $this->user_validation_id = $user->id;
1496
                $this->datev = $now;
1497
                $this->date_validation = $now;
1498
1499
                $this->db->commit();
1500
                return 1;
1501
            } else {
1502
                $this->db->rollback();
1503
                return -1;
1504
            }
1505
        } else {
1506
            dol_syslog("You don't have permission to validate supplier proposal", LOG_WARNING);
1507
            return -2;
1508
        }
1509
    }
1510
1511
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1512
    /**
1513
     *  Set delivery date
1514
     *
1515
     *  @param      User    $user               Object user that modify
1516
     *  @param      int     $delivery_date      Delivery date
1517
     *  @return     int                         Return integer <0 if ko, >0 if ok
1518
     *  @deprecated Use  setDeliveryDate
1519
     */
1520
    public function set_date_livraison($user, $delivery_date)
1521
    {
1522
		// phpcs:enable
1523
        return $this->setDeliveryDate($user, $delivery_date);
1524
    }
1525
1526
    /**
1527
     *  Set delivery date
1528
     *
1529
     *  @param      User        $user               Object user that modify
1530
     *  @param      int         $delivery_date     Delivery date
1531
     *  @return     int                             Return integer <0 if ko, >0 if ok
1532
     */
1533
    public function setDeliveryDate($user, $delivery_date)
1534
    {
1535
        if ($user->hasRight('supplier_proposal', 'creer')) {
1536
            $sql = "UPDATE " . MAIN_DB_PREFIX . "supplier_proposal ";
1537
            $sql .= " SET date_livraison = " . ($delivery_date != '' ? "'" . $this->db->idate($delivery_date) . "'" : 'null');
1538
            $sql .= " WHERE rowid = " . ((int) $this->id);
1539
1540
            if ($this->db->query($sql)) {
1541
                $this->delivery_date = $delivery_date;
1542
                return 1;
1543
            } else {
1544
                $this->error = $this->db->error();
1545
                dol_syslog(get_class($this) . "::setDeliveryDate Erreur SQL");
1546
                return -1;
1547
            }
1548
        }
1549
        return 0;
1550
    }
1551
1552
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1553
    /**
1554
     *  Set an overall discount on the proposal
1555
     *
1556
     *  @param      User    $user       Object user that modify
1557
     *  @param      double  $remise      Amount discount
1558
     *  @return     int                 Return integer <0 if ko, >0 if ok
1559
     */
1560
    /*
1561
    public function set_remise_percent($user, $remise)
1562
    {
1563
		// phpcs:enable
1564
        $remise = trim($remise) ?trim($remise) : 0;
1565
1566
        if ($user->hasRight('supplier_proposal', 'creer')) {
1567
            $remise = price2num($remise, 2);
1568
1569
            $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal SET remise_percent = ".((float) $remise);
1570
            $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1571
1572
            if ($this->db->query($sql)) {
1573
                $this->remise_percent = ((float) $remise);
1574
                $this->update_price(1);
1575
                return 1;
1576
            } else {
1577
                $this->error = $this->db->error();
1578
                return -1;
1579
            }
1580
        }
1581
        return 0;
1582
    }
1583
    */
1584
1585
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1586
    /**
1587
     *  Set an absolute overall discount on the proposal
1588
     *
1589
     *  @param      User    $user        Object user that modify
1590
     *  @param      double  $remise      Amount discount
1591
     *  @return     int                 Return integer <0 if ko, >0 if ok
1592
     */
1593
    /*
1594
    public function set_remise_absolue($user, $remise)
1595
    {
1596
		// phpcs:enable
1597
        if (empty($remise)) {
1598
            $remise = 0;
1599
        }
1600
1601
        $remise = price2num($remise);
1602
1603
        if ($user->hasRight('supplier_proposal', 'creer')) {
1604
            $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1605
            $sql .= " SET remise_absolue = ".((float) $remise);
1606
            $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1607
1608
            if ($this->db->query($sql)) {
1609
                $this->remise_absolue = $remise;
1610
                $this->update_price(1);
1611
                return 1;
1612
            } else {
1613
                $this->error = $this->db->error();
1614
                return -1;
1615
            }
1616
        }
1617
        return 0;
1618
    }
1619
    */
1620
1621
1622
    /**
1623
     *  Reopen the commercial proposal
1624
     *
1625
     *  @param      User    $user       Object user that close
1626
     *  @param      int     $statut     Statut
1627
     *  @param      string  $note       Comment
1628
     *  @param      int     $notrigger  1=Does not execute triggers, 0= execute triggers
1629
     *  @return     int                 Return integer <0 if KO, >0 if OK
1630
     */
1631
    public function reopen($user, $statut, $note = '', $notrigger = 0)
1632
    {
1633
        global $langs, $conf;
1634
1635
        $this->statut = $statut;
1636
        $error = 0;
1637
1638
        $sql = "UPDATE " . MAIN_DB_PREFIX . "supplier_proposal";
1639
        $sql .= " SET fk_statut = " . ((int) $this->statut) . ",";
1640
        if (!empty($note)) {
1641
            $sql .= " note_private = '" . $this->db->escape($note) . "',";
1642
        }
1643
        $sql .= " date_cloture = NULL, fk_user_cloture = NULL";
1644
        $sql .= " WHERE rowid = " . ((int) $this->id);
1645
1646
        $this->db->begin();
1647
1648
        dol_syslog(get_class($this) . "::reopen", LOG_DEBUG);
1649
        $resql = $this->db->query($sql);
1650
        if (!$resql) {
1651
            $error++;
1652
            $this->errors[] = "Error " . $this->db->lasterror();
1653
        }
1654
        if (!$error) {
1655
            if (!$notrigger) {
1656
                // Call trigger
1657
                $result = $this->call_trigger('PROPOSAL_SUPPLIER_REOPEN', $user);
1658
                if ($result < 0) {
1659
                    $error++;
1660
                }
1661
                // End call triggers
1662
            }
1663
        }
1664
1665
        // Commit or rollback
1666
        if ($error) {
1667
            if (!empty($this->errors)) {
1668
                foreach ($this->errors as $errmsg) {
1669
                    dol_syslog(get_class($this) . "::update " . $errmsg, LOG_ERR);
1670
                    $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
1671
                }
1672
            }
1673
            $this->db->rollback();
1674
            return -1 * $error;
1675
        } else {
1676
            $this->db->commit();
1677
            return 1;
1678
        }
1679
    }
1680
1681
1682
    /**
1683
     *  Close the askprice
1684
     *
1685
     *  @param      User    $user       Object user that close
1686
     *  @param      int     $status     Status
1687
     *  @param      string  $note       Comment
1688
     *  @return     int                 Return integer <0 if KO, >0 if OK
1689
     */
1690
    public function cloture($user, $status, $note)
1691
    {
1692
        global $langs, $conf;
1693
        $hidedetails = 0;
1694
        $hidedesc = 0;
1695
        $hideref = 0;
1696
        $this->statut = $status;
1697
        $error = 0;
1698
        $now = dol_now();
1699
1700
        $this->db->begin();
1701
1702
        $sql = "UPDATE " . MAIN_DB_PREFIX . "supplier_proposal";
1703
        $sql .= " SET fk_statut = " . ((int) $status) . ", note_private = '" . $this->db->escape($note) . "', date_cloture='" . $this->db->idate($now) . "', fk_user_cloture=" . $user->id;
1704
        $sql .= " WHERE rowid = " . ((int) $this->id);
1705
1706
        $resql = $this->db->query($sql);
1707
        if ($resql) {
1708
            $modelpdf = $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED ? $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED : (empty($this->model_pdf) ? '' : $this->model_pdf);
1709
            $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_REFUSED';
1710
1711
            if ($status == 2) {
1712
                $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_SIGNED';
1713
                $modelpdf = $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL ? $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL : (empty($this->model_pdf) ? '' : $this->model_pdf);
1714
1715
                if (getDolGlobalString('SUPPLIER_PROPOSAL_UPDATE_PRICE_ON_SUPPlIER_PROPOSAL')) {     // TODO This option was not tested correctly. Error if product ref does not exists
1716
                    $result = $this->updateOrCreatePriceFournisseur($user);
1717
                }
1718
            }
1719
            if ($status == 4) {
1720
                $triggerName = 'PROPOSAL_SUPPLIER_CLASSIFY_BILLED';
1721
            }
1722
1723
            if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1724
                // Define output language
1725
                $outputlangs = $langs;
1726
                if (getDolGlobalInt('MAIN_MULTILANGS')) {
1727
                    $outputlangs = new Translate("", $conf);
1728
                    $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
1729
                    $outputlangs->setDefaultLang($newlang);
1730
                }
1731
                //$ret=$object->fetch($id);    // Reload to get new records
1732
                $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1733
            }
1734
1735
            // Call trigger
1736
            $result = $this->call_trigger($triggerName, $user);
1737
            if ($result < 0) {
1738
                $error++;
1739
            }
1740
            // End call triggers
1741
1742
            if (!$error) {
1743
                $this->db->commit();
1744
                return 1;
1745
            } else {
1746
                $this->db->rollback();
1747
                return -1;
1748
            }
1749
        } else {
1750
            $this->error = $this->db->lasterror();
1751
            $this->errors[] = $this->db->lasterror();
1752
            $this->db->rollback();
1753
            return -1;
1754
        }
1755
    }
1756
1757
    /**
1758
     *  Add or update supplier price according to result of proposal
1759
     *
1760
     *  @param     User     $user       Object user
1761
     *  @return    int                  > 0 if OK
1762
     */
1763
    public function updateOrCreatePriceFournisseur($user)
1764
    {
1765
        global $conf;
1766
1767
        dol_syslog(get_class($this) . "::updateOrCreatePriceFournisseur", LOG_DEBUG);
1768
        foreach ($this->lines as $product) {
1769
            if ($product->subprice <= 0) {
1770
                continue;
1771
            }
1772
            $productsupplier = new ProductFournisseur($this->db);
1773
1774
            $multicurrency_tx = 1;
1775
            $fk_multicurrency = 0;
1776
1777
            if (empty($this->thirdparty)) {
1778
                $this->fetch_thirdparty();
1779
            }
1780
1781
            $ref_fourn = $product->ref_fourn;
1782
            if (empty($ref_fourn)) {
1783
                $ref_fourn = $product->ref_supplier;
1784
            }
1785
            if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1786
                list($fk_multicurrency, $multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $product->multicurrency_code);
1787
            }
1788
            $productsupplier->id = $product->fk_product;
1789
1790
            $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, '', '', '');
1791
        }
1792
1793
        return 1;
1794
    }
1795
1796
    /**
1797
     *  Update ProductFournisseur
1798
     *
1799
     *  @param      int     $idProductFournPrice    id of llx_product_fournisseur_price
1800
     *  @param      Product $product                contain information to update
1801
     *  @param      User    $user                   Object user
1802
     *  @return     int                             Return integer <0 if KO, >0 if OK
1803
     */
1804
    public function updatePriceFournisseur($idProductFournPrice, $product, $user)
1805
    {
1806
        $price = price2num($product->subprice * $product->qty, 'MU');
1807
        $unitPrice = price2num($product->subprice, 'MU');
1808
1809
        $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);
1810
1811
        $resql = $this->db->query($sql);
1812
        if (!$resql) {
1813
            $this->error = $this->db->error();
1814
            $this->db->rollback();
1815
            return -1;
1816
        }
1817
        return 1;
1818
    }
1819
1820
    /**
1821
     *  Create ProductFournisseur
1822
     *
1823
     *  @param      Product     $product    Object Product
1824
     *  @param      User        $user       Object user
1825
     *  @return     int                     Return integer <0 if KO, >0 if OK
1826
     */
1827
    public function createPriceFournisseur($product, $user)
1828
    {
1829
        global $conf;
1830
1831
        $price = price2num($product->subprice * $product->qty, 'MU');
1832
        $qty = price2num($product->qty);
1833
        $unitPrice = price2num($product->subprice, 'MU');
1834
1835
        $now = dol_now();
1836
1837
        $values = array(
1838
            "'" . $this->db->idate($now) . "'",
1839
            $product->fk_product,
1840
            $this->thirdparty->id,
1841
            "'" . $product->ref_fourn . "'",
1842
            $price,
1843
            $qty,
1844
            $unitPrice,
1845
            $product->tva_tx,
1846
            $user->id
1847
        );
1848
        if (isModEnabled("multicurrency")) {
1849
            if (!empty($product->multicurrency_code)) {
1850
                $multicurrency = new MultiCurrency($this->db); //need to fetch because empty fk_multicurrency and rate
1851
                $multicurrency->fetch(0, $product->multicurrency_code);
1852
                if (!empty($multicurrency->id)) {
1853
                    $values[] = $multicurrency->id;
1854
                    $values[] = "'" . $product->multicurrency_code . "'";
1855
                    $values[] = $product->multicurrency_subprice;
1856
                    $values[] = $product->multicurrency_total_ht;
1857
                    $values[] = $multicurrency->rate->rate;
1858
                } else {
1859
                    for ($i = 0; $i < 5; $i++) {
1860
                        $values[] = 'NULL';
1861
                    }
1862
                }
1863
            }
1864
        }
1865
1866
        $sql = 'INSERT INTO ' . MAIN_DB_PREFIX . 'product_fournisseur_price ';
1867
        $sql .= '(datec, fk_product, fk_soc, ref_fourn, price, quantity, unitprice, tva_tx, fk_user';
1868
        if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1869
            $sql .= ',fk_multicurrency, multicurrency_code, multicurrency_unitprice, multicurrency_price, multicurrency_tx';
1870
        }
1871
        $sql .= ')  VALUES (' . implode(',', $values) . ')';
1872
1873
        $resql = $this->db->query($sql);
1874
        if (!$resql) {
1875
            $this->error = $this->db->error();
1876
            $this->db->rollback();
1877
            return -1;
1878
        }
1879
        return 1;
1880
    }
1881
1882
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1883
    /**
1884
     *  Set draft status
1885
     *
1886
     *  @param      User    $user       Object user that modify
1887
     *  @return     int                 Return integer <0 if KO, >0 if OK
1888
     */
1889
    public function setDraft($user)
1890
    {
1891
		// phpcs:enable
1892
        global $conf, $langs;
1893
1894
        $error = 0;
1895
1896
        if ($this->statut == self::STATUS_DRAFT) {
1897
            dol_syslog(get_class($this) . "::setDraft already draft status", LOG_WARNING);
1898
            return 0;
1899
        }
1900
1901
        $sql = "UPDATE " . MAIN_DB_PREFIX . "supplier_proposal";
1902
        $sql .= " SET fk_statut = " . self::STATUS_DRAFT;
1903
        $sql .= " WHERE rowid = " . ((int) $this->id);
1904
1905
        if ($this->db->query($sql)) {
1906
            if (!$error) {
1907
                $this->oldcopy = clone $this;
1908
            }
1909
1910
            if (!$error) {
1911
                // Call trigger
1912
                $result = $this->call_trigger('PROPOSAL_SUPPLIER_UNVALIDATE', $user);
1913
                if ($result < 0) {
1914
                    $error++;
1915
                }
1916
            }
1917
1918
            if (!$error) {
1919
                $this->status = self::STATUS_DRAFT;
1920
                $this->statut = self::STATUS_DRAFT; // deprecated
1921
                $this->db->commit();
1922
                return 1;
1923
            } else {
1924
                $this->db->rollback();
1925
                return -1;
1926
            }
1927
        } else {
1928
            return -1;
1929
        }
1930
    }
1931
1932
1933
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1934
    /**
1935
     *    Return list of askprice (eventually filtered on user) into an array
1936
     *
1937
     *    @param    int     $shortlist          0=Return array[id]=ref, 1=Return array[](id=>id,ref=>ref,name=>name)
1938
     *    @param    int     $draft              0=not draft, 1=draft
1939
     *    @param    int     $notcurrentuser     0=all user, 1=not current user
1940
     *    @param    int     $socid              Id third party
1941
     *    @param    int     $limit              For pagination
1942
     *    @param    int     $offset             For pagination
1943
     *    @param    string  $sortfield          Sort criteria
1944
     *    @param    string  $sortorder          Sort order
1945
     *    @return   array|int                           -1 if KO, array with result if OK
1946
     */
1947
    public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datec', $sortorder = 'DESC')
1948
    {
1949
		// phpcs:enable
1950
        global $user;
1951
1952
        $ga = array();
1953
1954
        $search_sale = 0;
1955
        if (!$user->hasRight('societe', 'client', 'voir')) {
1956
            $search_sale = $user->id;
1957
        }
1958
1959
        $sql = "SELECT s.rowid, s.nom as name, s.client,";
1960
        $sql .= " p.rowid as supplier_proposalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
1961
        $sql .= " p.datep as dp, p.fin_validite as datelimite";
1962
        $sql .= " FROM " . MAIN_DB_PREFIX . "societe as s, " . MAIN_DB_PREFIX . "supplier_proposal as p, " . MAIN_DB_PREFIX . "c_propalst as c";
1963
        $sql .= " WHERE p.entity IN (" . getEntity('supplier_proposal') . ")";
1964
        $sql .= " AND p.fk_soc = s.rowid";
1965
        $sql .= " AND p.fk_statut = c.id";
1966
        if ($socid) {
1967
            $sql .= " AND s.rowid = " . ((int) $socid);
1968
        }
1969
        if ($draft) {
1970
            $sql .= " AND p.fk_statut = 0";
1971
        }
1972
        if ($notcurrentuser > 0) {
1973
            $sql .= " AND p.fk_user_author <> " . ((int) $user->id);
1974
        }
1975
        // Search on sale representative
1976
        if ($search_sale && $search_sale != '-1') {
1977
            if ($search_sale == -2) {
1978
                $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM " . MAIN_DB_PREFIX . "societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
1979
            } elseif ($search_sale > 0) {
1980
                $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) . ")";
1981
            }
1982
        }
1983
        $sql .= $this->db->order($sortfield, $sortorder);
1984
        $sql .= $this->db->plimit($limit, $offset);
1985
1986
        $result = $this->db->query($sql);
1987
        if ($result) {
1988
            $num = $this->db->num_rows($result);
1989
            if ($num) {
1990
                $i = 0;
1991
                while ($i < $num) {
1992
                    $obj = $this->db->fetch_object($result);
1993
1994
                    if ($shortlist == 1) {
1995
                        $ga[$obj->supplier_proposalid] = $obj->ref;
1996
                    } elseif ($shortlist == 2) {
1997
                        $ga[$obj->supplier_proposalid] = $obj->ref . ' (' . $obj->name . ')';
1998
                    } else {
1999
                        $ga[$i]['id'] = $obj->supplier_proposalid;
2000
                        $ga[$i]['ref']  = $obj->ref;
2001
                        $ga[$i]['name'] = $obj->name;
2002
                    }
2003
2004
                    $i++;
2005
                }
2006
            }
2007
            return $ga;
2008
        } else {
2009
            dol_print_error($this->db);
2010
            return -1;
2011
        }
2012
    }
2013
2014
    /**
2015
     *  Delete askprice
2016
     *
2017
     *  @param  User    $user           Object user that delete
2018
     *  @param  int     $notrigger      1=Does not execute triggers, 0= execute triggers
2019
     *  @return int                     1 if ok, otherwise if error
2020
     */
2021
    public function delete($user, $notrigger = 0)
2022
    {
2023
        global $conf, $langs;
2024
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
2025
2026
        $error = 0;
2027
2028
        $this->db->begin();
2029
2030
        if (!$notrigger) {
2031
            // Call trigger
2032
            $result = $this->call_trigger('PROPOSAL_SUPPLIER_DELETE', $user);
2033
            if ($result < 0) {
2034
                $error++;
2035
            }
2036
            // End call triggers
2037
        }
2038
2039
        if (!$error) {
2040
            $main = MAIN_DB_PREFIX . 'supplier_proposaldet';
2041
            $ef = $main . "_extrafields";
2042
            $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_supplier_proposal = " . ((int) $this->id) . ")";
2043
            $sql = "DELETE FROM " . MAIN_DB_PREFIX . "supplier_proposaldet WHERE fk_supplier_proposal = " . ((int) $this->id);
2044
            if ($this->db->query($sql)) {
2045
                $sql = "DELETE FROM " . MAIN_DB_PREFIX . "supplier_proposal WHERE rowid = " . ((int) $this->id);
2046
                if ($this->db->query($sqlef) && $this->db->query($sql)) {
2047
                    // Delete linked object
2048
                    $res = $this->deleteObjectLinked();
2049
                    if ($res < 0) {
2050
                        $error++;
2051
                    }
2052
2053
                    if (!$error) {
2054
                        // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2055
                        $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
2056
                        $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
2057
2058
                        // We remove directory
2059
                        $ref = dol_sanitizeFileName($this->ref);
2060
                        if ($conf->supplier_proposal->dir_output && !empty($this->ref)) {
2061
                            $dir = $conf->supplier_proposal->dir_output . "/" . $ref;
2062
                            $file = $dir . "/" . $ref . ".pdf";
2063
                            if (file_exists($file)) {
2064
                                dol_delete_preview($this);
2065
2066
                                if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2067
                                    $this->error = 'ErrorFailToDeleteFile';
2068
                                    $this->errors = array('ErrorFailToDeleteFile');
2069
                                    $this->db->rollback();
2070
                                    return 0;
2071
                                }
2072
                            }
2073
                            if (file_exists($dir)) {
2074
                                $res = @dol_delete_dir_recursive($dir);
2075
                                if (!$res) {
2076
                                    $this->error = 'ErrorFailToDeleteDir';
2077
                                    $this->errors = array('ErrorFailToDeleteDir');
2078
                                    $this->db->rollback();
2079
                                    return 0;
2080
                                }
2081
                            }
2082
                        }
2083
                    }
2084
2085
                    // Removed extrafields
2086
                    if (!$error) {
2087
                        $result = $this->deleteExtraFields();
2088
                        if ($result < 0) {
2089
                            $error++;
2090
                            $errorflag = -4;
2091
                            dol_syslog(get_class($this) . "::delete erreur " . $errorflag . " " . $this->error, LOG_ERR);
2092
                        }
2093
                    }
2094
2095
                    if (!$error) {
2096
                        dol_syslog(get_class($this) . "::delete " . $this->id . " by " . $user->id, LOG_DEBUG);
2097
                        $this->db->commit();
2098
                        return 1;
2099
                    } else {
2100
                        $this->error = $this->db->lasterror();
2101
                        $this->db->rollback();
2102
                        return 0;
2103
                    }
2104
                } else {
2105
                    $this->error = $this->db->lasterror();
2106
                    $this->db->rollback();
2107
                    return -3;
2108
                }
2109
            } else {
2110
                $this->error = $this->db->lasterror();
2111
                $this->db->rollback();
2112
                return -2;
2113
            }
2114
        } else {
2115
            $this->db->rollback();
2116
            return -1;
2117
        }
2118
    }
2119
2120
    /**
2121
     *  Object SupplierProposal Information
2122
     *
2123
     *  @param  int     $id     Proposal id
2124
     *  @return void
2125
     */
2126
    public function info($id)
2127
    {
2128
        $sql = "SELECT c.rowid, ";
2129
        $sql .= " c.datec as date_creation, c.date_valid as date_validation, c.date_cloture as date_closure,";
2130
        $sql .= " c.fk_user_author, c.fk_user_valid, c.fk_user_cloture";
2131
        $sql .= " FROM " . MAIN_DB_PREFIX . "supplier_proposal as c";
2132
        $sql .= " WHERE c.rowid = " . ((int) $id);
2133
2134
        $result = $this->db->query($sql);
2135
2136
        if ($result) {
2137
            if ($this->db->num_rows($result)) {
2138
                $obj = $this->db->fetch_object($result);
2139
2140
                $this->id                = $obj->rowid;
2141
2142
                $this->date_creation     = $this->db->jdate($obj->date_creation);
2143
                $this->date_validation   = $this->db->jdate($obj->date_validation);
2144
                $this->date_cloture      = $this->db->jdate($obj->date_closure);
2145
2146
                $this->user_creation_id = $obj->fk_user_author;
2147
                $this->user_validation_id = $obj->fk_user_valid;
2148
                $this->user_closing_id = $obj->fk_user_cloture;
2149
            }
2150
            $this->db->free($result);
2151
        } else {
2152
            dol_print_error($this->db);
2153
        }
2154
    }
2155
2156
2157
    /**
2158
     *      Return label of status of proposal (draft, validated, ...)
2159
     *
2160
     *      @param      int         $mode        0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto
2161
     *      @return     string      Label
2162
     */
2163
    public function getLibStatut($mode = 0)
2164
    {
2165
        return $this->LibStatut((isset($this->statut) ? $this->statut : $this->status), $mode);
2166
    }
2167
2168
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2169
    /**
2170
     *  Return label of a status (draft, validated, ...)
2171
     *
2172
     *  @param      int         $status     Id status
2173
     *  @param      int         $mode       0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto
2174
     *  @return     string      Label
2175
     */
2176
    public function LibStatut($status, $mode = 1)
2177
    {
2178
		// phpcs:enable
2179
2180
        // Init/load array of translation of status
2181
        if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
2182
            global $langs;
2183
            $langs->load("supplier_proposal");
2184
            $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraft");
2185
            $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidated");
2186
            $this->labelStatus[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSigned");
2187
            $this->labelStatus[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSigned");
2188
            $this->labelStatus[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosed");
2189
            $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraftShort");
2190
            $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidatedShort");
2191
            $this->labelStatusShort[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSignedShort");
2192
            $this->labelStatusShort[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSignedShort");
2193
            $this->labelStatusShort[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosedShort");
2194
        }
2195
2196
        $statusnew = '';
2197
        if ($status == self::STATUS_DRAFT) {
2198
            $statusnew = 'status0';
2199
        } elseif ($status == self::STATUS_VALIDATED) {
2200
            $statusnew = 'status1';
2201
        } elseif ($status == self::STATUS_SIGNED) {
2202
            $statusnew = 'status4';
2203
        } elseif ($status == self::STATUS_NOTSIGNED) {
2204
            $statusnew = 'status9';
2205
        } elseif ($status == self::STATUS_CLOSE) {
2206
            $statusnew = 'status6';
2207
        }
2208
2209
        return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusnew, $mode);
2210
    }
2211
2212
2213
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2214
    /**
2215
     *      Load indicators for dashboard (this->nbtodo and this->nbtodolate)
2216
     *
2217
     *      @param          User    $user   Object user
2218
     *      @param          string  $mode   "opened" for askprice to close, "signed" for proposal to invoice
2219
     *      @return         WorkboardResponse|int   Return integer <0 if KO, WorkboardResponse if OK
2220
     */
2221
    public function load_board($user, $mode)
2222
    {
2223
		// phpcs:enable
2224
        global $conf, $user, $langs;
2225
2226
        $now = dol_now();
2227
2228
        $clause = " WHERE";
2229
2230
        $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.date_cloture as datefin";
2231
        $sql .= " FROM " . MAIN_DB_PREFIX . "supplier_proposal as p";
2232
        if (!$user->hasRight('societe', 'client', 'voir')) {
2233
            $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
2234
            $sql .= " WHERE sc.fk_user = " . ((int) $user->id);
2235
            $clause = " AND";
2236
        }
2237
        $sql .= $clause . " p.entity IN (" . getEntity('supplier_proposal') . ")";
2238
        if ($mode == 'opened') {
2239
            $sql .= " AND p.fk_statut = 1";
2240
        }
2241
        if ($mode == 'signed') {
2242
            $sql .= " AND p.fk_statut = 2";
2243
        }
2244
        if ($user->socid) {
2245
            $sql .= " AND p.fk_soc = " . ((int) $user->socid);
2246
        }
2247
2248
        $resql = $this->db->query($sql);
2249
        if ($resql) {
2250
            $label = $labelShort = '';
2251
            $status = '';
2252
            if ($mode == 'opened') {
2253
                $delay_warning = !empty($conf->supplier_proposal->cloture->warning_delay) ? $conf->supplier_proposal->cloture->warning_delay : 0;
2254
                $status = self::STATUS_VALIDATED;
2255
                $label = $langs->trans("SupplierProposalsToClose");
2256
                $labelShort = $langs->trans("ToAcceptRefuse");
2257
            }
2258
            if ($mode == 'signed') {
2259
                $delay_warning = !empty($conf->supplier_proposal->facturation->warning_delay) ? $conf->supplier_proposal->facturation->warning_delay : 0;
2260
                $status = self::STATUS_SIGNED;
2261
                $label = $langs->trans("SupplierProposalsToProcess"); // May be billed or ordered
2262
                $labelShort = $langs->trans("ToClose");
2263
            }
2264
2265
            $response = new WorkboardResponse();
2266
            $response->warning_delay = $delay_warning / 60 / 60 / 24;
2267
            $response->label = $label;
2268
            $response->labelShort = $labelShort;
2269
            $response->url = constant('BASE_URL') . '/supplier_proposal/list.php?search_status=' . $status;
2270
            $response->img = img_object('', "propal");
2271
2272
            // This assignment in condition is not a bug. It allows walking the results.
2273
            while ($obj = $this->db->fetch_object($resql)) {
2274
                $response->nbtodo++;
2275
                if ($mode == 'opened') {
2276
                    $datelimit = $this->db->jdate($obj->datefin);
2277
                    if ($datelimit < ($now - $delay_warning)) {
2278
                        $response->nbtodolate++;
2279
                    }
2280
                }
2281
                // TODO Definir regle des propales a facturer en retard
2282
                // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
2283
            }
2284
            return $response;
2285
        } else {
2286
            $this->error = $this->db->lasterror();
2287
            return -1;
2288
        }
2289
    }
2290
2291
2292
    /**
2293
     *  Initialise an instance with random values.
2294
     *  Used to build previews or test instances.
2295
     *  id must be 0 if object instance is a specimen.
2296
     *
2297
     *  @return int
2298
     */
2299
    public function initAsSpecimen()
2300
    {
2301
        global $user, $langs, $conf;
2302
2303
        // Load array of products prodids
2304
        $num_prods = 0;
2305
        $prodids = array();
2306
        $sql = "SELECT rowid";
2307
        $sql .= " FROM " . MAIN_DB_PREFIX . "product";
2308
        $sql .= " WHERE entity IN (" . getEntity('product') . ")";
2309
        $sql .= $this->db->plimit(100);
2310
2311
        $resql = $this->db->query($sql);
2312
        if ($resql) {
2313
            $num_prods = $this->db->num_rows($resql);
2314
            $i = 0;
2315
            while ($i < $num_prods) {
2316
                $i++;
2317
                $row = $this->db->fetch_row($resql);
2318
                $prodids[$i] = $row[0];
2319
            }
2320
        }
2321
2322
        // Initialise parameters
2323
        $this->id = 0;
2324
        $this->ref = 'SPECIMEN';
2325
        $this->specimen = 1;
2326
        $this->socid = 1;
2327
        $this->date = time();
2328
        $this->cond_reglement_id   = 1;
2329
        $this->cond_reglement_code = 'RECEP';
2330
        $this->mode_reglement_id   = 7;
2331
        $this->mode_reglement_code = 'CHQ';
2332
        $this->note_public = 'This is a comment (public)';
2333
        $this->note_private = 'This is a comment (private)';
2334
        // Lines
2335
        $nbp = 5;
2336
        $xnbp = 0;
2337
        while ($xnbp < $nbp) {
2338
            $line = new SupplierProposalLine($this->db);
2339
            $line->desc = $langs->trans("Description") . " " . $xnbp;
2340
            $line->qty = 1;
2341
            $line->subprice = 100;
2342
            $line->tva_tx = 19.6;
2343
            $line->localtax1_tx = 0;
2344
            $line->localtax2_tx = 0;
2345
            if ($xnbp == 2) {
2346
                $line->total_ht = 50;
2347
                $line->total_ttc = 59.8;
2348
                $line->total_tva = 9.8;
2349
                $line->remise_percent = 50;
2350
            } else {
2351
                $line->total_ht = 100;
2352
                $line->total_ttc = 119.6;
2353
                $line->total_tva = 19.6;
2354
                $line->remise_percent = 0;
2355
            }
2356
2357
            if ($num_prods > 0) {
2358
                $prodid = mt_rand(1, $num_prods);
2359
                $line->fk_product = $prodids[$prodid];
2360
            }
2361
2362
            $this->lines[$xnbp] = $line;
2363
2364
            $this->total_ht       += $line->total_ht;
2365
            $this->total_tva      += $line->total_tva;
2366
            $this->total_ttc      += $line->total_ttc;
2367
2368
            $xnbp++;
2369
        }
2370
2371
        return 1;
2372
    }
2373
2374
    /**
2375
     *      Load indicator this->nb of global stats widget
2376
     *
2377
     *      @return     int         Return integer <0 if ko, >0 if ok
2378
     */
2379
    public function loadStateBoard()
2380
    {
2381
        global $conf, $user;
2382
2383
        $this->nb = array();
2384
        $clause = "WHERE";
2385
2386
        $sql = "SELECT count(p.rowid) as nb";
2387
        $sql .= " FROM " . MAIN_DB_PREFIX . "supplier_proposal as p";
2388
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe as s ON p.fk_soc = s.rowid";
2389
        if (!$user->hasRight('societe', 'client', 'voir')) {
2390
            $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2391
            $sql .= " WHERE sc.fk_user = " . ((int) $user->id);
2392
            $clause = "AND";
2393
        }
2394
        $sql .= " " . $clause . " p.entity IN (" . getEntity('supplier_proposal') . ")";
2395
2396
        $resql = $this->db->query($sql);
2397
        if ($resql) {
2398
            // This assignment in condition is not a bug. It allows walking the results.
2399
            while ($obj = $this->db->fetch_object($resql)) {
2400
                $this->nb["supplier_proposals"] = $obj->nb;
2401
            }
2402
            $this->db->free($resql);
2403
            return 1;
2404
        } else {
2405
            dol_print_error($this->db);
2406
            $this->error = $this->db->lasterror();
2407
            return -1;
2408
        }
2409
    }
2410
2411
2412
    /**
2413
     *  Returns the reference to the following non used Proposal used depending on the active numbering module
2414
     *  defined into SUPPLIER_PROPOSAL_ADDON
2415
     *
2416
     *  @param  Societe     $soc    Object thirdparty
2417
     *  @return string              Reference libre pour la propale
2418
     */
2419
    public function getNextNumRef($soc)
2420
    {
2421
        global $conf, $db, $langs;
2422
        $langs->load("supplier_proposal");
2423
2424
        if (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON')) {
2425
            $mybool = false;
2426
2427
            $file = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') . ".php";
2428
            $classname = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
2429
2430
            // Include file with class
2431
            $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2432
            foreach ($dirmodels as $reldir) {
2433
                $dir = dol_buildpath($reldir . "core/modules/supplier_proposal/");
2434
2435
                // Load file with numbering class (if found)
2436
                $mybool = ((bool) @include_once $dir . $file) || $mybool;
2437
            }
2438
2439
            if (!$mybool) {
2440
                dol_print_error(null, "Failed to include file " . $file);
2441
                return '';
2442
            }
2443
2444
            $obj = new $classname();
2445
            $numref = "";
2446
            $numref = $obj->getNextValue($soc, $this);
2447
2448
            if ($numref != "") {
2449
                return $numref;
2450
            } else {
2451
                $this->error = $obj->error;
2452
                return "";
2453
            }
2454
        } else {
2455
            $langs->load("errors");
2456
            print $langs->trans("Error") . " " . $langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("SupplierProposal"));
2457
            return "";
2458
        }
2459
    }
2460
2461
    /**
2462
     * getTooltipContentArray
2463
     *
2464
     * @param array $params ex option, infologin
2465
     * @since v18
2466
     * @return array
2467
     */
2468
    public function getTooltipContentArray($params)
2469
    {
2470
        global $conf, $langs, $menumanager;
2471
2472
        $langs->load('supplier_proposal');
2473
2474
        if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2475
            return ['optimize' => $langs->trans("ShowSupplierProposal")];
2476
        }
2477
2478
        $option = $params['option'] ?? '';
2479
        $datas = [];
2480
2481
        $datas['picto'] = img_picto('', $this->picto) . ' <u class="paddingrightonly">' . $langs->trans("SupplierProposal") . '</u>';
2482
        if (isset($this->status)) {
2483
            $datas['picto'] .= ' ' . $this->getLibStatut(5);
2484
        }
2485
        if (!empty($this->ref)) {
2486
            $datas['ref'] = '<br><b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
2487
        }
2488
        if (!empty($this->ref_fourn)) {
2489
            $datas['ref_supplier'] = '<br><b>' . $langs->trans('RefSupplier') . ':</b> ' . $this->ref_fourn;
2490
        }
2491
        if (!empty($this->total_ht)) {
2492
            $datas['amount_ht'] = '<br><b>' . $langs->trans('AmountHT') . ':</b> ' . price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2493
        }
2494
        if (!empty($this->total_tva)) {
2495
            $datas['amount_vat'] = '<br><b>' . $langs->trans('VAT') . ':</b> ' . price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2496
        }
2497
        if (!empty($this->total_ttc)) {
2498
            $datas['amount_ttc'] = '<br><b>' . $langs->trans('AmountTTC') . ':</b> ' . price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2499
        }
2500
2501
        return $datas;
2502
    }
2503
2504
    /**
2505
     *  Return clicable link of object (with eventually picto)
2506
     *
2507
     *  @param      int     $withpicto                  Add picto into link
2508
     *  @param      string  $option                     Where point the link ('compta', 'expedition', 'document', ...)
2509
     *  @param      string  $get_params                 Parameters added to url
2510
     *  @param      int     $notooltip                  1=Disable tooltip
2511
     *  @param      int     $save_lastsearch_value      -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
2512
     *  @param      int     $addlinktonotes             Add link to show notes
2513
     *  @return     string                              String with URL
2514
     */
2515
    public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2516
    {
2517
        global $langs, $conf, $user, $hookmanager;
2518
2519
        if (!empty($conf->dol_no_mouse_hover)) {
2520
            $notooltip = 1; // Force disable tooltips
2521
        }
2522
2523
        $url = '';
2524
        $result = '';
2525
        $params = [
2526
            'id' => $this->id,
2527
            'objecttype' => $this->element,
2528
            'option' => $option,
2529
        ];
2530
        $classfortooltip = 'classfortooltip';
2531
        $dataparams = '';
2532
        if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2533
            $classfortooltip = 'classforajaxtooltip';
2534
            $dataparams = ' data-params="' . dol_escape_htmltag(json_encode($params)) . '"';
2535
            $label = '';
2536
        } else {
2537
            $label = implode($this->getTooltipContentArray($params));
2538
        }
2539
2540
        if ($option == '') {
2541
            $url = constant('BASE_URL') . '/supplier_proposal/card.php?id=' . $this->id . $get_params;
2542
        }
2543
        if ($option == 'document') {
2544
            $url = constant('BASE_URL') . '/supplier_proposal/document.php?id=' . $this->id . $get_params;
2545
        }
2546
2547
        if ($option !== 'nolink') {
2548
            // Add param to save lastsearch_values or not
2549
            $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2550
            if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2551
                $add_save_lastsearch_values = 1;
2552
            }
2553
            if ($add_save_lastsearch_values) {
2554
                $url .= '&save_lastsearch_values=1';
2555
            }
2556
        }
2557
2558
        $linkclose = '';
2559
        if (empty($notooltip) && $user->hasRight('propal', 'lire')) {
2560
            if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2561
                $label = $langs->trans("ShowSupplierProposal");
2562
                $linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
2563
            }
2564
            $linkclose .= ($label ? ' title="' . dol_escape_htmltag($label, 1) . '"' : ' title="tocomplete"');
2565
            $linkclose .= $dataparams . ' class="' . $classfortooltip . '"';
2566
        }
2567
2568
        $linkstart = '<a href="' . $url . '"';
2569
        $linkstart .= $linkclose . '>';
2570
        $linkend = '</a>';
2571
2572
        $result .= $linkstart;
2573
        if ($withpicto) {
2574
            $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
2575
        }
2576
        if ($withpicto != 2) {
2577
            $result .= $this->ref;
2578
        }
2579
        $result .= $linkend;
2580
2581
        if ($addlinktonotes) {
2582
            $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2583
            if ($txttoshow) {
2584
                $notetoshow = $langs->trans("ViewPrivateNote") . ':<br>' . dol_string_nohtmltag($txttoshow, 1);
2585
                $result .= ' <span class="note inline-block">';
2586
                $result .= '<a href="' . constant('BASE_URL') . '/supplier_proposal/note.php?id=' . $this->id . '" class="classfortooltip" title="' . dol_escape_htmltag($notetoshow) . '">';
2587
                $result .= img_picto('', 'note');
2588
                $result .= '</a>';
2589
                //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
2590
                //$result.='</a>';
2591
                $result .= '</span>';
2592
            }
2593
        }
2594
        global $action;
2595
        $hookmanager->initHooks(array($this->element . 'dao'));
2596
        $parameters = array('id' => $this->id, 'getnomurl' => &$result);
2597
        $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2598
        if ($reshook > 0) {
2599
            $result = $hookmanager->resPrint;
2600
        } else {
2601
            $result .= $hookmanager->resPrint;
2602
        }
2603
        return $result;
2604
    }
2605
2606
    /**
2607
     *  Retrieve an array of supplier proposal lines
2608
     *
2609
     *  @return int     >0 if OK, <0 if KO
2610
     */
2611
    public function getLinesArray()
2612
    {
2613
        // For other object, here we call fetch_lines. But fetch_lines does not exists on supplier proposal
2614
2615
        $sql = 'SELECT pt.rowid, pt.label as custom_label, pt.description, pt.fk_product, pt.fk_remise_except,';
2616
        $sql .= ' pt.qty, pt.tva_tx, pt.vat_src_code, pt.remise_percent, pt.subprice, pt.info_bits,';
2617
        $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,';
2618
        $sql .= ' pt.product_type, pt.rang, pt.fk_parent_line,';
2619
        $sql .= ' p.label as product_label, p.ref, p.fk_product_type, p.rowid as prodid,';
2620
        $sql .= ' p.description as product_desc, pt.ref_fourn as ref_supplier,';
2621
        $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';
2622
        $sql .= ' FROM ' . MAIN_DB_PREFIX . 'supplier_proposaldet as pt';
2623
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'product as p ON pt.fk_product=p.rowid';
2624
        $sql .= ' WHERE pt.fk_supplier_proposal = ' . ((int) $this->id);
2625
        $sql .= ' ORDER BY pt.rang ASC, pt.rowid';
2626
2627
        dol_syslog(get_class($this) . '::getLinesArray', LOG_DEBUG);
2628
        $resql = $this->db->query($sql);
2629
        if ($resql) {
2630
            $num = $this->db->num_rows($resql);
2631
            $i = 0;
2632
2633
            while ($i < $num) {
2634
                $obj = $this->db->fetch_object($resql);
2635
2636
                $this->lines[$i] = new SupplierProposalLine($this->db);
2637
                $this->lines[$i]->id = $obj->rowid; // for backward compatibility
2638
                $this->lines[$i]->rowid             = $obj->rowid;
2639
                $this->lines[$i]->label             = $obj->custom_label;
2640
                $this->lines[$i]->description = $obj->description;
2641
                $this->lines[$i]->fk_product = $obj->fk_product;
2642
                $this->lines[$i]->ref = $obj->ref;
2643
                $this->lines[$i]->product_label = $obj->product_label;
2644
                $this->lines[$i]->product_desc      = $obj->product_desc;
2645
                $this->lines[$i]->fk_product_type = $obj->fk_product_type; // deprecated
2646
                $this->lines[$i]->product_type      = $obj->product_type;
2647
                $this->lines[$i]->qty = $obj->qty;
2648
                $this->lines[$i]->subprice = $obj->subprice;
2649
                $this->lines[$i]->fk_remise_except = $obj->fk_remise_except;
2650
                $this->lines[$i]->remise_percent = $obj->remise_percent;
2651
                $this->lines[$i]->tva_tx = $obj->tva_tx;
2652
                $this->lines[$i]->vat_src_code = $obj->vat_src_code;
2653
                $this->lines[$i]->info_bits         = $obj->info_bits;
2654
                $this->lines[$i]->total_ht = $obj->total_ht;
2655
                $this->lines[$i]->total_tva         = $obj->total_tva;
2656
                $this->lines[$i]->total_ttc         = $obj->total_ttc;
2657
                $this->lines[$i]->fk_fournprice = $obj->fk_fournprice;
2658
                $marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->lines[$i]->fk_fournprice, $obj->pa_ht);
2659
                $this->lines[$i]->pa_ht = $marginInfos[0];
2660
                $this->lines[$i]->marge_tx = $marginInfos[1];
2661
                $this->lines[$i]->marque_tx = $marginInfos[2];
2662
                $this->lines[$i]->fk_parent_line = $obj->fk_parent_line;
2663
                $this->lines[$i]->special_code = $obj->special_code;
2664
                $this->lines[$i]->rang = $obj->rang;
2665
2666
                $this->lines[$i]->ref_fourn = $obj->ref_supplier; // deprecated
2667
                $this->lines[$i]->ref_supplier = $obj->ref_supplier;
2668
2669
                // Multicurrency
2670
                $this->lines[$i]->fk_multicurrency = $obj->fk_multicurrency;
2671
                $this->lines[$i]->multicurrency_code = $obj->multicurrency_code;
2672
                $this->lines[$i]->multicurrency_subprice    = $obj->multicurrency_subprice;
2673
                $this->lines[$i]->multicurrency_total_ht    = $obj->multicurrency_total_ht;
2674
                $this->lines[$i]->multicurrency_total_tva   = $obj->multicurrency_total_tva;
2675
                $this->lines[$i]->multicurrency_total_ttc   = $obj->multicurrency_total_ttc;
2676
                $this->lines[$i]->fk_unit = $obj->fk_unit;
2677
2678
                $i++;
2679
            }
2680
            $this->db->free($resql);
2681
2682
            return 1;
2683
        } else {
2684
            $this->error = $this->db->error();
2685
            return -1;
2686
        }
2687
    }
2688
2689
    /**
2690
     *  Create a document onto disk according to template module.
2691
     *
2692
     *  @param      string      $modele         Force model to use ('' to not force)
2693
     *  @param      Translate   $outputlangs    Object langs to use for output
2694
     *  @param      int         $hidedetails    Hide details of lines
2695
     *  @param      int         $hidedesc       Hide description
2696
     *  @param      int         $hideref        Hide ref
2697
     *  @param   null|array  $moreparams     Array to provide more information
2698
     *  @return     int                         0 if KO, 1 if OK
2699
     */
2700
    public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2701
    {
2702
        global $conf, $langs;
2703
2704
        $langs->load("supplier_proposal");
2705
        $outputlangs->load("products");
2706
2707
        if (!dol_strlen($modele)) {
2708
            $modele = 'aurore';
2709
2710
            if ($this->model_pdf) {
2711
                $modele = $this->model_pdf;
2712
            } elseif (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF')) {
2713
                $modele = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF');
2714
            }
2715
        }
2716
2717
        $modelpath = "core/modules/supplier_proposal/doc/";
2718
2719
        return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2720
    }
2721
2722
2723
    /**
2724
     * Function used to replace a thirdparty id with another one.
2725
     *
2726
     * @param   DoliDB  $dbs        Database handler, because function is static we name it $dbs not $db to avoid breaking coding test
2727
     * @param   int     $origin_id  Old thirdparty id
2728
     * @param   int     $dest_id    New thirdparty id
2729
     * @return  bool
2730
     */
2731
    public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2732
    {
2733
        $tables = array(
2734
            'supplier_proposal'
2735
        );
2736
2737
        return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2738
    }
2739
2740
    /**
2741
     * Function used to replace a product id with another one.
2742
     *
2743
     * @param DoliDB $db Database handler
2744
     * @param int $origin_id Old product id
2745
     * @param int $dest_id New product id
2746
     * @return bool
2747
     */
2748
    public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
2749
    {
2750
        $tables = array(
2751
            'supplier_proposaldet'
2752
        );
2753
2754
        return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
2755
    }
2756
2757
2758
    /**
2759
     *  Return clicable link of object (with eventually picto)
2760
     *
2761
     *  @param      string      $option                 Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link)
2762
     *  @param      array       $arraydata              Array of data
2763
     *  @return     string                              HTML Code for Kanban thumb.
2764
     */
2765
    public function getKanbanView($option = '', $arraydata = null)
2766
    {
2767
        global $langs;
2768
2769
        $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2770
2771
        $return = '<div class="box-flex-item box-flex-grow-zero">';
2772
        $return .= '<div class="info-box info-box-sm">';
2773
        $return .= '<span class="info-box-icon bg-infobox-action">';
2774
        $return .= img_picto('', $this->picto);
2775
        //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2776
        $return .= '</span>';
2777
        $return .= '<div class="info-box-content">';
2778
        $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . (method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref) . '</span>';
2779
        if ($selected >= 0) {
2780
            $return .= '<input id="cb' . $this->id . '" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="' . $this->id . '"' . ($selected ? ' checked="checked"' : '') . '>';
2781
        }
2782
        if (property_exists($this, 'socid')) {
2783
            $return .= '<span class="info-box-ref"> | ' . $this->socid . '</span>';
2784
        }
2785
        if (property_exists($this, 'delivery_date')) {
2786
            $return .= '<br><span class="opacitymedium">' . $langs->trans("DateEnd") . '</span> : <span class="info-box-label">' . dol_print_date($this->delivery_date) . '</span>';
2787
        }
2788
        if (property_exists($this, 'total_ttc')) {
2789
            $return .= '<br><span class="opacitymedium" >' . $langs->trans("AmountHT") . ' : </span><span class="info-box-label amount">' . price($this->total_ttc) . '</span>';
2790
        }
2791
        if (method_exists($this, 'getLibStatut')) {
2792
            $return .= '<br><div class="info-box-status">' . $this->getLibStatut(3) . '</div>';
2793
        }
2794
        $return .= '</div>';
2795
        $return .= '</div>';
2796
        $return .= '</div>';
2797
        return $return;
2798
    }
2799
}
2800