Test Failed
Branch main (a69845)
by Rafael
63:42
created

SupplierProposal::getNomUrl()   F

Complexity

Conditions 25
Paths > 20000

Size

Total Lines 89
Code Lines 61

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 25
eloc 61
c 0
b 0
f 0
nc 28800
nop 6
dl 0
loc 89
rs 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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

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

1248
                /** @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...
1249
                $this->note_private         = $obj->note_private;
1250
                $this->note_public          = $obj->note_public;
1251
                $this->statut               = (int) $obj->fk_statut;
1252
                $this->status               = (int) $obj->fk_statut;
1253
                $this->datec                = $this->db->jdate($obj->datec); // TODO deprecated
1254
                $this->datev                = $this->db->jdate($obj->datev); // TODO deprecated
1255
                $this->date_creation = $this->db->jdate($obj->datec);   // Creation date
1256
                $this->date                 = $this->date_creation;
1257
                $this->date_validation = $this->db->jdate($obj->datev); // Validation date
1258
                $this->delivery_date        = $this->db->jdate($obj->delivery_date);
1259
                $this->shipping_method_id   = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1260
1261
                $this->mode_reglement_id    = $obj->fk_mode_reglement;
1262
                $this->mode_reglement_code  = $obj->mode_reglement_code;
1263
                $this->mode_reglement       = $obj->mode_reglement;
1264
                $this->fk_account           = ($obj->fk_account > 0) ? $obj->fk_account : null;
1265
                $this->cond_reglement_id    = $obj->fk_cond_reglement;
1266
                $this->cond_reglement_code  = $obj->cond_reglement_code;
1267
                $this->cond_reglement       = $obj->cond_reglement;
0 ignored issues
show
Deprecated Code introduced by
The property 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

1267
                /** @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...
Bug Best Practice introduced by
The property $cond_reglement is declared private in CommonObject. Since you implement __set, consider adding a @property or @property-write.
Loading history...
1268
                $this->cond_reglement_doc   = $obj->cond_reglement_libelle_doc;
1269
1270
                $this->extraparams = (array) json_decode($obj->extraparams, true);
1271
1272
                $this->user_author_id = $obj->fk_user_author;
1273
                $this->user_validation_id = $obj->fk_user_valid;
1274
                $this->user_closing_id = $obj->fk_user_cloture;
1275
1276
                // Multicurrency
1277
                $this->fk_multicurrency         = $obj->fk_multicurrency;
1278
                $this->multicurrency_code = $obj->multicurrency_code;
1279
                $this->multicurrency_tx         = $obj->multicurrency_tx;
1280
                $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1281
                $this->multicurrency_total_tva  = $obj->multicurrency_total_tva;
1282
                $this->multicurrency_total_ttc  = $obj->multicurrency_total_ttc;
1283
1284
                // Retrieve all extrafield
1285
                // fetch optionals attributes and labels
1286
                $this->fetch_optionals();
1287
1288
                $this->db->free($resql);
1289
1290
                $this->lines = array();
1291
1292
                // Lines of supplier proposals
1293
                $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,";
1294
                $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,";
1295
                $sql .= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label,';
1296
                $sql .= ' d.ref_fourn as ref_produit_fourn,';
1297
                $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';
1298
                $sql .= " FROM " . MAIN_DB_PREFIX . "supplier_proposaldet as d";
1299
                $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "product as p ON d.fk_product = p.rowid";
1300
                $sql .= " WHERE d.fk_supplier_proposal = " . ((int) $this->id);
1301
                $sql .= " ORDER by d.rang";
1302
1303
                $result = $this->db->query($sql);
1304
                if ($result) {
1305
                    $num = $this->db->num_rows($result);
1306
                    $i = 0;
1307
1308
                    while ($i < $num) {
1309
                        $objp                   = $this->db->fetch_object($result);
1310
1311
                        $line                   = new SupplierProposalLine($this->db);
1312
1313
                        $line->rowid = $objp->rowid; // deprecated
0 ignored issues
show
Deprecated Code introduced by
The property 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

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

1484
                                /** @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...
1485
                            }
1486
                        }
1487
                    }
1488
                }
1489
1490
                $this->ref = $num;
1491
                $this->statut = self::STATUS_VALIDATED;
1492
                $this->status = self::STATUS_VALIDATED;
1493
                $this->user_validation_id = $user->id;
1494
                $this->datev = $now;
1495
                $this->date_validation = $now;
1496
1497
                $this->db->commit();
1498
                return 1;
1499
            } else {
1500
                $this->db->rollback();
1501
                return -1;
1502
            }
1503
        } else {
1504
            dol_syslog("You don't have permission to validate supplier proposal", LOG_WARNING);
1505
            return -2;
1506
        }
1507
    }
1508
1509
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1510
    /**
1511
     *  Set delivery date
1512
     *
1513
     *  @param      User    $user               Object user that modify
1514
     *  @param      int     $delivery_date      Delivery date
1515
     *  @return     int                         Return integer <0 if ko, >0 if ok
1516
     *  @deprecated Use  setDeliveryDate
1517
     */
1518
    public function set_date_livraison($user, $delivery_date)
1519
    {
1520
		// phpcs:enable
1521
        return $this->setDeliveryDate($user, $delivery_date);
1522
    }
1523
1524
    /**
1525
     *  Set delivery date
1526
     *
1527
     *  @param      User        $user               Object user that modify
1528
     *  @param      int         $delivery_date     Delivery date
1529
     *  @return     int                             Return integer <0 if ko, >0 if ok
1530
     */
1531
    public function setDeliveryDate($user, $delivery_date)
1532
    {
1533
        if ($user->hasRight('supplier_proposal', 'creer')) {
1534
            $sql = "UPDATE " . MAIN_DB_PREFIX . "supplier_proposal ";
1535
            $sql .= " SET date_livraison = " . ($delivery_date != '' ? "'" . $this->db->idate($delivery_date) . "'" : 'null');
1536
            $sql .= " WHERE rowid = " . ((int) $this->id);
1537
1538
            if ($this->db->query($sql)) {
1539
                $this->delivery_date = $delivery_date;
1540
                return 1;
1541
            } else {
1542
                $this->error = $this->db->error();
1543
                dol_syslog(get_class($this) . "::setDeliveryDate Erreur SQL");
1544
                return -1;
1545
            }
1546
        }
1547
        return 0;
1548
    }
1549
1550
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1551
    /**
1552
     *  Set an overall discount on the proposal
1553
     *
1554
     *  @param      User    $user       Object user that modify
1555
     *  @param      double  $remise      Amount discount
1556
     *  @return     int                 Return integer <0 if ko, >0 if ok
1557
     */
1558
    /*
1559
    public function set_remise_percent($user, $remise)
1560
    {
1561
		// phpcs:enable
1562
        $remise = trim($remise) ?trim($remise) : 0;
1563
1564
        if ($user->hasRight('supplier_proposal', 'creer')) {
1565
            $remise = price2num($remise, 2);
1566
1567
            $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal SET remise_percent = ".((float) $remise);
1568
            $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1569
1570
            if ($this->db->query($sql)) {
1571
                $this->remise_percent = ((float) $remise);
1572
                $this->update_price(1);
1573
                return 1;
1574
            } else {
1575
                $this->error = $this->db->error();
1576
                return -1;
1577
            }
1578
        }
1579
        return 0;
1580
    }
1581
    */
1582
1583
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1584
    /**
1585
     *  Set an absolute overall discount on the proposal
1586
     *
1587
     *  @param      User    $user        Object user that modify
1588
     *  @param      double  $remise      Amount discount
1589
     *  @return     int                 Return integer <0 if ko, >0 if ok
1590
     */
1591
    /*
1592
    public function set_remise_absolue($user, $remise)
1593
    {
1594
		// phpcs:enable
1595
        if (empty($remise)) {
1596
            $remise = 0;
1597
        }
1598
1599
        $remise = price2num($remise);
1600
1601
        if ($user->hasRight('supplier_proposal', 'creer')) {
1602
            $sql = "UPDATE ".MAIN_DB_PREFIX."supplier_proposal ";
1603
            $sql .= " SET remise_absolue = ".((float) $remise);
1604
            $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
1605
1606
            if ($this->db->query($sql)) {
1607
                $this->remise_absolue = $remise;
1608
                $this->update_price(1);
1609
                return 1;
1610
            } else {
1611
                $this->error = $this->db->error();
1612
                return -1;
1613
            }
1614
        }
1615
        return 0;
1616
    }
1617
    */
1618
1619
1620
    /**
1621
     *  Reopen the commercial proposal
1622
     *
1623
     *  @param      User    $user       Object user that close
1624
     *  @param      int     $statut     Statut
1625
     *  @param      string  $note       Comment
1626
     *  @param      int     $notrigger  1=Does not execute triggers, 0= execute triggers
1627
     *  @return     int                 Return integer <0 if KO, >0 if OK
1628
     */
1629
    public function reopen($user, $statut, $note = '', $notrigger = 0)
1630
    {
1631
        global $langs, $conf;
1632
1633
        $this->statut = $statut;
1634
        $error = 0;
1635
1636
        $sql = "UPDATE " . MAIN_DB_PREFIX . "supplier_proposal";
1637
        $sql .= " SET fk_statut = " . ((int) $this->statut) . ",";
1638
        if (!empty($note)) {
1639
            $sql .= " note_private = '" . $this->db->escape($note) . "',";
1640
        }
1641
        $sql .= " date_cloture = NULL, fk_user_cloture = NULL";
1642
        $sql .= " WHERE rowid = " . ((int) $this->id);
1643
1644
        $this->db->begin();
1645
1646
        dol_syslog(get_class($this) . "::reopen", LOG_DEBUG);
1647
        $resql = $this->db->query($sql);
1648
        if (!$resql) {
1649
            $error++;
1650
            $this->errors[] = "Error " . $this->db->lasterror();
1651
        }
1652
        if (!$error) {
1653
            if (!$notrigger) {
1654
                // Call trigger
1655
                $result = $this->call_trigger('PROPOSAL_SUPPLIER_REOPEN', $user);
1656
                if ($result < 0) {
1657
                    $error++;
1658
                }
1659
                // End call triggers
1660
            }
1661
        }
1662
1663
        // Commit or rollback
1664
        if ($error) {
1665
            if (!empty($this->errors)) {
1666
                foreach ($this->errors as $errmsg) {
1667
                    dol_syslog(get_class($this) . "::update " . $errmsg, LOG_ERR);
1668
                    $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
1669
                }
1670
            }
1671
            $this->db->rollback();
1672
            return -1 * $error;
1673
        } else {
1674
            $this->db->commit();
1675
            return 1;
1676
        }
1677
    }
