Passed
Branch develop (5cbde9)
by
unknown
26:38
created

Invoices::validate()   B

Complexity

Conditions 8
Paths 8

Size

Total Lines 32
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 18
c 0
b 0
f 0
nc 8
nop 3
dl 0
loc 32
rs 8.4444
1
<?php
2
/* Copyright (C) 2015   Jean-François Ferry     <[email protected]>
3
 *
4
 * This program is free software; you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation; either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16
 */
17
18
use Luracast\Restler\RestException;
19
20
require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
21
22
/**
23
 * API class for invoices
24
 *
25
 * @access protected
26
 * @class  DolibarrApiAccess {@requires user,external}
27
 */
28
class Invoices extends DolibarrApi
29
{
30
    /**
31
     *
32
     * @var array   $FIELDS     Mandatory fields, checked when create and update object
33
     */
34
    static $FIELDS = array(
35
        'socid',
36
    );
37
38
    /**
39
     * @var Facture $invoice {@type Facture}
40
     */
41
    public $invoice;
42
43
    /**
44
     * Constructor
45
     */
46
    public function __construct()
47
    {
48
        global $db, $conf;
49
        $this->db = $db;
50
        $this->invoice = new Facture($this->db);
51
    }
52
53
    /**
54
     * Get properties of a invoice object
55
     *
56
     * Return an array with invoice informations
57
     *
58
     * @param 	int 	$id           ID of invoice
59
     * @param   int     $contact_list 0:Return array contains all properties, 1:Return array contains just id
60
     * @return 	array|mixed data without useless information
61
     *
62
     * @throws 	RestException
63
     */
64
	public function get($id, $contact_list = 1)
65
	{
66
		if(! DolibarrApiAccess::$user->rights->facture->lire) {
67
			throw new RestException(401);
68
		}
69
70
		$result = $this->invoice->fetch($id);
71
		if (! $result) {
72
			throw new RestException(404, 'Invoice not found');
73
		}
74
75
		// Get payment details
76
		$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...
77
		$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...
78
		$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...
79
		$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...
80
81
		if (! DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
82
			throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
83
		}
84
85
		// Add external contacts ids
86
		$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...
87
88
		$this->invoice->fetchObjectLinked();
89
		return $this->_cleanObjectDatas($this->invoice);
90
	}
91
92
    /**
93
     * List invoices
94
     *
95
     * Get a list of invoices
96
     *
97
     * @param string	$sortfield	      Sort field
98
     * @param string	$sortorder	      Sort order
99
     * @param int		$limit		      Limit for list
100
     * @param int		$page		      Page number
101
     * @param string   	$thirdparty_ids	  Thirdparty ids to filter orders of. {@example '1' or '1,2,3'} {@pattern /^[0-9,]*$/i}
102
     * @param string	$status		      Filter by invoice status : draft | unpaid | paid | cancelled
103
     * @param string    $sqlfilters       Other criteria to filter answers separated by a comma. Syntax example "(t.ref:like:'SO-%') and (t.date_creation:<:'20160101')"
104
     * @return array                      Array of invoice objects
105
     *
106
	 * @throws RestException
107
     */
108
    public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $thirdparty_ids = '', $status = '', $sqlfilters = '')
109
    {
110
        global $db, $conf;
111
112
        $obj_ret = array();
113
114
        // case of external user, $thirdparty_ids param is ignored and replaced by user's socid
115
        $socids = DolibarrApiAccess::$user->societe_id ? DolibarrApiAccess::$user->societe_id : $thirdparty_ids;
1 ignored issue
show
Deprecated Code introduced by
The property User::$societe_id 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

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

1318
    	/** @scrutinizer ignore-deprecated */ $paiement->num_paiement = $num_paiement;
Loading history...
1319
    	$paiement->note = $comment;
1 ignored issue
show
Deprecated Code introduced by
The property CommonObject::$note 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

1319
    	/** @scrutinizer ignore-deprecated */ $paiement->note = $comment;
Loading history...
1320
1321
    	$paiement_id = $paiement->create(DolibarrApiAccess::$user, ($closepaidinvoices=='yes'?1:0));    // This include closing invoices
1322
    	if ($paiement_id < 0)
1323
    	{
1324
    		$this->db->rollback();
1325
    		throw new RestException(400, 'Payment error : '.$paiement->error);
1326
    	}
1327
1328
    	if (! empty($conf->banque->enabled)) {
1329
    		$label='(CustomerInvoicePayment)';
1330
1331
    		if($paiement->paiementcode == 'CHQ' && empty($chqemetteur)) {
1332
    			throw new RestException(400, 'Emetteur is mandatory when payment code is '.$paiement->paiementcode);
1333
    		}
1334
    		if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) $label='(CustomerInvoicePaymentBack)';  // Refund of a credit note
1335
    		$result=$paiement->addPaymentToBank(DolibarrApiAccess::$user, 'payment', $label, $accountid, $chqemetteur, $chqbank);
1336
    		if ($result < 0)
1337
    		{
1338
    			$this->db->rollback();
1339
    			throw new RestException(400, 'Add payment to bank error : '.$paiement->error);
1340
    		}
1341
    	}
