Passed
Push — CHECK_API ( 78ffdd...fa4f39 )
by Rafael
41:29
created

Invoices::index()   F

Complexity

Conditions 23
Paths 15873

Size

Total Lines 97
Code Lines 60

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 23
eloc 60
nc 15873
nop 8
dl 0
loc 97
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

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

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/* Copyright (C) 2015   Jean-François Ferry     <[email protected]>
4
 * Copyright (C) 2020   Thibault FOUCART		<[email protected]>
5
 * Copyright (C) 2023	Joachim Kueter			<[email protected]>
6
 * Copyright (C) 2024		Frédéric France			<[email protected]>
7
 * Copyright (C) 2024		MDW							<[email protected]>
8
 * Copyright (C) 2024       Rafael San José             <[email protected]>
9
 *
10
 * This program is free software; you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation; either version 3 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License
21
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22
 */
23
24
use Dolibarr\Code\Adherents\Classes\Adherent;
25
use Luracast\Restler\RestException;
26
27
require_once constant('DOL_DOCUMENT_ROOT') . '/compta/facture/class/facture-rec.class.php';
28
29
30
/**
31
 * API class for invoices
32
 *
33
 * @access protected
34
 * @class  DolibarrApiAccess {@requires user,external}
35
 */
36
class Invoices extends DolibarrApi
37
{
38
    /**
39
     *
40
     * @var array   $FIELDS     Mandatory fields, checked when create and update object
41
     */
42
    public static $FIELDS = array(
43
        'socid',
44
    );
45
46
    /**
47
     * @var Facture $invoice {@type Facture}
48
     */
49
    private $invoice;
50
51
    /**
52
     * @var FactureRec $templte_invoice {@type FactureRec}
53
     */
54
    private $template_invoice;
55
56
57
    /**
58
     * Constructor
59
     */
60
    public function __construct()
61
    {
62
        global $db;
63
        $this->db = $db;
64
        $this->invoice = new Facture($this->db);
65
        $this->template_invoice = new FactureRec($this->db);
66
    }
67
68
    /**
69
     * Get properties of a invoice object
70
     *
71
     * Return an array with invoice information
72
     *
73
     * @param   int     $id             ID of invoice
74
     * @param   int     $contact_list   0:Return array contains all properties, 1:Return array contains just id, -1: Do not return contacts/adddesses
75
     * @return  Object                  Object with cleaned properties
76
     *
77
     * @throws  RestException
78
     */
79
    public function get($id, $contact_list = 1)
80
    {
81
        return $this->_fetch($id, '', '', $contact_list);
82
    }
83
84
    /**
85
     * Get properties of an invoice object by ref
86
     *
87
     * Return an array with invoice information
88
     *
89
     * @param   string      $ref            Ref of object
90
     * @param   int         $contact_list   0: Returned array of contacts/addresses contains all properties, 1: Return array contains just id, -1: Do not return contacts/adddesses
91
     * @return  Object                      Object with cleaned properties
92
     *
93
     * @url GET    ref/{ref}
94
     *
95
     * @throws  RestException
96
     */
97
    public function getByRef($ref, $contact_list = 1)
98
    {
99
        return $this->_fetch('', $ref, '', $contact_list);
100
    }
101
102
    /**
103
     * Get properties of an invoice object by ref_ext
104
     *
105
     * Return an array with invoice information
106
     *
107
     * @param   string      $ref_ext        External reference of object
108
     * @param   int         $contact_list   0: Returned array of contacts/addresses contains all properties, 1: Return array contains just id, -1: Do not return contacts/adddesses
109
     * @return  Object                      Object with cleaned properties
110
     *
111
     * @url GET    ref_ext/{ref_ext}
112
     *
113
     * @throws  RestException
114
     */
115
    public function getByRefExt($ref_ext, $contact_list = 1)
116
    {
117
        return $this->_fetch('', '', $ref_ext, $contact_list);
118
    }
119
120
    /**
121
     * Get properties of an invoice object
122
     *
123
     * Return an array with invoice information
124
     *
125
     * @param   int         $id             ID of order
126
     * @param   string      $ref            Ref of object
127
     * @param   string      $ref_ext        External reference of object
128
     * @param   int         $contact_list   0: Returned array of contacts/addresses contains all properties, 1: Return array contains just id, -1: Do not return contacts/adddesses
129
     * @return  Object                      Object with cleaned properties
130
     *
131
     * @throws  RestException
132
     */
133
    private function _fetch($id, $ref = '', $ref_ext = '', $contact_list = 1)
134
    {
135
        if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
136
            throw new RestException(403);
137
        }
138
139
        $result = $this->invoice->fetch($id, $ref, $ref_ext);
140
        if (!$result) {
141
            throw new RestException(404, 'Invoice not found');
142
        }
143
144
        // Get payment details
145
        $this->invoice->totalpaid = $this->invoice->getSommePaiement();
146
        $this->invoice->totalcreditnotes = $this->invoice->getSumCreditNotesUsed();
147
        $this->invoice->totaldeposits = $this->invoice->getSumDepositsUsed();
148
        $this->invoice->remaintopay = price2num($this->invoice->total_ttc - $this->invoice->totalpaid - $this->invoice->totalcreditnotes - $this->invoice->totaldeposits, 'MT');
149
150
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
151
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
152
        }
153
154
        // Add external contacts ids
155
        if ($contact_list > -1) {
156
            $tmparray = $this->invoice->liste_contact(-1, 'external', $contact_list);
157
            if (is_array($tmparray)) {
158
                $this->invoice->contacts_ids = $tmparray;
159
            }
160
        }
161
162
        $this->invoice->fetchObjectLinked();
163
164
        // Add online_payment_url, copied from order
165
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/payments.lib.php';
166
        $this->invoice->online_payment_url = getOnlinePaymentUrl(0, 'invoice', $this->invoice->ref);
167
168
        return $this->_cleanObjectDatas($this->invoice);
169
    }
170
171
    /**
172
     * List invoices
173
     *
174
     * Get a list of invoices
175
     *
176
     * @param string    $sortfield        Sort field
177
     * @param string    $sortorder        Sort order
178
     * @param int       $limit            Limit for list
179
     * @param int       $page             Page number
180
     * @param string    $thirdparty_ids   Thirdparty ids to filter orders of (example '1' or '1,2,3') {@pattern /^[0-9,]*$/i}
181
     * @param string    $status           Filter by invoice status : draft | unpaid | paid | cancelled
182
     * @param string    $sqlfilters       Other criteria to filter answers separated by a comma. Syntax example "(t.ref:like:'SO-%') and (t.date_creation:<:'20160101')"
183
     * @param string    $properties Restrict the data returned to these properties. Ignored if empty. Comma separated list of properties names
184
     * @return array                      Array of invoice objects
185
     *
186
     * @throws RestException 404 Not found
187
     * @throws RestException 503 Error
188
     */
189
    public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $thirdparty_ids = '', $status = '', $sqlfilters = '', $properties = '')
190
    {
191
        if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
192
            throw new RestException(403);
193
        }
194
195
        $obj_ret = array();
196
197
        // case of external user, $thirdparty_ids param is ignored and replaced by user's socid
198
        $socids = DolibarrApiAccess::$user->socid ? DolibarrApiAccess::$user->socid : $thirdparty_ids;
199
200
        // If the internal user must only see his customers, force searching by him
201
        $search_sale = 0;
202
        if (!DolibarrApiAccess::$user->hasRight('societe', 'client', 'voir') && !$socids) {
203
            $search_sale = DolibarrApiAccess::$user->id;
204
        }
205
206
        $sql = "SELECT t.rowid";
207
        $sql .= " FROM " . MAIN_DB_PREFIX . "facture AS t";
208
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "facture_extrafields AS ef ON (ef.fk_object = t.rowid)"; // Modification VMR Global Solutions to include extrafields as search parameters in the API GET call, so we will be able to filter on extrafields
209
        $sql .= ' WHERE t.entity IN (' . getEntity('invoice') . ')';
210
        if ($socids) {
211
            $sql .= " AND t.fk_soc IN (" . $this->db->sanitize($socids) . ")";
212
        }
213
        // Search on sale representative
214
        if ($search_sale && $search_sale != '-1') {
215
            if ($search_sale == -2) {
216
                $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM " . MAIN_DB_PREFIX . "societe_commerciaux as sc WHERE sc.fk_soc = t.fk_soc)";
217
            } elseif ($search_sale > 0) {
218
                $sql .= " AND EXISTS (SELECT sc.fk_soc FROM " . MAIN_DB_PREFIX . "societe_commerciaux as sc WHERE sc.fk_soc = t.fk_soc AND sc.fk_user = " . ((int) $search_sale) . ")";
219
            }