1678
1679
1680
    /**
1681
     *  Close the askprice
1682
     *
1683
     *  @param      User    $user       Object user that close
1684
     *  @param      int     $status     Status
1685
     *  @param      string  $note       Comment
1686
     *  @return     int                 Return integer <0 if KO, >0 if OK
1687
     */
1688
    public function cloture($user, $status, $note)
1689
    {
1690
        global $langs, $conf;
1691
        $hidedetails = 0;
1692
        $hidedesc = 0;
1693
        $hideref = 0;
1694
        $this->statut = $status;
1695
        $error = 0;
1696
        $now = dol_now();
1697
1698
        $this->db->begin();
1699
1700
        $sql = "UPDATE " . MAIN_DB_PREFIX . "supplier_proposal";
1701
        $sql .= " SET fk_statut = " . ((int) $status) . ", note_private = '" . $this->db->escape($note) . "', date_cloture='" . $this->db->idate($now) . "', fk_user_cloture=" . $user->id;
1702
        $sql .= " WHERE rowid = " . ((int) $this->id);
1703
1704
        $resql = $this->db->query($sql);
1705
        if ($resql) {
1706
            $modelpdf = $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED ? $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_CLOSED : (empty($this->model_pdf) ? '' : $this->model_pdf);
1707
            $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_REFUSED';
1708
1709
            if ($status == 2) {
1710
                $triggerName = 'PROPOSAL_SUPPLIER_CLOSE_SIGNED';
1711
                $modelpdf = $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL ? $conf->global->SUPPLIER_PROPOSAL_ADDON_PDF_ODT_TOBILL : (empty($this->model_pdf) ? '' : $this->model_pdf);
1712
1713
                if (getDolGlobalString('SUPPLIER_PROPOSAL_UPDATE_PRICE_ON_SUPPlIER_PROPOSAL')) {     // TODO This option was not tested correctly. Error if product ref does not exists
1714
                    $result = $this->updateOrCreatePriceFournisseur($user);
1715
                }
1716
            }
1717
            if ($status == 4) {
1718
                $triggerName = 'PROPOSAL_SUPPLIER_CLASSIFY_BILLED';
1719
            }
1720
1721
            if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1722
                // Define output language
1723
                $outputlangs = $langs;
1724
                if (getDolGlobalInt('MAIN_MULTILANGS')) {
1725
                    $outputlangs = new Translate("", $conf);
1726
                    $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
1727
                    $outputlangs->setDefaultLang($newlang);
1728
                }
1729
                //$ret=$object->fetch($id);    // Reload to get new records
1730
                $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1731
            }
1732
1733
            // Call trigger
1734
            $result = $this->call_trigger($triggerName, $user);
1735
            if ($result < 0) {
1736
                $error++;
1737
            }
1738
            // End call triggers
1739
1740
            if (!$error) {
1741
                $this->db->commit();
1742
                return 1;
1743
            } else {
1744
                $this->db->rollback();
1745
                return -1;
1746
            }
1747
        } else {
1748
            $this->error = $this->db->lasterror();
1749
            $this->errors[] = $this->db->lasterror();
1750
            $this->db->rollback();
1751
            return -1;
1752
        }
1753
    }
1754
1755
    /**
1756
     *  Add or update supplier price according to result of proposal
1757
     *
1758
     *  @param     User     $user       Object user
1759
     *  @return    int                  > 0 if OK
1760
     */
1761
    public function updateOrCreatePriceFournisseur($user)
1762
    {
1763
        global $conf;
1764
1765
        dol_syslog(get_class($this) . "::updateOrCreatePriceFournisseur", LOG_DEBUG);
1766
        foreach ($this->lines as $product) {
1767
            if ($product->subprice <= 0) {
1768
                continue;
1769
            }
1770
            $productsupplier = new ProductFournisseur($this->db);
1771
1772
            $multicurrency_tx = 1;
1773
            $fk_multicurrency = 0;
1774
1775
            if (empty($this->thirdparty)) {
1776
                $this->fetch_thirdparty();
1777
            }
1778
1779
            $ref_fourn = $product->ref_fourn;
1780
            if (empty($ref_fourn)) {
1781
                $ref_fourn = $product->ref_supplier;
1782
            }
1783
            if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1784
                list($fk_multicurrency, $multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $product->multicurrency_code);
1785
            }
1786
            $productsupplier->id = $product->fk_product;
1787
1788
            $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, '', '', '');
1789
        }
1790
1791
        return 1;
1792
    }
1793
1794
    /**
1795
     *  Update ProductFournisseur
1796
     *
1797
     *  @param      int     $idProductFournPrice    id of llx_product_fournisseur_price
1798
     *  @param      Product $product                contain information to update
1799
     *  @param      User    $user                   Object user
1800
     *  @return     int                             Return integer <0 if KO, >0 if OK
1801
     */
1802
    public function updatePriceFournisseur($idProductFournPrice, $product, $user)
1803
    {
1804
        $price = price2num($product->subprice * $product->qty, 'MU');
1805
        $unitPrice = price2num($product->subprice, 'MU');
1806
1807
        $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);
1808
1809
        $resql = $this->db->query($sql);
1810
        if (!$resql) {
1811
            $this->error = $this->db->error();
1812
            $this->db->rollback();
1813
            return -1;
1814
        }
1815
        return 1;
1816
    }
1817
1818
    /**
1819
     *  Create ProductFournisseur
1820
     *
1821
     *  @param      Product     $product    Object Product
1822
     *  @param      User        $user       Object user
1823
     *  @return     int                     Return integer <0 if KO, >0 if OK
1824
     */
1825
    public function createPriceFournisseur($product, $user)
1826
    {
1827
        global $conf;
1828
1829
        $price = price2num($product->subprice * $product->qty, 'MU');
1830
        $qty = price2num($product->qty);
1831
        $unitPrice = price2num($product->subprice, 'MU');
1832
1833
        $now = dol_now();
1834
1835
        $values = array(
1836
            "'" . $this->db->idate($now) . "'",
1837
            $product->fk_product,
1838
            $this->thirdparty->id,
1839
            "'" . $product->ref_fourn . "'",
1840
            $price,
1841
            $qty,
1842
            $unitPrice,
1843
            $product->tva_tx,
1844
            $user->id
1845
        );
1846
        if (isModEnabled("multicurrency")) {
1847
            if (!empty($product->multicurrency_code)) {
1848
                include_once DOL_DOCUMENT_ROOT . '/multicurrency/class/multicurrency.class.php';
1849
                $multicurrency = new MultiCurrency($this->db); //need to fetch because empty fk_multicurrency and rate
1850
                $multicurrency->fetch(0, $product->multicurrency_code);
1851
                if (!empty($multicurrency->id)) {
1852
                    $values[] = $multicurrency->id;
1853
                    $values[] = "'" . $product->multicurrency_code . "'";
1854
                    $values[] = $product->multicurrency_subprice;
1855
                    $values[] = $product->multicurrency_total_ht;
1856
                    $values[] = $multicurrency->rate->rate;
1857
                } else {
1858
                    for ($i = 0; $i < 5; $i++) {
1859
                        $values[] = 'NULL';
1860
                    }
1861
                }
1862
            }
1863
        }
1864
1865
        $sql = 'INSERT INTO ' . MAIN_DB_PREFIX . 'product_fournisseur_price ';
1866
        $sql .= '(datec, fk_product, fk_soc, ref_fourn, price, quantity, unitprice, tva_tx, fk_user';
1867
        if (isModEnabled("multicurrency") && !empty($product->multicurrency_code)) {
1868
            $sql .= ',fk_multicurrency, multicurrency_code, multicurrency_unitprice, multicurrency_price, multicurrency_tx';
1869
        }
1870
        $sql .= ')  VALUES (' . implode(',', $values) . ')';
1871
1872
        $resql = $this->db->query($sql);
1873
        if (!$resql) {
1874
            $this->error = $this->db->error();
1875
            $this->db->rollback();
1876
            return -1;
1877
        }
1878
        return 1;
1879
    }
1880
1881
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1882
    /**
1883
     *  Set draft status
1884
     *
1885
     *  @param      User    $user       Object user that modify
1886
     *  @return     int                 Return integer <0 if KO, >0 if OK
1887
     */
1888
    public function setDraft($user)
1889
    {
1890
		// phpcs:enable
1891
        global $conf, $langs;
1892
1893
        $error = 0;
1894
1895
        if ($this->statut == self::STATUS_DRAFT) {
1896
            dol_syslog(get_class($this) . "::setDraft already draft status", LOG_WARNING);
1897
            return 0;
1898
        }
1899
1900
        $sql = "UPDATE " . MAIN_DB_PREFIX . "supplier_proposal";
1901
        $sql .= " SET fk_statut = " . self::STATUS_DRAFT;
1902
        $sql .= " WHERE rowid = " . ((int) $this->id);
1903
1904
        if ($this->db->query($sql)) {
1905
            if (!$error) {
1906
                $this->oldcopy = clone $this;
1907
            }
1908
1909
            if (!$error) {
1910
                // Call trigger
1911
                $result = $this->call_trigger('PROPOSAL_SUPPLIER_UNVALIDATE', $user);
1912
                if ($result < 0) {
1913
                    $error++;
1914
                }
1915
            }
1916
1917
            if (!$error) {
1918
                $this->status = self::STATUS_DRAFT;
1919
                $this->statut = self::STATUS_DRAFT; // deprecated
1920
                $this->db->commit();
1921
                return 1;
1922
            } else {
1923
                $this->db->rollback();
1924
                return -1;
1925
            }
1926
        } else {
1927
            return -1;
1928
        }
1929
    }
