Passed
Push — EXTRACT_CLASSES ( c25e41...9f3ede )
by Rafael
55:18
created

CommandeFournisseur::loadStateBoard()   A

Complexity

Conditions 5

Size

Total Lines 28
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 21
nop 0
dl 0
loc 28
rs 9.2728
c 0
b 0
f 0
1
<?php
2
3
/* Copyright (C) 2003-2006  Rodolphe Quiedeville        <[email protected]>
4
 * Copyright (C) 2004-2017	Laurent Destailleur		    <[email protected]>
5
 * Copyright (C) 2005-2012	Regis Houssin			    <[email protected]>
6
 * Copyright (C) 2007		Franky Van Liedekerke	    <[email protected]>
7
 * Copyright (C) 2010-2020	Juanjo Menent			    <[email protected]>
8
 * Copyright (C) 2010-2018	Philippe Grand			    <[email protected]>
9
 * Copyright (C) 2012-2015	Marcos García			    <[email protected]>
10
 * Copyright (C) 2013		Florian Henry			    <[email protected]>
11
 * Copyright (C) 2013		Cédric Salvador			    <[email protected]>
12
 * Copyright (C) 2018		Nicolas ZABOURI			    <[email protected]>
13
 * Copyright (C) 2018-2024	Frédéric France			    <[email protected]>
14
 * Copyright (C) 2018-2022	Ferran Marcet			    <[email protected]>
15
 * Copyright (C) 2021		Josep Lluís Amador		    <[email protected]>
16
 * Copyright (C) 2022		Gauthier VERDOL			    <[email protected]>
17
 * Copyright (C) 2024		Solution Libre SAS		    <[email protected]>
18
 * Copyright (C) 2024		MDW							<[email protected]>
19
 * Copyright (C) 2024       Rafael San José             <[email protected]>
20
 *
21
 * This program is free software; you can redistribute it and/or modify
22
 * it under the terms of the GNU General Public License as published by
23
 * the Free Software Foundation; either version 3 of the License, or
24
 * (at your option) any later version.
25
 *
26
 * This program is distributed in the hope that it will be useful,
27
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
29
 * GNU General Public License for more details.
30
 *
31
 * You should have received a copy of the GNU General Public License
32
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
33
 */
34
35
namespace Dolibarr\Code\Fourn\Classes;
36
37
use Dolibarr\Code\MultiCurrency\Classes\MultiCurrency;
38
39
/**
40
 *  \file       htdocs/fourn/class/fournisseur.commande.class.php
41
 *  \ingroup    fournisseur,commande
42
 *  \brief      File of class to manage suppliers orders
43
 */
44
45
require_once constant('DOL_DOCUMENT_ROOT') . '/core/class/commonorder.class.php';
46
require_once constant('DOL_DOCUMENT_ROOT') . '/fourn/class/fournisseur.product.class.php';
47
if (isModEnabled('productbatch')) {
48
    require_once constant('DOL_DOCUMENT_ROOT') . '/product/class/productbatch.class.php';
49
}
50
51
52
/**
53
 *  Class to manage predefined suppliers products
54
 */
55
class CommandeFournisseur extends CommonOrder
56
{
57
    /**
58
     * @var string ID to identify managed object
59
     */
60
    public $element = 'order_supplier';
61
62
    /**
63
     * @var string Name of table without prefix where object is stored
64
     */
65
    public $table_element = 'commande_fournisseur';
66
67
    /**
68
     * @var string    Name of subtable line
69
     */
70
    public $table_element_line = 'commande_fournisseurdet';
71
72
    /**
73
     * @var string Name of class line
74
     */
75
    public $class_element_line = 'CommandeFournisseurLigne';
76
77
    /**
78
     * @var string Field with ID of parent key if this field has a parent
79
     */
80
    public $fk_element = 'fk_commande';
81
82
    /**
83
     * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png
84
     */
85
    public $picto = 'supplier_order';
86
87
    /**
88
     * 0=Default, 1=View may be restricted to sales representative only if no permission to see all or to company of external user if external user
89
     * @var integer
90
     */
91
    public $restrictiononfksoc = 1;
92
93
    /**
94
     * {@inheritdoc}
95
     */
96
    protected $table_ref_field = 'ref';
97
98
    /**
99
     * @var int Purchase Order ID
100
     */
101
    public $id;
102
103
    /**
104
     * @var string Supplier order reference
105
     */
106
    public $ref;
107
108
    /**
109
     * @var string Supplier reference
110
     */
111
    public $ref_supplier;
112
113
    /**
114
     * @var string ref supplier
115
     * @deprecated
116
     * @see $ref_supplier
117
     */
118
    public $ref_fourn;
119
120
    /**
121
     * @var int
122
     */
123
    public $statut; // 0=Draft -> 1=Validated -> 2=Approved -> 3=Ordered/Process running -> 4=Received partially -> 5=Received totally -> (reopen) 4=Received partially
124
    //                                                                                          -> 7=Canceled/Never received -> (reopen) 3=Process running
125
    //                                                              -> 6=Canceled -> (reopen) 2=Approved
126
    //                                                -> 9=Refused  -> (reopen) 1=Validated
127
    //  Note: billed or not is on another field "billed"
128
129
    public $billed;
130
131
    /**
132
     * @var int Company ID
133
     */
134
    public $socid;
135
136
    /**
137
     * @var int Supplier ID
138
     */
139
    public $fourn_id;
140
141
    /**
142
     * @var int Date
143
     */
144
    public $date;
145
146
    /**
147
     * @var int Date of the purchase order creation
148
     */
149
    public $date_creation;
150
151
    /**
152
     * @var int Date of the purchase order validation
153
     */
154
    public $date_valid;
155
156
    /**
157
     * @var int Date of the purchase order approval
158
     */
159
    public $date_approve;
160
161
    /**
162
     * @var int Date of the purchase order second approval
163
     * Used when SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED is set
164
     */
165
    public $date_approve2;
166
167
    /**
168
     * @var int Date of the purchase order ordering
169
     */
170
    public $date_commande;
171
172
    //For backward compatibility
173
    public $remise_percent;
174
    public $methode_commande_id;
175
    public $methode_commande;
176
177
    /**
178
     *  @var int Expected Delivery Date
179
     */
180
    public $delivery_date;
181
182
    /**
183
     *  @var float Total value, excluding taxes (HT = "Hors Taxe" in French)
184
     */
185
    public $total_ht;
186
187
    /**
188
     *  @var float Total VAT
189
     */
190
    public $total_tva;
191
192
    /**
193
     *  @var float Total Local tax 1
194
     */
195
    public $total_localtax1;
196
197
    /**
198
     *  @var float Total Local tax 2
199
     */
200
    public $total_localtax2;
201
202
    /**
203
     *  @var float Total value, including taxes (TTC = "Toutes Taxes Comprises" in French)
204
     */
205
    public $total_ttc;
206
207
    public $source;
208
209
    /**
210
     * @var int ID
211
     */
212
    public $fk_project;
213
214
    /**
215
     * @var int Payment conditions ID
216
     */
217
    public $cond_reglement_id;
218
219
    /**
220
     * @var string Payment conditions code
221
     */
222
    public $cond_reglement_code;
223
224
    /**
225
     * @var string Payment conditions label
226
     */
227
    public $cond_reglement_label;
228
229
    /**
230
     * @var string Payment conditions label on documents
231
     */
232
    public $cond_reglement_doc;
233
234
    /**
235
     * @var int Account ID
236
     */
237
    public $fk_account;
238
239
    /**
240
     * @var int Payment choice ID
241
     */
242
    public $mode_reglement_id;
243
244
    /**
245
     * @var string Payment choice code
246
     */
247
    public $mode_reglement_code;
248
249
    /**
250
     * @var string Payment choice label
251
     */
252
    public $mode_reglement;
253
254
    /**
255
     * @var int User ID of the purchase order author
256
     */
257
    public $user_author_id;
258
259
    /**
260
     * @var int User ID of the purchase order approver
261
     */
262
    public $user_approve_id;
263
264
    /**
265
     * @var int User ID of the purchase order second approver
266
     * Used when SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED is set
267
     */
268
    public $user_approve_id2;
269
270
    public $refuse_note;
271
272
    public $extraparams = array();
273
274
    /**
275
     * @var CommandeFournisseurLigne[]
276
     */
277
    public $lines = array();
278
279
    /**
280
     * @var CommandeFournisseurLigne
281
     */
282
    public $line;
283
284
    // Add for supplier_proposal
285
    public $origin;
286
    public $origin_id;
287
    public $linked_objects = array();
288
289
    /**
290
     * @var int Date of the purchase order payment deadline
291
     */
292
    public $date_lim_reglement;
293
    public $receptions = array();
294
295
    // Multicurrency
296
    /**
297
     * @var int ID
298
     */
299
    public $fk_multicurrency;
300
301
    /**
302
     * @var string
303
     */
304
    public $multicurrency_code;
305
306
    /**
307
     * @var float Rate
308
     */
309
    public $multicurrency_tx;
310
311
    /**
312
     * @var float Total value in the other currency, excluding taxes (HT = "Hors Taxes" in French)
313
     */
314
    public $multicurrency_total_ht;
315
316
    /**
317
     * @var float Total VAT in the other currency (TVA = "Taxe sur la Valeur Ajoutée" in French)
318
     */
319
    public $multicurrency_total_tva;
320
321
    /**
322
     * @var float Total value in the other currency, including taxes (TTC = "Toutes Taxes Comprises in French)
323
     */
324
    public $multicurrency_total_ttc;
325
326
    /**
327
     *  'type' field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]', 'sellist:TableName:LabelFieldName[:KeyFieldName[:KeyFieldParent[:Filter[:Sortfield]]]]', 'varchar(x)', 'double(24,8)', 'real', 'price', 'text', 'text:none', 'html', 'date', 'datetime', 'timestamp', 'duration', 'mail', 'phone', 'url', 'password')
328
     *         Note: Filter can be a string like "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.nature:is:NULL)"
329
     *  'label' the translation key.
330
     *  'picto' is code of a picto to show before value in forms
331
     *  'enabled' is a condition when the field must be managed (Example: 1 or 'getDolGlobalString("MY_SETUP_PARAM")' or 'isModEnabled("multicurrency")' ...)
332
     *  'position' is the sort order of field.
333
     *  'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
334
     *  '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)
335
     *  'noteditable' says if field is not editable (1 or 0)
336
     *  '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.
337
     *  'index' if we want an index in database.
338
     *  'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommended to name the field fk_...).
339
     *  'searchall' is 1 if we want to search in this field when making a search from the quick search button.
340
     *  'isameasure' must be set to 1 or 2 if field can be used for measure. Field type must be summable like integer or double(24,8). Use 1 in most cases, or 2 if you don't want to see the column total into list (for example for percentage)
341
     *  'css' and 'cssview' and 'csslist' is the CSS style to use on field. 'css' is used in creation and update. 'cssview' is used in view mode. 'csslist' is used for columns in lists. For example: 'css'=>'minwidth300 maxwidth500 widthcentpercentminusx', 'cssview'=>'wordbreak', 'csslist'=>'tdoverflowmax200'
342
     *  'help' is a 'TranslationString' to use to show a tooltip on field. You can also use 'TranslationString:keyfortooltiponlick' for a tooltip on click.
343
     *  'showoncombobox' if value of the field must be visible into the label of the combobox that list record
344
     *  '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.
345
     *  'arrayofkeyval' to set a list of values if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel"). Note that type can be 'integer' or 'varchar'
346
     *  'autofocusoncreate' to have field having the focus on a create form. Only 1 field should have this property set to 1.
347
     *  'comment' is not used. You can store here any text of your choice. It is not used by application.
348
     *  'validate' is 1 if need to validate with $this->validateField()
349
     *  'copytoclipboard' is 1 or 2 to allow to add a picto to copy value into clipboard (1=picto after label, 2=picto after value)
350
     *
351
     *  Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor.
352
     */
353
    public $fields = array(
354
        'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'position' => 10),
355
        'ref' => array('type' => 'varchar(255)', 'label' => 'Ref', 'enabled' => 1, 'visible' => 1, 'showoncombobox' => 1, 'position' => 25, 'searchall' => 1),
356
        'ref_ext' => array('type' => 'varchar(255)', 'label' => 'Ref ext', 'enabled' => 1, 'visible' => 0, 'position' => 35),
357
        'ref_supplier' => array('type' => 'varchar(255)', 'label' => 'RefOrderSupplierShort', 'enabled' => 1, 'visible' => 1, 'position' => 40, 'searchall' => 1),
358
        'fk_projet' => array('type' => 'integer:Project:projet/class/project.class.php:1:(fk_statut:=:1)', 'label' => 'Project', 'enabled' => "isModEnabled('project')", 'visible' => -1, 'position' => 45),
359
        'date_valid' => array('type' => 'datetime', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -1, 'position' => 710),
360
        'date_approve' => array('type' => 'datetime', 'label' => 'DateApprove', 'enabled' => 1, 'visible' => -1, 'position' => 720),
361
        'date_approve2' => array('type' => 'datetime', 'label' => 'DateApprove2', 'enabled' => 1, 'visible' => 3, 'position' => 725),
362
        'date_commande' => array('type' => 'date', 'label' => 'OrderDateShort', 'enabled' => 1, 'visible' => 1, 'position' => 70),
363
        'date_livraison' => array('type' => 'datetime', 'label' => 'DeliveryDate', 'enabled' => 'empty($conf->global->ORDER_DISABLE_DELIVERY_DATE)', 'visible' => 1, 'position' => 74),
364
        'fk_user_author' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'enabled' => 1, 'visible' => 3, 'position' => 41),
365
        'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => 3, 'notnull' => -1, 'position' => 80),
366
        'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'enabled' => 1, 'visible' => 3, 'position' => 711),
367
        'fk_user_approve' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserApproval', 'enabled' => 1, 'visible' => 3, 'position' => 721),
368
        'fk_user_approve2' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserApproval2', 'enabled' => 1, 'visible' => 3, 'position' => 726),
369
        'source' => array('type' => 'smallint(6)', 'label' => 'Source', 'enabled' => 1, 'visible' => 3, 'notnull' => 1, 'position' => 100),
370
        'billed' => array('type' => 'smallint(6)', 'label' => 'Billed', 'enabled' => 1, 'visible' => 1, 'position' => 710),
371
        'total_ht' => array('type' => 'double(24,8)', 'label' => 'AmountHT', 'enabled' => 1, 'visible' => 1, 'position' => 130, 'isameasure' => 1),
372
        'total_tva' => array('type' => 'double(24,8)', 'label' => 'AmountVAT', 'enabled' => 1, 'visible' => 1, 'position' => 135, 'isameasure' => 1),
373
        'localtax1' => array('type' => 'double(24,8)', 'label' => 'LT1', 'enabled' => 1, 'visible' => 3, 'position' => 140, 'isameasure' => 1),
374
        'localtax2' => array('type' => 'double(24,8)', 'label' => 'LT2', 'enabled' => 1, 'visible' => 3, 'position' => 145, 'isameasure' => 1),
375
        'total_ttc' => array('type' => 'double(24,8)', 'label' => 'AmountTTC', 'enabled' => 1, 'visible' => -1, 'position' => 150, 'isameasure' => 1),
376
        'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 750, 'searchall' => 1),
377
        'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 760, 'searchall' => 1),
378
        'model_pdf' => array('type' => 'varchar(255)', 'label' => 'ModelPDF', 'enabled' => 1, 'visible' => 0, 'position' => 165),
379
        'fk_input_method' => array('type' => 'integer', 'label' => 'OrderMode', 'enabled' => 1, 'visible' => 3, 'position' => 170),
380
        'fk_cond_reglement' => array('type' => 'integer', 'label' => 'PaymentTerm', 'enabled' => 1, 'visible' => 3, 'position' => 175),
381
        'fk_mode_reglement' => array('type' => 'integer', 'label' => 'PaymentMode', 'enabled' => 1, 'visible' => 3, 'position' => 180),
382
        'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => 0, 'position' => 190),
383
        'fk_account' => array('type' => 'integer', 'label' => 'BankAccount', 'enabled' => 'isModEnabled("bank")', 'visible' => 3, 'position' => 200),
384
        'fk_incoterms' => array('type' => 'integer', 'label' => 'IncotermCode', 'enabled' => 1, 'visible' => 3, 'position' => 205),
385
        'location_incoterms' => array('type' => 'varchar(255)', 'label' => 'IncotermLocation', 'enabled' => 1, 'visible' => 3, 'position' => 210),
386
        'fk_multicurrency' => array('type' => 'integer', 'label' => 'Fk multicurrency', 'enabled' => 1, 'visible' => 0, 'position' => 215),
387
        'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'Currency', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 220),
388
        'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'CurrencyRate', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 225),
389
        'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountHT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 230),
390
        'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountVAT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 235),
391
        'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountTTC', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 240),
392
        'date_creation' => array('type' => 'datetime', 'label' => 'Date creation', 'enabled' => 1, 'visible' => -1, 'position' => 500),
393
        'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'enabled' => 'isModEnabled("societe")', 'visible' => 1, 'notnull' => 1, 'position' => 50),
394
        'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'position' => 1000, 'index' => 1),
395
        'tms' => array('type' => 'datetime', 'label' => "DateModificationShort", 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 501),
396
        'last_main_doc' => array('type' => 'varchar(255)', 'label' => 'LastMainDoc', 'enabled' => 1, 'visible' => 0, 'position' => 700),
397
        'fk_statut' => array('type' => 'smallint(6)', 'label' => 'Status', 'enabled' => 1, 'visible' => 1, 'position' => 701),
398
        'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => 0, 'position' => 900),
399
    );
400
401
402
    /**
403
     * Draft status
404
     */
405
    const STATUS_DRAFT = 0;
406
407
    /**
408
     * Validated status
409
     */
410
    const STATUS_VALIDATED = 1;
411
412
    /**
413
     * Accepted
414
     */
415
    const STATUS_ACCEPTED = 2;
416
417
    /**
418
     * Order sent, shipment on process
419
     */
420
    const STATUS_ORDERSENT = 3;
421
422
    /**
423
     * Received partially
424
     */
425
    const STATUS_RECEIVED_PARTIALLY = 4;
426
427
    /**
428
     * Received completely
429
     */
430
    const STATUS_RECEIVED_COMPLETELY = 5;
431
432
    /**
433
     * Order canceled
434
     */
435
    const STATUS_CANCELED = 6;
436
437
    /**
438
     * Order canceled/never received
439
     */
440
    const STATUS_CANCELED_AFTER_ORDER = 7;
441
442
    /**
443
     * Refused
444
     */
445
    const STATUS_REFUSED = 9;
446
447
448
    /**
449
     * The constant used into source field to track the order was generated by the replenishement feature
450
     */
451
    const SOURCE_ID_REPLENISHMENT = 42;
452
453
    /**
454
     *  Constructor
455
     *
456
     *  @param      DoliDB      $db      Database handler
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\Fourn\Classes\DoliDB was not found. Did you mean DoliDB? If so, make sure to prefix the type with \.
Loading history...
457
     */
458
    public function __construct($db)
459
    {
460
        $this->db = $db;
461
462
        $this->ismultientitymanaged = 1;
463
    }
464
465
466
    /**
467
     *  Get object and lines from database
468
     *
469
     *  @param  int     $id         Id of order to load
470
     *  @param  string  $ref        Ref of object
471
     *  @return int                 >0 if OK, <0 if KO, 0 if not found
472
     */
473
    public function fetch($id, $ref = '')
