Passed
Branch develop (7b8a20)
by
unknown
32:48
created

Invoices::postContact()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 27
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 13
c 0
b 0
f 0
nc 6
nop 3
dl 0
loc 27
rs 9.2222
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       int         $contact_list  0: Returned array of contacts/addresses contains all properties, 1: Return array contains just id
115
	 * @return 	array|mixed data without useless information
116
	 *
117
	 * @throws 	RestException
118
	 */
119
	private function _fetch($id, $ref = '', $ref_ext = '', $contact_list = 1)
120
	{
121
		if (!DolibarrApiAccess::$user->rights->facture->lire) {
122
			throw new RestException(401);
123
		}
124
125
		$result = $this->invoice->fetch($id, $ref, $ref_ext);
126
		if (!$result) {
127
			throw new RestException(404, 'Invoice not found');
128
		}
129
130
		// Get payment details
131
		$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...
132
		$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...
133
		$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...
134
		$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...
135
136
		if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
137
			throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
138
		}
139
140
		// Add external contacts ids
141
		$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...
142
143
		$this->invoice->fetchObjectLinked();
144
		return $this->_cleanObjectDatas($this->invoice);
145
	}
146
147
	/**
148
	 * List invoices
149
	 *
150
	 * Get a list of invoices
151
	 *
152
	 * @param string	$sortfield	      Sort field
153
	 * @param string	$sortorder	      Sort order
154
	 * @param int		$limit		      Limit for list
155
	 * @param int		$page		      Page number
156
	 * @param string   	$thirdparty_ids	  Thirdparty ids to filter orders of (example '1' or '1,2,3') {@pattern /^[0-9,]*$/i}
157
	 * @param string	$status		      Filter by invoice status : draft | unpaid | paid | cancelled
158
	 * @param string    $sqlfilters       Other criteria to filter answers separated by a comma. Syntax example "(t.ref:like:'SO-%') and (t.date_creation:<:'20160101')"
159
	 * @return array                      Array of invoice objects
160
	 *
161
	 * @throws RestException 404 Not found
162
	 * @throws RestException 503 Error
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
		} else {
248
			throw new RestException(503, 'Error when retrieve invoice list : '.$db->lasterror());
249
		}
250
		if (!count($obj_ret)) {
251
			throw new RestException(404, 'No invoice found');
252
		}
253
		return $obj_ret;
254
	}
255
256
	/**
257
	 * Create invoice object
258
	 *
259
	 * @param array $request_data   Request datas
260
	 * @return int                  ID of invoice
261
	 */
262
	public function post($request_data = null)
263
	{
264
		if (!DolibarrApiAccess::$user->rights->facture->creer) {
265
			throw new RestException(401, "Insuffisant rights");
266
		}
267
		// Check mandatory fields
268
		$result = $this->_validate($request_data);
269
270
		foreach ($request_data as $field => $value) {
271
			$this->invoice->$field = $value;
272
		}
273
		if (!array_key_exists('date', $request_data)) {
274
			$this->invoice->date = dol_now();
275
		}
276
		/* We keep lines as an array
277
         if (isset($request_data["lines"])) {
278
            $lines = array();
279
            foreach ($request_data["lines"] as $line) {
280
                array_push($lines, (object) $line);
281
            }
282
            $this->invoice->lines = $lines;
283
        }*/
284
285
		if ($this->invoice->create(DolibarrApiAccess::$user, 0, (empty($request_data["date_lim_reglement"]) ? 0 : $request_data["date_lim_reglement"])) < 0) {
286
			throw new RestException(500, "Error creating invoice", array_merge(array($this->invoice->error), $this->invoice->errors));
287
		}
288
		return $this->invoice->id;
289
	}
290
291
	 /**
292
	  * Create an invoice using an existing order.
293
	  *
294
	  *
295
	  * @param int   $orderid       Id of the order
296
	  *
297
	  * @url     POST /createfromorder/{orderid}
298
	  *
299
	  * @return int
300
	  * @throws RestException 400
301
	  * @throws RestException 401
302
	  * @throws RestException 404
303
	  * @throws RestException 405
304
	  */
305
	public function createInvoiceFromOrder($orderid)
306
	{
307
308
		require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
309
310
		if (!DolibarrApiAccess::$user->rights->commande->lire) {
311
			throw new RestException(401);
312
		}
313
		if (!DolibarrApiAccess::$user->rights->facture->creer) {
314
			throw new RestException(401);
315
		}
316
		if (empty($orderid)) {
317
			throw new RestException(400, 'Order ID is mandatory');
318
		}
319
320
		$order = new Commande($this->db);
321
		$result = $order->fetch($orderid);
322
		if (!$result) {
323
			throw new RestException(404, 'Order not found');
324
		}
325
326
		$result = $this->invoice->createFromOrder($order, DolibarrApiAccess::$user);
327
		if ($result < 0) {
328
			throw new RestException(405, $this->invoice->error);
329
		}
330
		$this->invoice->fetchObjectLinked();
331
		return $this->_cleanObjectDatas($this->invoice);
332
	}
333
334
	/**
335
	 * Get lines of an invoice
336
	 *
337
	 * @param int   $id             Id of invoice
338
	 *
339
	 * @url	GET {id}/lines
340
	 *
341
	 * @return int
342
	 */
343
	public function getLines($id)
344
	{
345
		if (!DolibarrApiAccess::$user->rights->facture->lire) {
346
			throw new RestException(401);
347
		}
348
349
		$result = $this->invoice->fetch($id);
350
		if (!$result) {
351
			throw new RestException(404, 'Invoice not found');
352
		}
353
354
		if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
355
			throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
356
		}
357
		$this->invoice->getLinesArray();
358
		$result = array();
359
		foreach ($this->invoice->lines as $line) {
360
			array_push($result, $this->_cleanObjectDatas($line));
361
		}
362
		return $result;
363
	}
364
365
	/**
366
	 * Update a line to a given invoice
367
	 *
368
	 * @param int   $id             Id of invoice to update
369
	 * @param int   $lineid         Id of line to update
370
	 * @param array $request_data   InvoiceLine data
371
	 *
372
	 * @url	PUT {id}/lines/{lineid}
373
	 *
374
	 * @return array
375
	 *
376
	 * @throws RestException 304
377
	 * @throws RestException 401
378
	 * @throws RestException 404 Invoice not found
379
	 */