1930
1931
1932
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1933
    /**
1934
     *    Return list of askprice (eventually filtered on user) into an array
1935
     *
1936
     *    @param    int     $shortlist          0=Return array[id]=ref, 1=Return array[](id=>id,ref=>ref,name=>name)
1937
     *    @param    int     $draft              0=not draft, 1=draft
1938
     *    @param    int     $notcurrentuser     0=all user, 1=not current user
1939
     *    @param    int     $socid              Id third party
1940
     *    @param    int     $limit              For pagination
1941
     *    @param    int     $offset             For pagination
1942
     *    @param    string  $sortfield          Sort criteria
1943
     *    @param    string  $sortorder          Sort order
1944
     *    @return   array|int                           -1 if KO, array with result if OK
1945
     */
1946
    public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datec', $sortorder = 'DESC')
1947
    {
1948
		// phpcs:enable
1949
        global $user;
1950
1951
        $ga = array();
1952
1953
        $search_sale = 0;
1954
        if (!$user->hasRight('societe', 'client', 'voir')) {
1955
            $search_sale = $user->id;
1956
        }
1957
1958
        $sql = "SELECT s.rowid, s.nom as name, s.client,";
1959
        $sql .= " p.rowid as supplier_proposalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
1960
        $sql .= " p.datep as dp, p.fin_validite as datelimite";
1961
        $sql .= " FROM " . MAIN_DB_PREFIX . "societe as s, " . MAIN_DB_PREFIX . "supplier_proposal as p, " . MAIN_DB_PREFIX . "c_propalst as c";
1962
        $sql .= " WHERE p.entity IN (" . getEntity('supplier_proposal') . ")";
1963
        $sql .= " AND p.fk_soc = s.rowid";
1964
        $sql .= " AND p.fk_statut = c.id";
1965
        if ($socid) {
1966
            $sql .= " AND s.rowid = " . ((int) $socid);
1967
        }
1968
        if ($draft) {
1969
            $sql .= " AND p.fk_statut = 0";
1970
        }
1971
        if ($notcurrentuser > 0) {
1972
            $sql .= " AND p.fk_user_author <> " . ((int) $user->id);
1973
        }
1974
        // Search on sale representative
1975
        if ($search_sale && $search_sale != '-1') {
1976
            if ($search_sale == -2) {
1977
                $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM " . MAIN_DB_PREFIX . "societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
1978
            } elseif ($search_sale > 0) {
1979
                $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) . ")";
1980
            }
1981
        }
1982
        $sql .= $this->db->order($sortfield, $sortorder);
1983
        $sql .= $this->db->plimit($limit, $offset);
1984
1985
        $result = $this->db->query($sql);
1986
        if ($result) {
1987
            $num = $this->db->num_rows($result);
1988
            if ($num) {
1989
                $i = 0;
1990
                while ($i < $num) {
1991
                    $obj = $this->db->fetch_object($result);
1992
1993
                    if ($shortlist == 1) {
1994
                        $ga[$obj->supplier_proposalid] = $obj->ref;
1995
                    } elseif ($shortlist == 2) {
1996
                        $ga[$obj->supplier_proposalid] = $obj->ref . ' (' . $obj->name . ')';
1997
                    } else {
1998
                        $ga[$i]['id'] = $obj->supplier_proposalid;
1999
                        $ga[$i]['ref']  = $obj->ref;
2000
                        $ga[$i]['name'] = $obj->name;
2001
                    }
2002
2003
                    $i++;
2004
                }
2005
            }
2006
            return $ga;
2007
        } else {
2008
            dol_print_error($this->db);
2009
            return -1;
2010
        }
2011
    }
2012
2013
    /**
2014
     *  Delete askprice
2015
     *
2016
     *  @param  User    $user           Object user that delete
2017
     *  @param  int     $notrigger      1=Does not execute triggers, 0= execute triggers
2018
     *  @return int                     1 if ok, otherwise if error
2019
     */
2020
    public function delete($user, $notrigger = 0)
2021
    {
2022
        global $conf, $langs;
2023
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
2024
2025
        $error = 0;
2026
2027
        $this->db->begin();
2028
2029
        if (!$notrigger) {
2030
            // Call trigger
2031
            $result = $this->call_trigger('PROPOSAL_SUPPLIER_DELETE', $user);
2032
            if ($result < 0) {
2033
                $error++;
2034
            }
2035
            // End call triggers
2036
        }
2037
2038
        if (!$error) {
2039
            $main = MAIN_DB_PREFIX . 'supplier_proposaldet';
2040
            $ef = $main . "_extrafields";
2041
            $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_supplier_proposal = " . ((int) $this->id) . ")";
2042
            $sql = "DELETE FROM " . MAIN_DB_PREFIX . "supplier_proposaldet WHERE fk_supplier_proposal = " . ((int) $this->id);
2043
            if ($this->db->query($sql)) {
2044
                $sql = "DELETE FROM " . MAIN_DB_PREFIX . "supplier_proposal WHERE rowid = " . ((int) $this->id);
2045
                if ($this->db->query($sqlef) && $this->db->query($sql)) {
2046
                    // Delete linked object
2047
                    $res = $this->deleteObjectLinked();
2048
                    if ($res < 0) {
2049
                        $error++;
2050
                    }
2051
2052
                    if (!$error) {
2053
                        // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2054
                        $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
2055
                        $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
2056
2057
                        // We remove directory
2058
                        $ref = dol_sanitizeFileName($this->ref);
2059
                        if ($conf->supplier_proposal->dir_output && !empty($this->ref)) {
2060
                            $dir = $conf->supplier_proposal->dir_output . "/" . $ref;
2061
                            $file = $dir . "/" . $ref . ".pdf";
2062
                            if (file_exists($file)) {
2063
                                dol_delete_preview($this);
2064
2065
                                if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2066
                                    $this->error = 'ErrorFailToDeleteFile';
2067
                                    $this->errors = array('ErrorFailToDeleteFile');
2068
                                    $this->db->rollback();
2069
                                    return 0;
2070
                                }
2071
                            }
2072
                            if (file_exists($dir)) {
2073
                                $res = @dol_delete_dir_recursive($dir);
2074
                                if (!$res) {
2075
                                    $this->error = 'ErrorFailToDeleteDir';
2076
                                    $this->errors = array('ErrorFailToDeleteDir');
2077
                                    $this->db->rollback();
2078
                                    return 0;
2079
                                }
2080
                            }
2081
                        }
2082
                    }
2083
2084
                    // Removed extrafields
2085
                    if (!$error) {
2086
                        $result = $this->deleteExtraFields();
2087
                        if ($result < 0) {
2088
                            $error++;
2089
                            $errorflag = -4;
2090
                            dol_syslog(get_class($this) . "::delete erreur " . $errorflag . " " . $this->error, LOG_ERR);
2091
                        }
2092
                    }
2093
2094
                    if (!$error) {
2095
                        dol_syslog(get_class($this) . "::delete " . $this->id . " by " . $user->id, LOG_DEBUG);
2096
                        $this->db->commit();
2097
                        return 1;
2098
                    } else {
2099
                        $this->error = $this->db->lasterror();
2100
                        $this->db->rollback();
2101
                        return 0;
2102
                    }
2103
                } else {
2104
                    $this->error = $this->db->lasterror();
2105
                    $this->db->rollback();
2106
                    return -3;
2107
                }
2108
            } else {
2109
                $this->error = $this->db->lasterror();
2110
                $this->db->rollback();
2111
                return -2;
2112
            }
2113
        } else {
2114
            $this->db->rollback();
2115
            return -1;
2116
        }
2117
    }
2118
2119
    /**
2120
     *  Object SupplierProposal Information
2121
     *
2122
     *  @param  int     $id     Proposal id
2123
     *  @return void
2124
     */
2125
    public function info($id)
2126
    {
2127
        $sql = "SELECT c.rowid, ";
2128
        $sql .= " c.datec as date_creation, c.date_valid as date_validation, c.date_cloture as date_closure,";
2129
        $sql .= " c.fk_user_author, c.fk_user_valid, c.fk_user_cloture";
2130
        $sql .= " FROM " . MAIN_DB_PREFIX . "supplier_proposal as c";
2131
        $sql .= " WHERE c.rowid = " . ((int) $id);
2132
2133
        $result = $this->db->query($sql);
2134
2135
        if ($result) {
2136
            if ($this->db->num_rows($result)) {
2137
                $obj = $this->db->fetch_object($result);
2138
2139
                $this->id                = $obj->rowid;
2140
2141
                $this->date_creation     = $this->db->jdate($obj->date_creation);
2142
                $this->date_validation   = $this->db->jdate($obj->date_validation);
2143
                $this->date_cloture      = $this->db->jdate($obj->date_closure);
2144
2145
                $this->user_creation_id = $obj->fk_user_author;
2146
                $this->user_validation_id = $obj->fk_user_valid;
2147
                $this->user_closing_id = $obj->fk_user_cloture;
2148
            }
2149
            $this->db->free($result);
2150
        } else {
2151
            dol_print_error($this->db);
2152
        }
2153
    }
2154
2155
2156
    /**
2157
     *      Return label of status of proposal (draft, validated, ...)
2158
     *
2159
     *      @param      int         $mode        0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto
2160
     *      @return     string      Label
2161
     */
2162
    public function getLibStatut($mode = 0)
2163
    {
2164
        return $this->LibStatut((isset($this->statut) ? $this->statut : $this->status), $mode);
2165
    }
2166
2167
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2168
    /**
2169
     *  Return label of a status (draft, validated, ...)
2170
     *
2171
     *  @param      int         $status     Id status
2172
     *  @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
2173
     *  @return     string      Label
2174
     */
2175
    public function LibStatut($status, $mode = 1)
