Passed
Branch develop (2b65df)
by
unknown
40:00
created

Invoices::useDiscount()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 28
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 15
nc 7
nop 2
dl 0
loc 28
rs 8.8333
c 1
b 0
f 0
1
<?php
2
/* Copyright (C) 2015   Jean-François Ferry     <[email protected]>
3
/* Copyright (C) 2020   Thibault FOUCART     	<[email protected]>
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 3 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17
 */
18
19
use Luracast\Restler\RestException;
20
21
require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
22
23
/**
24
 * API class for invoices
25
 *
26
 * @access protected
27
 * @class  DolibarrApiAccess {@requires user,external}
28
 */
29
class Invoices extends DolibarrApi
30
{
31
    /**
32
     *
33
     * @var array   $FIELDS     Mandatory fields, checked when create and update object
34
     */
35
    static $FIELDS = array(
36
        'socid',
37
    );
38
39
    /**
40
     * @var Facture $invoice {@type Facture}
41
     */
42
    public $invoice;
43
44
    /**
45
     * Constructor
46
     */
47
    public function __construct()
48
    {
49
        global $db, $conf;
50
        $this->db = $db;
51
        $this->invoice = new Facture($this->db);
52
    }
53
54
    /**
55
     * Get properties of a invoice object
56
     *
57
     * Return an array with invoice informations
58
     *
59
     * @param 	int 	$id           ID of invoice
60
     * @param   int     $contact_list 0:Return array contains all properties, 1:Return array contains just id
61
     * @return 	array|mixed data without useless information
62
     *
63
     * @throws 	RestException
64
     */
65
	public function get($id, $contact_list = 1)
66
	{
67
        return $this->_fetch($id, '', '', '', $contact_list);
68
	}
69
70
    /**
71
     * Get properties of an invoice object by ref
72
     *
73
     * Return an array with invoice informations
74
     *
75
     * @param       string		$ref			Ref of object
76
     * @param       int         $contact_list  0: Returned array of contacts/addresses contains all properties, 1: Return array contains just id
77
     * @return 	array|mixed data without useless information
78
     *
79
     * @url GET    ref/{ref}
80
     *
81
     * @throws 	RestException
82
     */
83
    public function getByRef($ref, $contact_list = 1)
84
    {
85
        return $this->_fetch('', $ref, '', '', $contact_list);
86
    }
87
88
    /**
89
     * Get properties of an invoice object by ref_ext
90
     *
91
     * Return an array with invoice informations
92
     *
93
     * @param       string		$ref_ext			External reference of object
94
     * @param       int         $contact_list  0: Returned array of contacts/addresses contains all properties, 1: Return array contains just id
95
     * @return 	array|mixed data without useless information
96
     *
97
     * @url GET    ref_ext/{ref_ext}
98
     *
99
     * @throws 	RestException
100
     */
101
    public function getByRefExt($ref_ext, $contact_list = 1)
102
    {
103
        return $this->_fetch('', '', $ref_ext, '', $contact_list);
104
    }
105
106
    /**
107
     * Get properties of an invoice object
108
     *
109
     * Return an array with invoice informations
110
     *
111
     * @param       int         $id            ID of order
112
	 * @param		string		$ref			Ref of object
113
	 * @param		string		$ref_ext		External reference of object
114
	 * @param		string		$ref_int		Internal reference of other objec
115
     * @param       int         $contact_list  0: Returned array of contacts/addresses contains all properties, 1: Return array contains just id
116
     * @return 	array|mixed data without useless information
117
     *
118
     * @throws 	RestException
119
     */
120
    private function _fetch($id, $ref = '', $ref_ext = '', $ref_int = '', $contact_list = 1)
121
    {
122
		if (!DolibarrApiAccess::$user->rights->facture->lire) {
123
			throw new RestException(401);
124
		}
125
126
		$result = $this->invoice->fetch($id, $ref, $ref_ext, $ref_int);
127
		if (!$result) {
128
			throw new RestException(404, 'Invoice not found');
129
		}
130
131
		// Get payment details
132
		$this->invoice->totalpaid = $this->invoice->getSommePaiement();
0 ignored issues
show
Bug introduced by
The property totalpaid does not seem to exist on Facture.
Loading history...
133
		$this->invoice->totalcreditnotes = $this->invoice->getSumCreditNotesUsed();
0 ignored issues
show
Bug introduced by
The property totalcreditnotes does not seem to exist on Facture.
Loading history...
134
		$this->invoice->totaldeposits = $this->invoice->getSumDepositsUsed();
0 ignored issues
show
Bug introduced by
The property totaldeposits does not seem to exist on Facture.
Loading history...
135
		$this->invoice->remaintopay = price2num($this->invoice->total_ttc - $this->invoice->totalpaid - $this->invoice->totalcreditnotes - $this->invoice->totaldeposits, 'MT');
0 ignored issues
show
Bug introduced by
The property remaintopay does not seem to exist on Facture.
Loading history...
136
137
		if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
138
			throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
139
		}
140
141
		// Add external contacts ids
142
		$this->invoice->contacts_ids = $this->invoice->liste_contact(-1, 'external', $contact_list);
0 ignored issues
show
Bug introduced by
The property contacts_ids does not exist on Facture. Did you mean contact?
Loading history...
143
144
		$this->invoice->fetchObjectLinked();
145
		return $this->_cleanObjectDatas($this->invoice);
146
    }
147
148
    /**
149
     * List invoices
150
     *
151
     * Get a list of invoices
152
     *
153
     * @param string	$sortfield	      Sort field
154
     * @param string	$sortorder	      Sort order
155
     * @param int		$limit		      Limit for list
156
     * @param int		$page		      Page number
157
     * @param string   	$thirdparty_ids	  Thirdparty ids to filter orders of (example '1' or '1,2,3') {@pattern /^[0-9,]*$/i}
158
     * @param string	$status		      Filter by invoice status : draft | unpaid | paid | cancelled
159
     * @param string    $sqlfilters       Other criteria to filter answers separated by a comma. Syntax example "(t.ref:like:'SO-%') and (t.date_creation:<:'20160101')"
160
     * @return array                      Array of invoice objects
161
     *
162
	 * @throws RestException
163
     */
164
    public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $thirdparty_ids = '', $status = '', $sqlfilters = '')
165
    {
166
        global $db, $conf;
167
168
        $obj_ret = array();
169
170
        // case of external user, $thirdparty_ids param is ignored and replaced by user's socid
171
        $socids = DolibarrApiAccess::$user->socid ? DolibarrApiAccess::$user->socid : $thirdparty_ids;
172
173
        // If the internal user must only see his customers, force searching by him
174
        $search_sale = 0;
175
        if (!DolibarrApiAccess::$user->rights->societe->client->voir && !$socids) $search_sale = DolibarrApiAccess::$user->id;
176
177
        $sql = "SELECT t.rowid";
178
        if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socids) || $search_sale > 0) $sql .= ", sc.fk_soc, sc.fk_user"; // We need these fields in order to filter by sale (including the case where the user can only see his prospects)
179
        $sql .= " FROM ".MAIN_DB_PREFIX."facture as t";
180
181
        if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socids) || $search_sale > 0) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; // We need this table joined to the select in order to filter by sale
182
183
        $sql .= ' WHERE t.entity IN ('.getEntity('invoice').')';
184
        if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socids) || $search_sale > 0) $sql .= " AND t.fk_soc = sc.fk_soc";