474
    {
475
        // Check parameters
476
        if (empty($id) && empty($ref)) {
477
            return -1;
478
        }
479
480
        $sql = "SELECT c.rowid, c.entity, c.ref, ref_supplier, c.fk_soc, c.fk_statut as status, c.amount_ht, c.total_ht, c.total_ttc, c.total_tva,";
481
        $sql .= " c.localtax1, c.localtax2, ";
482
        $sql .= " c.date_creation, c.date_valid, c.date_approve, c.date_approve2,";
483
        $sql .= " c.fk_user_author as user_author_id, c.fk_user_valid as user_validation_id, c.fk_user_approve as user_approve_id, c.fk_user_approve2 as user_approve_id2,";
484
        $sql .= " c.date_commande as date_commande, c.date_livraison as delivery_date, c.fk_cond_reglement, c.fk_mode_reglement, c.fk_projet as fk_project, c.remise_percent, c.source, c.fk_input_method,";
485
        $sql .= " c.fk_account,";
486
        $sql .= " c.note_private, c.note_public, c.model_pdf, c.extraparams, c.billed,";
487
        $sql .= " c.fk_multicurrency, c.multicurrency_code, c.multicurrency_tx, c.multicurrency_total_ht, c.multicurrency_total_tva, c.multicurrency_total_ttc,";
488
        $sql .= " cm.libelle as methode_commande,";
489
        $sql .= " cr.code as cond_reglement_code, cr.libelle as cond_reglement_label, cr.libelle_facture as cond_reglement_doc,";
490
        $sql .= " p.code as mode_reglement_code, p.libelle as mode_reglement_libelle";
491
        $sql .= ', c.fk_incoterms, c.location_incoterms';
492
        $sql .= ', i.libelle as label_incoterms';
493
        $sql .= " FROM " . $this->db->prefix() . "commande_fournisseur as c";
494
        $sql .= " LEFT JOIN " . $this->db->prefix() . "c_payment_term as cr ON c.fk_cond_reglement = cr.rowid";
495
        $sql .= " LEFT JOIN " . $this->db->prefix() . "c_paiement as p ON c.fk_mode_reglement = p.id";
496
        $sql .= " LEFT JOIN " . $this->db->prefix() . "c_input_method as cm ON cm.rowid = c.fk_input_method";
497
        $sql .= ' LEFT JOIN ' . $this->db->prefix() . 'c_incoterms as i ON c.fk_incoterms = i.rowid';
498
499
        if (empty($id)) {
500
            $sql .= " WHERE c.entity IN (" . getEntity('supplier_order') . ")";
501
        } else {
502
            $sql .= " WHERE c.rowid=" . ((int) $id);
503
        }
504
505
        if ($ref) {
506
            $sql .= " AND c.ref='" . $this->db->escape($ref) . "'";
507
        }
508
509
        dol_syslog(get_class($this) . "::fetch", LOG_DEBUG);
510
        $resql = $this->db->query($sql);
511
        if ($resql) {
512
            $obj = $this->db->fetch_object($resql);
513
            if (!$obj) {
514
                $this->error = 'Bill with id ' . $id . ' not found';
515
                dol_syslog(get_class($this) . '::fetch ' . $this->error);
516
                return 0;
517
            }
518
519
            $this->id = $obj->rowid;
520
            $this->entity = $obj->entity;
521
522
            $this->ref = $obj->ref;
523
            $this->ref_supplier = $obj->ref_supplier;
524
            $this->socid = $obj->fk_soc;
525
            $this->fourn_id = $obj->fk_soc;
526
            $this->statut = $obj->status;   // deprecated
527
            $this->status = $obj->status;
528
            $this->billed = $obj->billed;
529
            $this->user_author_id = $obj->user_author_id;
530
            $this->user_validation_id = $obj->user_validation_id;
531
            $this->user_approve_id = $obj->user_approve_id;
532
            $this->user_approve_id2 = $obj->user_approve_id2;
533
            $this->total_ht             = $obj->total_ht;
534
            $this->total_tva            = $obj->total_tva;
535
            $this->total_localtax1      = $obj->localtax1;
536
            $this->total_localtax2      = $obj->localtax2;
537
            $this->total_ttc            = $obj->total_ttc;
538
            $this->date_creation = $this->db->jdate($obj->date_creation);
539
            $this->date_valid = $this->db->jdate($obj->date_valid);
540
            $this->date_approve         = $this->db->jdate($obj->date_approve);
541
            $this->date_approve2        = $this->db->jdate($obj->date_approve2);
542
            $this->date_commande        = $this->db->jdate($obj->date_commande); // date we make the order to supplier
543
            if (isset($obj->date_commande)) {
544
                $this->date = $this->date_commande;
545
            } else {
546
                $this->date = $this->date_creation;
547
            }
548
            $this->delivery_date = $this->db->jdate($obj->delivery_date);
549
            $this->remise_percent = $obj->remise_percent;
550
            $this->methode_commande_id = $obj->fk_input_method;
551
            $this->methode_commande = $obj->methode_commande;
552
553
            $this->source = $obj->source;
554
            $this->fk_project = $obj->fk_project;
555
            $this->cond_reglement_id = $obj->fk_cond_reglement;
556
            $this->cond_reglement_code = $obj->cond_reglement_code;
557
            $this->cond_reglement = $obj->cond_reglement_label;         // deprecated
558
            $this->cond_reglement_label = $obj->cond_reglement_label;
559
            $this->cond_reglement_doc = $obj->cond_reglement_doc;
560
            $this->fk_account = $obj->fk_account;
561
            $this->mode_reglement_id = $obj->fk_mode_reglement;
562
            $this->mode_reglement_code = $obj->mode_reglement_code;
563
            $this->mode_reglement = $obj->mode_reglement_libelle;
564
            $this->note = $obj->note_private; // deprecated
565
            $this->note_private = $obj->note_private;
566
            $this->note_public = $obj->note_public;
567
            $this->model_pdf = $obj->model_pdf;
568
569
            //Incoterms
570
            $this->fk_incoterms = $obj->fk_incoterms;
571
            $this->location_incoterms = $obj->location_incoterms;
572
            $this->label_incoterms = $obj->label_incoterms;
573
574
            // Multicurrency
575
            $this->fk_multicurrency         = $obj->fk_multicurrency;
576
            $this->multicurrency_code = $obj->multicurrency_code;
577
            $this->multicurrency_tx         = $obj->multicurrency_tx;
578
            $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
579
            $this->multicurrency_total_tva  = $obj->multicurrency_total_tva;
580
            $this->multicurrency_total_ttc  = $obj->multicurrency_total_ttc;
581
582
            $this->extraparams = isset($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
583
584
            $this->db->free($resql);
585
586
            // Retrieve all extrafield
587
            // fetch optionals attributes and labels
588
            $this->fetch_optionals();
589
590
            // Lines
591
            $result = $this->fetch_lines();
592
593
            if ($result < 0) {
594
                return -1;
595
            } else {
596
                return 1;
597
            }
598
        } else {
599
            $this->error = $this->db->error() . " sql=" . $sql;
600
            return -1;
601
        }
602
    }
603
604
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
605
    /**
606
     * Load array lines
607
     *
608
     * @param       int     $only_product   Return only physical products
609
     * @return      int                     Return integer <0 if KO, >0 if OK
610
     */
611
    public function fetch_lines($only_product = 0)
612
    {
613
		// phpcs:enable
614
615
        $this->lines = array();
616
617
        $sql = "SELECT l.rowid, l.fk_commande, l.ref as ref_supplier, l.fk_product, l.product_type, l.label, l.description, l.qty,";
618
        $sql .= " l.vat_src_code, l.tva_tx, l.remise_percent, l.subprice,";
619
        $sql .= " l.localtax1_tx, l. localtax2_tx, l.localtax1_type, l. localtax2_type, l.total_localtax1, l.total_localtax2,";
620
        $sql .= " l.total_ht, l.total_tva, l.total_ttc, l.special_code, l.fk_parent_line, l.rang,";
621
        $sql .= " p.rowid as product_id, p.ref as product_ref, p.label as product_label, p.description as product_desc, p.tobatch as product_tobatch, p.barcode as product_barcode,";
622
        $sql .= " l.fk_unit,";
623
        $sql .= " l.date_start, l.date_end,";
624
        $sql .= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc';
625
        $sql .= " FROM " . $this->db->prefix() . "commande_fournisseurdet as l";
626
        $sql .= ' LEFT JOIN ' . $this->db->prefix() . 'product as p ON l.fk_product = p.rowid';
627
        $sql .= " WHERE l.fk_commande = " . ((int) $this->id);
628
        if ($only_product) {
629
            $sql .= ' AND p.fk_product_type = 0';
630
        }
631
        $sql .= " ORDER BY l.rang, l.rowid";
632
        //print $sql;
633
634
        dol_syslog(get_class($this) . "::fetch_lines", LOG_DEBUG);
635
636
        $result = $this->db->query($sql);
637
        if ($result) {
638
            $num = $this->db->num_rows($result);
639
            $i = 0;
640
641
            while ($i < $num) {
642
                $objp = $this->db->fetch_object($result);
643
644
                $line = new CommandeFournisseurLigne($this->db);
645
646
                $line->id                  = $objp->rowid;
647
                $line->fk_commande         = $objp->fk_commande;
648
                $line->desc                = $objp->description;
649
                $line->description         = $objp->description;
650
                $line->qty                 = $objp->qty;
651
                $line->tva_tx              = $objp->tva_tx;
652
                $line->localtax1_tx        = $objp->localtax1_tx;
653
                $line->localtax2_tx        = $objp->localtax2_tx;
654
                $line->localtax1_type      = $objp->localtax1_type;
655
                $line->localtax2_type      = $objp->localtax2_type;
656
                $line->subprice            = $objp->subprice;
657
                $line->pu_ht = $objp->subprice;
658
                $line->remise_percent      = $objp->remise_percent;
659
660
                $line->vat_src_code        = $objp->vat_src_code;
661
                $line->total_ht            = $objp->total_ht;
662
                $line->total_tva           = $objp->total_tva;
663
                $line->total_localtax1     = $objp->total_localtax1;
664
                $line->total_localtax2     = $objp->total_localtax2;
665
                $line->total_ttc           = $objp->total_ttc;
666
                $line->product_type        = $objp->product_type;
667
668
                $line->fk_product          = $objp->fk_product;
669
670
                $line->libelle             = $objp->product_label; // deprecated
671
                $line->product_label       = $objp->product_label;
672
                $line->product_desc        = $objp->product_desc;
673
                $line->product_tobatch     = $objp->product_tobatch;
674
                $line->product_barcode     = $objp->product_barcode;
675
676
                $line->ref                 = $objp->product_ref; // Ref of product
677
                $line->product_ref         = $objp->product_ref; // Ref of product
678
                $line->ref_fourn           = $objp->ref_supplier; // The supplier ref of price when product was added. May have change since
679
                $line->ref_supplier        = $objp->ref_supplier; // The supplier ref of price when product was added. May have change since
680
681
                if (getDolGlobalString('PRODUCT_USE_SUPPLIER_PACKAGING')) {
682
                    // TODO We should not fetch this properties into the fetch_lines. This is NOT properties of a line.
683
                    // Move this into another method and call it when required.
684
685
                    // Take better packaging for $objp->qty (first supplier ref quantity <= $objp->qty)
686
                    $sqlsearchpackage = 'SELECT rowid, packaging FROM ' . $this->db->prefix() . "product_fournisseur_price";
687
                    $sqlsearchpackage .= ' WHERE entity IN (' . getEntity('product_fournisseur_price') . ")";
688
                    $sqlsearchpackage .= " AND fk_product = " . ((int) $objp->fk_product);
689
                    $sqlsearchpackage .= " AND ref_fourn = '" . $this->db->escape($objp->ref_supplier) . "'";
690
                    $sqlsearchpackage .= " AND quantity <= " . ((float) $objp->qty);  // required to be qualified
691
                    $sqlsearchpackage .= " AND (packaging IS NULL OR packaging = 0 OR packaging <= " . ((float) $objp->qty) . ")";  // required to be qualified
692
                    $sqlsearchpackage .= " AND fk_soc = " . ((int) $this->socid);
693
                    $sqlsearchpackage .= " ORDER BY packaging ASC";     // Take the smaller package first
694
                    $sqlsearchpackage .= " LIMIT 1";
695
696
                    $resqlsearchpackage = $this->db->query($sqlsearchpackage);
697
                    if ($resqlsearchpackage) {
698
                        $objsearchpackage = $this->db->fetch_object($resqlsearchpackage);
699
                        if ($objsearchpackage) {
700
                            $line->fk_fournprice = $objsearchpackage->rowid;
701
                            $line->packaging     = $objsearchpackage->packaging;
702
                        }
703
                    } else {
704
                        $this->error = $this->db->lasterror();
705
                        return -1;
706
                    }
707
                }
708
709
                $line->date_start          = $this->db->jdate($objp->date_start);
710
                $line->date_end            = $this->db->jdate($objp->date_end);
711
                $line->fk_unit             = $objp->fk_unit;
712
713
                // Multicurrency
714
                $line->fk_multicurrency = $objp->fk_multicurrency;
715
                $line->multicurrency_code = $objp->multicurrency_code;
716
                $line->multicurrency_subprice = $objp->multicurrency_subprice;
717
                $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
718
                $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
719
                $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
720
721
                $line->special_code        = $objp->special_code;
722
                $line->fk_parent_line      = $objp->fk_parent_line;
723
724
                $line->rang                = $objp->rang;
725
726
                // Retrieve all extrafield
727
                // fetch optionals attributes and labels
728
                $line->fetch_optionals();
729
730
                $this->lines[$i] = $line;
731
732
                $i++;
733
            }
734
            $this->db->free($result);
735
736
            return $num;
737
        } else {
738
            $this->error = $this->db->error() . " sql=" . $sql;
739
            return -1;
740
        }
741
    }
742
743
    /**
744
     *  Validate an order
745
     *
746
     *  @param  User    $user           Validator User
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\Fourn\Classes\User was not found. Did you mean User? If so, make sure to prefix the type with \.
Loading history...
747
     *  @param  int     $idwarehouse    Id of warehouse to use for stock decrease
748
     *  @param  int     $notrigger      1=Does not execute triggers, 0= execute triggers
749
     *  @return int                     Return integer <0 if KO, >0 if OK
750
     */
751
    public function valid($user, $idwarehouse = 0, $notrigger = 0)
752
    {
753
        global $conf;
754
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
755
756
        $error = 0;
757
758
        dol_syslog(get_class($this) . "::valid");
759
        $result = 0;
760
        if (
761
            (!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")))
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: (! getDolGlobalString('M...r_advance', 'validate'), Probably Intended Meaning: ! getDolGlobalString('MA..._advance', 'validate'))
Loading history...
762
            || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight("fournisseur", "supplier_order_advance", "validate"))
763
        ) {
764
            $this->db->begin();
765
766
            // Definition of supplier order numbering model name
767
            $soc = new Societe($this->db);
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\Fourn\Classes\Societe was not found. Did you mean Societe? If so, make sure to prefix the type with \.
Loading history...
768
            $soc->fetch($this->fourn_id);
769
770
            // Check if object has a temporary ref
771
            if (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
772
                $num = $this->getNextNumRef($soc);
773
            } else {
774
                $num = $this->ref;
775
            }
776
            $this->newref = dol_sanitizeFileName($num);
777
778
            $sql = 'UPDATE ' . $this->db->prefix() . "commande_fournisseur";
779
            $sql .= " SET ref='" . $this->db->escape($num) . "',";
780
            $sql .= " fk_statut = " . ((int) self::STATUS_VALIDATED) . ",";
781
            $sql .= " date_valid='" . $this->db->idate(dol_now()) . "',";
782
            $sql .= " fk_user_valid = " . ((int) $user->id);
783
            $sql .= " WHERE rowid = " . ((int) $this->id);
784
            $sql .= " AND fk_statut = " . ((int) self::STATUS_DRAFT);
785
786
            $resql = $this->db->query($sql);
787
            if (!$resql) {
788
                dol_print_error($this->db);
789
                $error++;
790
            }
791
792
            if (!$error && !$notrigger) {
793
                // Call trigger
794
                $result = $this->call_trigger('ORDER_SUPPLIER_VALIDATE', $user);
795
                if ($result < 0) {
796
                    $error++;
797
                }
798
                // End call triggers
799
            }
800
801
            if (!$error) {
802
                $this->oldref = $this->ref;
803
804
                // Rename directory if dir was a temporary ref
805
                if (preg_match('/^[\(]?PROV/i', $this->ref)) {
806
                    // Now we rename also files into index
807
                    $sql = 'UPDATE ' . $this->db->prefix() . "ecm_files set filename = CONCAT('" . $this->db->escape($this->newref) . "', SUBSTR(filename, " . (strlen($this->ref) + 1) . ")), filepath = 'fournisseur/commande/" . $this->db->escape($this->newref) . "'";
808
                    $sql .= " WHERE filename LIKE '" . $this->db->escape($this->ref) . "%' AND filepath = 'fournisseur/commande/" . $this->db->escape($this->ref) . "' and entity = " . ((int) $conf->entity);
809
                    $resql = $this->db->query($sql);
810
                    if (!$resql) {
811
                        $error++;
812
                        $this->error = $this->db->lasterror();
813
                    }
814
                    $sql = 'UPDATE ' . $this->db->prefix() . "ecm_files set filepath = 'fournisseur/commande/" . $this->db->escape($this->newref) . "'";
815
                    $sql .= " WHERE filepath = 'fournisseur/commande/" . $this->db->escape($this->ref) . "' and entity = " . $conf->entity;
816
                    $resql = $this->db->query($sql);
817
                    if (!$resql) {
818
                        $error++;
819
                        $this->error = $this->db->lasterror();
820
                    }
821
822
                    // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
823
                    $oldref = dol_sanitizeFileName($this->ref);
824
                    $newref = dol_sanitizeFileName($num);
825
                    $dirsource = $conf->fournisseur->commande->dir_output . '/' . $oldref;
826
                    $dirdest = $conf->fournisseur->commande->dir_output . '/' . $newref;
827
                    if (!$error && file_exists($dirsource)) {
828
                        dol_syslog(get_class($this) . "::valid rename dir " . $dirsource . " into " . $dirdest);
829
830
                        if (@rename($dirsource, $dirdest)) {
831
                            dol_syslog("Rename ok");
832
                            // Rename docs starting with $oldref with $newref
833
                            $listoffiles = dol_dir_list($conf->fournisseur->commande->dir_output . '/' . $newref, 'files', 1, '^' . preg_quote($oldref, '/'));
834
                            foreach ($listoffiles as $fileentry) {
835
                                $dirsource = $fileentry['name'];
836
                                $dirdest = preg_replace('/^' . preg_quote($oldref, '/') . '/', $newref, $dirsource);
837
                                $dirsource = $fileentry['path'] . '/' . $dirsource;
838
                                $dirdest = $fileentry['path'] . '/' . $dirdest;
839
                                @rename($dirsource, $dirdest);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for rename(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

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

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
840
                            }
841
                        }
842
                    }
843
                }
844
            }
845
846
            if (!$error) {
847
                $result = 1;
848
                $this->status = self::STATUS_VALIDATED;
849
                $this->statut = self::STATUS_VALIDATED; // deprecated
850
                $this->ref = $num;
851
            }
852
853
            if (!$error) {
854
                $this->db->commit();
855
                return 1;
856
            } else {
857
                $this->db->rollback();
858
                return -1;
859
            }
860
        } else {
861
            $this->error = 'NotAuthorized';
862
            dol_syslog(get_class($this) . "::valid " . $this->error, LOG_ERR);
863
            return -1;
864
        }
865
    }
866
867
    /**
868
     *  Return label of the status of object
869
     *
870
     *  @param      int     $mode           0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=short label + picto
871
     *  @return     string                  Label
872
     */
873
    public function getLibStatut($mode = 0)
874
    {
875
        return $this->LibStatut($this->statut, $mode, $this->billed);
876
    }
877
878
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
879
    /**
880
     *  Return label of a status
881
     *
882
     *  @param  int     $status     Id statut
883
     *  @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
884
     *  @param  int     $billed     1=Billed
885
     *  @return string              Label of status
886
     */
887
    public function LibStatut($status, $mode = 0, $billed = 0)
888
    {
889
		// phpcs:enable
890
        global $langs, $hookmanager;
891
892
        if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
893
            $langs->load('orders');
894
895
            $this->labelStatus[0] = 'StatusSupplierOrderDraft';
896
            $this->labelStatus[1] = 'StatusSupplierOrderValidated';
897
            $this->labelStatus[2] = 'StatusSupplierOrderApproved';
898
            if (!getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
899
                $this->labelStatus[3] = 'StatusSupplierOrderOnProcess';
900
            } else {
901
                $this->labelStatus[3] = 'StatusSupplierOrderOnProcessWithValidation';
902
            }
903
            $this->labelStatus[4] = 'StatusSupplierOrderReceivedPartially';
904
            $this->labelStatus[5] = 'StatusSupplierOrderReceivedAll';
905
            $this->labelStatus[6] = 'StatusSupplierOrderCanceled'; // Approved->Canceled
906
            $this->labelStatus[7] = 'StatusSupplierOrderCanceled'; // Process running->canceled
907
            $this->labelStatus[9] = 'StatusSupplierOrderRefused';
908
909
            // List of language codes for status
910
            $this->labelStatusShort[0] = 'StatusSupplierOrderDraftShort';
911
            $this->labelStatusShort[1] = 'StatusSupplierOrderValidatedShort';
912
            $this->labelStatusShort[2] = 'StatusSupplierOrderApprovedShort';
913
            $this->labelStatusShort[3] = 'StatusSupplierOrderOnProcessShort';
914
            $this->labelStatusShort[4] = 'StatusSupplierOrderReceivedPartiallyShort';
915
            $this->labelStatusShort[5] = 'StatusSupplierOrderReceivedAllShort';
916
            $this->labelStatusShort[6] = 'StatusSupplierOrderCanceledShort';
917
            $this->labelStatusShort[7] = 'StatusSupplierOrderCanceledShort';
918
            $this->labelStatusShort[9] = 'StatusSupplierOrderRefusedShort';
919
        }
920
921
        $statustrans = array(
922
            0 => 'status0',
923
            1 => 'status1b',
924
            2 => 'status1',
925
            3 => 'status4',
926
            4 => 'status4b',
927
            5 => 'status6',
928
            6 => 'status9',
929
            7 => 'status9',
930
            9 => 'status9',
931
        );
932
933
        $statusClass = 'status0';
934
        if (!empty($statustrans[$status])) {
935
            $statusClass = $statustrans[$status];
936
        }
937
938
        $billedtext = '';
939
        if ($billed) {
940
            $billedtext = ' - ' . $langs->trans("Billed");
941
        }
942
        if ($status == 5 && $billed) {
943
            $statusClass = 'status6';
944
        }
945
946
        $statusLong = $langs->transnoentitiesnoconv($this->labelStatus[$status]) . $billedtext;
947
        $statusShort = $langs->transnoentitiesnoconv($this->labelStatusShort[$status]);
948
949
        $parameters = array('status' => $status, 'mode' => $mode, 'billed' => $billed);
950
        $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
951
        if ($reshook > 0) {
952
            return $hookmanager->resPrint;
953
        }
954
955
        return dolGetStatus($statusLong, $statusShort, '', $statusClass, $mode);
956
    }
957
958
    /**
959
     * getTooltipContentArray
960
     *
961
     * @param array $params ex option, infologin
962
     * @since v18
963
     * @return array
964
     */
965
    public function getTooltipContentArray($params)
966
    {
967
        global $conf, $langs, $user;
968
969
        $langs->loadLangs(['bills', 'orders']);
970
971
        $datas = [];
972
        $nofetch = !empty($params['nofetch']);
973
974
        if ($user->hasRight("fournisseur", "commande", "read")) {
975
            $datas['picto'] = '<u class="paddingrightonly">' . $langs->trans("SupplierOrder") . '</u>';
976
            if (isset($this->statut)) {
977
                $datas['picto'] .= ' ' . $this->getLibStatut(5);
978
            }
979
            if (!empty($this->ref)) {
980
                $datas['ref'] = '<br><b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
981
            }
982
            if (!empty($this->ref_supplier)) {
983
                $datas['refsupplier'] = '<br><b>' . $langs->trans('RefSupplier') . ':</b> ' . $this->ref_supplier;
984
            }
985
            if (!$nofetch) {
986
                $langs->load('companies');
987
                if (empty($this->thirdparty)) {
988
                    $this->fetch_thirdparty();
989
                }
990
                $datas['supplier'] = '<br><b>' . $langs->trans('Supplier') . ':</b> ' . $this->thirdparty->getNomUrl(1, '', 0, 1);
991
            }
992
            if (!empty($this->total_ht)) {
993
                $datas['totalht'] = '<br><b>' . $langs->trans('AmountHT') . ':</b> ' . price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
994
            }
995
            if (!empty($this->total_tva)) {
996
                $datas['totaltva'] = '<br><b>' . $langs->trans('VAT') . ':</b> ' . price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
997
            }
998
            if (!empty($this->total_ttc)) {
999
                $datas['totalttc'] = '<br><b>' . $langs->trans('AmountTTC') . ':</b> ' . price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
1000
            }
1001
            if (!empty($this->date)) {
1002
                $datas['date'] = '<br><b>' . $langs->trans('Date') . ':</b> ' . dol_print_date($this->date, 'day');
1003
            }
1004
            if (!empty($this->delivery_date)) {
1005
                $datas['deliverydate'] = '<br><b>' . $langs->trans('DeliveryDate') . ':</b> ' . dol_print_date($this->delivery_date, 'dayhour');
1006
            }
1007
        }
1008
        return $datas;
1009
    }
1010
1011
    /**
1012
     *  Return clicable name (with picto eventually)
1013
     *
1014
     *  @param      int     $withpicto                  0=No picto, 1=Include picto into link, 2=Only picto
1015
     *  @param      string  $option                     On what the link points
1016
     *  @param      int     $notooltip                  1=Disable tooltip
1017
     *  @param      int     $save_lastsearch_value      -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
1018
     *  @param      int     $addlinktonotes             Add link to show notes
1019
     *  @return     string                              Chain with URL
1020
     */
1021
    public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
1022
    {
1023
        global $langs, $user, $hookmanager;
1024
1025
        $result = '';
1026
        $params = [
1027
            'id' => $this->id,
1028
            'objecttype' => $this->element,
1029
            'option' => $option,
1030
            'nofetch' => 1
1031
        ];
1032
        $classfortooltip = 'classfortooltip';
1033
        $dataparams = '';
1034
        if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1035
            $classfortooltip = 'classforajaxtooltip';
1036
            $dataparams = ' data-params="' . dol_escape_htmltag(json_encode($params)) . '"';
1037
            $label = '';
1038
        } else {
1039
            $label = implode($this->getTooltipContentArray($params));
1040
        }
1041
1042
        $url = constant('BASE_URL') . '/fourn/commande/card.php?id=' . $this->id;
1043
1044
        if ($option !== 'nolink') {
1045
            // Add param to save lastsearch_values or not
1046
            $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1047
            if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1048
                $add_save_lastsearch_values = 1;
1049
            }
1050
            if ($add_save_lastsearch_values) {
1051
                $url .= '&save_lastsearch_values=1';
1052
            }
1053
        }