380
	public function putLine($id, $lineid, $request_data = null)
381
	{
382
		if (!DolibarrApiAccess::$user->rights->facture->creer) {
383
			throw new RestException(401);
384
		}
385
386
		$result = $this->invoice->fetch($id);
387
		if (!$result) {
388
			throw new RestException(404, 'Invoice not found');
389
		}
390
391
		if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
392
			throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
393
		}
394
		$request_data = (object) $request_data;
395
		$updateRes = $this->invoice->updateline(
396
			$lineid,
397
			$request_data->desc,
398
			$request_data->subprice,
399
			$request_data->qty,
400
			$request_data->remise_percent,
401
			$request_data->date_start,
402
			$request_data->date_end,
403
			$request_data->tva_tx,
404
			$request_data->localtax1_tx,
405
			$request_data->localtax2_tx,
406
			'HT',
407
			$request_data->info_bits,
408
			$request_data->product_type,
409
			$request_data->fk_parent_line,
410
			0,
411
			$request_data->fk_fournprice,
412
			$request_data->pa_ht,
413
			$request_data->label,
414
			$request_data->special_code,
415
			$request_data->array_options,
416
			$request_data->situation_percent,
417
			$request_data->fk_unit,
418
			$request_data->multicurrency_subprice,
419
			0,
420
			$request_data->ref_ext
421
		);
422
423
		if ($updateRes > 0) {
424
			$result = $this->get($id);
425
			unset($result->line);
426
			return $this->_cleanObjectDatas($result);
427
		} else {
428
			throw new RestException(304, $this->invoice->error);
429
		}
430
	}
431
432
	/**
433
	 * Add a contact type of given invoice
434
	 *
435
	 * @param int    $id             Id of invoice to update
436
	 * @param int    $contactid      Id of contact to add
437
	 * @param string $type           Type of the contact (BILLING, SHIPPING, CUSTOMER)
438
	 *
439
	 * @url	POST {id}/contact/{contactid}/{type}
440
	 *
441
	 * @return int
442
	 *
443
	 * @throws RestException 401
444
	 * @throws RestException 404
445
	 */
446
	public function postContact($id, $contactid, $type)
447
	{
448
		if (!DolibarrApiAccess::$user->rights->facture->creer) {
449
			throw new RestException(401);
450
		}
451
452
		$result = $this->invoice->fetch($id);
453
454
		if (!$result) {
455
			throw new RestException(404, 'Invoice not found');
456
		}
457
458
		if (!in_array($type, array('BILLING', 'SHIPPING', 'CUSTOMER'), true)) {
459
			throw new RestException(500, 'Availables types: BILLING, SHIPPING OR CUSTOMER');
460
		}
461
462
		if (!DolibarrApi::_checkAccessToResource('invoice', $this->invoice->id)) {
463
			throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
464
		}
465
466
		$result = $this->invoice->add_contact($contactid, $type, 'external');
467
468
		if (!$result) {
469
			throw new RestException(500, 'Error when added the contact');
470
		}
471
472
		return $this->_cleanObjectDatas($this->invoice);
473
	}
474
475
	/**
476
	 * Delete a contact type of given invoice
477
	 *
478
	 * @param int    $id             Id of invoice to update
479
	 * @param int    $rowid          Row key of the contact in the array contact_ids.
480
	 *
481
	 * @url	DELETE {id}/contact/{rowid}
482
	 *
483
	 * @return array
484
  	 *
485
     * @throws RestException 401
486
     * @throws RestException 404
487
     * @throws RestException 500
488
	 */
489
    public function deleteContact($id, $rowid)
490
    {
491
        if (!DolibarrApiAccess::$user->rights->facture->creer) {
492
            throw new RestException(401);
493
        }
494
495
        $result = $this->invoice->fetch($id);
496
497
		if (!$result) {
498
			throw new RestException(404, 'Invoice not found');
499
		}
500
501
        if (!DolibarrApi::_checkAccessToResource('invoice', $this->invoice->id)) {
502
			throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
503
		}
504
505
        $result = $this->invoice->delete_contact($rowid);
506
        if ($result < 0) {
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
    	} else {
553
    		throw new RestException(405, $this->invoice->error);
554
    	}
555
    }
556
557
    /**
558
     * Update invoice
559
     *
560
     * @param int   $id             Id of invoice to update
561
     * @param array $request_data   Datas
562
     * @return int
563
     */
564
    public function put($id, $request_data = null)
565
    {
566
        if (!DolibarrApiAccess::$user->rights->facture->creer) {
567
			throw new RestException(401);
568
		}
569
570
        $result = $this->invoice->fetch($id);
571
        if (!$result) {
572
            throw new RestException(404, 'Invoice not found');
573
        }
574
575
		if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
576
			throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
577
		}
578
579
        foreach ($request_data as $field => $value) {
580
            if ($field == 'id') continue;
581
            $this->invoice->$field = $value;
582
        }
583
584
        // update bank account
585
        if (!empty($this->invoice->fk_account))
586
        {
587
            if ($this->invoice->setBankAccount($this->invoice->fk_account) == 0) {
588
                throw new RestException(400, $this->invoice->error);
589
            }
590
        }
591
592
        if ($this->invoice->update(DolibarrApiAccess::$user))
593
            return $this->get($id);
594
595
        return false;
596
    }
597
598
    /**
599
     * Delete invoice
600
     *
601
     * @param int   $id 	Invoice ID
602
     * @return array
603
     */
604
    public function delete($id)
605
    {
606
        if (!DolibarrApiAccess::$user->rights->facture->supprimer) {
607
			throw new RestException(401);
608
		}
609
        $result = $this->invoice->fetch($id);
610
        if (!$result) {
611
            throw new RestException(404, 'Invoice not found');
612
        }
613
614
		if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
615
			throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
616
		}
617
618
		$result = $this->invoice->delete(DolibarrApiAccess::$user);
619
        if ($result < 0)
620
        {
621
            throw new RestException(500);
622
        }