185
        if ($socids) $sql .= " AND t.fk_soc IN (".$socids.")";
186
187
        if ($search_sale > 0) $sql .= " AND t.rowid = sc.fk_soc"; // Join for the needed table to filter by sale
188
189
		// Filter by status
190
        if ($status == 'draft')     $sql .= " AND t.fk_statut IN (0)";
191
        if ($status == 'unpaid')    $sql .= " AND t.fk_statut IN (1)";
192
        if ($status == 'paid')      $sql .= " AND t.fk_statut IN (2)";
193
        if ($status == 'cancelled') $sql .= " AND t.fk_statut IN (3)";
194
        // Insert sale filter
195
        if ($search_sale > 0)
196
        {
197
            $sql .= " AND sc.fk_user = ".$search_sale;
198
        }
199
        // Add sql filters
200
        if ($sqlfilters)
201
        {
202
            if (!DolibarrApi::_checkFilters($sqlfilters))
203
            {
204
                throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters);
205
            }
206
	        $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)';
207
            $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")";
208
        }
209
210
        $sql .= $db->order($sortfield, $sortorder);
211
        if ($limit)
212
        {
213
            if ($page < 0)
214
            {
215
                $page = 0;
216
            }
217
            $offset = $limit * $page;
218
219
            $sql .= $db->plimit($limit + 1, $offset);
220
        }
221
222
        $result = $db->query($sql);
223
        if ($result)
224
        {
225
            $i = 0;
226
            $num = $db->num_rows($result);
227
            $min = min($num, ($limit <= 0 ? $num : $limit));
228
            while ($i < $min)
229
            {
230
                $obj = $db->fetch_object($result);
231
                $invoice_static = new Facture($db);
232
                if ($invoice_static->fetch($obj->rowid))
233
                {
234
                	// Get payment details
235
                	$invoice_static->totalpaid = $invoice_static->getSommePaiement();
0 ignored issues
show
Bug introduced by
The property totalpaid does not seem to exist on Facture.
Loading history...
236
                	$invoice_static->totalcreditnotes = $invoice_static->getSumCreditNotesUsed();
0 ignored issues
show
Bug introduced by
The property totalcreditnotes does not seem to exist on Facture.
Loading history...
237
                	$invoice_static->totaldeposits = $invoice_static->getSumDepositsUsed();
0 ignored issues
show
Bug introduced by
The property totaldeposits does not seem to exist on Facture.
Loading history...
238
                	$invoice_static->remaintopay = price2num($invoice_static->total_ttc - $invoice_static->totalpaid - $invoice_static->totalcreditnotes - $invoice_static->totaldeposits, 'MT');
0 ignored issues
show
Bug introduced by
The property remaintopay does not seem to exist on Facture.
Loading history...
239
240
					// Add external contacts ids
241
					$invoice_static->contacts_ids = $invoice_static->liste_contact(-1, 'external', 1);
0 ignored issues
show
Bug introduced by
The property contacts_ids does not exist on Facture. Did you mean contact?
Loading history...
242
243
                	$obj_ret[] = $this->_cleanObjectDatas($invoice_static);
244
                }
245
                $i++;
246
            }
247
        }
248
        else {
249
            throw new RestException(503, 'Error when retrieve invoice list : '.$db->lasterror());
250
        }
251
        if (!count($obj_ret)) {
252
            throw new RestException(404, 'No invoice found');
253
        }
254
		return $obj_ret;
255
    }
256
257
    /**
258
     * Create invoice object
259
     *
260
     * @param array $request_data   Request datas
261
     * @return int                  ID of invoice
262
     */
263
    public function post($request_data = null)
264
    {
265
        if (!DolibarrApiAccess::$user->rights->facture->creer) {
266
			throw new RestException(401, "Insuffisant rights");
267
		}
268
        // Check mandatory fields
269
        $result = $this->_validate($request_data);
270
271
        foreach ($request_data as $field => $value) {
272
            $this->invoice->$field = $value;
273
        }
274
        if (!array_key_exists('date', $request_data)) {
275
            $this->invoice->date = dol_now();
276
        }
277
        /* We keep lines as an array
278
         if (isset($request_data["lines"])) {
279
            $lines = array();
280
            foreach ($request_data["lines"] as $line) {
281
                array_push($lines, (object) $line);
282
            }
283
            $this->invoice->lines = $lines;
284
        }*/
285
286
        if ($this->invoice->create(DolibarrApiAccess::$user, 0, (empty($request_data["date_lim_reglement"]) ? 0 : $request_data["date_lim_reglement"])) < 0) {
287
            throw new RestException(500, "Error creating invoice", array_merge(array($this->invoice->error), $this->invoice->errors));
288
        }
289
        return $this->invoice->id;
290
    }
291
292
     /**
293
     * Create an invoice using an existing order.
294
     *
295
     *
296
     * @param int   $orderid       Id of the order
297
     *
298
     * @url     POST /createfromorder/{orderid}
299
     *
300
     * @return int
301
     * @throws RestException 400
302
     * @throws RestException 401
303
     * @throws RestException 404
304
     * @throws RestException 405
305
     */
306
    public function createInvoiceFromOrder($orderid)
307
    {
308
309
        require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
310
311
        if (!DolibarrApiAccess::$user->rights->commande->lire) {
312
            throw new RestException(401);
313
        }
314
        if (!DolibarrApiAccess::$user->rights->facture->creer) {
315
            throw new RestException(401);
316
        }
317
        if (empty($orderid)) {
318
            throw new RestException(400, 'Order ID is mandatory');
319
        }
320
321
        $order = new Commande($this->db);
322
        $result = $order->fetch($orderid);
323
        if (!$result) {
324
            throw new RestException(404, 'Order not found');
325
        }
326
327
        $result = $this->invoice->createFromOrder($order, DolibarrApiAccess::$user);
328
        if ($result < 0) {
329
            throw new RestException(405, $this->invoice->error);
330
        }
331
        $this->invoice->fetchObjectLinked();
332
        return $this->_cleanObjectDatas($this->invoice);
333
    }
334
335
    /**
336
     * Get lines of an invoice
337
     *
338
     * @param int   $id             Id of invoice
339
     *
340
     * @url	GET {id}/lines
341
     *
342
     * @return int
343
     */
344
    public function getLines($id)
345
    {
346
    	if (!DolibarrApiAccess::$user->rights->facture->lire) {
347
    		throw new RestException(401);
348
    	}
349
350
    	$result = $this->invoice->fetch($id);
351
    	if (!$result) {
352
    		throw new RestException(404, 'Invoice not found');
353
    	}
354
355
    	if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
356
    		throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
357
    	}
358
    	$this->invoice->getLinesArray();
359
    	$result = array();
360
    	foreach ($this->invoice->lines as $line) {
361
    		array_push($result, $this->_cleanObjectDatas($line));
362
    	}
363
    	return $result;
364
    }
365
366
    /**
367
     * Update a line to a given invoice
368
     *
369
     * @param int   $id             Id of invoice to update
370
     * @param int   $lineid         Id of line to update
371
     * @param array $request_data   InvoiceLine data
372
     *
373
     * @url	PUT {id}/lines/{lineid}
374
     *
375
     * @return array
376
     *
377
     * @throws RestException 304
378
     * @throws RestException 401
379
     * @throws RestException 404 Invoice not found
380
     */
381
    public function putLine($id, $lineid, $request_data = null)
