Passed
Pull Request — dev (#6)
by Rafael
79:24 queued 24:08
created

FactureRec::updateline()   F

Complexity

Conditions 23
Paths > 20000

Size

Total Lines 145
Code Lines 104

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 23
eloc 104
nc 1048578
nop 25
dl 0
loc 145
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

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:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/* Copyright (C) 2003-2005  Rodolphe Quiedeville        <[email protected]>
4
 * Copyright (C) 2004-2019	Laurent Destailleur		    <[email protected]>
5
 * Copyright (C) 2009-2012	Regis Houssin			    <[email protected]>
6
 * Copyright (C) 2010-2011	Juanjo Menent			    <[email protected]>
7
 * Copyright (C) 2012       Cedric Salvador             <[email protected]>
8
 * Copyright (C) 2013       Florian Henry		  	    <[email protected]>
9
 * Copyright (C) 2015       Marcos García               <[email protected]>
10
 * Copyright (C) 2017-2024  Frédéric France             <[email protected]>
11
 * Copyright (C) 2023       Nick Fragoulis
12
 * Copyright (C) 2024		MDW							<[email protected]>
13
 * Copyright (C) 2024       Rafael San José             <[email protected]>
14
 *
15
 * This program is free software; you can redistribute it and/or modify
16
 * it under the terms of the GNU General Public License as published by
17
 * the Free Software Foundation; either version 3 of the License, or
18
 * (at your option) any later version.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 * GNU General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU General Public License
26
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
27
 */
28
29
namespace Dolibarr\Code\Compta\Classes;
30
31
use Dolibarr\Code\Core\Classes\CommonInvoice;
32
use Dolibarr\Code\User\Classes\User;
33
use DoliDB;
34
35
/**
36
 *  \file       htdocs/compta/facture/class/facture-rec.class.php
37
 *  \ingroup    invoice
38
 *  \brief      File of class to manage recurring invoices
39
 */
40
41
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/date.lib.php';
42
43
/**
44
 *  Class to manage invoice templates
45
 */
46
class FactureRec extends CommonInvoice
47
{
48
    const TRIGGER_PREFIX = 'BILLREC';
49
    /**
50
     * @var string ID to identify managed object
51
     */
52
    public $element = 'facturerec';
53
54
    /**
55
     * @var string Name of table without prefix where object is stored
56
     */
57
    public $table_element = 'facture_rec';
58
59
    /**
60
     * @var string    Name of subtable line
61
     */
62
    public $table_element_line = 'facturedet_rec';
63
64
    /**
65
     * @var string Field with ID of parent key if this field has a parent
66
     */
67
    public $fk_element = 'fk_facture';
68
69
    /**
70
     * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png
71
     */
72
    public $picto = 'bill';
73
74
    /**
75
     * @var int Entity
76
     */
77
    public $entity;
78
79
    /**
80
     * {@inheritdoc}
81
     */
82
    protected $table_ref_field = 'titre';
83
84
    /**
85
     * @var string The label of recurring invoice
86
     */
87
    public $title;
88
89
    /**
90
     * @var string  The label of recurring invoice
91
     * @deprecated  Use $title instead
92
     */
93
    public $titre;
94
95
    /**
96
     * @var double
97
     */
98
    public $multicurrency_subprice;
99
    public $socid;
100
    public $number;
101
    public $date;
102
    //public $remise;
103
    //public $remise_absolue;
104
    //public $remise_percent;
105
106
    /**
107
     * @deprecated
108
     * @see $total_ht
109
     */
110
    public $total;
111
112
    /**
113
     * @deprecated
114
     * @see $total_tva
115
     */
116
    public $tva;
117
118
    public $date_last_gen;
119
    public $date_when;
120
    public $nb_gen_done;
121
    public $nb_gen_max;
122
123
    public $user_author;
124
125
    /**
126
     * @var int Frequency
127
     */
128
    public $frequency;
129
130
    /**
131
     * @var string Unit frequency
132
     */
133
    public $unit_frequency;
134
135
    public $rang;
136
137
    /**
138
     * @var int special code
139
     */
140
    public $special_code;
141
142
    public $usenewprice = 0;
143
144
    /**
145
     * @var int Deadline for payment
146
     */
147
    public $date_lim_reglement;
148
    public $cond_reglement_code; // Code in llx_c_paiement
149
    public $mode_reglement_code; // Code in llx_c_paiement
150
151
    public $suspended; // status
152
153
    public $auto_validate; // 0 to create in draft, 1 to create and validate the new invoice
154
    public $generate_pdf; // 1 to generate PDF on invoice generation (default)
155
156
157
    /**
158
     *  'type' if the field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter]]', 'varchar(x)', 'double(24,8)', 'real', 'price', 'text', 'html', 'date', 'datetime', 'timestamp', 'duration', 'mail', 'phone', 'url', 'password')
159
     *         Note: Filter can be a string like "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.nature:is:NULL)"
160
     *  'label' the translation key.
161
     *  'enabled' is a condition when the field must be managed.
162
     *  'position' is the sort order of field.
163
     *  'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
164
     *  'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only, 3=Visible on create/update/view form only (not list), 4=Visible on list and update/view form only (not create). 5=Visible on list and view only (not create/not update). Using a negative value means field is not shown by default on list but can be selected for viewing)
165
     *  'noteditable' says if field is not editable (1 or 0)
166
     *  'default' is a default value for creation (can still be overwrote by the Setup of Default Values if field is editable in creation form). Note: If default is set to '(PROV)' and field is 'ref', the default value will be set to '(PROVid)' where id is rowid when a new record is created.
167
     *  'index' if we want an index in database.
168
     *  'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommended to name the field fk_...).
169
     *  'searchall' is 1 if we want to search in this field when making a search from the quick search button.
170
     *  'isameasure' must be set to 1 if you want to have a total on list for this field. Field type must be summable like integer or double(24,8).
171
     *  'css' is the CSS style to use on field. For example: 'maxwidth200'
172
     *  'help' is a string visible as a tooltip on field
173
     *  'showoncombobox' if value of the field must be visible into the label of the combobox that list record
174
     *  'disabled' is 1 if we want to have the field locked by a 'disabled' attribute. In most cases, this is never set into the definition of $fields into class, but is set dynamically by some part of code.
175
     *  'arrayofkeyval' to set list of value if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel")
176
     *  'comment' is not used. You can store here any text of your choice. It is not used by application.
177
     *
178
     *  Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor.
179
     */
180
181
    // BEGIN MODULEBUILDER PROPERTIES
182
    /**
183
     * @var array<string,array{type:string,label:string,enabled:int<0,2>|string,position:int,notnull?:int,visible:int,noteditable?:int,default?:string,index?:int,foreignkey?:string,searchall?:int,isameasure?:int,css?:string,csslist?:string,help?:string,showoncombobox?:int,disabled?:int,arrayofkeyval?:array<int,string>,comment?:string}>  Array with all fields and their property. Do not use it as a static var. It may be modified by constructor.
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<string,array{type:...ring>,comment?:string}> at position 16 could not be parsed: Expected '}' at position 16, but found 'int'.
Loading history...
184
     */
185
    public $fields = array(
186
        'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10),
187
        'titre' => array('type' => 'varchar(100)', 'label' => 'Titre', 'enabled' => 1, 'showoncombobox' => 1, 'visible' => -1, 'position' => 15),
188
        'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 20, 'index' => 1),
189
        'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'enabled' => 'isModEnabled("societe")', 'visible' => -1, 'notnull' => 1, 'position' => 25),
190
        'subtype' => array('type' => 'smallint(6)', 'label' => 'InvoiceSubtype', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 30),
191
        'datec' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -1, 'position' => 35),
192
        'total_tva' => array('type' => 'double(24,8)', 'label' => 'Tva', 'enabled' => 1, 'visible' => -1, 'position' => 55, 'isameasure' => 1),
193
        'localtax1' => array('type' => 'double(24,8)', 'label' => 'Localtax1', 'enabled' => 1, 'visible' => -1, 'position' => 60, 'isameasure' => 1),
194
        'localtax2' => array('type' => 'double(24,8)', 'label' => 'Localtax2', 'enabled' => 1, 'visible' => -1, 'position' => 65, 'isameasure' => 1),
195
        'total_ht' => array('type' => 'double(24,8)', 'label' => 'Total', 'enabled' => 1, 'visible' => -1, 'position' => 70, 'isameasure' => 1),
196
        'total_ttc' => array('type' => 'double(24,8)', 'label' => 'Total ttc', 'enabled' => 1, 'visible' => -1, 'position' => 75, 'isameasure' => 1),
197
        'fk_user_author' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'Fk user author', 'enabled' => 1, 'visible' => -1, 'position' => 80),
198
        'fk_projet' => array('type' => 'integer:Project:projet/class/project.class.php:1:(fk_statut:=:1)', 'label' => 'Fk projet', 'enabled' => "isModEnabled('project')", 'visible' => -1, 'position' => 85),
199
        'fk_cond_reglement' => array('type' => 'integer', 'label' => 'Fk cond reglement', 'enabled' => 1, 'visible' => -1, 'position' => 90),
200
        'fk_mode_reglement' => array('type' => 'integer', 'label' => 'Fk mode reglement', 'enabled' => 1, 'visible' => -1, 'position' => 95),
201
        'date_lim_reglement' => array('type' => 'date', 'label' => 'Date lim reglement', 'enabled' => 1, 'visible' => -1, 'position' => 100),
202
        'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 105),
203
        'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 110),
204
        'modelpdf' => array('type' => 'varchar(255)', 'label' => 'Modelpdf', 'enabled' => 1, 'visible' => -1, 'position' => 115),
205
        'date_when' => array('type' => 'datetime', 'label' => 'Date when', 'enabled' => 1, 'visible' => -1, 'position' => 130),
206
        'date_last_gen' => array('type' => 'datetime', 'label' => 'Date last gen', 'enabled' => 1, 'visible' => -1, 'position' => 135),
207
        'nb_gen_done' => array('type' => 'integer', 'label' => 'Nb gen done', 'enabled' => 1, 'visible' => -1, 'position' => 140),
208
        'nb_gen_max' => array('type' => 'integer', 'label' => 'Nb gen max', 'enabled' => 1, 'visible' => -1, 'position' => 145),
209
        'frequency' => array('type' => 'integer', 'label' => 'Frequency', 'enabled' => 1, 'visible' => -1, 'position' => 150),
210
        'unit_frequency' => array('type' => 'varchar(2)', 'label' => 'UnitFrequency', 'enabled' => 1, 'visible' => -1, 'position' => 152),
211
        'usenewprice' => array('type' => 'integer', 'label' => 'UseNewPrice', 'enabled' => 1, 'visible' => 0, 'position' => 155),
212
        'revenuestamp' => array('type' => 'double(24,8)', 'label' => 'RevenueStamp', 'enabled' => 1, 'visible' => -1, 'position' => 160, 'isameasure' => 1),
213
        'auto_validate' => array('type' => 'integer', 'label' => 'Auto validate', 'enabled' => 1, 'visible' => -1, 'position' => 165),
214
        'generate_pdf' => array('type' => 'integer', 'label' => 'Generate pdf', 'enabled' => 1, 'visible' => -1, 'position' => 170),
215
        'fk_account' => array('type' => 'integer', 'label' => 'Fk account', 'enabled' => 'isModEnabled("bank")', 'visible' => -1, 'position' => 175),
216
        'fk_multicurrency' => array('type' => 'integer', 'label' => 'Fk multicurrency', 'enabled' => 1, 'visible' => -1, 'position' => 180),
217
        'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'Multicurrency code', 'enabled' => 1, 'visible' => -1, 'position' => 185),
218
        'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'Multicurrency tx', 'enabled' => 1, 'visible' => -1, 'position' => 190, 'isameasure' => 1),
219
        'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'Multicurrency total ht', 'enabled' => 1, 'visible' => -1, 'position' => 195, 'isameasure' => 1),
220
        'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'Multicurrency total tva', 'enabled' => 1, 'visible' => -1, 'position' => 200, 'isameasure' => 1),
221
        'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'Multicurrency total ttc', 'enabled' => 1, 'visible' => -1, 'position' => 205, 'isameasure' => 1),
222
        'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'position' => 210),
223
        'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 215),
224
        'suspended' => array('type' => 'integer', 'label' => 'Suspended', 'enabled' => 1, 'visible' => -1, 'position' => 225),
225
    );
226
    // END MODULEBUILDER PROPERTIES