2176
    {
2177
		// phpcs:enable
2178
2179
        // Init/load array of translation of status
2180
        if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
2181
            global $langs;
2182
            $langs->load("supplier_proposal");
2183
            $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraft");
2184
            $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidated");
2185
            $this->labelStatus[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSigned");
2186
            $this->labelStatus[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSigned");
2187
            $this->labelStatus[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosed");
2188
            $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv("SupplierProposalStatusDraftShort");
2189
            $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv("SupplierProposalStatusValidatedShort");
2190
            $this->labelStatusShort[self::STATUS_SIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusSignedShort");
2191
            $this->labelStatusShort[self::STATUS_NOTSIGNED] = $langs->transnoentitiesnoconv("SupplierProposalStatusNotSignedShort");
2192
            $this->labelStatusShort[self::STATUS_CLOSE] = $langs->transnoentitiesnoconv("SupplierProposalStatusClosedShort");
2193
        }
2194
2195
        $statusnew = '';
2196
        if ($status == self::STATUS_DRAFT) {
2197
            $statusnew = 'status0';
2198
        } elseif ($status == self::STATUS_VALIDATED) {
2199
            $statusnew = 'status1';
2200
        } elseif ($status == self::STATUS_SIGNED) {
2201
            $statusnew = 'status4';
2202
        } elseif ($status == self::STATUS_NOTSIGNED) {
2203
            $statusnew = 'status9';
2204
        } elseif ($status == self::STATUS_CLOSE) {
2205
            $statusnew = 'status6';
2206
        }
2207
2208
        return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusnew, $mode);
2209
    }
2210
2211
2212
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2213
    /**
2214
     *      Load indicators for dashboard (this->nbtodo and this->nbtodolate)
2215
     *
2216
     *      @param          User    $user   Object user
2217
     *      @param          string  $mode   "opened" for askprice to close, "signed" for proposal to invoice
2218
     *      @return         WorkboardResponse|int   Return integer <0 if KO, WorkboardResponse if OK
2219
     */
2220
    public function load_board($user, $mode)
2221
    {
2222
		// phpcs:enable
2223
        global $conf, $user, $langs;
2224
2225
        $now = dol_now();
2226
2227
        $clause = " WHERE";
2228
2229
        $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.date_cloture as datefin";
2230
        $sql .= " FROM " . MAIN_DB_PREFIX . "supplier_proposal as p";
2231
        if (!$user->hasRight('societe', 'client', 'voir')) {
2232
            $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
2233
            $sql .= " WHERE sc.fk_user = " . ((int) $user->id);
2234
            $clause = " AND";
2235
        }
2236
        $sql .= $clause . " p.entity IN (" . getEntity('supplier_proposal') . ")";
2237
        if ($mode == 'opened') {
2238
            $sql .= " AND p.fk_statut = 1";
2239
        }
2240
        if ($mode == 'signed') {
2241
            $sql .= " AND p.fk_statut = 2";
2242
        }
2243
        if ($user->socid) {
2244
            $sql .= " AND p.fk_soc = " . ((int) $user->socid);
2245
        }
2246
2247
        $resql = $this->db->query($sql);
2248
        if ($resql) {
2249
            $label = $labelShort = '';
2250
            $status = '';
2251
            if ($mode == 'opened') {
2252
                $delay_warning = !empty($conf->supplier_proposal->cloture->warning_delay) ? $conf->supplier_proposal->cloture->warning_delay : 0;
2253
                $status = self::STATUS_VALIDATED;
2254
                $label = $langs->trans("SupplierProposalsToClose");
2255
                $labelShort = $langs->trans("ToAcceptRefuse");
2256
            }
2257
            if ($mode == 'signed') {
2258
                $delay_warning = !empty($conf->supplier_proposal->facturation->warning_delay) ? $conf->supplier_proposal->facturation->warning_delay : 0;
2259
                $status = self::STATUS_SIGNED;
2260
                $label = $langs->trans("SupplierProposalsToProcess"); // May be billed or ordered
2261
                $labelShort = $langs->trans("ToClose");
2262
            }
2263
2264
            $response = new WorkboardResponse();
2265
            $response->warning_delay = $delay_warning / 60 / 60 / 24;
2266
            $response->label = $label;
2267
            $response->labelShort = $labelShort;
2268
            $response->url = constant('BASE_URL') . '/supplier_proposal/list.php?search_status=' . $status;
2269
            $response->img = img_object('', "propal");
2270
2271
            // This assignment in condition is not a bug. It allows walking the results.
2272
            while ($obj = $this->db->fetch_object($resql)) {
2273
                $response->nbtodo++;
2274
                if ($mode == 'opened') {
2275
                    $datelimit = $this->db->jdate($obj->datefin);
2276
                    if ($datelimit < ($now - $delay_warning)) {
2277
                        $response->nbtodolate++;
2278
                    }
2279
                }
2280
                // TODO Definir regle des propales a facturer en retard
2281
                // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
2282
            }
2283
            return $response;
2284
        } else {
2285
            $this->error = $this->db->lasterror();
2286
            return -1;
2287
        }
2288
    }
2289
2290
2291
    /**
2292
     *  Initialise an instance with random values.
2293
     *  Used to build previews or test instances.
2294
     *  id must be 0 if object instance is a specimen.
2295
     *
2296
     *  @return int
2297
     */
2298
    public function initAsSpecimen()
2299
    {
2300
        global $user, $langs, $conf;
2301
2302
        // Load array of products prodids
2303
        $num_prods = 0;
2304
        $prodids = array();
2305
        $sql = "SELECT rowid";
2306
        $sql .= " FROM " . MAIN_DB_PREFIX . "product";
2307
        $sql .= " WHERE entity IN (" . getEntity('product') . ")";
2308
        $sql .= $this->db->plimit(100);
2309
2310
        $resql = $this->db->query($sql);
2311
        if ($resql) {
2312
            $num_prods = $this->db->num_rows($resql);
2313
            $i = 0;
2314
            while ($i < $num_prods) {
2315
                $i++;
2316
                $row = $this->db->fetch_row($resql);
2317
                $prodids[$i] = $row[0];
2318
            }
2319
        }
2320
2321
        // Initialise parameters
2322
        $this->id = 0;
2323
        $this->ref = 'SPECIMEN';
2324
        $this->specimen = 1;
2325
        $this->socid = 1;
2326
        $this->date = time();
2327
        $this->cond_reglement_id   = 1;
2328
        $this->cond_reglement_code = 'RECEP';
2329
        $this->mode_reglement_id   = 7;
2330
        $this->mode_reglement_code = 'CHQ';
2331
        $this->note_public = 'This is a comment (public)';
2332
        $this->note_private = 'This is a comment (private)';
2333
        // Lines
2334
        $nbp = 5;
2335
        $xnbp = 0;
2336
        while ($xnbp < $nbp) {
2337
            $line = new SupplierProposalLine($this->db);
2338
            $line->desc = $langs->trans("Description") . " " . $xnbp;
2339
            $line->qty = 1;
2340
            $line->subprice = 100;
2341
            $line->tva_tx = 19.6;
2342
            $line->localtax1_tx = 0;
2343
            $line->localtax2_tx = 0;
2344
            if ($xnbp == 2) {
2345
                $line->total_ht = 50;
2346
                $line->total_ttc = 59.8;
2347
                $line->total_tva = 9.8;
2348
                $line->remise_percent = 50;
2349
            } else {
2350
                $line->total_ht = 100;
2351
                $line->total_ttc = 119.6;
2352
                $line->total_tva = 19.6;
2353
                $line->remise_percent = 0;
2354
            }
2355
2356
            if ($num_prods > 0) {
2357
                $prodid = mt_rand(1, $num_prods);
2358
                $line->fk_product = $prodids[$prodid];
2359
            }
2360
2361
            $this->lines[$xnbp] = $line;
2362
2363
            $this->total_ht       += $line->total_ht;
2364
            $this->total_tva      += $line->total_tva;
2365
            $this->total_ttc      += $line->total_ttc;
2366
2367
            $xnbp++;
2368
        }
2369
2370
        return 1;
2371
    }
2372
2373
    /**
2374
     *      Load indicator this->nb of global stats widget
2375
     *
2376
     *      @return     int         Return integer <0 if ko, >0 if ok
2377
     */
2378
    public function loadStateBoard()
2379
    {
2380
        global $conf, $user;
2381
2382
        $this->nb = array();
2383
        $clause = "WHERE";
2384
2385
        $sql = "SELECT count(p.rowid) as nb";
2386
        $sql .= " FROM " . MAIN_DB_PREFIX . "supplier_proposal as p";
2387
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe as s ON p.fk_soc = s.rowid";
2388
        if (!$user->hasRight('societe', 'client', 'voir')) {
2389
            $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2390
            $sql .= " WHERE sc.fk_user = " . ((int) $user->id);
2391
            $clause = "AND";
2392
        }
2393
        $sql .= " " . $clause . " p.entity IN (" . getEntity('supplier_proposal') . ")";
2394
2395
        $resql = $this->db->query($sql);
2396
        if ($resql) {
2397
            // This assignment in condition is not a bug. It allows walking the results.
2398
            while ($obj = $this->db->fetch_object($resql)) {
2399
                $this->nb["supplier_proposals"] = $obj->nb;
2400
            }
2401
            $this->db->free($resql);
2402
            return 1;
2403
        } else {
2404
            dol_print_error($this->db);
2405
            $this->error = $this->db->lasterror();
2406
            return -1;
2407
        }
2408
    }
2409
2410
2411
    /**
2412
     *  Returns the reference to the following non used Proposal used depending on the active numbering module
2413
     *  defined into SUPPLIER_PROPOSAL_ADDON
2414
     *
2415
     *  @param  Societe     $soc    Object thirdparty
2416
     *  @return string              Reference libre pour la propale
2417
     */
2418
    public function getNextNumRef($soc)
2419
    {
2420
        global $conf, $db, $langs;
2421
        $langs->load("supplier_proposal");
2422
2423
        if (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON')) {
2424
            $mybool = false;
2425
2426
            $file = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON') . ".php";
2427
            $classname = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON');
2428
2429
            // Include file with class
2430
            $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2431
            foreach ($dirmodels as $reldir) {
2432
                $dir = dol_buildpath($reldir . "core/modules/supplier_proposal/");
2433
2434
                // Load file with numbering class (if found)
2435
                $mybool = ((bool) @include_once $dir . $file) || $mybool;
2436
            }
2437
2438
            if (!$mybool) {
2439
                dol_print_error(null, "Failed to include file " . $file);
2440
                return '';
2441
            }
2442
2443
            $obj = new $classname();
2444
            $numref = "";
2445
            $numref = $obj->getNextValue($soc, $this);
2446
2447
            if ($numref != "") {
2448
                return $numref;
2449
            } else {
2450
                $this->error = $obj->error;
2451
                return "";
2452
            }
2453
        } else {
2454
            $langs->load("errors");
2455
            print $langs->trans("Error") . " " . $langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("SupplierProposal"));
2456
            return "";
2457
        }
2458
    }
2459
2460
    /**
2461
     * getTooltipContentArray
2462
     *
2463
     * @param array $params ex option, infologin
2464
     * @since v18
2465
     * @return array
2466
     */
2467
    public function getTooltipContentArray($params)
2468
    {
2469
        global $conf, $langs, $menumanager;
2470
2471
        $langs->load('supplier_proposal');
2472
2473
        if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2474
            return ['optimize' => $langs->trans("ShowSupplierProposal")];
2475
        }
2476
2477
        $option = $params['option'] ?? '';
2478
        $datas = [];
2479
2480
        $datas['picto'] = img_picto('', $this->picto) . ' <u class="paddingrightonly">' . $langs->trans("SupplierProposal") . '</u>';
2481
        if (isset($this->status)) {
2482
            $datas['picto'] .= ' ' . $this->getLibStatut(5);
2483
        }
2484
        if (!empty($this->ref)) {
2485
            $datas['ref'] = '<br><b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
2486
        }
2487
        if (!empty($this->ref_fourn)) {
2488
            $datas['ref_supplier'] = '<br><b>' . $langs->trans('RefSupplier') . ':</b> ' . $this->ref_fourn;
2489
        }
2490
        if (!empty($this->total_ht)) {
2491
            $datas['amount_ht'] = '<br><b>' . $langs->trans('AmountHT') . ':</b> ' . price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2492
        }
2493
        if (!empty($this->total_tva)) {
2494
            $datas['amount_vat'] = '<br><b>' . $langs->trans('VAT') . ':</b> ' . price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2495
        }
2496
        if (!empty($this->total_ttc)) {
2497
            $datas['amount_ttc'] = '<br><b>' . $langs->trans('AmountTTC') . ':</b> ' . price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2498
        }
2499
2500
        return $datas;
2501
    }