1342
1343
    	$this->db->commit();
1344
1345
    	return $paiement_id;
1346
    }
1347
1348
    /**
1349
     * Add a payment to pay partially or completely one or several invoices.
1350
     * Warning: Take care that all invoices are owned by the same customer.
1351
     * Example of value for parameter arrayofamounts: {"1": "99.99", "2": "10"}
1352
     *
1353
     * @param array   $arrayofamounts     {@from body}  Array with id of invoices with amount to pay for each invoice
1354
     * @param string  $datepaye           {@from body}  Payment date        {@type timestamp}
1355
     * @param int     $paiementid         {@from body}  Payment mode Id {@min 1}
1356
     * @param string  $closepaidinvoices  {@from body}  Close paid invoices {@choice yes,no}
1357
     * @param int     $accountid          {@from body}  Account Id {@min 1}
1358
     * @param string  $num_paiement       {@from body}  Payment number (optional)
1359
     * @param string  $comment            {@from body}  Note (optional)
1360
     * @param string  $chqemetteur        {@from body}  Payment issuer (mandatory if paiementcode = 'CHQ')
1361
     * @param string  $chqbank            {@from body}  Issuer bank name (optional)
1362
     *
1363
     * @url     POST /paymentsdistributed
1364
     *
1365
     * @return int  Payment ID
1366
     * @throws 400
1367
     * @throws 401
1368
     * @throws 403
1369
     * @throws 404
1370
     */
1371
    public function addPaymentDistributed($arrayofamounts, $datepaye, $paiementid, $closepaidinvoices, $accountid, $num_paiement = '', $comment = '', $chqemetteur = '', $chqbank = '')
1372
    {
1373
        global $conf;
1374
1375
        require_once DOL_DOCUMENT_ROOT . '/compta/paiement/class/paiement.class.php';
1376
1377
        if(! DolibarrApiAccess::$user->rights->facture->creer) {
1378
            throw new RestException(403);
1379
        }
1380
        foreach($arrayofamounts as $id => $amount) {
1381
        	if(empty($id)) {
1382
        		throw new RestException(400, 'Invoice ID is mandatory. Fill the invoice id and amount into arrayofamounts parameter. For example: {"1": "99.99", "2": "10"}');
1383
        	}
1384
        	if( ! DolibarrApi::_checkAccessToResource('facture', $id)) {
1385
        		throw new RestException(403, 'Access not allowed on invoice ID '.$id.' for login '.DolibarrApiAccess::$user->login);
1386
        	}
1387
        }
1388
1389
        if (! empty($conf->banque->enabled)) {
1390
        	if(empty($accountid)) {
1391
        		throw new RestException(400, 'Account ID is mandatory');
1392
        	}
1393
        }
1394
        if(empty($paiementid)) {
1395
        	throw new RestException(400, 'Paiement ID or Paiement Code is mandatory');
1396
        }
1397
1398
        $this->db->begin();
1399
1400
        $amounts = array();
1401
        $multicurrency_amounts = array();
1402
1403
        // Loop on each invoice to pay
1404
        foreach($arrayofamounts as $id => $amount)
1405
        {
1406
        	$result = $this->invoice->fetch($id);
1407
        	if( ! $result ) {
1408
        		$this->db->rollback();
1409
        		throw new RestException(404, 'Invoice ID '.$id.' not found');
1410
        	}
1411
1412
        	// Calculate amount to pay
1413
        	$totalpaye = $this->invoice->getSommePaiement();
1414
        	$totalcreditnotes = $this->invoice->getSumCreditNotesUsed();
1415
        	$totaldeposits = $this->invoice->getSumDepositsUsed();
1416
        	$resteapayer = price2num($this->invoice->total_ttc - $totalpaye - $totalcreditnotes - $totaldeposits, 'MT');
1417
        	if ($amount != 'remain')
1418
        	{
1419
        		if ($amount > $resteapayer)
1420
        		{
1421
        			$this->db->rollback();
1422
        			throw new RestException(400, 'Payment amount on invoice ID '.$id.' ('.$amount.') is higher than remain to pay ('.$resteapayer.')');
1423
        		}
1424
        		$resteapayer = $amount;
1425
        	}
1426
            // Clean parameters amount if payment is for a credit note
1427
            if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1428
                $resteapayer = price2num($resteapayer, 'MT');
1429
                $amounts[$id] = -$resteapayer;
1430
                // Multicurrency
1431
                $newvalue = price2num($this->invoice->multicurrency_total_ttc, 'MT');
1432
                $multicurrency_amounts[$id] = -$newvalue;
1433
            } else {
1434
                $resteapayer = price2num($resteapayer, 'MT');
1435
                $amounts[$id] = $resteapayer;
1436
                // Multicurrency
1437
                $newvalue = price2num($this->invoice->multicurrency_total_ttc, 'MT');
1438
                $multicurrency_amounts[$id] = $newvalue;
1439
            }
1440
        }