227
228
    const STATUS_NOTSUSPENDED = 0;
229
    const STATUS_SUSPENDED = 1;
230
231
232
233
    /**
234
     *  Constructor
235
     *
236
     *  @param      DoliDB      $db     Database handler
237
     */
238
    public function __construct(DoliDB $db)
239
    {
240
        $this->db = $db;
241
    }
242
243
    /**
244
     *  Create a predefined invoice
245
     *
246
     *  @param      User    $user       User object
247
     *  @param      int     $facid      Id of source invoice
248
     *  @param      int     $notrigger  No trigger
249
     *  @param      array   $onlylines  Only the lines of the array
250
     *  @return     int                 Return integer <0 if KO, id of invoice created if OK
251
     */
252
    public function create($user, $facid, $notrigger = 0, $onlylines = array())
253
    {
254
        global $conf;
255
256
        $error = 0;
257
        $now = dol_now();
258
259
        // Clean parameters
260
        $this->titre = trim(isset($this->titre) ? $this->titre : $this->title); // deprecated
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Code\Compta\Classes\FactureRec::$titre has been deprecated: Use $title instead ( Ignorable by Annotation )

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

260
        $this->titre = trim(isset(/** @scrutinizer ignore-deprecated */ $this->titre) ? $this->titre : $this->title); // 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...
261
        $this->title = trim($this->title);
262
        $this->usenewprice = empty($this->usenewprice) ? 0 : $this->usenewprice;
263
        if (empty($this->suspended)) {
264
            $this->suspended = 0;
265
        }
266
267
        // No frequency defined then no next date to execution
268
        if (empty($this->frequency)) {
269
            $this->frequency = 0;
270
            $this->date_when = null;
271
        }
272
273
        $this->frequency = abs($this->frequency);
274
        $this->nb_gen_done = 0;
275
        $this->nb_gen_max = empty($this->nb_gen_max) ? 0 : $this->nb_gen_max;
276
        $this->auto_validate = empty($this->auto_validate) ? 0 : $this->auto_validate;
277
        $this->generate_pdf = empty($this->generate_pdf) ? 0 : $this->generate_pdf;
278
279
        $this->db->begin();
280
281
        // Load invoice template
282
        $facsrc = new Facture($this->db);
283
        $result = $facsrc->fetch($facid);
284
        if ($result > 0) {
285
            $this->socid = $facsrc->socid;
286
287
            $sql = "INSERT INTO " . MAIN_DB_PREFIX . "facture_rec (";
288
            $sql .= "titre";
289
            $sql .= ", fk_soc";
290
            $sql .= ", subtype";
291
            $sql .= ", entity";
292
            $sql .= ", datec";
293
            $sql .= ", amount";
294
            //$sql .= ", remise";
295
            $sql .= ", note_private";
296
            $sql .= ", note_public";
297
            $sql .= ", modelpdf";
298
            $sql .= ", fk_user_author";
299
            $sql .= ", fk_projet";
300
            $sql .= ", fk_account";
301
            $sql .= ", fk_cond_reglement";
302
            $sql .= ", fk_mode_reglement";
303
            $sql .= ", usenewprice";
304
            $sql .= ", frequency";
305
            $sql .= ", unit_frequency";
306
            $sql .= ", date_when";
307
            $sql .= ", date_last_gen";
308
            $sql .= ", nb_gen_done";
309
            $sql .= ", nb_gen_max";
310
            $sql .= ", auto_validate";
311
            $sql .= ", generate_pdf";
312
            $sql .= ", fk_multicurrency";
313
            $sql .= ", multicurrency_code";
314
            $sql .= ", multicurrency_tx";
315
            $sql .= ", suspended";
316
            $sql .= ") VALUES (";
317
            $sql .= "'" . $this->db->escape($this->titre ? $this->titre : $this->title) . "'";
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Code\Compta\Classes\FactureRec::$titre has been deprecated: Use $title instead ( Ignorable by Annotation )

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

317
            $sql .= "'" . $this->db->escape($this->titre ? /** @scrutinizer ignore-deprecated */ $this->titre : $this->title) . "'";

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...
318
            $sql .= ", " . ((int) $this->socid);
319
            $sql .= ", " . ($this->subtype ? "'" . $this->db->escape($this->subtype) . "'" : "null");
320
            $sql .= ", " . ((int) $conf->entity);
321
            $sql .= ", '" . $this->db->idate($now) . "'";
322
            $sql .= ", " . (!empty($facsrc->total_ttc) ? ((float) $facsrc->total_ttc) : '0');
323
            //$sql .= ", ".(!empty($facsrc->remise_absolue) ? ((float) $this->remise_absolue) : '0');
324
            $sql .= ", " . (!empty($this->note_private) ? ("'" . $this->db->escape($this->note_private) . "'") : "NULL");
325
            $sql .= ", " . (!empty($this->note_public) ? ("'" . $this->db->escape($this->note_public) . "'") : "NULL");
326
            $sql .= ", " . (!empty($this->model_pdf) ? ("'" . $this->db->escape($this->model_pdf) . "'") : "NULL");
327
            $sql .= ", " . ((int) $user->id);
328
            $sql .= ", " . (!empty($this->fk_project) ? ((int) $this->fk_project) : "null");
329
            $sql .= ", " . (!empty($facsrc->fk_account) ? ((int) $facsrc->fk_account) : "null");
330
            $sql .= ", " . ($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : "null");
331
            $sql .= ", " . ($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : "null");
332
            $sql .= ", " . ((int) $this->usenewprice);
333
            $sql .= ", " . ((int) $this->frequency);
334
            $sql .= ", '" . $this->db->escape($this->unit_frequency) . "'";
335
            $sql .= ", " . (!empty($this->date_when) ? "'" . $this->db->idate($this->date_when) . "'" : 'NULL');
336
            $sql .= ", " . (!empty($this->date_last_gen) ? "'" . $this->db->idate($this->date_last_gen) . "'" : 'NULL');
337
            $sql .= ", " . ((int) $this->nb_gen_done);
338
            $sql .= ", " . ((int) $this->nb_gen_max);
339
            $sql .= ", " . ((int) $this->auto_validate);
340
            $sql .= ", " . ((int) $this->generate_pdf);
341
            $sql .= ", " . ((int) $facsrc->fk_multicurrency);
342
            $sql .= ", '" . $this->db->escape($facsrc->multicurrency_code) . "'";
343
            $sql .= ", " . ((float) $facsrc->multicurrency_tx);
344
            $sql .= ", " . ((int) $this->suspended);
345
            $sql .= ")";
346
347
            if ($this->db->query($sql)) {
348
                $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . "facture_rec");
349
350
                // Fields used into addline later
351
                $this->fk_multicurrency = $facsrc->fk_multicurrency;
352
                $this->multicurrency_code = $facsrc->multicurrency_code;
353
                $this->multicurrency_tx = $facsrc->multicurrency_tx;
354
355
                // Add lines
356
                $fk_parent_line = 0;
357
358
                $num = count($facsrc->lines);
359
                for ($i = 0; $i < $num; $i++) {
360
                    if (!empty($onlylines) && !in_array($facsrc->lines[$i]->id, $onlylines)) {
361
                        continue; // Skip unselected lines
362
                    }
363
364
                    // Reset fk_parent_line for no child products and special product
365
                    if (($facsrc->lines[$i]->product_type != 9 && empty($facsrc->lines[$i]->fk_parent_line)) || $facsrc->lines[$i]->product_type == 9) {
366
                        $fk_parent_line = 0;
367
                    }
368
369
                    $tva_tx = $facsrc->lines[$i]->tva_tx;
370
                    if (!empty($facsrc->lines[$i]->vat_src_code) && !preg_match('/\(/', (string) $tva_tx)) {
371
                        $tva_tx .= ' (' . $facsrc->lines[$i]->vat_src_code . ')';
372
                    }
373
374
                    $default_start_fill = getDolGlobalInt('INVOICEREC_SET_AUTOFILL_DATE_START');
375
                    $default_end_fill = getDolGlobalInt('INVOICEREC_SET_AUTOFILL_DATE_END');
376
377
                    $result_insert = $this->addline(
378
                        $facsrc->lines[$i]->desc,
379
                        $facsrc->lines[$i]->subprice,
380
                        $facsrc->lines[$i]->qty,
381
                        $tva_tx,
382
                        $facsrc->lines[$i]->localtax1_tx,
383
                        $facsrc->lines[$i]->localtax2_tx,
384
                        $facsrc->lines[$i]->fk_product,
385
                        $facsrc->lines[$i]->remise_percent,
386
                        'HT',
387
                        $facsrc->lines[$i]->info_bits,
388
                        '',
389
                        0,
390
                        $facsrc->lines[$i]->product_type,
391
                        $facsrc->lines[$i]->rang,
392
                        $facsrc->lines[$i]->special_code,
393
                        $facsrc->lines[$i]->label,
394
                        $facsrc->lines[$i]->fk_unit,
395
                        $facsrc->lines[$i]->multicurrency_subprice,
396
                        $default_start_fill,
397
                        $default_end_fill,
398
                        null,
399
                        $facsrc->lines[$i]->pa_ht,
400
                        $fk_parent_line
401
                    );
402
403
                    // Defined the new fk_parent_line
404
                    if ($result_insert > 0 && $facsrc->lines[$i]->product_type == 9) {
405
                        $fk_parent_line = $result_insert;
406
                    }
407
408
                    if ($result_insert < 0) {
409
                        $error++;
410
                    } else {
411
                        $objectline = new FactureLigneRec($this->db);
412
413
                        $result2 = $objectline->fetch($result_insert);
414
                        if ($result2 > 0) {
415
                            // Extrafields
416
                            if (method_exists($facsrc->lines[$i], 'fetch_optionals')) {
417
                                $facsrc->lines[$i]->fetch_optionals($facsrc->lines[$i]->id);
418
                                $objectline->array_options = $facsrc->lines[$i]->array_options;
419
                            }
420
421
                            $result = $objectline->insertExtraFields();
422
                            if ($result < 0) {
423
                                $error++;
424
                            }
425
                        } elseif ($result2 < 0) {
426
                            $this->errors[] = $objectline->error;
427
                            $error++;
428
                        }
429
                    }
430
                }
431
432
                if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) {  // To use new linkedObjectsIds instead of old linked_objects
433
                    $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
434
                }
435
436
                // Add object linked
437
                if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
438
                    foreach ($this->linked_objects as $origin => $tmp_origin_id) {
439
                        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, ...))
440
                            foreach ($tmp_origin_id as $origin_id) {
441
                                $ret = $this->add_object_linked($origin, $origin_id);
442
                                if (!$ret) {
443
                                    $this->error = $this->db->lasterror();
444
                                    $error++;
445
                                }
446
                            }
447
                        } else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
448
                            $origin_id = $tmp_origin_id;
449
                            $ret = $this->add_object_linked($origin, $origin_id);
450
                            if (!$ret) {
451
                                $this->error = $this->db->lasterror();
452
                                $error++;
453
                            }
454
                        }
455
                    }
456
                }
457
458
                if (!$error) {
459
                    $result = $this->insertExtraFields();
460
                    if ($result < 0) {
461
                        $error++;
462
                    }
463
                }
464
465
                if (!$error && !$notrigger) {
466
                    // Call trigger
467
                    $result = $this->call_trigger('BILLREC_CREATE', $user);
468
                    if ($result < 0) {
469
                        $this->db->rollback();
470
                        return -2;
471
                    }
472
                    // End call triggers
473
                }
474
475
                if ($error) {
476
                    $this->db->rollback();
477
                    return -3;
478
                } else {
479
                    $this->db->commit();
480
                    return $this->id;
481
                }
482
            } else {
483
                $this->error = $this->db->lasterror();
484
                $this->db->rollback();
485
                return -2;
486
            }
487
        } else {
488
            $this->db->rollback();
489
            return -1;
490
        }
491
    }
492
493
494
    /**
495
     *  Update a line invoice_rec.
496
     *
497
     *  @param      User    $user                   User
498
     *  @param      int     $notrigger              No trigger
499
     *  @return     int                             Return integer <0 if KO, Id of line if OK
500
     */
501
    public function update(User $user, $notrigger = 0)