2502
2503
    /**
2504
     *  Return clicable link of object (with eventually picto)
2505
     *
2506
     *  @param      int     $withpicto                  Add picto into link
2507
     *  @param      string  $option                     Where point the link ('compta', 'expedition', 'document', ...)
2508
     *  @param      string  $get_params                 Parameters added to url
2509
     *  @param      int     $notooltip                  1=Disable tooltip
2510
     *  @param      int     $save_lastsearch_value      -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
2511
     *  @param      int     $addlinktonotes             Add link to show notes
2512
     *  @return     string                              String with URL
2513
     */
2514
    public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2515
    {
2516
        global $langs, $conf, $user, $hookmanager;
2517
2518
        if (!empty($conf->dol_no_mouse_hover)) {
2519
            $notooltip = 1; // Force disable tooltips
2520
        }
2521
2522
        $url = '';
2523
        $result = '';
2524
        $params = [
2525
            'id' => $this->id,
2526
            'objecttype' => $this->element,
2527
            'option' => $option,
2528
        ];
2529
        $classfortooltip = 'classfortooltip';
2530
        $dataparams = '';
2531
        if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2532
            $classfortooltip = 'classforajaxtooltip';
2533
            $dataparams = ' data-params="' . dol_escape_htmltag(json_encode($params)) . '"';
2534
            $label = '';
2535
        } else {
2536
            $label = implode($this->getTooltipContentArray($params));
2537
        }
2538
2539
        if ($option == '') {
2540
            $url = constant('BASE_URL') . '/supplier_proposal/card.php?id=' . $this->id . $get_params;
2541
        }
2542
        if ($option == 'document') {
2543
            $url = constant('BASE_URL') . '/supplier_proposal/document.php?id=' . $this->id . $get_params;
2544
        }
2545
2546
        if ($option !== 'nolink') {
2547
            // Add param to save lastsearch_values or not
2548
            $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2549
            if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2550
                $add_save_lastsearch_values = 1;
2551
            }
2552
            if ($add_save_lastsearch_values) {
2553
                $url .= '&save_lastsearch_values=1';
2554
            }
2555
        }
2556
2557
        $linkclose = '';
2558
        if (empty($notooltip) && $user->hasRight('propal', 'lire')) {
2559
            if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2560
                $label = $langs->trans("ShowSupplierProposal");
2561
                $linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
2562
            }
2563
            $linkclose .= ($label ? ' title="' . dol_escape_htmltag($label, 1) . '"' : ' title="tocomplete"');
2564
            $linkclose .= $dataparams . ' class="' . $classfortooltip . '"';
2565
        }
2566
2567
        $linkstart = '<a href="' . $url . '"';
2568
        $linkstart .= $linkclose . '>';
2569
        $linkend = '</a>';
2570
2571
        $result .= $linkstart;
2572
        if ($withpicto) {
2573
            $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
2574
        }
2575
        if ($withpicto != 2) {
2576
            $result .= $this->ref;
2577
        }
2578
        $result .= $linkend;
2579
2580
        if ($addlinktonotes) {
2581
            $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2582
            if ($txttoshow) {
2583
                $notetoshow = $langs->trans("ViewPrivateNote") . ':<br>' . dol_string_nohtmltag($txttoshow, 1);
2584
                $result .= ' <span class="note inline-block">';
2585
                $result .= '<a href="' . constant('BASE_URL') . '/supplier_proposal/note.php?id=' . $this->id . '" class="classfortooltip" title="' . dol_escape_htmltag($notetoshow) . '">';
2586
                $result .= img_picto('', 'note');
2587
                $result .= '</a>';
2588
                //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
2589
                //$result.='</a>';
2590
                $result .= '</span>';
2591
            }
2592
        }
2593
        global $action;
2594
        $hookmanager->initHooks(array($this->element . 'dao'));
2595
        $parameters = array('id' => $this->id, 'getnomurl' => &$result);
2596
        $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2597
        if ($reshook > 0) {
2598
            $result = $hookmanager->resPrint;
2599
        } else {
2600
            $result .= $hookmanager->resPrint;
2601
        }
2602
        return $result;
2603
    }
2604
2605
    /**
2606
     *  Retrieve an array of supplier proposal lines
2607
     *
2608
     *  @return int     >0 if OK, <0 if KO
2609
     */
2610
    public function getLinesArray()
2611
    {
2612
        // For other object, here we call fetch_lines. But fetch_lines does not exists on supplier proposal
2613
2614
        $sql = 'SELECT pt.rowid, pt.label as custom_label, pt.description, pt.fk_product, pt.fk_remise_except,';
2615
        $sql .= ' pt.qty, pt.tva_tx, pt.vat_src_code, pt.remise_percent, pt.subprice, pt.info_bits,';
2616
        $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,';
2617
        $sql .= ' pt.product_type, pt.rang, pt.fk_parent_line,';
2618
        $sql .= ' p.label as product_label, p.ref, p.fk_product_type, p.rowid as prodid,';
2619
        $sql .= ' p.description as product_desc, pt.ref_fourn as ref_supplier,';
2620
        $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';
2621
        $sql .= ' FROM ' . MAIN_DB_PREFIX . 'supplier_proposaldet as pt';
2622
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'product as p ON pt.fk_product=p.rowid';
2623
        $sql .= ' WHERE pt.fk_supplier_proposal = ' . ((int) $this->id);
2624
        $sql .= ' ORDER BY pt.rang ASC, pt.rowid';
2625
2626
        dol_syslog(get_class($this) . '::getLinesArray', LOG_DEBUG);
2627
        $resql = $this->db->query($sql);
2628
        if ($resql) {
2629
            $num = $this->db->num_rows($resql);
2630
            $i = 0;
2631
2632
            while ($i < $num) {
2633
                $obj = $this->db->fetch_object($resql);
2634
2635
                $this->lines[$i] = new SupplierProposalLine($this->db);
2636
                $this->lines[$i]->id = $obj->rowid; // for backward compatibility
2637
                $this->lines[$i]->rowid             = $obj->rowid;
2638
                $this->lines[$i]->label             = $obj->custom_label;
2639
                $this->lines[$i]->description = $obj->description;
2640
                $this->lines[$i]->fk_product = $obj->fk_product;
2641
                $this->lines[$i]->ref = $obj->ref;
2642
                $this->lines[$i]->product_label = $obj->product_label;
2643
                $this->lines[$i]->product_desc      = $obj->product_desc;
2644
                $this->lines[$i]->fk_product_type = $obj->fk_product_type; // deprecated
2645
                $this->lines[$i]->product_type      = $obj->product_type;
2646
                $this->lines[$i]->qty = $obj->qty;
2647
                $this->lines[$i]->subprice = $obj->subprice;
2648
                $this->lines[$i]->fk_remise_except = $obj->fk_remise_except;
2649
                $this->lines[$i]->remise_percent = $obj->remise_percent;
2650
                $this->lines[$i]->tva_tx = $obj->tva_tx;
2651
                $this->lines[$i]->vat_src_code = $obj->vat_src_code;
2652
                $this->lines[$i]->info_bits         = $obj->info_bits;
2653
                $this->lines[$i]->total_ht = $obj->total_ht;
2654
                $this->lines[$i]->total_tva         = $obj->total_tva;
2655
                $this->lines[$i]->total_ttc         = $obj->total_ttc;
2656
                $this->lines[$i]->fk_fournprice = $obj->fk_fournprice;
2657
                $marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->lines[$i]->fk_fournprice, $obj->pa_ht);
2658
                $this->lines[$i]->pa_ht = $marginInfos[0];
2659
                $this->lines[$i]->marge_tx = $marginInfos[1];
2660
                $this->lines[$i]->marque_tx = $marginInfos[2];
2661
                $this->lines[$i]->fk_parent_line = $obj->fk_parent_line;
2662
                $this->lines[$i]->special_code = $obj->special_code;
2663
                $this->lines[$i]->rang = $obj->rang;
2664
2665
                $this->lines[$i]->ref_fourn = $obj->ref_supplier; // deprecated
2666
                $this->lines[$i]->ref_supplier = $obj->ref_supplier;
2667
2668
                // Multicurrency
2669
                $this->lines[$i]->fk_multicurrency = $obj->fk_multicurrency;
2670
                $this->lines[$i]->multicurrency_code = $obj->multicurrency_code;
2671
                $this->lines[$i]->multicurrency_subprice    = $obj->multicurrency_subprice;
2672
                $this->lines[$i]->multicurrency_total_ht    = $obj->multicurrency_total_ht;
2673
                $this->lines[$i]->multicurrency_total_tva   = $obj->multicurrency_total_tva;
2674
                $this->lines[$i]->multicurrency_total_ttc   = $obj->multicurrency_total_ttc;
2675
                $this->lines[$i]->fk_unit = $obj->fk_unit;
2676
2677
                $i++;
2678
            }
2679
            $this->db->free($resql);
2680
2681
            return 1;
2682
        } else {
2683
            $this->error = $this->db->error();
2684
            return -1;
2685
        }