623
624
        return array(
625
            'success' => array(
626
                'code' => 200,
627
                'message' => 'Invoice deleted'
628
            )
629
        );
630
    }
631
632
    /**
633
     * Add a line to a given invoice
634
     *
635
     * Exemple of POST query :
636
     * {
637
     *     "desc": "Desc", "subprice": "1.00000000", "qty": "1", "tva_tx": "20.000", "localtax1_tx": "0.000", "localtax2_tx": "0.000",
638
     *     "fk_product": "1", "remise_percent": "0", "date_start": "", "date_end": "", "fk_code_ventilation": 0,  "info_bits": "0",
639
     *     "fk_remise_except": null,  "product_type": "1", "rang": "-1", "special_code": "0", "fk_parent_line": null, "fk_fournprice": null,
640
     *     "pa_ht": "0.00000000", "label": "", "array_options": [], "situation_percent": "100", "fk_prev_id": null, "fk_unit": null
641
     * }
642
     *
643
     * @param int   $id             Id of invoice
644
     * @param array $request_data   InvoiceLine data
645
     *
646
     * @url     POST {id}/lines
647
     *
648
     * @return int
649
     *
650
     * @throws RestException 304
651
     * @throws RestException 401
652
     * @throws RestException 404
653
     * @throws RestException 400
654
     */
655
    public function postLine($id, $request_data = null)
656
    {
657
        if (!DolibarrApiAccess::$user->rights->facture->creer) {
658
            throw new RestException(401);
659
        }
660
661
        $result = $this->invoice->fetch($id);
662
        if (!$result) {
663
            throw new RestException(404, 'Invoice not found');
664
        }
665
666
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
667
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
668
        }
669
670
        $request_data = (object) $request_data;
671
672
        // Reset fk_parent_line for no child products and special product
673
        if (($request_data->product_type != 9 && empty($request_data->fk_parent_line)) || $request_data->product_type == 9) {
674
            $request_data->fk_parent_line = 0;
675
        }
676
677
        // calculate pa_ht
678
        $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);
679
        $pa_ht = $marginInfos[0];
680
681
        $updateRes = $this->invoice->addline(
682
            $request_data->desc,
683
            $request_data->subprice,
684
            $request_data->qty,
685
            $request_data->tva_tx,
686
            $request_data->localtax1_tx,
687
            $request_data->localtax2_tx,
688
            $request_data->fk_product,
689
            $request_data->remise_percent,
690
            $request_data->date_start,
691
            $request_data->date_end,
692
            $request_data->fk_code_ventilation,
693
            $request_data->info_bits,
694
            $request_data->fk_remise_except,
695
            'HT',
696
            0,
697
            $request_data->product_type,
698
            $request_data->rang,
699
            $request_data->special_code,
700
            $request_data->origin,
701
            $request_data->origin_id,
702
            $request_data->fk_parent_line,
703
            empty($request_data->fk_fournprice) ?null:$request_data->fk_fournprice,
704
            $pa_ht,
705
            $request_data->label,
706
            $request_data->array_options,
707
            $request_data->situation_percent,
708
            $request_data->fk_prev_id,
709
            $request_data->fk_unit,
710
            0,
711
            $request_data->ref_ext
712
        );
713
714
        if ($updateRes < 0) {
715
            throw new RestException(400, 'Unable to insert the new line. Check your inputs. '.$this->invoice->error);
716
        }
717
718
        return $updateRes;
719
    }
720
721
    /**
722
     * Adds a contact to an invoice
723
     *
724
     * @param   int 	$id             	Order ID
725
     * @param   int 	$fk_socpeople       	Id of thirdparty contact (if source = 'external') or id of user (if souce = 'internal') to link
726
     * @param   string 	$type_contact           Type of contact (code). Must a code found into table llx_c_type_contact. For example: BILLING
727
     * @param   string  $source             	external=Contact extern (llx_socpeople), internal=Contact intern (llx_user)
728
     * @param   int     $notrigger              Disable all triggers
729
     *
730
     * @url POST    {id}/contacts
731
     *
732
     * @return  array
733
     *
734
     * @throws RestException 304
735
     * @throws RestException 401
736
     * @throws RestException 404
737
     * @throws RestException 500
738
     *
739
     */
740
    public function addContact($id, $fk_socpeople, $type_contact, $source, $notrigger = 0)
741
    {
742
        if (!DolibarrApiAccess::$user->rights->facture->creer) {
743
            throw new RestException(401);
744
        }
745
        $result = $this->invoice->fetch($id);
746
        if (!$result) {
747
            throw new RestException(404, 'Invoice not found');
748
        }
749
750
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
751
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
752
        }
753
754
        $result = $this->invoice->add_contact($fk_socpeople, $type_contact, $source, $notrigger);
755
        if ($result < 0) {
756
            throw new RestException(500, 'Error : '.$this->invoice->error);
757
        }
758
759
        $result = $this->invoice->fetch($id);
760
        if (!$result) {
761
            throw new RestException(404, 'Invoice not found');
762
        }
763
764
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
765
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
766
        }
767
768
        return $this->_cleanObjectDatas($this->invoice);
769
    }
770
771
772
773
    /**
774
     * Sets an invoice as draft
775
     *
776
     * @param   int $id             Order ID
777
     * @param   int $idwarehouse    Warehouse ID
778
     *
779
     * @url POST    {id}/settodraft
780
     *
781
     * @return  array
782
     *
783
     * @throws RestException 304
784
     * @throws RestException 401
785
     * @throws RestException 404
786
     * @throws RestException 500
787
     *
788
     */
789
    public function settodraft($id, $idwarehouse = -1)
790
    {
791
        if (!DolibarrApiAccess::$user->rights->facture->creer) {
792
            throw new RestException(401);
793
        }
794
        $result = $this->invoice->fetch($id);
795
        if (!$result) {
796
            throw new RestException(404, 'Invoice not found');
797
        }
798
799
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
800
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
801
        }
802
803
        $result = $this->invoice->setDraft(DolibarrApiAccess::$user, $idwarehouse);
804
        if ($result == 0) {
805
            throw new RestException(304, 'Nothing done.');
806
        }