220
        }
221
        // Filter by status
222
        if ($status == 'draft') {
223
            $sql .= " AND t.fk_statut IN (0)";
224
        }
225
        if ($status == 'unpaid') {
226
            $sql .= " AND t.fk_statut IN (1)";
227
        }
228
        if ($status == 'paid') {
229
            $sql .= " AND t.fk_statut IN (2)";
230
        }
231
        if ($status == 'cancelled') {
232
            $sql .= " AND t.fk_statut IN (3)";
233
        }
234
        // Add sql filters
235
        if ($sqlfilters) {
236
            $errormessage = '';
237
            $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
238
            if ($errormessage) {
239
                throw new RestException(400, 'Error when validating parameter sqlfilters -> ' . $errormessage);
240
            }
241
        }
242
243
        $sql .= $this->db->order($sortfield, $sortorder);
244
        if ($limit) {
245
            if ($page < 0) {
246
                $page = 0;
247
            }
248
            $offset = $limit * $page;
249
250
            $sql .= $this->db->plimit($limit + 1, $offset);
251
        }
252
253
        $result = $this->db->query($sql);
254
        if ($result) {
255
            $i = 0;
256
            $num = $this->db->num_rows($result);
257
            $min = min($num, ($limit <= 0 ? $num : $limit));
258
            while ($i < $min) {
259
                $obj = $this->db->fetch_object($result);
260
                $invoice_static = new Facture($this->db);
261
                if ($invoice_static->fetch($obj->rowid)) {
262
                    // Get payment details
263
                    $invoice_static->totalpaid = $invoice_static->getSommePaiement();
264
                    $invoice_static->totalcreditnotes = $invoice_static->getSumCreditNotesUsed();
265
                    $invoice_static->totaldeposits = $invoice_static->getSumDepositsUsed();
266
                    $invoice_static->remaintopay = price2num($invoice_static->total_ttc - $invoice_static->totalpaid - $invoice_static->totalcreditnotes - $invoice_static->totaldeposits, 'MT');
267
268
                    // Add external contacts ids
269
                    $tmparray = $invoice_static->liste_contact(-1, 'external', 1);
270
                    if (is_array($tmparray)) {
271
                        $invoice_static->contacts_ids = $tmparray;
272
                    }
273
                    // Add online_payment_url, copied from order
274
                    require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/payments.lib.php';
275
                    $invoice_static->online_payment_url = getOnlinePaymentUrl(0, 'invoice', $invoice_static->ref);
276
277
                    $obj_ret[] = $this->_filterObjectProperties($this->_cleanObjectDatas($invoice_static), $properties);
278
                }
279
                $i++;
280
            }
281
        } else {
282
            throw new RestException(503, 'Error when retrieve invoice list : ' . $this->db->lasterror());
283
        }
284
285
        return $obj_ret;
286
    }
287
288
    /**
289
     * Create invoice object
290
     *
291
     * @param array $request_data   Request datas
292
     * @return int                  ID of invoice
293
     */
294
    public function post($request_data = null)
295
    {
296
        if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
297
            throw new RestException(403, "Insuffisant rights");
298
        }
299
        // Check mandatory fields
300
        $result = $this->_validate($request_data);
301
302
        foreach ($request_data as $field => $value) {
303
            if ($field === 'caller') {
304
                // Add a mention of caller so on trigger called after action, we can filter to avoid a loop if we try to sync back again with the caller
305
                $this->invoice->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
306
                continue;
307
            }
308
309
            $this->invoice->$field = $this->_checkValForAPI($field, $value, $this->invoice);
310
        }
311
        if (!array_key_exists('date', $request_data)) {
312
            $this->invoice->date = dol_now();
313
        }
314
        /* We keep lines as an array
315
         if (isset($request_data["lines"])) {
316
            $lines = array();
317
            foreach ($request_data["lines"] as $line) {
318
                array_push($lines, (object) $line);
319
            }
320
            $this->invoice->lines = $lines;
321
        }*/
322
323
        if ($this->invoice->create(DolibarrApiAccess::$user, 0, (empty($request_data["date_lim_reglement"]) ? 0 : $request_data["date_lim_reglement"])) < 0) {
324
            throw new RestException(500, "Error creating invoice", array_merge(array($this->invoice->error), $this->invoice->errors));
325
        }
326
        return ((int) $this->invoice->id);
327
    }
328
329
    /**
330
     * Create an invoice using an existing order.
331
     *
332
     * @param int   $orderid       Id of the order
333
     * @return  Object              Object with cleaned properties
334
     *
335
     * @url     POST /createfromorder/{orderid}
336
     *
337
     * @throws RestException 400
338
     * @throws RestException 401
339
     * @throws RestException 404
340
     * @throws RestException 405
341
     */
342
    public function createInvoiceFromOrder($orderid)
343
    {
344
        if (!DolibarrApiAccess::$user->hasRight('commande', 'lire')) {
345
            throw new RestException(403);
346
        }
347
        if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
348
            throw new RestException(403);
349
        }
350
        if (empty($orderid)) {
351
            throw new RestException(400, 'Order ID is mandatory');
352
        }
353
354
        $order = new Commande($this->db);
355
        $result = $order->fetch($orderid);
356
        if (!$result) {
357
            throw new RestException(404, 'Order not found');
358
        }
359
360
        $result = $this->invoice->createFromOrder($order, DolibarrApiAccess::$user);
361
        if ($result < 0) {
362
            throw new RestException(405, $this->invoice->error);
363
        }
364
        $this->invoice->fetchObjectLinked();
365
        return $this->_cleanObjectDatas($this->invoice);
366
    }
367
368
    /**
369
    * Create an invoice using a contract.
370
    *
371
    * @param int   $contractid       Id of the contract
372
    * @return     Object                          Object with cleaned properties
373
    *
374
    * @url     POST /createfromcontract/{contractid}
375
    *
376
    * @throws RestException 400
377
    * @throws RestException 401
378
    * @throws RestException 404
379
    * @throws RestException 405
380
    */
381
    public function createInvoiceFromContract($contractid)
382
    {
383
    
384
        if (!DolibarrApiAccess::$user->hasRight('contrat', 'lire')) {
385
            throw new RestException(403);
386
        }
387
        if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
388
            throw new RestException(403);
389
        }
390
        if (empty($contractid)) {
391
            throw new RestException(400, 'Contract ID is mandatory');
392
        }
393
394
        $contract = new Contrat($this->db);
395
        $result = $contract->fetch($contractid);
396
        if (!$result) {
397
            throw new RestException(404, 'Contract not found');
398
        }
399
400
        $result = $this->invoice->createFromContract($contract, DolibarrApiAccess::$user);
401
        if ($result < 0) {
402
            throw new RestException(405, $this->invoice->error);
403
        }
404
        $this->invoice->fetchObjectLinked();
405
        return $this->_cleanObjectDatas($this->invoice);
406
    }
407
408
    /**
409
     * Get lines of an invoice
410
     *
411
     * @param   int   $id               Id of invoice
412
     * @return  array                   Array of lines
413
     *
414
     * @url GET {id}/lines
415
     */
416
    public function getLines($id)
417
    {
418
        if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
419
            throw new RestException(403);
420
        }
421
422
        $result = $this->invoice->fetch($id);
423
        if (!$result) {
424
            throw new RestException(404, 'Invoice not found');
425
        }
426
427
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
428
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
429
        }
430
        $this->invoice->getLinesArray();
431
        $result = array();
432
        foreach ($this->invoice->lines as $line) {
433
            array_push($result, $this->_cleanObjectDatas($line));
434
        }
435
        return $result;
436
    }
437
438
    /**
439
     * Update a line to a given invoice
440
     *
441
     * @param   int   $id             Id of invoice to update
442
     * @param   int   $lineid         Id of line to update
443
     * @param   array $request_data   InvoiceLine data
444
     * @return  Object                Object with cleaned properties
445
     *
446
     * @url PUT {id}/lines/{lineid}
447
     *
448
     * @throws RestException 304
449
     * @throws RestException 401
450
     * @throws RestException 404 Invoice not found
451
     */
452
    public function putLine($id, $lineid, $request_data = null)
453
    {
454
        if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
455
            throw new RestException(403);
456
        }
457
458
        $result = $this->invoice->fetch($id);
459
        if (!$result) {
460
            throw new RestException(404, 'Invoice not found');
461
        }
462
463
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
464
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
465
        }
466
467
        $request_data = (object) $request_data;
