dolibarr 23.0.3
api_invoices.class.php
1<?php
2/* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
3 * Copyright (C) 2020 Thibault FOUCART <support@ptibogxiv.net>
4 * Copyright (C) 2023 Joachim Kueter <git-jk@bloxera.com>
5 * Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
6 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
7 * Copyright (C) 2025 Charlene Benke <charlene@patas-monkey.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
23use Luracast\Restler\RestException;
24
25require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
26require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture-rec.class.php';
27
28
35class Invoices extends DolibarrApi
36{
40 public static $FIELDS = array(
41 'socid',
42 );
43
47 private $invoice;
48
52 private $template_invoice;
53
54
58 public function __construct()
59 {
60 global $db;
61 $this->db = $db;
62 $this->invoice = new Facture($this->db);
63 $this->template_invoice = new FactureRec($this->db);
64 }
65
79 public function get($id, $contact_list = 1, $properties = '', $withLines = true)
80 {
81 $invoice = $this->_fetch($id, '', '', $contact_list);
82
83 if (!$withLines) {
84 unset($invoice->lines);
85 }
86
87 return $this->_filterObjectProperties($invoice, $properties);
88 }
89
103 public function getByRef($ref, $contact_list = 1)
104 {
105 return $this->_fetch(0, $ref, '', $contact_list);
106 }
107
121 public function getByRefExt($ref_ext, $contact_list = 1)
122 {
123 return $this->_fetch(0, '', $ref_ext, $contact_list);
124 }
125
139 private function _fetch($id, $ref = '', $ref_ext = '', $contact_list = 1)
140 {
141 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
142 throw new RestException(403);
143 }
144 if (empty($id) && empty($ref)&& empty($ref_ext)) {
145 throw new RestException(400, 'No invoice can be found with no criteria');
146 }
147 $result = $this->invoice->fetch($id, $ref, $ref_ext);
148 if (!$result) {
149 throw new RestException(404, 'Invoice not found');
150 }
151
152 // Get payment details
153 $this->invoice->totalpaid = $this->invoice->getSommePaiement();
154 $this->invoice->totalcreditnotes = $this->invoice->getSumCreditNotesUsed();
155 $this->invoice->totaldeposits = $this->invoice->getSumDepositsUsed();
156 $this->invoice->remaintopay = price2num($this->invoice->total_ttc - $this->invoice->totalpaid - $this->invoice->totalcreditnotes - $this->invoice->totaldeposits, 'MT');
157
158 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
159 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
160 }
161
162 // Retrieve credit note ids
163 $this->invoice->getListIdAvoirFromInvoice();
164
165 // Add external contacts ids
166 if ($contact_list > -1) {
167 $tmparray = $this->invoice->liste_contact(-1, 'external', $contact_list);
168 if (is_array($tmparray)) {
169 $this->invoice->contacts_ids = $tmparray;
170 }
171 $tmparray = $this->invoice->liste_contact(-1, 'internal', $contact_list);
172 if (is_array($tmparray)) {
173 $this->invoice->contacts_ids = $tmparray;
174 }
175 }
176
177 $this->invoice->fetchObjectLinked();
178
179 // Add online_payment_url, copied from order
180 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
181 $this->invoice->online_payment_url = getOnlinePaymentUrl(0, 'invoice', $this->invoice->ref);
182
183 return $this->_cleanObjectDatas($this->invoice);
184 }
185
209 public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $thirdparty_ids = '', $status = '', $sqlfilters = '', $properties = '', $pagination_data = false, $loadlinkedobjects = 0, $withLines = true)
210 {
211 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
212 throw new RestException(403);
213 }
214
215 $obj_ret = array();
216
217 // case of external user, $thirdparty_ids param is ignored and replaced by user's socid
218 $socids = DolibarrApiAccess::$user->socid ?: $thirdparty_ids;
219
220 // If the internal user must only see his customers, force searching by him
221 $search_sale = 0;
222 if (!DolibarrApiAccess::$user->hasRight('societe', 'client', 'voir') && !$socids) {
223 $search_sale = DolibarrApiAccess::$user->id;
224 }
225
226 $sql = "SELECT t.rowid";
227 $sql .= " FROM ".MAIN_DB_PREFIX."facture AS t";
228 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."societe AS s ON (s.rowid = t.fk_soc)";
229 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facture_extrafields AS ef ON (ef.fk_object = t.rowid)"; // Modification VMR Global Solutions to include extrafields as search parameters in the API GET call, so we will be able to filter on extrafields
230 $sql .= ' WHERE t.entity IN ('.getEntity('invoice').')';
231 if ($socids) {
232 $sql .= " AND t.fk_soc IN (".$this->db->sanitize($socids).")";
233 }
234 // Search on sale representative
235 if ($search_sale && $search_sale != '-1') {
236 if ($search_sale == -2) {
237 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = t.fk_soc)";
238 } elseif ($search_sale > 0) {
239 $sql .= " AND EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = t.fk_soc AND sc.fk_user = ".((int) $search_sale).")";
240 }
241 }
242 // Filter by status
243 if ($status == 'draft') {
244 $sql .= " AND t.fk_statut IN (0)";
245 }
246 if ($status == 'unpaid') {
247 $sql .= " AND t.fk_statut IN (1)";
248 }
249 if ($status == 'paid') {
250 $sql .= " AND t.fk_statut IN (2)";
251 }
252 if ($status == 'cancelled') {
253 $sql .= " AND t.fk_statut IN (3)";
254 }
255 // Add sql filters
256 if ($sqlfilters) {
257 $errormessage = '';
258 $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
259 if ($errormessage) {
260 throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);
261 }
262 }
263
264 //this query will return total invoices with the filters given
265 $sqlTotals = str_replace('SELECT t.rowid', 'SELECT count(t.rowid) as total', $sql);
266
267 $sql .= $this->db->order($sortfield, $sortorder);
268 if ($limit) {
269 if ($page < 0) {
270 $page = 0;
271 }
272 $offset = $limit * $page;
273
274 $sql .= $this->db->plimit($limit + 1, $offset);
275 }
276
277 $result = $this->db->query($sql);
278 if ($result) {
279 $i = 0;
280 $num = $this->db->num_rows($result);
281 $min = min($num, ($limit <= 0 ? $num : $limit));
282 while ($i < $min) {
283 $obj = $this->db->fetch_object($result);
284 $invoice_static = new Facture($this->db);
285 if ($invoice_static->fetch($obj->rowid) > 0) {
286 // Get payment details
287 $invoice_static->totalpaid = $invoice_static->getSommePaiement();
288 $invoice_static->totalcreditnotes = $invoice_static->getSumCreditNotesUsed();
289 $invoice_static->totaldeposits = $invoice_static->getSumDepositsUsed();
290 $invoice_static->remaintopay = price2num($invoice_static->total_ttc - $invoice_static->totalpaid - $invoice_static->totalcreditnotes - $invoice_static->totaldeposits, 'MT');
291
292 // Retrieve credit note ids
293 $invoice_static->getListIdAvoirFromInvoice();
294
295 // Add external contacts ids
296 $tmparray = $invoice_static->liste_contact(-1, 'external', 1);
297 if (is_array($tmparray)) {
298 $invoice_static->contacts_ids = $tmparray;
299 }
300
301 if ($loadlinkedobjects) {
302 // retrieve linked objects
303 $invoice_static->fetchObjectLinked();
304 }
305
306 if (!$withLines) {
307 unset($invoice_static->lines);
308 }
309
310 // Add online_payment_url, copied from order
311 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
312 $invoice_static->online_payment_url = getOnlinePaymentUrl(0, 'invoice', $invoice_static->ref);
313
314 $obj_ret[] = $this->_filterObjectProperties($this->_cleanObjectDatas($invoice_static), $properties);
315 }
316 $i++;
317 }
318 } else {
319 throw new RestException(503, 'Error when retrieve invoice list : '.$this->db->lasterror());
320 }
321
322 //if $pagination_data is true the response will contain element data with all values and element pagination with pagination data(total,page,limit)
323 if ($pagination_data) {
324 $totalsResult = $this->db->query($sqlTotals);
325 $total = $this->db->fetch_object($totalsResult)->total;
326
327 $tmp = $obj_ret;
328 $obj_ret = [];
329
330 $obj_ret['data'] = $tmp;
331 $obj_ret['pagination'] = [
332 'total' => (int) $total,
333 'page' => $page, //count starts from 0
334 'page_count' => ceil((int) $total / $limit),
335 'limit' => $limit
336 ];
337 }
338
339 return $obj_ret;
340 }
341
350 public function post($request_data = null)
351 {
352 global $conf;
353 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
354 throw new RestException(403, "Insufficiant rights");
355 }
356
357 if (!is_array($request_data)) {
358 $request_data = array();
359 }
360
361 // Check mandatory fields (not using output, only possible exception is important)
362 $this->_validate($request_data);
363
364 // Check thirdparty validity
365 $socid = (int) $request_data['socid'];
366 $thirdpartytmp = new Societe($this->db);
367 $thirdparty_result = $thirdpartytmp->fetch($socid);
368 if ($thirdparty_result < 1) {
369 throw new RestException(404, 'Thirdparty with id='.$socid.' not found or not allowed');
370 }
371 if (!DolibarrApi::_checkAccessToResource('societe', $thirdpartytmp->id)) {
372 throw new RestException(404, 'Thirdparty with id='.$thirdpartytmp->id.' not found or not allowed');
373 }
374
375 foreach ($request_data as $field => $value) {
376 if ($field === 'caller') {
377 // Add a mention of caller so on trigger called after action, we can filter to avoid a loop if we try to sync back again with the caller
378 $this->invoice->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
379 continue;
380 }
381 if ($field == 'id') {
382 throw new RestException(400, 'Creating with id field is forbidden');
383 }
384 if ($field == 'entity' && ((int) $value) != ((int) $conf->entity)) {
385 throw new RestException(403, 'Creating with entity='.((int) $value).' MUST be the same entity='.((int) $conf->entity).' as your API user/key belongs to');
386 }
387
388 $this->invoice->$field = $this->_checkValForAPI($field, $value, $this->invoice);
389 }
390 if (!array_key_exists('date', $request_data)) {
391 $this->invoice->date = dol_now();
392 }
393 /* We keep lines as an array
394 if (isset($request_data["lines"])) {
395 $lines = array();
396 foreach ($request_data["lines"] as $line) {
397 array_push($lines, (object) $line);
398 }
399 $this->invoice->lines = $lines;
400 }*/
401
402 if ($this->invoice->create(DolibarrApiAccess::$user, 0, (empty($request_data["date_lim_reglement"]) ? 0 : $request_data["date_lim_reglement"])) < 0) {
403 throw new RestException(500, "Error creating invoice", array_merge(array($this->invoice->error), $this->invoice->errors));
404 }
405 return ((int) $this->invoice->id);
406 }
407
422 public function createInvoiceFromOrder($orderid)
423 {
424 require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
425
426 if (!DolibarrApiAccess::$user->hasRight('commande', 'lire')) {
427 throw new RestException(403);
428 }
429 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
430 throw new RestException(403);
431 }
432 if (empty($orderid)) {
433 throw new RestException(400, 'Order ID is mandatory');
434 }
435 if (!DolibarrApi::_checkAccessToResource('commande', $orderid)) {
436 throw new RestException(403, 'Access not allowed on order for login '.DolibarrApiAccess::$user->login);
437 }
438
439 $order = new Commande($this->db);
440 $result = $order->fetch($orderid);
441 if (!$result) {
442 throw new RestException(404, 'Order not found');
443 }
444
445 $result = $this->invoice->createFromOrder($order, DolibarrApiAccess::$user);
446 if ($result < 0) {
447 throw new RestException(405, $this->invoice->error);
448 }
449 $this->invoice->fetchObjectLinked();
450 return $this->_cleanObjectDatas($this->invoice);
451 }
452
466 public function createInvoiceFromContract($contractid)
467 {
468 require_once DOL_DOCUMENT_ROOT.'/contrat/class/contrat.class.php';
469
470 if (!DolibarrApiAccess::$user->hasRight('contrat', 'lire')) {
471 throw new RestException(403);
472 }
473 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
474 throw new RestException(403);
475 }
476 if (empty($contractid)) {
477 throw new RestException(400, 'Contract ID is mandatory');
478 }
479
480 $contract = new Contrat($this->db);
481 $result = $contract->fetch($contractid);
482 if (!$result) {
483 throw new RestException(404, 'Contract not found');
484 }
485
486 $result = $this->invoice->createFromContract($contract, DolibarrApiAccess::$user);
487 if ($result < 0) {
488 throw new RestException(405, $this->invoice->error);
489 }
490 $this->invoice->fetchObjectLinked();
491 return $this->_cleanObjectDatas($this->invoice);
492 }
493
504 public function getLines($id)
505 {
506 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
507 throw new RestException(403);
508 }
509
510 $result = $this->invoice->fetch($id);
511 if (!$result) {
512 throw new RestException(404, 'Invoice not found');
513 }
514
515 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
516 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
517 }
518 $this->invoice->getLinesArray();
519 $result = array();
520 foreach ($this->invoice->lines as $line) {
521 array_push($result, $this->_cleanObjectDatas($line));
522 }
523 return $result;
524 }
525
542 public function putLine($id, $lineid, $request_data = null)
543 {
544 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
545 throw new RestException(403);
546 }
547
548 $result = $this->invoice->fetch($id);
549 if (!$result) {
550 throw new RestException(404, 'Invoice not found');
551 }
552
553 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
554 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
555 }
556
557 $request_data = (object) $request_data;
558
559 $request_data->desc = sanitizeVal($request_data->desc, 'restricthtml');
560 $request_data->label = sanitizeVal($request_data->label);
561
562 $updateRes = $this->invoice->updateline(
563 $lineid,
564 $request_data->desc,
565 $request_data->subprice,
566 $request_data->qty,
567 $request_data->remise_percent,
568 $request_data->date_start,
569 $request_data->date_end,
570 $request_data->tva_tx,
571 $request_data->localtax1_tx,
572 $request_data->localtax2_tx,
573 $request_data->price_base_type ? $request_data->price_base_type : 'HT',
574 $request_data->info_bits,
575 $request_data->product_type,
576 $request_data->fk_parent_line,
577 0,
578 $request_data->fk_fournprice,
579 $request_data->pa_ht,
580 $request_data->label,
581 $request_data->special_code,
582 $request_data->array_options,
583 $request_data->situation_percent,
584 $request_data->fk_unit,
585 $request_data->multicurrency_subprice,
586 0,
587 $request_data->ref_ext,
588 $request_data->rang
589 );
590
591 if ($updateRes > 0) {
592 $result = $this->get($id);
593 unset($result->line);
594 return $this->_cleanObjectDatas($result);
595 } else {
596 throw new RestException(304, $this->invoice->error);
597 }
598 }
599
617 public function postContact($id, $contactid, $type, $source = 'external', $notrigger = 0)
618 {
619 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
620 throw new RestException(403);
621 }
622
623 // test source
624 if (empty($source)) {
625 throw new RestException(400, 'Source can not be empty');
626 }
627 $sql_distinct_source = "SELECT DISTINCT source";
628 $sql_distinct_source .= " FROM ".MAIN_DB_PREFIX."c_type_contact";
629 $sql_distinct_source .= " WHERE element LIKE 'facture'";
630 $sql_distinct_source .= " AND source is NOT NULL";
631 $sql_distinct_source .= " AND active != 0";
632 $source_result = $this->db->query($sql_distinct_source);
633 $source_array = array();
634
635 if ($source_result) {
636 $num = $this->db->num_rows($source_result);
637 $i = 0;
638 while ($i < $num) {
639 $obj = $this->db->fetch_object($source_result);
640 $source_kind = (string) $obj->source;
641 array_push($source_array, $source_kind);
642 dol_syslog("source_kind=".$source_kind);
643 $i++;
644 }
645 } else {
646 throw new RestException(503, 'Error when retrieving a list of invoice contact sources: '.$this->db->lasterror());
647 }
648 if (!in_array($source, (array) $source_array, true)) {
649 throw new RestException(400, 'Combo of Source='.$source.' and Type='.$type.' not found in dictionary with active invoice contact types');
650 }
651
652 // test type
653 if (empty($type)) {
654 throw new RestException(400, 'type can not be empty');
655 }
656 // variable called type here, but code in dictionary and database
657 $sql_distinct_type = "SELECT DISTINCT code";
658 $sql_distinct_type .= " FROM ".MAIN_DB_PREFIX."c_type_contact";
659 $sql_distinct_type .= " WHERE element LIKE 'facture'";
660 $sql_distinct_type .= " AND source='".$this->db->escape($source)."'";
661 $sql_distinct_type .= " AND code is NOT NULL";
662 $sql_distinct_type .= " AND active != 0";
663 $type_result = $this->db->query($sql_distinct_type);
664 $type_array = array();
665
666 if ($type_result) {
667 $num = $this->db->num_rows($type_result);
668 $i = 0;
669 while ($i < $num) {
670 $obj = $this->db->fetch_object($type_result);
671 // variable called type here, but code in dictionary and database
672 $type_kind = (string) $obj->code;
673 array_push($type_array, $type_kind);
674 dol_syslog("type_kind=".$type_kind);
675 $i++;
676 }
677 } else {
678 throw new RestException(503, 'Error when retrieving a list of invoice contact types: '.$this->db->lasterror());
679 }
680 if (!in_array($type, (array) $type_array, true)) {
681 throw new RestException(400, 'Combo of Type='.$type.' and Source='.$source.' not found in dictionary with active invoice contact types');
682 }
683
684 // tests done, let's get it
685 $result = $this->invoice->fetch($id);
686 if (!$result) {
687 throw new RestException(404, 'Invoice not found');
688 }
689 if (!DolibarrApi::_checkAccessToResource('invoice', $this->invoice->id)) {
690 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
691 }
692
693 $result = $this->invoice->add_contact($contactid, $type, $source, $notrigger);
694
695 if ($result == 0) {
696 throw new RestException(400, 'Already exists: Contact='.$contactid.' is already linked to the invoice='.$id.' as source='.$source.' and type='.$type);
697 } elseif ($result == -1) {
698 throw new RestException(400, 'Wrong contact='.$contactid);
699 } elseif ($result == -2) {
700 throw new RestException(400, 'Wrong type='.$type);
701 } elseif ($result == -3) {
702 throw new RestException(400, 'Not allowed contacts');
703 } elseif ($result == -4) {
704 throw new RestException(400, 'ErrorCommercialNotAllowedForThirdparty');
705 } elseif ($result == -5) {
706 throw new RestException(400, 'Trigger failed');
707 } elseif ($result == -6) {
708 throw new RestException(400, 'DB_ERROR_RECORD_ALREADY_EXISTS');
709 } elseif ($result == -7) {
710 throw new RestException(400, 'Some other error');
711 }
712
713 if (!$result) {
714 throw new RestException(500, 'Error when added the contact');
715 }
716
717 return array(
718 'success' => array(
719 'code' => 200,
720 'message' => 'Contact='.$contactid.' linked to the invoice='.$id.' as '.$source.' '.$type
721 )
722 );
723 }
724
738 public function getContacts($id, $type = '')
739 {
740 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
741 throw new RestException(403);
742 }
743
744 $result = $this->invoice->fetch($id);
745 if (!$result) {
746 throw new RestException(404, 'Invoice not found');
747 }
748
749 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
750 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
751 }
752
753 $contacts = $this->invoice->liste_contact(-1, 'external', 0, $type);
754 $socpeoples = $this->invoice->liste_contact(-1, 'internal', 0, $type);
755
756 $contacts = array_merge($contacts, $socpeoples);
757
758 return $contacts;
759 }
760
775 public function deleteContact($id, $contactid, $type)
776 {
777 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
778 throw new RestException(403);
779 }
780
781 $result = $this->invoice->fetch($id);
782
783 if (!$result) {
784 throw new RestException(404, 'Invoice not found');
785 }
786
787 if (!DolibarrApi::_checkAccessToResource('invoice', $this->invoice->id)) {
788 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
789 }
790
791 $contacts = $this->invoice->liste_contact();
792
793 foreach ($contacts as $contact) {
794 if ($contact['id'] == $contactid && $contact['code'] == $type) {
795 $result = $this->invoice->delete_contact($contact['rowid']);
796
797 if (!$result) {
798 throw new RestException(500, 'Error when deleted the contact');
799 }
800 }
801 }
802
803 return $this->_cleanObjectDatas($this->invoice);
804 }
805
820 public function deleteLine($id, $lineid)
821 {
822 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
823 throw new RestException(403);
824 }
825 if (empty($lineid)) {
826 throw new RestException(400, 'Line ID is mandatory');
827 }
828
829 if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
830 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
831 }
832
833 $result = $this->invoice->fetch($id);
834 if (!$result) {
835 throw new RestException(404, 'Invoice not found');
836 }
837 if ($this->invoice->status != 0) {
838 throw new RestException(403, 'Invoice not in Draft Status : '.$this->invoice->getLibStatut(1));
839 }
840
841 $updateRes = $this->invoice->deleteLine($lineid, $id);
842 if ($updateRes > 0) {
843 return $this->get($id);
844 } else {
845 throw new RestException(405, $this->invoice->error);
846 }
847 }
848
858 public function put($id, $request_data = null)
859 {
860 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
861 throw new RestException(403);
862 }
863 if ($id == 0) {
864 throw new RestException(400, 'No invoice with id=0 can exist');
865 }
866 $result = $this->invoice->fetch($id);
867 if (!$result) {
868 throw new RestException(404, 'Invoice not found');
869 }
870
871 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
872 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
873 }
874
875 foreach ($request_data as $field => $value) {
876 if ($field == 'id') {
877 continue;
878 }
879 if ($field === 'caller') {
880 // Add a mention of caller so on trigger called after action, we can filter to avoid a loop if we try to sync back again with the caller
881 $this->invoice->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
882 continue;
883 }
884 if ($field == 'array_options' && is_array($value)) {
885 foreach ($value as $index => $val) {
886 $this->invoice->array_options[$index] = $this->_checkValForAPI($field, $val, $this->invoice);
887 }
888 continue;
889 }
890
891 $this->invoice->$field = $this->_checkValForAPI($field, $value, $this->invoice);
892
893 // If cond reglement => update date lim reglement
894 if ($field == 'cond_reglement_id') {
895 $this->invoice->date_lim_reglement = $this->invoice->calculate_date_lim_reglement();
896 }
897 }
898
899 // update bank account
900 if (!empty($this->invoice->fk_account)) {
901 if ($this->invoice->setBankAccount($this->invoice->fk_account) == 0) {
902 throw new RestException(400, $this->invoice->error);
903 }
904 }
905
906 if ($this->invoice->update(DolibarrApiAccess::$user) > 0) {
907 return $this->get($id);
908 } else {
909 throw new RestException(500, $this->invoice->error);
910 }
911 }
912
921 public function delete($id)
922 {
923 if (!DolibarrApiAccess::$user->hasRight('facture', 'supprimer')) {
924 throw new RestException(403);
925 }
926 if ($id == 0) {
927 throw new RestException(400, 'No invoice with id=0 can exist');
928 }
929 $result = $this->invoice->fetch($id);
930 if (!$result) {
931 throw new RestException(404, 'Invoice not found');
932 }
933
934 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
935 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
936 }
937
938 $result = $this->invoice->delete(DolibarrApiAccess::$user);
939 if ($result < 0) {
940 throw new RestException(500, 'Error when deleting invoice');
941 } elseif ($result == 0) {
942 throw new RestException(403, 'Invoice not erasable');
943 }
944
945 return array(
946 'success' => array(
947 'code' => 200,
948 'message' => 'Invoice deleted'
949 )
950 );
951 }
952
978 public function postLine($id, $request_data = null)
979 {
980 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
981 throw new RestException(403);
982 }
983
984 $result = $this->invoice->fetch($id);
985 if (!$result) {
986 throw new RestException(404, 'Invoice not found');
987 }
988
989 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
990 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
991 }
992
993 $request_data = (object) $request_data;
994
995 $request_data->desc = sanitizeVal($request_data->desc, 'restricthtml');
996 $request_data->label = sanitizeVal($request_data->label);
997
998 // Reset fk_parent_line for no child products and special product
999 if (($request_data->product_type != 9 && empty($request_data->fk_parent_line)) || $request_data->product_type == 9) {
1000 $request_data->fk_parent_line = 0;
1001 }
1002
1003 // calculate pa_ht
1004 $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);
1005 $pa_ht = $marginInfos[0];
1006
1007 $updateRes = $this->invoice->addline(
1008 $request_data->desc,
1009 $request_data->subprice,
1010 $request_data->qty,
1011 $request_data->tva_tx,
1012 $request_data->localtax1_tx,
1013 $request_data->localtax2_tx,
1014 $request_data->fk_product,
1015 $request_data->remise_percent,
1016 $request_data->date_start,
1017 $request_data->date_end,
1018 $request_data->fk_code_ventilation,
1019 $request_data->info_bits,
1020 $request_data->fk_remise_except,
1021 $request_data->price_base_type ? $request_data->price_base_type : 'HT',
1022 $request_data->subprice,
1023 $request_data->product_type,
1024 $request_data->rang,
1025 $request_data->special_code,
1026 $request_data->origin,
1027 $request_data->origin_id,
1028 $request_data->fk_parent_line,
1029 empty($request_data->fk_fournprice) ? null : $request_data->fk_fournprice,
1030 $pa_ht,
1031 $request_data->label,
1032 $request_data->array_options,
1033 $request_data->situation_percent,
1034 $request_data->fk_prev_id,
1035 $request_data->fk_unit,
1036 0,
1037 $request_data->ref_ext
1038 );
1039
1040 if ($updateRes < 0) {
1041 throw new RestException(400, 'Unable to insert the new line. Check your inputs. '.$this->invoice->error);
1042 }
1043
1044 return $updateRes;
1045 }
1046
1065 public function addContact($id, $fk_socpeople, $type_contact, $source, $notrigger = 0)
1066 {
1067 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1068 throw new RestException(403);
1069 }
1070 $result = $this->invoice->fetch($id);
1071 if (!$result) {
1072 throw new RestException(404, 'Invoice not found');
1073 }
1074
1075 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1076 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1077 }
1078
1079 $result = $this->invoice->add_contact($fk_socpeople, $type_contact, $source, $notrigger);
1080 if ($result < 0) {
1081 throw new RestException(500, 'Error : '.$this->invoice->error);
1082 }
1083
1084 $result = $this->invoice->fetch($id);
1085 if (!$result) {
1086 throw new RestException(404, 'Invoice not found');
1087 }
1088
1089 // test already done
1090 // if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1091 // throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1092 // }
1093
1094 return $this->_cleanObjectDatas($this->invoice);
1095 }
1096
1097
1098
1113 public function settodraft($id, $idwarehouse = -1)
1114 {
1115 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1116 throw new RestException(403);
1117 }
1118 $result = $this->invoice->fetch($id);
1119 if (!$result) {
1120 throw new RestException(404, 'Invoice not found');
1121 }
1122
1123 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1124 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1125 }
1126
1127 $result = $this->invoice->setDraft(DolibarrApiAccess::$user, $idwarehouse);
1128 if ($result == 0) {
1129 throw new RestException(304, 'Nothing done.');
1130 }
1131 if ($result < 0) {
1132 throw new RestException(500, 'Error : '.$this->invoice->error);
1133 }
1134
1135 $result = $this->invoice->fetch($id);
1136 if (!$result) {
1137 throw new RestException(404, 'Invoice not found');
1138 }
1139
1140 return $this->_cleanObjectDatas($this->invoice);
1141 }
1142
1143
1161 public function validate($id, $force_number = '', $idwarehouse = 0, $notrigger = 0)
1162 {
1163 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1164 throw new RestException(403);
1165 }
1166 $result = $this->invoice->fetch($id);
1167 if (!$result) {
1168 throw new RestException(404, 'Invoice not found');
1169 }
1170
1171 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1172 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1173 }
1174
1175 $result = $this->invoice->validate(DolibarrApiAccess::$user, $force_number, $idwarehouse, $notrigger);
1176 if ($result == 0) {
1177 throw new RestException(304, 'Error nothing done. May be object is already validated');
1178 }
1179 if ($result < 0) {
1180 throw new RestException(500, 'Error when validating Invoice: '.$this->invoice->error);
1181 }
1182
1183 $result = $this->invoice->fetch($id);
1184 if (!$result) {
1185 throw new RestException(404, 'Invoice not found');
1186 }
1187
1188 // test already done
1189 // if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1190 // throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1191 // }
1192
1193 // copy from order
1194 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
1195 $this->invoice->online_payment_url = getOnlinePaymentUrl(0, 'invoice', $this->invoice->ref);
1196
1197 return $this->_cleanObjectDatas($this->invoice);
1198 }
1199
1215 public function settopaid($id, $close_code = '', $close_note = '')
1216 {
1217 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1218 throw new RestException(403);
1219 }
1220 $result = $this->invoice->fetch($id);
1221 if (!$result) {
1222 throw new RestException(404, 'Invoice not found');
1223 }
1224
1225 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1226 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1227 }
1228
1229 $result = $this->invoice->setPaid(DolibarrApiAccess::$user, $close_code, $close_note);
1230 if ($result == 0) {
1231 throw new RestException(304, 'Error nothing done. May be object is already validated');
1232 }
1233 if ($result < 0) {
1234 throw new RestException(500, 'Error : '.$this->invoice->error);
1235 }
1236
1237
1238 $result = $this->invoice->fetch($id);
1239 if (!$result) {
1240 throw new RestException(404, 'Invoice not found');
1241 }
1242
1243 // test already done
1244 // if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1245 // throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1246 // }
1247
1248 return $this->_cleanObjectDatas($this->invoice);
1249 }
1250
1251
1265 public function settounpaid($id)
1266 {
1267 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1268 throw new RestException(403);
1269 }
1270 $result = $this->invoice->fetch($id);
1271 if (!$result) {
1272 throw new RestException(404, 'Invoice not found');
1273 }
1274
1275 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1276 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1277 }
1278
1279 $result = $this->invoice->setUnpaid(DolibarrApiAccess::$user);
1280 if ($result == 0) {
1281 throw new RestException(304, 'Nothing done');
1282 }
1283 if ($result < 0) {
1284 throw new RestException(500, 'Error : '.$this->invoice->error);
1285 }
1286
1287
1288 $result = $this->invoice->fetch($id);
1289 if (!$result) {
1290 throw new RestException(404, 'Invoice not found');
1291 }
1292
1293 // test already done
1294 // if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1295 // throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1296 // }
1297
1298 return $this->_cleanObjectDatas($this->invoice);
1299 }
1300
1309 public function getDiscount($id)
1310 {
1311 require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1312
1313 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
1314 throw new RestException(403);
1315 }
1316
1317 $result = $this->invoice->fetch($id);
1318 if (!$result) {
1319 throw new RestException(404, 'Invoice not found');
1320 }
1321
1322 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1323 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1324 }
1325
1326 $discountcheck = new DiscountAbsolute($this->db);
1327 $result = $discountcheck->fetch(0, $this->invoice->id);
1328
1329 if ($result == 0) {
1330 throw new RestException(404, 'Discount not found');
1331 }
1332 if ($result < 0) {
1333 throw new RestException(500, $discountcheck->error);
1334 }
1335
1336 return parent::_cleanObjectDatas($discountcheck);
1337 }
1338
1353 {
1354 require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1355
1356 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1357 throw new RestException(403);
1358 }
1359
1360 $result = $this->invoice->fetch($id);
1361 if (!$result) {
1362 throw new RestException(404, 'Invoice not found');
1363 }
1364
1365 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1366 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1367 }
1368
1369 if ($this->invoice->paye) { // TODO Replace by a test on status
1370 throw new RestException(500, 'Alreay paid');
1371 }
1372
1373 $this->invoice->fetch($id);
1374 $this->invoice->fetch_thirdparty();
1375
1376 // Check if there is already a discount (protection to avoid duplicate creation when resubmit post)
1377 $discountcheck = new DiscountAbsolute($this->db);
1378 $result = $discountcheck->fetch(0, $this->invoice->id);
1379
1380 $canconvert = 0;
1381 if ($this->invoice->type == Facture::TYPE_DEPOSIT && empty($discountcheck->id)) {
1382 $canconvert = 1; // we can convert deposit into discount if deposit is paid (completely, partially or not at all) and not already converted (see real condition into condition used to show button converttoreduc)
1383 }
1384 if (($this->invoice->type == Facture::TYPE_CREDIT_NOTE || $this->invoice->type == Facture::TYPE_STANDARD) && $this->invoice->paye == 0 && empty($discountcheck->id)) {
1385 $canconvert = 1; // we can convert credit note into discount if credit note is not paid back and not already converted and amount of payment is 0 (see real condition into condition used to show button converttoreduc)
1386 }
1387 if ($canconvert) {
1388 $this->db->begin();
1389
1390 $amount_ht = $amount_tva = $amount_ttc = array();
1391 $multicurrency_amount_ht = $multicurrency_amount_tva = $multicurrency_amount_ttc = array();
1392 '
1393 @phan-var-force array<string,float> $amount_ht
1394 @phan-var-force array<string,float> $amount_tva
1395 @phan-var-force array<string,float> $amount_ttc
1396 @phan-var-force array<string,float> $multicurrency_amount_ht
1397 @phan-var-force array<string,float> $multicurrency_amount_tva
1398 @phan-var-force array<string,float> $multicurrency_amount_ttc
1399 ';
1400
1401 // Loop on each vat rate
1402 $i = 0;
1403 foreach ($this->invoice->lines as $line) {
1404 if ($line->product_type < 9 && $line->total_ht != 0) { // Remove lines with product_type greater than or equal to 9
1405 if (!array_key_exists($line->tva_tx, $amount_ht)) {
1406 $amount_ht[$line->tva_tx] = 0.0;
1407 $amount_tva[$line->tva_tx] = 0.0;
1408 $amount_ttc[$line->tva_tx] = 0.0;
1409 $multicurrency_amount_ht[$line->tva_tx] = 0.0;
1410 $multicurrency_amount_tva[$line->tva_tx] = 0.0;
1411 $multicurrency_amount_ttc[$line->tva_tx] = 0.0;
1412 }
1413 // no need to create discount if amount is null
1414 $amount_ht[$line->tva_tx] += $line->total_ht;
1415 $amount_tva[$line->tva_tx] += $line->total_tva;
1416 $amount_ttc[$line->tva_tx] += $line->total_ttc;
1417 $multicurrency_amount_ht[$line->tva_tx] += $line->multicurrency_total_ht;
1418 $multicurrency_amount_tva[$line->tva_tx] += $line->multicurrency_total_tva;
1419 $multicurrency_amount_ttc[$line->tva_tx] += $line->multicurrency_total_ttc;
1420 $i++;
1421 }
1422 }
1423
1424 // Insert one discount by VAT rate category
1425 $discount = new DiscountAbsolute($this->db);
1426 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1427 $discount->description = '(CREDIT_NOTE)';
1428 } elseif ($this->invoice->type == Facture::TYPE_DEPOSIT) {
1429 $discount->description = '(DEPOSIT)';
1430 } elseif ($this->invoice->type == Facture::TYPE_STANDARD || $this->invoice->type == Facture::TYPE_REPLACEMENT || $this->invoice->type == Facture::TYPE_SITUATION) {
1431 $discount->description = '(EXCESS RECEIVED)';
1432 } else {
1433 throw new RestException(500, 'Cant convert to reduc an Invoice of this type');
1434 }
1435
1436 $discount->fk_soc = $this->invoice->socid;
1437 $discount->socid = $this->invoice->socid;
1438 $discount->fk_facture_source = $this->invoice->id;
1439
1440 $error = 0;
1441
1442 if ($this->invoice->type == Facture::TYPE_STANDARD || $this->invoice->type == Facture::TYPE_REPLACEMENT || $this->invoice->type == Facture::TYPE_SITUATION) {
1443 // If we're on a standard invoice, we have to get excess received to create a discount in TTC without VAT
1444
1445 // Total payments
1446 $sql = 'SELECT SUM(pf.amount) as total_payments';
1447 $sql .= ' FROM '.MAIN_DB_PREFIX.'paiement_facture as pf, '.MAIN_DB_PREFIX.'paiement as p';
1448 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as c ON p.fk_paiement = c.id';
1449 $sql .= ' WHERE pf.fk_facture = '.((int) $this->invoice->id);
1450 $sql .= ' AND pf.fk_paiement = p.rowid';
1451 $sql .= ' AND p.entity IN ('.getEntity('invoice').')';
1452 $resql = $this->db->query($sql);
1453 if (!$resql) {
1454 dol_print_error($this->db);
1455 }
1456
1457 $res = $this->db->fetch_object($resql);
1458 $total_payments = $res->total_payments;
1459
1460 // Total credit note and deposit
1461 $total_creditnote_and_deposit = 0;
1462 $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
1463 $sql .= " re.description, re.fk_facture_source";
1464 $sql .= " FROM ".MAIN_DB_PREFIX."societe_remise_except as re";
1465 $sql .= " WHERE fk_facture = ".((int) $this->invoice->id);
1466 $resql = $this->db->query($sql);
1467 if (!empty($resql)) {
1468 while ($obj = $this->db->fetch_object($resql)) {
1469 $total_creditnote_and_deposit += $obj->amount_ttc;
1470 }
1471 } else {
1472 dol_print_error($this->db);
1473 }
1474
1475 $discount->amount_ht = $discount->amount_ttc = $total_payments + $total_creditnote_and_deposit - $this->invoice->total_ttc;
1476 $discount->total_ht = $discount->total_ttc = $total_payments + $total_creditnote_and_deposit - $this->invoice->total_ttc;
1477 $discount->amount_tva = 0;
1478 $discount->total_tva = 0;
1479 $discount->tva_tx = 0;
1480
1481 $result = $discount->create(DolibarrApiAccess::$user);
1482 if ($result < 0) {
1483 $error++;
1484 }
1485 }
1486 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE || $this->invoice->type == Facture::TYPE_DEPOSIT) {
1487 foreach ($amount_ht as $tva_tx => $xxx) {
1488 $discount->amount_ht = abs($amount_ht[$tva_tx]);
1489 $discount->amount_tva = abs($amount_tva[$tva_tx]);
1490 $discount->amount_ttc = abs($amount_ttc[$tva_tx]);
1491 $discount->total_ht = abs($amount_ht[$tva_tx]);
1492 $discount->total_tva = abs($amount_tva[$tva_tx]);
1493 $discount->total_ttc = abs($amount_ttc[$tva_tx]);
1494 $discount->multicurrency_amount_ht = abs($multicurrency_amount_ht[$tva_tx]);
1495 $discount->multicurrency_amount_tva = abs($multicurrency_amount_tva[$tva_tx]);
1496 $discount->multicurrency_amount_ttc = abs($multicurrency_amount_ttc[$tva_tx]);
1497 $discount->multicurrency_total_ht = abs($multicurrency_amount_ht[$tva_tx]);
1498 $discount->multicurrency_total_tva = abs($multicurrency_amount_tva[$tva_tx]);
1499 $discount->multicurrency_total_ttc = abs($multicurrency_amount_ttc[$tva_tx]);
1500 $discount->tva_tx = abs((float) $tva_tx);
1501
1502 $result = $discount->create(DolibarrApiAccess::$user);
1503 if ($result < 0) {
1504 $error++;
1505 break;
1506 }
1507 }
1508 }
1509
1510 if (empty($error)) {
1511 if ($this->invoice->type != Facture::TYPE_DEPOSIT) {
1512 // Set the invoice as paid
1513 $result = $this->invoice->setPaid(DolibarrApiAccess::$user);
1514 if ($result >= 0) {
1515 $this->db->commit();
1516 } else {
1517 $this->db->rollback();
1518 throw new RestException(500, 'Could not set paid');
1519 }
1520 } else {
1521 $this->db->commit();
1522 }
1523 } else {
1524 $this->db->rollback();
1525 throw new RestException(500, 'Discount creation error');
1526 }
1527 }
1528
1529 return $this->_cleanObjectDatas($this->invoice);
1530 }
1531
1548 public function useDiscount($id, $discountid)
1549 {
1550 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1551 throw new RestException(403);
1552 }
1553 if (empty($id)) {
1554 throw new RestException(400, 'Invoice ID is mandatory');
1555 }
1556 if (empty($discountid)) {
1557 throw new RestException(400, 'Discount ID is mandatory');
1558 }
1559
1560 if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1561 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1562 }
1563
1564 $result = $this->invoice->fetch($id);
1565 if (!$result) {
1566 throw new RestException(404, 'Invoice not found');
1567 }
1568
1569 $result = $this->invoice->insert_discount($discountid);
1570 if ($result < 0) {
1571 throw new RestException(405, $this->invoice->error);
1572 }
1573
1574 return $result;
1575 }
1576
1593 public function useCreditNote($id, $discountid)
1594 {
1595 require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1596
1597 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1598 throw new RestException(403);
1599 }
1600 if (empty($id)) {
1601 throw new RestException(400, 'Invoice ID is mandatory');
1602 }
1603 if (empty($discountid)) {
1604 throw new RestException(400, 'Credit ID is mandatory');
1605 }
1606
1607 if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1608 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1609 }
1610 $discount = new DiscountAbsolute($this->db);
1611 $result = $discount->fetch($discountid);
1612 if (!$result) {
1613 throw new RestException(404, 'Credit not found');
1614 }
1615
1616 $result = $discount->link_to_invoice(0, $id);
1617 if ($result < 0) {
1618 throw new RestException(405, $discount->error);
1619 }
1620
1621 return $result;
1622 }
1623
1639 public function getPayments($id)
1640 {
1641 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
1642 throw new RestException(403);
1643 }
1644 if (empty($id)) {
1645 throw new RestException(400, 'Invoice ID is mandatory');
1646 }
1647
1648 if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1649 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1650 }
1651
1652 $result = $this->invoice->fetch($id);
1653 if (!$result) {
1654 throw new RestException(404, 'Invoice not found');
1655 }
1656
1657 $result = $this->invoice->getListOfPayments();
1658 if (!is_array($result) && $result < 0) {
1659 throw new RestException(405, $this->invoice->error);
1660 }
1661
1662 return $result;
1663 }
1664
1665
1687 public function addPayment($id, $datepaye, $paymentid, $closepaidinvoices, $accountid, $num_payment = '', $comment = '', $chqemetteur = '', $chqbank = '')
1688 {
1689 require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
1690
1691 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1692 throw new RestException(403);
1693 }
1694 if (empty($id)) {
1695 throw new RestException(400, 'Invoice ID is mandatory');
1696 }
1697
1698 if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1699 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1700 }
1701
1702 if (isModEnabled("bank")) {
1703 if (empty($accountid)) {
1704 throw new RestException(400, 'Account ID is mandatory');
1705 }
1706 }
1707
1708 if (empty($paymentid)) {
1709 throw new RestException(400, 'Payment ID or Payment Code is mandatory');
1710 }
1711
1712
1713 $result = $this->invoice->fetch($id);
1714 if (!$result) {
1715 throw new RestException(404, 'Invoice not found');
1716 }
1717
1718 // Calculate amount to pay
1719 $totalpaid = $this->invoice->getSommePaiement();
1720 $totalcreditnotes = $this->invoice->getSumCreditNotesUsed();
1721 $totaldeposits = $this->invoice->getSumDepositsUsed();
1722 $resteapayer = price2num($this->invoice->total_ttc - $totalpaid - $totalcreditnotes - $totaldeposits, 'MT');
1723
1724 $this->db->begin();
1725
1726 $amounts = array();
1727 $multicurrency_amounts = array();
1728
1729 // Clean parameters amount if payment is for a credit note
1730 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1731 $resteapayer = price2num($resteapayer, 'MT');
1732 $amounts[$id] = (float) price2num(-1 * abs((float) $resteapayer), 'MT');
1733 // Multicurrency
1734 $newvalue = price2num($this->invoice->multicurrency_total_ttc, 'MT');
1735 $multicurrency_amounts[$id] = (float) price2num(-1 * (float) $newvalue, 'MT');
1736 } else {
1737 $resteapayer = price2num($resteapayer, 'MT');
1738 $amounts[$id] = (float) $resteapayer;
1739 // Multicurrency
1740 $newvalue = price2num($this->invoice->multicurrency_total_ttc, 'MT');
1741 $multicurrency_amounts[$id] = (float) $newvalue;
1742 }
1743
1744 // Creation of payment line
1745 $paymentobj = new Paiement($this->db);
1746 if (is_numeric($datepaye)) {
1747 $paymentobj->datepaye = $datepaye;
1748 } else {
1749 $paymentobj->datepaye = dol_stringtotime($datepaye);
1750 }
1751 $paymentobj->amounts = $amounts; // Array with all payments dispatching with invoice id
1752 $paymentobj->multicurrency_amounts = $multicurrency_amounts; // Array with all payments dispatching
1753 $paymentobj->paiementid = $paymentid;
1754 $paymentobj->paiementcode = (string) dol_getIdFromCode($this->db, (string) $paymentid, 'c_paiement', 'id', 'code', 1);
1755 $paymentobj->num_payment = $num_payment;
1756 $paymentobj->note_private = $comment;
1757
1758 $payment_id = $paymentobj->create(DolibarrApiAccess::$user, ($closepaidinvoices == 'yes' ? 1 : 0)); // This include closing invoices
1759 if ($payment_id < 0) {
1760 $this->db->rollback();
1761 throw new RestException(400, 'Payment error : '.$paymentobj->error);
1762 }
1763
1764 if (isModEnabled("bank")) {
1765 $label = '(CustomerInvoicePayment)';
1766
1767 if ($paymentobj->paiementcode == 'CHQ' && empty($chqemetteur)) {
1768 throw new RestException(400, 'Emetteur is mandatory when payment code is '.$paymentobj->paiementcode);
1769 }
1770 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1771 $label = '(CustomerInvoicePaymentBack)'; // Refund of a credit note
1772 }
1773 $result = $paymentobj->addPaymentToBank(DolibarrApiAccess::$user, 'payment', $label, $accountid, $chqemetteur, $chqbank);
1774 if ($result < 0) {
1775 $this->db->rollback();
1776 throw new RestException(400, 'Add payment to bank error : '.$paymentobj->error);
1777 }
1778 }
1779
1780 $this->db->commit();
1781
1782 return $payment_id;
1783 }
1784
1813 public function addPaymentDistributed($arrayofamounts, $datepaye, $paymentid, $closepaidinvoices, $accountid, $num_payment = '', $comment = '', $chqemetteur = '', $chqbank = '', $ref_ext = '', $accepthigherpayment = false)
1814 {
1815 require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
1816
1817 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1818 throw new RestException(403);
1819 }
1820 foreach ($arrayofamounts as $id => $amount) {
1821 if (empty($id)) {
1822 throw new RestException(400, 'Invoice ID is mandatory. Fill the invoice id and amount into arrayofamounts parameter. For example: {"1": "99.99", "2": "10"}');
1823 }
1824 if (!DolibarrApi::_checkAccessToResource('facture', (int) $id)) {
1825 throw new RestException(403, 'Access not allowed on invoice ID '.$id.' for login '.DolibarrApiAccess::$user->login);
1826 }
1827 }
1828
1829 if (isModEnabled("bank")) {
1830 if (empty($accountid)) {
1831 throw new RestException(400, 'Account ID is mandatory');
1832 }
1833 }
1834 if (empty($paymentid)) {
1835 throw new RestException(400, 'Payment ID or Payment Code is mandatory');
1836 }
1837
1838 $this->db->begin();
1839
1840 $amounts = array();
1841 $multicurrency_amounts = array();
1842
1843 // Loop on each invoice to pay
1844 foreach ($arrayofamounts as $id => $amountarray) {
1845 $result = $this->invoice->fetch((int) $id);
1846 if (!$result) {
1847 $this->db->rollback();
1848 throw new RestException(404, 'Invoice ID '.$id.' not found');
1849 }
1850
1851 if (($amountarray["amount"] == "remain" || $amountarray["amount"] > 0) && ($amountarray["multicurrency_amount"] == "remain" || $amountarray["multicurrency_amount"] > 0)) {
1852 $this->db->rollback();
1853 throw new RestException(400, 'Payment in both currency '.$id.' ( amount: '.$amountarray["amount"].', multicurrency_amount: '.$amountarray["multicurrency_amount"].')');
1854 }
1855
1856 $is_multicurrency = 0;
1857 $total_ttc = $this->invoice->total_ttc;
1858
1859 if ($amountarray["multicurrency_amount"] > 0 || $amountarray["multicurrency_amount"] == "remain") {
1860 $is_multicurrency = 1;
1861 $total_ttc = $this->invoice->multicurrency_total_ttc;
1862 }
1863
1864 // Calculate amount to pay
1865 $totalpaid = $this->invoice->getSommePaiement($is_multicurrency);
1866 $totalcreditnotes = $this->invoice->getSumCreditNotesUsed($is_multicurrency);
1867 $totaldeposits = $this->invoice->getSumDepositsUsed($is_multicurrency);
1868 $remainstopay = $amount = (float) price2num($total_ttc - $totalpaid - $totalcreditnotes - $totaldeposits, 'MT');
1869
1870 if (!$is_multicurrency && $amountarray["amount"] != 'remain') {
1871 $amount = (float) price2num($amountarray["amount"], 'MT');
1872 }
1873
1874 if ($is_multicurrency && $amountarray["multicurrency_amount"] != 'remain') {
1875 $amount = (float) price2num($amountarray["multicurrency_amount"], 'MT');
1876 }
1877
1878 if (abs($amount) > abs($remainstopay) && !$accepthigherpayment) {
1879 $this->db->rollback();
1880 throw new RestException(400, 'Payment amount on invoice ID '.$id.' ('.$amount.') is higher than remain to pay ('.$remainstopay.')');
1881 }
1882
1883 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1884 $amount = (float) price2num(-1 * abs((float) $amount), 'MT');
1885 }
1886
1887 if ($is_multicurrency) {
1888 $amounts[$id] = null;
1889 // Multicurrency
1890 $multicurrency_amounts[$id] = (float) $amount;
1891 } else {
1892 $amounts[$id] = (float) $amount;
1893 // Multicurrency
1894 $multicurrency_amounts[$id] = null;
1895 }
1896 }
1897
1898 // Creation of payment line
1899 $paymentobj = new Paiement($this->db);
1900 if (is_numeric($datepaye)) {
1901 $paymentobj->datepaye = $datepaye;
1902 } else {
1903 $paymentobj->datepaye = dol_stringtotime($datepaye);
1904 }
1905 $paymentobj->amounts = $amounts; // Array with all payments dispatching with invoice id
1906 $paymentobj->multicurrency_amounts = $multicurrency_amounts; // Array with all payments dispatching
1907 $paymentobj->paiementid = $paymentid;
1908 $paymentobj->paiementcode = (string) dol_getIdFromCode($this->db, (string) $paymentid, 'c_paiement', 'id', 'code', 1);
1909 $paymentobj->num_payment = $num_payment;
1910 $paymentobj->note_private = $comment;
1911 $paymentobj->ref_ext = $ref_ext;
1912 $payment_id = $paymentobj->create(DolibarrApiAccess::$user, ($closepaidinvoices == 'yes' ? 1 : 0)); // This include closing invoices
1913 if ($payment_id < 0) {
1914 $this->db->rollback();
1915 throw new RestException(400, 'Payment error : '.$paymentobj->error);
1916 }
1917 if (isModEnabled("bank")) {
1918 $label = '(CustomerInvoicePayment)';
1919 if ($paymentobj->paiementcode == 'CHQ' && empty($chqemetteur)) {
1920 throw new RestException(400, 'Emetteur is mandatory when payment code is '.$paymentobj->paiementcode);
1921 }
1922 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1923 $label = '(CustomerInvoicePaymentBack)'; // Refund of a credit note
1924 }
1925 $result = $paymentobj->addPaymentToBank(DolibarrApiAccess::$user, 'payment', $label, $accountid, $chqemetteur, $chqbank);
1926 if ($result < 0) {
1927 $this->db->rollback();
1928 throw new RestException(400, 'Add payment to bank error : '.$paymentobj->error);
1929 }
1930 }
1931
1932 $this->db->commit();
1933
1934 return $payment_id;
1935 }
1936
1953 public function putPayment($id, $num_payment = '')
1954 {
1955 require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
1956
1957 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1958 throw new RestException(403);
1959 }
1960 if (empty($id)) {
1961 throw new RestException(400, 'Payment ID is mandatory');
1962 }
1963
1964 $paymentobj = new Paiement($this->db);
1965 $result = $paymentobj->fetch($id);
1966
1967 if (!$result) {
1968 throw new RestException(404, 'Payment not found');
1969 }
1970
1971 if (!empty($num_payment)) {
1972 $result = $paymentobj->update_num($num_payment);
1973 if ($result < 0) {
1974 throw new RestException(500, 'Error when updating the payment num');
1975 }
1976 }
1977
1978 return [
1979 'success' => [
1980 'code' => 200,
1981 'message' => 'Payment updated'
1982 ]
1983 ];
1984 }
1985
1986 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1996 protected function _cleanObjectDatas($object)
1997 {
1998 // phpcs:enable
1999 $object = parent::_cleanObjectDatas($object);
2000
2001 unset($object->note);
2002 unset($object->address);
2003 unset($object->barcode_type);
2004 unset($object->barcode_type_code);
2005 unset($object->barcode_type_label);
2006 unset($object->barcode_type_coder);
2007 unset($object->canvas);
2008
2009 return $object;
2010 }
2011
2020 private function _validate($data)
2021 {
2022 if ($data === null) {
2023 $data = array();
2024 }
2025 $invoice = array();
2026 foreach (Invoices::$FIELDS as $field) {
2027 if (!isset($data[$field])) {
2028 throw new RestException(400, "$field field missing");
2029 }
2030 $invoice[$field] = $data[$field];
2031 }
2032 return $invoice;
2033 }
2034
2035
2049 public function getTemplateInvoice($id, $contact_list = 1)
2050 {
2051 return $this->_fetchTemplateInvoice($id, '', '', $contact_list);
2052 }
2053
2054
2080 public function indexTemplateInvoices($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $thirdparty_ids = '', $status = '', $sqlfilters = '', $properties = '', $pagination_data = false, $loadlinkedobjects = 0, $withLines = true)
2081 {
2082 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
2083 throw new RestException(403);
2084 }
2085
2086 $obj_ret = array();
2087
2088 // case of external user, $thirdparty_ids param is ignored and replaced by user's socid
2089 $socids = DolibarrApiAccess::$user->socid ?: $thirdparty_ids;
2090
2091
2092 // If the internal user must only see his customers, force searching by him
2093 $search_sale = 0;
2094 if (!DolibarrApiAccess::$user->hasRight('societe', 'client', 'voir') && !$socids) {
2095 $search_sale = DolibarrApiAccess::$user->id;
2096 }
2097
2098 $sql = "SELECT t.rowid";
2099 $sql .= " FROM ".MAIN_DB_PREFIX."facture_rec AS t";
2100 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."societe AS s ON (s.rowid = t.fk_soc)";
2101 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facture_rec_extrafields AS ef ON (ef.fk_object = t.rowid)";
2102 $sql .= ' WHERE t.entity IN ('.getEntity('invoice').')';
2103 if ($socids) {
2104 $sql .= " AND t.fk_soc IN (".$this->db->sanitize($socids).")";
2105 }
2106
2107 // Search on sale representative
2108 if ($search_sale && $search_sale != '-1') {
2109 if ($search_sale == -2) {
2110 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux AS sc WHERE sc.fk_soc = t.fk_soc)";
2111 } elseif ($search_sale > 0) {
2112 $sql .= " AND EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux AS sc WHERE sc.fk_soc = t.fk_soc AND sc.fk_user = ".((int) $search_sale).")";
2113 }
2114 }
2115
2116 // Filter by status
2117 if ($status == 'active') {
2118 $sql .= " AND t.suspended = 0 AND t.frequency IS NOT NULL";
2119 }
2120 if ($status == 'suspended') {
2121 $sql .= " AND t.suspended = 1 AND t.frequency IS NOT NULL";
2122 }
2123 if ($status == 'draft') {
2124 $sql .= " AND t.frequency IS NULL";
2125 }
2126 // add sql filters
2127 if ($sqlfilters) {
2128 $errormessage = '';
2129 $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
2130 if ($errormessage) {
2131 throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);
2132 }
2133 }
2134
2135 //this query will return total template invoices with the filters given
2136 $sqlTotals = str_replace('SELECT t.rowid', 'SELECT count(t.rowid) as total', $sql);
2137
2138 $sql .= $this->db->order($sortfield, $sortorder);
2139 if ($limit) {
2140 if ($page < 0) {
2141 $page = 0;
2142 }
2143 $offset = $limit * $page;
2144
2145 $sql .= $this->db->plimit($limit + 1, $offset);
2146 }
2147
2148 $result = $this->db->query($sql);
2149 if ($result) {
2150 $i = 0;
2151 $num = $this->db->num_rows($result);
2152 $min = min($num, ($limit <= 0 ? $num : $limit));
2153 while ($i < $min) {
2154 $obj = $this->db->fetch_object($result);
2155 $factureRec = new FactureRec($this->db);
2156 if ($factureRec->fetch($obj->rowid) > 0) {
2157 if ($loadlinkedobjects) {
2158 // retrieve linked objects
2159 $factureRec->fetchObjectLinked();
2160 }
2161
2162 if (!$withLines) {
2163 unset($factureRec->lines);
2164 }
2165
2166 $obj_ret[] = $this->_filterObjectProperties($this->_cleanTemplateObjectDatas($factureRec), $properties);
2167 }
2168 $i++;
2169 }
2170 } else {
2171 throw new RestException(503, 'Error when retrieving recurring invoice templates: '.$this->db->lasterror());
2172 }
2173
2174 //if $pagination_data is true the response will contain element data with all values and element pagination with pagination data(total,page,limit)
2175 if ($pagination_data) {
2176 $totalsResult = $this->db->query($sqlTotals);
2177 $total = $this->db->fetch_object($totalsResult)->total;
2178
2179 $tmp = $obj_ret;
2180 $obj_ret = array();
2181
2182 $obj_ret['data'] = $tmp;
2183 $obj_ret['pagination'] = array(
2184 'total' => (int) $total,
2185 'page' => $page,
2186 'page_count' => ceil((int) $total / $limit),
2187 'limit' => $limit
2188 );
2189 }
2190
2191 return $obj_ret;
2192 }
2193
2207 private function _fetchTemplateInvoice($id, $ref = '', $ref_ext = '', $contact_list = 1)
2208 {
2209 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
2210 throw new RestException(403);
2211 }
2212
2213 $result = $this->template_invoice->fetch($id, $ref, $ref_ext);
2214 if (!$result) {
2215 throw new RestException(404, 'Template invoice not found');
2216 }
2217
2218 if (!DolibarrApi::_checkAccessToResource('facturerec', $this->template_invoice->id)) {
2219 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
2220 }
2221
2222 // Add external contacts ids
2223 if ($contact_list > -1) {
2224 $tmparray = $this->template_invoice->liste_contact(-1, 'external', $contact_list);
2225 if (is_array($tmparray)) {
2226 $this->template_invoice->contacts_ids = $tmparray;
2227 }
2228 }
2229
2230 $this->template_invoice->fetchObjectLinked();
2231 return $this->_cleanTemplateObjectDatas($this->template_invoice);
2232 }
2233
2234
2235 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
2243 {
2244 // phpcs:enable
2245 $object = parent::_cleanObjectDatas($object);
2246
2247 unset($object->note);
2248 unset($object->address);
2249 unset($object->barcode_type);
2250 unset($object->barcode_type_code);
2251 unset($object->barcode_type_label);
2252 unset($object->barcode_type_coder);
2253 unset($object->canvas);
2254
2255 return $object;
2256 }
2257}
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:47
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
Class to manage customers orders.
Class to manage absolute discounts.
Class for API REST v1.
Definition api.class.php:33
_filterObjectProperties($object, $properties)
Filter properties that will be returned on object.
static _checkAccessToResource($resource, $resource_id=0, $dbtablename='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid')
Check access by user to a given resource.
_checkValForAPI($field, $value, $object)
Check and convert a string depending on its type/name.
Definition api.class.php:98
Class to manage invoices.
const TYPE_REPLACEMENT
Replacement invoice.
const TYPE_STANDARD
Standard invoice.
const TYPE_SITUATION
Situation invoice.
const TYPE_DEPOSIT
Deposit invoice.
const TYPE_CREDIT_NOTE
Credit note invoice.
Class to manage invoice templates.
putPayment($id, $num_payment='')
Update a payment.
addContact($id, $fk_socpeople, $type_contact, $source, $notrigger=0)
Adds a contact to an invoice.
indexTemplateInvoices($sortfield="t.rowid", $sortorder='ASC', $limit=100, $page=0, $thirdparty_ids='', $status='', $sqlfilters='', $properties='', $pagination_data=false, $loadlinkedobjects=0, $withLines=true)
List template invoices.
createInvoiceFromOrder($orderid)
Create an invoice using an existing order.
markAsCreditAvailable($id)
Create a discount (credit available) for a credit note or a deposit.
getDiscount($id)
Get discount from invoice.
__construct()
Constructor.
put($id, $request_data=null)
Update invoice.
validate($id, $force_number='', $idwarehouse=0, $notrigger=0)
Validate an invoice.
getContacts($id, $type='')
Get contacts of given invoice.
getByRefExt($ref_ext, $contact_list=1)
Get properties of an invoice object by ref_ext.
_fetch($id, $ref='', $ref_ext='', $contact_list=1)
Get properties of an invoice object.
getByRef($ref, $contact_list=1)
Get properties of an invoice object by ref.
getPayments($id)
Get list of payments of a given invoice.
_cleanObjectDatas($object)
Clean sensible object datas @phpstan-template T.
post($request_data=null)
Create invoice object.
index($sortfield="t.rowid", $sortorder='ASC', $limit=100, $page=0, $thirdparty_ids='', $status='', $sqlfilters='', $properties='', $pagination_data=false, $loadlinkedobjects=0, $withLines=true)
List invoices.
useDiscount($id, $discountid)
Add a discount line into an invoice (as an invoice line) using an existing absolute discount.
settounpaid($id)
Sets an invoice as unpaid.
getTemplateInvoice($id, $contact_list=1)
Get properties of a template invoice object.
putLine($id, $lineid, $request_data=null)
Update a line to a given invoice.
getLines($id)
Get lines of an invoice.
useCreditNote($id, $discountid)
Add an available credit note discount to payments of an existing invoice.
createInvoiceFromContract($contractid)
Create an invoice using a contract.
addPaymentDistributed($arrayofamounts, $datepaye, $paymentid, $closepaidinvoices, $accountid, $num_payment='', $comment='', $chqemetteur='', $chqbank='', $ref_ext='', $accepthigherpayment=false)
Add a payment to pay partially or completely one or several invoices.
postContact($id, $contactid, $type, $source='external', $notrigger=0)
Add a contact type of given invoice.
_cleanTemplateObjectDatas($object)
Clean sensible object datas.
deleteContact($id, $contactid, $type)
Delete a contact type of given invoice.
postLine($id, $request_data=null)
Add a line to a given invoice.
_fetchTemplateInvoice($id, $ref='', $ref_ext='', $contact_list=1)
Get properties of an invoice object.
_validate($data)
Validate fields before create or update object.
addPayment($id, $datepaye, $paymentid, $closepaidinvoices, $accountid, $num_payment='', $comment='', $chqemetteur='', $chqbank='')
Add payment line to a specific invoice with the remain to pay as amount.
settopaid($id, $close_code='', $close_note='')
Sets an invoice as paid.
settodraft($id, $idwarehouse=-1)
Sets an invoice as draft.
deleteLine($id, $lineid)
Deletes a line of a given invoice.
Class to manage payments of customer invoices.
Class to manage third parties objects (customers, suppliers, prospects...)
dol_stringtotime($string, $gm=1)
Convert a string date into a GM Timestamps date Warning: YYYY-MM-DDTHH:MM:SS+02:00 (RFC3339) is not s...
Definition date.lib.php:434
dol_now($mode='gmt')
Return date for now.
dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0, $filters='', $useCache=true)
Return an id or code from a code or id.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0)
forgeSQLFromUniversalSearchCriteria
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
sanitizeVal($out='', $check='alphanohtml', $filter=null, $options=null)
Return a sanitized or empty value after checking value against a rule.
isModEnabled($module)
Is Dolibarr module enabled.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getMarginInfos($pv_ht, $remise_percent, $tva_tx, $localtax1_tx, $localtax2_tx, $fk_pa, $pa_ht)
Return an array with margins information of a line.