382
    {
383
    	if (!DolibarrApiAccess::$user->rights->facture->creer) {
384
    		throw new RestException(401);
385
    	}
386
387
    	$result = $this->invoice->fetch($id);
388
    	if (!$result) {
389
    		throw new RestException(404, 'Invoice not found');
390
    	}
391
392
    	if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
393
    		throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
394
    	}
395
    	$request_data = (object) $request_data;
396
        $updateRes = $this->invoice->updateline(
397
    		$lineid,
398
    		$request_data->desc,
399
    		$request_data->subprice,
400
    		$request_data->qty,
401
    		$request_data->remise_percent,
402
    		$request_data->date_start,
403
    		$request_data->date_end,
404
    		$request_data->tva_tx,
405
    		$request_data->localtax1_tx,
406
    		$request_data->localtax2_tx,
407
    		'HT',
408
    		$request_data->info_bits,
409
    		$request_data->product_type,
410
    		$request_data->fk_parent_line,
411
    		0,
412
    		$request_data->fk_fournprice,
413
    		$request_data->pa_ht,
414
    		$request_data->label,
415
    		$request_data->special_code,
416
    		$request_data->array_options,
417
    		$request_data->situation_percent,
418
    		$request_data->fk_unit,
419
    		$request_data->multicurrency_subprice
420
    	);
421
422
    	if ($updateRes > 0) {
423
    		$result = $this->get($id);
424
    		unset($result->line);
425
    		return $this->_cleanObjectDatas($result);
426
	    } else {
427
	    	throw new RestException(304, $this->invoice->error);
428
    	}
429
    }
430
431
    /**
432
	 * Add a contact type of given invoice
433
	 *
434
	 * @param int    $id             Id of invoice to update
435
	 * @param int    $contactid      Id of contact to add
436
	 * @param string $type           Type of the contact (BILLING, SHIPPING, CUSTOMER)
437
	 *
438
	 * @url	POST {id}/contact/{contactid}/{type}
439
	 *
440
	 * @return int
441
	 *
442
     * @throws RestException 401
443
     * @throws RestException 404
444
	 */
445
    public function postContact($id, $contactid, $type)
446
    {
447
        if (!DolibarrApiAccess::$user->rights->facture->creer) {
448
            throw new RestException(401);
449
        }
450
451
        $result = $this->invoice->fetch($id);
452
453
		if (!$result) {
454
			throw new RestException(404, 'Invoice not found');
455
		}
456
457
        if (!in_array($type, array('BILLING', 'SHIPPING', 'CUSTOMER'), true)) {
458
            throw new RestException(500, 'Availables types: BILLING, SHIPPING OR CUSTOMER');
459
        }
460
461
        if (!DolibarrApi::_checkAccessToResource('invoice', $this->invoice->id)) {
462
			throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
463
		}
464
465
        $result = $this->invoice->add_contact($contactid, $type, 'external');
466
467
        if (!$result) {
468
            throw new RestException(500, 'Error when added the contact');
469
        }
470
471
        return $this->_cleanObjectDatas($this->invoice);
472
    }
473
474
    /**
475
	 * Delete a contact type of given invoice
476
	 *
477
	 * @param int    $id             Id of invoice to update
478
	 * @param int    $rowid          Row key of the contact in the array contact_ids.
479
	 *
480
	 * @url	DELETE {id}/contact/{rowid}
481
	 *
482
	 * @return array
483
  	 *
484
     * @throws RestException 401
485
     * @throws RestException 404
486
     * @throws RestException 500
487
	 */
488
    public function deleteContact($id, $rowid)
489
    {
490
        if (!DolibarrApiAccess::$user->rights->facture->creer) {
491
            throw new RestException(401);
492
        }
493
494
        $result = $this->invoice->fetch($id);
495
496
		if (!$result) {
497
			throw new RestException(404, 'Invoice not found');
498
		}
499
500
        if (!DolibarrApi::_checkAccessToResource('invoice', $this->invoice->id)) {
501
			throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
502
		}
503
504
        $result = $this->invoice->delete_contact($rowid);
505
506
        if (!$result) {
507
            throw new RestException(500, 'Error when deleted the contact');
508
        }
509
510
        return $this->_cleanObjectDatas($this->invoice);
511
    }
512
513
    /**
514
     * Deletes a line of a given invoice
515
     *
516
     * @param int   $id             Id of invoice
517
     * @param int   $lineid 		Id of the line to delete
518
     *
519
     * @url     DELETE {id}/lines/{lineid}
520
     *
521
     * @return array
522
     *
523
     * @throws RestException 400
524
     * @throws RestException 401
525
     * @throws RestException 404
526
     * @throws RestException 405
527
     */
528
    public function deleteLine($id, $lineid)
529
    {
530
531
    	if (!DolibarrApiAccess::$user->rights->facture->creer) {
532
    		throw new RestException(401);
533
    	}
534
    	if (empty($lineid)) {
535
    		throw new RestException(400, 'Line ID is mandatory');
536
    	}
537
538
    	if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
539
    		throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
540
    	}
541
542
    	$result = $this->invoice->fetch($id);
543
    	if (!$result) {
544
    		throw new RestException(404, 'Invoice not found');
545
    	}
546
547
    	// TODO Check the lineid $lineid is a line of ojbect
548
549
    	$updateRes = $this->invoice->deleteline($lineid);
550
    	if ($updateRes > 0) {
551
    		return $this->get($id);
552
    	}
553
    	else
554
    	{
555
    		throw new RestException(405, $this->invoice->error);
556
    	}
557
    }
558
559
    /**
560
     * Update invoice
561
     *
562
     * @param int   $id             Id of invoice to update
563
     * @param array $request_data   Datas
564
     * @return int
565
     */
566
    public function put($id, $request_data = null)
567
    {
568
        if (!DolibarrApiAccess::$user->rights->facture->creer) {
569
			throw new RestException(401);
570
		}
571
572
        $result = $this->invoice->fetch($id);
573
        if (!$result) {
574
            throw new RestException(404, 'Invoice not found');
575
        }
576
577
		if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
578
			throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
579
		}
580
581
        foreach ($request_data as $field => $value) {
582
            if ($field == 'id') continue;
583
            $this->invoice->$field = $value;
584
        }
585
586
        // update bank account
587
        if (!empty($this->invoice->fk_account))
588
        {
589
            if ($this->invoice->setBankAccount($this->invoice->fk_account) == 0) {
590
                throw new RestException(400, $this->invoice->error);
591
            }
592
        }
593
594
        if($this->invoice->update(DolibarrApiAccess::$user))
595
            return $this->get($id);
596
597
        return false;
598
    }
599
600
    /**
601
     * Delete invoice
602
     *
603
     * @param int   $id Invoice ID
604
     * @return array
605
     */
606
    public function delete($id)
607
    {
608
        if (!DolibarrApiAccess::$user->rights->facture->supprimer) {
609
			throw new RestException(401);
610
		}
611
        $result = $this->invoice->fetch($id);
612
        if (!$result) {
613
            throw new RestException(404, 'Invoice not found');
614
        }
615
616
		if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
617
			throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
618
		}
619
620
        if ($this->invoice->delete($id) < 0)
621
        {
622
            throw new RestException(500);
623
        }
624
625
        return array(
626
            'success' => array(
627
                'code' => 200,
628
                'message' => 'Invoice deleted'
629
            )
630
        );