468
469
        $request_data->desc = sanitizeVal($request_data->desc, 'restricthtml');
470
        $request_data->label = sanitizeVal($request_data->label);
471
472
        $updateRes = $this->invoice->updateline(
473
            $lineid,
474
            $request_data->desc,
475
            $request_data->subprice,
476
            $request_data->qty,
477
            $request_data->remise_percent,
478
            $request_data->date_start,
479
            $request_data->date_end,
480
            $request_data->tva_tx,
481
            $request_data->localtax1_tx,
482
            $request_data->localtax2_tx,
483
            $request_data->price_base_type ? $request_data->price_base_type : 'HT',
484
            $request_data->info_bits,
485
            $request_data->product_type,
486
            $request_data->fk_parent_line,
487
            0,
488
            $request_data->fk_fournprice,
489
            $request_data->pa_ht,
490
            $request_data->label,
491
            $request_data->special_code,
492
            $request_data->array_options,
493
            $request_data->situation_percent,
494
            $request_data->fk_unit,
495
            $request_data->multicurrency_subprice,
496
            0,
497
            $request_data->ref_ext,
498
            $request_data->rang
499
        );
500
501
        if ($updateRes > 0) {
502
            $result = $this->get($id);
503
            unset($result->line);
504
            return $this->_cleanObjectDatas($result);
505
        } else {
506
            throw new RestException(304, $this->invoice->error);
507
        }
508
    }
509
510
    /**
511
     * Add a contact type of given invoice
512
     *
513
     * @param   int    $id             Id of invoice to update
514
     * @param   int    $contactid      Id of contact to add
515
     * @param   string $type           Type of the contact (BILLING, SHIPPING, CUSTOMER)
516
     * @return  array
517
     *
518
     * @url POST {id}/contact/{contactid}/{type}
519
     *
520
     * @throws RestException 401
521
     * @throws RestException 404
522
     */
523
    public function postContact($id, $contactid, $type)
524
    {
525
        if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
526
            throw new RestException(403);
527
        }
528
529
        $result = $this->invoice->fetch($id);
530
531
        if (!$result) {
532
            throw new RestException(404, 'Invoice not found');
533
        }
534
535
        if (!in_array($type, array('BILLING', 'SHIPPING', 'CUSTOMER'), true)) {
536
            throw new RestException(500, 'Availables types: BILLING, SHIPPING OR CUSTOMER');
537
        }
538
539
        if (!DolibarrApi::_checkAccessToResource('invoice', $this->invoice->id)) {
540
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
541
        }
542
543
        $result = $this->invoice->add_contact($contactid, $type, 'external');
544
545
        if (!$result) {
546
            throw new RestException(500, 'Error when added the contact');
547
        }
548
549
        return array(
550
            'success' => array(
551
                'code' => 200,
552
                'message' => 'Contact linked to the invoice'
553
            )
554
        );
555
    }
556
557
    /**
558
     * Delete a contact type of given invoice
559
     *
560
     * @param   int    $id             Id of invoice to update
561
     * @param   int    $contactid      Row key of the contact in the array contact_ids.
562
     * @param   string $type           Type of the contact (BILLING, SHIPPING, CUSTOMER).
563
     * @return  Object                 Object with cleaned properties
564
     *
565
     * @url DELETE {id}/contact/{contactid}/{type}
566
     *
567
     * @throws RestException 401
568
     * @throws RestException 404
569
     * @throws RestException 500 System error
570
     */
571
    public function deleteContact($id, $contactid, $type)
572
    {
573
        if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
574
            throw new RestException(403);
575
        }
576
577
        $result = $this->invoice->fetch($id);
578
579
        if (!$result) {
580
            throw new RestException(404, 'Invoice not found');
581
        }
582
583
        if (!DolibarrApi::_checkAccessToResource('invoice', $this->invoice->id)) {
584
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
585
        }
586
587
        $contacts = $this->invoice->liste_contact();
588
589
        foreach ($contacts as $contact) {
590
            if ($contact['id'] == $contactid && $contact['code'] == $type) {
591
                $result = $this->invoice->delete_contact($contact['rowid']);
592
593
                if (!$result) {
594
                    throw new RestException(500, 'Error when deleted the contact');
595
                }
596
            }
597
        }
598
599
        return $this->_cleanObjectDatas($this->invoice);
600
    }
601
602
    /**
603
     * Deletes a line of a given invoice
604
     *
605
     * @param   int   $id               Id of invoice
606
     * @param   int   $lineid           Id of the line to delete
607
     * @return  Object                  Object with cleaned properties
608
     *
609
     * @url     DELETE {id}/lines/{lineid}
610
     *
611
     * @throws RestException 400
612
     * @throws RestException 401
613
     * @throws RestException 404
614
     * @throws RestException 405
615
     */
616
    public function deleteLine($id, $lineid)
617
    {
618
        if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
619
            throw new RestException(403);
620
        }
621
        if (empty($lineid)) {
622
            throw new RestException(400, 'Line ID is mandatory');
623
        }
624
625
        if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
626
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
627
        }
628
629
        $result = $this->invoice->fetch($id);
630
        if (!$result) {
631
            throw new RestException(404, 'Invoice not found');
632
        }
633
634
        $updateRes = $this->invoice->deleteLine($lineid, $id);
635
        if ($updateRes > 0) {
636
            return $this->get($id);
637
        } else {
638
            throw new RestException(405, $this->invoice->error);
639
        }
640
    }
641
642
    /**
643
     * Update invoice
644
     *
645
     * @param   int             $id             Id of invoice to update
646
     * @param   array           $request_data   Datas
647
     * @return  Object|false                    Object with cleaned properties
648
     */
649
    public function put($id, $request_data = null)
650
    {
651
        if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
652
            throw new RestException(403);
653
        }
654
655
        $result = $this->invoice->fetch($id);
656
        if (!$result) {
657
            throw new RestException(404, 'Invoice not found');
658
        }
659
660
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
661
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
662
        }
663
664
        foreach ($request_data as $field => $value) {
665
            if ($field == 'id') {
666
                continue;
667
            }
668
            if ($field === 'caller') {
669
                // Add a mention of caller so on trigger called after action, we can filter to avoid a loop if we try to sync back again with the caller
670
                $this->invoice->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
671
                continue;
672
            }
673
            if ($field == 'array_options' && is_array($value)) {
674
                foreach ($value as $index => $val) {
675
                    $this->invoice->array_options[$index] = $this->_checkValForAPI($field, $val, $this->invoice);
676
                }
677
                continue;
678
            }
679
680
            $this->invoice->$field = $this->_checkValForAPI($field, $value, $this->invoice);
681
682
            // If cond reglement => update date lim reglement
683
            if ($field == 'cond_reglement_id') {
684
                $this->invoice->date_lim_reglement = $this->invoice->calculate_date_lim_reglement();
685
            }
686
        }
687
688
        // update bank account
689
        if (!empty($this->invoice->fk_account)) {
690
            if ($this->invoice->setBankAccount($this->invoice->fk_account) == 0) {
691
                throw new RestException(400, $this->invoice->error);
692
            }
693
        }
694
695
        if ($this->invoice->update(DolibarrApiAccess::$user)) {
696
            return $this->get($id);
697
        }
698
699
        return false;
700
    }
701
702
    /**
703
     * Delete invoice
704
     *
705
     * @param   int     $id     Invoice ID
706
     * @return  array
707
     */
708
    public function delete($id)
709
    {
710
        if (!DolibarrApiAccess::$user->hasRight('facture', 'supprimer')) {
711
            throw new RestException(403);
712
        }
713
        $result = $this->invoice->fetch($id);
714
        if (!$result) {
715
            throw new RestException(404, 'Invoice not found');
716
        }
717
718
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
719
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
720
        }
721
722
        $result = $this->invoice->delete(DolibarrApiAccess::$user);
723
        if ($result < 0) {
724
            throw new RestException(500, 'Error when deleting invoice');
725
        } elseif ($result == 0) {
726
            throw new RestException(403, 'Invoice not erasable');
727
        }
728
729
        return array(
730
            'success' => array(
731
                'code' => 200,
732
                'message' => 'Invoice deleted'
733
            )
734
        );
735
    }