502
    {
503
        $error = 0;
504
505
        $sql = "UPDATE " . MAIN_DB_PREFIX . "facture_rec SET";
506
        $sql .= " entity = " . ((int) $this->entity) . ",";
507
        $sql .= " titre = '" . $this->db->escape($this->title) . "',";
508
        $sql .= " suspended = " . ((int) $this->suspended) . ",";
509
        $sql .= " fk_soc = " . ((int) $this->socid) . ",";
510
        $sql .= " total_tva = " . ((float) $this->total_tva) . ",";
511
        $sql .= " localtax1 = " . ((float) $this->total_localtax1) . ",";
512
        $sql .= " localtax2 = " . ((float) $this->total_localtax2) . ",";
513
        $sql .= " total_ht = " . ((float) $this->total_ht) . ",";
514
        $sql .= " total_ttc = " . ((float) $this->total_ttc);
515
        // TODO Add missing fields
516
        $sql .= " WHERE rowid = " . ((int) $this->id);
517
518
        $this->db->begin();
519
520
        dol_syslog(get_class($this) . "::update", LOG_DEBUG);
521
522
        $resql = $this->db->query($sql);
523
        if ($resql) {
524
            if (!$error) {
525
                $result = $this->insertExtraFields();
526
                if ($result < 0) {
527
                    $error++;
528
                }
529
            }
530
531
            if (!$error && !$notrigger) {
532
                // Call trigger
533
                $result = $this->call_trigger('BILLREC_MODIFY', $user);
534
                if ($result < 0) {
535
                    $this->db->rollback();
536
                    return -2;
537
                }
538
                // End call triggers
539
            }
540
            $this->db->commit();
541
            return 1;
542
        } else {
543
            $this->error = $this->db->lasterror();
544
            $this->db->rollback();
545
            return -1;
546
        }
547
    }
548
549
    /**
550
     *  Load object and lines
551
     *
552
     *  @param      int     $rowid          Id of object to load
553
     *  @param      string  $ref            Reference of recurring invoice
554
     *  @param      string  $ref_ext        External reference of invoice
555
     *  @param      int     $noextrafields  0=Default to load extrafields, 1=No extrafields
556
     *  @param      int     $nolines        0=Default to load lines, 1=No lines
557
     *  @return     int                     >0 if OK, <0 if KO, 0 if not found
558
     */
559
    public function fetch($rowid, $ref = '', $ref_ext = '', $noextrafields = 0, $nolines = 0)
560
    {
561
        dol_syslog('FactureRec::fetch', LOG_DEBUG);
562
563
        $sql = 'SELECT f.rowid, f.entity, f.titre as title, f.suspended, f.fk_soc, f.subtype, f.total_tva, f.localtax1, f.localtax2, f.total_ht, f.total_ttc';
564
        $sql .= ', f.date_lim_reglement as dlr';
565
        $sql .= ', f.note_private, f.note_public, f.fk_user_author';
566
        $sql .= ', f.modelpdf as model_pdf';
567
        $sql .= ', f.fk_mode_reglement, f.fk_cond_reglement, f.fk_projet as fk_project';
568
        $sql .= ', f.fk_account';
569
        $sql .= ', f.frequency, f.unit_frequency, f.date_when, f.date_last_gen, f.nb_gen_done, f.nb_gen_max, f.usenewprice, f.auto_validate';
570
        $sql .= ', f.generate_pdf';
571
        $sql .= ", f.fk_multicurrency, f.multicurrency_code, f.multicurrency_tx, f.multicurrency_total_ht, f.multicurrency_total_tva, f.multicurrency_total_ttc";
572
        $sql .= ', p.code as mode_reglement_code, p.libelle as mode_reglement_libelle';
573
        $sql .= ', c.code as cond_reglement_code, c.libelle as cond_reglement_libelle, c.libelle_facture as cond_reglement_libelle_doc';
574
        //$sql.= ', el.fk_source';
575
        $sql .= ' FROM ' . MAIN_DB_PREFIX . 'facture_rec as f';
576
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_payment_term as c ON f.fk_cond_reglement = c.rowid';
577
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_paiement as p ON f.fk_mode_reglement = p.id';
578
        //$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."element_element as el ON el.fk_target = f.rowid AND el.targettype = 'facture'";
579
        $sql .= ' WHERE f.entity IN (' . getEntity('invoice') . ')';
580
        if ($rowid) {
581
            $sql .= ' AND f.rowid = ' . ((int) $rowid);
582
        } elseif ($ref) {
583
            $sql .= " AND f.titre = '" . $this->db->escape($ref) . "'";
584
        } else {
585
            $sql .= ' AND f.rowid = 0';
586
        }
587
        /* This field are not used for template invoice
588
         if ($ref_ext) $sql.= " AND f.ref_ext='".$this->db->escape($ref_ext)."'";
589
         */
590
591
        $result = $this->db->query($sql);
592
        if ($result) {
593
            if ($this->db->num_rows($result)) {
594
                $obj = $this->db->fetch_object($result);
595
596
                $this->id                     = $obj->rowid;
597
                $this->entity                 = $obj->entity;
598
                $this->titre                  = $obj->title; // deprecated
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Code\Compta\Classes\FactureRec::$titre has been deprecated: Use $title instead ( Ignorable by Annotation )

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

598
                /** @scrutinizer ignore-deprecated */ $this->titre                  = $obj->title; // 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...
599
                $this->title                  = $obj->title;
600
                $this->ref                    = $obj->title;
601
                $this->subtype                = $obj->subtype;
602
                $this->suspended              = $obj->suspended;
603
                $this->total_ht               = $obj->total_ht;
604
                $this->total_tva              = $obj->total_tva;
605
                $this->total_localtax1        = $obj->localtax1;
606
                $this->total_localtax2        = $obj->localtax2;
607
                $this->total_ttc              = $obj->total_ttc;
608
                $this->socid                  = $obj->fk_soc;
609
                $this->date_lim_reglement     = $this->db->jdate($obj->dlr);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->db->jdate($obj->dlr) can also be of type string. However, the property $date_lim_reglement is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
610
                $this->mode_reglement_id      = $obj->fk_mode_reglement;
611
                $this->mode_reglement_code    = $obj->mode_reglement_code;
612
                $this->mode_reglement         = $obj->mode_reglement_libelle;
613
                $this->cond_reglement_id      = $obj->fk_cond_reglement;
614
                $this->cond_reglement_code    = $obj->cond_reglement_code;
615
                $this->cond_reglement         = $obj->cond_reglement_libelle;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$cond_reglement has been deprecated: Use $cond_reglement_id instead - Kept for compatibility ( Ignorable by Annotation )

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

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

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 Dolibarr\Core\Base\CommonObject. Since you implement __set, consider adding a @property or @property-write.
Loading history...
616
                $this->cond_reglement_doc     = $obj->cond_reglement_libelle_doc;
617
                $this->fk_project             = $obj->fk_project;
618
                $this->fk_account             = $obj->fk_account;
619
                $this->note_private           = $obj->note_private;
620
                $this->note_public            = $obj->note_public;
621
                $this->user_author            = $obj->fk_user_author;
622
                $this->model_pdf              = $obj->model_pdf;
623
                //$this->special_code = $obj->special_code;
624
                $this->frequency              = $obj->frequency;
625
                $this->unit_frequency = $obj->unit_frequency;
626
                $this->date_when              = $this->db->jdate($obj->date_when);
627
                $this->date_last_gen = $this->db->jdate($obj->date_last_gen);
628
                $this->nb_gen_done            = $obj->nb_gen_done;
629
                $this->nb_gen_max = $obj->nb_gen_max;
630
                $this->usenewprice            = $obj->usenewprice;
631
                $this->auto_validate = $obj->auto_validate;
632
                $this->generate_pdf = $obj->generate_pdf;
633
634
                // Multicurrency
635
                $this->fk_multicurrency         = $obj->fk_multicurrency;
636
                $this->multicurrency_code = $obj->multicurrency_code;
637
                $this->multicurrency_tx         = $obj->multicurrency_tx;
638
                $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
639
                $this->multicurrency_total_tva  = $obj->multicurrency_total_tva;
640
                $this->multicurrency_total_ttc  = $obj->multicurrency_total_ttc;
641
642
                // Retrieve all extrafield
643
                // fetch optionals attributes and labels
644
                if (empty($noextrafields)) {
645
                    $result = $this->fetch_optionals();
646
                    if ($result < 0) {
647
                        $this->error = $this->db->lasterror();
648
                        return -4;
649
                    }
650
                }
651
652
                // Retrieve lines
653
                if (empty($nolines)) {
654
                    $result = $this->fetch_lines();
655
                    if ($result < 0) {
656
                        $this->error = $this->db->lasterror();
657
                        return -3;
658
                    }
659
                }
660
661
                return 1;
662
            } else {
663
                $this->error = 'Bill with id ' . $rowid . ' or ref ' . $ref . ' not found';
664
                dol_syslog('Facture::Fetch Error ' . $this->error, LOG_ERR);
665
                return -2;
666
            }
667
        } else {
668
            $this->error = $this->db->error();
669
            return -1;
670
        }
671
    }
672
673
674
    /**
675
     *  Create an array of invoice lines
676
     *
677
     *  @return int     >0 if OK, <0 if KO
678
     */
679
    public function getLinesArray()
680
    {
681
        return $this->fetch_lines();
682
    }
683
684
685
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
686
    /**
687
     *  Get lines of template invoices into this->lines
688
     *
689
     *  @return     int         1 if OK, < 0 if KO
690
     */
691
    public function fetch_lines()
692
    {
693
		// phpcs:enable
694
695
        $this->lines = array();
696
697
        dol_syslog('FactureRec::fetch_lines', LOG_DEBUG);
698
699
        $sql = 'SELECT l.rowid, l.fk_facture, l.fk_product, l.fk_parent_line, l.product_type, l.label as custom_label, l.description, l.product_type, l.price, l.qty, l.vat_src_code, l.tva_tx, ';
700
        $sql .= ' l.localtax1_tx, l.localtax2_tx, l.localtax1_type, l.localtax2_type, l.remise, l.remise_percent, l.subprice,';
701
        $sql .= ' l.info_bits, l.date_start_fill, l.date_end_fill, l.total_ht, l.total_tva, l.total_ttc, l.fk_product_fournisseur_price, l.buy_price_ht as pa_ht,';
702
        $sql .= ' l.rang, l.special_code,';
703
        $sql .= ' l.fk_unit, l.fk_contract_line,';
704
        $sql .= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc,';
705
        $sql .= ' p.ref as product_ref, p.fk_product_type as fk_product_type, p.label as product_label, p.description as product_desc';
706
        $sql .= ' FROM ' . MAIN_DB_PREFIX . 'facturedet_rec as l';
707
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'product as p ON l.fk_product = p.rowid';
708
        $sql .= ' WHERE l.fk_facture = ' . ((int) $this->id);
709
        $sql .= ' ORDER BY l.rang';
710
711
        $result = $this->db->query($sql);
712
        if ($result) {
713
            $num = $this->db->num_rows($result);
714
            $i = 0;
715
            while ($i < $num) {
716
                $objp = $this->db->fetch_object($result);
717
                $line = new FactureLigneRec($this->db);
718
719
                $line->id               = $objp->rowid;
720
                $line->rowid            = $objp->rowid;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObjectLine::$rowid has been deprecated: Try to use id property as possible (even if field into database is still rowid) ( Ignorable by Annotation )

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

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

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...
721
                $line->fk_facture       = $objp->fk_facture;
722
                $line->fk_parent_line   = $objp->fk_parent_line;
723
                $line->desc             = $objp->description; // Description line
724
                $line->description      = $objp->description; // Description line
725
                $line->ref              = $objp->product_ref; // Ref product
726
                $line->product_ref      = $objp->product_ref; // Ref product
727
                $line->libelle          = $objp->product_label; // deprecated
728
                $line->product_label    = $objp->product_label; // Label product
729
                $line->product_desc     = $objp->product_desc; // Description product
730
                $line->product_type     = $objp->product_type; // Type of line
731
                $line->fk_product_type  = $objp->fk_product_type; // Type of product
732
                $line->qty              = $objp->qty;
733
                $line->subprice         = $objp->subprice;
734
735
                $line->label            = $objp->custom_label; // @deprecated
736
737
                $line->vat_src_code     = $objp->vat_src_code;
738
                $line->tva_tx           = $objp->tva_tx;
739
                $line->localtax1_tx     = $objp->localtax1_tx;
740
                $line->localtax2_tx     = $objp->localtax2_tx;
741
                $line->localtax1_type   = $objp->localtax1_type;
742
                $line->localtax2_type   = $objp->localtax2_type;
743
                $line->remise_percent   = $objp->remise_percent;
744
                //$line->fk_remise_except = $objp->fk_remise_except;
745
                $line->fk_product       = $objp->fk_product;
746
                $line->date_start_fill  = $objp->date_start_fill;
747
                $line->date_end_fill    = $objp->date_end_fill;
748
                $line->info_bits        = $objp->info_bits;
749
                $line->total_ht         = $objp->total_ht;
750
                $line->total_tva        = $objp->total_tva;
751
                $line->total_ttc        = $objp->total_ttc;
752
753
                $line->fk_product_fournisseur_price = $objp->fk_product_fournisseur_price;
754
                $line->fk_fournprice = $objp->fk_product_fournisseur_price; // For backward compatibility
755
756
                $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $objp->fk_product_fournisseur_price, $objp->pa_ht);
757
758
                $line->buyprice         = $marginInfos[0];
759
                $line->pa_ht            = $marginInfos[0]; // For backward compatibility
760
                $line->marge_tx         = $marginInfos[1];
761
                $line->marque_tx        = $marginInfos[2];
762
                $line->rang             = $objp->rang;
763
                $line->special_code     = $objp->special_code;
764
                $line->fk_unit          = $objp->fk_unit;
765
                $line->fk_contract_line = $objp->fk_contract_line;
766
767
                // Ne plus utiliser
768
                $line->price            = $objp->price;
769
                $line->remise           = $objp->remise;
770
771
                $line->fetch_optionals();
772
773
                // Multicurrency
774
                $line->fk_multicurrency = $objp->fk_multicurrency;
775
                $line->multicurrency_code = $objp->multicurrency_code;
776
                $line->multicurrency_subprice   = $objp->multicurrency_subprice;
777
                $line->multicurrency_total_ht   = $objp->multicurrency_total_ht;
778
                $line->multicurrency_total_tva  = $objp->multicurrency_total_tva;
779
                $line->multicurrency_total_ttc  = $objp->multicurrency_total_ttc;
780
781
                $this->lines[$i] = $line;
782
783
                $i++;
784
            }
785
786
            $this->db->free($result);
787
            return 1;
788
        } else {
789
            $this->error = $this->db->lasterror();
790
            return -3;
791
        }