631
    }
632
633
    /**
634
     * Add a line to a given invoice
635
     *
636
     * Exemple of POST query :
637
     * {
638
     *     "desc": "Desc", "subprice": "1.00000000", "qty": "1", "tva_tx": "20.000", "localtax1_tx": "0.000", "localtax2_tx": "0.000",
639
     *     "fk_product": "1", "remise_percent": "0", "date_start": "", "date_end": "", "fk_code_ventilation": 0,  "info_bits": "0",
640
     *     "fk_remise_except": null,  "product_type": "1", "rang": "-1", "special_code": "0", "fk_parent_line": null, "fk_fournprice": null,
641
     *     "pa_ht": "0.00000000", "label": "", "array_options": [], "situation_percent": "100", "fk_prev_id": null, "fk_unit": null
642
     * }
643
     *
644
     * @param int   $id             Id of invoice
645
     * @param array $request_data   InvoiceLine data
646
     *
647
     * @url     POST {id}/lines
648
     *
649
     * @return int
650
     *
651
     * @throws RestException 304
652
     * @throws RestException 401
653
     * @throws RestException 404
654
     * @throws RestException 400
655
     */
656
    public function postLine($id, $request_data = null)
657
    {
658
        if (!DolibarrApiAccess::$user->rights->facture->creer) {
659
            throw new RestException(401);
660
        }
661
662
        $result = $this->invoice->fetch($id);
663
        if (!$result) {
664
            throw new RestException(404, 'Invoice not found');
665
        }
666
667
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
668
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
669
        }
670
671
        $request_data = (object) $request_data;
672
673
        // Reset fk_parent_line for no child products and special product
674
        if (($request_data->product_type != 9 && empty($request_data->fk_parent_line)) || $request_data->product_type == 9) {
675
            $request_data->fk_parent_line = 0;
676
        }
677
678
        // calculate pa_ht
679
        $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);
680
        $pa_ht = $marginInfos[0];
681
682
        $updateRes = $this->invoice->addline(
683
            $request_data->desc,
684
            $request_data->subprice,
685
            $request_data->qty,
686
            $request_data->tva_tx,
687
            $request_data->localtax1_tx,
688
            $request_data->localtax2_tx,
689
            $request_data->fk_product,
690
            $request_data->remise_percent,
691
            $request_data->date_start,
692
            $request_data->date_end,
693
            $request_data->fk_code_ventilation,
694
            $request_data->info_bits,
695
            $request_data->fk_remise_except,
696
            'HT',
697
            0,
698
            $request_data->product_type,
699
            $request_data->rang,
700
            $request_data->special_code,
701
            $request_data->origin,
702
            $request_data->origin_id,
703
            $request_data->fk_parent_line,
704
            empty($request_data->fk_fournprice) ?null:$request_data->fk_fournprice,
705
            $pa_ht,
706
            $request_data->label,
707
            $request_data->array_options,
708
            $request_data->situation_percent,
709
            $request_data->fk_prev_id,
710
            $request_data->fk_unit
711
        );
712
713
        if ($updateRes < 0) {
714
            throw new RestException(400, 'Unable to insert the new line. Check your inputs. '.$this->invoice->error);
715
        }
716
717
        return $updateRes;
718
    }
719
720
    /**
721
     * Adds a contact to an invoice
722
     *
723
     * @param   int 	$id             	Order ID
724
     * @param   int 	$fk_socpeople       	Id of thirdparty contact (if source = 'external') or id of user (if souce = 'internal') to link
725
     * @param   string 	$type_contact           Type of contact (code). Must a code found into table llx_c_type_contact. For example: BILLING
726
     * @param   string  $source             	external=Contact extern (llx_socpeople), internal=Contact intern (llx_user)
727
     * @param   int     $notrigger              Disable all triggers
728
     *
729
     * @url POST    {id}/contacts
730
     *
731
     * @return  array
732
     *
733
     * @throws RestException 304
734
     * @throws RestException 401
735
     * @throws RestException 404
736
     * @throws RestException 500
737
     *
738
     */
739
    public function addContact($id, $fk_socpeople, $type_contact, $source, $notrigger = 0)
740
    {
741
        if (!DolibarrApiAccess::$user->rights->facture->creer) {
742
            throw new RestException(401);
743
        }
744
        $result = $this->invoice->fetch($id);
745
        if (!$result) {
746
            throw new RestException(404, 'Invoice not found');
747
        }
748
749
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
750
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
751
        }
752
753
        $result = $this->invoice->add_contact($fk_socpeople, $type_contact, $source, $notrigger);
754
        if ($result < 0) {
755
            throw new RestException(500, 'Error : '.$this->invoice->error);
756
        }
757
758
        $result = $this->invoice->fetch($id);
759
        if (!$result) {
760
            throw new RestException(404, 'Invoice not found');
761
        }
762
763
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
764
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
765
        }
766
767
        return $this->_cleanObjectDatas($this->invoice);
768
    }
769
770
771
772
    /**
773
     * Sets an invoice as draft
774
     *
775
     * @param   int $id             Order ID
776
     * @param   int $idwarehouse    Warehouse ID
777
     *
778
     * @url POST    {id}/settodraft
779
     *
780
     * @return  array
781
     *
782
     * @throws RestException 304
783
     * @throws RestException 401
784
     * @throws RestException 404
785
     * @throws RestException 500
786
     *
787
     */
788
    public function settodraft($id, $idwarehouse = -1)
789
    {
790
        if (!DolibarrApiAccess::$user->rights->facture->creer) {
791
            throw new RestException(401);
792
        }
793
        $result = $this->invoice->fetch($id);
794
        if (!$result) {
795
            throw new RestException(404, 'Invoice not found');
796
        }
797
798
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
799
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
800
        }
801
802
        $result = $this->invoice->setDraft(DolibarrApiAccess::$user, $idwarehouse);
803
        if ($result == 0) {
804
            throw new RestException(304, 'Nothing done.');
805
        }
806
        if ($result < 0) {
807
            throw new RestException(500, 'Error : '.$this->invoice->error);
808
        }
809
810
        $result = $this->invoice->fetch($id);
811
        if (!$result) {
812
            throw new RestException(404, 'Invoice not found');
813
        }
814
815
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
816
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
817
        }
818
819
        return $this->_cleanObjectDatas($this->invoice);
820
    }
821
822
823
    /**
824
     * Validate an invoice
825
     *
826
	 * If you get a bad value for param notrigger check that ou provide this in body
827
     * {
828
     *   "idwarehouse": 0,
829
     *   "notrigger": 0
830
     * }
831
     *
832
     * @param   int $id             Invoice ID
833
     * @param   int $idwarehouse    Warehouse ID
834
     * @param   int $notrigger      1=Does not execute triggers, 0= execute triggers
835
     *
836
     * @url POST    {id}/validate
837
     *
838
     * @return  array
839
     */
840
    public function validate($id, $idwarehouse = 0, $notrigger = 0)
841
    {
842
    	if (!DolibarrApiAccess::$user->rights->facture->creer) {
843
    		throw new RestException(401);
844
    	}
845
    	$result = $this->invoice->fetch($id);
846
    	if (!$result) {
847
    		throw new RestException(404, 'Invoice not found');
848
    	}
849
850
    	if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
851
    		throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
852
    	}
853
854
    	$result = $this->invoice->validate(DolibarrApiAccess::$user, '', $idwarehouse, $notrigger);