1054
1055
        $linkclose = '';
1056
        if (empty($notooltip)) {
1057
            if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1058
                $label = $langs->trans("ShowOrder");
1059
                $linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
1060
            }
1061
            $linkclose .= ($label ? ' title="' . dol_escape_htmltag($label, 1) . '"' : ' title="tocomplete"');
1062
            $linkclose .= $dataparams . ' class="' . $classfortooltip . '"';
1063
        }
1064
1065
        $linkstart = '<a href="' . $url . '"';
1066
        $linkstart .= $linkclose . '>';
1067
        $linkend = '</a>';
1068
1069
        $result .= $linkstart;
1070
        if ($withpicto) {
1071
            $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
1072
        }
1073
        if ($withpicto != 2) {
1074
            $result .= $this->ref;
1075
        }
1076
        $result .= $linkend;
1077
1078
        if ($addlinktonotes) {
1079
            $txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
1080
            if ($txttoshow) {
1081
                $notetoshow = $langs->trans("ViewPrivateNote") . ':<br>' . dol_string_nohtmltag($txttoshow, 1);
1082
                $result .= ' <span class="note inline-block">';
1083
                $result .= '<a href="' . constant('BASE_URL') . '/fourn/commande/note.php?id=' . $this->id . '" class="classfortooltip" title="' . dol_escape_htmltag($notetoshow) . '">';
1084
                $result .= img_picto('', 'note');
1085
                $result .= '</a>';
1086
                //$result.=img_picto($langs->trans("ViewNote"),'object_generic');
1087
                //$result.='</a>';
1088
                $result .= '</span>';
1089
            }
1090
        }
1091
1092
        global $action;
1093
        $hookmanager->initHooks(array($this->element . 'dao'));
1094
        $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1095
        $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1096
        if ($reshook > 0) {
1097
            $result = $hookmanager->resPrint;
1098
        } else {
1099
            $result .= $hookmanager->resPrint;
1100
        }
1101
        return $result;
1102
    }
1103
1104
1105
    /**
1106
     *  Returns the next order reference not used, based on the
1107
     *  numbering model defined within COMMANDE_SUPPLIER_ADDON_NUMBER
1108
     *
1109
     *  @param      Societe     $soc        company object
1110
     *  @return     string|int              free reference for the invoice. '', -1 or -2 if error.
1111
     */
1112
    public function getNextNumRef($soc)
1113
    {
1114
        global $langs, $conf;
1115
        $langs->load("orders");
1116
1117
        if (getDolGlobalString('COMMANDE_SUPPLIER_ADDON_NUMBER')) {
1118
            $mybool = false;
1119
1120
            $file = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_NUMBER') . '.php';
1121
            $classname = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_NUMBER');
1122
1123
            // Include file with class
1124
            $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
1125
1126
            foreach ($dirmodels as $reldir) {
1127
                $dir = dol_buildpath($reldir . "core/modules/supplier_order/");
1128
1129
                // Load file with numbering class (if found)
1130
                $mybool = ((bool) @include_once $dir . $file) || $mybool;
1131
            }
1132
1133
            if ($mybool === false) {
1134
                dol_print_error(null, "Failed to include file " . $file);
1135
                return '';
1136
            }
1137
1138
            $obj = new $classname();
1139
            $numref = $obj->getNextValue($soc, $this);
1140
1141
            if ($numref != "") {
1142
                return $numref;
1143
            } else {
1144
                $this->error = $obj->error;
1145
                return -1;
1146
            }
1147
        } else {
1148
            $this->error = "Error_COMMANDE_SUPPLIER_ADDON_NotDefined";
1149
            return -2;
1150
        }
1151
    }
1152
    /**
1153
     *  Class invoiced the supplier order
1154
     *
1155
     *  @param      User        $user       Object user making the change
1156
     *  @return     int                     Return integer <0 if KO, 0 if already billed,  >0 if OK
1157
     */
1158
    public function classifyBilled(User $user)
1159
    {
1160
        $error = 0;
1161
1162
        if ($this->billed) {
1163
            return 0;
1164
        }
1165
1166
        $this->db->begin();
1167
1168
        $sql = 'UPDATE ' . $this->db->prefix() . 'commande_fournisseur SET billed = 1';
1169
        $sql .= " WHERE rowid = " . ((int) $this->id) . ' AND fk_statut > ' . self::STATUS_DRAFT;
1170
1171
        if ($this->db->query($sql)) {
1172
            if (!$error) {
1173
                // Call trigger
1174
                $result = $this->call_trigger('ORDER_SUPPLIER_CLASSIFY_BILLED', $user);
1175
                if ($result < 0) {
1176
                    $error++;
1177
                }
1178
                // End call triggers
1179
            }
1180
1181
            if (!$error) {
1182
                $this->billed = 1;
1183
1184
                $this->db->commit();
1185
                return 1;
1186
            } else {
1187
                $this->db->rollback();
1188
                return -1;
1189
            }
1190
        } else {
1191
            dol_print_error($this->db);
1192
1193
            $this->db->rollback();
1194
            return -1;
1195
        }
1196
    }
1197
1198
    /**
1199
     *  Approve a supplier order
1200
     *
1201
     *  @param  User    $user           Object user
1202
     *  @param  int     $idwarehouse    Id of warhouse for stock change
1203
     *  @param  int     $secondlevel    0=Standard approval, 1=Second level approval (used when option SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED is set)
1204
     *  @return int                     Return integer <0 if KO, >0 if OK
1205
     */
1206
    public function approve($user, $idwarehouse = 0, $secondlevel = 0)
1207
    {
1208
        global $langs, $conf;
1209
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
1210
1211
        $error = 0;
1212
1213
        dol_syslog(get_class($this) . "::approve");
1214
1215
        if ($user->hasRight("fournisseur", "commande", "approuver")) {
1216
            $now = dol_now();
1217
1218
            $this->db->begin();
1219
1220
            // Definition of order numbering model name
1221
            $soc = new Societe($this->db);
1222
            $soc->fetch($this->fourn_id);
1223
1224
            // Check if object has a temporary ref
1225
            if (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
1226
                $num = $this->getNextNumRef($soc);
1227
            } else {
1228
                $num = $this->ref;
1229
            }
1230
            $this->newref = dol_sanitizeFileName($num);
1231
1232
            // Do we have to change status now ? (If double approval is required and first approval, we keep status to 1 = validated)
1233
            $movetoapprovestatus = true;
1234
            $comment = '';
1235
1236
            $sql = "UPDATE " . $this->db->prefix() . "commande_fournisseur";
1237
            $sql .= " SET ref='" . $this->db->escape($num) . "',";
1238
            if (empty($secondlevel)) {  // standard or first level approval
1239
                $sql .= " date_approve='" . $this->db->idate($now) . "',";
1240
                $sql .= " fk_user_approve = " . $user->id;
1241
                if (getDolGlobalString('SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED') && $this->total_ht >= $conf->global->SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED) {
1242
                    if (empty($this->user_approve_id2)) {
1243
                        $movetoapprovestatus = false; // second level approval not done
1244
                        $comment = ' (first level)';
1245
                    }
1246
                }
1247
            } else { // request a second level approval
1248
                $sql .= " date_approve2='" . $this->db->idate($now) . "',";
1249
                $sql .= " fk_user_approve2 = " . ((int) $user->id);
1250
                if (empty($this->user_approve_id)) {
1251
                    $movetoapprovestatus = false; // first level approval not done
1252
                }
1253
                $comment = ' (second level)';
1254
            }
1255
            // If double approval is required and first approval, we keep status to 1 = validated
1256
            if ($movetoapprovestatus) {
1257
                $sql .= ", fk_statut = " . self::STATUS_ACCEPTED;
1258
            } else {
1259
                $sql .= ", fk_statut = " . self::STATUS_VALIDATED;
1260
            }
1261
            $sql .= " WHERE rowid = " . ((int) $this->id);
1262
            $sql .= " AND fk_statut = " . self::STATUS_VALIDATED;
1263
1264
            if ($this->db->query($sql)) {
1265
                if (getDolGlobalString('SUPPLIER_ORDER_AUTOADD_USER_CONTACT')) {
1266
                    $result = $this->add_contact($user->id, 'SALESREPFOLL', 'internal', 1);
1267
                    if ($result < 0 && $result != -2) { // -2 means already exists
1268
                        $error++;
1269
                    }
1270
                }
1271
1272
                // If stock is incremented on validate order, we must increment it
1273
                if (!$error && $movetoapprovestatus && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER')) {
1274
                    require_once constant('DOL_DOCUMENT_ROOT') . '/product/stock/class/mouvementstock.class.php';
1275
                    $langs->load("agenda");
1276
1277
                    $cpt = count($this->lines);
1278
                    for ($i = 0; $i < $cpt; $i++) {
1279
                        // Product with reference
1280
                        if ($this->lines[$i]->fk_product > 0) {
1281
                            $this->line = $this->lines[$i];
1282
                            $mouvP = new MouvementStock($this->db);
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\Fourn\Classes\MouvementStock was not found. Did you mean MouvementStock? If so, make sure to prefix the type with \.
Loading history...
1283
                            $mouvP->origin = &$this;
1284
                            $mouvP->setOrigin($this->element, $this->id);
1285
                            // We decrement stock of product (and sub-products)
1286
                            $up_ht_disc = $this->lines[$i]->subprice;
1287
                            if (!empty($this->lines[$i]->remise_percent) && !getDolGlobalString('STOCK_EXCLUDE_DISCOUNT_FOR_PMP')) {
1288
                                $up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
1289
                            }
1290
                            $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("OrderApprovedInDolibarr", $this->ref));
1291
                            if ($result < 0) {
1292
                                $error++;
1293
                            }
1294
                            unset($this->line);
1295
                        }
1296
                    }
1297
                }
1298
1299
                if (!$error) {
1300
                    // Call trigger
1301
                    $result = $this->call_trigger('ORDER_SUPPLIER_APPROVE', $user);
1302
                    if ($result < 0) {
1303
                        $error++;
1304
                    }
1305
                    // End call triggers
1306
                }
1307
1308
                if (!$error) {
1309
                    $this->ref = $this->newref;
1310
1311
                    if ($movetoapprovestatus) {
1312
                        $this->statut = self::STATUS_ACCEPTED;
1313
                    } else {
1314
                        $this->statut = self::STATUS_VALIDATED;
1315
                    }
1316
                    if (empty($secondlevel)) {  // standard or first level approval
1317
                        $this->date_approve = $now;
1318
                        $this->user_approve_id = $user->id;
1319
                    } else { // request a second level approval
1320
                        $this->date_approve2 = $now;
1321
                        $this->user_approve_id2 = $user->id;
1322
                    }
1323
1324
                    $this->db->commit();
1325
                    return 1;
1326
                } else {
1327
                    $this->db->rollback();
1328
                    return -1;
1329
                }
1330
            } else {
1331
                $this->db->rollback();
1332
                $this->error = $this->db->lasterror();
1333
                return -1;
1334
            }
1335
        } else {
1336
            dol_syslog(get_class($this) . "::approve Not Authorized", LOG_ERR);
1337
        }
1338
        return -1;
1339
    }
1340
1341
    /**
1342
     *  Refuse an order
1343
     *
1344
     *  @param      User    $user       User making action
1345
     *  @return     int                 0 if Ok, <0 if Ko
1346
     */
1347
    public function refuse($user)
1348
    {
1349
        global $conf, $langs;
1350
1351
        $error = 0;
1352
1353
        dol_syslog(get_class($this) . "::refuse");
1354
        $result = 0;
1355
        if ($user->hasRight("fournisseur", "commande", "approuver")) {
1356
            $this->db->begin();
1357
1358
            $sql = "UPDATE " . $this->db->prefix() . "commande_fournisseur SET fk_statut = " . self::STATUS_REFUSED;
1359
            $sql .= " WHERE rowid = " . ((int) $this->id);
1360
1361
            if ($this->db->query($sql)) {
1362
                $result = 0;
1363
1364
                if ($error == 0) {
1365
                    // Call trigger
1366
                    $result = $this->call_trigger('ORDER_SUPPLIER_REFUSE', $user);
1367
                    if ($result < 0) {
1368
                        $error++;
1369
                        $this->db->rollback();
1370
                    } else {
1371
                        $this->db->commit();
1372
                    }
1373
                    // End call triggers
1374
                }
1375
            } else {
1376
                $this->db->rollback();
1377
                $this->error = $this->db->lasterror();
1378
                dol_syslog(get_class($this) . "::refuse Error -1");
1379
                $result = -1;
1380
            }
1381
        } else {
1382
            dol_syslog(get_class($this) . "::refuse Not Authorized");
1383
        }
1384
        return $result;
1385
    }