792
    }
793
794
795
    /**
796
     *  Delete template invoice
797
     *
798
     *  @param      User    $user           User that delete.
799
     *  @param      int     $notrigger      1=Does not execute triggers, 0= execute triggers
800
     *  @param      int     $idwarehouse    Id warehouse to use for stock change.
801
     *  @return     int                     Return integer <0 if KO, >0 if OK
802
     */
803
    public function delete(User $user, $notrigger = 0, $idwarehouse = -1)
804
    {
805
        $rowid = $this->id;
806
807
        dol_syslog(get_class($this) . "::delete rowid=" . ((int) $rowid), LOG_DEBUG);
808
809
        $error = 0;
810
        $this->db->begin();
811
812
        $main = MAIN_DB_PREFIX . 'facturedet_rec';
813
        $ef = $main . "_extrafields";
814
815
        $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM " . $main . " WHERE fk_facture = " . ((int) $rowid) . ")";
816
        $sql = "DELETE FROM " . MAIN_DB_PREFIX . "facturedet_rec WHERE fk_facture = " . ((int) $rowid);
817
818
        if ($this->db->query($sqlef) && $this->db->query($sql)) {
819
            $sql = "DELETE FROM " . MAIN_DB_PREFIX . "facture_rec WHERE rowid = " . ((int) $rowid);
820
            dol_syslog($sql);
821
            if ($this->db->query($sql)) {
822
                // Delete linked object
823
                $res = $this->deleteObjectLinked();
824
                if ($res < 0) {
825
                    $error = -3;
826
                }
827
                // Delete extrafields
828
                $res = $this->deleteExtraFields();
829
                if ($res < 0) {
830
                    $error = -4;
831
                }
832
            } else {
833
                $this->error = $this->db->lasterror();
834
                $error = -1;
835
            }
836
        } else {
837
            $this->error = $this->db->lasterror();
838
            $error = -2;
839
        }
840
        if (!$error && !$notrigger) {
841
            // Call trigger
842
            $result = $this->call_trigger('BILLREC_DELETE', $user);
843
            if ($result < 0) {
844
                $error++;
845
            }
846
            // End call triggers
847
        }
848
        if (!$error) {
849
            $this->db->commit();
850
            return 1;
851
        } else {
852
            $this->db->rollback();
853
            return $error;
854
        }
855
    }
856
857
858
    /**
859
     *  Add a line to invoice
860
     *
861
     *  @param      string      $desc               Description de la ligne
862
     *  @param      double      $pu_ht              Prix unitaire HT (> 0 even for credit note)
863
     *  @param      double      $qty                Quantite
864
     *  @param      double      $txtva              Taux de tva force, sinon -1
865
     *  @param      double      $txlocaltax1        Local tax 1 rate (deprecated)
866
     *  @param      double      $txlocaltax2        Local tax 2 rate (deprecated)
867
     *  @param      int         $fk_product         Product/Service ID predefined
868
     *  @param      double      $remise_percent     Percentage discount of the line
869
     *  @param      string      $price_base_type    HT or TTC
870
     *  @param      int         $info_bits          VAT npr or not ?
871
     *  @param      int         $fk_remise_except   Id remise
872
     *  @param      double      $pu_ttc             Prix unitaire TTC (> 0 even for credit note)
873
     *  @param      int         $type               Type of line (0=product, 1=service)
874
     *  @param      int         $rang               Position of line
875
     *  @param      int         $special_code       Special code
876
     *  @param      string      $label              Label of the line
877
     *  @param      string      $fk_unit            Unit
878
     *  @param      double      $pu_ht_devise       Unit price in currency
879
     *  @param      int         $date_start_fill    1=Flag to fill start date when generating invoice
880
     *  @param      int         $date_end_fill      1=Flag to fill end date when generating invoice
881
     *  @param      int         $fk_fournprice      Supplier price id (to calculate margin) or ''
882
     *  @param      int         $pa_ht              Buying price of line (to calculate margin) or ''
883
     *  @param      int         $fk_parent_line     Id of parent line
884
     *  @return     int                             Return integer <0 if KO, Id of line if OK
885
     */
886
    public function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1 = 0, $txlocaltax2 = 0, $fk_product = 0, $remise_percent = 0, $price_base_type = 'HT', $info_bits = 0, $fk_remise_except = 0, $pu_ttc = 0, $type = 0, $rang = -1, $special_code = 0, $label = '', $fk_unit = null, $pu_ht_devise = 0, $date_start_fill = 0, $date_end_fill = 0, $fk_fournprice = null, $pa_ht = 0, $fk_parent_line = 0)
887
    {
888
        global $mysoc;
889
890
        $facid = $this->id;
891
892
        dol_syslog(get_class($this) . "::addline facid=$facid,desc=$desc,pu_ht=$pu_ht,qty=$qty,txtva=$txtva,txlocaltax1=$txlocaltax1,txlocaltax2=$txlocaltax2,fk_product=$fk_product,remise_percent=$remise_percent,info_bits=$info_bits,fk_remise_except=$fk_remise_except,price_base_type=$price_base_type,pu_ttc=$pu_ttc,type=$type,fk_unit=$fk_unit,pu_ht_devise=$pu_ht_devise,date_start_fill=$date_start_fill,date_end_fill=$date_end_fill,pa_ht=$pa_ht", LOG_DEBUG);
893
        include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php';
894
895
        // Check parameters
896
        if ($type < 0) {
897
            return -1;
898
        }
899
900
        $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
901
902
        // Clean vat code
903
        $reg = array();
904
        $vat_src_code = '';
905
        if (preg_match('/\((.*)\)/', (string) $txtva, $reg)) {
906
            $vat_src_code = $reg[1];
907
            $txtva = preg_replace('/\s*\(.*\)/', '', (string) $txtva); // Remove code from vatrate.
908
        }
909
910
911
        // Clean parameters
912
        $remise_percent = price2num($remise_percent);
913
        if (empty($remise_percent)) {
914
            $remise_percent = 0;
915
        }
916
        $qty = price2num($qty);
917
        $pu_ht = price2num($pu_ht);
918
        $pu_ttc = price2num($pu_ttc);
919
        if (!preg_match('/\((.*)\)/', $txtva)) {
920
            $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
921
        }
922
        $txlocaltax1 = price2num($txlocaltax1);
923
        $txlocaltax2 = price2num($txlocaltax2);
924
        if (empty($txtva)) {
925
            $txtva = 0;
926
        }
927
        if (empty($txlocaltax1)) {
928
            $txlocaltax1 = 0;
929
        }
930
        if (empty($txlocaltax2)) {
931
            $txlocaltax2 = 0;
932
        }
933
        if (empty($info_bits)) {
934
            $info_bits = 0;
935
        }
936
937
        if ($price_base_type == 'HT') {
938
            $pu = $pu_ht;
939
        } else {
940
            $pu = $pu_ttc;
941
        }
942
943
        // Calcul du total TTC et de la TVA pour la ligne a partir de
944
        // qty, pu, remise_percent et txtva
945
        // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
946
        // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
947
948
        $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $mysoc, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
949
        $total_ht  = $tabprice[0];
950
        $total_tva = $tabprice[1];
951
        $total_ttc = $tabprice[2];
952
        $total_localtax1 = $tabprice[9];
953
        $total_localtax2 = $tabprice[10];
954
        $pu_ht = $tabprice[3];
955
956
        // MultiCurrency
957
        $multicurrency_total_ht  = $tabprice[16];
958
        $multicurrency_total_tva = $tabprice[17];
959
        $multicurrency_total_ttc = $tabprice[18];
960
        $pu_ht_devise = $tabprice[19];
961
962
        $product_type = $type;
963
        if ($fk_product) {
964
            $product = new Product($this->db);
965
            $result = $product->fetch($fk_product);
966
            $product_type = $product->type;
967
        }
968
969
        if (empty($fk_parent_line) || $fk_parent_line < 0) {
970
            $fk_parent_line = 0;
971
        }
972
973
        // Rank to use
974
        $ranktouse = $rang;
975
        if ($ranktouse == -1) {
976
            $rangmax = $this->line_max(0);
977
            $ranktouse = $rangmax + 1;
978
        }
979
980
        $sql = "INSERT INTO " . MAIN_DB_PREFIX . "facturedet_rec (";
981
        $sql .= "fk_facture";
982
        $sql .= ", fk_parent_line";
983
        $sql .= ", label";
984
        $sql .= ", description";
985
        $sql .= ", price";
986
        $sql .= ", qty";
987
        $sql .= ", tva_tx";
988
        $sql .= ", vat_src_code";
989
        $sql .= ", localtax1_tx";
990
        $sql .= ", localtax1_type";
991
        $sql .= ", localtax2_tx";
992
        $sql .= ", localtax2_type";
993
        $sql .= ", fk_product";
994
        $sql .= ", product_type";
995
        $sql .= ", remise_percent";
996
        $sql .= ", subprice";
997
        $sql .= ", remise";
998
        $sql .= ", total_ht";
999
        $sql .= ", total_tva";
1000
        $sql .= ", total_localtax1";
1001
        $sql .= ", total_localtax2";
1002
        $sql .= ", total_ttc";
1003
        $sql .= ", date_start_fill";
1004
        $sql .= ", date_end_fill";
1005
        $sql .= ", fk_product_fournisseur_price";
1006
        $sql .= ", buy_price_ht";
1007
        $sql .= ", info_bits";
1008
        $sql .= ", rang";
1009
        $sql .= ", special_code";
1010
        $sql .= ", fk_unit";
1011
        $sql .= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
1012
        $sql .= ") VALUES (";
1013
        $sql .= " " . ((int) $facid);
1014
        $sql .= ", " . ($fk_parent_line > 0 ? ((int) $fk_parent_line) : "null");
1015
        $sql .= ", " . (!empty($label) ? "'" . $this->db->escape($label) . "'" : "null");
1016
        $sql .= ", '" . $this->db->escape($desc) . "'";
1017
        $sql .= ", " . price2num($pu_ht);
1018
        $sql .= ", " . price2num($qty);
1019
        $sql .= ", " . price2num($txtva);
1020
        $sql .= ", '" . $this->db->escape($vat_src_code) . "'";
1021
        $sql .= ", " . price2num($txlocaltax1);
1022
        $sql .= ", '" . $this->db->escape(isset($localtaxes_type[0]) ? $localtaxes_type[0] : '') . "'";
1023
        $sql .= ", " . price2num($txlocaltax2);
1024
        $sql .= ", '" . $this->db->escape(isset($localtaxes_type[2]) ? $localtaxes_type[2] : '') . "'";
1025
        $sql .= ", " . (!empty($fk_product) ? "'" . $this->db->escape($fk_product) . "'" : "null");
1026
        $sql .= ", " . ((int) $product_type);
1027
        $sql .= ", " . price2num($remise_percent);
1028
        $sql .= ", " . price2num($pu_ht);
1029
        $sql .= ", null";
1030
        $sql .= ", " . price2num($total_ht);
1031
        $sql .= ", " . price2num($total_tva);
1032
        $sql .= ", " . price2num($total_localtax1);
1033
        $sql .= ", " . price2num($total_localtax2);
1034
        $sql .= ", " . price2num($total_ttc);
1035
        $sql .= ", " . (int) $date_start_fill;
1036
        $sql .= ", " . (int) $date_end_fill;
1037
        $sql .= ", " . ($fk_fournprice > 0 ? $fk_fournprice : 'null');
1038
        $sql .= ", " . ($pa_ht ? price2num($pa_ht) : 0);
1039
        $sql .= ", " . ((int) $info_bits);
1040
        $sql .= ", " . ((int) $ranktouse);
1041
        $sql .= ", " . ((int) $special_code);
1042
        $sql .= ", " . ($fk_unit ? ((int) $fk_unit) : "null");
1043
        $sql .= ", " . (int) $this->fk_multicurrency;
1044
        $sql .= ", '" . $this->db->escape($this->multicurrency_code) . "'";
1045
        $sql .= ", " . price2num($pu_ht_devise, 'CU');
1046
        $sql .= ", " . price2num($multicurrency_total_ht, 'CT');
1047
        $sql .= ", " . price2num($multicurrency_total_tva, 'CT');
1048
        $sql .= ", " . price2num($multicurrency_total_ttc, 'CT');
1049
        $sql .= ")";
1050
1051
        dol_syslog(get_class($this) . "::addline", LOG_DEBUG);
1052
        if ($this->db->query($sql)) {
1053
            $lineId = $this->db->last_insert_id(MAIN_DB_PREFIX . "facturedet_rec");
1054
            $this->id = $facid;
1055
            $this->update_price(1);
1056
            return $lineId;
1057
        } else {
1058
            $this->error = $this->db->lasterror();
1059
            return -1;
1060
        }
1061
    }