1441
1442
        // Creation of payment line
1443
        $paiement = new Paiement($this->db);
1444
        $paiement->datepaye     = $datepaye;
1445
        $paiement->amounts      = $amounts;                           // Array with all payments dispatching with invoice id
1446
        $paiement->multicurrency_amounts = $multicurrency_amounts;    // Array with all payments dispatching
1447
        $paiement->paiementid   = $paiementid;
1448
        $paiement->paiementcode = dol_getIdFromCode($this->db, $paiementid, 'c_paiement', 'id', 'code', 1);
0 ignored issues
show
Bug introduced by
The property paiementcode does not exist on Paiement. Did you mean paiementid?
Loading history...
1449
        $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

1449
        /** @scrutinizer ignore-deprecated */ $paiement->num_paiement = $num_paiement;
Loading history...
1450
        $paiement->note         = $comment;
1 ignored issue
show
Deprecated Code introduced by
The property CommonObject::$note 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

1450
        /** @scrutinizer ignore-deprecated */ $paiement->note         = $comment;
Loading history...
1451
        $paiement_id = $paiement->create(DolibarrApiAccess::$user, ($closepaidinvoices=='yes'?1:0));    // This include closing invoices
1452
        if ($paiement_id < 0)
1453
        {
1454
            $this->db->rollback();
1455
            throw new RestException(400, 'Payment error : '.$paiement->error);
1456
        }
1457
        if (! empty($conf->banque->enabled)) {
1458
            $label='(CustomerInvoicePayment)';
1459
            if($paiement->paiementcode == 'CHQ' && empty($chqemetteur)) {
1460
                  throw new RestException(400, 'Emetteur is mandatory when payment code is '.$paiement->paiementcode);
1461
            }
1462
            if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) $label='(CustomerInvoicePaymentBack)';  // Refund of a credit note
1463
            $result=$paiement->addPaymentToBank(DolibarrApiAccess::$user, 'payment', $label, $accountid, $chqemetteur, $chqbank);
1464
            if ($result < 0)
1465
            {
1466
                $this->db->rollback();
1467
                throw new RestException(400, 'Add payment to bank error : '.$paiement->error);
1468
            }
1469
        }
1470
1471
        $this->db->commit();
1472
1473
        return $paiement_id;
1474
    }
1475
1476
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1477
    /**
1478
     * Clean sensible object datas
1479
     *
1480
     * @param   object  $object    Object to clean
1481
     * @return    array    Array of cleaned object properties
1482
     */
1483
    protected function _cleanObjectDatas($object)
1484
    {
1485
        // phpcs:enable
1486
        $object = parent::_cleanObjectDatas($object);
1487
1488
        unset($object->note);
1489
        unset($object->address);
1490
        unset($object->barcode_type);
1491
        unset($object->barcode_type_code);
1492
        unset($object->barcode_type_label);
1493
        unset($object->barcode_type_coder);
1494
1495
        return $object;
1496
    }
1497
1498
    /**
1499
     * Validate fields before create or update object
1500
     *
1501
     * @param array|null    $data       Datas to validate
1502
     * @return array
1503
     *
1504
     * @throws RestException
1505
     */
1506
    private function _validate($data)
1507
    {
1508
        $invoice = array();
1509
        foreach (Invoices::$FIELDS as $field) {
1510
            if (!isset($data[$field])) {
1511
                throw new RestException(400, "$field field missing");
1512
            }
1513
            $invoice[$field] = $data[$field];
1514
        }
1515
        return $invoice;
1516
    }
1517
}
1518