807
        if ($result < 0) {
808
            throw new RestException(500, 'Error : '.$this->invoice->error);
809
        }
810
811
        $result = $this->invoice->fetch($id);
812
        if (!$result) {
813
            throw new RestException(404, 'Invoice not found');
814
        }
815
816
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
817
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
818
        }
819
820
        return $this->_cleanObjectDatas($this->invoice);
821
    }
822
823
824
    /**
825
     * Validate an invoice
826
     *
827
	 * If you get a bad value for param notrigger check that ou provide this in body
828
     * {
829
     *   "idwarehouse": 0,
830
     *   "notrigger": 0
831
     * }
832
     *
833
     * @param   int $id             Invoice ID
834
     * @param   int $idwarehouse    Warehouse ID
835
     * @param   int $notrigger      1=Does not execute triggers, 0= execute triggers
836
     *
837
     * @url POST    {id}/validate
838
     *
839
     * @return  array
840
     */
841
    public function validate($id, $idwarehouse = 0, $notrigger = 0)
842
    {
843
    	if (!DolibarrApiAccess::$user->rights->facture->creer) {
844
    		throw new RestException(401);
845
    	}
846
    	$result = $this->invoice->fetch($id);
847
    	if (!$result) {
848
    		throw new RestException(404, 'Invoice not found');
849
    	}
850
851
    	if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
852
    		throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
853
    	}
854
855
    	$result = $this->invoice->validate(DolibarrApiAccess::$user, '', $idwarehouse, $notrigger);
856
    	if ($result == 0) {
857
    		throw new RestException(304, 'Error nothing done. May be object is already validated');
858
    	}
859
    	if ($result < 0) {
860
    		throw new RestException(500, 'Error when validating Invoice: '.$this->invoice->error);
861
    	}
862
863
        $result = $this->invoice->fetch($id);
864
        if (!$result) {
865
            throw new RestException(404, 'Invoice not found');
866
        }
867
868
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
869
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
870
        }
871
872
        return $this->_cleanObjectDatas($this->invoice);
873
    }
874
875
    /**
876
     * Sets an invoice as paid
877
     *
878
     * @param   int 	$id            Order ID
879
     * @param   string 	$close_code    Code filled if we classify to 'Paid completely' when payment is not complete (for escompte for example)
880
     * @param   string 	$close_note    Comment defined if we classify to 'Paid' when payment is not complete (for escompte for example)
881
     *
882
     * @url POST    {id}/settopaid
883
     *
884
     * @return  array 	An invoice object
885
     *
886
     * @throws RestException 304
887
     * @throws RestException 401
888
     * @throws RestException 404
889
     * @throws RestException 500
890
     */
891
    public function settopaid($id, $close_code = '', $close_note = '')
892
    {
893
        if (!DolibarrApiAccess::$user->rights->facture->creer) {
894
            throw new RestException(401);
895
        }
896
        $result = $this->invoice->fetch($id);
897
        if (!$result) {
898
            throw new RestException(404, 'Invoice not found');
899
        }
900
901
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
902
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
903
        }
904
905
        $result = $this->invoice->set_paid(DolibarrApiAccess::$user, $close_code, $close_note);
906
        if ($result == 0) {
907
            throw new RestException(304, 'Error nothing done. May be object is already validated');
908
        }
909
        if ($result < 0) {
910
            throw new RestException(500, 'Error : '.$this->invoice->error);
911
        }
912
913
914
        $result = $this->invoice->fetch($id);
915
        if (!$result) {
916
            throw new RestException(404, 'Invoice not found');
917
        }
918
919
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
920
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
921
        }
922
923
        return $this->_cleanObjectDatas($this->invoice);
924
    }
925
926
927
    /**
928
     * Sets an invoice as unpaid
929
     *
930
     * @param   int     $id            Order ID
931
     *
932
     * @url POST    {id}/settounpaid
933
     *
934
     * @return  array   An invoice object
935
     *
936
     * @throws RestException 304
937
     * @throws RestException 401
938
     * @throws RestException 404
939
     * @throws RestException 500
940
     */
941
    public function settounpaid($id)
942
    {
943
        if (!DolibarrApiAccess::$user->rights->facture->creer) {
944
            throw new RestException(401);
945
        }
946
        $result = $this->invoice->fetch($id);
947
        if (!$result) {
948
            throw new RestException(404, 'Invoice not found');
949
        }
950
951
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
952
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
953
        }
954
955
        $result = $this->invoice->set_unpaid(DolibarrApiAccess::$user);
956
        if ($result == 0) {
957
            throw new RestException(304, 'Nothing done');
958
        }
959
        if ($result < 0) {
960
            throw new RestException(500, 'Error : '.$this->invoice->error);
961
        }
962
963
964
        $result = $this->invoice->fetch($id);
965
        if (!$result) {
966
            throw new RestException(404, 'Invoice not found');
967
        }
968
969
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
970
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
971
        }
972
973
        return $this->_cleanObjectDatas($this->invoice);
974
    }
975
976
    /**
977
     * Create a discount (credit available) for a credit note or a deposit.
978
     *
979
     * @param   int 	$id            Invoice ID
980
     * @url POST    {id}/markAsCreditAvailable
981
     *
982
     * @return  array 	An invoice object
983
     *
984
     * @throws RestException 304
985
     * @throws RestException 401
986
     * @throws RestException 404
987
     * @throws RestException 500
988
     */
989
    public function markAsCreditAvailable($id)
990
    {
991
        if (!DolibarrApiAccess::$user->rights->facture->creer) {
992
            throw new RestException(401);
993
        }
994
995
        $result = $this->invoice->fetch($id);
996
        if (!$result) {
997
            throw new RestException(404, 'Invoice not found');
998
        }
999
1000
        if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1001
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1002
        }
1003
1004
        if ($this->invoice->paye) {
1005
            throw new RestException(500, 'Alreay payed');
1006
        }
1007
1008
        $this->invoice->fetch($id);
1009
        $this->invoice->fetch_thirdparty();
1010
1011
        // Check if there is already a discount (protection to avoid duplicate creation when resubmit post)
1012
        $discountcheck = new DiscountAbsolute($this->db);