1062
1063
    /**
1064
     *  Update a line to invoice
1065
     *
1066
     *  @param      int         $rowid              Id of line to update
1067
     *  @param      string      $desc               Description de la ligne
1068
     *  @param      double      $pu_ht              Prix unitaire HT (> 0 even for credit note)
1069
     *  @param      double      $qty                Quantite
1070
     *  @param      double      $txtva              Taux de tva force, sinon -1
1071
     *  @param      double      $txlocaltax1        Local tax 1 rate (deprecated)
1072
     *  @param      double      $txlocaltax2        Local tax 2 rate (deprecated)
1073
     *  @param      int         $fk_product         Product/Service ID predefined
1074
     *  @param      double      $remise_percent     Percentage discount of the line
1075
     *  @param      string      $price_base_type    HT or TTC
1076
     *  @param      int         $info_bits          Bits of type of lines
1077
     *  @param      int         $fk_remise_except   Id remise
1078
     *  @param      double      $pu_ttc             Prix unitaire TTC (> 0 even for credit note)
1079
     *  @param      int         $type               Type of line (0=product, 1=service)
1080
     *  @param      int         $rang               Position of line
1081
     *  @param      int         $special_code       Special code
1082
     *  @param      string      $label              Label of the line
1083
     *  @param      string      $fk_unit            Unit
1084
     *  @param      double      $pu_ht_devise       Unit price in currency
1085
     *  @param      int         $notrigger          disable line update trigger
1086
     *  @param      int         $date_start_fill    1=Flag to fill start date when generating invoice
1087
     *  @param      int         $date_end_fill      1=Flag to fill end date when generating invoice
1088
     *  @param      int         $fk_fournprice      Id of origin supplier price
1089
     *  @param      int         $pa_ht              Price (without tax) of product for margin calculation
1090
     *  @param      int         $fk_parent_line     Id of parent line
1091
     *  @return     int                             Return integer <0 if KO, Id of line if OK
1092
     */
1093
    public function updateline($rowid, $desc, $pu_ht, $qty, $txtva, $txlocaltax1 = 0, $txlocaltax2 = 0, $fk_product = 0, $remise_percent = 0, $price_base_type = 'HT', $info_bits = 0, $fk_remise_except = 0, $pu_ttc = 0, $type = 0, $rang = -1, $special_code = 0, $label = '', $fk_unit = null, $pu_ht_devise = 0, $notrigger = 0, $date_start_fill = 0, $date_end_fill = 0, $fk_fournprice = null, $pa_ht = 0, $fk_parent_line = 0)
1094
    {
1095
        global $mysoc;
1096
1097
        $facid = $this->id;
1098
1099
        dol_syslog(get_class($this) . "::updateline facid=" . $facid . " rowid=$rowid, desc=$desc, pu_ht=$pu_ht, qty=$qty, txtva=$txtva, txlocaltax1=$txlocaltax1, txlocaltax2=$txlocaltax2, fk_product=$fk_product, remise_percent=$remise_percent, info_bits=$info_bits, fk_remise_except=$fk_remise_except, price_base_type=$price_base_type, pu_ttc=$pu_ttc, type=$type, fk_unit=$fk_unit, pu_ht_devise=$pu_ht_devise", LOG_DEBUG);
1100
        include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php';
1101
1102
        // Clean parameters
1103
        if (empty($remise_percent)) {
1104
            $remise_percent = 0;
1105
        }
1106
1107
        // Check parameters
1108
        if ($type < 0) {
1109
            return -1;
1110
        }
1111
1112
        // Clean parameters
1113
        $remise_percent = price2num($remise_percent);
1114
        $qty = price2num($qty);
1115
        if (empty($info_bits)) {
1116
            $info_bits = 0;
1117
        }
1118
        $pu_ht          = price2num($pu_ht);
1119
        $pu_ttc         = price2num($pu_ttc);
1120
        $pu_ht_devise = price2num($pu_ht_devise);
1121
        if (!preg_match('/\((.*)\)/', (string) $txtva)) {
1122
            $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
1123
        }
1124
        $txlocaltax1    = price2num($txlocaltax1);
1125
        $txlocaltax2    = price2num($txlocaltax2);
1126
        if (empty($txlocaltax1)) {
1127
            $txlocaltax1 = 0;
1128
        }
1129
        if (empty($txlocaltax2)) {
1130
            $txlocaltax2 = 0;
1131
        }
1132
1133
        if (empty($this->multicurrency_subprice)) {
1134
            $this->multicurrency_subprice = 0;
1135
        }
1136
        if (empty($this->multicurrency_total_ht)) {
1137
            $this->multicurrency_total_ht = 0;
1138
        }
1139
        if (empty($this->multicurrency_total_tva)) {
1140
            $this->multicurrency_total_tva = 0;
1141
        }
1142
        if (empty($this->multicurrency_total_ttc)) {
1143
            $this->multicurrency_total_ttc = 0;
1144
        }
1145
1146
        if ($price_base_type == 'HT') {
1147
            $pu = $pu_ht;
1148
        } else {
1149
            $pu = $pu_ttc;
1150
        }
1151
1152
        // Calculate total with, without tax and tax from qty, pu, remise_percent and txtva
1153
        // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1154
        // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1155
1156
        $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
1157
1158
        // Clean vat code
1159
        $vat_src_code = '';
1160
        $reg = array();
1161
        if (preg_match('/\((.*)\)/', $txtva, $reg)) {
1162
            $vat_src_code = $reg[1];
1163
            $txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
1164
        }
1165
1166
        $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $mysoc, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
1167
1168
        $total_ht  = $tabprice[0];
1169
        $total_tva = $tabprice[1];
1170
        $total_ttc = $tabprice[2];
1171
        $total_localtax1 = $tabprice[9];
1172
        $total_localtax2 = $tabprice[10];
1173
        $pu_ht  = $tabprice[3];
1174
        $pu_tva = $tabprice[4];
1175
        $pu_ttc = $tabprice[5];
1176
1177
        // MultiCurrency
1178
        $multicurrency_total_ht  = $tabprice[16];
1179
        $multicurrency_total_tva = $tabprice[17];
1180
        $multicurrency_total_ttc = $tabprice[18];
1181
        $pu_ht_devise = $tabprice[19];
1182
1183
        $product_type = $type;
1184
        if ($fk_product) {
1185
            $product = new Product($this->db);
1186
            $result = $product->fetch($fk_product);
1187
            $product_type = $product->type;
1188
        }
1189
1190
        if (empty($fk_parent_line) || $fk_parent_line < 0) {
1191
            $fk_parent_line = 0;
1192
        }
1193
1194
        $sql = "UPDATE " . MAIN_DB_PREFIX . "facturedet_rec SET ";
1195
        $sql .= "fk_facture = " . ((int) $facid);
1196
        $sql .= ", fk_parent_line = " . ($fk_parent_line > 0 ? ((int) $fk_parent_line) : "null");
1197
        $sql .= ", label=" . (!empty($label) ? "'" . $this->db->escape($label) . "'" : "null");
1198
        $sql .= ", description='" . $this->db->escape($desc) . "'";
1199
        $sql .= ", price=" . price2num($pu_ht);
1200
        $sql .= ", qty=" . price2num($qty);
1201
        $sql .= ", tva_tx=" . price2num($txtva);
1202
        $sql .= ", vat_src_code='" . $this->db->escape($vat_src_code) . "'";
1203
        $sql .= ", localtax1_tx=" . ((float) $txlocaltax1);
1204
        $sql .= ", localtax1_type='" . $this->db->escape($localtaxes_type[0]) . "'";
1205
        $sql .= ", localtax2_tx=" . ((float) $txlocaltax2);
1206
        $sql .= ", localtax2_type='" . $this->db->escape($localtaxes_type[2]) . "'";
1207
        $sql .= ", fk_product=" . (!empty($fk_product) ? "'" . $this->db->escape($fk_product) . "'" : "null");
1208
        $sql .= ", product_type=" . ((int) $product_type);
1209
        $sql .= ", remise_percent='" . price2num($remise_percent) . "'";
1210
        $sql .= ", subprice='" . price2num($pu_ht) . "'";
1211
        $sql .= ", total_ht='" . price2num($total_ht) . "'";
1212
        $sql .= ", total_tva='" . price2num($total_tva) . "'";
1213
        $sql .= ", total_localtax1='" . price2num($total_localtax1) . "'";
1214
        $sql .= ", total_localtax2='" . price2num($total_localtax2) . "'";
1215
        $sql .= ", total_ttc='" . price2num($total_ttc) . "'";
1216
        $sql .= ", date_start_fill=" . ((int) $date_start_fill);
1217
        $sql .= ", date_end_fill=" . ((int) $date_end_fill);
1218
        $sql .= ", fk_product_fournisseur_price=" . ($fk_fournprice > 0 ? $fk_fournprice : 'null');
1219
        $sql .= ", buy_price_ht=" . ($pa_ht ? price2num($pa_ht) : 0);
1220
        $sql .= ", info_bits=" . ((int) $info_bits);
1221
        $sql .= ", rang=" . ((int) $rang);
1222
        $sql .= ", special_code=" . ((int) $special_code);
1223
        $sql .= ", fk_unit=" . ($fk_unit ? "'" . $this->db->escape($fk_unit) . "'" : "null");
1224
        $sql .= ', multicurrency_subprice = ' . price2num($pu_ht_devise);
1225
        $sql .= ', multicurrency_total_ht = ' . price2num($multicurrency_total_ht);
1226
        $sql .= ', multicurrency_total_tva = ' . price2num($multicurrency_total_tva);
1227
        $sql .= ', multicurrency_total_ttc = ' . price2num($multicurrency_total_ttc);
1228
        $sql .= " WHERE rowid = " . ((int) $rowid);
1229
1230
        dol_syslog(get_class($this) . "::updateline", LOG_DEBUG);
1231
        if ($this->db->query($sql)) {
1232
            $this->id = $facid;
1233
            $this->update_price(1);
1234
            return 1;
1235
        } else {
1236
            $this->error = $this->db->lasterror();
1237
            return -1;
1238
        }
1239
    }