736
737
    /**
738
     * Add a line to a given invoice
739
     *
740
     * Example of POST query :
741
     * {
742
     *     "desc": "Desc", "subprice": "1.00000000", "qty": "1", "tva_tx": "20.000", "localtax1_tx": "0.000", "localtax2_tx": "0.000",
743
     *     "fk_product": "1", "remise_percent": "0", "date_start": "", "date_end": "", "fk_code_ventilation": 0,  "info_bits": "0",
744
     *     "fk_remise_except": null,  "product_type": "1", "rang": "-1", "special_code": "0", "fk_parent_line": null, "fk_fournprice": null,
745
     *     "pa_ht": "0.00000000", "label": "", "array_options": [], "situation_percent": "100", "fk_prev_id": null, "fk_unit": null
746
     * }
747
     *
748
     * @param int   $id             Id of invoice
749
     * @param array $request_data   InvoiceLine data
750
     *
751
     * @url     POST {id}/lines
752
     *
753
     * @return int
754
     *
755
     * @throws RestException 304
756
     * @throws RestException 401
757
     * @throws RestException 404
758
     * @throws RestException 400
759
     */
760
    public function postLine($id, $request_data = null)
761
    {
762
        if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
763
            throw new RestException(403);
764
        }
765
766
        $result = $this->invoice->fetch($id);
767
        if (!$result) {
768
            throw new RestException(404, 'Invoice not found');
769
        }
770
771
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
772
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
773
        }
774
775
        $request_data = (object) $request_data;
776
777
        $request_data->desc = sanitizeVal($request_data->desc, 'restricthtml');
778
        $request_data->label = sanitizeVal($request_data->label);
779
780
        // Reset fk_parent_line for no child products and special product
781
        if (($request_data->product_type != 9 && empty($request_data->fk_parent_line)) || $request_data->product_type == 9) {
782
            $request_data->fk_parent_line = 0;
783
        }
784
785
        // calculate pa_ht
786
        $marginInfos = getMarginInfos($request_data->subprice, $request_data->remise_percent, $request_data->tva_tx, $request_data->localtax1_tx, $request_data->localtax2_tx, $request_data->fk_fournprice, $request_data->pa_ht);
787
        $pa_ht = $marginInfos[0];
788
789
        $updateRes = $this->invoice->addline(
790
            $request_data->desc,
791
            $request_data->subprice,
792
            $request_data->qty,
793
            $request_data->tva_tx,
794
            $request_data->localtax1_tx,
795
            $request_data->localtax2_tx,
796
            $request_data->fk_product,
797
            $request_data->remise_percent,
798
            $request_data->date_start,
799
            $request_data->date_end,
800
            $request_data->fk_code_ventilation,
801
            $request_data->info_bits,
802
            $request_data->fk_remise_except,
803
            $request_data->price_base_type ? $request_data->price_base_type : 'HT',
804
            $request_data->subprice,
805
            $request_data->product_type,
806
            $request_data->rang,
807
            $request_data->special_code,
808
            $request_data->origin,
809
            $request_data->origin_id,
810
            $request_data->fk_parent_line,
811
            empty($request_data->fk_fournprice) ? null : $request_data->fk_fournprice,
812
            $pa_ht,
813
            $request_data->label,
814
            $request_data->array_options,
815
            $request_data->situation_percent,
816
            $request_data->fk_prev_id,
817
            $request_data->fk_unit,
818
            0,
819
            $request_data->ref_ext
820
        );
821
822
        if ($updateRes < 0) {
823
            throw new RestException(400, 'Unable to insert the new line. Check your inputs. ' . $this->invoice->error);
824
        }
825
826
        return $updateRes;
827
    }
828
829
    /**
830
     * Adds a contact to an invoice
831
     *
832
     * @param   int     $id                 Order ID
833
     * @param   int     $fk_socpeople           Id of thirdparty contact (if source = 'external') or id of user (if source = 'internal') to link
834
     * @param   string  $type_contact           Type of contact (code). Must a code found into table llx_c_type_contact. For example: BILLING
835
     * @param   string  $source                 external=Contact extern (llx_socpeople), internal=Contact intern (llx_user)
836
     * @param   int     $notrigger              Disable all triggers
837
     *
838
     * @url POST    {id}/contacts
839
     *
840
     * @return  object
841
     *
842
     * @throws RestException 304
843
     * @throws RestException 401
844
     * @throws RestException 404
845
     * @throws RestException 500 System error
846
     *
847
     */
848
    public function addContact($id, $fk_socpeople, $type_contact, $source, $notrigger = 0)
849
    {
850
        if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
851
            throw new RestException(403);
852
        }
853
        $result = $this->invoice->fetch($id);
854
        if (!$result) {
855
            throw new RestException(404, 'Invoice not found');
856
        }
857
858
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
859
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
860
        }
861
862
        $result = $this->invoice->add_contact($fk_socpeople, $type_contact, $source, $notrigger);
863
        if ($result < 0) {
864
            throw new RestException(500, 'Error : ' . $this->invoice->error);
865
        }
866
867
        $result = $this->invoice->fetch($id);
868
        if (!$result) {
869
            throw new RestException(404, 'Invoice not found');
870
        }
871
872
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
873
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
874
        }
875
876
        return $this->_cleanObjectDatas($this->invoice);
877
    }
878
879
880
881
    /**
882
     * Sets an invoice as draft
883
     *
884
     * @param   int $id             Order ID
885
     * @param   int $idwarehouse    Warehouse ID
886
     * @return  Object              Object with cleaned properties
887
     *
888
     * @url POST    {id}/settodraft
889
     *
890
     * @throws RestException 304
891
     * @throws RestException 401
892
     * @throws RestException 404
893
     * @throws RestException 500 System error
894
     *
895
     */
896
    public function settodraft($id, $idwarehouse = -1)
897
    {
898
        if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
899
            throw new RestException(403);
900
        }
901
        $result = $this->invoice->fetch($id);
902
        if (!$result) {
903
            throw new RestException(404, 'Invoice not found');
904
        }
905
906
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
907
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
908
        }
909
910
        $result = $this->invoice->setDraft(DolibarrApiAccess::$user, $idwarehouse);
911
        if ($result == 0) {
912
            throw new RestException(304, 'Nothing done.');
913
        }
914
        if ($result < 0) {
915
            throw new RestException(500, 'Error : ' . $this->invoice->error);
916
        }
917
918
        $result = $this->invoice->fetch($id);
919
        if (!$result) {
920
            throw new RestException(404, 'Invoice not found');
921
        }
922
923
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
924
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
925
        }
926
927
        return $this->_cleanObjectDatas($this->invoice);
928
    }
929
930
931
    /**
932
     * Validate an invoice
933
     *
934
     * If you get a bad value for param notrigger check that ou provide this in body
935
     * {
936
     *   "idwarehouse": 0,
937
     *   "notrigger": 0
938
     * }
939
     *
940
     * @param   int $id                 Invoice ID
941
     * @param   string $force_number    force ref invoice
942
     * @param   int $idwarehouse        Warehouse ID
943
     * @param   int $notrigger          1=Does not execute triggers, 0= execute triggers
944
     * @return  Object|false            Object with cleaned properties
945
     *
946
     * @url POST    {id}/validate
947
     */
948
    public function validate($id, $force_number = '', $idwarehouse = 0, $notrigger = 0)
949
    {
950
        if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
951
            throw new RestException(403);
952
        }
953
        $result = $this->invoice->fetch($id);
954
        if (!$result) {
955
            throw new RestException(404, 'Invoice not found');
956
        }
957
958
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
959
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
960
        }
961
962
        $result = $this->invoice->validate(DolibarrApiAccess::$user, $force_number, $idwarehouse, $notrigger);
963
        if ($result == 0) {
964
            throw new RestException(304, 'Error nothing done. May be object is already validated');
965
        }
966
        if ($result < 0) {
967
            throw new RestException(500, 'Error when validating Invoice: ' . $this->invoice->error);
968
        }
969
970
        $result = $this->invoice->fetch($id);
971
        if (!$result) {
972
            throw new RestException(404, 'Invoice not found');
973
        }
974
975
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
976
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
977
        }
978
979
        // copy from order
980
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/payments.lib.php';
981
        $this->invoice->online_payment_url = getOnlinePaymentUrl(0, 'invoice', $this->invoice->ref);
982
983
        return $this->_cleanObjectDatas($this->invoice);
984
    }
985
986
    /**
987
     * Sets an invoice as paid
988
     *
989
     * @param   int     $id            Order ID
990
     * @param   string  $close_code    Code filled if we classify to 'Paid completely' when payment is not complete (for escompte for example)
991
     * @param   string  $close_note    Comment defined if we classify to 'Paid' when payment is not complete (for escompte for example)
992
     * @return  Object                 Object with cleaned properties
993
     *
994
     * @url POST    {id}/settopaid
995
     *
996
     * @throws RestException 304
997
     * @throws RestException 401
998
     * @throws RestException 404
999
     * @throws RestException 500 System error
1000
     */