2686
    }
2687
2688
    /**
2689
     *  Create a document onto disk according to template module.
2690
     *
2691
     *  @param      string      $modele         Force model to use ('' to not force)
2692
     *  @param      Translate   $outputlangs    Object langs to use for output
2693
     *  @param      int         $hidedetails    Hide details of lines
2694
     *  @param      int         $hidedesc       Hide description
2695
     *  @param      int         $hideref        Hide ref
2696
     *  @param   null|array  $moreparams     Array to provide more information
2697
     *  @return     int                         0 if KO, 1 if OK
2698
     */
2699
    public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2700
    {
2701
        global $conf, $langs;
2702
2703
        $langs->load("supplier_proposal");
2704
        $outputlangs->load("products");
2705
2706
        if (!dol_strlen($modele)) {
2707
            $modele = 'aurore';
2708
2709
            if ($this->model_pdf) {
2710
                $modele = $this->model_pdf;
2711
            } elseif (getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF')) {
2712
                $modele = getDolGlobalString('SUPPLIER_PROPOSAL_ADDON_PDF');
2713
            }
2714
        }
2715
2716
        $modelpath = "core/modules/supplier_proposal/doc/";
2717
2718
        return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2719
    }
2720
2721
2722
    /**
2723
     * Function used to replace a thirdparty id with another one.
2724
     *
2725
     * @param   DoliDB  $dbs        Database handler, because function is static we name it $dbs not $db to avoid breaking coding test
2726
     * @param   int     $origin_id  Old thirdparty id
2727
     * @param   int     $dest_id    New thirdparty id
2728
     * @return  bool
2729
     */
2730
    public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2731
    {
2732
        $tables = array(
2733
            'supplier_proposal'
2734
        );
2735
2736
        return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
2737
    }
2738
2739
    /**
2740
     * Function used to replace a product id with another one.
2741
     *
2742
     * @param DoliDB $db Database handler
2743
     * @param int $origin_id Old product id
2744
     * @param int $dest_id New product id
2745
     * @return bool
2746
     */
2747
    public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
2748
    {
2749
        $tables = array(
2750
            'supplier_proposaldet'
2751
        );
2752
2753
        return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
2754
    }
2755
2756
2757
    /**
2758
     *  Return clicable link of object (with eventually picto)
2759
     *
2760
     *  @param      string      $option                 Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link)
2761
     *  @param      array       $arraydata              Array of data
2762
     *  @return     string                              HTML Code for Kanban thumb.
2763
     */
2764
    public function getKanbanView($option = '', $arraydata = null)
2765
    {
2766
        global $langs;
2767
2768
        $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2769
2770
        $return = '<div class="box-flex-item box-flex-grow-zero">';
2771
        $return .= '<div class="info-box info-box-sm">';
2772
        $return .= '<span class="info-box-icon bg-infobox-action">';
2773
        $return .= img_picto('', $this->picto);
2774
        //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
2775
        $return .= '</span>';
2776
        $return .= '<div class="info-box-content">';
2777
        $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . (method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref) . '</span>';
2778
        if ($selected >= 0) {
2779
            $return .= '<input id="cb' . $this->id . '" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="' . $this->id . '"' . ($selected ? ' checked="checked"' : '') . '>';
2780
        }
2781
        if (property_exists($this, 'socid')) {
2782
            $return .= '<span class="info-box-ref"> | ' . $this->socid . '</span>';
2783
        }
2784
        if (property_exists($this, 'delivery_date')) {
2785
            $return .= '<br><span class="opacitymedium">' . $langs->trans("DateEnd") . '</span> : <span class="info-box-label">' . dol_print_date($this->delivery_date) . '</span>';
2786
        }
2787
        if (property_exists($this, 'total_ttc')) {
2788
            $return .= '<br><span class="opacitymedium" >' . $langs->trans("AmountHT") . ' : </span><span class="info-box-label amount">' . price($this->total_ttc) . '</span>';
2789
        }
2790
        if (method_exists($this, 'getLibStatut')) {
2791
            $return .= '<br><div class="info-box-status">' . $this->getLibStatut(3) . '</div>';
2792
        }
2793
        $return .= '</div>';
2794
        $return .= '</div>';
2795
        $return .= '</div>';
2796
        return $return;
2797
    }
2798
}
2799
2800
2801
/**
2802
 *  Class to manage supplier_proposal lines
2803
 */