1386
1387
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1388
    /**
1389
     *  Cancel an approved order.
1390
     *  The cancellation is done after approval
1391
     *
1392
     *  @param  User    $user           User making action
1393
     *  @param  int     $idwarehouse    Id warehouse to use for stock change (not used for supplier orders).
1394
     *  @return int                     >0 if Ok, <0 if Ko
1395
     */
1396
    public function Cancel($user, $idwarehouse = -1)
1397
    {
1398
		// phpcs:enable
1399
        global $langs, $conf;
1400
1401
        $error = 0;
1402
1403
        //dol_syslog("CommandeFournisseur::Cancel");
1404
        $result = 0;
1405
        if ($user->hasRight("fournisseur", "commande", "commander")) {
1406
            $statut = self::STATUS_CANCELED;
1407
1408
            $this->db->begin();
1409
1410
            $sql = "UPDATE " . $this->db->prefix() . "commande_fournisseur SET fk_statut = " . ((int) $statut);
1411
            $sql .= " WHERE rowid = " . ((int) $this->id);
1412
            dol_syslog(get_class($this) . "::cancel", LOG_DEBUG);
1413
            if ($this->db->query($sql)) {
1414
                $result = 0;
1415
1416
                // Call trigger
1417
                $result = $this->call_trigger('ORDER_SUPPLIER_CANCEL', $user);
1418
                if ($result < 0) {
1419
                    $error++;
1420
                }
1421
                // End call triggers
1422
1423
                if ($error == 0) {
1424
                    $this->db->commit();
1425
                    return 1;
1426
                } else {
1427
                    $this->db->rollback();
1428
                    return -1;
1429
                }
1430
            } else {
1431
                $this->db->rollback();
1432
                $this->error = $this->db->lasterror();
1433
                dol_syslog(get_class($this) . "::cancel " . $this->error);
1434
                return -1;
1435
            }
1436
        } else {
1437
            dol_syslog(get_class($this) . "::cancel Not Authorized");
1438
            return -1;
1439
        }
1440
    }
1441
1442
    /**
1443
     *  Submit a supplier order to supplier
1444
     *
1445
     *  @param      User    $user       User making change
1446
     *  @param      integer $date       Date
1447
     *  @param      int     $methode    Method
1448
     *  @param      string  $comment    Comment
1449
     *  @return     int                 Return integer <0 if KO, >0 if OK
1450
     */
1451
    public function commande($user, $date, $methode, $comment = '')
1452
    {
1453
        global $langs;
1454
        dol_syslog(get_class($this) . "::commande");
1455
        $error = 0;
1456
        if ($user->hasRight("fournisseur", "commande", "commander")) {
1457
            $this->db->begin();
1458
1459
            $newnoteprivate = $this->note_private;
1460
            if ($comment) {
1461
                $newnoteprivate = dol_concatdesc($newnoteprivate, $langs->trans("Comment") . ': ' . $comment);
1462
            }
1463
1464
            $sql = "UPDATE " . $this->db->prefix() . "commande_fournisseur";
1465
            $sql .= " SET fk_statut=" . self::STATUS_ORDERSENT . ", fk_input_method=" . $methode . ", date_commande='" . $this->db->idate($date) . "', ";
1466
            $sql .= " note_private='" . $this->db->escape($newnoteprivate) . "'";
1467
            $sql .= " WHERE rowid=" . ((int) $this->id);
1468
1469
            dol_syslog(get_class($this) . "::commande", LOG_DEBUG);
1470
            if ($this->db->query($sql)) {
1471
                $this->statut = self::STATUS_ORDERSENT;
1472
                $this->methode_commande_id = $methode;
1473
                $this->date_commande = $date;
1474
                $this->context = array('comments' => $comment);
1475
1476
                // Call trigger
1477
                $result = $this->call_trigger('ORDER_SUPPLIER_SUBMIT', $user);
1478
                if ($result < 0) {
1479
                    $error++;
1480
                }
1481
                // End call triggers
1482
            } else {
1483
                $error++;
1484
                $this->error = $this->db->lasterror();
1485
                $this->errors[] = $this->db->lasterror();
1486
            }
1487
1488
            if (!$error) {
1489
                $this->db->commit();
1490
            } else {
1491
                $this->db->rollback();
1492
            }
1493
        } else {
1494
            $error++;
1495
            $this->error = $langs->trans('NotAuthorized');
1496
            $this->errors[] = $langs->trans('NotAuthorized');
1497
            dol_syslog(get_class($this) . "::commande User not Authorized", LOG_WARNING);
1498
        }
1499
1500
        return ($error ? -1 : 1);
1501
    }
1502
1503
    /**
1504
     *  Create order with draft status
1505
     *
1506
     *  @param      User    $user       User making creation
1507
     *  @param      int     $notrigger  Disable all triggers
1508
     *  @return     int                 Return integer <0 if KO, Id of supplier order if OK
1509
     */
1510
    public function create($user, $notrigger = 0)
1511
    {
1512
        global $langs, $conf, $hookmanager;
1513
1514
        $this->db->begin();
1515
1516
        $error = 0;
1517
        $now = dol_now();
1518
1519
        // set tmp vars
1520
        $date = ($this->date_commande ? $this->date_commande : $this->date); // in case of date is set
1521
        if (empty($date)) {
1522
            $date = $now;
1523
        }
1524
        $delivery_date = $this->delivery_date;
1525
1526
        // Clean parameters
1527
        if (empty($this->source)) {
1528
            $this->source = 0;
1529
        }
1530
1531
        // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1532
        if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1533
            list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $date);
1534
        } else {
1535
            $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1536
        }
1537
        if (empty($this->fk_multicurrency)) {
1538
            $this->multicurrency_code = $conf->currency;
1539
            $this->fk_multicurrency = 0;
1540
            $this->multicurrency_tx = 1;
1541
        }
1542
1543
        $this->statut = self::STATUS_DRAFT; // deprecated
1544
        $this->status = self::STATUS_DRAFT;
1545
1546
        $sql = "INSERT INTO " . $this->db->prefix() . "commande_fournisseur (";
1547
        $sql .= "ref";
1548
        $sql .= ", ref_supplier";
1549
        $sql .= ", note_private";
1550
        $sql .= ", note_public";
1551
        $sql .= ", entity";
1552
        $sql .= ", fk_soc";
1553
        $sql .= ", fk_projet";
1554
        $sql .= ", date_creation";
1555
        $sql .= ", date_livraison";
1556
        $sql .= ", fk_user_author";
1557
        $sql .= ", fk_statut";
1558
        $sql .= ", source";
1559
        $sql .= ", model_pdf";
1560
        $sql .= ", fk_mode_reglement";
1561
        $sql .= ", fk_cond_reglement";
1562
        $sql .= ", fk_account";
1563
        $sql .= ", fk_incoterms, location_incoterms";
1564
        $sql .= ", fk_multicurrency";
1565
        $sql .= ", multicurrency_code";
1566
        $sql .= ", multicurrency_tx";
1567
        $sql .= ") ";
1568
        $sql .= " VALUES (";
1569
        $sql .= "'(PROV)'";
1570
        $sql .= ", " . (isset($this->ref_supplier) ? "'" . $this->db->escape($this->ref_supplier) . "'" : "NULL");
1571
        $sql .= ", '" . $this->db->escape($this->note_private) . "'";
1572
        $sql .= ", '" . $this->db->escape($this->note_public) . "'";
1573
        $sql .= ", " . setEntity($this);
1574
        $sql .= ", " . ((int) $this->socid);
1575
        $sql .= ", " . ($this->fk_project > 0 ? ((int) $this->fk_project) : "null");
1576
        $sql .= ", '" . $this->db->idate($date) . "'";
1577
        $sql .= ", " . ($delivery_date ? "'" . $this->db->idate($delivery_date) . "'" : "null");
1578
        $sql .= ", " . ((int) $user->id);
1579
        $sql .= ", " . self::STATUS_DRAFT;
1580
        $sql .= ", " . ((int) $this->source);
1581
        $sql .= ", '" . $this->db->escape(getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF')) . "'";
1582
        $sql .= ", " . ($this->mode_reglement_id > 0 ? $this->mode_reglement_id : 'null');
1583
        $sql .= ", " . ($this->cond_reglement_id > 0 ? $this->cond_reglement_id : 'null');
1584
        $sql .= ", " . ($this->fk_account > 0 ? $this->fk_account : 'NULL');
1585
        $sql .= ", " . (int) $this->fk_incoterms;
1586
        $sql .= ", '" . $this->db->escape($this->location_incoterms) . "'";
1587
        $sql .= ", " . (int) $this->fk_multicurrency;
1588
        $sql .= ", '" . $this->db->escape($this->multicurrency_code) . "'";
1589
        $sql .= ", " . (float) $this->multicurrency_tx;
1590
        $sql .= ")";
1591
1592
        dol_syslog(get_class($this) . "::create", LOG_DEBUG);
1593
        if ($this->db->query($sql)) {
1594
            $this->id = $this->db->last_insert_id($this->db->prefix() . "commande_fournisseur");
1595
1596
            if ($this->id) {
1597
                $num = count($this->lines);
1598
1599
                // insert products details into database
1600
                for ($i = 0; $i < $num; $i++) {
1601
                    $line = $this->lines[$i];
1602
                    if (!is_object($line)) {
1603
                        $line = (object) $line;
1604
                    }
1605
1606
                    //$this->special_code = $line->special_code; // TODO : remove this in 9.0 and add special_code param to addline()
1607
1608
                    // This include test on qty if option SUPPLIER_ORDER_WITH_NOPRICEDEFINED is not set
1609
                    $result = $this->addline(
1610
                        $line->desc,
1611
                        $line->subprice,
1612
                        $line->qty,
1613
                        $line->tva_tx,
1614
                        $line->localtax1_tx,
1615
                        $line->localtax2_tx,
1616
                        $line->fk_product,
1617
                        0,
1618
                        $line->ref_fourn, // $line->ref_fourn comes from field ref into table of lines. Value may ba a ref that does not exists anymore, so we first try with value of product
1619
                        $line->remise_percent,
1620
                        'HT',
1621
                        0,
1622
                        $line->product_type,
1623
                        $line->info_bits,
1624
                        false,
1625
                        $line->date_start,
1626
                        $line->date_end,
1627
                        $line->array_options,
1628
                        $line->fk_unit,
1629
                        $line->multicurrency_subprice,  // pu_ht_devise
1630
                        $line->origin,     // origin
1631
                        $line->origin_id,  // origin_id
1632
                        $line->rang,       // rang
1633
                        $line->special_code
1634
                    );
1635
                    if ($result < 0) {
1636
                        dol_syslog(get_class($this) . "::create " . $this->error, LOG_WARNING); // do not use dol_print_error here as it may be a functional error
1637
                        $this->db->rollback();
1638
                        return -1;
1639
                    }
1640
                }
1641
1642
                $sql = "UPDATE " . $this->db->prefix() . "commande_fournisseur";
1643
                $sql .= " SET ref='(PROV" . $this->id . ")'";
1644
                $sql .= " WHERE rowid=" . ((int) $this->id);
1645
1646
                dol_syslog(get_class($this) . "::create", LOG_DEBUG);
1647
                if ($this->db->query($sql)) {
1648
                    // Add link with price request and supplier order
1649
                    if ($this->id) {
1650
                        $this->ref = "(PROV" . $this->id . ")";
1651
1652
                        if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) {  // To use new linkedObjectsIds instead of old linked_objects
1653
                            $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1654
                        }
1655
1656
                        // Add object linked
1657
                        if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1658
                            foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1659
                                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, ...))
1660
                                    foreach ($tmp_origin_id as $origin_id) {
1661
                                        $ret = $this->add_object_linked($origin, $origin_id);
1662
                                        if (!$ret) {
1663
                                            dol_print_error($this->db);
1664
                                            $error++;
1665
                                        }
1666
                                    }
1667
                                } else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1668
                                    $origin_id = $tmp_origin_id;
1669
                                    $ret = $this->add_object_linked($origin, $origin_id);
1670
                                    if (!$ret) {
1671
                                        dol_print_error($this->db);
1672
                                        $error++;
1673
                                    }
1674
                                }
1675
                            }
1676
                        }
1677
                    }
1678
1679
                    if (!$error) {
1680
                        $result = $this->insertExtraFields();
1681
                        if ($result < 0) {
1682
                            $error++;
1683
                        }
1684
                    }
1685
1686
                    if (!$error && !$notrigger) {
1687
                        // Call trigger
1688
                        $result = $this->call_trigger('ORDER_SUPPLIER_CREATE', $user);
1689
                        if ($result < 0) {
1690
                            $this->db->rollback();
1691
1692
                            return -1;
1693
                        }
1694
                        // End call triggers
1695
                    }
1696
1697
                    $this->db->commit();
1698
                    return $this->id;
1699
                } else {
1700
                    $this->error = $this->db->lasterror();
1701
                    $this->db->rollback();
1702
1703
                    return -2;
1704
                }
1705
            } else {
1706
                $this->error = 'Failed to get ID of inserted line';
1707
1708
                return -1;
1709
            }
1710
        } else {
1711
            $this->error = $this->db->lasterror();
1712
            $this->db->rollback();
1713
1714
            return -1;
1715
        }
1716
    }
1717
1718
    /**
1719
     *  Update Supplier Order
1720
     *
1721
     *  @param      User    $user           User that modify
1722
     *  @param      int     $notrigger      0=launch triggers after, 1=disable triggers
1723
     *  @return     int                     Return integer <0 if KO, >0 if OK
1724
     */
1725
    public function update(User $user, $notrigger = 0)