1001
    public function settopaid($id, $close_code = '', $close_note = '')
1002
    {
1003
        if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1004
            throw new RestException(403);
1005
        }
1006
        $result = $this->invoice->fetch($id);
1007
        if (!$result) {
1008
            throw new RestException(404, 'Invoice not found');
1009
        }
1010
1011
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1012
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
1013
        }
1014
1015
        $result = $this->invoice->setPaid(DolibarrApiAccess::$user, $close_code, $close_note);
1016
        if ($result == 0) {
1017
            throw new RestException(304, 'Error nothing done. May be object is already validated');
1018
        }
1019
        if ($result < 0) {
1020
            throw new RestException(500, 'Error : ' . $this->invoice->error);
1021
        }
1022
1023
1024
        $result = $this->invoice->fetch($id);
1025
        if (!$result) {
1026
            throw new RestException(404, 'Invoice not found');
1027
        }
1028
1029
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1030
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
1031
        }
1032
1033
        return $this->_cleanObjectDatas($this->invoice);
1034
    }
1035
1036
1037
    /**
1038
     * Sets an invoice as unpaid
1039
     *
1040
     * @param   int     $id             Order ID
1041
     * @return  Object                  Object with cleaned properties
1042
     *
1043
     * @url POST    {id}/settounpaid
1044
     *
1045
     * @throws RestException 304
1046
     * @throws RestException 401
1047
     * @throws RestException 404
1048
     * @throws RestException 500 System error
1049
     */
1050
    public function settounpaid($id)
1051
    {
1052
        if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1053
            throw new RestException(403);
1054
        }
1055
        $result = $this->invoice->fetch($id);
1056
        if (!$result) {
1057
            throw new RestException(404, 'Invoice not found');
1058
        }
1059
1060
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1061
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
1062
        }
1063
1064
        $result = $this->invoice->setUnpaid(DolibarrApiAccess::$user);
1065
        if ($result == 0) {
1066
            throw new RestException(304, 'Nothing done');
1067
        }
1068
        if ($result < 0) {
1069
            throw new RestException(500, 'Error : ' . $this->invoice->error);
1070
        }
1071
1072
1073
        $result = $this->invoice->fetch($id);
1074
        if (!$result) {
1075
            throw new RestException(404, 'Invoice not found');
1076
        }
1077
1078
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1079
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
1080
        }
1081
1082
        return $this->_cleanObjectDatas($this->invoice);
1083
    }
1084
1085
    /**
1086
     * Get discount from invoice
1087
     *
1088
     * @param int   $id             Id of invoice
1089
     * @return  Object              Object with cleaned properties
1090
     *
1091
     * @url GET {id}/discount
1092
     */
1093
    public function getDiscount($id)
1094
    {
1095
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/class/discount.class.php';
1096
1097
        if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
1098
            throw new RestException(403);
1099
        }
1100
1101
        $result = $this->invoice->fetch($id);
1102
        if (!$result) {
1103
            throw new RestException(404, 'Invoice not found');
1104
        }
1105
1106
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1107
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
1108
        }
1109
1110
        $discountcheck = new DiscountAbsolute($this->db);
1111
        $result = $discountcheck->fetch(0, $this->invoice->id);
1112
1113
        if ($result == 0) {
1114
            throw new RestException(404, 'Discount not found');
1115
        }
1116
        if ($result < 0) {
1117
            throw new RestException(500, $discountcheck->error);
1118
        }
1119
1120
        return parent::_cleanObjectDatas($discountcheck);
1121
    }
1122
1123
    /**
1124
     * Create a discount (credit available) for a credit note or a deposit.
1125
     *
1126
     * @param   int     $id             Invoice ID
1127
     * @return  Object                  Object with cleaned properties
1128
     *
1129
     * @url POST    {id}/markAsCreditAvailable
1130
     *
1131
     * @throws RestException 304
1132
     * @throws RestException 401
1133
     * @throws RestException 404
1134
     * @throws RestException 500 System error
1135
     */
1136
    public function markAsCreditAvailable($id)
1137
    {
1138
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/class/discount.class.php';
1139
1140
        if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1141
            throw new RestException(403);
1142
        }
1143
1144
        $result = $this->invoice->fetch($id);
1145
        if (!$result) {
1146
            throw new RestException(404, 'Invoice not found');
1147
        }
1148
1149
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1150
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
1151
        }
1152
1153
        if ($this->invoice->paye) {
1154
            throw new RestException(500, 'Alreay paid');
1155
        }
1156
1157
        $this->invoice->fetch($id);
1158
        $this->invoice->fetch_thirdparty();
1159
1160
        // Check if there is already a discount (protection to avoid duplicate creation when resubmit post)
1161
        $discountcheck = new DiscountAbsolute($this->db);
1162
        $result = $discountcheck->fetch(0, $this->invoice->id);
1163
1164
        $canconvert = 0;
1165
        if ($this->invoice->type == Facture::TYPE_DEPOSIT && empty($discountcheck->id)) {
1166
            $canconvert = 1; // we can convert deposit into discount if deposit is paid (completely, partially or not at all) and not already converted (see real condition into condition used to show button converttoreduc)
1167
        }
1168
        if (($this->invoice->type == Facture::TYPE_CREDIT_NOTE || $this->invoice->type == Facture::TYPE_STANDARD) && $this->invoice->paye == 0 && empty($discountcheck->id)) {
1169
            $canconvert = 1; // we can convert credit note into discount if credit note is not paid back and not already converted and amount of payment is 0 (see real condition into condition used to show button converttoreduc)
1170
        }
1171
        if ($canconvert) {
1172
            $this->db->begin();
1173
1174
            $amount_ht = $amount_tva = $amount_ttc = array();
1175
            $multicurrency_amount_ht = $multicurrency_amount_tva = $multicurrency_amount_ttc = array();
1176
1177
            // Loop on each vat rate
1178
            $i = 0;
1179
            foreach ($this->invoice->lines as $line) {
1180
                if ($line->product_type < 9 && $line->total_ht != 0) { // Remove lines with product_type greater than or equal to 9
1181
                    // no need to create discount if amount is null
1182
                    $amount_ht[$line->tva_tx] += $line->total_ht;
1183
                    $amount_tva[$line->tva_tx] += $line->total_tva;
1184
                    $amount_ttc[$line->tva_tx] += $line->total_ttc;
1185
                    $multicurrency_amount_ht[$line->tva_tx] += $line->multicurrency_total_ht;
1186
                    $multicurrency_amount_tva[$line->tva_tx] += $line->multicurrency_total_tva;
1187
                    $multicurrency_amount_ttc[$line->tva_tx] += $line->multicurrency_total_ttc;
1188
                    $i++;
1189
                }
1190
            }
1191
1192
            // Insert one discount by VAT rate category
1193
            $discount = new DiscountAbsolute($this->db);
1194
            if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1195
                $discount->description = '(CREDIT_NOTE)';
1196
            } elseif ($this->invoice->type == Facture::TYPE_DEPOSIT) {
1197
                $discount->description = '(DEPOSIT)';
1198
            } elseif ($this->invoice->type == Facture::TYPE_STANDARD || $this->invoice->type == Facture::TYPE_REPLACEMENT || $this->invoice->type == Facture::TYPE_SITUATION) {
1199
                $discount->description = '(EXCESS RECEIVED)';
1200
            } else {
1201
                throw new RestException(500, 'Cant convert to reduc an Invoice of this type');
1202
            }
1203
1204
            $discount->fk_soc = $this->invoice->socid;
1205
            $discount->socid = $this->invoice->socid;
1206
            $discount->fk_facture_source = $this->invoice->id;
1207
1208
            $error = 0;