2804
class SupplierProposalLine extends CommonObjectLine
2805
{
2806
    /**
2807
     * @var DoliDB Database handler.
2808
     */
2809
    public $db;
2810
2811
    /**
2812
     * @var string Error code (or message)
2813
     */
2814
    public $error = '';
2815
2816
    /**
2817
     * @var string ID to identify managed object
2818
     */
2819
    public $element = 'supplier_proposaldet';
2820
2821
    /**
2822
     * @var string Name of table without prefix where object is stored
2823
     */
2824
    public $table_element = 'supplier_proposaldet';
2825
2826
    /**
2827
     * @see CommonObjectLine
2828
     */
2829
    public $parent_element = 'supplier_proposal';
2830
2831
    /**
2832
     * @see CommonObjectLine
2833
     */
2834
    public $fk_parent_attribute = 'fk_supplier_proposal';
2835
2836
    public $oldline;
2837
2838
    /**
2839
     * @var int ID
2840
     */
2841
    public $id;
2842
2843
    /**
2844
     * @var int ID
2845
     */
2846
    public $fk_supplier_proposal;
2847
2848
    /**
2849
     * @var int ID
2850
     */
2851
    public $fk_parent_line;
2852
2853
    public $desc; // Description ligne
2854
2855
    /**
2856
     * @var int ID
2857
     */
2858
    public $fk_product; // Id produit predefini
2859
2860
    /**
2861
     * @deprecated
2862
     * @see $product_type
2863
     */
2864
    public $fk_product_type;
2865
    /**
2866
     * Product type
2867
     * @var int
2868
     * @see Product::TYPE_PRODUCT, Product::TYPE_SERVICE
2869
     */
2870
    public $product_type = Product::TYPE_PRODUCT;
2871
2872
    /**
2873
     * @var float Quantity
2874
     */
2875
    public $qty;
2876
    public $tva_tx;
2877
    public $vat_src_code;
2878
2879
    /**
2880
     * Unit price before taxes
2881
     * @var float
2882
     */
2883
    public $subprice;
2884
    public $remise_percent;
2885
2886
    /**
2887
     * @var int ID
2888
     */
2889
    public $fk_remise_except;
2890
2891
    public $rang = 0;
2892
2893
    /**
2894
     * @var int ID
2895
     */
2896
    public $fk_fournprice;
2897
2898
    public $pa_ht;
2899
    public $marge_tx;
2900
    public $marque_tx;
2901
2902
    /**
2903
     * @var int special code
2904
     */
2905
    public $special_code; // Tag for special lines (exclusive tags)
2906
    // 1: frais de port
2907
    // 2: ecotaxe
2908
    // 3: option line (when qty = 0)
2909
2910
    public $info_bits = 0; // Liste d'options cumulables:
2911
    // Bit 0:   0 si TVA normal - 1 if TVA NPR
2912
    // Bit 1:   0 ligne normal - 1 if fixed reduction
2913
2914
    public $total_ht; // Total HT de la ligne toute quantite et incluant la remise ligne
2915
    public $total_tva; // Total TVA de la ligne toute quantite et incluant la remise ligne
2916
    public $total_ttc; // Total TTC de la ligne toute quantite et incluant la remise ligne
2917
2918
    public $date_start;
2919
    public $date_end;
2920
2921
    // From llx_product
2922
    /**
2923
     * @deprecated
2924
     * @see $product_ref
2925
     */
2926
    public $ref;
2927
2928
    /**
2929
     * Product reference
2930
     * @var string
2931
     */
2932
    public $product_ref;
2933
2934
    /**
2935
     * @deprecated
2936
     * @see $product_label
2937
     */
2938
    public $libelle;
2939
2940
    /**
2941
     *  Product label
2942
     * @var string
2943
     */
2944
    public $product_label;
2945
2946
    /**
2947
     * Custom label
2948
     * @var string
2949
     */
2950
    public $label;
2951
2952
    /**
2953
     * Product description
2954
     * @var string
2955
     */
2956
    public $product_desc;
2957
2958
    public $localtax1_tx; // Local tax 1
2959
    public $localtax2_tx; // Local tax 2
2960
    public $localtax1_type; // Local tax 1 type
2961
    public $localtax2_type; // Local tax 2 type
2962
    public $total_localtax1; // Line total local tax 1
2963
    public $total_localtax2; // Line total local tax 2
2964
2965
    public $skip_update_total; // Skip update price total for special lines
2966
2967
    public $ref_fourn;
2968
    public $ref_supplier;
2969
2970
    // Multicurrency
2971
    /**
2972
     * @var int ID
2973
     */
2974
    public $fk_multicurrency;
2975
2976
    public $multicurrency_code;
2977
    public $multicurrency_subprice;
2978
    public $multicurrency_total_ht;
2979
    public $multicurrency_total_tva;
2980
    public $multicurrency_total_ttc;
2981
2982
    /**
2983
     *  Class line Constructor
2984
     *
2985
     *  @param  DoliDB  $db Database handler
2986
     */
2987
    public function __construct($db)
2988
    {
2989
        $this->db = $db;
2990
    }
2991
2992
    /**
2993
     *  Retrieve the propal line object
2994
     *
2995
     *  @param  int     $rowid      Propal line id
2996
     *  @return int                 Return integer <0 if KO, >0 if OK
2997
     */
2998
    public function fetch($rowid)
2999
    {
3000
        $sql = 'SELECT pd.rowid, pd.fk_supplier_proposal, pd.fk_parent_line, pd.fk_product, pd.label as custom_label, pd.description, pd.price, pd.qty, pd.tva_tx,';
3001
        $sql .= ' pd.date_start, pd.date_end,';
3002
        $sql .= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice,';
3003
        $sql .= ' pd.info_bits, pd.total_ht, pd.total_tva, pd.total_ttc, pd.fk_product_fournisseur_price as fk_fournprice, pd.buy_price_ht as pa_ht, pd.special_code, pd.rang,';
3004
        $sql .= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
3005
        $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
3006
        $sql .= ' pd.product_type, pd.ref_fourn as ref_produit_fourn,';
3007
        $sql .= ' pd.fk_multicurrency, pd.multicurrency_code, pd.multicurrency_subprice, pd.multicurrency_total_ht, pd.multicurrency_total_tva, pd.multicurrency_total_ttc, pd.fk_unit';
3008
        $sql .= ' FROM ' . MAIN_DB_PREFIX . 'supplier_proposaldet as pd';
3009
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'product as p ON pd.fk_product = p.rowid';
3010
        $sql .= ' WHERE pd.rowid = ' . ((int) $rowid);
3011
3012
        $result = $this->db->query($sql);
3013
        if ($result) {
3014
            $objp = $this->db->fetch_object($result);
3015
3016
            $this->id = $objp->rowid;
3017
            $this->fk_supplier_proposal = $objp->fk_supplier_proposal;
3018
            $this->fk_parent_line = $objp->fk_parent_line;
3019
            $this->label            = $objp->custom_label;
3020
            $this->desc             = $objp->description;
3021
            $this->qty = $objp->qty;
3022
            $this->subprice = $objp->subprice;
3023
            $this->tva_tx = $objp->tva_tx;
3024
            $this->remise_percent = $objp->remise_percent;
3025
            $this->fk_remise_except = $objp->fk_remise_except;
3026
            $this->fk_product       = $objp->fk_product;
3027
            $this->info_bits        = $objp->info_bits;
3028
            $this->date_start       = $this->db->jdate($objp->date_start);
3029
            $this->date_end         = $this->db->jdate($objp->date_end);
3030
3031
            $this->total_ht         = $objp->total_ht;
3032
            $this->total_tva        = $objp->total_tva;
3033
            $this->total_ttc        = $objp->total_ttc;
3034
3035
            $this->fk_fournprice = $objp->fk_fournprice;
3036
3037
            $marginInfos            = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
3038
            $this->pa_ht            = $marginInfos[0];
3039
            $this->marge_tx         = $marginInfos[1];
3040
            $this->marque_tx        = $marginInfos[2];
3041
3042
            $this->special_code     = $objp->special_code;
3043
            $this->product_type     = $objp->product_type;
3044
            $this->rang = $objp->rang;
3045
3046
            $this->ref = $objp->product_ref; // deprecated
3047
            $this->product_ref = $objp->product_ref;
3048
            $this->libelle = $objp->product_label; // deprecated
3049
            $this->product_label    = $objp->product_label;
3050
            $this->product_desc     = $objp->product_desc;
3051
3052
            $this->ref_fourn = $objp->ref_produit_fourn;
3053
3054
            // Multicurrency
3055
            $this->fk_multicurrency = $objp->fk_multicurrency;
3056
            $this->multicurrency_code = $objp->multicurrency_code;
3057
            $this->multicurrency_subprice   = $objp->multicurrency_subprice;
3058
            $this->multicurrency_total_ht   = $objp->multicurrency_total_ht;
3059
            $this->multicurrency_total_tva  = $objp->multicurrency_total_tva;
3060
            $this->multicurrency_total_ttc  = $objp->multicurrency_total_ttc;
3061
            $this->fk_unit = $objp->fk_unit;
3062
3063
            $this->db->free($result);
3064
            return 1;
3065
        } else {
3066
            dol_print_error($this->db);
3067
            return -1;
3068
        }
3069
    }
3070
3071
    /**
3072
     *  Insert object line propal in database
3073
     *
3074
     *  @param      int     $notrigger      1=Does not execute triggers, 0= execute triggers
3075
     *  @return     int                     Return integer <0 if KO, >0 if OK
3076
     */
3077
    public function insert($notrigger = 0)
3078
    {
3079
        global $conf, $langs, $user;
3080
3081
        $error = 0;
3082
3083
        dol_syslog(get_class($this) . "::insert rang=" . $this->rang);
3084
3085
        // Clean parameters
3086
        if (empty($this->tva_tx)) {
3087
            $this->tva_tx = 0;
3088
        }
3089
        if (empty($this->vat_src_code)) {
3090
            $this->vat_src_code = '';
3091
        }
3092
        if (empty($this->localtax1_tx)) {
3093
            $this->localtax1_tx = 0;
3094
        }
3095
        if (empty($this->localtax2_tx)) {
3096
            $this->localtax2_tx = 0;
3097
        }
3098
        if (empty($this->localtax1_type)) {
3099
            $this->localtax1_type = 0;
3100
        }
3101
        if (empty($this->localtax2_type)) {
3102
            $this->localtax2_type = 0;
3103
        }
3104
        if (empty($this->total_localtax1)) {
3105
            $this->total_localtax1 = 0;
3106
        }
3107
        if (empty($this->total_localtax2)) {
3108
            $this->total_localtax2 = 0;
3109
        }
3110
        if (empty($this->rang)) {
3111
            $this->rang = 0;
3112
        }
3113
        if (empty($this->remise_percent)) {
3114
            $this->remise_percent = 0;
3115
        }
3116
        if (empty($this->info_bits)) {
3117
            $this->info_bits = 0;
3118
        }
3119
        if (empty($this->special_code)) {
3120
            $this->special_code = 0;
3121
        }
3122
        if (empty($this->fk_parent_line)) {
3123
            $this->fk_parent_line = 0;
3124
        }
3125
        if (empty($this->fk_fournprice)) {
3126
            $this->fk_fournprice = 0;
3127
        }
3128
        if (empty($this->fk_unit)) {
3129
            $this->fk_unit = 0;
3130
        }
3131
        if (empty($this->subprice)) {
3132
            $this->subprice = 0;
3133
        }
3134
3135
        if (empty($this->pa_ht)) {
3136
            $this->pa_ht = 0;
3137
        }
3138
3139
        // if buy price not defined, define buyprice as configured in margin admin
3140
        if ($this->pa_ht == 0) {
3141
            $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3142
            if ($result < 0) {
3143
                return $result;
3144
            } else {
3145
                $this->pa_ht = $result;
3146
            }
3147
        }
3148
3149
        // Check parameters
3150
        if ($this->product_type < 0) {
3151
            return -1;
3152
        }
3153
3154
        $this->db->begin();
3155
3156
        // Insert line into database
3157
        $sql = 'INSERT INTO ' . MAIN_DB_PREFIX . 'supplier_proposaldet';
3158
        $sql .= ' (fk_supplier_proposal, fk_parent_line, label, description, fk_product, product_type,';
3159
        $sql .= ' date_start, date_end,';
3160
        $sql .= ' fk_remise_except, qty, tva_tx, vat_src_code, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3161
        $sql .= ' subprice, remise_percent, ';
3162
        $sql .= ' info_bits, ';
3163
        $sql .= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
3164
        $sql .= ' ref_fourn,';
3165
        $sql .= ' fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc, fk_unit)';
3166
        $sql .= " VALUES (" . $this->fk_supplier_proposal . ",";
3167
        $sql .= " " . ($this->fk_parent_line > 0 ? ((int) $this->fk_parent_line) : "null") . ",";
3168
        $sql .= " " . (!empty($this->label) ? "'" . $this->db->escape($this->label) . "'" : "null") . ",";
3169
        $sql .= " '" . $this->db->escape($this->desc) . "',";
3170
        $sql .= " " . ($this->fk_product ? ((int) $this->fk_product) : "null") . ",";
3171
        $sql .= " '" . $this->db->escape($this->product_type) . "',";
3172
        $sql .= " " . ($this->date_start ? "'" . $this->db->idate($this->date_start) . "'" : "null") . ",";
3173
        $sql .= " " . ($this->date_end ? "'" . $this->db->idate($this->date_end) . "'" : "null") . ",";
3174
        $sql .= " " . ($this->fk_remise_except ? ((int) $this->fk_remise_except) : "null") . ",";
3175
        $sql .= " " . price2num($this->qty, 'MS') . ",";
3176
        $sql .= " " . price2num($this->tva_tx) . ",";
3177
        $sql .= " '" . $this->db->escape($this->vat_src_code) . "',";
3178
        $sql .= " " . price2num($this->localtax1_tx) . ",";
3179
        $sql .= " " . price2num($this->localtax2_tx) . ",";
3180
        $sql .= " '" . $this->db->escape($this->localtax1_type) . "',";
3181
        $sql .= " '" . $this->db->escape($this->localtax2_type) . "',";
3182
        $sql .= " " . price2num($this->subprice, 'MU') . ",";
3183
        $sql .= " " . ((float) $this->remise_percent) . ",";
3184
        $sql .= " " . (isset($this->info_bits) ? ((int) $this->info_bits) : "null") . ",";
3185
        $sql .= " " . price2num($this->total_ht, 'MT') . ",";
3186
        $sql .= " " . price2num($this->total_tva, 'MT') . ",";
3187
        $sql .= " " . price2num($this->total_localtax1, 'MT') . ",";
3188
        $sql .= " " . price2num($this->total_localtax2, 'MT') . ",";
3189
        $sql .= " " . price2num($this->total_ttc, 'MT') . ",";
3190
        $sql .= " " . (!empty($this->fk_fournprice) ? ((int) $this->fk_fournprice) : "null") . ",";
3191
        $sql .= " " . (isset($this->pa_ht) ? price2num($this->pa_ht, 'MU') : "null") . ",";
3192
        $sql .= ' ' . ((int) $this->special_code) . ',';
3193
        $sql .= ' ' . ((int) $this->rang) . ',';
3194
        $sql .= " '" . $this->db->escape($this->ref_fourn) . "'";
3195
        $sql .= ", " . ($this->fk_multicurrency > 0 ? ((int) $this->fk_multicurrency) : 'null');
3196
        $sql .= ", '" . $this->db->escape($this->multicurrency_code) . "'";
3197
        $sql .= ", " . price2num($this->multicurrency_subprice, 'CU');
3198
        $sql .= ", " . price2num($this->multicurrency_total_ht, 'CT');
3199
        $sql .= ", " . price2num($this->multicurrency_total_tva, 'CT');
3200
        $sql .= ", " . price2num($this->multicurrency_total_ttc, 'CT');
3201
        $sql .= ", " . ($this->fk_unit ? ((int) $this->fk_unit) : 'null');
3202
        $sql .= ')';
3203
3204
        dol_syslog(get_class($this) . '::insert', LOG_DEBUG);
3205
        $resql = $this->db->query($sql);
3206
        if ($resql) {
3207
            $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . 'supplier_proposaldet');
3208
3209
            if (!$error) {
3210
                $result = $this->insertExtraFields();
3211
                if ($result < 0) {
3212
                    $error++;
3213
                }
3214
            }
3215
3216
            if (!$error && !$notrigger) {
3217
                // Call trigger
3218
                $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_INSERT', $user);
3219
                if ($result < 0) {
3220
                    $this->db->rollback();
3221
                    return -1;
3222
                }
3223
                // End call triggers
3224
            }
3225
3226
            $this->db->commit();
3227
            return 1;
3228
        } else {
3229
            $this->error = $this->db->error() . " sql=" . $sql;
3230
            $this->db->rollback();
3231
            return -1;
3232
        }
3233
    }