1013
        $result = $discountcheck->fetch(0, $this->invoice->id);
1014
1015
        $canconvert = 0;
1016
        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)
1017
        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)
1018
        if ($canconvert)
1019
        {
1020
            $this->db->begin();
1021
1022
            $amount_ht = $amount_tva = $amount_ttc = array();
1023
            $multicurrency_amount_ht = $multicurrency_amount_tva = $multicurrency_amount_ttc = array();
1024
1025
            // Loop on each vat rate
1026
            $i = 0;
1027
            foreach ($this->invoice->lines as $line)
1028
            {
1029
                if ($line->product_type < 9 && $line->total_ht != 0) // Remove lines with product_type greater than or equal to 9
1030
                { 	// no need to create discount if amount is null
1031
                    $amount_ht[$line->tva_tx] += $line->total_ht;
1032
                    $amount_tva[$line->tva_tx] += $line->total_tva;
1033
                    $amount_ttc[$line->tva_tx] += $line->total_ttc;
1034
                    $multicurrency_amount_ht[$line->tva_tx] += $line->multicurrency_total_ht;
1035
                    $multicurrency_amount_tva[$line->tva_tx] += $line->multicurrency_total_tva;
1036
                    $multicurrency_amount_ttc[$line->tva_tx] += $line->multicurrency_total_ttc;
1037
                    $i++;
1038
                }
1039
            }
1040
1041
            // Insert one discount by VAT rate category
1042
            $discount = new DiscountAbsolute($this->db);
1043
            if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1044
                $discount->description = '(CREDIT_NOTE)';
1045
            } elseif ($this->invoice->type == Facture::TYPE_DEPOSIT) {
1046
                $discount->description = '(DEPOSIT)';
1047
            } elseif ($this->invoice->type == Facture::TYPE_STANDARD || $this->invoice->type == Facture::TYPE_REPLACEMENT || $this->invoice->type == Facture::TYPE_SITUATION) {
1048
                $discount->description = '(EXCESS RECEIVED)';
1049
            } else {
1050
                throw new RestException(500, 'Cant convert to reduc an Invoice of this type');
1051
            }
1052
1053
            $discount->fk_soc = $this->invoice->socid;
1054
            $discount->fk_facture_source = $this->invoice->id;
1055
1056
            $error = 0;
1057
1058
            if ($this->invoice->type == Facture::TYPE_STANDARD || $this->invoice->type == Facture::TYPE_REPLACEMENT || $this->invoice->type == Facture::TYPE_SITUATION)
1059
            {
1060
                // If we're on a standard invoice, we have to get excess received to create a discount in TTC without VAT
1061
1062
                // Total payments
1063
                $sql = 'SELECT SUM(pf.amount) as total_payments';
1064
                $sql .= ' FROM '.MAIN_DB_PREFIX.'paiement_facture as pf, '.MAIN_DB_PREFIX.'paiement as p';
1065
                $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as c ON p.fk_paiement = c.id';
1066
                $sql .= ' WHERE pf.fk_facture = '.$this->invoice->id;
1067
                $sql .= ' AND pf.fk_paiement = p.rowid';
1068
                $sql .= ' AND p.entity IN ('.getEntity('invoice').')';
1069
                $resql = $this->db->query($sql);
1070
                if (!$resql) dol_print_error($this->db);
1071
1072
                $res = $this->db->fetch_object($resql);
1073
                $total_payments = $res->total_payments;
1074
1075
                // Total credit note and deposit
1076
                $total_creditnote_and_deposit = 0;
1077
                $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
1078
                $sql .= " re.description, re.fk_facture_source";
1079
                $sql .= " FROM ".MAIN_DB_PREFIX."societe_remise_except as re";
1080
                $sql .= " WHERE fk_facture = ".$this->invoice->id;
1081
                $resql = $this->db->query($sql);
1082
                if (!empty($resql)) {
1083
                    while ($obj = $this->db->fetch_object($resql)) $total_creditnote_and_deposit += $obj->amount_ttc;
1084
                } else dol_print_error($this->db);
1085
1086
                $discount->amount_ht = $discount->amount_ttc = $total_payments + $total_creditnote_and_deposit - $this->invoice->total_ttc;
1087
                $discount->amount_tva = 0;
1088
                $discount->tva_tx = 0;
1089
1090
                $result = $discount->create(DolibarrApiAccess::$user);
1091
                if ($result < 0)
1092
                {
1093
                    $error++;
1094
                }
1095
            }
1096
            if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE || $this->invoice->type == Facture::TYPE_DEPOSIT)
1097
            {
1098
                foreach ($amount_ht as $tva_tx => $xxx)
1099
                {
1100
                    $discount->amount_ht = abs($amount_ht[$tva_tx]);
1101
                    $discount->amount_tva = abs($amount_tva[$tva_tx]);
1102
                    $discount->amount_ttc = abs($amount_ttc[$tva_tx]);
1103
                    $discount->multicurrency_amount_ht = abs($multicurrency_amount_ht[$tva_tx]);
1104
                    $discount->multicurrency_amount_tva = abs($multicurrency_amount_tva[$tva_tx]);
1105
                    $discount->multicurrency_amount_ttc = abs($multicurrency_amount_ttc[$tva_tx]);
1106
                    $discount->tva_tx = abs($tva_tx);
1107
1108
                    $result = $discount->create(DolibarrApiAccess::$user);
1109
                    if ($result < 0)
1110
                    {
1111
                        $error++;
1112
                        break;
1113
                    }
1114
                }
1115
            }
1116
1117
            if (empty($error))
1118
            {
1119
                if ($this->invoice->type != Facture::TYPE_DEPOSIT) {
1120
                    // Classe facture
1121
                    $result = $this->invoice->set_paid(DolibarrApiAccess::$user);
1122
                    if ($result >= 0)
1123
                    {
1124
                        $this->db->commit();
1125
                    } else {
1126
                        $this->db->rollback();
1127
                        throw new RestException(500, 'Could not set paid');
1128
                    }
1129
                } else {
1130
                    $this->db->commit();
1131
                }
1132
            } else {
1133
                $this->db->rollback();
1134
                throw new RestException(500, 'Discount creation error');
1135
            }