1726
    {
1727
        global $conf;
1728
1729
        $error = 0;
1730
1731
        // Clean parameters
1732
        if (isset($this->ref)) {
1733
            $this->ref = trim($this->ref);
1734
        }
1735
        if (isset($this->ref_supplier)) {
1736
            $this->ref_supplier = trim($this->ref_supplier);
1737
        }
1738
        if (isset($this->note_private)) {
1739
            $this->note_private = trim($this->note_private);
1740
        }
1741
        if (isset($this->note_public)) {
1742
            $this->note_public = trim($this->note_public);
1743
        }
1744
        if (isset($this->model_pdf)) {
1745
            $this->model_pdf = trim($this->model_pdf);
1746
        }
1747
        if (isset($this->import_key)) {
1748
            $this->import_key = trim($this->import_key);
1749
        }
1750
1751
        // Update request
1752
        $sql = "UPDATE " . $this->db->prefix() . $this->table_element . " SET";
1753
1754
        $sql .= " ref=" . (isset($this->ref) ? "'" . $this->db->escape($this->ref) . "'" : "null") . ",";
1755
        $sql .= " ref_supplier=" . (isset($this->ref_supplier) ? "'" . $this->db->escape($this->ref_supplier) . "'" : "null") . ",";
1756
        $sql .= " ref_ext=" . (isset($this->ref_ext) ? "'" . $this->db->escape($this->ref_ext) . "'" : "null") . ",";
1757
        $sql .= " fk_soc=" . (isset($this->socid) ? $this->socid : "null") . ",";
1758
        $sql .= " date_commande=" . (strval($this->date_commande) != '' ? "'" . $this->db->idate($this->date_commande) . "'" : 'null') . ",";
1759
        $sql .= " date_valid=" . (strval($this->date_validation) != '' ? "'" . $this->db->idate($this->date_validation) . "'" : 'null') . ",";
1760
        $sql .= " total_tva=" . (isset($this->total_tva) ? $this->total_tva : "null") . ",";
1761
        $sql .= " localtax1=" . (isset($this->total_localtax1) ? $this->total_localtax1 : "null") . ",";
1762
        $sql .= " localtax2=" . (isset($this->total_localtax2) ? $this->total_localtax2 : "null") . ",";
1763
        $sql .= " total_ht=" . (isset($this->total_ht) ? $this->total_ht : "null") . ",";
1764
        $sql .= " total_ttc=" . (isset($this->total_ttc) ? $this->total_ttc : "null") . ",";
1765
        $sql .= " fk_statut=" . (isset($this->statut) ? $this->statut : "null") . ",";
1766
        $sql .= " fk_user_author=" . (isset($this->user_author_id) ? $this->user_author_id : "null") . ",";
1767
        $sql .= " fk_user_valid=" . (isset($this->user_validation_id) && $this->user_validation_id > 0 ? $this->user_validation_id : "null") . ",";
1768
        $sql .= " fk_projet=" . (isset($this->fk_project) ? $this->fk_project : "null") . ",";
1769
        $sql .= " fk_cond_reglement=" . (isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null") . ",";
1770
        $sql .= " fk_mode_reglement=" . (isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null") . ",";
1771
        $sql .= " date_livraison=" . (strval($this->delivery_date) != '' ? "'" . $this->db->idate($this->delivery_date) . "'" : 'null') . ",";
1772
        //$sql .= " fk_shipping_method=".(isset($this->shipping_method_id) ? $this->shipping_method_id : "null").",";
1773
        $sql .= " fk_account=" . ($this->fk_account > 0 ? $this->fk_account : "null") . ",";
1774
        //$sql .= " fk_input_reason=".($this->demand_reason_id > 0 ? $this->demand_reason_id : "null").",";
1775
        $sql .= " note_private=" . (isset($this->note_private) ? "'" . $this->db->escape($this->note_private) . "'" : "null") . ",";
1776
        $sql .= " note_public=" . (isset($this->note_public) ? "'" . $this->db->escape($this->note_public) . "'" : "null") . ",";
1777
        $sql .= " model_pdf=" . (isset($this->model_pdf) ? "'" . $this->db->escape($this->model_pdf) . "'" : "null") . ",";
1778
        $sql .= " import_key=" . (isset($this->import_key) ? "'" . $this->db->escape($this->import_key) . "'" : "null");
1779
1780
        $sql .= " WHERE rowid=" . ((int) $this->id);
1781
1782
        $this->db->begin();
1783
1784
        dol_syslog(get_class($this) . "::update", LOG_DEBUG);
1785
        $resql = $this->db->query($sql);
1786
        if (!$resql) {
1787
            $error++;
1788
            $this->errors[] = "Error " . $this->db->lasterror();
1789
        }
1790
1791
        if (!$error) {
1792
            $result = $this->insertExtraFields();
1793
            if ($result < 0) {
1794
                $error++;
1795
            }
1796
        }
1797
1798
        if (!$error && !$notrigger) {
1799
            // Call trigger
1800
            $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
1801
            if ($result < 0) {
1802
                $error++;
1803
            }
1804
            // End call triggers
1805
        }
1806
1807
        // Commit or rollback
1808
        if ($error) {
1809
            foreach ($this->errors as $errmsg) {
1810
                dol_syslog(get_class($this) . "::update " . $errmsg, LOG_ERR);
1811
                $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
1812
            }
1813
            $this->db->rollback();
1814
            return -1 * $error;
1815
        } else {
1816
            $this->db->commit();
1817
            return 1;
1818
        }
1819
    }
1820
1821
    /**
1822
     *  Load an object from its id and create a new one in database
1823
     *
1824
     *  @param      User    $user       User making the clone
1825
     *  @param      int     $socid      Id of thirdparty
1826
     *  @param      int     $notrigger  Disable all triggers
1827
     *  @return     int                 New id of clone
1828
     */
1829
    public function createFromClone(User $user, $socid = 0, $notrigger = 0)
1830
    {
1831
        global $conf, $user, $hookmanager;
1832
1833
        $error = 0;
1834
1835
        $this->db->begin();
1836
1837
        // get extrafields so they will be clone
1838
        foreach ($this->lines as $line) {
1839
            $line->fetch_optionals();
1840
        }
1841
1842
        // Load source object
1843
        $objFrom = clone $this;
1844
1845
        // Change socid if needed
1846
        if (!empty($socid) && $socid != $this->socid) {
1847
            $objsoc = new Societe($this->db);
1848
1849
            if ($objsoc->fetch($socid) > 0) {
1850
                $this->socid = $objsoc->id;
1851
                $this->cond_reglement_id    = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1852
                $this->mode_reglement_id    = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1853
                $this->fk_project = 0;
1854
                $this->fk_delivery_address = 0;
1855
            }
1856
1857
            // TODO Change product price if multi-prices
1858
        }
1859
1860
        $this->id = 0;
1861
        $this->statut = self::STATUS_DRAFT;
1862
1863
        // Clear fields
1864
        $this->user_author_id     = $user->id;
1865
        $this->user_validation_id = 0;
1866
1867
        $this->date               = dol_now();
1868
        $this->date_creation      = 0;
1869
        $this->date_validation    = 0;
1870
        $this->date_commande      = 0;
1871
        $this->ref_supplier       = '';
1872
        $this->user_approve_id    = 0;
1873
        $this->user_approve_id2   = 0;
1874
        $this->date_approve       = 0;
1875
        $this->date_approve2      = 0;
1876
1877
        // Create clone
1878
        $this->context['createfromclone'] = 'createfromclone';
1879
        $result = $this->create($user, $notrigger);
1880
        if ($result < 0) {
1881
            $error++;
1882
        }
1883
1884
        if (!$error) {
1885
            // Hook of thirdparty module
1886
            if (is_object($hookmanager)) {
1887
                $parameters = array('objFrom' => $objFrom);
1888
                $action = '';
1889
                $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1890
                if ($reshook < 0) {
1891
                    $this->setErrorsFromObject($hookmanager);
1892
                    $error++;
1893
                }
1894
            }
1895
        }
1896
1897
        unset($this->context['createfromclone']);
1898
1899
        // End
1900
        if (!$error) {
1901
            $this->db->commit();
1902
            return $this->id;
1903
        } else {
1904
            $this->db->rollback();
1905
            return -1;
1906
        }
1907
    }
1908
1909
    /**
1910
     *  Add order line
1911
     *
1912
     *  @param      string      $desc                   Description
1913
     *  @param      float       $pu_ht                  Unit price (used if $price_base_type is 'HT')
1914
     *  @param      float       $qty                    Quantity
1915
     *  @param      float       $txtva                  VAT Rate
1916
     *  @param      float       $txlocaltax1            Localtax1 tax
1917
     *  @param      float       $txlocaltax2            Localtax2 tax
1918
     *  @param      int         $fk_product             Id product
1919
     *  @param      int         $fk_prod_fourn_price    Id supplier price
1920
     *  @param      string      $ref_supplier           Supplier reference price
1921
     *  @param      float       $remise_percent         Remise
1922
     *  @param      string      $price_base_type        HT or TTC
1923
     *  @param      float       $pu_ttc                 Unit price TTC (used if $price_base_type is 'TTC')
1924
     *  @param      int         $type                   Type of line (0=product, 1=service)
1925
     *  @param      int         $info_bits              More information
1926
     *  @param      int         $notrigger              Disable triggers
1927
     *  @param      int         $date_start             Date start of service
1928
     *  @param      int         $date_end               Date end of service
1929
     *  @param      array       $array_options          extrafields array
1930
     *  @param      int|null    $fk_unit                Code of the unit to use. Null to use the default one
1931
     *  @param      int|string      $pu_ht_devise           Amount in currency
1932
     *  @param      string      $origin                 'order', ...
1933
     *  @param      int         $origin_id              Id of origin object
1934
     *  @param      int         $rang                   Rank
1935
     *  @param      int         $special_code           Special code
1936
     *  @return     int                                 Return integer <=0 if KO, >0 if OK
1937
     */
1938
    public function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1 = 0.0, $txlocaltax2 = 0.0, $fk_product = 0, $fk_prod_fourn_price = 0, $ref_supplier = '', $remise_percent = 0.0, $price_base_type = 'HT', $pu_ttc = 0.0, $type = 0, $info_bits = 0, $notrigger = 0, $date_start = null, $date_end = null, $array_options = [], $fk_unit = null, $pu_ht_devise = 0, $origin = '', $origin_id = 0, $rang = -1, $special_code = 0)
1939
    {
1940
        global $langs, $mysoc, $conf;
1941
1942
        dol_syslog(get_class($this) . "::addline $desc, $pu_ht, $qty, $txtva, $txlocaltax1, $txlocaltax2, $fk_product, $fk_prod_fourn_price, $ref_supplier, $remise_percent, $price_base_type, $pu_ttc, $type, $info_bits, $notrigger, $date_start, $date_end, $fk_unit, $pu_ht_devise, $origin, $origin_id");
1943
        include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php';
1944
1945
        if ($this->statut == self::STATUS_DRAFT) {
1946
            include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php';
1947
1948
            // Clean parameters
1949
            if (empty($qty)) {
1950
                $qty = 0;
1951
            }
1952
            if (!$info_bits) {
1953
                $info_bits = 0;
1954
            }
1955
            if (empty($txtva)) {
1956
                $txtva = 0;
1957
            }
1958
            if (empty($rang)) {
1959
                $rang = 0;
1960
            }
1961
            if (empty($txlocaltax1)) {
1962
                $txlocaltax1 = 0;
1963
            }
1964
            if (empty($txlocaltax2)) {
1965
                $txlocaltax2 = 0;
1966
            }
1967
            if (empty($remise_percent)) {
1968
                $remise_percent = 0;
1969
            }
1970
1971
            $remise_percent = price2num($remise_percent);
1972
            $qty = price2num($qty);
1973
            $pu_ht = price2num($pu_ht);
1974
            $pu_ht_devise = price2num($pu_ht_devise);
1975
            $pu_ttc = price2num($pu_ttc);
1976
            if (!preg_match('/\((.*)\)/', (string) $txtva)) {
1977
                $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
1978
            }
1979
            $txlocaltax1 = price2num($txlocaltax1);
1980
            $txlocaltax2 = price2num($txlocaltax2);
1981
            if ($price_base_type == 'HT') {
1982
                $pu = $pu_ht;
1983
            } else {
1984
                $pu = $pu_ttc;
1985
            }
1986
            $desc = trim($desc);
1987
1988
            // Check parameters
1989
            if ($qty < 0 && !$fk_product) {
1990
                $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Product"));
1991
                return -1;
1992
            }
1993
            if ($type < 0) {
1994
                return -1;
1995
            }
1996
            if ($date_start && $date_end && $date_start > $date_end) {
1997
                $langs->load("errors");
1998
                $this->error = $langs->trans('ErrorStartDateGreaterEnd');
1999
                return -1;
2000
            }
2001
2002
2003
            $this->db->begin();
2004
2005
            $product_type = $type;
2006
            $label = '';    // deprecated
2007
2008
            if ($fk_product > 0) {
2009
                if (getDolGlobalString('SUPPLIER_ORDER_WITH_PREDEFINED_PRICES_ONLY')) { // Not the common case
2010
                    // Check quantity is enough
2011
                    dol_syslog(get_class($this) . "::addline we check supplier prices fk_product=" . $fk_product . " fk_prod_fourn_price=" . $fk_prod_fourn_price . " qty=" . $qty . " ref_supplier=" . $ref_supplier);
2012
                    $prod = new ProductFournisseur($this->db);
2013
                    if ($prod->fetch($fk_product) > 0) {
2014
                        $product_type = $prod->type;
2015
                        $label = $prod->label;
2016
2017
                        // We use 'none' instead of $ref_supplier, because fourn_ref may not exists anymore. So we will take the first supplier price ok.
2018
                        // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
2019
                        $result = $prod->get_buyprice($fk_prod_fourn_price, $qty, $fk_product, 'none', (isset($this->fk_soc) ? $this->fk_soc : $this->socid)); // Search on couple $fk_prod_fourn_price/$qty first, then on triplet $qty/$fk_product/$ref_supplier/$this->fk_soc
2020
2021
                        // If supplier order created from sales order, we take best supplier price
2022
                        // If $pu (defined previously from pu_ht or pu_ttc) is not defined at all, we also take the best supplier price
2023
                        if ($result > 0 && ($origin == 'commande' || $pu === '')) {
2024
                            $pu = $prod->fourn_pu; // Unit price supplier price set by get_buyprice
2025
                            $ref_supplier = $prod->ref_supplier; // Ref supplier price set by get_buyprice
2026
                            // is remise percent not keyed but present for the product we add it
2027
                            if ($remise_percent == 0 && $prod->remise_percent != 0) {
2028
                                $remise_percent = $prod->remise_percent;
2029
                            }
2030
                        }
2031
                        if ($result == 0) {                   // If result == 0, we failed to found the supplier reference price
2032
                            $langs->load("errors");
2033
                            $this->error = "Ref " . $prod->ref . " " . $langs->trans("ErrorQtyTooLowForThisSupplier");
2034
                            $this->db->rollback();
2035
                            dol_syslog(get_class($this) . "::addline we did not found supplier price, so we can't guess unit price");
2036
                            //$pu    = $prod->fourn_pu;     // We do not overwrite unit price
2037
                            //$ref   = $prod->ref_fourn;    // We do not overwrite ref supplier price
2038
                            return -1;
2039
                        }
2040
                        if ($result == -1) {
2041
                            $langs->load("errors");
2042
                            $this->error = "Ref " . $prod->ref . " " . $langs->trans("ErrorQtyTooLowForThisSupplier");
2043
                            $this->db->rollback();
2044
                            dol_syslog(get_class($this) . "::addline result=" . $result . " - " . $this->error, LOG_DEBUG);
2045
                            return -1;
2046
                        }
2047
                        if ($result < -1) {
2048
                            $this->error = $prod->error;
2049
                            $this->db->rollback();
2050
                            dol_syslog(get_class($this) . "::addline result=" . $result . " - " . $this->error, LOG_ERR);
2051
                            return -1;
2052
                        }
2053
                    } else {
2054
                        $this->error = $prod->error;
2055
                        $this->db->rollback();
2056
                        return -1;
2057
                    }
2058
                }
2059
2060
                // Predefine quantity according to packaging
2061
                if (getDolGlobalString('PRODUCT_USE_SUPPLIER_PACKAGING')) {
2062
                    $prod = new Product($this->db);
2063
                    $prod->get_buyprice($fk_prod_fourn_price, $qty, $fk_product, 'none', (empty($this->fk_soc) ? $this->socid : $this->fk_soc));
2064
2065
                    if ($qty < $prod->packaging) {
2066
                        $qty = $prod->packaging;
2067
                    } else {
2068
                        if (!empty($prod->packaging) && ($qty % $prod->packaging) > 0) {
2069
                            $coeff = intval($qty / $prod->packaging) + 1;
2070
                            $qty = $prod->packaging * $coeff;
2071
                            setEventMessages($langs->trans('QtyRecalculatedWithPackaging'), null, 'mesgs');
2072
                        }
2073
                    }
2074
                }
2075
            }
2076
2077
            if (isModEnabled("multicurrency") && $pu_ht_devise > 0) {
2078
                $pu = 0;
2079
            }
2080
2081
            $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2082
2083
            // Clean vat code
2084
            $reg = array();
2085
            $vat_src_code = '';
2086
            if (preg_match('/\((.*)\)/', $txtva, $reg)) {
2087
                $vat_src_code = $reg[1];
2088
                $txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
2089
            }
2090
2091
            // Calcul du total TTC et de la TVA pour la ligne a partir de
2092
            // qty, pu, remise_percent et txtva
2093
            // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2094
            // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2095
2096
            $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $product_type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
2097
2098
            $total_ht  = $tabprice[0];
2099
            $total_tva = $tabprice[1];
2100
            $total_ttc = $tabprice[2];
2101
            $total_localtax1 = $tabprice[9];
2102
            $total_localtax2 = $tabprice[10];
2103
            $pu = $pu_ht = $tabprice[3];
2104
2105
            // MultiCurrency
2106
            $multicurrency_total_ht = $tabprice[16];
2107
            $multicurrency_total_tva = $tabprice[17];
2108
            $multicurrency_total_ttc = $tabprice[18];
2109
            $pu_ht_devise = $tabprice[19];
2110
2111
            $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2112
            $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2113
2114
            if ($rang < 0) {
2115
                $rangmax = $this->line_max();
2116
                $rang = $rangmax + 1;
2117
            }
2118
2119
            // Insert line
2120
            $this->line = new CommandeFournisseurLigne($this->db);
2121
2122
            $this->line->context = $this->context;
2123
2124
            $this->line->fk_commande = $this->id;
2125
            $this->line->label = $label;
2126
            $this->line->ref_fourn = $ref_supplier;
2127
            $this->line->ref_supplier = $ref_supplier;
2128
            $this->line->desc = $desc;
2129
            $this->line->qty = $qty;
2130
            $this->line->tva_tx = $txtva;
2131
            $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
2132
            $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
2133
            $this->line->localtax1_type = $localtax1_type;
2134
            $this->line->localtax2_type = $localtax2_type;
2135
            $this->line->fk_product = $fk_product;
2136
            $this->line->product_type = $product_type;
2137
            $this->line->remise_percent = $remise_percent;
2138
            $this->line->subprice = $pu_ht;
2139
            $this->line->rang = $rang;
2140
            $this->line->info_bits = $info_bits;
2141
2142
            $this->line->vat_src_code = $vat_src_code;
2143
            $this->line->total_ht = $total_ht;
2144
            $this->line->total_tva = $total_tva;
2145
            $this->line->total_localtax1 = $total_localtax1;
2146
            $this->line->total_localtax2 = $total_localtax2;
2147
            $this->line->total_ttc = $total_ttc;
2148
            $this->line->product_type = $type;
2149
            $this->line->special_code   = (!empty($special_code) ? $special_code : 0);
2150
            $this->line->origin = $origin;
2151
            $this->line->origin_id = $origin_id;
2152
            $this->line->fk_unit = $fk_unit;
2153
2154
            $this->line->date_start = $date_start;
2155
            $this->line->date_end = $date_end;
2156
2157
            // Multicurrency
2158
            $this->line->fk_multicurrency = $this->fk_multicurrency;
2159
            $this->line->multicurrency_code = $this->multicurrency_code;
2160
            $this->line->multicurrency_subprice = $pu_ht_devise;
2161
            $this->line->multicurrency_total_ht     = $multicurrency_total_ht;
2162
            $this->line->multicurrency_total_tva    = $multicurrency_total_tva;
2163
            $this->line->multicurrency_total_ttc    = $multicurrency_total_ttc;
2164
2165
            $this->line->subprice = $pu_ht;
2166
            $this->line->price = $this->line->subprice;
2167
2168
            $this->line->remise_percent = $remise_percent;
2169
2170
            if (is_array($array_options) && count($array_options) > 0) {
2171
                $this->line->array_options = $array_options;
2172
            }
2173
2174
            $result = $this->line->insert($notrigger);
2175
            if ($result > 0) {
2176
                // Reorder if child line
2177
                if (!empty($this->line->fk_parent_line)) {
2178
                    $this->line_order(true, 'DESC');
2179
                } elseif ($rang > 0 && $rang <= count($this->lines)) { // Update all rank of all other lines
2180
                    $linecount = count($this->lines);
2181
                    for ($ii = $rang; $ii <= $linecount; $ii++) {
2182
                        $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
2183
                    }
2184
                }
2185
2186
                // Mise a jour information denormalisees au niveau de la commande meme
2187
                $result = $this->update_price(1, 'auto', 0, $this->thirdparty); // This method is designed to add line from user input so total calculation must be done using 'auto' mode.
2188
                if ($result > 0) {
2189
                    $this->db->commit();
2190
                    return $this->line->id;
2191
                } else {
2192
                    $this->db->rollback();
2193
                    return -1;
2194
                }
2195
            } else {
2196
                $this->error = $this->line->error;
2197
                $this->errors = $this->line->errors;
2198
                dol_syslog(get_class($this) . "::addline error=" . $this->error, LOG_ERR);
2199
                $this->db->rollback();
2200
                return -1;
2201
            }
2202
        }
2203
        return -1;
2204
    }
2205
2206
2207
    /**
2208
     * Save a receiving into the tracking table of receiving (receptiondet_batch) and add product into stock warehouse.
2209
     *
2210
     * @param   User        $user                   User object making change
2211
     * @param   int         $product                Id of product to dispatch
2212
     * @param   double      $qty                    Qty to dispatch
2213
     * @param   int         $entrepot               Id of warehouse to add product
2214
     * @param   double      $price                  Unit Price for PMP value calculation (Unit price without Tax and taking into account discount)
2215
     * @param   string      $comment                Comment for stock movement
2216
     * @param   int|string  $eatby                  eat-by date
2217
     * @param   int|string  $sellby                 sell-by date
2218
     * @param   string      $batch                  Lot number
2219
     * @param   int         $fk_commandefourndet    Id of supplier order line
2220
     * @param   int         $notrigger              1 = notrigger
2221
     * @param   int         $fk_reception           Id of reception to link
2222
     * @return  int                     Return integer <0 if KO, >0 if OK
2223
     */
2224
    public function dispatchProduct($user, $product, $qty, $entrepot, $price = 0, $comment = '', $eatby = '', $sellby = '', $batch = '', $fk_commandefourndet = 0, $notrigger = 0, $fk_reception = 0)
2225
    {
2226
        global $conf, $langs;
2227
2228
        $error = 0;
2229
        require_once constant('DOL_DOCUMENT_ROOT') . '/product/stock/class/mouvementstock.class.php';
2230
2231
        // Check parameters (if test are wrong here, there is bug into caller)
2232
        if ($entrepot <= 0) {
2233
            $this->error = 'ErrorBadValueForParameterWarehouse';
2234
            return -1;
2235
        }
2236
        if ($qty == 0) {
2237
            $this->error = 'ErrorBadValueForParameterQty';
2238
            return -1;
2239
        }
2240
2241
        $dispatchstatus = 1;
2242
        if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
2243
            $dispatchstatus = 0; // Setting dispatch status (a validation step after receiving products) will be done manually to 1 or 2 if this option is on
2244
        }
2245
2246
        $now = dol_now();
2247
2248
        $inventorycode = dol_print_date(dol_now(), 'dayhourlog');
2249
2250
        if (($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY || $this->statut == self::STATUS_RECEIVED_COMPLETELY)) {
2251
            $this->db->begin();
2252
2253
            $sql = "INSERT INTO " . $this->db->prefix() . "receptiondet_batch";
2254
            $sql .= " (fk_element, fk_product, qty, fk_entrepot, fk_user, datec, fk_elementdet, status, comment, eatby, sellby, batch, fk_reception) VALUES";
2255
            $sql .= " ('" . $this->id . "','" . $product . "','" . $qty . "'," . ($entrepot > 0 ? "'" . $entrepot . "'" : "null") . ",'" . $user->id . "','" . $this->db->idate($now) . "','" . $fk_commandefourndet . "', " . $dispatchstatus . ", '" . $this->db->escape($comment) . "', ";
2256
            $sql .= ($eatby ? "'" . $this->db->idate($eatby) . "'" : "null") . ", " . ($sellby ? "'" . $this->db->idate($sellby) . "'" : "null") . ", " . ($batch ? "'" . $this->db->escape($batch) . "'" : "null") . ", " . ($fk_reception > 0 ? "'" . $this->db->escape($fk_reception) . "'" : "null");
2257
            $sql .= ")";
2258
2259
            dol_syslog(get_class($this) . "::dispatchProduct", LOG_DEBUG);
2260
            $resql = $this->db->query($sql);
2261
            if ($resql) {
2262
                if (!$notrigger) {
2263
                    global $conf, $langs, $user;
2264
                    // Call trigger
2265
                    $result = $this->call_trigger('LINEORDER_SUPPLIER_DISPATCH', $user);
2266
                    if ($result < 0) {
2267
                        $error++;
2268
                    }
2269
                    // End call triggers
2270
                }
2271
            } else {
2272
                $this->error = $this->db->lasterror();
2273
                $error++;
2274
            }
2275
2276
            // If module stock is enabled and the stock increase is done on purchase order dispatching
2277
            if (!$error && $entrepot > 0 && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER')) {
2278
                $mouv = new MouvementStock($this->db);
2279
                if ($product > 0) {
2280
                    // $price should take into account discount (except if option STOCK_EXCLUDE_DISCOUNT_FOR_PMP is on)
2281
                    $mouv->origin = &$this;
2282
                    $mouv->setOrigin($this->element, $this->id);
2283
2284
                    // Method change if qty < 0
2285
                    if (getDolGlobalString('SUPPLIER_ORDER_ALLOW_NEGATIVE_QTY_FOR_SUPPLIER_ORDER_RETURN') && $qty < 0) {
2286
                        $result = $mouv->livraison($user, $product, $entrepot, $qty * (-1), $price, $comment, $now, $eatby, $sellby, $batch, 0, $inventorycode);
2287
                    } else {
2288
                        $result = $mouv->reception($user, $product, $entrepot, $qty, $price, $comment, $eatby, $sellby, $batch, '', 0, $inventorycode);
2289
                    }
2290
2291
                    if ($result < 0) {
2292
                        $this->error = $mouv->error;
2293
                        $this->errors = $mouv->errors;
2294
                        dol_syslog(get_class($this) . "::dispatchProduct " . $this->error . " " . implode(',', $this->errors), LOG_ERR);
2295
                        $error++;
2296
                    }
2297
                }
2298
            }
2299
2300
            if ($error == 0) {
2301
                $this->db->commit();
2302
                return 1;
2303
            } else {
2304
                $this->db->rollback();
2305
                return -1;
2306
            }
2307
        } else {
2308
            $this->error = 'BadStatusForObject';
2309
            return -2;
2310
        }
2311
    }