855
    	if ($result == 0) {
856
    		throw new RestException(304, 'Error nothing done. May be object is already validated');
857
    	}
858
    	if ($result < 0) {
859
    		throw new RestException(500, 'Error when validating Invoice: '.$this->invoice->error);
860
    	}
861
862
        $result = $this->invoice->fetch($id);
863
        if (!$result) {
864
            throw new RestException(404, 'Invoice not found');
865
        }
866
867
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
868
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
869
        }
870
871
        return $this->_cleanObjectDatas($this->invoice);
872
    }
873
874
    /**
875
     * Sets an invoice as paid
876
     *
877
     * @param   int 	$id            Order ID
878
     * @param   string 	$close_code    Code renseigne si on classe a payee completement alors que paiement incomplet (cas escompte par exemple)
879
     * @param   string 	$close_note    Commentaire renseigne si on classe a payee alors que paiement incomplet (cas escompte par exemple)
880
     *
881
     * @url POST    {id}/settopaid
882
     *
883
     * @return  array 	An invoice object
884
     *
885
     * @throws RestException 304
886
     * @throws RestException 401
887
     * @throws RestException 404
888
     * @throws RestException 500
889
     */
890
    public function settopaid($id, $close_code = '', $close_note = '')
891
    {
892
        if (!DolibarrApiAccess::$user->rights->facture->creer) {
893
            throw new RestException(401);
894
        }
895
        $result = $this->invoice->fetch($id);
896
        if (!$result) {
897
            throw new RestException(404, 'Invoice not found');
898
        }
899
900
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
901
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
902
        }
903
904
        $result = $this->invoice->set_paid(DolibarrApiAccess::$user, $close_code, $close_note);
905
        if ($result == 0) {
906
            throw new RestException(304, 'Error nothing done. May be object is already validated');
907
        }
908
        if ($result < 0) {
909
            throw new RestException(500, 'Error : '.$this->invoice->error);
910
        }
911
912
913
        $result = $this->invoice->fetch($id);
914
        if (!$result) {
915
            throw new RestException(404, 'Invoice not found');
916
        }
917
918
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
919
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
920
        }
921
922
        return $this->_cleanObjectDatas($this->invoice);
923
    }
924
925
926
    /**
927
     * Sets an invoice as unpaid
928
     *
929
     * @param   int     $id            Order ID
930
     *
931
     * @url POST    {id}/settounpaid
932
     *
933
     * @return  array   An invoice object
934
     *
935
     * @throws RestException 304
936
     * @throws RestException 401
937
     * @throws RestException 404
938
     * @throws RestException 500
939
     */
940
    public function settounpaid($id)
941
    {
942
        if (!DolibarrApiAccess::$user->rights->facture->creer) {
943
            throw new RestException(401);
944
        }
945
        $result = $this->invoice->fetch($id);
946
        if (!$result) {
947
            throw new RestException(404, 'Invoice not found');
948
        }
949
950
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
951
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
952
        }
953
954
        $result = $this->invoice->set_unpaid(DolibarrApiAccess::$user);
955
        if ($result == 0) {
956
            throw new RestException(304, 'Nothing done');
957
        }
958
        if ($result < 0) {
959
            throw new RestException(500, 'Error : '.$this->invoice->error);
960
        }
961
962
963
        $result = $this->invoice->fetch($id);
964
        if (! $result) {
965
            throw new RestException(404, 'Invoice not found');
966
        }
967
968
        if (! DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
969
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
970
        }
971
972
        return $this->_cleanObjectDatas($this->invoice);
973
    }
974
975
    /**
976
     * Create a discount (credit available) for a credit note or a deposit.
977
     *
978
     * @param   int 	$id            Invoice ID
979
     * @url POST    {id}/markAsCreditAvailable
980
     *
981
     * @return  array 	An invoice object
982
     *
983
     * @throws RestException 304
984
     * @throws RestException 401
985
     * @throws RestException 404
986
     * @throws RestException 500
987
     */
988
    public function markAsCreditAvailable($id)
989
    {
990
        if( ! DolibarrApiAccess::$user->rights->facture->creer) {
991
            throw new RestException(401);
992
        }
993
994
        $result = $this->invoice->fetch($id);
995
        if (!$result) {
996
            throw new RestException(404, 'Invoice not found');
997
        }
998
999
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1000
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1001
        }
1002
1003
        if ($this->invoice->paye) {
1004
            throw new RestException(500, 'Alreay payed');
1005
        }
1006
1007
        $this->invoice->fetch($id);
1008
        $this->invoice->fetch_thirdparty();
1009
1010
        // Check if there is already a discount (protection to avoid duplicate creation when resubmit post)
1011
        $discountcheck = new DiscountAbsolute($this->db);
1012
        $result = $discountcheck->fetch(0, $this->invoice->id);
1013
1014
        $canconvert = 0;
1015
        if ($this->invoice->type == Facture::TYPE_DEPOSIT && empty($discountcheck->id)) $canconvert = 1; // we can convert deposit into discount if deposit is payed (completely, partially or not at all) and not already converted (see real condition into condition used to show button converttoreduc)
1016
        if (($this->invoice->type == Facture::TYPE_CREDIT_NOTE || $this->invoice->type == Facture::TYPE_STANDARD) && $this->invoice->paye == 0 && empty($discountcheck->id)) $canconvert = 1; // we can convert credit note into discount if credit note is not payed back and not already converted and amount of payment is 0 (see real condition into condition used to show button converttoreduc)
1017
        if ($canconvert)
1018
        {
1019
            $this->db->begin();
1020
1021
            $amount_ht = $amount_tva = $amount_ttc = array();
1022
            $multicurrency_amount_ht = $multicurrency_amount_tva = $multicurrency_amount_ttc = array();
1023
1024
            // Loop on each vat rate
1025
            $i = 0;
1026
            foreach ($this->invoice->lines as $line)
1027
            {
1028
                if ($line->product_type < 9 && $line->total_ht != 0) // Remove lines with product_type greater than or equal to 9
1029
                { 	// no need to create discount if amount is null
1030
                    $amount_ht[$line->tva_tx] += $line->total_ht;
1031
                    $amount_tva[$line->tva_tx] += $line->total_tva;
1032
                    $amount_ttc[$line->tva_tx] += $line->total_ttc;
1033
                    $multicurrency_amount_ht[$line->tva_tx] += $line->multicurrency_total_ht;
1034
                    $multicurrency_amount_tva[$line->tva_tx] += $line->multicurrency_total_tva;
1035
                    $multicurrency_amount_ttc[$line->tva_tx] += $line->multicurrency_total_ttc;
1036
                    $i++;
1037
                }
1038
            }
1039
1040
            // Insert one discount by VAT rate category
1041
            $discount = new DiscountAbsolute($this->db);
1042
            if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1043
                $discount->description = '(CREDIT_NOTE)';
1044
            }
1045
            elseif ($this->invoice->type == Facture::TYPE_DEPOSIT) {
1046
                $discount->description = '(DEPOSIT)';
1047
            }
1048
            elseif ($this->invoice->type == Facture::TYPE_STANDARD || $this->invoice->type == Facture::TYPE_REPLACEMENT || $this->invoice->type == Facture::TYPE_SITUATION) {
1049
                $discount->description = '(EXCESS RECEIVED)';
1050
            }
1051
            else {
1052
                throw new RestException(500, 'Cant convert to reduc an Invoice of this type');
1053
            }