1136
        }
1137
1138
        return $this->_cleanObjectDatas($this->invoice);
1139
    }
1140
1141
     /**
1142
     * Add a discount line into an invoice (as an invoice line) using an existing absolute discount
1143
     *
1144
     * Note that this consume the discount.
1145
     *
1146
     * @param int   $id             Id of invoice
1147
     * @param int   $discountid     Id of discount
1148
     *
1149
     * @url     POST {id}/usediscount/{discountid}
1150
     *
1151
     * @return int
1152
	 *
1153
     * @throws RestException 400
1154
     * @throws RestException 401
1155
     * @throws RestException 404
1156
     * @throws RestException 405
1157
     */
1158
    public function useDiscount($id, $discountid)
1159
    {
1160
1161
        if (!DolibarrApiAccess::$user->rights->facture->creer) {
1162
            throw new RestException(401);
1163
        }
1164
        if (empty($id)) {
1165
            throw new RestException(400, 'Invoice ID is mandatory');
1166
        }
1167
        if (empty($discountid)) {
1168
            throw new RestException(400, 'Discount ID is mandatory');
1169
        }
1170
1171
        if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1172
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1173
        }
1174
1175
        $result = $this->invoice->fetch($id);
1176
        if (!$result) {
1177
            throw new RestException(404, 'Invoice not found');
1178
        }
1179
1180
        $result = $this->invoice->insert_discount($discountid);
1181
        if ($result < 0) {
1182
            throw new RestException(405, $this->invoice->error);
1183
        }
1184
1185
        return $result;
1186
    }
1187
1188
     /**
1189
     * Add an available credit note discount to payments of an existing invoice.
1190
     *
1191
     *  Note that this consume the credit note.
1192
     *
1193
     * @param int   $id            Id of invoice
1194
     * @param int   $discountid    Id of a discount coming from a credit note
1195
     *
1196
     * @url     POST {id}/usecreditnote/{discountid}
1197
     *
1198
     * @return int
1199
     *
1200
     * @throws RestException 400
1201
     * @throws RestException 401
1202
     * @throws RestException 404
1203
     * @throws RestException 405
1204
     */
1205
    public function useCreditNote($id, $discountid)
1206
    {
1207
1208
        require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1209
1210
        if (!DolibarrApiAccess::$user->rights->facture->creer) {
1211
            throw new RestException(401);
1212
        }
1213
        if (empty($id)) {
1214
            throw new RestException(400, 'Invoice ID is mandatory');
1215
        }
1216
        if (empty($discountid)) {
1217
            throw new RestException(400, 'Credit ID is mandatory');
1218
        }
1219
1220
        if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1221
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1222
        }
1223
        $discount = new DiscountAbsolute($this->db);
1224
        $result = $discount->fetch($discountid);
1225
        if (!$result) {
1226
            throw new RestException(404, 'Credit not found');
1227
        }
1228
1229
        $result = $discount->link_to_invoice(0, $id);
1230
        if ($result < 0) {
1231
            throw new RestException(405, $discount->error);
1232
        }
1233
1234
        return $result;
1235
    }
1236
1237
    /**
1238
     * Get list of payments of a given invoice
1239
     *
1240
     * @param int   $id             Id of invoice
1241
     *
1242
     * @url     GET {id}/payments
1243
     *
1244
     * @return array
1245
	 *
1246
     * @throws RestException 400
1247
     * @throws RestException 401
1248
     * @throws RestException 404
1249
     * @throws RestException 405
1250
     */
1251
    public function getPayments($id)
1252
    {
1253
1254
        if (!DolibarrApiAccess::$user->rights->facture->lire) {
1255
            throw new RestException(401);
1256
        }
1257
        if (empty($id)) {
1258
            throw new RestException(400, 'Invoice ID is mandatory');
1259
        }
1260
1261
        if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1262
            throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1263
        }
1264
1265
        $result = $this->invoice->fetch($id);
1266
        if (!$result) {
1267
            throw new RestException(404, 'Invoice not found');
1268
        }
1269
1270
        $result = $this->invoice->getListOfPayments();
1271
        if ($result < 0) {
1272
            throw new RestException(405, $this->invoice->error);
1273
        }
1274
1275
        return $result;
1276
    }
1277
1278
1279
    /**
1280
     * Add payment line to a specific invoice with the remain to pay as amount.
1281
     *
1282
     * @param int     $id                               Id of invoice
1283
     * @param string  $datepaye           {@from body}  Payment date        {@type timestamp}
1284
     * @param int     $paymentid          {@from body}  Payment mode Id {@min 1}
1285
     * @param string  $closepaidinvoices  {@from body}  Close paid invoices {@choice yes,no}
1286
     * @param int     $accountid          {@from body}  Account Id {@min 1}
1287
     * @param string  $num_payment        {@from body}  Payment number (optional)
1288
     * @param string  $comment            {@from body}  Note private (optional)
1289
     * @param string  $chqemetteur        {@from body}  Payment issuer (mandatory if paymentcode = 'CHQ')
1290
     * @param string  $chqbank            {@from body}  Issuer bank name (optional)
1291
     *
1292
     * @url     POST {id}/payments
1293
     *
1294
     * @return int  Payment ID
1295
	 *
1296
     * @throws RestException 400
1297
     * @throws RestException 401
1298
     * @throws RestException 404
1299
     */
1300
    public function addPayment($id, $datepaye, $paymentid, $closepaidinvoices, $accountid, $num_payment = '', $comment = '', $chqemetteur = '', $chqbank = '')
1301
    {
1302
        global $conf;
1303
1304
    	require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
1305
1306
    	if (!DolibarrApiAccess::$user->rights->facture->creer) {
1307
    		throw new RestException(403);
1308
    	}
1309
    	if (empty($id)) {
1310
    		throw new RestException(400, 'Invoice ID is mandatory');
1311
    	}
1312
1313
    	if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1314
    		throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1315
    	}
1316
1317
    	if (!empty($conf->banque->enabled)) {
1318
    		if (empty($accountid)) {
1319
    			throw new RestException(400, 'Account ID is mandatory');
1320
    		}
1321
    	}