2312
2313
    /**
2314
     *  Delete line
2315
     *
2316
     *  @param  int     $idline     Id of line to delete
2317
     *  @param  int     $notrigger  1=Disable call to triggers
2318
     *  @return int                 Return integer <0 if KO, >0 if OK
2319
     */
2320
    public function deleteLine($idline, $notrigger = 0)
2321
    {
2322
        global $user;
2323
2324
        if ($this->statut == 0) {
2325
            $line = new CommandeFournisseurLigne($this->db);
2326
2327
            if ($line->fetch($idline) <= 0) {
2328
                return 0;
2329
            }
2330
2331
            // check if not yet received
2332
            $dispatchedLines = $this->getDispachedLines();
2333
            foreach ($dispatchedLines as $dispatchLine) {
2334
                if ($dispatchLine['orderlineid'] == $idline) {
2335
                    $this->error = "LineAlreadyDispatched";
2336
                    $this->errors[] = $this->error;
2337
                    return -3;
2338
                }
2339
            }
2340
2341
            if ($line->delete($user, $notrigger) > 0) {
2342
                $this->update_price(1);
2343
                return 1;
2344
            } else {
2345
                $this->setErrorsFromObject($line);
2346
                return -1;
2347
            }
2348
        } else {
2349
            return -2;
2350
        }
2351
    }
2352
2353
    /**
2354
     *  Delete an order
2355
     *
2356
     *  @param  User    $user       Object user
2357
     *  @param  int     $notrigger  1=Does not execute triggers, 0= execute triggers
2358
     *  @return int                 Return integer <0 if KO, >0 if OK
2359
     */
2360
    public function delete(User $user, $notrigger = 0)
2361
    {
2362
        global $langs, $conf;
2363
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
2364
2365
        $error = 0;
2366
2367
        $this->db->begin();
2368
2369
        if (empty($notrigger)) {
2370
            // Call trigger
2371
            $result = $this->call_trigger('ORDER_SUPPLIER_DELETE', $user);
2372
            if ($result < 0) {
2373
                $this->errors[] = 'ErrorWhenRunningTrigger';
2374
                dol_syslog(get_class($this) . "::delete " . $this->error, LOG_ERR);
2375
                $this->db->rollback();
2376
                return -1;
2377
            }
2378
            // End call triggers
2379
        }
2380
2381
        // Test we can delete
2382
        $this->fetchObjectLinked(null, 'order_supplier');
2383
        if (!empty($this->linkedObjects) && array_key_exists('reception', $this->linkedObjects)) {
2384
            foreach ($this->linkedObjects['reception'] as $element) {
2385
                if ($element->statut >= 0) {
2386
                    $this->errors[] = $langs->trans('ReceptionExist');
2387
                    $error++;
2388
                    break;
2389
                }
2390
            }
2391
        }
2392
2393
        $main = $this->db->prefix() . 'commande_fournisseurdet';
2394
        $ef = $main . "_extrafields";
2395
        $sql = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_commande = " . ((int) $this->id) . ")";
2396
        dol_syslog(get_class($this) . "::delete extrafields lines", LOG_DEBUG);
2397
        if (!$this->db->query($sql)) {
2398
            $this->error = $this->db->lasterror();
2399
            $this->errors[] = $this->db->lasterror();
2400
            $error++;
2401
        }
2402
2403
        $sql = "DELETE FROM " . $this->db->prefix() . "commande_fournisseurdet WHERE fk_commande =" . ((int) $this->id);
2404
        dol_syslog(get_class($this) . "::delete", LOG_DEBUG);
2405
        if (!$this->db->query($sql)) {
2406
            $this->error = $this->db->lasterror();
2407
            $this->errors[] = $this->db->lasterror();
2408
            $error++;
2409
        }
2410
2411
        $sql = "DELETE FROM " . $this->db->prefix() . "commande_fournisseur WHERE rowid =" . ((int) $this->id);
2412
        dol_syslog(get_class($this) . "::delete", LOG_DEBUG);
2413
        if ($resql = $this->db->query($sql)) {
2414
            if ($this->db->affected_rows($resql) < 1) {
2415
                $this->error = $this->db->lasterror();
2416
                $this->errors[] = $this->db->lasterror();
2417
                $error++;
2418
            }
2419
        } else {
2420
            $this->error = $this->db->lasterror();
2421
            $this->errors[] = $this->db->lasterror();
2422
            $error++;
2423
        }
2424
2425
        // Remove extrafields
2426
        if (!$error) {
2427
            $result = $this->deleteExtraFields();
2428
            if ($result < 0) {
2429
                $this->error = 'FailToDeleteExtraFields';
2430
                $this->errors[] = 'FailToDeleteExtraFields';
2431
                $error++;
2432
                dol_syslog(get_class($this) . "::delete error -4 " . $this->error, LOG_ERR);
2433
            }
2434
        }
2435
2436
        // Delete linked object
2437
        $res = $this->deleteObjectLinked();
2438
        if ($res < 0) {
2439
            $this->error = 'FailToDeleteObjectLinked';
2440
            $this->errors[] = 'FailToDeleteObjectLinked';
2441
            $error++;
2442
        }
2443
2444
        if (!$error) {
2445
            // Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
2446
            $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
2447
            $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
2448
2449
            // We remove directory
2450
            $ref = dol_sanitizeFileName($this->ref);
2451
            if ($conf->fournisseur->commande->dir_output) {
2452
                $dir = $conf->fournisseur->commande->dir_output . "/" . $ref;
2453
                $file = $dir . "/" . $ref . ".pdf";
2454
                if (file_exists($file)) {
2455
                    if (!dol_delete_file($file, 0, 0, 0, $this)) { // For triggers
2456
                        $this->error = 'ErrorFailToDeleteFile';
2457
                        $this->errors[] = 'ErrorFailToDeleteFile';
2458
                        $error++;
2459
                    }
2460
                }
2461
                if (file_exists($dir)) {
2462
                    $res = @dol_delete_dir_recursive($dir);
2463
                    if (!$res) {
2464
                        $this->error = 'ErrorFailToDeleteDir';
2465
                        $this->errors[] = 'ErrorFailToDeleteDir';
2466
                        $error++;
2467
                    }
2468
                }
2469
            }
2470
        }
2471
2472
        if (!$error) {
2473
            dol_syslog(get_class($this) . "::delete $this->id by $user->id", LOG_DEBUG);
2474
            $this->db->commit();
2475
            return 1;
2476
        } else {
2477
            dol_syslog(get_class($this) . "::delete " . $this->error, LOG_ERR);
2478
            $this->db->rollback();
2479
            return -$error;
2480
        }
2481
    }
2482
2483
2484
    /**
2485
     * Return array of dispatched lines waiting to be approved for this order
2486
     *
2487
     * @since 8.0 Return dispatched quantity (qty).
2488
     *
2489
     * @param   int     $status     Filter on stats (-1 = no filter, 0 = lines draft to be approved, 1 = approved lines)
2490
     * @return  array               Array of lines
2491
     */
2492
    public function getDispachedLines($status = -1)
2493
    {
2494
        $ret = array();
2495
2496
        // List of already dispatched lines
2497
        $sql = "SELECT p.ref, p.label,";
2498
        $sql .= " e.rowid as warehouse_id, e.ref as entrepot,";
2499
        $sql .= " cfd.rowid as dispatchedlineid, cfd.fk_product, cfd.qty, cfd.eatby, cfd.sellby, cfd.batch, cfd.comment, cfd.status, cfd.fk_elementdet";
2500
        $sql .= " FROM " . $this->db->prefix() . "product as p,";
2501
        $sql .= " " . $this->db->prefix() . "receptiondet_batch as cfd";
2502
        $sql .= " LEFT JOIN " . $this->db->prefix() . "entrepot as e ON cfd.fk_entrepot = e.rowid";
2503
        $sql .= " WHERE cfd.fk_element = " . ((int) $this->id);
2504
        $sql .= " AND cfd.fk_product = p.rowid";
2505
        if ($status >= 0) {
2506
            $sql .= " AND cfd.status = " . ((int) $status);
2507
        }
2508
        $sql .= " ORDER BY cfd.rowid ASC";
2509
2510
        $resql = $this->db->query($sql);
2511
        if ($resql) {
2512
            $num = $this->db->num_rows($resql);
2513
            $i = 0;
2514
2515
            while ($i < $num) {
2516
                $objp = $this->db->fetch_object($resql);
2517
                if ($objp) {
2518
                    $ret[] = array(
2519
                        'id' => $objp->dispatchedlineid,
2520
                        'productid' => $objp->fk_product,
2521
                        'warehouseid' => $objp->warehouse_id,
2522
                        'qty' => $objp->qty,
2523
                        'orderlineid' => $objp->fk_elementdet
2524
                    );
2525
                }
2526
2527
                $i++;
2528
            }
2529
        } else {
2530
            dol_print_error($this->db, 'Failed to execute request to get dispatched lines');
2531
        }
2532
2533
        return $ret;
2534
    }
2535
2536
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2537
    /**
2538
     *  Set a delivery in database for this supplier order
2539
     *
2540
     *  @param  User    $user       User that input data
2541
     *  @param  integer $date       Date of reception
2542
     *  @param  string  $type       Type of receipt ('tot' = total/done, 'par' = partial, 'nev' = never, 'can' = cancel)
2543
     *  @param  string  $comment    Comment
2544
     *  @return int                 Return integer <0 if KO, >0 if OK
2545
     */
2546
    public function Livraison($user, $date, $type, $comment)
2547
    {
2548
		// phpcs:enable
2549
        global $conf, $langs;
2550
2551
        $result = 0;
2552
        $error = 0;
2553
2554
        dol_syslog(get_class($this) . "::Livraison");
2555
2556
        $usercanreceive = 0;
2557
        if (!isModEnabled('reception')) {
2558
            $usercanreceive = $user->hasRight("fournisseur", "commande", "receptionner");
2559
        } else {
2560
            $usercanreceive = $user->hasRight("reception", "creer");
2561
        }
2562
2563
        if ($usercanreceive) {
2564
            // Define the new status
2565
            if ($type == 'par') {
2566
                $statut = self::STATUS_RECEIVED_PARTIALLY;
2567
            } elseif ($type == 'tot') {
2568
                $statut = self::STATUS_RECEIVED_COMPLETELY;
2569
            } elseif ($type == 'nev') {
2570
                $statut = self::STATUS_CANCELED_AFTER_ORDER;
2571
            } elseif ($type == 'can') {
2572
                $statut = self::STATUS_CANCELED_AFTER_ORDER;
2573
            } else {
2574
                $error++;
2575
                dol_syslog(get_class($this) . "::Livraison Error -2", LOG_ERR);
2576
                return -2;
2577
            }
2578
2579
            // Some checks to accept the record
2580
            if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
2581
                // If option SUPPLIER_ORDER_USE_DISPATCH_STATUS is on, we check all reception are approved to allow status "total/done"
2582
                if (!$error && ($type == 'tot')) {
2583
                    $dispatchedlinearray = $this->getDispachedLines(0);
2584
                    if (count($dispatchedlinearray) > 0) {
2585
                        $result = -1;
2586
                        $error++;
2587
                        $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionToApprove';
2588
                        dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionToApprove', LOG_DEBUG);
2589
                    }
2590
                }
2591
                if (!$error && getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS_NEED_APPROVE') && ($type == 'tot')) { // Accept to move to reception done, only if status of all line are ok (refuse denied)
2592
                    $dispatcheddenied = $this->getDispachedLines(2);
2593
                    if (count($dispatchedlinearray) > 0) {
2594
                        $result = -1;
2595
                        $error++;
2596
                        $this->errors[] = 'ErrorCantSetReceptionToTotalDoneWithReceptionDenied';
2597
                        dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionDenied', LOG_DEBUG);
2598
                    }
2599
                }
2600
            }
2601
2602
            // TODO LDR01 Add a control test to accept only if ALL predefined products are received (same qty).
2603
2604
            if (empty($error)) {
2605
                $this->db->begin();
2606
2607
                $sql = "UPDATE " . $this->db->prefix() . "commande_fournisseur";
2608
                $sql .= " SET fk_statut = " . ((int) $statut);
2609
                $sql .= " WHERE rowid = " . ((int) $this->id);
2610
                $sql .= " AND fk_statut IN (" . self::STATUS_ORDERSENT . "," . self::STATUS_RECEIVED_PARTIALLY . ")"; // Process running or Partially received
2611
2612
                dol_syslog(get_class($this) . "::Livraison", LOG_DEBUG);
2613
                $resql = $this->db->query($sql);
2614
                if ($resql) {
2615
                    $result = 1;
2616
                    $old_statut = $this->statut;
2617
                    $this->statut = $statut;
2618
                    $this->context['actionmsg2'] = $comment;
2619
2620
                    // Call trigger
2621
                    $result_trigger = $this->call_trigger('ORDER_SUPPLIER_RECEIVE', $user);
2622
                    if ($result_trigger < 0) {
2623
                        $error++;
2624
                    }
2625
                    // End call triggers
2626
2627
                    if (empty($error)) {
2628
                        $this->db->commit();
2629
                    } else {
2630
                        $this->statut = $old_statut;
2631
                        $this->db->rollback();
2632
                        $this->error = $this->db->lasterror();
2633
                        $result = -1;
2634
                    }
2635
                } else {
2636
                    $this->db->rollback();
2637
                    $this->error = $this->db->lasterror();
2638
                    $result = -1;
2639
                }
2640
            }
2641
        } else {
2642
            $this->error = $langs->trans('NotAuthorized');
2643
            $this->errors[] = $langs->trans('NotAuthorized');
2644
            dol_syslog(get_class($this) . "::Livraison Not Authorized");
2645
            $result = -3;
2646
        }
2647
        return $result;
2648
    }
2649
2650
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2651
    /**
2652
     *  Set delivery date
2653
     *
2654
     *  @param      User    $user               Object user that modify
2655
     *  @param      int     $delivery_date      Delivery date
2656
     *  @param      int     $notrigger          1=Does not execute triggers, 0= execute triggers
2657
     *  @return     int                         Return integer <0 if ko, >0 if ok
2658
     *  @deprecated Use  setDeliveryDate
2659
     */
2660
    public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2661
    {
2662
		// phpcs:enable
2663
        return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2664
    }
2665
2666
    /**
2667
     *  Set the planned delivery date
2668
     *
2669
     *  @param      User            $user               Object user making change
2670
     *  @param      integer         $delivery_date     Planned delivery date
2671
     *  @param      int             $notrigger          1=Does not execute triggers, 0= execute triggers
2672
     *  @return     int                                 Return integer <0 if KO, >0 if OK
2673
     */
2674
    public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2675
    {
2676
        if ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")) {
2677
            $error = 0;
2678
2679
            $this->db->begin();
2680
2681
            $sql = "UPDATE " . $this->db->prefix() . "commande_fournisseur";
2682
            $sql .= " SET date_livraison = " . ($delivery_date ? "'" . $this->db->idate($delivery_date) . "'" : 'null');
2683
            $sql .= " WHERE rowid = " . ((int) $this->id);
2684
2685
            dol_syslog(__METHOD__, LOG_DEBUG);
2686
            $resql = $this->db->query($sql);
2687
            if (!$resql) {
2688
                $this->errors[] = $this->db->error();
2689
                $error++;
2690
            }
2691
2692
            if (!$error) {
2693
                $this->oldcopy = clone $this;
2694
                $this->delivery_date = $delivery_date;
2695
            }
2696
2697
            if (!$notrigger && empty($error)) {
2698
                // Call trigger
2699
                $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2700
                if ($result < 0) {
2701
                    $error++;
2702
                }
2703
                // End call triggers
2704
            }
2705
2706
            if (!$error) {
2707
                $this->db->commit();
2708
                return 1;
2709
            } else {
2710
                foreach ($this->errors as $errmsg) {
2711
                    dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR);
2712
                    $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
2713
                }
2714
                $this->db->rollback();
2715
                return -1 * $error;
2716
            }
2717
        } else {
2718
            return -2;
2719
        }
2720
    }
2721
2722
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2723
    /**
2724
     *  Set the id projet
2725
     *
2726
     *  @param      User            $user               Object utilisateur qui modifie
2727
     *  @param      int             $id_projet          Delivery date
2728
     *  @param      int             $notrigger          1=Does not execute triggers, 0= execute triggers
2729
     *  @return     int                                 Return integer <0 si ko, >0 si ok
2730
     */
2731
    public function set_id_projet($user, $id_projet, $notrigger = 0)
2732
    {
2733
		// phpcs:enable
2734
        if ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer")) {
2735
            $error = 0;
2736
2737
            $this->db->begin();
2738
2739
            $sql = "UPDATE " . $this->db->prefix() . "commande_fournisseur";
2740
            $sql .= " SET fk_projet = " . ($id_projet > 0 ? (int) $id_projet : 'null');
2741
            $sql .= " WHERE rowid = " . ((int) $this->id);
2742
2743
            dol_syslog(__METHOD__, LOG_DEBUG);
2744
            $resql = $this->db->query($sql);
2745
            if (!$resql) {
2746
                $this->errors[] = $this->db->error();
2747
                $error++;
2748
            }
2749
2750
            if (!$error) {
2751
                $this->oldcopy = clone $this;
2752
                $this->fk_projet = $id_projet;
2753
                $this->fk_project = $id_projet;
2754
            }
2755
2756
            if (!$notrigger && empty($error)) {
2757
                // Call trigger
2758
                $result = $this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2759
                if ($result < 0) {
2760
                    $error++;
2761
                }
2762
                // End call triggers
2763
            }
2764
2765
            if (!$error) {
2766
                $this->db->commit();
2767
                return 1;
2768
            } else {
2769
                foreach ($this->errors as $errmsg) {
2770
                    dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR);
2771
                    $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
2772
                }
2773
                $this->db->rollback();
2774
                return -1 * $error;
2775
            }