3234
3235
    /**
3236
     * Delete line in database
3237
     *
3238
     * @param   User    $user       User making the deletion
3239
     * @return  int                 Return integer <0 if KO, >0 if OK
3240
     */
3241
    public function delete($user)
3242
    {
3243
        $error = 0;
3244
3245
        $this->db->begin();
3246
3247
        $sql = "DELETE FROM " . MAIN_DB_PREFIX . "supplier_proposaldet";
3248
        $sql .= " WHERE rowid = " . ((int) $this->id);
3249
3250
        if ($this->db->query($sql)) {
3251
            // Remove extrafields
3252
            if (!$error) {
3253
                $result = $this->deleteExtraFields();
3254
                if ($result < 0) {
3255
                    $error++;
3256
                    dol_syslog(get_class($this) . "::delete error -4 " . $this->error, LOG_ERR);
3257
                }
3258
            }
3259
3260
            // Call trigger
3261
            $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_DELETE', $user);
3262
            if ($result < 0) {
3263
                $this->db->rollback();
3264
                return -1;
3265
            }
3266
            // End call triggers
3267
3268
            $this->db->commit();
3269
3270
            return 1;
3271
        } else {
3272
            $this->error = $this->db->error() . " sql=" . $sql;
3273
            $this->db->rollback();
3274
            return -1;
3275
        }
3276
    }
3277
3278
    /**
3279
     *  Update propal line object into DB
3280
     *
3281
     *  @param  int     $notrigger  1=Does not execute triggers, 0= execute triggers
3282
     *  @return int                 Return integer <0 if ko, >0 if ok
3283
     */
3284
    public function update($notrigger = 0)
3285
    {
3286
        global $conf, $langs, $user;
3287
3288
        $error = 0;
3289
3290
        // Clean parameters
3291
        if (empty($this->tva_tx)) {
3292
            $this->tva_tx = 0;
3293
        }
3294
        if (empty($this->localtax1_tx)) {
3295
            $this->localtax1_tx = 0;
3296
        }
3297
        if (empty($this->localtax2_tx)) {
3298
            $this->localtax2_tx = 0;
3299
        }
3300
        if (empty($this->total_localtax1)) {
3301
            $this->total_localtax1 = 0;
3302
        }
3303
        if (empty($this->total_localtax2)) {
3304
            $this->total_localtax2 = 0;
3305
        }
3306
        if (empty($this->localtax1_type)) {
3307
            $this->localtax1_type = 0;
3308
        }
3309
        if (empty($this->localtax2_type)) {
3310
            $this->localtax2_type = 0;
3311
        }
3312
        if (empty($this->marque_tx)) {
3313
            $this->marque_tx = 0;
3314
        }
3315
        if (empty($this->marge_tx)) {
3316
            $this->marge_tx = 0;
3317
        }
3318
        if (empty($this->remise_percent)) {
3319
            $this->remise_percent = 0;
3320
        }
3321
        if (empty($this->info_bits)) {
3322
            $this->info_bits = 0;
3323
        }
3324
        if (empty($this->special_code)) {
3325
            $this->special_code = 0;
3326
        }
3327
        if (empty($this->fk_parent_line)) {
3328
            $this->fk_parent_line = 0;
3329
        }
3330
        if (empty($this->fk_fournprice)) {
3331
            $this->fk_fournprice = 0;
3332
        }
3333
        if (empty($this->fk_unit)) {
3334
            $this->fk_unit = 0;
3335
        }
3336
        if (empty($this->subprice)) {
3337
            $this->subprice = 0;
3338
        }
3339
3340
        if (empty($this->pa_ht)) {
3341
            $this->pa_ht = 0;
3342
        }
3343
3344
        // if buy price not defined, define buyprice as configured in margin admin
3345
        if ($this->pa_ht == 0) {
3346
            $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
3347
            if ($result < 0) {
3348
                return $result;
3349
            } else {
3350
                $this->pa_ht = $result;
3351
            }
3352
        }
3353
3354
        $this->db->begin();
3355
3356
        // Mise a jour ligne en base
3357
        $sql = "UPDATE " . MAIN_DB_PREFIX . "supplier_proposaldet SET";
3358
        $sql .= " description='" . $this->db->escape($this->desc) . "'";
3359
        $sql .= " , label=" . (!empty($this->label) ? "'" . $this->db->escape($this->label) . "'" : "null");
3360
        $sql .= " , product_type=" . ((int) $this->product_type);
3361
        $sql .= " , date_start=" . ($this->date_start ? "'" . $this->db->idate($this->date_start) . "'" : "null");
3362
        $sql .= " , date_end=" . ($this->date_end ? "'" . $this->db->idate($this->date_end) . "'" : "null");
3363
        $sql .= " , tva_tx='" . price2num($this->tva_tx) . "'";
3364
        $sql .= " , localtax1_tx=" . price2num($this->localtax1_tx);
3365
        $sql .= " , localtax2_tx=" . price2num($this->localtax2_tx);
3366
        $sql .= " , localtax1_type='" . $this->db->escape($this->localtax1_type) . "'";
3367
        $sql .= " , localtax2_type='" . $this->db->escape($this->localtax2_type) . "'";
3368
        $sql .= " , qty='" . price2num($this->qty) . "'";
3369
        $sql .= " , subprice=" . price2num($this->subprice);
3370
        $sql .= " , remise_percent=" . price2num($this->remise_percent);
3371
        $sql .= " , info_bits='" . $this->db->escape($this->info_bits) . "'";
3372
        if (empty($this->skip_update_total)) {
3373
            $sql .= " , total_ht=" . price2num($this->total_ht);
3374
            $sql .= " , total_tva=" . price2num($this->total_tva);
3375
            $sql .= " , total_ttc=" . price2num($this->total_ttc);
3376
            $sql .= " , total_localtax1=" . price2num($this->total_localtax1);
3377
            $sql .= " , total_localtax2=" . price2num($this->total_localtax2);
3378
        }
3379
        $sql .= " , fk_product_fournisseur_price=" . (!empty($this->fk_fournprice) ? "'" . $this->db->escape($this->fk_fournprice) . "'" : "null");
3380
        $sql .= " , buy_price_ht=" . price2num($this->pa_ht);
3381
        $sql .= " , special_code=" . ((int) $this->special_code);
3382
        $sql .= " , fk_parent_line=" . ($this->fk_parent_line > 0 ? $this->fk_parent_line : "null");
3383
        if (!empty($this->rang)) {
3384
            $sql .= ", rang=" . ((int) $this->rang);
3385
        }
3386
        $sql .= " , ref_fourn=" . (!empty($this->ref_fourn) ? "'" . $this->db->escape($this->ref_fourn) . "'" : "null");
3387
        $sql .= " , fk_unit=" . ($this->fk_unit ? $this->fk_unit : 'null');
3388
3389
        // Multicurrency
3390
        $sql .= " , multicurrency_subprice=" . price2num($this->multicurrency_subprice);
3391
        $sql .= " , multicurrency_total_ht=" . price2num($this->multicurrency_total_ht);
3392
        $sql .= " , multicurrency_total_tva=" . price2num($this->multicurrency_total_tva);
3393
        $sql .= " , multicurrency_total_ttc=" . price2num($this->multicurrency_total_ttc);
3394
3395
        $sql .= " WHERE rowid = " . ((int) $this->id);
3396
3397
        dol_syslog(get_class($this) . "::update", LOG_DEBUG);
3398
        $resql = $this->db->query($sql);
3399
        if ($resql) {
3400
            if (!$error) {
3401
                $result = $this->insertExtraFields();
3402
                if ($result < 0) {
3403
                    $error++;
3404
                }
3405
            }
3406
3407
            if (!$error && !$notrigger) {
3408
                // Call trigger
3409
                $result = $this->call_trigger('LINESUPPLIER_PROPOSAL_MODIFY', $user);
3410
                if ($result < 0) {
3411
                    $this->db->rollback();
3412
                    return -1;
3413
                }
3414
                // End call triggers
3415
            }
3416
3417
            $this->db->commit();
3418
            return 1;
3419
        } else {
3420
            $this->error = $this->db->error();
3421
            $this->db->rollback();
3422
            return -2;
3423
        }
3424
    }
3425
3426
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3427
    /**
3428
     *  Update DB line fields total_xxx
3429
     *  Used by migration
3430
     *
3431
     *  @return     int     Return integer <0 if ko, >0 if ok
3432
     */
3433
    public function update_total()
3434
    {
3435
		// phpcs:enable
3436
        $this->db->begin();
3437
3438
        // Mise a jour ligne en base
3439
        $sql = "UPDATE " . MAIN_DB_PREFIX . "supplier_proposaldet SET";
3440
        $sql .= " total_ht=" . price2num($this->total_ht, 'MT');
3441
        $sql .= ",total_tva=" . price2num($this->total_tva, 'MT');
3442
        $sql .= ",total_ttc=" . price2num($this->total_ttc, 'MT');
3443
        $sql .= " WHERE rowid = " . ((int) $this->id);
3444
3445
        dol_syslog("SupplierProposalLine::update_total", LOG_DEBUG);
3446
3447
        $resql = $this->db->query($sql);
3448
        if ($resql) {
3449
            $this->db->commit();
3450
            return 1;
3451
        } else {
3452
            $this->error = $this->db->error();
3453
            $this->db->rollback();
3454
            return -2;
3455
        }
3456
    }
3457
}
3458