1054
1055
            $discount->fk_soc = $this->invoice->socid;
1056
            $discount->fk_facture_source = $this->invoice->id;
1057
1058
            $error = 0;
1059
1060
            if ($this->invoice->type == Facture::TYPE_STANDARD || $this->invoice->type == Facture::TYPE_REPLACEMENT || $this->invoice->type == Facture::TYPE_SITUATION)
1061
            {
1062
                // If we're on a standard invoice, we have to get excess received to create a discount in TTC without VAT
1063
1064
                // Total payments
1065
                $sql = 'SELECT SUM(pf.amount) as total_paiements';
1066
                $sql .= ' FROM '.MAIN_DB_PREFIX.'paiement_facture as pf, '.MAIN_DB_PREFIX.'paiement as p';
1067
                $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as c ON p.fk_paiement = c.id';
1068
                $sql .= ' WHERE pf.fk_facture = '.$this->invoice->id;
1069
                $sql .= ' AND pf.fk_paiement = p.rowid';
1070
                $sql .= ' AND p.entity IN ('.getEntity('invoice').')';
1071
                $resql = $this->db->query($sql);
1072
                if (!$resql) dol_print_error($this->db);
1073
1074
                $res = $this->db->fetch_object($resql);
1075
                $total_paiements = $res->total_paiements;
1076
1077
                // Total credit note and deposit
1078
                $total_creditnote_and_deposit = 0;
1079
                $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
1080
                $sql .= " re.description, re.fk_facture_source";
1081
                $sql .= " FROM ".MAIN_DB_PREFIX."societe_remise_except as re";
1082
                $sql .= " WHERE fk_facture = ".$this->invoice->id;
1083
                $resql = $this->db->query($sql);
1084
                if (!empty($resql)) {
1085
                    while ($obj = $this->db->fetch_object($resql)) $total_creditnote_and_deposit += $obj->amount_ttc;
1086
                } else dol_print_error($this->db);
1087
1088
                $discount->amount_ht = $discount->amount_ttc = $total_paiements + $total_creditnote_and_deposit - $this->invoice->total_ttc;
1089
                $discount->amount_tva = 0;
1090
                $discount->tva_tx = 0;
1091
1092
                $result = $discount->create(DolibarrApiAccess::$user);
1093
                if ($result < 0)
1094
                {
1095
                    $error++;
1096
                }
1097
            }
1098
            if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE || $this->invoice->type == Facture::TYPE_DEPOSIT)
1099
            {
1100
                foreach ($amount_ht as $tva_tx => $xxx)
1101
                {
1102
                    $discount->amount_ht = abs($amount_ht[$tva_tx]);
1103
                    $discount->amount_tva = abs($amount_tva[$tva_tx]);
1104
                    $discount->amount_ttc = abs($amount_ttc[$tva_tx]);
1105
                    $discount->multicurrency_amount_ht = abs($multicurrency_amount_ht[$tva_tx]);
1106
                    $discount->multicurrency_amount_tva = abs($multicurrency_amount_tva[$tva_tx]);
1107
                    $discount->multicurrency_amount_ttc = abs($multicurrency_amount_ttc[$tva_tx]);
1108
                    $discount->tva_tx = abs($tva_tx);
1109
1110
                    $result = $discount->create(DolibarrApiAccess::$user);
1111
                    if ($result < 0)
1112
                    {
1113
                        $error++;
1114
                        break;
1115
                    }
1116
                }
1117
            }
1118
1119
            if (empty($error))
1120
            {
1121
                if ($this->invoice->type != Facture::TYPE_DEPOSIT) {
1122
                    // Classe facture
1123
                    $result = $this->invoice->set_paid(DolibarrApiAccess::$user);
1124
                    if ($result >= 0)
1125
                    {
1126
                        $this->db->commit();
1127
                    }
1128
                    else
1129
                    {
1130
                        $this->db->rollback();
1131
                        throw new RestException(500, 'Could not set paid');
1132
                    }
1133
                } else {
1134
                    $this->db->commit();
1135
                }
1136
            }
1137
            else
1138
            {
1139
                $this->db->rollback();
1140
                throw new RestException(500, 'Discount creation error');
1141
            }
1142
        }
1143
1144
        return $this->_cleanObjectDatas($this->invoice);
1145
    }
1146
1147
     /**
1148
     * Add a discount line into an invoice (as an invoice line) using an existing absolute discount
1149
     *
1150
     * Note that this consume the discount.
1151
     *
1152
     * @param int   $id             Id of invoice
1153
     * @param int   $discountid     Id of discount
1154
     *
1155
     * @url     POST {id}/usediscount/{discountid}
1156
     *
1157
     * @return int
1158
	 *
1159
     * @throws RestException 400
1160
     * @throws RestException 401
1161
     * @throws RestException 404
1162
     * @throws RestException 405
1163
     */
1164
    public function useDiscount($id, $discountid)
1165
    {
1166
1167
        if (!DolibarrApiAccess::$user->rights->facture->creer) {
1168
            throw new RestException(401);
1169
        }
1170
        if (empty($id)) {
1171
            throw new RestException(400, 'Invoice ID is mandatory');
1172
        }
1173
        if (empty($discountid)) {
1174
            throw new RestException(400, 'Discount ID is mandatory');
1175
        }
1176
1177
        if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1178
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1179
        }
1180
1181
        $result = $this->invoice->fetch($id);
1182
        if (!$result) {
1183
            throw new RestException(404, 'Invoice not found');
1184
        }
1185
1186
        $result = $this->invoice->insert_discount($discountid);
1187
        if ($result < 0) {
1188
            throw new RestException(405, $this->invoice->error);
1189
        }
1190
1191
        return $result;
1192
    }
1193
1194
     /**
1195
     * Add an available credit note discount to payments of an existing invoice.
1196
     *
1197
     *  Note that this consume the credit note.
1198
     *
1199
     * @param int   $id            Id of invoice
1200
     * @param int   $discountid    Id of a discount coming from a credit note
1201
     *
1202
     * @url     POST {id}/usecreditnote/{discountid}
1203
     *
1204
     * @return int
1205
     *
1206
     * @throws RestException 400
1207
     * @throws RestException 401
1208
     * @throws RestException 404
1209
     * @throws RestException 405
1210
     */
1211
    public function useCreditNote($id, $discountid)
1212
    {
1213
1214
        require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1215
1216
        if (!DolibarrApiAccess::$user->rights->facture->creer) {
1217
            throw new RestException(401);
1218
        }
1219
        if (empty($id)) {
1220
            throw new RestException(400, 'Invoice ID is mandatory');
1221
        }
1222
        if (empty($discountid)) {
1223
            throw new RestException(400, 'Credit ID is mandatory');
1224
        }
1225
1226
        if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1227
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1228
        }
1229
        $discount = new DiscountAbsolute($this->db);
1230
        $result = $discount->fetch($discountid);
1231
        if (!$result) {
1232
            throw new RestException(404, 'Credit not found');
1233
        }
1234
1235
        $result = $discount->link_to_invoice(0, $id);
1236
        if ($result < 0) {
1237
            throw new RestException(405, $discount->error);
1238
        }
1239
1240
        return $result;
1241
    }