1209
1210
            if ($this->invoice->type == Facture::TYPE_STANDARD || $this->invoice->type == Facture::TYPE_REPLACEMENT || $this->invoice->type == Facture::TYPE_SITUATION) {
1211
                // If we're on a standard invoice, we have to get excess received to create a discount in TTC without VAT
1212
1213
                // Total payments
1214
                $sql = 'SELECT SUM(pf.amount) as total_payments';
1215
                $sql .= ' FROM ' . MAIN_DB_PREFIX . 'paiement_facture as pf, ' . MAIN_DB_PREFIX . 'paiement as p';
1216
                $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_paiement as c ON p.fk_paiement = c.id';
1217
                $sql .= ' WHERE pf.fk_facture = ' . ((int) $this->invoice->id);
1218
                $sql .= ' AND pf.fk_paiement = p.rowid';
1219
                $sql .= ' AND p.entity IN (' . getEntity('invoice') . ')';
1220
                $resql = $this->db->query($sql);
1221
                if (!$resql) {
1222
                    dol_print_error($this->db);
1223
                }
1224
1225
                $res = $this->db->fetch_object($resql);
1226
                $total_payments = $res->total_payments;
1227
1228
                // Total credit note and deposit
1229
                $total_creditnote_and_deposit = 0;
1230
                $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
1231
                $sql .= " re.description, re.fk_facture_source";
1232
                $sql .= " FROM " . MAIN_DB_PREFIX . "societe_remise_except as re";
1233
                $sql .= " WHERE fk_facture = " . ((int) $this->invoice->id);
1234
                $resql = $this->db->query($sql);
1235
                if (!empty($resql)) {
1236
                    while ($obj = $this->db->fetch_object($resql)) {
1237
                        $total_creditnote_and_deposit += $obj->amount_ttc;
1238
                    }
1239
                } else {
1240
                    dol_print_error($this->db);
1241
                }
1242
1243
                $discount->amount_ht = $discount->amount_ttc = $total_payments + $total_creditnote_and_deposit - $this->invoice->total_ttc;
1244
                $discount->amount_tva = 0;
1245
                $discount->tva_tx = 0;
1246
1247
                $result = $discount->create(DolibarrApiAccess::$user);
1248
                if ($result < 0) {
1249
                    $error++;
1250
                }
1251
            }
1252
            if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE || $this->invoice->type == Facture::TYPE_DEPOSIT) {
1253
                foreach ($amount_ht as $tva_tx => $xxx) {
1254
                    $discount->amount_ht = abs($amount_ht[$tva_tx]);
1255
                    $discount->amount_tva = abs($amount_tva[$tva_tx]);
1256
                    $discount->amount_ttc = abs($amount_ttc[$tva_tx]);
1257
                    $discount->multicurrency_amount_ht = abs($multicurrency_amount_ht[$tva_tx]);
1258
                    $discount->multicurrency_amount_tva = abs($multicurrency_amount_tva[$tva_tx]);
1259
                    $discount->multicurrency_amount_ttc = abs($multicurrency_amount_ttc[$tva_tx]);
1260
                    $discount->tva_tx = abs($tva_tx);
1261
1262
                    $result = $discount->create(DolibarrApiAccess::$user);
1263
                    if ($result < 0) {
1264
                        $error++;
1265
                        break;
1266
                    }
1267
                }
1268
            }
1269
1270
            if (empty($error)) {
1271
                if ($this->invoice->type != Facture::TYPE_DEPOSIT) {
1272
                    // Set the invoice as paid
1273
                    $result = $this->invoice->setPaid(DolibarrApiAccess::$user);
1274
                    if ($result >= 0) {
1275
                        $this->db->commit();
1276
                    } else {
1277
                        $this->db->rollback();
1278
                        throw new RestException(500, 'Could not set paid');
1279
                    }
1280
                } else {
1281
                    $this->db->commit();
1282
                }
1283
            } else {
1284
                $this->db->rollback();
1285
                throw new RestException(500, 'Discount creation error');
1286
            }
1287
        }
1288
1289
        return $this->_cleanObjectDatas($this->invoice);
1290
    }
1291
1292
    /**
1293
     * Add a discount line into an invoice (as an invoice line) using an existing absolute discount
1294
     *
1295
     * Note that this consume the discount.
1296
     *
1297
     * @param int   $id             Id of invoice
1298
     * @param int   $discountid     Id of discount
1299
     * @return int
1300
     *
1301
     * @url     POST {id}/usediscount/{discountid}
1302
     *
1303
     * @throws RestException 400
1304
     * @throws RestException 401
1305
     * @throws RestException 404
1306
     * @throws RestException 405
1307
     */
1308
    public function useDiscount($id, $discountid)
1309
    {
1310
        if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1311
            throw new RestException(403);
1312
        }
1313
        if (empty($id)) {
1314
            throw new RestException(400, 'Invoice ID is mandatory');
1315
        }
1316
        if (empty($discountid)) {
1317
            throw new RestException(400, 'Discount ID is mandatory');
1318
        }
1319
1320
        if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1321
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
1322
        }
1323
1324
        $result = $this->invoice->fetch($id);
1325
        if (!$result) {
1326
            throw new RestException(404, 'Invoice not found');
1327
        }
1328
1329
        $result = $this->invoice->insert_discount($discountid);
1330
        if ($result < 0) {
1331
            throw new RestException(405, $this->invoice->error);
1332
        }
1333
1334
        return $result;
1335
    }
1336
1337
    /**
1338
     * Add an available credit note discount to payments of an existing invoice.
1339
     *
1340
     *  Note that this consume the credit note.
1341
     *
1342
     * @param int   $id            Id of invoice
1343
     * @param int   $discountid    Id of a discount coming from a credit note
1344
     * @return  int
1345
     *
1346
     * @url     POST {id}/usecreditnote/{discountid}
1347
     *
1348
     * @throws RestException 400
1349
     * @throws RestException 401
1350
     * @throws RestException 404
1351
     * @throws RestException 405
1352
     */
1353
    public function useCreditNote($id, $discountid)
1354
    {
1355
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/class/discount.class.php';
1356
1357
        if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1358
            throw new RestException(403);
1359
        }
1360
        if (empty($id)) {
1361
            throw new RestException(400, 'Invoice ID is mandatory');
1362
        }
1363
        if (empty($discountid)) {
1364
            throw new RestException(400, 'Credit ID is mandatory');
1365
        }
1366
1367
        if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1368
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
1369
        }
1370
        $discount = new DiscountAbsolute($this->db);
1371
        $result = $discount->fetch($discountid);
1372
        if (!$result) {
1373
            throw new RestException(404, 'Credit not found');
1374
        }
1375
1376
        $result = $discount->link_to_invoice(0, $id);
1377
        if ($result < 0) {
1378
            throw new RestException(405, $discount->error);
1379
        }
1380
1381
        return $result;
1382
    }
1383
1384
    /**
1385
     * Get list of payments of a given invoice
1386
     *
1387
     * @param   int   $id             Id of invoice
1388
     * @return  array
1389
     *
1390
     * @url     GET {id}/payments
1391
     *
1392
     * @throws RestException 400
1393
     * @throws RestException 401
1394
     * @throws RestException 404
1395
     * @throws RestException 405
1396
     */
1397
    public function getPayments($id)
1398
    {
1399
        if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
1400
            throw new RestException(403);
1401
        }
1402
        if (empty($id)) {
1403
            throw new RestException(400, 'Invoice ID is mandatory');
1404
        }
1405
1406
        if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1407
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
1408
        }
1409
1410
        $result = $this->invoice->fetch($id);
1411
        if (!$result) {
1412
            throw new RestException(404, 'Invoice not found');
1413
        }
1414
1415
        $result = $this->invoice->getListOfPayments();
1416
        if ($result < 0) {
1417
            throw new RestException(405, $this->invoice->error);
1418
        }
1419
1420
        return $result;
1421
    }
1422
1423
1424
    /**
1425
     * Add payment line to a specific invoice with the remain to pay as amount.
1426
     *
1427
     * @param int     $id                               Id of invoice
1428
     * @param string  $datepaye           {@from body}  Payment date        {@type timestamp}
1429
     * @param int     $paymentid          {@from body}  Payment mode Id {@min 1}
1430
     * @param string  $closepaidinvoices  {@from body}  Close paid invoices {@choice yes,no}
1431
     * @param int     $accountid          {@from body}  Account Id {@min 1}
1432
     * @param string  $num_payment        {@from body}  Payment number (optional)
1433
     * @param string  $comment            {@from body}  Note private (optional)
1434
     * @param string  $chqemetteur        {@from body}  Payment issuer (mandatory if paymentcode = 'CHQ')
1435
     * @param string  $chqbank            {@from body}  Issuer bank name (optional)
1436
     *
1437
     * @url     POST {id}/payments
1438
     *
1439
     * @return int  Payment ID
1440
     *
1441
     * @throws RestException 400
1442
     * @throws RestException 401
1443
     * @throws RestException 404
1444
     */
1445
    public function addPayment($id, $datepaye, $paymentid, $closepaidinvoices, $accountid, $num_payment = '', $comment = '', $chqemetteur = '', $chqbank = '')
1446
    {
1447
1448
        if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1449
            throw new RestException(403);
1450
        }
1451
        if (empty($id)) {
1452
            throw new RestException(400, 'Invoice ID is mandatory');
1453
        }
1454
1455
        if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1456
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
1457
        }