2776
        } else {
2777
            return -2;
2778
        }
2779
    }
2780
2781
    /**
2782
     *  Update a supplier order from a sales order
2783
     *
2784
     *  @param  User    $user           User that create
2785
     *  @param  int     $idc            Id of purchase order to update
2786
     *  @param  int     $comclientid    Id of sale order to use as template
2787
     *  @return int                     Return integer <0 if KO, >0 if OK
2788
     */
2789
    public function updateFromCommandeClient($user, $idc, $comclientid)
2790
    {
2791
        $comclient = new Commande($this->db);
2792
        $comclient->fetch($comclientid);
2793
2794
        $this->id = $idc;
2795
2796
        $this->lines = array();
2797
2798
        $num = count($comclient->lines);
2799
        for ($i = 0; $i < $num; $i++) {
2800
            $prod = new Product($this->db);
2801
            $label = '';
2802
            $ref = '';
2803
            if ($prod->fetch($comclient->lines[$i]->fk_product) > 0) {
2804
                $label  = $prod->label;
2805
                $ref    = $prod->ref;
2806
            }
2807
2808
            $sql = "INSERT INTO " . $this->db->prefix() . "commande_fournisseurdet";
2809
            $sql .= " (fk_commande, label, description, fk_product, price, qty, tva_tx, localtax1_tx, localtax2_tx, remise_percent, subprice, remise, ref)";
2810
            $sql .= " VALUES (" . ((int) $idc) . ", '" . $this->db->escape($label) . "', '" . $this->db->escape($comclient->lines[$i]->desc) . "'";
2811
            $sql .= "," . $comclient->lines[$i]->fk_product . ", " . price2num($comclient->lines[$i]->price, 'MU');
2812
            $sql .= ", " . price2num($comclient->lines[$i]->qty, 'MS') . ", " . price2num($comclient->lines[$i]->tva_tx, 5) . ", " . price2num($comclient->lines[$i]->localtax1_tx, 5) . ", " . price2num($comclient->lines[$i]->localtax2_tx, 5) . ", " . price2num($comclient->lines[$i]->remise_percent, 3);
2813
            $sql .= ", '" . price2num($comclient->lines[$i]->subprice, 'MT') . "','0', '" . $this->db->escape($ref) . "');";
2814
            if ($this->db->query($sql)) {
2815
                $this->update_price(1);
2816
            }
2817
        }
2818
2819
        return 1;
2820
    }
2821
2822
    /**
2823
     *  Tag order with a particular status
2824
     *
2825
     *  @param      User    $user       Object user that change status
2826
     *  @param      int     $status     New status
2827
     *  @return     int                 Return integer <0 if KO, >0 if OK
2828
     */
2829
    public function setStatus($user, $status)
2830
    {
2831
        global $conf, $langs;
2832
        $error = 0;
2833
2834
        $this->db->begin();
2835
2836
        $sql = 'UPDATE ' . $this->db->prefix() . 'commande_fournisseur';
2837
        $sql .= " SET fk_statut = " . $status;
2838
        $sql .= " WHERE rowid = " . ((int) $this->id);
2839
2840
        dol_syslog(get_class($this) . "::setStatus", LOG_DEBUG);
2841
        $resql = $this->db->query($sql);
2842
        if ($resql) {
2843
            // Trigger names for each status
2844
            $triggerName = array();
2845
            $triggerName[0] = 'DRAFT';
2846
            $triggerName[1] = 'VALIDATED';
2847
            $triggerName[2] = 'APPROVED';
2848
            $triggerName[3] = 'ORDERED'; // Ordered
2849
            $triggerName[4] = 'RECEIVED_PARTIALLY';
2850
            $triggerName[5] = 'RECEIVED_COMPLETELY';
2851
            $triggerName[6] = 'CANCELED';
2852
            $triggerName[7] = 'CANCELED';
2853
            $triggerName[9] = 'REFUSED';
2854
2855
            // Call trigger
2856
            $result = $this->call_trigger("ORDER_SUPPLIER_STATUS_" . $triggerName[$status], $user);
2857
            if ($result < 0) {
2858
                $error++;
2859
            }
2860
            // End call triggers
2861
        } else {
2862
            $error++;
2863
            $this->error = $this->db->lasterror();
2864
            dol_syslog(get_class($this) . "::setStatus " . $this->error);
2865
        }
2866
2867
        if (!$error) {
2868
            $this->statut = $status;
2869
            $this->db->commit();
2870
            return 1;
2871
        } else {
2872
            $this->db->rollback();
2873
            return -1;
2874
        }
2875
    }
2876
2877
    /**
2878
     *  Update line
2879
     *
2880
     *  @param      int         $rowid              ID de la ligne de facture
2881
     *  @param      string      $desc               Line description
2882
     *  @param      int|float   $pu                 Unit price
2883
     *  @param      int|float   $qty                Quantity
2884
     *  @param      int|float   $remise_percent     Percent discount on line
2885
     *  @param      int|float   $txtva              VAT rate
2886
     *  @param      int|float   $txlocaltax1        Localtax1 tax
2887
     *  @param      int|float   $txlocaltax2        Localtax2 tax
2888
     *  @param      string      $price_base_type    Type of price base
2889
     *  @param      int         $info_bits          Miscellaneous information
2890
     *  @param      int         $type               Type of line (0=product, 1=service)
2891
     *  @param      int         $notrigger          Disable triggers
2892
     *  @param      integer     $date_start         Date start of service
2893
     *  @param      integer     $date_end           Date end of service
2894
     *  @param      array       $array_options      Extrafields array
2895
     *  @param      int|null    $fk_unit            Code of the unit to use. Null to use the default one
2896
     *  @param      int|float   $pu_ht_devise       Unit price in currency
2897
     *  @param      string      $ref_supplier       Supplier ref
2898
     *  @return     int                             Return integer < 0 if error, > 0 if ok
2899
     */
2900
    public function updateline($rowid, $desc, $pu, $qty, $remise_percent, $txtva, $txlocaltax1 = 0, $txlocaltax2 = 0, $price_base_type = 'HT', $info_bits = 0, $type = 0, $notrigger = 0, $date_start = 0, $date_end = 0, $array_options = [], $fk_unit = null, $pu_ht_devise = 0, $ref_supplier = '')
2901
    {
2902
        global $mysoc, $conf, $langs;
2903
        dol_syslog(get_class($this) . "::updateline $rowid, $desc, $pu, $qty, $remise_percent, $txtva, $price_base_type, $info_bits, $type, $fk_unit");
2904
        include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php';
2905
2906
        $error = 0;
2907
2908
        if ($this->statut == self::STATUS_DRAFT) {
2909
            // Clean parameters
2910
            if (empty($qty)) {
2911
                $qty = 0;
2912
            }
2913
            if (empty($info_bits)) {
2914
                $info_bits = 0;
2915
            }
2916
            if (empty($txtva)) {
2917
                $txtva = 0;
2918
            }
2919
            if (empty($txlocaltax1)) {
2920
                $txlocaltax1 = 0;
2921
            }
2922
            if (empty($txlocaltax2)) {
2923
                $txlocaltax2 = 0;
2924
            }
2925
            if (empty($remise_percent)) {
2926
                $remise_percent = 0;
2927
            }
2928
2929
            $remise_percent = (float) price2num($remise_percent);
2930
            $qty = price2num($qty);
2931
            if (!$qty) {
2932
                $qty = 1;
2933
            }
2934
            $pu = price2num($pu);
2935
            $pu_ht_devise = price2num($pu_ht_devise);
2936
            if (!preg_match('/\((.*)\)/', (string) $txtva)) {
2937
                $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
2938
            }
2939
            $txlocaltax1 = (float) price2num($txlocaltax1);
2940
            $txlocaltax2 = (float) price2num($txlocaltax2);
2941
2942
            // Check parameters
2943
            if ($type < 0) {
2944
                return -1;
2945
            }
2946
            if ($date_start && $date_end && $date_start > $date_end) {
2947
                $langs->load("errors");
2948
                $this->error = $langs->trans('ErrorStartDateGreaterEnd');
2949
                return -1;
2950
            }
2951
2952
            $this->db->begin();
2953
2954
            // Calcul du total TTC et de la TVA pour la ligne a partir de
2955
            // qty, pu, remise_percent et txtva
2956
            // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2957
            // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2958
2959
            $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2960
2961
            // Clean vat code
2962
            $reg = array();
2963
            $vat_src_code = '';
2964
            if (preg_match('/\((.*)\)/', $txtva, $reg)) {
2965
                $vat_src_code = $reg[1];
2966
                $txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
2967
            }
2968
2969
            $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
2970
            $total_ht  = $tabprice[0];
2971
            $total_tva = $tabprice[1];
2972
            $total_ttc = $tabprice[2];
2973
            $total_localtax1 = $tabprice[9];
2974
            $total_localtax2 = $tabprice[10];
2975
            $pu_ht  = $tabprice[3];
2976
            $pu_tva = $tabprice[4];
2977
            $pu_ttc = $tabprice[5];
2978
2979
            // MultiCurrency
2980
            $multicurrency_total_ht = $tabprice[16];
2981
            $multicurrency_total_tva = $tabprice[17];
2982
            $multicurrency_total_ttc = $tabprice[18];
2983
            $pu_ht_devise = $tabprice[19];
2984
2985
            $localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
2986
            $localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
2987
2988
            //Fetch current line from the database and then clone the object and set it in $oldline property
2989
            $this->line = new CommandeFournisseurLigne($this->db);
2990
            $this->line->fetch($rowid);
2991
2992
            $oldline = clone $this->line;
2993
            $this->line->oldline = $oldline;
2994
2995
            $this->line->context = $this->context;
2996
2997
            $this->line->fk_commande = $this->id;
2998
            //$this->line->label=$label;
2999
            $this->line->desc = $desc;
3000
3001
            // redefine quantity according to packaging
3002
            if (getDolGlobalString('PRODUCT_USE_SUPPLIER_PACKAGING')) {
3003
                if ($qty < $this->line->packaging) {
3004
                    $qty = $this->line->packaging;
3005
                } else {
3006
                    if (!empty($this->line->packaging) && ($qty % $this->line->packaging) > 0) {
3007
                        $coeff = intval($qty / $this->line->packaging) + 1;
3008
                        $qty = $this->line->packaging * $coeff;
3009
                        setEventMessage($langs->trans('QtyRecalculatedWithPackaging'), 'mesgs');
3010
                    }
3011
                }
3012
            }
3013
3014
            $this->line->qty = $qty;
3015
            $this->line->ref_supplier = $ref_supplier;
3016
3017
            $this->line->vat_src_code = $vat_src_code;
3018
            $this->line->tva_tx         = $txtva;
3019
            $this->line->localtax1_tx   = $txlocaltax1;
3020
            $this->line->localtax2_tx   = $txlocaltax2;
3021
            $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
3022
            $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
3023
            $this->line->remise_percent = $remise_percent;
3024
            $this->line->subprice       = $pu_ht;
3025
            $this->line->info_bits      = $info_bits;
3026
            $this->line->total_ht       = $total_ht;
3027
            $this->line->total_tva      = $total_tva;
3028
            $this->line->total_localtax1 = $total_localtax1;
3029
            $this->line->total_localtax2 = $total_localtax2;
3030
            $this->line->total_ttc      = $total_ttc;
3031
            $this->line->product_type   = $type;
3032
            $this->line->special_code   = $oldline->special_code;
3033
            $this->line->rang           = $oldline->rang;
3034
            $this->line->origin         = $this->origin;
3035
            $this->line->fk_unit        = $fk_unit;
3036
3037
            $this->line->date_start     = $date_start;
3038
            $this->line->date_end       = $date_end;
3039
3040
            // Multicurrency
3041
            $this->line->fk_multicurrency = $this->fk_multicurrency;
3042
            $this->line->multicurrency_code = $this->multicurrency_code;
3043
            $this->line->multicurrency_subprice     = $pu_ht_devise;
3044
            $this->line->multicurrency_total_ht     = $multicurrency_total_ht;
3045
            $this->line->multicurrency_total_tva    = $multicurrency_total_tva;
3046
            $this->line->multicurrency_total_ttc    = $multicurrency_total_ttc;
3047
3048
            $this->line->subprice = $pu_ht;
3049
            $this->line->price = $this->line->subprice;
3050
3051
            $this->line->remise_percent = $remise_percent;
3052
3053
            if (is_array($array_options) && count($array_options) > 0) {
3054
                // We replace values in this->line->array_options only for entries defined into $array_options
3055
                foreach ($array_options as $key => $value) {
3056
                    $this->line->array_options[$key] = $array_options[$key];
3057
                }
3058
            }
3059
3060
            $result = $this->line->update($notrigger);
3061
3062
3063
            // Mise a jour info denormalisees au niveau facture
3064
            if ($result >= 0) {
3065
                $this->update_price('1', 'auto');
3066
                $this->db->commit();
3067
                return $result;
3068
            } else {
3069
                $this->error = $this->db->lasterror();
3070
                $this->db->rollback();
3071
                return -1;
3072
            }
3073
        } else {
3074
            $this->error = "Order status makes operation forbidden";
3075
            dol_syslog(get_class($this) . "::updateline " . $this->error, LOG_ERR);
3076
            return -2;
3077
        }
3078
    }
3079
3080
3081
    /**
3082
     *  Initialise an instance with random values.
3083
     *  Used to build previews or test instances.
3084
     *  id must be 0 if object instance is a specimen.
3085
     *
3086
     *  @return int
3087
     */
3088
    public function initAsSpecimen()
3089
    {
3090
        global $user, $langs, $conf;
3091
3092
        include_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
3093
3094
        dol_syslog(get_class($this) . "::initAsSpecimen");
3095
3096
        $now = dol_now();
3097
3098
        // Find first product
3099
        $prodid = 0;
3100
        $product = new ProductFournisseur($this->db);
3101
        $sql = "SELECT rowid";
3102
        $sql .= " FROM " . $this->db->prefix() . "product";
3103
        $sql .= " WHERE entity IN (" . getEntity('product') . ")";
3104
        $sql .= $this->db->order("rowid", "ASC");
3105
        $sql .= $this->db->plimit(1);
3106
        $resql = $this->db->query($sql);
3107
        if ($resql) {
3108
            $obj = $this->db->fetch_object($resql);
3109
            $prodid = $obj->rowid;
3110
        }
3111
3112
        // Initialise parameters
3113
        $this->id = 0;
3114
        $this->ref = 'SPECIMEN';
3115
        $this->specimen = 1;
3116
        $this->socid = 1;
3117
        $this->date = $now;
3118
        $this->date_commande = $now;
3119
        $this->date_lim_reglement = $this->date + 3600 * 24 * 30;
3120
        $this->cond_reglement_code = 'RECEP';
3121
        $this->mode_reglement_code = 'CHQ';
3122
3123
        $this->note_public = 'This is a comment (public)';
3124
        $this->note_private = 'This is a comment (private)';
3125
3126
        $this->multicurrency_tx = 1;
3127
        $this->multicurrency_code = $conf->currency;
3128
3129
        $this->statut = 0;
3130
3131
        // Lines
3132
        $nbp = 5;
3133
        $xnbp = 0;
3134
        while ($xnbp < $nbp) {
3135
            $line = new CommandeFournisseurLigne($this->db);
3136
            $line->desc = $langs->trans("Description") . " " . $xnbp;
3137
            $line->qty = 1;
3138
            $line->subprice = 100;
3139
            $line->tva_tx = 19.6;
3140
            $line->localtax1_tx = 0;
3141
            $line->localtax2_tx = 0;
3142
            if ($xnbp == 2) {
3143
                $line->total_ht = 50;
3144
                $line->total_ttc = 59.8;
3145
                $line->total_tva = 9.8;
3146
                $line->remise_percent = 50;
3147
            } else {
3148
                $line->total_ht = 100;
3149
                $line->total_ttc = 119.6;
3150
                $line->total_tva = 19.6;
3151
                $line->remise_percent = 0;
3152
            }
3153
            $line->fk_product = $prodid;
3154
3155
            $this->lines[$xnbp] = $line;
3156
3157
            $this->total_ht       += $line->total_ht;
3158
            $this->total_tva      += $line->total_tva;
3159
            $this->total_ttc      += $line->total_ttc;
3160
3161
            $xnbp++;
3162
        }
3163
3164
        return 1;
3165
    }
3166
3167
    /**
3168
     *  Charge les information d'ordre info dans l'objet facture
3169
     *
3170
     *  @param  int     $id         Id de la facture a charger
3171
     *  @return void
3172
     */
3173
    public function info($id)
3174
    {
3175
        $sql = 'SELECT c.rowid, date_creation as datec, tms as datem, date_valid as date_validation, date_approve as datea, date_approve2 as datea2,';
3176
        $sql .= ' fk_user_author, fk_user_modif, fk_user_valid, fk_user_approve, fk_user_approve2';
3177
        $sql .= ' FROM ' . $this->db->prefix() . 'commande_fournisseur as c';
3178
        $sql .= ' WHERE c.rowid = ' . ((int) $id);
3179
3180
        $result = $this->db->query($sql);
3181
        if ($result) {
3182
            if ($this->db->num_rows($result)) {
3183
                $obj = $this->db->fetch_object($result);
3184
3185
                $this->id = $obj->rowid;
3186
3187
                $this->user_creation_id = $obj->fk_user_author;
3188
                $this->user_validation_id = $obj->fk_user_valid;
3189
                $this->user_modification_id = $obj->fk_user_modif;
3190
                $this->user_approve_id = $obj->fk_user_approve;
3191
                $this->user_approve_id2 = $obj->fk_user_approve2;
3192
3193
                $this->date_creation     = $this->db->jdate($obj->datec);
3194
                $this->date_modification = $this->db->jdate($obj->datem);
3195
                $this->date_approve      = $this->db->jdate($obj->datea);
3196
                $this->date_approve2     = $this->db->jdate($obj->datea2);
3197
                $this->date_validation   = $this->db->jdate($obj->date_validation);
3198
            }
3199
            $this->db->free($result);
3200
        } else {
3201
            dol_print_error($this->db);
3202
        }
3203
    }
3204
3205
    /**
3206
     *  Load the indicators this->nb for the state board
3207
     *
3208
     *  @return     int         Return integer <0 si ko, >0 si ok
3209
     */
3210
    public function loadStateBoard()
3211
    {
3212
        global $conf, $user;
3213
3214
        $this->nb = array();
3215
        $clause = "WHERE";
3216
3217
        $sql = "SELECT count(co.rowid) as nb";
3218
        $sql .= " FROM " . $this->db->prefix() . "commande_fournisseur as co";
3219
        $sql .= " LEFT JOIN " . $this->db->prefix() . "societe as s ON co.fk_soc = s.rowid";
3220
        if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
3221
            $sql .= " LEFT JOIN " . $this->db->prefix() . "societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3222
            $sql .= " WHERE sc.fk_user = " . ((int) $user->id);
3223
            $clause = "AND";
3224
        }
3225
        $sql .= " " . $clause . " co.entity IN (" . getEntity('supplier_order') . ")";
3226
3227
        $resql = $this->db->query($sql);
3228
        if ($resql) {
3229
            while ($obj = $this->db->fetch_object($resql)) {
3230
                $this->nb["supplier_orders"] = $obj->nb;
3231
            }
3232
            $this->db->free($resql);
3233
            return 1;
3234
        } else {
3235
            dol_print_error($this->db);
3236
            $this->error = $this->db->error();
3237
            return -1;
3238
        }
3239
    }
3240
3241
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3242
    /**
3243
     *  Load indicators for dashboard (this->nbtodo and this->nbtodolate)
3244
     *
3245
     *  @param  User    $user   Object user
3246
     *  @param  string  $mode   "opened", "awaiting" for orders awaiting reception
3247
     *  @return WorkboardResponse|int   Return integer <0 if KO, WorkboardResponse if OK
3248
     */