1242
1243
    /**
1244
     * Get list of payments of a given invoice
1245
     *
1246
     * @param int   $id             Id of invoice
1247
     *
1248
     * @url     GET {id}/payments
1249
     *
1250
     * @return array
1251
	 *
1252
     * @throws RestException 400
1253
     * @throws RestException 401
1254
     * @throws RestException 404
1255
     * @throws RestException 405
1256
     */
1257
    public function getPayments($id)
1258
    {
1259
1260
        if (!DolibarrApiAccess::$user->rights->facture->lire) {
1261
            throw new RestException(401);
1262
        }
1263
        if (empty($id)) {
1264
            throw new RestException(400, 'Invoice ID is mandatory');
1265
        }
1266
1267
        if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1268
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1269
        }
1270
1271
        $result = $this->invoice->fetch($id);
1272
        if (!$result) {
1273
            throw new RestException(404, 'Invoice not found');
1274
        }
1275
1276
        $result = $this->invoice->getListOfPayments();
1277
        if ($result < 0) {
1278
            throw new RestException(405, $this->invoice->error);
1279
        }
1280
1281
        return $result;
1282
    }
1283
1284
1285
    /**
1286
     * Add payment line to a specific invoice with the remain to pay as amount.
1287
     *
1288
     * @param int     $id                               Id of invoice
1289
     * @param string  $datepaye           {@from body}  Payment date        {@type timestamp}
1290
     * @param int     $paiementid         {@from body}  Payment mode Id {@min 1}
1291
     * @param string  $closepaidinvoices  {@from body}  Close paid invoices {@choice yes,no}
1292
     * @param int     $accountid          {@from body}  Account Id {@min 1}
1293
     * @param string  $num_paiement       {@from body}  Payment number (optional)
1294
     * @param string  $comment            {@from body}  Note (optional)
1295
     * @param string  $chqemetteur        {@from body}  Payment issuer (mandatory if paiementcode = 'CHQ')
1296
     * @param string  $chqbank            {@from body}  Issuer bank name (optional)
1297
     *
1298
     * @url     POST {id}/payments
1299
     *
1300
     * @return int  Payment ID
1301
	 *
1302
     * @throws RestException 400
1303
     * @throws RestException 401
1304
     * @throws RestException 404
1305
     */
1306
    public function addPayment($id, $datepaye, $paiementid, $closepaidinvoices, $accountid, $num_paiement = '', $comment = '', $chqemetteur = '', $chqbank = '')
1307
    {
1308
        global $conf;
1309
1310
    	require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
1311
1312
    	if (!DolibarrApiAccess::$user->rights->facture->creer) {
1313
    		throw new RestException(403);
1314
    	}
1315
    	if (empty($id)) {
1316
    		throw new RestException(400, 'Invoice ID is mandatory');
1317
    	}
1318
1319
    	if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1320
    		throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1321
    	}
1322
1323
    	if (!empty($conf->banque->enabled)) {
1324
    		if (empty($accountid)) {
1325
    			throw new RestException(400, 'Account ID is mandatory');
1326
    		}
1327
    	}
1328
1329
    	if (empty($paiementid)) {
1330
    		throw new RestException(400, 'Paiement ID or Paiement Code is mandatory');
1331
    	}
1332
1333
1334
    	$result = $this->invoice->fetch($id);
1335
    	if (!$result) {
1336
    		throw new RestException(404, 'Invoice not found');
1337
    	}
1338
1339
    	// Calculate amount to pay
1340
    	$totalpaye = $this->invoice->getSommePaiement();
1341
    	$totalcreditnotes = $this->invoice->getSumCreditNotesUsed();
1342
    	$totaldeposits = $this->invoice->getSumDepositsUsed();
1343
    	$resteapayer = price2num($this->invoice->total_ttc - $totalpaye - $totalcreditnotes - $totaldeposits, 'MT');
1344
1345
    	$this->db->begin();
1346
1347
    	$amounts = array();
1348
    	$multicurrency_amounts = array();
1349
1350
    	// Clean parameters amount if payment is for a credit note
1351
    	if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1352
    		$resteapayer = price2num($resteapayer, 'MT');
1353
    		$amounts[$id] = -$resteapayer;
1354
    		// Multicurrency
1355
    		$newvalue = price2num($this->invoice->multicurrency_total_ttc, 'MT');
1356
    		$multicurrency_amounts[$id] = -$newvalue;
1357
    	} else {
1358
    		$resteapayer = price2num($resteapayer, 'MT');
1359
    		$amounts[$id] = $resteapayer;
1360
    		// Multicurrency
1361
    		$newvalue = price2num($this->invoice->multicurrency_total_ttc, 'MT');
1362
    		$multicurrency_amounts[$id] = $newvalue;
1363
    	}
1364
1365
1366
    	// Creation of payment line
1367
    	$paiement = new Paiement($this->db);
1368
    	$paiement->datepaye     = $datepaye;
1369
    	$paiement->amounts      = $amounts; // Array with all payments dispatching with invoice id
1370
    	$paiement->multicurrency_amounts = $multicurrency_amounts; // Array with all payments dispatching
1371
    	$paiement->paiementid = $paiementid;
1372
    	$paiement->paiementcode = dol_getIdFromCode($this->db, $paiementid, 'c_paiement', 'id', 'code', 1);
1373
    	$paiement->num_paiement = $num_paiement;
1 ignored issue
show
Deprecated Code introduced by
The property Paiement::$num_paiement has been deprecated. ( Ignorable by Annotation )

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

1373
    	/** @scrutinizer ignore-deprecated */ $paiement->num_paiement = $num_paiement;
Loading history...
1374
    	$paiement->note = $comment;
1375
1376
    	$paiement_id = $paiement->create(DolibarrApiAccess::$user, ($closepaidinvoices == 'yes' ? 1 : 0)); // This include closing invoices
1377
    	if ($paiement_id < 0)
1378
    	{
1379
    		$this->db->rollback();
1380
    		throw new RestException(400, 'Payment error : '.$paiement->error);
1381
    	}
1382
1383
    	if (!empty($conf->banque->enabled)) {
1384
    		$label = '(CustomerInvoicePayment)';
1385
1386
    		if ($paiement->paiementcode == 'CHQ' && empty($chqemetteur)) {
1387
    			throw new RestException(400, 'Emetteur is mandatory when payment code is '.$paiement->paiementcode);
1388
    		}
1389
    		if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) $label = '(CustomerInvoicePaymentBack)'; // Refund of a credit note
1390
    		$result = $paiement->addPaymentToBank(DolibarrApiAccess::$user, 'payment', $label, $accountid, $chqemetteur, $chqbank);
1391
    		if ($result < 0)
1392
    		{
1393
    			$this->db->rollback();
1394
    			throw new RestException(400, 'Add payment to bank error : '.$paiement->error);
1395
    		}
1396
    	}
1397
1398
    	$this->db->commit();
1399
1400
    	return $paiement_id;
1401
    }
