dolibarr 25.0.0-alpha
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-2026 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
36class Invoices extends DolibarrApi
37{
41 public static $FIELDS = array(
42 'socid',
43 );
44
48 private $invoice;
49
53 private $template_invoice;
54
55
59 public function __construct()
60 {
61 global $db;
62 $this->db = $db;
63 $this->invoice = new Facture($this->db);
64 $this->template_invoice = new FactureRec($this->db);
65 }
66
82 public function get($id, $contact_list = 1, $properties = '', $withLines = true)
83 {
84 $invoice = $this->_fetch($id, '', '', $contact_list);
85
86 if (!$withLines) {
87 unset($invoice->lines);
88 }
89
90 return $this->_filterObjectProperties($invoice, $properties);
91 }
92
108 public function getByRef($ref, $contact_list = 1)
109 {
110 return $this->_fetch(0, $ref, '', $contact_list);
111 }
112
128 public function getByRefExt($ref_ext, $contact_list = 1)
129 {
130 return $this->_fetch(0, '', $ref_ext, $contact_list);
131 }
132
146 private function _fetch($id, $ref = '', $ref_ext = '', $contact_list = 1)
147 {
148 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
149 throw new RestException(403);
150 }
151 if (empty($id) && empty($ref) && empty($ref_ext)) {
152 throw new RestException(400, 'No invoice can be found with no criteria');
153 }
154 $result = $this->invoice->fetch($id, $ref, $ref_ext);
155 if (!$result) {
156 throw new RestException(404, 'Invoice not found');
157 }
158
159 // Get payment details
160 $this->invoice->totalpaid = $this->invoice->getSommePaiement();
161 $this->invoice->totalcreditnotes = $this->invoice->getSumCreditNotesUsed();
162 $this->invoice->totaldeposits = $this->invoice->getSumDepositsUsed();
163 $this->invoice->remaintopay = price2num($this->invoice->total_ttc - $this->invoice->totalpaid - $this->invoice->totalcreditnotes - $this->invoice->totaldeposits, 'MT');
164
165 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
166 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
167 }
168
169 // Retrieve credit note ids
170 $this->invoice->getListIdAvoirFromInvoice();
171
172 // Add external contacts ids
173 if ($contact_list > -1) {
174 $tmparray = $this->invoice->liste_contact(-1, 'external', $contact_list);
175 if (is_array($tmparray)) {
176 $this->invoice->contacts_ids = $tmparray;
177 }
178 $tmparray = $this->invoice->liste_contact(-1, 'internal', $contact_list);
179 if (is_array($tmparray)) {
180 $this->invoice->contacts_ids = $tmparray;
181 }
182 }
183
184 $this->invoice->fetchObjectLinked();
185
186 // Add online_payment_url, copied from order
187 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
188 $this->invoice->online_payment_url = getOnlinePaymentUrl(0, 'invoice', (string) $this->invoice->ref);
189
190 return $this->_cleanObjectDatas($this->invoice);
191 }
192
218 public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $thirdparty_ids = '', $status = '', $sqlfilters = '', $properties = '', $pagination_data = false, $loadlinkedobjects = 0, $withLines = true)
219 {
220 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
221 throw new RestException(403);
222 }
223
224 $obj_ret = array();
225
226 // case of external user, $thirdparty_ids param is ignored and replaced by user's socid
227 $socids = DolibarrApiAccess::$user->socid ?: $thirdparty_ids;
228
229 // If the internal user must only see his customers, force searching by him
230 $search_sale = 0;
231 if (!DolibarrApiAccess::$user->hasRight('societe', 'client', 'voir') && !$socids) {
232 $search_sale = DolibarrApiAccess::$user->id;
233 }
234
235 $sql = "SELECT t.rowid";
236 $sql .= " FROM ".MAIN_DB_PREFIX."facture AS t";
237 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."societe AS s ON (s.rowid = t.fk_soc)";
238 $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
239 $sql .= ' WHERE t.entity IN ('.getEntity('invoice').')';
240 if ($socids) {
241 $sql .= " AND t.fk_soc IN (".$this->db->sanitize($socids).")";
242 }
243 // Search on sale representative
244 if ($search_sale && $search_sale != '-1') {
245 if ($search_sale == -2) {
246 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = t.fk_soc)";
247 } elseif ($search_sale > 0) {
248 $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).")";
249 }
250 }
251 // Filter by status
252 if ($status == 'draft') {
253 $sql .= " AND t.fk_statut IN (0)";
254 }
255 if ($status == 'unpaid') {
256 $sql .= " AND t.fk_statut IN (1)";
257 }
258 if ($status == 'paid') {
259 $sql .= " AND t.fk_statut IN (2)";
260 }
261 if ($status == 'cancelled') {
262 $sql .= " AND t.fk_statut IN (3)";
263 }
264 // Add sql filters
265 if ($sqlfilters) {
266 $errormessage = '';
267 $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
268 if ($errormessage) {
269 throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);
270 }
271 }
272
273 //this query will return total invoices with the filters given
274 $sqlTotals = str_replace('SELECT t.rowid', 'SELECT count(t.rowid) as total', $sql);
275
276 $sql .= $this->db->order($sortfield, $sortorder);
277 if ($limit) {
278 if ($page < 0) {
279 $page = 0;
280 }
281 $offset = $limit * $page;
282
283 $sql .= $this->db->plimit($limit + 1, $offset);
284 }
285
286 $result = $this->db->query($sql);
287 if ($result) {
288 $i = 0;
289 $num = $this->db->num_rows($result);
290 $min = min($num, ($limit <= 0 ? $num : $limit));
291 while ($i < $min) {
292 $obj = $this->db->fetch_object($result);
293 $invoice_static = new Facture($this->db);
294 if ($invoice_static->fetch($obj->rowid) > 0) {
295 // Get payment details
296 $invoice_static->totalpaid = $invoice_static->getSommePaiement();
297 $invoice_static->totalcreditnotes = $invoice_static->getSumCreditNotesUsed();
298 $invoice_static->totaldeposits = $invoice_static->getSumDepositsUsed();
299 $invoice_static->remaintopay = price2num($invoice_static->total_ttc - $invoice_static->totalpaid - $invoice_static->totalcreditnotes - $invoice_static->totaldeposits, 'MT');
300
301 // Retrieve credit note ids
302 $invoice_static->getListIdAvoirFromInvoice();
303
304 // Add external contacts ids
305 $tmparray = $invoice_static->liste_contact(-1, 'external', 1);
306 if (is_array($tmparray)) {
307 $invoice_static->contacts_ids = $tmparray;
308 }
309
310 if ($loadlinkedobjects) {
311 // retrieve linked objects
312 $invoice_static->fetchObjectLinked();
313 }
314
315 if (!$withLines) {
316 unset($invoice_static->lines);
317 }
318
319 // Add online_payment_url, copied from order
320 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
321 $invoice_static->online_payment_url = getOnlinePaymentUrl(0, 'invoice', (string) $invoice_static->ref);
322
323 $obj_ret[] = $this->_filterObjectProperties($this->_cleanObjectDatas($invoice_static), $properties);
324 }
325 $i++;
326 }
327 } else {
328 throw new RestException(503, 'Error when retrieve invoice list : '.$this->db->lasterror());
329 }
330
331 //if $pagination_data is true the response will contain element data with all values and element pagination with pagination data(total,page,limit)
332 if ($pagination_data) {
333 $totalsResult = $this->db->query($sqlTotals);
334 $total = $this->db->fetch_object($totalsResult)->total;
335
336 $tmp = $obj_ret;
337 $obj_ret = [];
338
339 $obj_ret['data'] = $tmp;
340 $obj_ret['pagination'] = [
341 'total' => (int) $total,
342 'page' => $page, //count starts from 0
343 'page_count' => ceil((int) $total / $limit),
344 'limit' => $limit
345 ];
346 }
347
348 return $obj_ret;
349 }
350
361 public function post($request_data = null)
362 {
363 global $conf;
364 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
365 throw new RestException(403, "Insufficiant rights");
366 }
367
368 if (!is_array($request_data)) {
369 $request_data = array();
370 }
371
372 // Check mandatory fields (not using output, only possible exception is important)
373 $this->_validate($request_data);
374
375 // Check thirdparty validity
376 $socid = (int) $request_data['socid'];
377 $thirdpartytmp = new Societe($this->db);
378 $thirdparty_result = $thirdpartytmp->fetch($socid);
379 if ($thirdparty_result < 1) {
380 throw new RestException(404, 'Thirdparty with id='.$socid.' not found or not allowed');
381 }
382 if (!DolibarrApi::_checkAccessToResource('societe', $thirdpartytmp->id)) {
383 throw new RestException(404, 'Thirdparty with id='.$thirdpartytmp->id.' not found or not allowed');
384 }
385
386 foreach ($request_data as $field => $value) {
387 if ($field === 'caller') {
388 // 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
389 $this->invoice->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
390 continue;
391 }
392 if ($field == 'id') {
393 throw new RestException(400, 'Creating with id field is forbidden');
394 }
395 if ($field == 'entity' && ((int) $value) != ((int) $conf->entity)) {
396 throw new RestException(403, 'Creating with entity='.((int) $value).' MUST be the same entity='.((int) $conf->entity).' as your API user/key belongs to');
397 }
398
399 $this->invoice->$field = $this->_checkValForAPI($field, $value, $this->invoice);
400 }
401 if (!array_key_exists('date', $request_data)) {
402 $this->invoice->date = dol_now();
403 }
404 /* We keep lines as an array
405 if (isset($request_data["lines"])) {
406 $lines = array();
407 foreach ($request_data["lines"] as $line) {
408 array_push($lines, (object) $line);
409 }
410 $this->invoice->lines = $lines;
411 }*/
412
413 if ($this->invoice->create(DolibarrApiAccess::$user, 0, (empty($request_data["date_lim_reglement"]) ? 0 : $request_data["date_lim_reglement"])) < 0) {
414 throw new RestException(500, "Error creating invoice", array_merge(array($this->invoice->error), $this->invoice->errors));
415 }
416 return ((int) $this->invoice->id);
417 }
418
435 public function createInvoiceFromOrder($orderid)
436 {
437 require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
438
439 if (!DolibarrApiAccess::$user->hasRight('commande', 'lire')) {
440 throw new RestException(403);
441 }
442 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
443 throw new RestException(403);
444 }
445 if (empty($orderid)) {
446 throw new RestException(400, 'Order ID is mandatory');
447 }
448 if (!DolibarrApi::_checkAccessToResource('commande', $orderid)) {
449 throw new RestException(403, 'Access not allowed on order for login '.DolibarrApiAccess::$user->login);
450 }
451
452 $order = new Commande($this->db);
453 $result = $order->fetch($orderid);
454 if (!$result) {
455 throw new RestException(404, 'Order not found');
456 }
457
458 // Refuse orders that cannot be billed, to mirror the GUI (order card "CreateBill" button and list mass action):
459 // this excludes draft and canceled orders, as well as orders already classified as billed.
460 if ($order->status <= Commande::STATUS_DRAFT || !empty($order->billed)) {
461 throw new RestException(405, 'Order '.$order->ref.' is not eligible for invoicing: its status does not allow creating an invoice');
462 }
463
464 $result = $this->invoice->createFromOrder($order, DolibarrApiAccess::$user);
465 if ($result < 0) {
466 throw new RestException(405, $this->invoice->error);
467 }
468 $this->invoice->fetchObjectLinked();
469 return $this->_cleanObjectDatas($this->invoice);
470 }
471
487 public function createInvoiceFromContract($contractid)
488 {
489 require_once DOL_DOCUMENT_ROOT.'/contrat/class/contrat.class.php';
490
491 if (!DolibarrApiAccess::$user->hasRight('contrat', 'lire')) {
492 throw new RestException(403);
493 }
494 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
495 throw new RestException(403);
496 }
497 if (empty($contractid)) {
498 throw new RestException(400, 'Contract ID is mandatory');
499 }
500
501 $contract = new Contrat($this->db);
502 $result = $contract->fetch($contractid);
503 if (!$result) {
504 throw new RestException(404, 'Contract not found');
505 }
506
507 $result = $this->invoice->createFromContract($contract, DolibarrApiAccess::$user);
508 if ($result < 0) {
509 throw new RestException(405, $this->invoice->error);
510 }
511 $this->invoice->fetchObjectLinked();
512 return $this->_cleanObjectDatas($this->invoice);
513 }
514
527 public function getLines($id)
528 {
529 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
530 throw new RestException(403);
531 }
532
533 $result = $this->invoice->fetch($id);
534 if (!$result) {
535 throw new RestException(404, 'Invoice not found');
536 }
537
538 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
539 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
540 }
541 $this->invoice->getLinesArray();
542 $result = array();
543 foreach ($this->invoice->lines as $line) {
544 array_push($result, $this->_cleanObjectDatas($line));
545 }
546 return $result;
547 }
548
567 public function putLine($id, $lineid, $request_data = null)
568 {
569 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
570 throw new RestException(403);
571 }
572
573 $result = $this->invoice->fetch($id);
574 if (!$result) {
575 throw new RestException(404, 'Invoice not found');
576 }
577
578 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
579 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
580 }
581
582 $request_data = (object) $request_data;
583
584 $request_data->desc = sanitizeVal($request_data->desc, 'restricthtml');
585 $request_data->label = sanitizeVal($request_data->label);
586
587 $updateRes = $this->invoice->updateline(
588 $lineid,
589 $request_data->desc,
590 $request_data->subprice,
591 $request_data->qty,
592 $request_data->remise_percent,
593 $request_data->date_start,
594 $request_data->date_end,
595 $request_data->tva_tx,
596 $request_data->localtax1_tx,
597 $request_data->localtax2_tx,
598 $request_data->price_base_type ? $request_data->price_base_type : 'HT',
599 $request_data->info_bits,
600 $request_data->product_type,
601 $request_data->fk_parent_line,
602 0,
603 $request_data->fk_fournprice,
604 $request_data->pa_ht,
605 $request_data->label,
606 $request_data->special_code,
607 $request_data->array_options,
608 $request_data->situation_percent,
609 $request_data->fk_unit,
610 $request_data->multicurrency_subprice,
611 0,
612 $request_data->ref_ext,
613 $request_data->rang
614 );
615
616 if ($updateRes > 0) {
617 $result = $this->get($id);
618 unset($result->line);
619 return $this->_cleanObjectDatas($result);
620 } else {
621 throw new RestException(304, $this->invoice->error);
622 }
623 }
624
644 public function postContact($id, $contactid, $type, $source = 'external', $notrigger = 0)
645 {
646 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
647 throw new RestException(403);
648 }
649
650 // test source
651 if (empty($source)) {
652 throw new RestException(400, 'Source can not be empty');
653 }
654 $sql_distinct_source = "SELECT DISTINCT source";
655 $sql_distinct_source .= " FROM ".MAIN_DB_PREFIX."c_type_contact";
656 $sql_distinct_source .= " WHERE element LIKE 'facture'";
657 $sql_distinct_source .= " AND source is NOT NULL";
658 $sql_distinct_source .= " AND active != 0";
659 $source_result = $this->db->query($sql_distinct_source);
660 $source_array = array();
661
662 if ($source_result) {
663 $num = $this->db->num_rows($source_result);
664 $i = 0;
665 while ($i < $num) {
666 $obj = $this->db->fetch_object($source_result);
667 $source_kind = (string) $obj->source;
668 array_push($source_array, $source_kind);
669 dol_syslog("source_kind=".$source_kind);
670 $i++;
671 }
672 } else {
673 throw new RestException(503, 'Error when retrieving a list of invoice contact sources: '.$this->db->lasterror());
674 }
675 if (!in_array($source, (array) $source_array, true)) {
676 throw new RestException(400, 'Combo of Source='.$source.' and Type='.$type.' not found in dictionary with active invoice contact types');
677 }
678
679 // test type
680 if (empty($type)) {
681 throw new RestException(400, 'type can not be empty');
682 }
683 // variable called type here, but code in dictionary and database
684 $sql_distinct_type = "SELECT DISTINCT code";
685 $sql_distinct_type .= " FROM ".MAIN_DB_PREFIX."c_type_contact";
686 $sql_distinct_type .= " WHERE element LIKE 'facture'";
687 $sql_distinct_type .= " AND source='".$this->db->escape($source)."'";
688 $sql_distinct_type .= " AND code is NOT NULL";
689 $sql_distinct_type .= " AND active != 0";
690 $type_result = $this->db->query($sql_distinct_type);
691 $type_array = array();
692
693 if ($type_result) {
694 $num = $this->db->num_rows($type_result);
695 $i = 0;
696 while ($i < $num) {
697 $obj = $this->db->fetch_object($type_result);
698 // variable called type here, but code in dictionary and database
699 $type_kind = (string) $obj->code;
700 array_push($type_array, $type_kind);
701 dol_syslog("type_kind=".$type_kind);
702 $i++;
703 }
704 } else {
705 throw new RestException(503, 'Error when retrieving a list of invoice contact types: '.$this->db->lasterror());
706 }
707 if (!in_array($type, (array) $type_array, true)) {
708 throw new RestException(400, 'Combo of Type='.$type.' and Source='.$source.' not found in dictionary with active invoice contact types');
709 }
710
711 // tests done, let's get it
712 $result = $this->invoice->fetch($id);
713 if (!$result) {
714 throw new RestException(404, 'Invoice not found');
715 }
716 if (!DolibarrApi::_checkAccessToResource('invoice', $this->invoice->id)) {
717 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
718 }
719
720 $result = $this->invoice->add_contact($contactid, $type, $source, $notrigger);
721
722 if ($result == 0) {
723 throw new RestException(400, 'Already exists: Contact='.$contactid.' is already linked to the invoice='.$id.' as source='.$source.' and type='.$type);
724 } elseif ($result == -1) {
725 throw new RestException(400, 'Wrong contact='.$contactid);
726 } elseif ($result == -2) {
727 throw new RestException(400, 'Wrong type='.$type);
728 } elseif ($result == -3) {
729 throw new RestException(400, 'Not allowed contacts');
730 } elseif ($result == -4) {
731 throw new RestException(400, 'ErrorCommercialNotAllowedForThirdparty');
732 } elseif ($result == -5) {
733 throw new RestException(400, 'Trigger failed');
734 } elseif ($result == -6) {
735 throw new RestException(400, 'DB_ERROR_RECORD_ALREADY_EXISTS');
736 } elseif ($result == -7) {
737 throw new RestException(400, 'Some other error');
738 }
739
740 if (!$result) {
741 throw new RestException(500, 'Error when added the contact');
742 }
743
744 return array(
745 'success' => array(
746 'code' => 200,
747 'message' => 'Contact='.$contactid.' linked to the invoice='.$id.' as '.$source.' '.$type
748 )
749 );
750 }
751
767 public function getContacts($id, $type = '')
768 {
769 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
770 throw new RestException(403);
771 }
772
773 $result = $this->invoice->fetch($id);
774 if (!$result) {
775 throw new RestException(404, 'Invoice not found');
776 }
777
778 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
779 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
780 }
781
782 $contacts = $this->invoice->liste_contact(-1, 'external', 0, $type);
783 $socpeoples = $this->invoice->liste_contact(-1, 'internal', 0, $type);
784
785 $contacts = array_merge($contacts, $socpeoples);
786
787 return $contacts;
788 }
789
806 public function deleteContact($id, $contactid, $type)
807 {
808 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
809 throw new RestException(403);
810 }
811
812 $result = $this->invoice->fetch($id);
813
814 if (!$result) {
815 throw new RestException(404, 'Invoice not found');
816 }
817
818 if (!DolibarrApi::_checkAccessToResource('invoice', $this->invoice->id)) {
819 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
820 }
821
822 $contacts = $this->invoice->liste_contact();
823
824 foreach ($contacts as $contact) {
825 if ($contact['id'] == $contactid && $contact['code'] == $type) {
826 $result = $this->invoice->delete_contact($contact['rowid']);
827
828 if (!$result) {
829 throw new RestException(500, 'Error when deleted the contact');
830 }
831 }
832 }
833
834 return $this->_cleanObjectDatas($this->invoice);
835 }
836
853 public function deleteLine($id, $lineid)
854 {
855 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
856 throw new RestException(403);
857 }
858 if (empty($lineid)) {
859 throw new RestException(400, 'Line ID is mandatory');
860 }
861
862 if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
863 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
864 }
865
866 $result = $this->invoice->fetch($id);
867 if (!$result) {
868 throw new RestException(404, 'Invoice not found');
869 }
870 if ($this->invoice->status != 0) {
871 throw new RestException(403, 'Invoice not in Draft Status : '.$this->invoice->getLibStatut(1));
872 }
873
874 $updateRes = $this->invoice->deleteLine($lineid, $id);
875 if ($updateRes > 0) {
876 return $this->get($id);
877 } else {
878 throw new RestException(405, $this->invoice->error);
879 }
880 }
881
893 public function put($id, $request_data = null)
894 {
895 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
896 throw new RestException(403);
897 }
898 if ($id == 0) {
899 throw new RestException(400, 'No invoice with id=0 can exist');
900 }
901 $result = $this->invoice->fetch($id);
902 if (!$result) {
903 throw new RestException(404, 'Invoice not found');
904 }
905
906 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
907 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
908 }
909
910 foreach ($request_data as $field => $value) {
911 if ($field == 'id') {
912 continue;
913 }
914 if ($field === 'caller') {
915 // 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
916 $this->invoice->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
917 continue;
918 }
919 if ($field == 'array_options' && is_array($value)) {
920 foreach ($value as $index => $val) {
921 $this->invoice->array_options[$index] = $this->_checkValExtrafieldsForAPI($index, $val, $this->invoice);
922 }
923 continue;
924 }
925
926 $this->invoice->$field = $this->_checkValForAPI($field, $value, $this->invoice);
927
928 // If cond reglement => update date lim reglement
929 if ($field == 'cond_reglement_id') {
930 $this->invoice->date_lim_reglement = $this->invoice->calculate_date_lim_reglement();
931 }
932 }
933
934 // update bank account
935 if (!empty($this->invoice->fk_account)) {
936 if ($this->invoice->setBankAccount((int) $this->invoice->fk_account) == 0) {
937 throw new RestException(400, $this->invoice->error);
938 }
939 }
940
941 if ($this->invoice->update(DolibarrApiAccess::$user) > 0) {
942 return $this->get($id);
943 } else {
944 throw new RestException(500, $this->invoice->error);
945 }
946 }
947
958 public function delete($id)
959 {
960 if (!DolibarrApiAccess::$user->hasRight('facture', 'supprimer')) {
961 throw new RestException(403);
962 }
963 if ($id == 0) {
964 throw new RestException(400, 'No invoice with id=0 can exist');
965 }
966 $result = $this->invoice->fetch($id);
967 if (!$result) {
968 throw new RestException(404, 'Invoice not found');
969 }
970
971 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
972 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
973 }
974
975 $result = $this->invoice->delete(DolibarrApiAccess::$user);
976 if ($result < 0) {
977 throw new RestException(500, 'Error when deleting invoice');
978 } elseif ($result == 0) {
979 throw new RestException(403, 'Invoice not erasable');
980 }
981
982 return array(
983 'success' => array(
984 'code' => 200,
985 'message' => 'Invoice deleted'
986 )
987 );
988 }
989
1017 public function postLine($id, $request_data = null)
1018 {
1019 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1020 throw new RestException(403);
1021 }
1022
1023 $result = $this->invoice->fetch($id);
1024 if (!$result) {
1025 throw new RestException(404, 'Invoice not found');
1026 }
1027
1028 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1029 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1030 }
1031
1032 $request_data = (object) $request_data;
1033
1034 $request_data->desc = sanitizeVal($request_data->desc, 'restricthtml');
1035 $request_data->label = sanitizeVal($request_data->label);
1036
1037 // Reset fk_parent_line for no child products and special product
1038 if (($request_data->product_type != 9 && empty($request_data->fk_parent_line)) || $request_data->product_type == 9) {
1039 $request_data->fk_parent_line = 0;
1040 }
1041
1042 // calculate pa_ht
1043 $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);
1044 $pa_ht = $marginInfos[0];
1045
1046 $updateRes = $this->invoice->addline(
1047 $request_data->desc,
1048 $request_data->subprice,
1049 $request_data->qty,
1050 $request_data->tva_tx,
1051 $request_data->localtax1_tx,
1052 $request_data->localtax2_tx,
1053 $request_data->fk_product,
1054 $request_data->remise_percent,
1055 $request_data->date_start,
1056 $request_data->date_end,
1057 $request_data->fk_code_ventilation,
1058 $request_data->info_bits,
1059 $request_data->fk_remise_except,
1060 $request_data->price_base_type ? $request_data->price_base_type : 'HT',
1061 $request_data->subprice,
1062 $request_data->product_type,
1063 $request_data->rang,
1064 $request_data->special_code,
1065 $request_data->origin,
1066 $request_data->origin_id,
1067 $request_data->fk_parent_line,
1068 empty($request_data->fk_fournprice) ? null : $request_data->fk_fournprice,
1069 $pa_ht,
1070 $request_data->label,
1071 $request_data->array_options,
1072 $request_data->situation_percent,
1073 $request_data->fk_prev_id,
1074 $request_data->fk_unit,
1075 0,
1076 $request_data->ref_ext
1077 );
1078
1079 if ($updateRes < 0) {
1080 throw new RestException(400, 'Unable to insert the new line. Check your inputs. '.$this->invoice->error);
1081 }
1082
1083 return $updateRes;
1084 }
1085
1106 public function addContact($id, $fk_socpeople, $type_contact, $source, $notrigger = 0)
1107 {
1108 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1109 throw new RestException(403);
1110 }
1111 $result = $this->invoice->fetch($id);
1112 if (!$result) {
1113 throw new RestException(404, 'Invoice not found');
1114 }
1115
1116 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1117 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1118 }
1119
1120 $result = $this->invoice->add_contact($fk_socpeople, $type_contact, $source, $notrigger);
1121 if ($result < 0) {
1122 throw new RestException(500, 'Error : '.$this->invoice->error);
1123 }
1124
1125 $result = $this->invoice->fetch($id);
1126 if (!$result) {
1127 throw new RestException(404, 'Invoice not found');
1128 }
1129
1130 // test already done
1131 // if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1132 // throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1133 // }
1134
1135 return $this->_cleanObjectDatas($this->invoice);
1136 }
1137
1138
1139
1156 public function settodraft($id, $idwarehouse = -1)
1157 {
1158 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1159 throw new RestException(403);
1160 }
1161 $result = $this->invoice->fetch($id);
1162 if (!$result) {
1163 throw new RestException(404, 'Invoice not found');
1164 }
1165
1166 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1167 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1168 }
1169
1170 $result = $this->invoice->setDraft(DolibarrApiAccess::$user, $idwarehouse);
1171 if ($result == 0) {
1172 throw new RestException(304, 'Nothing done.');
1173 }
1174 if ($result < 0) {
1175 throw new RestException(500, 'Error : '.$this->invoice->error);
1176 }
1177
1178 $result = $this->invoice->fetch($id);
1179 if (!$result) {
1180 throw new RestException(404, 'Invoice not found');
1181 }
1182
1183 return $this->_cleanObjectDatas($this->invoice);
1184 }
1185
1186
1206 public function validate($id, $force_number = '', $idwarehouse = 0, $notrigger = 0)
1207 {
1208 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1209 throw new RestException(403);
1210 }
1211 $result = $this->invoice->fetch($id);
1212 if (!$result) {
1213 throw new RestException(404, 'Invoice not found');
1214 }
1215
1216 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1217 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1218 }
1219
1220 $result = $this->invoice->validate(DolibarrApiAccess::$user, $force_number, $idwarehouse, $notrigger);
1221 if ($result == 0) {
1222 throw new RestException(304, 'Error nothing done. May be object is already validated');
1223 }
1224 if ($result < 0) {
1225 throw new RestException(500, 'Error when validating Invoice: '.$this->invoice->error);
1226 }
1227
1228 $result = $this->invoice->fetch($id);
1229 if (!$result) {
1230 throw new RestException(404, 'Invoice not found');
1231 }
1232
1233 // test already done
1234 // if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1235 // throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1236 // }
1237
1238 // copy from order
1239 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
1240 $this->invoice->online_payment_url = getOnlinePaymentUrl(0, 'invoice', (string) $this->invoice->ref);
1241
1242 return $this->_cleanObjectDatas($this->invoice);
1243 }
1244
1262 public function settopaid($id, $close_code = '', $close_note = '')
1263 {
1264 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1265 throw new RestException(403);
1266 }
1267 $result = $this->invoice->fetch($id);
1268 if (!$result) {
1269 throw new RestException(404, 'Invoice not found');
1270 }
1271
1272 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1273 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1274 }
1275
1276 $result = $this->invoice->setPaid(DolibarrApiAccess::$user, $close_code, $close_note);
1277 if ($result == 0) {
1278 throw new RestException(304, 'Error nothing done. May be object is already validated');
1279 }
1280 if ($result < 0) {
1281 throw new RestException(500, 'Error : '.$this->invoice->error);
1282 }
1283
1284
1285 $result = $this->invoice->fetch($id);
1286 if (!$result) {
1287 throw new RestException(404, 'Invoice not found');
1288 }
1289
1290 // test already done
1291 // if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1292 // throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1293 // }
1294
1295 return $this->_cleanObjectDatas($this->invoice);
1296 }
1297
1298
1314 public function settounpaid($id)
1315 {
1316 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1317 throw new RestException(403);
1318 }
1319 $result = $this->invoice->fetch($id);
1320 if (!$result) {
1321 throw new RestException(404, 'Invoice not found');
1322 }
1323
1324 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1325 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1326 }
1327
1328 $result = $this->invoice->setUnpaid(DolibarrApiAccess::$user);
1329 if ($result == 0) {
1330 throw new RestException(304, 'Nothing done');
1331 }
1332 if ($result < 0) {
1333 throw new RestException(500, 'Error : '.$this->invoice->error);
1334 }
1335
1336
1337 $result = $this->invoice->fetch($id);
1338 if (!$result) {
1339 throw new RestException(404, 'Invoice not found');
1340 }
1341
1342 // test already done
1343 // if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1344 // throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1345 // }
1346
1347 return $this->_cleanObjectDatas($this->invoice);
1348 }
1349
1360 public function getDiscount($id)
1361 {
1362 require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1363
1364 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
1365 throw new RestException(403);
1366 }
1367
1368 $result = $this->invoice->fetch($id);
1369 if (!$result) {
1370 throw new RestException(404, 'Invoice not found');
1371 }
1372
1373 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1374 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1375 }
1376
1377 $discountcheck = new DiscountAbsolute($this->db);
1378 $result = $discountcheck->fetch(0, $this->invoice->id);
1379
1380 if ($result == 0) {
1381 throw new RestException(404, 'Discount not found');
1382 }
1383 if ($result < 0) {
1384 throw new RestException(500, $discountcheck->error);
1385 }
1386
1387 return parent::_cleanObjectDatas($discountcheck);
1388 }
1389
1406 {
1407 require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1408
1409 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1410 throw new RestException(403);
1411 }
1412
1413 $result = $this->invoice->fetch($id);
1414 if (!$result) {
1415 throw new RestException(404, 'Invoice not found');
1416 }
1417
1418 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1419 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1420 }
1421
1422 if ($this->invoice->paye) { // TODO Replace by a test on status
1423 throw new RestException(500, 'Alreay paid');
1424 }
1425
1426 $this->invoice->fetch($id);
1427 $this->invoice->fetch_thirdparty();
1428
1429 // Check if there is already a discount (protection to avoid duplicate creation when resubmit post)
1430 $discountcheck = new DiscountAbsolute($this->db);
1431 $result = $discountcheck->fetch(0, $this->invoice->id);
1432
1433 $canconvert = 0;
1434 if ($this->invoice->type == Facture::TYPE_DEPOSIT && empty($discountcheck->id)) {
1435 $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)
1436 }
1437 if (($this->invoice->type == Facture::TYPE_CREDIT_NOTE || $this->invoice->type == Facture::TYPE_STANDARD) && $this->invoice->paye == 0 && empty($discountcheck->id)) {
1438 $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)
1439 }
1440 if ($canconvert) {
1441 $this->db->begin();
1442
1443 $amount_ht = $amount_tva = $amount_ttc = array();
1444 $multicurrency_amount_ht = $multicurrency_amount_tva = $multicurrency_amount_ttc = array();
1445 '
1446 @phan-var-force array<string,float> $amount_ht
1447 @phan-var-force array<string,float> $amount_tva
1448 @phan-var-force array<string,float> $amount_ttc
1449 @phan-var-force array<string,float> $multicurrency_amount_ht
1450 @phan-var-force array<string,float> $multicurrency_amount_tva
1451 @phan-var-force array<string,float> $multicurrency_amount_ttc
1452 ';
1453
1454 // Loop on each vat rate
1455 $i = 0;
1456 foreach ($this->invoice->lines as $line) {
1457 if ($line->product_type < 9 && $line->total_ht != 0) { // Remove lines with product_type greater than or equal to 9
1458 if (!array_key_exists($line->tva_tx, $amount_ht)) {
1459 $amount_ht[$line->tva_tx] = 0.0;
1460 $amount_tva[$line->tva_tx] = 0.0;
1461 $amount_ttc[$line->tva_tx] = 0.0;
1462 $multicurrency_amount_ht[$line->tva_tx] = 0.0;
1463 $multicurrency_amount_tva[$line->tva_tx] = 0.0;
1464 $multicurrency_amount_ttc[$line->tva_tx] = 0.0;
1465 }
1466 // no need to create discount if amount is null
1467 $amount_ht[$line->tva_tx] += $line->total_ht;
1468 $amount_tva[$line->tva_tx] += $line->total_tva;
1469 $amount_ttc[$line->tva_tx] += $line->total_ttc;
1470 $multicurrency_amount_ht[$line->tva_tx] += $line->multicurrency_total_ht;
1471 $multicurrency_amount_tva[$line->tva_tx] += $line->multicurrency_total_tva;
1472 $multicurrency_amount_ttc[$line->tva_tx] += $line->multicurrency_total_ttc;
1473 $i++;
1474 }
1475 }
1476
1477 // Insert one discount by VAT rate category
1478 $discount = new DiscountAbsolute($this->db);
1479 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1480 $discount->description = '(CREDIT_NOTE)';
1481 } elseif ($this->invoice->type == Facture::TYPE_DEPOSIT) {
1482 $discount->description = '(DEPOSIT)';
1483 } elseif ($this->invoice->type == Facture::TYPE_STANDARD || $this->invoice->type == Facture::TYPE_REPLACEMENT || $this->invoice->type == Facture::TYPE_SITUATION) {
1484 $discount->description = '(EXCESS RECEIVED)';
1485 } else {
1486 throw new RestException(500, 'Cant convert to reduc an Invoice of this type');
1487 }
1488
1489 $discount->fk_soc = $this->invoice->socid;
1490 $discount->socid = $this->invoice->socid;
1491 $discount->fk_facture_source = $this->invoice->id;
1492
1493 $error = 0;
1494
1495 if ($this->invoice->type == Facture::TYPE_STANDARD || $this->invoice->type == Facture::TYPE_REPLACEMENT || $this->invoice->type == Facture::TYPE_SITUATION) {
1496 // If we're on a standard invoice, we have to get excess received to create a discount in TTC without VAT
1497
1498 // Total payments
1499 $sql = 'SELECT SUM(pf.amount) as total_payments';
1500 $sql .= ' FROM '.MAIN_DB_PREFIX.'paiement_facture as pf, '.MAIN_DB_PREFIX.'paiement as p';
1501 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as c ON p.fk_paiement = c.id';
1502 $sql .= ' WHERE pf.fk_facture = '.((int) $this->invoice->id);
1503 $sql .= ' AND pf.fk_paiement = p.rowid';
1504 $sql .= ' AND p.entity IN ('.getEntity('invoice').')';
1505 $resql = $this->db->query($sql);
1506 if (!$resql) {
1507 dol_print_error($this->db);
1508 }
1509
1510 $res = $this->db->fetch_object($resql);
1511 $total_payments = $res->total_payments;
1512
1513 // Total credit note and deposit
1514 $total_creditnote_and_deposit = 0;
1515 $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
1516 $sql .= " re.description, re.fk_facture_source";
1517 $sql .= " FROM ".MAIN_DB_PREFIX."societe_remise_except as re";
1518 $sql .= " WHERE fk_facture = ".((int) $this->invoice->id);
1519 $resql = $this->db->query($sql);
1520 if (!empty($resql)) {
1521 while ($obj = $this->db->fetch_object($resql)) {
1522 $total_creditnote_and_deposit += $obj->amount_ttc;
1523 }
1524 } else {
1525 dol_print_error($this->db);
1526 }
1527
1528 $discount->amount_ht = $discount->amount_ttc = $total_payments + $total_creditnote_and_deposit - $this->invoice->total_ttc;
1529 $discount->total_ht = $discount->total_ttc = $total_payments + $total_creditnote_and_deposit - $this->invoice->total_ttc;
1530 $discount->amount_tva = 0;
1531 $discount->total_tva = 0;
1532 $discount->tva_tx = 0;
1533
1534 $result = $discount->create(DolibarrApiAccess::$user);
1535 if ($result < 0) {
1536 $error++;
1537 }
1538 }
1539 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE || $this->invoice->type == Facture::TYPE_DEPOSIT) {
1540 foreach ($amount_ht as $tva_tx => $xxx) {
1541 $discount->amount_ht = abs($amount_ht[$tva_tx]);
1542 $discount->amount_tva = abs($amount_tva[$tva_tx]);
1543 $discount->amount_ttc = abs($amount_ttc[$tva_tx]);
1544 $discount->total_ht = abs($amount_ht[$tva_tx]);
1545 $discount->total_tva = abs($amount_tva[$tva_tx]);
1546 $discount->total_ttc = abs($amount_ttc[$tva_tx]);
1547 $discount->multicurrency_amount_ht = abs($multicurrency_amount_ht[$tva_tx]);
1548 $discount->multicurrency_amount_tva = abs($multicurrency_amount_tva[$tva_tx]);
1549 $discount->multicurrency_amount_ttc = abs($multicurrency_amount_ttc[$tva_tx]);
1550 $discount->multicurrency_total_ht = abs($multicurrency_amount_ht[$tva_tx]);
1551 $discount->multicurrency_total_tva = abs($multicurrency_amount_tva[$tva_tx]);
1552 $discount->multicurrency_total_ttc = abs($multicurrency_amount_ttc[$tva_tx]);
1553 $discount->tva_tx = abs((float) $tva_tx);
1554
1555 $result = $discount->create(DolibarrApiAccess::$user);
1556 if ($result < 0) {
1557 $error++;
1558 break;
1559 }
1560 }
1561 }
1562
1563 if (empty($error)) {
1564 if ($this->invoice->type != Facture::TYPE_DEPOSIT) {
1565 // Set the invoice as paid
1566 $result = $this->invoice->setPaid(DolibarrApiAccess::$user);
1567 if ($result >= 0) {
1568 $this->db->commit();
1569 } else {
1570 $this->db->rollback();
1571 throw new RestException(500, 'Could not set paid');
1572 }
1573 } else {
1574 $this->db->commit();
1575 }
1576 } else {
1577 $this->db->rollback();
1578 throw new RestException(500, 'Discount creation error');
1579 }
1580 }
1581
1582 return $this->_cleanObjectDatas($this->invoice);
1583 }
1584
1603 public function useDiscount($id, $discountid)
1604 {
1605 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1606 throw new RestException(403);
1607 }
1608 if (empty($id)) {
1609 throw new RestException(400, 'Invoice ID is mandatory');
1610 }
1611 if (empty($discountid)) {
1612 throw new RestException(400, 'Discount ID is mandatory');
1613 }
1614
1615 if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1616 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1617 }
1618
1619 $result = $this->invoice->fetch($id);
1620 if (!$result) {
1621 throw new RestException(404, 'Invoice not found');
1622 }
1623
1624 $result = $this->invoice->insert_discount($discountid);
1625 if ($result < 0) {
1626 throw new RestException(405, $this->invoice->error);
1627 }
1628
1629 return $result;
1630 }
1631
1650 public function useCreditNote($id, $discountid)
1651 {
1652 require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1653
1654 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1655 throw new RestException(403);
1656 }
1657 if (empty($id)) {
1658 throw new RestException(400, 'Invoice ID is mandatory');
1659 }
1660 if (empty($discountid)) {
1661 throw new RestException(400, 'Credit ID is mandatory');
1662 }
1663
1664 if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1665 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1666 }
1667 $discount = new DiscountAbsolute($this->db);
1668 $result = $discount->fetch($discountid);
1669 if (!$result) {
1670 throw new RestException(404, 'Credit not found');
1671 }
1672
1673 $result = $discount->link_to_invoice(0, $id);
1674 if ($result < 0) {
1675 throw new RestException(405, $discount->error);
1676 }
1677
1678 return $result;
1679 }
1680
1698 public function getPayments($id)
1699 {
1700 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
1701 throw new RestException(403);
1702 }
1703 if (empty($id)) {
1704 throw new RestException(400, 'Invoice ID is mandatory');
1705 }
1706
1707 if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1708 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1709 }
1710
1711 $result = $this->invoice->fetch($id);
1712 if (!$result) {
1713 throw new RestException(404, 'Invoice not found');
1714 }
1715
1716 $result = $this->invoice->getListOfPayments();
1717 if (!is_array($result) && $result < 0) {
1718 throw new RestException(405, $this->invoice->error);
1719 }
1720
1721 return $result;
1722 }
1723
1724
1748 public function addPayment($id, $datepaye, $paymentid, $closepaidinvoices, $accountid, $num_payment = '', $comment = '', $chqemetteur = '', $chqbank = '')
1749 {
1750 require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
1751
1752 if (!DolibarrApiAccess::$user->hasRight('facture', 'paiement')) {
1753 throw new RestException(403);
1754 }
1755 if (empty($id)) {
1756 throw new RestException(400, 'Invoice ID is mandatory');
1757 }
1758
1759 if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1760 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1761 }
1762
1763 if (isModEnabled("bank")) {
1764 if (empty($accountid)) {
1765 throw new RestException(400, 'Account ID is mandatory');
1766 }
1767 }
1768
1769 if (empty($paymentid)) {
1770 throw new RestException(400, 'Payment ID or Payment Code is mandatory');
1771 }
1772
1773
1774 $result = $this->invoice->fetch($id);
1775 if (!$result) {
1776 throw new RestException(404, 'Invoice not found');
1777 }
1778
1779 // Calculate amount to pay
1780 $totalpaid = $this->invoice->getSommePaiement();
1781 $totalcreditnotes = $this->invoice->getSumCreditNotesUsed();
1782 $totaldeposits = $this->invoice->getSumDepositsUsed();
1783
1784 $this->db->begin();
1785
1786 $amounts = array();
1787 $multicurrency_amounts = array();
1788
1789 // Clean parameters amount if payment is for a credit note
1790 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1791 $resteapayer = price2num($this->invoice->total_ttc + $totalpaid - $totalcreditnotes - $totaldeposits, 'MT');
1792 $amounts[$id] = (float) price2num(-1 * abs((float) $resteapayer), 'MT');
1793 // Multicurrency
1794 $newvalue = price2num($this->invoice->multicurrency_total_ttc, 'MT');
1795 $multicurrency_amounts[$id] = (float) price2num(-1 * (float) $newvalue, 'MT');
1796 } else {
1797 $resteapayer = price2num($this->invoice->total_ttc - $totalpaid - $totalcreditnotes - $totaldeposits, 'MT');
1798 $amounts[$id] = (float) $resteapayer;
1799 // Multicurrency
1800 $newvalue = price2num($this->invoice->multicurrency_total_ttc, 'MT');
1801 $multicurrency_amounts[$id] = (float) $newvalue;
1802 }
1803
1804 // Creation of payment line
1805 $paymentobj = new Paiement($this->db);
1806 if (is_numeric($datepaye)) {
1807 $paymentobj->datepaye = $datepaye;
1808 } else {
1809 $paymentobj->datepaye = dol_stringtotime($datepaye);
1810 }
1811 $paymentobj->amounts = $amounts; // Array with all payments dispatching with invoice id
1812 $paymentobj->multicurrency_amounts = $multicurrency_amounts; // Array with all payments dispatching
1813 $paymentobj->paiementid = $paymentid;
1814 $paymentobj->paiementcode = (string) dol_getIdFromCode($this->db, (string) $paymentid, 'c_paiement', 'id', 'code', 1);
1815 $paymentobj->num_payment = $num_payment;
1816 $paymentobj->note_private = $comment;
1817
1818 $payment_id = $paymentobj->create(DolibarrApiAccess::$user, ($closepaidinvoices == 'yes' ? 1 : 0)); // This include closing invoices
1819 if ($payment_id < 0) {
1820 $this->db->rollback();
1821 throw new RestException(400, 'Payment error : '.$paymentobj->error);
1822 }
1823
1824 if (isModEnabled("bank")) {
1825 $label = '(CustomerInvoicePayment)';
1826
1827 if ($paymentobj->paiementcode == 'CHQ' && empty($chqemetteur)) {
1828 throw new RestException(400, 'Emetteur is mandatory when payment code is '.$paymentobj->paiementcode);
1829 }
1830 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1831 $label = '(CustomerInvoicePaymentBack)'; // Refund of a credit note
1832 }
1833 $result = $paymentobj->addPaymentToBank(DolibarrApiAccess::$user, 'payment', $label, $accountid, $chqemetteur, $chqbank);
1834 if ($result < 0) {
1835 $this->db->rollback();
1836 throw new RestException(400, 'Add payment to bank error : '.$paymentobj->error);
1837 }
1838 }
1839
1840 $this->db->commit();
1841
1842 return $payment_id;
1843 }
1844
1875 public function addPaymentDistributed($arrayofamounts, $datepaye, $paymentid, $closepaidinvoices, $accountid, $num_payment = '', $comment = '', $chqemetteur = '', $chqbank = '', $ref_ext = '', $accepthigherpayment = false)
1876 {
1877 require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
1878
1879 if (!DolibarrApiAccess::$user->hasRight('facture', 'paiement')) {
1880 throw new RestException(403);
1881 }
1882 foreach ($arrayofamounts as $id => $amount) {
1883 if (empty($id)) {
1884 throw new RestException(400, 'Invoice ID is mandatory. Fill the invoice id and amount into arrayofamounts parameter. For example: {"1": "99.99", "2": "10"}');
1885 }
1886 if (!DolibarrApi::_checkAccessToResource('facture', (int) $id)) {
1887 throw new RestException(403, 'Access not allowed on invoice ID '.$id.' for login '.DolibarrApiAccess::$user->login);
1888 }
1889 }
1890
1891 if (isModEnabled("bank")) {
1892 if (empty($accountid)) {
1893 throw new RestException(400, 'Account ID is mandatory');
1894 }
1895 }
1896 if (empty($paymentid)) {
1897 throw new RestException(400, 'Payment ID or Payment Code is mandatory');
1898 }
1899
1900 $this->db->begin();
1901
1902 $amounts = array();
1903 $multicurrency_amounts = array();
1904
1905 // Loop on each invoice to pay
1906 foreach ($arrayofamounts as $id => $amountarray) {
1907 $id = (int) $id; // Ensure $id is seen as int, required by function calls and array indexes.
1908 $result = $this->invoice->fetch($id);
1909 if (!$result) {
1910 $this->db->rollback();
1911 throw new RestException(404, 'Invoice ID '.$id.' not found');
1912 }
1913
1914 if (($amountarray["amount"] == "remain" || $amountarray["amount"] > 0) && ($amountarray["multicurrency_amount"] == "remain" || $amountarray["multicurrency_amount"] > 0)) {
1915 $this->db->rollback();
1916 throw new RestException(400, 'Payment in both currency '.$id.' ( amount: '.$amountarray["amount"].', multicurrency_amount: '.$amountarray["multicurrency_amount"].')');
1917 }
1918
1919 $is_multicurrency = 0;
1920 $total_ttc = $this->invoice->total_ttc;
1921
1922 if ($amountarray["multicurrency_amount"] > 0 || $amountarray["multicurrency_amount"] == "remain") {
1923 $is_multicurrency = 1;
1924 $total_ttc = $this->invoice->multicurrency_total_ttc;
1925 }
1926
1927 // Calculate amount to pay
1928 $totalpaid = $this->invoice->getSommePaiement($is_multicurrency);
1929 $totalcreditnotes = $this->invoice->getSumCreditNotesUsed($is_multicurrency);
1930 $totaldeposits = $this->invoice->getSumDepositsUsed($is_multicurrency);
1931 $remainstopay = $amount = (float) price2num($total_ttc - $totalpaid - $totalcreditnotes - $totaldeposits, 'MT');
1932
1933 if (!$is_multicurrency && $amountarray["amount"] != 'remain') {
1934 $amount = (float) price2num($amountarray["amount"], 'MT');
1935 }
1936
1937 if ($is_multicurrency && $amountarray["multicurrency_amount"] != 'remain') {
1938 $amount = (float) price2num($amountarray["multicurrency_amount"], 'MT');
1939 }
1940
1941 if (abs($amount) > abs($remainstopay) && !$accepthigherpayment) {
1942 $this->db->rollback();
1943 throw new RestException(400, 'Payment amount on invoice ID '.$id.' ('.$amount.') is higher than remain to pay ('.$remainstopay.')');
1944 }
1945
1946 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1947 $amount = (float) price2num(-1 * abs((float) $amount), 'MT');
1948 }
1949
1950 if ($is_multicurrency) {
1951 $amounts[$id] = null;
1952 // Multicurrency
1953 $multicurrency_amounts[$id] = (float) $amount;
1954 } else {
1955 $amounts[$id] = (float) $amount;
1956 // Multicurrency
1957 $multicurrency_amounts[$id] = null;
1958 }
1959 }
1960
1961 // Creation of payment line
1962 $paymentobj = new Paiement($this->db);
1963 if (is_numeric($datepaye)) {
1964 $paymentobj->datepaye = $datepaye;
1965 } else {
1966 $paymentobj->datepaye = dol_stringtotime($datepaye);
1967 }
1968 $paymentobj->amounts = $amounts; // Array with all payments dispatching with invoice id
1969 $paymentobj->multicurrency_amounts = $multicurrency_amounts; // Array with all payments dispatching
1970 $paymentobj->paiementid = $paymentid;
1971 $paymentobj->paiementcode = (string) dol_getIdFromCode($this->db, (string) $paymentid, 'c_paiement', 'id', 'code', 1);
1972 $paymentobj->num_payment = $num_payment;
1973 $paymentobj->note_private = $comment;
1974 $paymentobj->ref_ext = $ref_ext;
1975 $payment_id = $paymentobj->create(DolibarrApiAccess::$user, ($closepaidinvoices == 'yes' ? 1 : 0)); // This include closing invoices
1976 if ($payment_id < 0) {
1977 $this->db->rollback();
1978 throw new RestException(400, 'Payment error : '.$paymentobj->error);
1979 }
1980 if (isModEnabled("bank")) {
1981 $label = '(CustomerInvoicePayment)';
1982 if ($paymentobj->paiementcode == 'CHQ' && empty($chqemetteur)) {
1983 throw new RestException(400, 'Emetteur is mandatory when payment code is '.$paymentobj->paiementcode);
1984 }
1985 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1986 $label = '(CustomerInvoicePaymentBack)'; // Refund of a credit note
1987 }
1988 $result = $paymentobj->addPaymentToBank(DolibarrApiAccess::$user, 'payment', $label, $accountid, $chqemetteur, $chqbank);
1989 if ($result < 0) {
1990 $this->db->rollback();
1991 throw new RestException(400, 'Add payment to bank error : '.$paymentobj->error);
1992 }
1993 }
1994
1995 $this->db->commit();
1996
1997 return $payment_id;
1998 }
1999
2018 public function putPayment($id, $num_payment = '')
2019 {
2020 require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
2021
2022 if (!DolibarrApiAccess::$user->hasRight('facture', 'paiement')) {
2023 throw new RestException(403);
2024 }
2025 if (empty($id)) {
2026 throw new RestException(400, 'Payment ID is mandatory');
2027 }
2028
2029 $paymentobj = new Paiement($this->db);
2030 $result = $paymentobj->fetch($id);
2031
2032 if (!$result) {
2033 throw new RestException(404, 'Payment not found');
2034 }
2035
2036 // Check all invoices of the payment to see if the user has permission on them for the object level permission test
2037 $tmparray = $paymentobj->getBillsArray();
2038 foreach ($tmparray as $tmpinvoiceid) {
2039 if (!DolibarrApi::_checkAccessToResource('facture', $tmpinvoiceid)) {
2040 throw new RestException(403, 'Payment is on invoices that are not all allowed for login '.DolibarrApiAccess::$user->login);
2041 }
2042 }
2043
2044 if (!empty($num_payment)) {
2045 $result = $paymentobj->update_num($num_payment);
2046 if ($result < 0) {
2047 throw new RestException(500, 'Error when updating the payment num');
2048 }
2049 }
2050
2051 return [
2052 'success' => [
2053 'code' => 200,
2054 'message' => 'Payment updated'
2055 ]
2056 ];
2057 }
2058
2059 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
2069 protected function _cleanObjectDatas($object)
2070 {
2071 // phpcs:enable
2072 $object = parent::_cleanObjectDatas($object);
2073
2074 unset($object->note);
2075 unset($object->address);
2076 unset($object->barcode_type);
2077 unset($object->barcode_type_code);
2078 unset($object->barcode_type_label);
2079 unset($object->barcode_type_coder);
2080 unset($object->canvas);
2081
2082 return $object;
2083 }
2084
2093 private function _validate($data)
2094 {
2095 if ($data === null) {
2096 $data = array();
2097 }
2098 $invoice = array();
2099 foreach (Invoices::$FIELDS as $field) {
2100 if (!isset($data[$field])) {
2101 throw new RestException(400, "$field field missing");
2102 }
2103 $invoice[$field] = $data[$field];
2104 }
2105 return $invoice;
2106 }
2107
2108
2124 public function getTemplateInvoice($id, $contact_list = 1)
2125 {
2126 return $this->_fetchTemplateInvoice($id, '', '', $contact_list);
2127 }
2128
2129
2157 public function indexTemplateInvoices($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $thirdparty_ids = '', $status = '', $sqlfilters = '', $properties = '', $pagination_data = false, $loadlinkedobjects = 0, $withLines = true)
2158 {
2159 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
2160 throw new RestException(403);
2161 }
2162
2163 $obj_ret = array();
2164
2165 // case of external user, $thirdparty_ids param is ignored and replaced by user's socid
2166 $socids = DolibarrApiAccess::$user->socid ?: $thirdparty_ids;
2167
2168
2169 // If the internal user must only see his customers, force searching by him
2170 $search_sale = 0;
2171 if (!DolibarrApiAccess::$user->hasRight('societe', 'client', 'voir') && !$socids) {
2172 $search_sale = DolibarrApiAccess::$user->id;
2173 }
2174
2175 $sql = "SELECT t.rowid";
2176 $sql .= " FROM ".MAIN_DB_PREFIX."facture_rec AS t";
2177 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."societe AS s ON (s.rowid = t.fk_soc)";
2178 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facture_rec_extrafields AS ef ON (ef.fk_object = t.rowid)";
2179 $sql .= ' WHERE t.entity IN ('.getEntity('invoice').')';
2180 if ($socids) {
2181 $sql .= " AND t.fk_soc IN (".$this->db->sanitize($socids).")";
2182 }
2183
2184 // Search on sale representative
2185 if ($search_sale && $search_sale != '-1') {
2186 if ($search_sale == -2) {
2187 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux AS sc WHERE sc.fk_soc = t.fk_soc)";
2188 } elseif ($search_sale > 0) {
2189 $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).")";
2190 }
2191 }
2192
2193 // Filter by status
2194 if ($status == 'active') {
2195 $sql .= " AND t.suspended = 0 AND t.frequency IS NOT NULL";
2196 }
2197 if ($status == 'suspended') {
2198 $sql .= " AND t.suspended = 1 AND t.frequency IS NOT NULL";
2199 }
2200 if ($status == 'draft') {
2201 $sql .= " AND t.frequency IS NULL";
2202 }
2203 // add sql filters
2204 if ($sqlfilters) {
2205 $errormessage = '';
2206 $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
2207 if ($errormessage) {
2208 throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);
2209 }
2210 }
2211
2212 //this query will return total template invoices with the filters given
2213 $sqlTotals = str_replace('SELECT t.rowid', 'SELECT count(t.rowid) as total', $sql);
2214
2215 $sql .= $this->db->order($sortfield, $sortorder);
2216 if ($limit) {
2217 if ($page < 0) {
2218 $page = 0;
2219 }
2220 $offset = $limit * $page;
2221
2222 $sql .= $this->db->plimit($limit + 1, $offset);
2223 }
2224
2225 $result = $this->db->query($sql);
2226 if ($result) {
2227 $i = 0;
2228 $num = $this->db->num_rows($result);
2229 $min = min($num, ($limit <= 0 ? $num : $limit));
2230 while ($i < $min) {
2231 $obj = $this->db->fetch_object($result);
2232 $factureRec = new FactureRec($this->db);
2233 if ($factureRec->fetch($obj->rowid) > 0) {
2234 if ($loadlinkedobjects) {
2235 // retrieve linked objects
2236 $factureRec->fetchObjectLinked();
2237 }
2238
2239 if (!$withLines) {
2240 unset($factureRec->lines);
2241 }
2242
2243 $obj_ret[] = $this->_filterObjectProperties($this->_cleanTemplateObjectDatas($factureRec), $properties);
2244 }
2245 $i++;
2246 }
2247 } else {
2248 throw new RestException(503, 'Error when retrieving recurring invoice templates: '.$this->db->lasterror());
2249 }
2250
2251 //if $pagination_data is true the response will contain element data with all values and element pagination with pagination data(total,page,limit)
2252 if ($pagination_data) {
2253 $totalsResult = $this->db->query($sqlTotals);
2254 $total = $this->db->fetch_object($totalsResult)->total;
2255
2256 $tmp = $obj_ret;
2257 $obj_ret = array();
2258
2259 $obj_ret['data'] = $tmp;
2260 $obj_ret['pagination'] = array(
2261 'total' => (int) $total,
2262 'page' => $page,
2263 'page_count' => ceil((int) $total / $limit),
2264 'limit' => $limit
2265 );
2266 }
2267
2268 return $obj_ret;
2269 }
2270
2284 private function _fetchTemplateInvoice($id, $ref = '', $ref_ext = '', $contact_list = 1)
2285 {
2286 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
2287 throw new RestException(403);
2288 }
2289
2290 $result = $this->template_invoice->fetch($id, $ref, $ref_ext);
2291 if (!$result) {
2292 throw new RestException(404, 'Template invoice not found');
2293 }
2294
2295 if (!DolibarrApi::_checkAccessToResource('facturerec', $this->template_invoice->id)) {
2296 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
2297 }
2298
2299 // Add external contacts ids
2300 if ($contact_list > -1) {
2301 $tmparray = $this->template_invoice->liste_contact(-1, 'external', $contact_list);
2302 if (is_array($tmparray)) {
2303 $this->template_invoice->contacts_ids = $tmparray;
2304 }
2305 }
2306
2307 $this->template_invoice->fetchObjectLinked();
2308 return $this->_cleanTemplateObjectDatas($this->template_invoice);
2309 }
2310
2311
2312 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
2320 {
2321 // phpcs:enable
2322 $object = parent::_cleanObjectDatas($object);
2323
2324 unset($object->note);
2325 unset($object->address);
2326 unset($object->barcode_type);
2327 unset($object->barcode_type_code);
2328 unset($object->barcode_type_label);
2329 unset($object->barcode_type_coder);
2330 unset($object->canvas);
2331
2332 return $object;
2333 }
2334}
$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.
const STATUS_DRAFT
Draft status.
Class to manage absolute discounts.
Class for API REST v1.
Definition api.class.php:35
_checkValExtrafieldsForAPI($field, $value, $object)
Check and convert a string depending on its type/name.
_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.
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:436
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
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.
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
buildzip.php
getMarginInfos($pv_ht, $remise_percent, $tva_tx, $localtax1_tx, $localtax2_tx, $fk_pa, $pa_ht)
Return an array with margins information of a line.