1322
1323
    	if (empty($paymentid)) {
1324
    		throw new RestException(400, 'Payment ID or Payment Code is mandatory');
1325
    	}
1326
1327
1328
    	$result = $this->invoice->fetch($id);
1329
    	if (!$result) {
1330
    		throw new RestException(404, 'Invoice not found');
1331
    	}
1332
1333
    	// Calculate amount to pay
1334
    	$totalpaye = $this->invoice->getSommePaiement();
1335
    	$totalcreditnotes = $this->invoice->getSumCreditNotesUsed();
1336
    	$totaldeposits = $this->invoice->getSumDepositsUsed();
1337
    	$resteapayer = price2num($this->invoice->total_ttc - $totalpaye - $totalcreditnotes - $totaldeposits, 'MT');
1338
1339
    	$this->db->begin();
1340
1341
    	$amounts = array();
1342
    	$multicurrency_amounts = array();
1343
1344
    	// Clean parameters amount if payment is for a credit note
1345
    	if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1346
    		$resteapayer = price2num($resteapayer, 'MT');
1347
    		$amounts[$id] = -$resteapayer;
1348
    		// Multicurrency
1349
    		$newvalue = price2num($this->invoice->multicurrency_total_ttc, 'MT');
1350
    		$multicurrency_amounts[$id] = -$newvalue;
1351
    	} else {
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
    	}
1358
1359
1360
    	// Creation of payment line
1361
    	$paymentobj = new Paiement($this->db);
1362
    	$paymentobj->datepaye     = $datepaye;
1363
    	$paymentobj->amounts      = $amounts; // Array with all payments dispatching with invoice id
1364
    	$paymentobj->multicurrency_amounts = $multicurrency_amounts; // Array with all payments dispatching
1365
    	$paymentobj->paiementid = $paymentid;
1366
    	$paymentobj->paiementcode = dol_getIdFromCode($this->db, $paymentid, 'c_paiement', 'id', 'code', 1);
1367
    	$paymentobj->num_payment = $num_payment;
1368
    	$paymentobj->note_private = $comment;
1369
1370
    	$payment_id = $paymentobj->create(DolibarrApiAccess::$user, ($closepaidinvoices == 'yes' ? 1 : 0)); // This include closing invoices
1371
    	if ($payment_id < 0)
1372
    	{
1373
    		$this->db->rollback();
1374
    		throw new RestException(400, 'Payment error : '.$paymentobj->error);
1375
    	}
1376
1377
    	if (!empty($conf->banque->enabled)) {
1378
    		$label = '(CustomerInvoicePayment)';
1379
1380
    		if ($paymentobj->paiementcode == 'CHQ' && empty($chqemetteur)) {
1381
    			throw new RestException(400, 'Emetteur is mandatory when payment code is '.$paymentobj->paiementcode);
1382
    		}
1383
    		if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) $label = '(CustomerInvoicePaymentBack)'; // Refund of a credit note
1384
    		$result = $paymentobj->addPaymentToBank(DolibarrApiAccess::$user, 'payment', $label, $accountid, $chqemetteur, $chqbank);
1385
    		if ($result < 0)
1386
    		{
1387
    			$this->db->rollback();
1388
    			throw new RestException(400, 'Add payment to bank error : '.$paymentobj->error);
1389
    		}
1390
    	}
1391
1392
    	$this->db->commit();
1393
1394
    	return $payment_id;
1395
    }
1396
1397
    /**
1398
     * Add a payment to pay partially or completely one or several invoices.
1399
     * Warning: Take care that all invoices are owned by the same customer.
1400
     * Example of value for parameter arrayofamounts: {"1": "99.99", "2": "10"}
1401
     *
1402
     * @param array   $arrayofamounts     {@from body}  Array with id of invoices with amount to pay for each invoice
1403
     * @param string  $datepaye           {@from body}  Payment date        {@type timestamp}
1404
     * @param int     $paymentid          {@from body}  Payment mode Id {@min 1}
1405
     * @param string  $closepaidinvoices  {@from body}  Close paid invoices {@choice yes,no}
1406
     * @param int     $accountid          {@from body}  Account Id {@min 1}
1407
     * @param string  $num_payment        {@from body}  Payment number (optional)
1408
     * @param string  $comment            {@from body}  Note private (optional)
1409
     * @param string  $chqemetteur        {@from body}  Payment issuer (mandatory if paiementcode = 'CHQ')
1410
     * @param string  $chqbank            {@from body}  Issuer bank name (optional)
1411
     *
1412
     * @url     POST /paymentsdistributed
1413
     *
1414
     * @return int  Payment ID
1415
	 *
1416
     * @throws RestException 400
1417
     * @throws RestException 401
1418
     * @throws RestException 403
1419
     * @throws RestException 404
1420
     */
1421
    public function addPaymentDistributed($arrayofamounts, $datepaye, $paymentid, $closepaidinvoices, $accountid, $num_payment = '', $comment = '', $chqemetteur = '', $chqbank = '')
1422
    {
1423
        global $conf;
1424
1425
        require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
1426
1427
        if (!DolibarrApiAccess::$user->rights->facture->creer) {
1428
            throw new RestException(403);
1429
        }
1430
        foreach ($arrayofamounts as $id => $amount) {
1431
        	if (empty($id)) {
1432
        		throw new RestException(400, 'Invoice ID is mandatory. Fill the invoice id and amount into arrayofamounts parameter. For example: {"1": "99.99", "2": "10"}');
1433
        	}
1434
        	if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1435
        		throw new RestException(403, 'Access not allowed on invoice ID '.$id.' for login '.DolibarrApiAccess::$user->login);
1436
        	}
1437
        }
1438
1439
        if (!empty($conf->banque->enabled)) {
1440
        	if (empty($accountid)) {
1441
        		throw new RestException(400, 'Account ID is mandatory');
1442
        	}
1443
        }
1444
        if (empty($paymentid)) {
1445
        	throw new RestException(400, 'Payment ID or Payment Code is mandatory');
1446
        }
1447
1448
        $this->db->begin();
1449
1450
        $amounts = array();
1451
        $multicurrency_amounts = array();
1452
1453
        // Loop on each invoice to pay