3249
    public function load_board($user, $mode = 'opened')
3250
    {
3251
		// phpcs:enable
3252
        global $conf, $langs;
3253
3254
        $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.fk_statut, c.date_livraison as delivery_date, c.total_ht";
3255
        $sql .= " FROM " . $this->db->prefix() . "commande_fournisseur as c";
3256
        if (!$user->hasRight("societe", "client", "voir") && !$user->socid) {
3257
            $sql .= " JOIN " . $this->db->prefix() . "societe_commerciaux as sc ON c.fk_soc = sc.fk_soc AND sc.fk_user = " . ((int) $user->id);
3258
        }
3259
        $sql .= " WHERE c.entity = " . $conf->entity;
3260
        if ($mode === 'awaiting') {
3261
            $sql .= " AND c.fk_statut IN (" . self::STATUS_ORDERSENT . ", " . self::STATUS_RECEIVED_PARTIALLY . ")";
3262
        } else {
3263
            $sql .= " AND c.fk_statut IN (" . self::STATUS_VALIDATED . ", " . self::STATUS_ACCEPTED . ")";
3264
        }
3265
        if ($user->socid) {
3266
            $sql .= " AND c.fk_soc = " . ((int) $user->socid);
3267
        }
3268
3269
        $resql = $this->db->query($sql);
3270
        if ($resql) {
3271
            $commandestatic = new CommandeFournisseur($this->db);
3272
3273
            $response = new WorkboardResponse();
3274
            $response->warning_delay = $conf->commande->fournisseur->warning_delay / 60 / 60 / 24;
3275
            $response->label = $langs->trans("SuppliersOrdersToProcess");
3276
            $response->labelShort = $langs->trans("Opened");
3277
            $response->url = constant('BASE_URL') . '/fourn/commande/list.php?search_status=1,2&mainmenu=commercial&leftmenu=orders_suppliers';
3278
            $response->img = img_object('', "order");
3279
3280
            if ($mode === 'awaiting') {
3281
                $response->label = $langs->trans("SuppliersOrdersAwaitingReception");
3282
                $response->labelShort = $langs->trans("AwaitingReception");
3283
                $response->url = constant('BASE_URL') . '/fourn/commande/list.php?search_status=3,4&mainmenu=commercial&leftmenu=orders_suppliers';
3284
            }
3285
3286
            while ($obj = $this->db->fetch_object($resql)) {
3287
                $commandestatic->delivery_date = $this->db->jdate($obj->delivery_date);
3288
                $commandestatic->date_commande = $this->db->jdate($obj->date_commande);
3289
                $commandestatic->statut = $obj->fk_statut;
3290
3291
                $response->nbtodo++;
3292
                $response->total += $obj->total_ht;
3293
3294
                if ($commandestatic->hasDelay()) {
3295
                    $response->nbtodolate++;
3296
                }
3297
            }
3298
3299
            return $response;
3300
        } else {
3301
            $this->error = $this->db->error();
3302
            return -1;
3303
        }
3304
    }
3305
3306
    /**
3307
     * Returns the translated input method of object (defined if $this->methode_commande_id > 0).
3308
     * This function make a sql request to get translation. No cache yet, try to not use it inside a loop.
3309
     *
3310
     * @return string
3311
     */
3312
    public function getInputMethod()
3313
    {
3314
        global $langs;
3315
3316
        if ($this->methode_commande_id > 0) {
3317
            $sql = "SELECT rowid, code, libelle as label";
3318
            $sql .= " FROM " . $this->db->prefix() . 'c_input_method';
3319
            $sql .= " WHERE active=1 AND rowid = " . ((int) $this->methode_commande_id);
3320
3321
            $resql = $this->db->query($sql);
3322
            if ($resql) {
3323
                if ($this->db->num_rows($resql)) {
3324
                    $obj = $this->db->fetch_object($resql);
3325
3326
                    $string = $langs->trans($obj->code);
3327
                    if ($string == $obj->code) {
3328
                        $string = $obj->label != '-' ? $obj->label : '';
3329
                    }
3330
                    return $string;
3331
                }
3332
            } else {
3333
                dol_print_error($this->db);
3334
            }
3335
        }
3336
3337
        return '';
3338
    }
3339
3340
    /**
3341
     *  Create a document onto disk according to template model.
3342
     *
3343
     *  @param      string      $modele         Force template to use ('' to not force)
3344
     *  @param      Translate   $outputlangs    Object lang to use for traduction
3345
     *  @param      int         $hidedetails    Hide details of lines
3346
     *  @param      int         $hidedesc       Hide description
3347
     *  @param      int         $hideref        Hide ref
3348
     *  @param      null|array  $moreparams     Array to provide more information
3349
     *  @return     int                         Return integer < 0 if KO, 0 = no doc generated, > 0 if OK
3350
     */
3351
    public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3352
    {
3353
        global $conf, $langs;
3354
3355
        if (!dol_strlen($modele)) {
3356
            $modele = '';   // No doc template/generation by default
3357
3358
            if (!empty($this->model_pdf)) {
3359
                $modele = $this->model_pdf;
3360
            } elseif (getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF')) {
3361
                $modele = getDolGlobalString('COMMANDE_SUPPLIER_ADDON_PDF');
3362
            }
3363
        }
3364
3365
        if (empty($modele)) {
3366
            return 0;
3367
        } else {
3368
            $langs->load("suppliers");
3369
            $outputlangs->load("products");
3370
3371
            $modelpath = "core/modules/supplier_order/doc/";
3372
            $result = $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3373
            return $result;
3374
        }
3375
    }
3376
3377
    /**
3378
     * Return the max number delivery delay in day
3379
     *
3380
     * @param   Translate   $langs      Language object
3381
     * @return  string                  Translated string
3382
     */
3383
    public function getMaxDeliveryTimeDay($langs)
3384
    {
3385
        if (empty($this->lines)) {
3386
            return '';
3387
        }
3388
3389
        $obj = new ProductFournisseur($this->db);
3390
3391
        $nb = 0;
3392
        foreach ($this->lines as $line) {
3393
            if ($line->fk_product > 0) {
3394
                $idp = $obj->find_min_price_product_fournisseur($line->fk_product, $line->qty);
3395
                if ($idp) {
3396
                    $obj->fetch($idp);
3397
                    if ($obj->delivery_time_days > $nb) {
3398
                        $nb = $obj->delivery_time_days;
3399
                    }
3400
                }
3401
            }
3402
        }
3403
3404
        if ($nb === 0) {
3405
            return '';
3406
        } else {
3407
            return $nb . ' ' . $langs->trans('Days');
3408
        }
3409
    }
3410
3411
    /**
3412
     * Returns the rights used for this class
3413
     * @return int
3414
     */
3415
    public function getRights()
3416
    {
3417
        global $user;
3418
3419
        return $user->hasRight("fournisseur", "commande");
3420
    }
3421
3422
3423
    /**
3424
     * Function used to replace a thirdparty id with another one.
3425
     *
3426
     * @param   DoliDB  $dbs        Database handler, because function is static we name it $dbs not $db to avoid breaking coding test
3427
     * @param   int     $origin_id  Old thirdparty id
3428
     * @param   int     $dest_id    New thirdparty id
3429
     * @return  bool
3430
     */
3431
    public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
3432
    {
3433
        $tables = array(
3434
            'commande_fournisseur'
3435
        );
3436
3437
        return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
3438
    }
3439
3440
    /**
3441
     * Function used to replace a product id with another one.
3442
     *
3443
     * @param DoliDB    $dbs        Database handler
3444
     * @param int       $origin_id  Old product id
3445
     * @param int       $dest_id    New product id
3446
     * @return bool
3447
     */
3448
    public static function replaceProduct(DoliDB $dbs, $origin_id, $dest_id)
3449
    {
3450
        $tables = array(
3451
            'commande_fournisseurdet'
3452
        );
3453
3454
        return CommonObject::commonReplaceProduct($dbs, $origin_id, $dest_id, $tables);
3455
    }
3456
3457
    /**
3458
     * Is the supplier order delayed?
3459
     * We suppose a purchase ordered as late if a the purchase order has been sent and the delivery date is set and before the delay.
3460
     * If order has not been sent, we use the order date.
3461
     *
3462
     * @return  bool                    True if object is delayed
3463
     */
3464
    public function hasDelay()
3465
    {
3466
        global $conf;
3467
3468
        if ($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY) {
3469
            $now = dol_now();
3470
            if (!empty($this->delivery_date)) {
3471
                $date_to_test = $this->delivery_date;
3472
                return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3473
            } else {
3474
                //$date_to_test = $this->date_commande;
3475
                //return $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3476
                return false;
3477
            }
3478
        } else {
3479
            $now = dol_now();
3480
            $date_to_test = $this->date_commande;
3481
3482
            return ($this->statut > 0 && $this->statut < 5) && $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3483
        }
3484
    }
3485
3486
    /**
3487
     * Show the customer delayed info.
3488
     * We suppose a purchase ordered as late if a the purchase order has been sent and the delivery date is set and before the delay.
3489
     * If order has not been sent, we use the order date.
3490
     *
3491
     * @return string       Show delayed information
3492
     */
3493
    public function showDelay()
3494
    {
3495
        global $conf, $langs;
3496
3497
        $langs->load('orders');
3498
3499
        $text = '';
3500
3501
        if ($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY) {
3502
            if (!empty($this->delivery_date)) {
3503
                $text = $langs->trans("DeliveryDate") . ' ' . dol_print_date($this->delivery_date, 'day');
3504
            } else {
3505
                $text = $langs->trans("OrderDate") . ' ' . dol_print_date($this->date_commande, 'day');
3506
            }
3507
        } else {
3508
            $text = $langs->trans("OrderDate") . ' ' . dol_print_date($this->date_commande, 'day');
3509
        }
3510
        if ($text) {
3511
            $text .= ' ' . ($conf->commande->fournisseur->warning_delay > 0 ? '+' : '-') . ' ' . round(abs($conf->commande->fournisseur->warning_delay) / 3600 / 24, 1) . ' ' . $langs->trans("days") . ' < ' . $langs->trans("Today");
3512
        }
3513
3514
        return $text;
3515
    }
3516
3517
3518
    /**
3519
     * Calc status regarding to dispatched stock
3520
     *
3521
     * @param       User    $user                   User action
3522
     * @param       int     $closeopenorder         Close if received
3523
     * @param       string  $comment                Comment
3524
     * @return      int                             Return integer <0 if KO, 0 if not applicable, >0 if OK
3525
     */
3526
    public function calcAndSetStatusDispatch(User $user, $closeopenorder = 1, $comment = '')
3527
    {
3528
        if (isModEnabled("supplier_order")) {
3529
            require_once constant('DOL_DOCUMENT_ROOT') . '/fourn/class/fournisseur.commande.dispatch.class.php';
3530
3531
            $qtydelivered = array();
3532
            $qtywished = array();
3533
3534
            $supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
3535
3536
            $filter = array('t.fk_element' => $this->id);
3537
            if (getDolGlobalString('SUPPLIER_ORDER_USE_DISPATCH_STATUS')) {
3538
                $filter['t.status'] = 1; // Restrict to lines with status validated
3539
            }
3540
3541
            $ret = $supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
3542
            if ($ret < 0) {
3543
                $this->error = $supplierorderdispatch->error;
3544
                $this->errors = $supplierorderdispatch->errors;
3545
                return $ret;
3546
            } else {
3547
                if (is_array($supplierorderdispatch->lines) && count($supplierorderdispatch->lines) > 0) {
3548
                    require_once constant('DOL_DOCUMENT_ROOT') . '/product/class/product.class.php';
3549
                    $date_liv = dol_now();
3550
3551
                    // Build array with quantity deliverd by product
3552
                    foreach ($supplierorderdispatch->lines as $line) {
3553
                        $qtydelivered[$line->fk_product] += $line->qty;
3554
                    }
3555
                    foreach ($this->lines as $line) {
3556
                        // Exclude lines not qualified for shipment, similar code is found into interface_20_modWrokflow for customers
3557
                        if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES') && $line->product_type > 0) {
3558
                            continue;
3559
                        }
3560
                        $qtywished[$line->fk_product] += $line->qty;
3561
                    }
3562
3563
                    //Compare array
3564
                    $diff_array = array_diff_assoc($qtydelivered, $qtywished); // Warning: $diff_array is done only on common keys.
3565
                    $keysinwishednotindelivered = array_diff(array_keys($qtywished), array_keys($qtydelivered)); // To check we also have same number of keys
3566
                    $keysindeliverednotinwished = array_diff(array_keys($qtydelivered), array_keys($qtywished)); // To check we also have same number of keys
3567
                    //var_dump(array_keys($qtydelivered));
3568
                    //var_dump(array_keys($qtywished));
3569
                    //var_dump($diff_array);
3570
                    //var_dump($keysinwishednotindelivered);
3571
                    //var_dump($keysindeliverednotinwished);
3572
                    //exit;
3573
3574
                    if (count($diff_array) == 0 && count($keysinwishednotindelivered) == 0 && count($keysindeliverednotinwished) == 0) { //No diff => mean everything is received
3575
                        if ($closeopenorder) {
3576
                            //$ret=$this->setStatus($user,5);
3577
                            $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // $type is 'tot', 'par', 'nev', 'can'
3578
                            if ($ret < 0) {
3579
                                return -1;
3580
                            }
3581
                            return 5;
3582
                        } else {
3583
                            //Diff => received partially
3584
                            //$ret=$this->setStatus($user,4);
3585
                            $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3586
                            if ($ret < 0) {
3587
                                return -1;
3588
                            }
3589
                            return 4;
3590
                        }
3591
                    } elseif (getDolGlobalString('SUPPLIER_ORDER_MORE_THAN_WISHED')) {
3592
                        //set livraison to 'tot' if more products received than wished. (and if $closeopenorder is set to 1 of course...)
3593
3594
                        $close = 0;
3595
3596
                        if (count($diff_array) > 0) {
3597
                            //there are some difference between  the two arrays
3598
3599
                            //scan the array of results
3600
                            foreach ($diff_array as $key => $value) {
3601
                                //if the quantity delivered is greater or equal to wish quantity
3602
                                if ($qtydelivered[$key] >= $qtywished[$key]) {
3603
                                    $close++;
3604
                                }
3605
                            }
3606
                        }
3607
3608
3609
                        if ($close == count($diff_array)) {
3610
                            //all the products are received equal or more than the wished quantity
3611
                            if ($closeopenorder) {
3612
                                $ret = $this->Livraison($user, $date_liv, 'tot', $comment); // $type is 'tot', 'par', 'nev', 'can'
3613
                                if ($ret < 0) {
3614
                                    return -1;
3615
                                }
3616
                                return 5;
3617
                            } else {
3618
                                //Diff => received partially
3619
                                $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3620
                                if ($ret < 0) {
3621
                                    return -1;
3622
                                }
3623
                                return 4;
3624
                            }
3625
                        } else {
3626
                            //all the products are not received
3627
                            $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3628
                            if ($ret < 0) {
3629
                                return -1;
3630
                            }
3631
                            return 4;
3632
                        }
3633
                    } else {
3634
                        //Diff => received partially
3635
                        $ret = $this->Livraison($user, $date_liv, 'par', $comment); // $type is 'tot', 'par', 'nev', 'can'
3636
                        if ($ret < 0) {
3637
                            return -1;
3638
                        }
3639
                        return 4;
3640
                    }
3641
                }
3642
                return 1;
3643
            }
3644
        }
3645
        return 0;
3646
    }
3647
3648
    /**
3649
     *  Load array this->receptions of lines of shipments with nb of products sent for each order line
3650
     *  Note: For a dedicated shipment, the fetch_lines can be used to load the qty_asked and qty_shipped. This function is use to return qty_shipped cumulated for the order
3651
     *
3652
     *  @param      int     $filtre_statut      Filter on shipment status
3653
     *  @return     int                         Return integer <0 if KO, Nb of lines found if OK
3654
     */
3655
    public function loadReceptions($filtre_statut = -1)
3656
    {
3657
        $this->receptions = array();
3658
3659
        dol_syslog(get_class($this) . "::loadReceptions", LOG_DEBUG);
3660
3661
        $sql = 'SELECT cd.rowid, cd.fk_product,';
3662
        $sql .= ' sum(cfd.qty) as qty';
3663
        $sql .= ' FROM ' . $this->db->prefix() . 'receptiondet_batch as cfd,';
3664
        if ($filtre_statut >= 0) {
3665
            $sql .= ' ' . $this->db->prefix() . 'reception as e,';
3666
        }
3667
        $sql .= ' ' . $this->db->prefix() . 'commande_fournisseurdet as cd';
3668
        $sql .= ' WHERE';
3669
        if ($filtre_statut >= 0) {
3670
            $sql .= ' cfd.fk_reception = e.rowid AND';
3671
        }
3672
        $sql .= ' cfd.fk_elementdet = cd.rowid';
3673
        $sql .= ' AND cd.fk_commande =' . ((int) $this->id);
3674
        if (isset($this->fk_product) && !empty($this->fk_product) > 0) {
3675
            $sql .= ' AND cd.fk_product = ' . ((int) $this->fk_product);
3676
        }
3677
        if ($filtre_statut >= 0) {
3678
            $sql .= ' AND e.fk_statut >= ' . ((int) $filtre_statut);
3679
        }
3680
        $sql .= ' GROUP BY cd.rowid, cd.fk_product';
3681
3682
        $resql = $this->db->query($sql);
3683
        if ($resql) {
3684
            $num = $this->db->num_rows($resql);
3685
            $i = 0;
3686
            while ($i < $num) {
3687
                $obj = $this->db->fetch_object($resql);
3688
                empty($this->receptions[$obj->rowid]) ? $this->receptions[$obj->rowid] = $obj->qty : $this->receptions[$obj->rowid] += $obj->qty;
3689
                $i++;
3690
            }
3691
            $this->db->free($resql);
3692
3693
            return $num;
3694
        } else {
3695
            $this->error = $this->db->lasterror();
3696
            return -1;
3697
        }
3698
    }
3699
3700
    /**
3701
     *  Return clicable link of object (with eventually picto)
3702
     *
3703
     *  @param      string      $option                 Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link)
3704
     *  @param      array       $arraydata              Array of data
3705
     *  @return     string                              HTML Code for Kanban thumb.
3706
     */
3707
    public function getKanbanView($option = '', $arraydata = null)
3708
    {
3709
        global $langs;
3710
3711
        $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3712
3713
        $return = '<div class="box-flex-item box-flex-grow-zero">';
3714
        $return .= '<div class="info-box info-box-sm">';
3715
        $return .= '<span class="info-box-icon bg-infobox-action">';
3716
        $return .= img_picto('', $this->picto);
3717
        $return .= '</span>';
3718
        $return .= '<div class="info-box-content">';
3719
        $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . (method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref) . '</span>';
3720
        if ($selected >= 0) {
3721
            $return .= '<input id="cb' . $this->id . '" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="' . $this->id . '"' . ($selected ? ' checked="checked"' : '') . '>';
3722
        }
3723
        if (property_exists($this, 'socid') || property_exists($this, 'total_tva')) {
3724
            $return .= '<br><span class="info-box-label amount">' . $this->socid . '</span>';
3725
        }
3726
        if (property_exists($this, 'billed')) {
3727
            $return .= '<br><span class="opacitymedium">' . $langs->trans("Billed") . ' : </span><span class="info-box-label">' . yn($this->billed) . '</span>';
3728
        }
3729
        if (method_exists($this, 'getLibStatut')) {
3730
            $return .= '<br><div class="info-box-status">' . $this->getLibStatut(3) . '</div>';
3731
        }
3732
        $return .= '</div>';
3733
        $return .= '</div>';
3734
        $return .= '</div>';
3735
        return $return;
3736
    }
3737
}
3738