1402
1403
    /**
1404
     * Add a payment to pay partially or completely one or several invoices.
1405
     * Warning: Take care that all invoices are owned by the same customer.
1406
     * Example of value for parameter arrayofamounts: {"1": "99.99", "2": "10"}
1407
     *
1408
     * @param array   $arrayofamounts     {@from body}  Array with id of invoices with amount to pay for each invoice
1409
     * @param string  $datepaye           {@from body}  Payment date        {@type timestamp}
1410
     * @param int     $paiementid         {@from body}  Payment mode Id {@min 1}
1411
     * @param string  $closepaidinvoices  {@from body}  Close paid invoices {@choice yes,no}
1412
     * @param int     $accountid          {@from body}  Account Id {@min 1}
1413
     * @param string  $num_paiement       {@from body}  Payment number (optional)
1414
     * @param string  $comment            {@from body}  Note (optional)
1415
     * @param string  $chqemetteur        {@from body}  Payment issuer (mandatory if paiementcode = 'CHQ')
1416
     * @param string  $chqbank            {@from body}  Issuer bank name (optional)
1417
     *
1418
     * @url     POST /paymentsdistributed
1419
     *
1420
     * @return int  Payment ID
1421
	 *
1422
     * @throws RestException 400
1423
     * @throws RestException 401
1424
     * @throws RestException 403
1425
     * @throws RestException 404
1426
     */
1427
    public function addPaymentDistributed($arrayofamounts, $datepaye, $paiementid, $closepaidinvoices, $accountid, $num_paiement = '', $comment = '', $chqemetteur = '', $chqbank = '')
1428
    {
1429
        global $conf;
1430
1431
        require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
1432
1433
        if (!DolibarrApiAccess::$user->rights->facture->creer) {
1434
            throw new RestException(403);
1435
        }
1436
        foreach ($arrayofamounts as $id => $amount) {
1437
        	if (empty($id)) {
1438
        		throw new RestException(400, 'Invoice ID is mandatory. Fill the invoice id and amount into arrayofamounts parameter. For example: {"1": "99.99", "2": "10"}');
1439
        	}
1440
        	if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1441
        		throw new RestException(403, 'Access not allowed on invoice ID '.$id.' for login '.DolibarrApiAccess::$user->login);
1442
        	}
1443
        }
1444
1445
        if (!empty($conf->banque->enabled)) {
1446
        	if (empty($accountid)) {
1447
        		throw new RestException(400, 'Account ID is mandatory');
1448
        	}
1449
        }
1450
        if (empty($paiementid)) {
1451
        	throw new RestException(400, 'Paiement ID or Paiement Code is mandatory');
1452
        }
1453
1454
        $this->db->begin();
1455
1456
        $amounts = array();
1457
        $multicurrency_amounts = array();
1458
1459
        // Loop on each invoice to pay
1460
        foreach ($arrayofamounts as $id => $amount)
1461
        {
1462
        	$result = $this->invoice->fetch($id);
1463
        	if (!$result) {
1464
        		$this->db->rollback();
1465
        		throw new RestException(404, 'Invoice ID '.$id.' not found');
1466
        	}
1467
1468
        	// Calculate amount to pay
1469
        	$totalpaye = $this->invoice->getSommePaiement();
1470
        	$totalcreditnotes = $this->invoice->getSumCreditNotesUsed();
1471
        	$totaldeposits = $this->invoice->getSumDepositsUsed();
1472
        	$resteapayer = price2num($this->invoice->total_ttc - $totalpaye - $totalcreditnotes - $totaldeposits, 'MT');
1473
        	if ($amount != 'remain')
1474
        	{
1475
        		if ($amount > $resteapayer)
1476
        		{
1477
        			$this->db->rollback();
1478
        			throw new RestException(400, 'Payment amount on invoice ID '.$id.' ('.$amount.') is higher than remain to pay ('.$resteapayer.')');
1479
        		}
1480
        		$resteapayer = $amount;
1481
        	}
1482
            // Clean parameters amount if payment is for a credit note
1483
            if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1484
                $resteapayer = price2num($resteapayer, 'MT');
1485
                $amounts[$id] = -$resteapayer;
1486
                // Multicurrency
1487
                $newvalue = price2num($this->invoice->multicurrency_total_ttc, 'MT');
1488
                $multicurrency_amounts[$id] = -$newvalue;
1489
            } else {
1490
                $resteapayer = price2num($resteapayer, 'MT');
1491
                $amounts[$id] = $resteapayer;
1492
                // Multicurrency
1493
                $newvalue = price2num($this->invoice->multicurrency_total_ttc, 'MT');
1494
                $multicurrency_amounts[$id] = $newvalue;
1495
            }
1496
        }
1497
1498
        // Creation of payment line
1499
        $paiement = new Paiement($this->db);
1500
        $paiement->datepaye     = $datepaye;
1501
        $paiement->amounts      = $amounts; // Array with all payments dispatching with invoice id
1502
        $paiement->multicurrency_amounts = $multicurrency_amounts; // Array with all payments dispatching
1503
        $paiement->paiementid   = $paiementid;
1504
        $paiement->paiementcode = dol_getIdFromCode($this->db, $paiementid, 'c_paiement', 'id', 'code', 1);
1505
        $paiement->num_paiement = $num_paiement;
1 ignored issue
show
Deprecated Code introduced by
The property Paiement::$num_paiement has been deprecated. ( Ignorable by Annotation )

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

1505
        /** @scrutinizer ignore-deprecated */ $paiement->num_paiement = $num_paiement;
Loading history...
1506
        $paiement->note         = $comment;
1507
        $paiement_id = $paiement->create(DolibarrApiAccess::$user, ($closepaidinvoices == 'yes' ? 1 : 0)); // This include closing invoices
1508
        if ($paiement_id < 0)
1509
        {
1510
            $this->db->rollback();
1511
            throw new RestException(400, 'Payment error : '.$paiement->error);
1512
        }
1513
        if (!empty($conf->banque->enabled)) {
1514
            $label = '(CustomerInvoicePayment)';
1515
            if ($paiement->paiementcode == 'CHQ' && empty($chqemetteur)) {
1516
                  throw new RestException(400, 'Emetteur is mandatory when payment code is '.$paiement->paiementcode);
1517
            }
1518
            if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) $label = '(CustomerInvoicePaymentBack)'; // Refund of a credit note
1519
            $result = $paiement->addPaymentToBank(DolibarrApiAccess::$user, 'payment', $label, $accountid, $chqemetteur, $chqbank);
1520
            if ($result < 0)
1521
            {
1522
                $this->db->rollback();
1523
                throw new RestException(400, 'Add payment to bank error : '.$paiement->error);
1524
            }
1525
        }
1526
1527
        $this->db->commit();
1528
1529
        return $paiement_id;
1530
    }
1531
1532
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1533
    /**
1534
     * Clean sensible object datas
1535
     *
1536
     * @param   object  $object    Object to clean
1537
     * @return    array    Array of cleaned object properties
1538
     */
1539
    protected function _cleanObjectDatas($object)
1540
    {
1541
        // phpcs:enable
1542
        $object = parent::_cleanObjectDatas($object);
1543
1544
        unset($object->note);
1545
        unset($object->address);
1546
        unset($object->barcode_type);
1547
        unset($object->barcode_type_code);
1548
        unset($object->barcode_type_label);
1549
        unset($object->barcode_type_coder);
1550
1551
        return $object;
1552
    }
1553
1554
    /**
1555
     * Validate fields before create or update object
1556
     *
1557
     * @param array|null    $data       Datas to validate
1558
     * @return array
1559
     *
1560
     * @throws RestException
1561
     */
1562
    private function _validate($data)
1563
    {
1564
        $invoice = array();
1565
        foreach (Invoices::$FIELDS as $field) {
1566
            if (!isset($data[$field])) {
1567
                throw new RestException(400, "$field field missing");
1568
            }
1569
            $invoice[$field] = $data[$field];
1570
        }
1571
        return $invoice;
1572
    }
1573
}
1574