1458
1459
        if (isModEnabled("bank")) {
1460
            if (empty($accountid)) {
1461
                throw new RestException(400, 'Account ID is mandatory');
1462
            }
1463
        }
1464
1465
        if (empty($paymentid)) {
1466
            throw new RestException(400, 'Payment ID or Payment Code is mandatory');
1467
        }
1468
1469
1470
        $result = $this->invoice->fetch($id);
1471
        if (!$result) {
1472
            throw new RestException(404, 'Invoice not found');
1473
        }
1474
1475
        // Calculate amount to pay
1476
        $totalpaid = $this->invoice->getSommePaiement();
1477
        $totalcreditnotes = $this->invoice->getSumCreditNotesUsed();
1478
        $totaldeposits = $this->invoice->getSumDepositsUsed();
1479
        $resteapayer = price2num($this->invoice->total_ttc - $totalpaid - $totalcreditnotes - $totaldeposits, 'MT');
1480
1481
        $this->db->begin();
1482
1483
        $amounts = array();
1484
        $multicurrency_amounts = array();
1485
1486
        // Clean parameters amount if payment is for a credit note
1487
        if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1488
            $resteapayer = price2num($resteapayer, 'MT');
1489
            $amounts[$id] = (float) price2num(-1 * (float) $resteapayer, 'MT');
1490
            // Multicurrency
1491
            $newvalue = price2num($this->invoice->multicurrency_total_ttc, 'MT');
1492
            $multicurrency_amounts[$id] = (float) price2num(-1 * (float) $newvalue, 'MT');
1493
        } else {
1494
            $resteapayer = price2num($resteapayer, 'MT');
1495
            $amounts[$id] = (float) $resteapayer;
1496
            // Multicurrency
1497
            $newvalue = price2num($this->invoice->multicurrency_total_ttc, 'MT');
1498
            $multicurrency_amounts[$id] = (float) $newvalue;
1499
        }
1500
1501
        // Creation of payment line
1502
        $paymentobj = new Paiement($this->db);
1503
        $paymentobj->datepaye     = dol_stringtotime($datepaye);
1504
        $paymentobj->amounts      = $amounts; // Array with all payments dispatching with invoice id
1505
        $paymentobj->multicurrency_amounts = $multicurrency_amounts; // Array with all payments dispatching
1506
        $paymentobj->paiementid = $paymentid;
1507
        $paymentobj->paiementcode = (string) dol_getIdFromCode($this->db, $paymentid, 'c_paiement', 'id', 'code', 1);
1508
        $paymentobj->num_payment = $num_payment;
1509
        $paymentobj->note_private = $comment;
1510
1511
        $payment_id = $paymentobj->create(DolibarrApiAccess::$user, ($closepaidinvoices == 'yes' ? 1 : 0)); // This include closing invoices
1512
        if ($payment_id < 0) {
1513
            $this->db->rollback();
1514
            throw new RestException(400, 'Payment error : ' . $paymentobj->error);
1515
        }
1516
1517
        if (isModEnabled("bank")) {
1518
            $label = '(CustomerInvoicePayment)';
1519
1520
            if ($paymentobj->paiementcode == 'CHQ' && empty($chqemetteur)) {
1521
                throw new RestException(400, 'Emetteur is mandatory when payment code is ' . $paymentobj->paiementcode);
1522
            }
1523
            if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1524
                $label = '(CustomerInvoicePaymentBack)'; // Refund of a credit note
1525
            }
1526
            $result = $paymentobj->addPaymentToBank(DolibarrApiAccess::$user, 'payment', $label, $accountid, $chqemetteur, $chqbank);
1527
            if ($result < 0) {
1528
                $this->db->rollback();
1529
                throw new RestException(400, 'Add payment to bank error : ' . $paymentobj->error);
1530
            }
1531
        }
1532
1533
        $this->db->commit();
1534
1535
        return $payment_id;
1536
    }
1537
1538
    /**
1539
     * Add a payment to pay partially or completely one or several invoices.
1540
     * Warning: Take care that all invoices are owned by the same customer.
1541
     * Example of value for parameter arrayofamounts: {"1": {"amount": "99.99", "multicurrency_amount": ""}, "2": {"amount": "", "multicurrency_amount": "10"}}
1542
     *
1543
     * @param array   $arrayofamounts     {@from body}  Array with id of invoices with amount to pay for each invoice
1544
     * @param string  $datepaye           {@from body}  Payment date        {@type timestamp}
1545
     * @param int     $paymentid           {@from body}  Payment mode Id {@min 1}
1546
     * @param string  $closepaidinvoices   {@from body}  Close paid invoices {@choice yes,no}
1547
     * @param int     $accountid           {@from body}  Account Id {@min 1}
1548
     * @param string  $num_payment         {@from body}  Payment number (optional)
1549
     * @param string  $comment             {@from body}  Note private (optional)
1550
     * @param string  $chqemetteur         {@from body}  Payment issuer (mandatory if paiementcode = 'CHQ')
1551
     * @param string  $chqbank             {@from body}  Issuer bank name (optional)
1552
     * @param string  $ref_ext             {@from body}  External reference (optional)
1553
     * @param bool    $accepthigherpayment {@from body}  Accept higher payments that it remains to be paid (optional)
1554
     *
1555
     * @url     POST /paymentsdistributed
1556
     *
1557
     * @return int  Payment ID
1558
     *
1559
     * @throws RestException 400
1560
     * @throws RestException 401
1561
     * @throws RestException 403
1562
     * @throws RestException 404
1563
     */
1564
    public function addPaymentDistributed($arrayofamounts, $datepaye, $paymentid, $closepaidinvoices, $accountid, $num_payment = '', $comment = '', $chqemetteur = '', $chqbank = '', $ref_ext = '', $accepthigherpayment = false)
1565
    {
1566
1567
        if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1568
            throw new RestException(403);
1569
        }
1570
        foreach ($arrayofamounts as $id => $amount) {
1571
            if (empty($id)) {
1572
                throw new RestException(400, 'Invoice ID is mandatory. Fill the invoice id and amount into arrayofamounts parameter. For example: {"1": "99.99", "2": "10"}');
1573
            }
1574
            if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1575
                throw new RestException(403, 'Access not allowed on invoice ID ' . $id . ' for login ' . DolibarrApiAccess::$user->login);
1576
            }
1577
        }
1578
1579
        if (isModEnabled("bank")) {
1580
            if (empty($accountid)) {
1581
                throw new RestException(400, 'Account ID is mandatory');
1582
            }
1583
        }
1584
        if (empty($paymentid)) {
1585
            throw new RestException(400, 'Payment ID or Payment Code is mandatory');
1586
        }
1587
1588
        $this->db->begin();
1589
1590
        $amounts = array();
1591
        $multicurrency_amounts = array();
1592
1593
        // Loop on each invoice to pay
1594
        foreach ($arrayofamounts as $id => $amountarray) {
1595
            $result = $this->invoice->fetch($id);
1596
            if (!$result) {
1597
                $this->db->rollback();
1598
                throw new RestException(404, 'Invoice ID ' . $id . ' not found');
1599
            }
1600
1601
            if (($amountarray["amount"] == "remain" || $amountarray["amount"] > 0) && ($amountarray["multicurrency_amount"] == "remain" || $amountarray["multicurrency_amount"] > 0)) {
1602
                $this->db->rollback();
1603
                throw new RestException(400, 'Payment in both currency ' . $id . ' ( amount: ' . $amountarray["amount"] . ', multicurrency_amount: ' . $amountarray["multicurrency_amount"] . ')');
1604
            }
1605
1606
            $is_multicurrency = 0;
1607
            $total_ttc = $this->invoice->total_ttc;
1608
1609
            if ($amountarray["multicurrency_amount"] > 0 || $amountarray["multicurrency_amount"] == "remain") {
1610
                $is_multicurrency = 1;
1611
                $total_ttc = $this->invoice->multicurrency_total_ttc;
1612
            }
1613
1614
            // Calculate amount to pay
1615
            $totalpaid = $this->invoice->getSommePaiement($is_multicurrency);
1616
            $totalcreditnotes = $this->invoice->getSumCreditNotesUsed($is_multicurrency);
1617
            $totaldeposits = $this->invoice->getSumDepositsUsed($is_multicurrency);
1618
            $remainstopay = $amount = price2num($total_ttc - $totalpaid - $totalcreditnotes - $totaldeposits, 'MT');
1619
1620
            if (!$is_multicurrency && $amountarray["amount"] != 'remain') {
1621
                $amount = price2num($amountarray["amount"], 'MT');
1622
            }
1623
1624
            if ($is_multicurrency && $amountarray["multicurrency_amount"] != 'remain') {
1625
                $amount = price2num($amountarray["multicurrency_amount"], 'MT');
1626
            }
1627
1628
            if ($amount > $remainstopay && !$accepthigherpayment) {
1629
                $this->db->rollback();
1630
                throw new RestException(400, 'Payment amount on invoice ID ' . $id . ' (' . $amount . ') is higher than remain to pay (' . $remainstopay . ')');
1631
            }
1632
1633
            if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1634
                $amount = price2num(-1 * (float) $amount, 'MT');
1635
            }