1240
1241
1242
    /**
1243
     * Return the next date of
1244
     *
1245
     * @return  int|false   false if KO, timestamp if OK
1246
     */
1247
    public function getNextDate()
1248
    {
1249
        if (empty($this->date_when)) {
1250
            return false;
1251
        }
1252
        return dol_time_plus_duree($this->date_when, $this->frequency, $this->unit_frequency);
1253
    }
1254
1255
    /**
1256
     * Return if maximum number of generation is reached
1257
     *
1258
     * @return  boolean         False by default, True if maximum number of generation is reached
1259
     */
1260
    public function isMaxNbGenReached()
1261
    {
1262
        $ret = false;
1263
        if ($this->nb_gen_max > 0 && ($this->nb_gen_done >= $this->nb_gen_max)) {
1264
            $ret = true;
1265
        }
1266
        return $ret;
1267
    }
1268
1269
    /**
1270
     * Format string to output with by striking the string if max number of generation was reached
1271
     *
1272
     * @param   string      $ret    Default value to output
1273
     * @return  string              html formatted string
1274
     */
1275
    public function strikeIfMaxNbGenReached($ret)
1276
    {
1277
        return $this->isMaxNbGenReached() ? '<strike>' . $ret . '</strike>' : $ret;
1278
    }
1279
1280
    /**
1281
     *  Create all recurrents invoices (for all entities if multicompany is used).
1282
     *  A result may also be provided into this->output.
1283
     *
1284
     *  WARNING: This method change temporarily context $conf->entity to be in correct context for each recurring invoice found.
1285
     *
1286
     *  @param  int     $restrictioninvoiceid       0=All qualified template invoices found. > 0 = restrict action on invoice ID
1287
     *  @param  int     $forcevalidation        1=Force validation of invoice whatever is template auto_validate flag.
1288
     *  @param      int     $notrigger          Disable the trigger
1289
     *  @return int                             0 if OK, < 0 if KO (this function is used also by cron so only 0 is OK)
1290
     */
1291
    public function createRecurringInvoices($restrictioninvoiceid = 0, $forcevalidation = 0, $notrigger = 0)
1292
    {
1293
        global $conf, $langs, $db, $user, $hookmanager;
1294
1295
        $error = 0;
1296
        $nb_create = 0;
1297
1298
        // Load translation files required by the page
1299
        $langs->loadLangs(array("main", "bills"));
1300
1301
        $now = dol_now();
1302
        $tmparray = dol_getdate($now);
1303
        $today = dol_mktime(23, 59, 59, $tmparray['mon'], $tmparray['mday'], $tmparray['year']); // Today is last second of current day
1304
1305
        $this->output = '';
1306
1307
        dol_syslog("createRecurringInvoices restrictioninvoiceid=" . $restrictioninvoiceid . " forcevalidation=" . $forcevalidation);
1308
1309
        $sql = 'SELECT rowid FROM ' . MAIN_DB_PREFIX . 'facture_rec';
1310
        $sql .= ' WHERE frequency > 0'; // A recurring invoice is an invoice with a frequency
1311
        $sql .= " AND (date_when IS NULL OR date_when <= '" . $this->db->idate($today) . "')";
1312
        $sql .= ' AND (nb_gen_done < nb_gen_max OR nb_gen_max = 0)';
1313
        $sql .= ' AND suspended = 0';
1314
        $sql .= ' AND entity = ' . $conf->entity; // MUST STAY = $conf->entity here
1315
        if ($restrictioninvoiceid > 0) {
1316
            $sql .= ' AND rowid = ' . ((int) $restrictioninvoiceid);
1317
        }
1318
        $sql .= $this->db->order('entity', 'ASC');
1319
        //print $sql;exit;
1320
        $parameters = array(
1321
            'restrictioninvoiceid' => $restrictioninvoiceid,
1322
            'forcevalidation' => $forcevalidation,
1323
        );
1324
        $reshook = $hookmanager->executeHooks('beforeCreationOfRecurringInvoices', $parameters, $sql); // note that $sql might be modified by hooks
1325
1326
        $resql = $this->db->query($sql);
1327
        if ($resql) {
1328
            $i = 0;
1329
            $num = $this->db->num_rows($resql);
1330
1331
            if ($num) {
1332
                $this->output .= $langs->trans("FoundXQualifiedRecurringInvoiceTemplate", $num) . "\n";
1333
            } else {
1334
                $this->output .= $langs->trans("NoQualifiedRecurringInvoiceTemplateFound");
1335
            }
1336
1337
            $saventity = $conf->entity;
1338
1339
            while ($i < $num) {     // Loop on each template invoice. If $num = 0, test is false at first pass.
1340
                $line = $this->db->fetch_object($resql);
1341
1342
                $this->db->begin();
1343
1344
                $invoiceidgenerated = 0;
1345
1346
                $facture = null;
1347
                $facturerec = new FactureRec($this->db);
1348
                $facturerec->fetch($line->rowid);
1349
1350
                if ($facturerec->id > 0) {
1351
                    // Set entity context
1352
                    $conf->entity = $facturerec->entity;
1353
1354
                    dol_syslog("createRecurringInvoices Process invoice template id=" . $facturerec->id . ", ref=" . $facturerec->ref . ", entity=" . $facturerec->entity);
1355
1356
                    $facture = new Facture($this->db);
1357
                    $facture->fac_rec = $facturerec->id; // We will create $facture from this recurring invoice
1358
                    $facture->fk_fac_rec_source = $facturerec->id; // We will create $facture from this recurring invoice
1359
1360
                    $facture->type = self::TYPE_STANDARD;
1361
                    $facture->subtype = $facturerec->subtype;
1362
                    $facture->statut = self::STATUS_DRAFT;  // deprecated
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$statut has been deprecated: Use $status instead. ( Ignorable by Annotation )

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

1362
                    /** @scrutinizer ignore-deprecated */ $facture->statut = self::STATUS_DRAFT;  // 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...
1363
                    $facture->status = self::STATUS_DRAFT;
1364
                    $facture->date = (empty($facturerec->date_when) ? $now : $facturerec->date_when); // We could also use dol_now here but we prefer date_when so invoice has real date when we would like even if we generate later.
1365
                    $facture->socid = $facturerec->socid;
1366
                    if (!empty($facturerec->fk_multicurrency)) {
1367
                        $facture->fk_multicurrency = $facturerec->fk_multicurrency;
1368
                        $facture->multicurrency_code = $facturerec->multicurrency_code;
1369
                        $facture->multicurrency_tx = $facturerec->multicurrency_tx;
1370
                    }
1371
1372
                    $invoiceidgenerated = $facture->create($user);
1373
                    if ($invoiceidgenerated <= 0) {
1374
                        $this->errors = $facture->errors;
1375
                        $this->error = $facture->error;
1376
                        $error++;
1377
                    }
1378
1379
1380
                    if (!$error && ($facturerec->auto_validate || $forcevalidation)) {
1381
                        $result = $facture->validate($user);
1382
                        if ($result <= 0) {
1383
                            $this->errors = $facture->errors;
1384
                            $this->error = $facture->error;
1385
                            $error++;
1386
                        }
1387
                    }
1388
                    if (!$error && $facturerec->generate_pdf) {
1389
                        // We refresh the object in order to have all necessary data (like date_lim_reglement)
1390
                        $facture->fetch($facture->id);
1391
                        $result = $facture->generateDocument($facturerec->model_pdf, $langs);
1392
                        if ($result <= 0) {
1393
                            $this->errors = $facture->errors;
1394
                            $this->error = $facture->error;
1395
                            $error++;
1396
                        }
1397
                    }
1398
                } else {
1399
                    $error++;
1400
                    $this->error = "Failed to load invoice template with id=" . $line->rowid . ", entity=" . $conf->entity . "\n";
1401
                    $this->errors[] = "Failed to load invoice template with id=" . $line->rowid . ", entity=" . $conf->entity;
1402
                    dol_syslog("createRecurringInvoices Failed to load invoice template with id=" . $line->rowid . ", entity=" . $conf->entity);
1403
                }
1404
1405
                if (!$error && $invoiceidgenerated >= 0) {
1406
                    $this->db->commit("createRecurringInvoices Process invoice template id=" . $facturerec->id . ", ref=" . $facturerec->ref);
1407
                    dol_syslog("createRecurringInvoices Process invoice template " . $facturerec->ref . " is finished with a success generation");
1408
                    $nb_create++;
1409
                    $this->output .= $langs->trans("InvoiceGeneratedFromTemplate", $facture->ref, $facturerec->ref) . "\n";
1410
                } else {
1411
                    $this->db->rollback("createRecurringInvoices Process invoice template id=" . $facturerec->id . ", ref=" . $facturerec->ref);
1412
                }
1413
1414
                $parameters = array(
1415
                    'cpt'        => $i,
1416
                    'total'      => $num,
1417
                    'errorCount' => $error,
1418
                    'invoiceidgenerated' => $invoiceidgenerated,
1419
                    'facturerec' => $facturerec, // it's an object which PHP passes by "reference", so modifiable by hooks.
1420
                    'this'       => $this, // it's an object which PHP passes by "reference", so modifiable by hooks.
1421
                );
1422
                $reshook = $hookmanager->executeHooks('afterCreationOfRecurringInvoice', $parameters, $facture); // note: $facture can be modified by hooks (warning: $facture can be null)
1423
1424
                $i++;
1425
            }
1426
1427
            $conf->entity = $saventity; // Restore entity context
1428
        } else {
1429
            dol_print_error($this->db);
1430
        }
1431
1432
        $this->output = trim($this->output);
1433
1434
        return $error ? $error : 0;
1435
    }
1436
1437
    /**
1438
     *  Return clicable name (with picto eventually)
1439
     *
1440
     * @param   int     $withpicto                  Add picto into link
1441
     * @param  string   $option                     Where point the link
1442
     * @param  int      $max                        Maxlength of ref
1443
     * @param  int      $short                      1=Return just URL
1444
     * @param  string   $moretitle                  Add more text to title tooltip
1445
     * @param   int     $notooltip                  1=Disable tooltip
1446
     * @param  int      $save_lastsearch_value      -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
1447
     * @return string                               String with URL
1448
     */
1449
    public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $moretitle = '', $notooltip = 0, $save_lastsearch_value = -1)
1450
    {
1451
        global $langs, $hookmanager;
1452
1453
        $result = '';
1454
1455
        $label = img_picto('', $this->picto) . ' <u class="paddingrightonly">' . $langs->trans("RepeatableInvoice") . '</u>';
1456
        if (!empty($this->ref)) {
1457
            $label .= '<br><b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
1458
        }
1459
        if ($this->frequency > 0) {
1460
            $label .= '<br><b>' . $langs->trans('Frequency') . ':</b> ' . $langs->trans('FrequencyPer_' . $this->unit_frequency, $this->frequency);
1461
        }
1462
        if (!empty($this->date_last_gen)) {
1463
            $label .= '<br><b>' . $langs->trans('DateLastGeneration') . ':</b> ' . dol_print_date($this->date_last_gen, 'dayhour');
1464
        }
1465
        if ($this->frequency > 0) {
1466
            if (!empty($this->date_when)) {
1467
                $label .= '<br><b>' . $langs->trans('NextDateToExecution') . ':</b> ';
1468
                $label .= (empty($this->suspended) ? '' : '<strike>') . dol_print_date($this->date_when, 'day') . (empty($this->suspended) ? '' : '</strike>'); // No hour for this property
1469
                if (!empty($this->suspended)) {
1470
                    $label .= ' (' . $langs->trans("Disabled") . ')';
1471
                }
1472
            }
1473
        }
1474
1475
        $url = constant('BASE_URL') . '/compta/facture/card-rec.php?facid=' . $this->id;
1476
1477
        if ($short) {
1478
            return $url;
1479
        }
1480
1481
        if ($option != 'nolink') {
1482
            // Add param to save lastsearch_values or not
1483
            $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1484
            if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1485
                $add_save_lastsearch_values = 1;
1486
            }
1487
            if ($add_save_lastsearch_values) {
1488
                $url .= '&save_lastsearch_values=1';
1489
            }
1490
        }
1491
1492
        $linkstart = '<a href="' . $url . '" title="' . dol_escape_htmltag($label, 1) . '" class="classfortooltip">';
1493
        $linkend = '</a>';