1454
        foreach ($arrayofamounts as $id => $amount)
1455
        {
1456
        	$result = $this->invoice->fetch($id);
1457
        	if (!$result) {
1458
        		$this->db->rollback();
1459
        		throw new RestException(404, 'Invoice ID '.$id.' not found');
1460
        	}
1461
1462
        	// Calculate amount to pay
1463
        	$totalpaye = $this->invoice->getSommePaiement();
1464
        	$totalcreditnotes = $this->invoice->getSumCreditNotesUsed();
1465
        	$totaldeposits = $this->invoice->getSumDepositsUsed();
1466
        	$resteapayer = price2num($this->invoice->total_ttc - $totalpaye - $totalcreditnotes - $totaldeposits, 'MT');
1467
        	if ($amount != 'remain')
1468
        	{
1469
        		if ($amount > $resteapayer)
1470
        		{
1471
        			$this->db->rollback();
1472
        			throw new RestException(400, 'Payment amount on invoice ID '.$id.' ('.$amount.') is higher than remain to pay ('.$resteapayer.')');
1473
        		}
1474
        		$resteapayer = $amount;
1475
        	}
1476
            // Clean parameters amount if payment is for a credit note
1477
            if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1478
                $resteapayer = price2num($resteapayer, 'MT');
1479
                $amounts[$id] = -$resteapayer;
1480
                // Multicurrency
1481
                $newvalue = price2num($this->invoice->multicurrency_total_ttc, 'MT');
1482
                $multicurrency_amounts[$id] = -$newvalue;
1483
            } else {
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
            }
1490
        }
1491
1492
        // Creation of payment line
1493
        $paymentobj = new Paiement($this->db);
1494
        $paymentobj->datepaye     = $datepaye;
1495
        $paymentobj->amounts      = $amounts; // Array with all payments dispatching with invoice id
1496
        $paymentobj->multicurrency_amounts = $multicurrency_amounts; // Array with all payments dispatching
1497
        $paymentobj->paiementid   = $paymentid;
1498
        $paymentobj->paiementcode = dol_getIdFromCode($this->db, $paymentid, 'c_paiement', 'id', 'code', 1);
1499
        $paymentobj->num_payment  = $num_payment;
1500
        $paymentobj->note_private = $comment;
1501
1502
        $payment_id = $paymentobj->create(DolibarrApiAccess::$user, ($closepaidinvoices == 'yes' ? 1 : 0)); // This include closing invoices
1503
        if ($payment_id < 0)
1504
        {
1505
            $this->db->rollback();
1506
            throw new RestException(400, 'Payment error : '.$paymentobj->error);
1507
        }
1508
        if (!empty($conf->banque->enabled)) {
1509
            $label = '(CustomerInvoicePayment)';
1510
            if ($paymentobj->paiementcode == 'CHQ' && empty($chqemetteur)) {
1511
                  throw new RestException(400, 'Emetteur is mandatory when payment code is '.$paymentobj->paiementcode);
1512
            }
1513
            if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) $label = '(CustomerInvoicePaymentBack)'; // Refund of a credit note
1514
            $result = $paymentobj->addPaymentToBank(DolibarrApiAccess::$user, 'payment', $label, $accountid, $chqemetteur, $chqbank);
1515
            if ($result < 0)
1516
            {
1517
                $this->db->rollback();
1518
                throw new RestException(400, 'Add payment to bank error : '.$paymentobj->error);
1519
            }
1520
        }
1521
1522
        $this->db->commit();
1523
1524
        return $payment_id;
1525
    }
1526
1527
    /**
1528
     * Update a payment
1529
     *
1530
     * @param int       $id             Id of payment
1531
     * @param string    $num_payment    Payment number
1532
     *
1533
     * @url     PUT payments/{id}
1534
     *
1535
     * @return array
1536
	 * @throws RestException 400 Bad parameters
1537
	 * @throws RestException 401 Not allowed
1538
	 * @throws RestException 404 Not found
1539
     */
1540
    public function putPayment($id, $num_payment = '')
1541
    {
1542
        require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
1543
1544
        if (!DolibarrApiAccess::$user->rights->facture->creer) {
1545
            throw new RestException(401);
1546
        }
1547
        if (empty($id)) {
1548
            throw new RestException(400, 'Payment ID is mandatory');
1549
        }
1550
1551
        $paymentobj = new Paiement($this->db);
1552
        $result = $paymentobj->fetch($id);
1553
1554
        if (!$result) {
1555
            throw new RestException(404, 'Payment not found');
1556
        }
1557
1558
        if (!empty($num_payment)) {
1559
            $result = $paymentobj->update_num($num_payment);
1560
            if ($result < 0) {
1561
                throw new RestException(500, 'Error when updating the payment num');
1562
            }
1563
        }
1564
1565
        return [
1566
            'success' => [
1567
                'code' => 200,
1568
                'message' => 'Payment updated'
1569
            ]
1570
        ];
1571
    }
1572
1573
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1574
    /**
1575
     * Clean sensible object datas
1576
     *
1577
     * @param   object  $object    Object to clean
1578
     * @return    array    Array of cleaned object properties
1579
     */
1580
    protected function _cleanObjectDatas($object)
1581
    {
1582
        // phpcs:enable
1583
        $object = parent::_cleanObjectDatas($object);
1584
1585
        unset($object->note);
1586
        unset($object->address);
1587
        unset($object->barcode_type);
1588
        unset($object->barcode_type_code);
1589
        unset($object->barcode_type_label);
1590
        unset($object->barcode_type_coder);
1591
1592
        return $object;
1593
    }
1594
1595
    /**
1596
     * Validate fields before create or update object
1597
     *
1598
     * @param array|null    $data       Datas to validate
1599
     * @return array
1600
     *
1601
     * @throws RestException
1602
     */
1603
    private function _validate($data)
1604
    {
1605
        $invoice = array();
1606
        foreach (Invoices::$FIELDS as $field) {
1607
            if (!isset($data[$field])) {
1608
                throw new RestException(400, "$field field missing");
1609
            }
1610
            $invoice[$field] = $data[$field];
1611
        }
1612
        return $invoice;
1613
    }
1614
}
1615