1636
1637
            if ($is_multicurrency) {
1638
                $amounts[$id] = null;
1639
                // Multicurrency
1640
                $multicurrency_amounts[$id] = (float) $amount;
1641
            } else {
1642
                $amounts[$id] = (float) $amount;
1643
                // Multicurrency
1644
                $multicurrency_amounts[$id] = null;
1645
            }
1646
        }
1647
1648
        // Creation of payment line
1649
        $paymentobj = new Paiement($this->db);
1650
        $paymentobj->datepaye     = $datepaye;
1651
        $paymentobj->amounts      = $amounts; // Array with all payments dispatching with invoice id
1652
        $paymentobj->multicurrency_amounts = $multicurrency_amounts; // Array with all payments dispatching
1653
        $paymentobj->paiementid   = $paymentid;
1654
        $paymentobj->paiementcode = (string) dol_getIdFromCode($this->db, $paymentid, 'c_paiement', 'id', 'code', 1);
1655
        $paymentobj->num_payment  = $num_payment;
1656
        $paymentobj->note_private = $comment;
1657
        $paymentobj->ref_ext      = $ref_ext;
1658
        $payment_id = $paymentobj->create(DolibarrApiAccess::$user, ($closepaidinvoices == 'yes' ? 1 : 0)); // This include closing invoices
1659
        if ($payment_id < 0) {
1660
            $this->db->rollback();
1661
            throw new RestException(400, 'Payment error : ' . $paymentobj->error);
1662
        }
1663
        if (isModEnabled("bank")) {
1664
            $label = '(CustomerInvoicePayment)';
1665
            if ($paymentobj->paiementcode == 'CHQ' && empty($chqemetteur)) {
1666
                throw new RestException(400, 'Emetteur is mandatory when payment code is ' . $paymentobj->paiementcode);
1667
            }
1668
            if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1669
                $label = '(CustomerInvoicePaymentBack)'; // Refund of a credit note
1670
            }
1671
            $result = $paymentobj->addPaymentToBank(DolibarrApiAccess::$user, 'payment', $label, $accountid, $chqemetteur, $chqbank);
1672
            if ($result < 0) {
1673
                $this->db->rollback();
1674
                throw new RestException(400, 'Add payment to bank error : ' . $paymentobj->error);
1675
            }
1676
        }
1677
1678
        $this->db->commit();
1679
1680
        return $payment_id;
1681
    }
1682
1683
    /**
1684
     * Update a payment
1685
     *
1686
     * @param int       $id             Id of payment
1687
     * @param string    $num_payment    Payment number
1688
     *
1689
     * @url     PUT payments/{id}
1690
     *
1691
     * @return array
1692
     *
1693
     * @throws RestException 400 Bad parameters
1694
     * @throws RestException 401 Not allowed
1695
     * @throws RestException 404 Not found
1696
     */
1697
    public function putPayment($id, $num_payment = '')
1698
    {
1699
1700
        if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1701
            throw new RestException(403);
1702
        }
1703
        if (empty($id)) {
1704
            throw new RestException(400, 'Payment ID is mandatory');
1705
        }
1706
1707
        $paymentobj = new Paiement($this->db);
1708
        $result = $paymentobj->fetch($id);
1709
1710
        if (!$result) {
1711
            throw new RestException(404, 'Payment not found');
1712
        }
1713
1714
        if (!empty($num_payment)) {
1715
            $result = $paymentobj->update_num($num_payment);
1716
            if ($result < 0) {
1717
                throw new RestException(500, 'Error when updating the payment num');
1718
            }
1719
        }
1720
1721
        return [
1722
            'success' => [
1723
                'code' => 200,
1724
                'message' => 'Payment updated'
1725
            ]
1726
        ];
1727
    }
1728
1729
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1730
    /**
1731
     * Clean sensible object datas
1732
     *
1733
     * @param   Object  $object     Object to clean
1734
     * @return  Object              Object with cleaned properties
1735
     */
1736
    protected function _cleanObjectDatas($object)
1737
    {
1738
		// phpcs:enable
1739
        $object = parent::_cleanObjectDatas($object);
1740
1741
        unset($object->note);
1742
        unset($object->address);
1743
        unset($object->barcode_type);
1744
        unset($object->barcode_type_code);
1745
        unset($object->barcode_type_label);
1746
        unset($object->barcode_type_coder);
1747
        unset($object->canvas);
1748
1749
        return $object;
1750
    }
1751
1752
    /**
1753
     * Validate fields before create or update object
1754
     *
1755
     * @param   array|null    $data       Datas to validate
1756
     * @return  array
1757
     *
1758
     * @throws RestException
1759
     */
1760
    private function _validate($data)
1761
    {
1762
        $invoice = array();
1763
        foreach (Invoices::$FIELDS as $field) {
1764
            if (!isset($data[$field])) {
1765
                throw new RestException(400, "$field field missing");
1766
            }
1767
            $invoice[$field] = $data[$field];
1768
        }
1769
        return $invoice;
1770
    }
1771
1772
1773
    /**
1774
     * Get properties of a template invoice object
1775
     *
1776
     * Return an array with invoice information
1777
     *
1778
     * @param   int     $id             ID of template invoice
1779
     * @param   int     $contact_list   0:Return array contains all properties, 1:Return array contains just id, -1: Do not return contacts/adddesses
1780
     * @return  Object                  Object with cleaned properties
1781
     *
1782
     * @url GET    templates/{id}
1783
     *
1784
     * @throws  RestException
1785
     */
1786
    public function getTemplateInvoice($id, $contact_list = 1)
1787
    {
1788
        return $this->_fetchTemplateInvoice($id, '', '', $contact_list);
1789
    }
1790
1791
    /**
1792
     * Get properties of an invoice object
1793
     *
1794
     * Return an array with invoice information
1795
     *
1796
     * @param   int         $id             ID of order
1797
     * @param   string      $ref            Ref of object
1798
     * @param   string      $ref_ext        External reference of object
1799
     * @param   int         $contact_list   0: Returned array of contacts/addresses contains all properties, 1: Return array contains just id, -1: Do not return contacts/adddesses
1800
     * @return  Object                      Object with cleaned properties
1801
     *
1802
     * @throws  RestException
1803
     */
1804
    private function _fetchTemplateInvoice($id, $ref = '', $ref_ext = '', $contact_list = 1)
1805
    {
1806
        if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
1807
            throw new RestException(403);
1808
        }
1809
1810
        $result = $this->template_invoice->fetch($id, $ref, $ref_ext);
1811
        if (!$result) {
1812
            throw new RestException(404, 'Template invoice not found');
1813
        }
1814
1815
        if (!DolibarrApi::_checkAccessToResource('facturerec', $this->template_invoice->id)) {
1816
            throw new RestException(403, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
1817
        }
1818
1819
        // Add external contacts ids
1820
        if ($contact_list > -1) {
1821
            $tmparray = $this->template_invoice->liste_contact(-1, 'external', $contact_list);
1822
            if (is_array($tmparray)) {
1823
                $this->template_invoice->contacts_ids = $tmparray;
1824
            }
1825
        }
1826
1827
        $this->template_invoice->fetchObjectLinked();
1828
        return $this->_cleanTemplateObjectDatas($this->template_invoice);
1829
    }
1830
1831
1832
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1833
    /**
1834
     * Clean sensible object datas
1835
     *
1836
     * @param   Object  $object     Object to clean
1837
     * @return  Object              Object with cleaned properties
1838
     */
1839
    protected function _cleanTemplateObjectDatas($object)
1840
    {
1841
		// phpcs:enable
1842
        $object = parent::_cleanObjectDatas($object);
1843
1844
        unset($object->note);
1845
        unset($object->address);
1846
        unset($object->barcode_type);
1847
        unset($object->barcode_type_code);
1848
        unset($object->barcode_type_label);
1849
        unset($object->barcode_type_coder);
1850
        unset($object->canvas);
1851
1852
        return $object;
1853
    }
1854
}
1855