1494
1495
        $result .= $linkstart;
1496
        if ($withpicto) {
1497
            $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="' . (($withpicto != 2) ? 'paddingright ' : '') . 'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
1498
        }
1499
        if ($withpicto != 2) {
1500
            $result .= $this->ref;
1501
        }
1502
        $result .= $linkend;
1503
        global $action;
1504
        $hookmanager->initHooks(array($this->element . 'dao'));
1505
        $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1506
        $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1507
        if ($reshook > 0) {
1508
            $result = $hookmanager->resPrint;
1509
        } else {
1510
            $result .= $hookmanager->resPrint;
1511
        }
1512
        return $result;
1513
    }
1514
1515
    /**
1516
     *  Return label of object status
1517
     *
1518
     *  @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
1519
     *  @param      integer $alreadypaid    Not used on recurring invoices
1520
     *  @return     string                  Label of status
1521
     */
1522
    public function getLibStatut($mode = 0, $alreadypaid = -1)
1523
    {
1524
        return $this->LibStatut($this->frequency ? 1 : 0, $this->suspended, $mode, $alreadypaid, empty($this->type) ? 0 : $this->type);
1525
    }
1526
1527
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1528
    /**
1529
     *  Return label of a status
1530
     *
1531
     *  @param      int     $recur          Is it a recurring invoice ?
1532
     *  @param      int     $status         Id status (suspended or not)
1533
     *  @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
1534
     *  @param      integer $alreadypaid    Not used for recurring invoices
1535
     *  @param      int     $type           Type invoice
1536
     *  @return     string                  Label of status
1537
     */
1538
    public function LibStatut($recur, $status, $mode = 0, $alreadypaid = -1, $type = 0)
1539
    {
1540
		// phpcs:enable
1541
        global $langs;
1542
        $langs->load('bills');
1543
1544
        $labelStatus = $langs->transnoentitiesnoconv('Active');
1545
        $statusType = 'status0';
1546
1547
        //print "$recur,$status,$mode,$alreadypaid,$type";
1548
        if ($mode == 0) {
1549
            if ($recur) {
1550
                if ($status == self::STATUS_SUSPENDED) {
1551
                    $labelStatus = $langs->transnoentitiesnoconv('Disabled');
1552
                } else {
1553
                    $labelStatus = $langs->transnoentitiesnoconv('Active');
1554
                }
1555
            } else {
1556
                if ($status == self::STATUS_SUSPENDED) {
1557
                    $labelStatus = $langs->transnoentitiesnoconv('Disabled');
1558
                } else {
1559
                    $labelStatus = $langs->transnoentitiesnoconv("Draft");
1560
                }
1561
            }
1562
        } elseif ($mode == 1) {
1563
            $prefix = 'Short';
1564
            if ($recur) {
1565
                if ($status == self::STATUS_SUSPENDED) {
1566
                    $labelStatus = $langs->transnoentitiesnoconv('Disabled');
1567
                } else {
1568
                    $labelStatus = $langs->transnoentitiesnoconv('Active');
1569
                }
1570
            } else {
1571
                if ($status == self::STATUS_SUSPENDED) {
1572
                    $labelStatus = $langs->transnoentitiesnoconv('Disabled');
1573
                } else {
1574
                    $labelStatus = $langs->transnoentitiesnoconv("Draft");
1575
                }
1576
            }
1577
        } elseif ($mode == 2) {
1578
            if ($recur) {
1579
                if ($status == self::STATUS_SUSPENDED) {
1580
                    $statusType = 'status6';
1581
                    $labelStatus = $langs->transnoentitiesnoconv('Disabled');
1582
                } else {
1583
                    $statusType = 'status4';
1584
                    $labelStatus = $langs->transnoentitiesnoconv('Active');
1585
                }
1586
            } else {
1587
                if ($status == self::STATUS_SUSPENDED) {
1588
                    $statusType = 'status6';
1589
                    $labelStatus = $langs->transnoentitiesnoconv('Disabled');
1590
                } else {
1591
                    $statusType = 'status0';
1592
                    $labelStatus = $langs->transnoentitiesnoconv('Draft');
1593
                }
1594
            }
1595
        } elseif ($mode == 3) {
1596
            if ($recur) {
1597
                $prefix = 'Short';
1598
                if ($status == self::STATUS_SUSPENDED) {
1599
                    $statusType = 'status6';
1600
                    $labelStatus = $langs->transnoentitiesnoconv('Disabled');
1601
                } else {
1602
                    $statusType = 'status4';
1603
                    $labelStatus = $langs->transnoentitiesnoconv('Active');
1604
                }
1605
            } else {
1606
                if ($status == self::STATUS_SUSPENDED) {
1607
                    $statusType = 'status6';
1608
                    $labelStatus = $langs->transnoentitiesnoconv('Disabled');
1609
                } else {
1610
                    $statusType = 'status0';
1611
                    $labelStatus = $langs->transnoentitiesnoconv('Draft');
1612
                }
1613
            }
1614
        } elseif ($mode == 4) {
1615
            $prefix = '';
1616
            if ($recur) {
1617
                if ($status == self::STATUS_SUSPENDED) {
1618
                    $statusType = 'status6';
1619
                    $labelStatus = $langs->transnoentitiesnoconv('Disabled');
1620
                } else {
1621
                    $statusType = 'status4';
1622
                    $labelStatus = $langs->transnoentitiesnoconv('Active');
1623
                }
1624
            } else {
1625
                if ($status == self::STATUS_SUSPENDED) {
1626
                    $statusType = 'status6';
1627
                    $labelStatus = $langs->transnoentitiesnoconv('Disabled');
1628
                } else {
1629
                    $statusType = 'status0';
1630
                    $labelStatus = $langs->transnoentitiesnoconv('Draft');
1631
                }
1632
            }
1633
        } elseif ($mode == 5 || $mode == 6) {
1634
            $prefix = '';
1635
            if ($mode == 5) {
1636
                $prefix = 'Short';
1637
            }
1638
            if ($recur) {
1639
                if ($status == self::STATUS_SUSPENDED) {
1640
                    $statusType = 'status6';
1641
                    $labelStatus = $langs->transnoentitiesnoconv('Disabled');
1642
                } else {
1643
                    $statusType = 'status4';
1644
                    $labelStatus = $langs->transnoentitiesnoconv('Active');
1645
                }
1646
            } else {
1647
                if ($status == self::STATUS_SUSPENDED) {
1648
                    $statusType = 'status6';
1649
                    $labelStatus = $langs->transnoentitiesnoconv('Disabled');
1650
                } else {
1651
                    $statusType = 'status0';
1652
                    $labelStatus = $langs->transnoentitiesnoconv('Draft');
1653
                }
1654
            }
1655
        }
1656
1657
        $labelStatusShort = $labelStatus;
1658
1659
        return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
1660
    }
1661
1662
    /**
1663
     * Return next reference of invoice not already used (or last reference)
1664
     *
1665
     * @param    Societe    $soc        Thirdparty object
1666
     * @param    string     $mode       'next' for next value or 'last' for last value
1667
     * @return   string                 free ref or last ref
1668
     */
1669
    public function getNextNumRef($soc, $mode = 'next')
1670
    {
1671
        // Not used for recurring invoices
1672
        return '';
1673
    }
1674
1675
    /**
1676
     *  Load miscellaneous information for tab "Info"
1677
     *
1678
     *  @param  int     $id     Id of object to load
1679
     *  @return void
1680
     */
1681
    public function info($id)
1682
    {
1683
        $sql = 'SELECT c.rowid, datec, tms as datem,';
1684
        $sql .= ' fk_user_author, fk_user_modif';
1685
        $sql .= ' FROM ' . MAIN_DB_PREFIX . 'facture_rec as c';
1686
        $sql .= ' WHERE c.rowid = ' . ((int) $id);
1687
1688
        $result = $this->db->query($sql);
1689
        if ($result) {
1690
            if ($this->db->num_rows($result)) {
1691
                $obj = $this->db->fetch_object($result);
1692
1693
                $this->id = $obj->rowid;
1694
1695
                $this->user_creation_id = $obj->fk_user_author;
1696
                $this->user_modification_id = $obj->fk_user_modif;
1697
                $this->date_creation     = $this->db->jdate($obj->datec);
1698
                $this->date_modification = $this->db->jdate($obj->datem);
1699
            }
1700
            $this->db->free($result);
1701
        } else {
1702
            dol_print_error($this->db);
1703
        }
1704
    }
1705
1706
    /**
1707
     *  Initialise an instance with random values.
1708
     *  Used to build previews or test instances.
1709
     *  id must be 0 if object instance is a specimen.
1710
     *
1711
     *  @param  string      $option     ''=Create a specimen invoice with lines, 'nolines'=No lines
1712
     *  @return int
1713
     */
1714
    public function initAsSpecimen($option = '')
1715
    {
1716
        global $langs;
1717
1718
        $now = dol_now();
1719
        $arraynow = dol_getdate($now);
1720
        $nownotime = dol_mktime(0, 0, 0, $arraynow['mon'], $arraynow['mday'], $arraynow['year']);
1721
1722
        // Load array of products prodids
1723
        $num_prods = 0;
1724
        $prodids = array();
1725
1726
        $sql = "SELECT rowid";
1727
        $sql .= " FROM " . MAIN_DB_PREFIX . "product";
1728
        $sql .= " WHERE entity IN (" . getEntity('product') . ")";
1729
        $sql .= $this->db->plimit(100);
1730
1731
        $resql = $this->db->query($sql);
1732
        if ($resql) {
1733
            $num_prods = $this->db->num_rows($resql);
1734
            $i = 0;
1735
            while ($i < $num_prods) {
1736
                $i++;
1737
                $row = $this->db->fetch_row($resql);
1738
                $prodids[$i] = $row[0];
1739
            }
1740
        }
1741
1742
        // Initialize parameters
1743
        $this->id = 0;
1744
        $this->ref = 'SPECIMEN';
1745
        $this->title = 'SPECIMEN';
1746
        $this->specimen = 1;
1747
        $this->socid = 1;
1748
        $this->date = $nownotime;
1749
        $this->date_lim_reglement = $nownotime + 3600 * 24 * 30;
1750
        $this->cond_reglement_id   = 1;
1751
        $this->cond_reglement_code = 'RECEP';
1752
        $this->date_lim_reglement = $this->calculate_date_lim_reglement();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->calculate_date_lim_reglement() can also be of type string. However, the property $date_lim_reglement is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1753
        $this->mode_reglement_id   = 0; // Not forced to show payment mode CHQ + VIR
1754
        $this->mode_reglement_code = ''; // Not forced to show payment mode CHQ + VIR
1755
        $this->note_public = 'This is a comment (public)';
1756
        $this->note_private = 'This is a comment (private)';
1757
        $this->note = 'This is a comment (private)';
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$note has been deprecated: Use $note_private instead. ( Ignorable by Annotation )

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

1757
        /** @scrutinizer ignore-deprecated */ $this->note = 'This is a comment (private)';

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...
1758
        $this->fk_incoterms = 0;
1759
        $this->location_incoterms = '';
1760
1761
        if (empty($option) || $option != 'nolines') {
1762
            // Lines
1763
            $nbp = 5;
1764
            $xnbp = 0;
1765
            while ($xnbp < $nbp) {
1766
                $line = new FactureLigne($this->db);
1767
                $line->desc = $langs->trans("Description") . " " . $xnbp;
1768
                $line->qty = 1;
1769
                $line->subprice = 100;
1770
                $line->tva_tx = 19.6;
1771
                $line->localtax1_tx = 0;
1772
                $line->localtax2_tx = 0;
1773
                $line->remise_percent = 0;
1774
                if ($xnbp == 1) {        // Qty is negative (product line)
1775
                    $prodid = mt_rand(1, $num_prods);
1776
                    $line->fk_product = $prodids[$prodid];
1777
                    $line->qty = -1;
1778
                    $line->total_ht = -100;
1779
                    $line->total_ttc = -119.6;
1780
                    $line->total_tva = -19.6;
1781
                } elseif ($xnbp == 2) {    // UP is negative (free line)
1782
                    $line->subprice = -100;
1783
                    $line->total_ht = -100;
1784
                    $line->total_ttc = -119.6;
1785
                    $line->total_tva = -19.6;
1786
                    $line->remise_percent = 0;
1787
                } elseif ($xnbp == 3) {    // Discount is 50% (product line)
1788
                    $prodid = mt_rand(1, $num_prods);
1789
                    $line->fk_product = $prodids[$prodid];
1790
                    $line->total_ht = 50;
1791
                    $line->total_ttc = 59.8;
1792
                    $line->total_tva = 9.8;
1793
                    $line->remise_percent = 50;
1794
                } else { // (product line)
1795
                    $prodid = mt_rand(1, $num_prods);
1796
                    $line->fk_product = $prodids[$prodid];
1797
                    $line->total_ht = 100;
1798
                    $line->total_ttc = 119.6;
1799
                    $line->total_tva = 19.6;
1800
                    $line->remise_percent = 0;
1801
                }
1802
1803
                $this->lines[$xnbp] = $line;
1804
                $xnbp++;
1805
1806
                $this->total_ht       += $line->total_ht;
1807
                $this->total_tva      += $line->total_tva;
1808
                $this->total_ttc      += $line->total_ttc;
1809
            }
1810
            $this->revenuestamp = 0;
1811
1812
            // Add a line "offered"
1813
            $line = new FactureLigne($this->db);
1814
            $line->desc = $langs->trans("Description") . " (offered line)";
1815
            $line->qty = 1;
1816
            $line->subprice = 100;
1817
            $line->tva_tx = 19.6;
1818
            $line->localtax1_tx = 0;
1819
            $line->localtax2_tx = 0;
1820
            $line->remise_percent = 100;
1821
            $line->total_ht = 0;
1822
            $line->total_ttc = 0; // 90 * 1.196
1823
            $line->total_tva = 0;
1824
            $prodid = mt_rand(1, $num_prods);
1825
            $line->fk_product = $prodids[$prodid];
1826
1827
            $this->lines[$xnbp] = $line;
1828
            $xnbp++;
1829
        }
1830
1831
        $this->usenewprice = 0;
1832
1833
        return 1;
1834
    }
1835
1836
    /**
1837
     * Function used to replace a thirdparty id with another one.
1838
     *
1839
     * @param   DoliDB  $dbs        Database handler, because function is static we name it $dbs not $db to avoid breaking coding test
1840
     * @param   int     $origin_id  Old thirdparty id
1841
     * @param   int     $dest_id    New thirdparty id
1842
     * @return  bool
1843
     */
1844
    public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
1845
    {
1846
        $tables = array(
1847
            'facture_rec'
1848
        );
1849
1850
        return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
1851
    }
1852
1853
    /**
1854
     * Function used to replace a product id with another one.
1855
     *
1856
     * @param   DoliDB  $dbs        Database handler, because function is static we name it $dbs not $db to avoid breaking coding test
1857
     * @param   int     $origin_id  Old product id
1858
     * @param   int     $dest_id    New product id
1859
     * @return  bool
1860
     */
1861
    public static function replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
1862
    {
1863
        $tables = array(
1864
            'facturedet_rec'
1865
        );
1866
1867
        return CommonObject::commonReplaceProduct($dbs, $origin_id, $dest_id, $tables);
1868
    }
1869
1870
    /**
1871
     *  Update frequency and unit
1872
     *
1873
     *  @param      int     $frequency      value of frequency
1874
     *  @param      string  $unit           unit of frequency  (d, m, y)
1875
     *  @param      int     $notrigger      Disable the trigger
1876
     *  @return     int                     Return integer <0 if KO, >0 if OK
1877
     */
1878
    public function setFrequencyAndUnit($frequency, $unit, $notrigger = 0)
1879
    {
1880
        global $user;
1881
1882
        if (!$this->table_element) {
1883
            dol_syslog(get_class($this) . "::setFrequencyAndUnit was called on object with property table_element not defined", LOG_ERR);
1884
            return -1;
1885
        }
1886
1887
        if (!empty($frequency) && empty($unit)) {
1888
            dol_syslog(get_class($this) . "::setFrequencyAndUnit was called on object with params frequency defined but unit not defined", LOG_ERR);
1889
            return -2;
1890
        }
1891
1892
        $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element;
1893
        $sql .= ' SET frequency = ' . ($frequency ? $this->db->escape($frequency) : 'null');
1894
        if (!empty($unit)) {
1895
            $sql .= ', unit_frequency = \'' . $this->db->escape($unit) . '\'';
1896
        }
1897
        $sql .= " WHERE rowid = " . ((int) $this->id);
1898
1899
        dol_syslog(get_class($this) . "::setFrequencyAndUnit", LOG_DEBUG);
1900
        if ($this->db->query($sql)) {
1901
            $this->frequency = $frequency;
1902
            if (!empty($unit)) {
1903
                $this->unit_frequency = $unit;
1904
            }
1905
1906
            if (!$notrigger) {
1907
                // Call trigger
1908
                $result = $this->call_trigger('BILLREC_MODIFY', $user);
1909
                if ($result < 0) {
1910
                    return $result;
1911
                }
1912
                // End call triggers
1913
            }
1914
1915
            return 1;
1916
        } else {
1917
            dol_print_error($this->db);
1918
            return -1;
1919
        }
1920
    }
1921
1922
    /**
1923
     *  Update the next date of execution
1924
     *
1925
     *  @param      datetime    $date                   date of execution
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\Compta\Classes\datetime was not found. Did you mean datetime? If so, make sure to prefix the type with \.
Loading history...
1926
     *  @param      int         $increment_nb_gen_done  0 do nothing more, >0 increment nb_gen_done
1927
     *  @param      int         $notrigger              Disable the trigger
1928
     *  @return     int                                 Return integer <0 if KO, >0 if OK
1929
     */
1930
    public function setNextDate($date, $increment_nb_gen_done = 0, $notrigger = 0)
1931
    {
1932
        global $user;
1933
1934
        if (!$this->table_element) {
1935
            dol_syslog(get_class($this) . "::setNextDate was called on object with property table_element not defined", LOG_ERR);
1936
            return -1;
1937
        }
1938
        $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element;
1939
        $sql .= " SET date_when = " . ($date ? "'" . $this->db->idate($date) . "'" : "null");
1940
        if ($increment_nb_gen_done > 0) {
1941
            $sql .= ', nb_gen_done = nb_gen_done + 1';
1942
        }
1943
        $sql .= " WHERE rowid = " . ((int) $this->id);
1944
1945
        dol_syslog(get_class($this) . "::setNextDate", LOG_DEBUG);
1946
        if ($this->db->query($sql)) {
1947
            $this->date_when = $date;
1948
            if ($increment_nb_gen_done > 0) {
1949
                $this->nb_gen_done++;
1950
            }
1951
1952
            if (!$notrigger) {
1953
                // Call trigger
1954
                $result = $this->call_trigger('BILLREC_MODIFY', $user);
1955
                if ($result < 0) {
1956
                    return $result;
1957
                }
1958
                // End call triggers
1959
            }
1960
            return 1;
1961
        } else {
1962
            dol_print_error($this->db);
1963
            return -1;
1964
        }
1965
    }
1966
1967
    /**
1968
     *  Update the maximum period
1969
     *
1970
     *  @param      int     $nb     number of maximum period
1971
     *  @param      int     $notrigger Disable the trigger
1972
     *  @return     int             Return integer <0 if KO, >0 if OK
1973
     */
1974
    public function setMaxPeriod($nb, $notrigger = 0)
1975
    {
1976
        global $user;
1977
1978
        if (!$this->table_element) {
1979
            dol_syslog(get_class($this) . "::setMaxPeriod was called on object with property table_element not defined", LOG_ERR);
1980
            return -1;
1981
        }
1982
1983
        if (empty($nb)) {
1984
            $nb = 0;
1985
        }
1986
1987
        $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element;
1988
        $sql .= ' SET nb_gen_max = ' . ((int) $nb);
1989
        $sql .= " WHERE rowid = " . ((int) $this->id);
1990
1991
        dol_syslog(get_class($this) . "::setMaxPeriod", LOG_DEBUG);
1992
        if ($this->db->query($sql)) {
1993
            $this->nb_gen_max = $nb;
1994
1995
            if (!$notrigger) {
1996
                // Call trigger
1997
                $result = $this->call_trigger('BILLREC_MODIFY', $user);
1998
                if ($result < 0) {
1999
                    return $result;
2000
                }
2001
                // End call triggers
2002
            }
2003
2004
            return 1;
2005
        } else {
2006
            dol_print_error($this->db);
2007
            return -1;
2008
        }
2009
    }
2010
2011
    /**
2012
     *  Update the auto validate flag of invoice
2013
     *
2014
     *  @param      int     $validate       0 to create in draft, 1 to create and validate invoice
2015
     *  @param      int     $notrigger      Disable the trigger
2016
     *  @return     int                     Return integer <0 if KO, >0 if OK
2017
     */
2018
    public function setAutoValidate($validate, $notrigger = 0)
2019
    {
2020
        global $user;
2021
2022
        if (!$this->table_element) {
2023
            dol_syslog(get_class($this) . "::setAutoValidate was called on object with property table_element not defined", LOG_ERR);
2024
            return -1;
2025
        }
2026
2027
        $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element;
2028
        $sql .= ' SET auto_validate = ' . ((int) $validate);
2029
        $sql .= " WHERE rowid = " . ((int) $this->id);
2030
2031
        dol_syslog(get_class($this) . "::setAutoValidate", LOG_DEBUG);
2032
        if ($this->db->query($sql)) {
2033
            $this->auto_validate = $validate;
2034
2035
            if (!$notrigger) {
2036
                // Call trigger
2037
                $result = $this->call_trigger('BILLREC_MODIFY', $user);
2038
                if ($result < 0) {
2039
                    return $result;
2040
                }
2041
                // End call triggers
2042
            }
2043
2044
            return 1;
2045
        } else {
2046
            dol_print_error($this->db);
2047
            return -1;
2048
        }
2049
    }
2050
2051
    /**
2052
     *  Update the auto generate documents
2053
     *
2054
     *  @param      int     $validate       0 no document, 1 to generate document
2055
     *  @param      int     $notrigger      Disable the trigger
2056
     *  @return     int                     Return integer <0 if KO, >0 if OK
2057
     */
2058
    public function setGeneratePdf($validate, $notrigger = 0)
2059
    {
2060
        global $user;
2061
2062
        if (!$this->table_element) {
2063
            dol_syslog(get_class($this) . "::setGeneratePdf was called on object with property table_element not defined", LOG_ERR);
2064
            return -1;
2065
        }
2066
2067
        $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element;
2068
        $sql .= ' SET generate_pdf = ' . ((int) $validate);
2069
        $sql .= " WHERE rowid = " . ((int) $this->id);
2070
2071
        dol_syslog(get_class($this) . "::setGeneratePdf", LOG_DEBUG);
2072
        if ($this->db->query($sql)) {
2073
            $this->generate_pdf = $validate;
2074
2075
            if (!$notrigger) {
2076
                // Call trigger
2077
                $result = $this->call_trigger('BILLREC_MODIFY', $user);
2078
                if ($result < 0) {
2079
                    return $result;
2080
                }
2081
                // End call triggers
2082
            }
2083
2084
            return 1;
2085
        } else {
2086
            dol_print_error($this->db);
2087
            return -1;
2088
        }
2089
    }
2090
2091
    /**
2092
     *  Update the model for documents
2093
     *
2094
     *  @param      string      $model      model of document generator
2095
     *  @param      int     $notrigger      Disable the trigger
2096
     *  @return     int                     Return integer <0 if KO, >0 if OK
2097
     */
2098
    public function setModelPdf($model, $notrigger = 0)
2099
    {
2100
        global $user;
2101
        if (!$this->table_element) {
2102
            dol_syslog(get_class($this) . "::setModelPdf was called on object with property table_element not defined", LOG_ERR);
2103
            return -1;
2104
        }
2105
2106
        $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element;
2107
        $sql .= " SET modelpdf = '" . $this->db->escape($model) . "'";
2108
        $sql .= " WHERE rowid = " . ((int) $this->id);
2109
2110
        dol_syslog(get_class($this) . "::setModelPdf", LOG_DEBUG);
2111
        if ($this->db->query($sql)) {
2112
            $this->model_pdf = $model;
2113
2114
            if (!$notrigger) {
2115
                // Call trigger
2116
                $result = $this->call_trigger('BILLREC_MODIFY', $user);
2117
                if ($result < 0) {
2118
                    return $result;
2119
                }
2120
                // End call triggers
2121
            }
2122
2123
            return 1;
2124
        } else {
2125
            dol_print_error($this->db);
2126
            return -1;
2127
        }
2128
    }
2129
}
2130