dolibarr 24.0.0-beta
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 $result = $this->invoice->createFromOrder($order, DolibarrApiAccess::$user);
459 if ($result < 0) {
460 throw new RestException(405, $this->invoice->error);
461 }
462 $this->invoice->fetchObjectLinked();
463 return $this->_cleanObjectDatas($this->invoice);
464 }
465
481 public function createInvoiceFromContract($contractid)
482 {
483 require_once DOL_DOCUMENT_ROOT.'/contrat/class/contrat.class.php';
484
485 if (!DolibarrApiAccess::$user->hasRight('contrat', 'lire')) {
486 throw new RestException(403);
487 }
488 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
489 throw new RestException(403);
490 }
491 if (empty($contractid)) {
492 throw new RestException(400, 'Contract ID is mandatory');
493 }
494
495 $contract = new Contrat($this->db);
496 $result = $contract->fetch($contractid);
497 if (!$result) {
498 throw new RestException(404, 'Contract not found');
499 }
500
501 $result = $this->invoice->createFromContract($contract, DolibarrApiAccess::$user);
502 if ($result < 0) {
503 throw new RestException(405, $this->invoice->error);
504 }
505 $this->invoice->fetchObjectLinked();
506 return $this->_cleanObjectDatas($this->invoice);
507 }
508
521 public function getLines($id)
522 {
523 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
524 throw new RestException(403);
525 }
526
527 $result = $this->invoice->fetch($id);
528 if (!$result) {
529 throw new RestException(404, 'Invoice not found');
530 }
531
532 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
533 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
534 }
535 $this->invoice->getLinesArray();
536 $result = array();
537 foreach ($this->invoice->lines as $line) {
538 array_push($result, $this->_cleanObjectDatas($line));
539 }
540 return $result;
541 }
542
561 public function putLine($id, $lineid, $request_data = null)
562 {
563 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
564 throw new RestException(403);
565 }
566
567 $result = $this->invoice->fetch($id);
568 if (!$result) {
569 throw new RestException(404, 'Invoice not found');
570 }
571
572 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
573 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
574 }
575
576 $request_data = (object) $request_data;
577
578 $request_data->desc = sanitizeVal($request_data->desc, 'restricthtml');
579 $request_data->label = sanitizeVal($request_data->label);
580
581 $updateRes = $this->invoice->updateline(
582 $lineid,
583 $request_data->desc,
584 $request_data->subprice,
585 $request_data->qty,
586 $request_data->remise_percent,
587 $request_data->date_start,
588 $request_data->date_end,
589 $request_data->tva_tx,
590 $request_data->localtax1_tx,
591 $request_data->localtax2_tx,
592 $request_data->price_base_type ? $request_data->price_base_type : 'HT',
593 $request_data->info_bits,
594 $request_data->product_type,
595 $request_data->fk_parent_line,
596 0,
597 $request_data->fk_fournprice,
598 $request_data->pa_ht,
599 $request_data->label,
600 $request_data->special_code,
601 $request_data->array_options,
602 $request_data->situation_percent,
603 $request_data->fk_unit,
604 $request_data->multicurrency_subprice,
605 0,
606 $request_data->ref_ext,
607 $request_data->rang
608 );
609
610 if ($updateRes > 0) {
611 $result = $this->get($id);
612 unset($result->line);
613 return $this->_cleanObjectDatas($result);
614 } else {
615 throw new RestException(304, $this->invoice->error);
616 }
617 }
618
638 public function postContact($id, $contactid, $type, $source = 'external', $notrigger = 0)
639 {
640 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
641 throw new RestException(403);
642 }
643
644 // test source
645 if (empty($source)) {
646 throw new RestException(400, 'Source can not be empty');
647 }
648 $sql_distinct_source = "SELECT DISTINCT source";
649 $sql_distinct_source .= " FROM ".MAIN_DB_PREFIX."c_type_contact";
650 $sql_distinct_source .= " WHERE element LIKE 'facture'";
651 $sql_distinct_source .= " AND source is NOT NULL";
652 $sql_distinct_source .= " AND active != 0";
653 $source_result = $this->db->query($sql_distinct_source);
654 $source_array = array();
655
656 if ($source_result) {
657 $num = $this->db->num_rows($source_result);
658 $i = 0;
659 while ($i < $num) {
660 $obj = $this->db->fetch_object($source_result);
661 $source_kind = (string) $obj->source;
662 array_push($source_array, $source_kind);
663 dol_syslog("source_kind=".$source_kind);
664 $i++;
665 }
666 } else {
667 throw new RestException(503, 'Error when retrieving a list of invoice contact sources: '.$this->db->lasterror());
668 }
669 if (!in_array($source, (array) $source_array, true)) {
670 throw new RestException(400, 'Combo of Source='.$source.' and Type='.$type.' not found in dictionary with active invoice contact types');
671 }
672
673 // test type
674 if (empty($type)) {
675 throw new RestException(400, 'type can not be empty');
676 }
677 // variable called type here, but code in dictionary and database
678 $sql_distinct_type = "SELECT DISTINCT code";
679 $sql_distinct_type .= " FROM ".MAIN_DB_PREFIX."c_type_contact";
680 $sql_distinct_type .= " WHERE element LIKE 'facture'";
681 $sql_distinct_type .= " AND source='".$this->db->escape($source)."'";
682 $sql_distinct_type .= " AND code is NOT NULL";
683 $sql_distinct_type .= " AND active != 0";
684 $type_result = $this->db->query($sql_distinct_type);
685 $type_array = array();
686
687 if ($type_result) {
688 $num = $this->db->num_rows($type_result);
689 $i = 0;
690 while ($i < $num) {
691 $obj = $this->db->fetch_object($type_result);
692 // variable called type here, but code in dictionary and database
693 $type_kind = (string) $obj->code;
694 array_push($type_array, $type_kind);
695 dol_syslog("type_kind=".$type_kind);
696 $i++;
697 }
698 } else {
699 throw new RestException(503, 'Error when retrieving a list of invoice contact types: '.$this->db->lasterror());
700 }
701 if (!in_array($type, (array) $type_array, true)) {
702 throw new RestException(400, 'Combo of Type='.$type.' and Source='.$source.' not found in dictionary with active invoice contact types');
703 }
704
705 // tests done, let's get it
706 $result = $this->invoice->fetch($id);
707 if (!$result) {
708 throw new RestException(404, 'Invoice not found');
709 }
710 if (!DolibarrApi::_checkAccessToResource('invoice', $this->invoice->id)) {
711 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
712 }
713
714 $result = $this->invoice->add_contact($contactid, $type, $source, $notrigger);
715
716 if ($result == 0) {
717 throw new RestException(400, 'Already exists: Contact='.$contactid.' is already linked to the invoice='.$id.' as source='.$source.' and type='.$type);
718 } elseif ($result == -1) {
719 throw new RestException(400, 'Wrong contact='.$contactid);
720 } elseif ($result == -2) {
721 throw new RestException(400, 'Wrong type='.$type);
722 } elseif ($result == -3) {
723 throw new RestException(400, 'Not allowed contacts');
724 } elseif ($result == -4) {
725 throw new RestException(400, 'ErrorCommercialNotAllowedForThirdparty');
726 } elseif ($result == -5) {
727 throw new RestException(400, 'Trigger failed');
728 } elseif ($result == -6) {
729 throw new RestException(400, 'DB_ERROR_RECORD_ALREADY_EXISTS');
730 } elseif ($result == -7) {
731 throw new RestException(400, 'Some other error');
732 }
733
734 if (!$result) {
735 throw new RestException(500, 'Error when added the contact');
736 }
737
738 return array(
739 'success' => array(
740 'code' => 200,
741 'message' => 'Contact='.$contactid.' linked to the invoice='.$id.' as '.$source.' '.$type
742 )
743 );
744 }
745
761 public function getContacts($id, $type = '')
762 {
763 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
764 throw new RestException(403);
765 }
766
767 $result = $this->invoice->fetch($id);
768 if (!$result) {
769 throw new RestException(404, 'Invoice not found');
770 }
771
772 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
773 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
774 }
775
776 $contacts = $this->invoice->liste_contact(-1, 'external', 0, $type);
777 $socpeoples = $this->invoice->liste_contact(-1, 'internal', 0, $type);
778
779 $contacts = array_merge($contacts, $socpeoples);
780
781 return $contacts;
782 }
783
800 public function deleteContact($id, $contactid, $type)
801 {
802 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
803 throw new RestException(403);
804 }
805
806 $result = $this->invoice->fetch($id);
807
808 if (!$result) {
809 throw new RestException(404, 'Invoice not found');
810 }
811
812 if (!DolibarrApi::_checkAccessToResource('invoice', $this->invoice->id)) {
813 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
814 }
815
816 $contacts = $this->invoice->liste_contact();
817
818 foreach ($contacts as $contact) {
819 if ($contact['id'] == $contactid && $contact['code'] == $type) {
820 $result = $this->invoice->delete_contact($contact['rowid']);
821
822 if (!$result) {
823 throw new RestException(500, 'Error when deleted the contact');
824 }
825 }
826 }
827
828 return $this->_cleanObjectDatas($this->invoice);
829 }
830
847 public function deleteLine($id, $lineid)
848 {
849 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
850 throw new RestException(403);
851 }
852 if (empty($lineid)) {
853 throw new RestException(400, 'Line ID is mandatory');
854 }
855
856 if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
857 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
858 }
859
860 $result = $this->invoice->fetch($id);
861 if (!$result) {
862 throw new RestException(404, 'Invoice not found');
863 }
864 if ($this->invoice->status != 0) {
865 throw new RestException(403, 'Invoice not in Draft Status : '.$this->invoice->getLibStatut(1));
866 }
867
868 $updateRes = $this->invoice->deleteLine($lineid, $id);
869 if ($updateRes > 0) {
870 return $this->get($id);
871 } else {
872 throw new RestException(405, $this->invoice->error);
873 }
874 }
875
887 public function put($id, $request_data = null)
888 {
889 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
890 throw new RestException(403);
891 }
892 if ($id == 0) {
893 throw new RestException(400, 'No invoice with id=0 can exist');
894 }
895 $result = $this->invoice->fetch($id);
896 if (!$result) {
897 throw new RestException(404, 'Invoice not found');
898 }
899
900 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
901 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
902 }
903
904 foreach ($request_data as $field => $value) {
905 if ($field == 'id') {
906 continue;
907 }
908 if ($field === 'caller') {
909 // 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
910 $this->invoice->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
911 continue;
912 }
913 if ($field == 'array_options' && is_array($value)) {
914 foreach ($value as $index => $val) {
915 $this->invoice->array_options[$index] = $this->_checkValExtrafieldsForAPI($index, $val, $this->invoice);
916 }
917 continue;
918 }
919
920 $this->invoice->$field = $this->_checkValForAPI($field, $value, $this->invoice);
921
922 // If cond reglement => update date lim reglement
923 if ($field == 'cond_reglement_id') {
924 $this->invoice->date_lim_reglement = $this->invoice->calculate_date_lim_reglement();
925 }
926 }
927
928 // update bank account
929 if (!empty($this->invoice->fk_account)) {
930 if ($this->invoice->setBankAccount((int) $this->invoice->fk_account) == 0) {
931 throw new RestException(400, $this->invoice->error);
932 }
933 }
934
935 if ($this->invoice->update(DolibarrApiAccess::$user) > 0) {
936 return $this->get($id);
937 } else {
938 throw new RestException(500, $this->invoice->error);
939 }
940 }
941
952 public function delete($id)
953 {
954 if (!DolibarrApiAccess::$user->hasRight('facture', 'supprimer')) {
955 throw new RestException(403);
956 }
957 if ($id == 0) {
958 throw new RestException(400, 'No invoice with id=0 can exist');
959 }
960 $result = $this->invoice->fetch($id);
961 if (!$result) {
962 throw new RestException(404, 'Invoice not found');
963 }
964
965 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
966 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
967 }
968
969 $result = $this->invoice->delete(DolibarrApiAccess::$user);
970 if ($result < 0) {
971 throw new RestException(500, 'Error when deleting invoice');
972 } elseif ($result == 0) {
973 throw new RestException(403, 'Invoice not erasable');
974 }
975
976 return array(
977 'success' => array(
978 'code' => 200,
979 'message' => 'Invoice deleted'
980 )
981 );
982 }
983
1011 public function postLine($id, $request_data = null)
1012 {
1013 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1014 throw new RestException(403);
1015 }
1016
1017 $result = $this->invoice->fetch($id);
1018 if (!$result) {
1019 throw new RestException(404, 'Invoice not found');
1020 }
1021
1022 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1023 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1024 }
1025
1026 $request_data = (object) $request_data;
1027
1028 $request_data->desc = sanitizeVal($request_data->desc, 'restricthtml');
1029 $request_data->label = sanitizeVal($request_data->label);
1030
1031 // Reset fk_parent_line for no child products and special product
1032 if (($request_data->product_type != 9 && empty($request_data->fk_parent_line)) || $request_data->product_type == 9) {
1033 $request_data->fk_parent_line = 0;
1034 }
1035
1036 // calculate pa_ht
1037 $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);
1038 $pa_ht = $marginInfos[0];
1039
1040 $updateRes = $this->invoice->addline(
1041 $request_data->desc,
1042 $request_data->subprice,
1043 $request_data->qty,
1044 $request_data->tva_tx,
1045 $request_data->localtax1_tx,
1046 $request_data->localtax2_tx,
1047 $request_data->fk_product,
1048 $request_data->remise_percent,
1049 $request_data->date_start,
1050 $request_data->date_end,
1051 $request_data->fk_code_ventilation,
1052 $request_data->info_bits,
1053 $request_data->fk_remise_except,
1054 $request_data->price_base_type ? $request_data->price_base_type : 'HT',
1055 $request_data->subprice,
1056 $request_data->product_type,
1057 $request_data->rang,
1058 $request_data->special_code,
1059 $request_data->origin,
1060 $request_data->origin_id,
1061 $request_data->fk_parent_line,
1062 empty($request_data->fk_fournprice) ? null : $request_data->fk_fournprice,
1063 $pa_ht,
1064 $request_data->label,
1065 $request_data->array_options,
1066 $request_data->situation_percent,
1067 $request_data->fk_prev_id,
1068 $request_data->fk_unit,
1069 0,
1070 $request_data->ref_ext
1071 );
1072
1073 if ($updateRes < 0) {
1074 throw new RestException(400, 'Unable to insert the new line. Check your inputs. '.$this->invoice->error);
1075 }
1076
1077 return $updateRes;
1078 }
1079
1100 public function addContact($id, $fk_socpeople, $type_contact, $source, $notrigger = 0)
1101 {
1102 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1103 throw new RestException(403);
1104 }
1105 $result = $this->invoice->fetch($id);
1106 if (!$result) {
1107 throw new RestException(404, 'Invoice not found');
1108 }
1109
1110 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1111 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1112 }
1113
1114 $result = $this->invoice->add_contact($fk_socpeople, $type_contact, $source, $notrigger);
1115 if ($result < 0) {
1116 throw new RestException(500, 'Error : '.$this->invoice->error);
1117 }
1118
1119 $result = $this->invoice->fetch($id);
1120 if (!$result) {
1121 throw new RestException(404, 'Invoice not found');
1122 }
1123
1124 // test already done
1125 // if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1126 // throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1127 // }
1128
1129 return $this->_cleanObjectDatas($this->invoice);
1130 }
1131
1132
1133
1150 public function settodraft($id, $idwarehouse = -1)
1151 {
1152 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1153 throw new RestException(403);
1154 }
1155 $result = $this->invoice->fetch($id);
1156 if (!$result) {
1157 throw new RestException(404, 'Invoice not found');
1158 }
1159
1160 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1161 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1162 }
1163
1164 $result = $this->invoice->setDraft(DolibarrApiAccess::$user, $idwarehouse);
1165 if ($result == 0) {
1166 throw new RestException(304, 'Nothing done.');
1167 }
1168 if ($result < 0) {
1169 throw new RestException(500, 'Error : '.$this->invoice->error);
1170 }
1171
1172 $result = $this->invoice->fetch($id);
1173 if (!$result) {
1174 throw new RestException(404, 'Invoice not found');
1175 }
1176
1177 return $this->_cleanObjectDatas($this->invoice);
1178 }
1179
1180
1200 public function validate($id, $force_number = '', $idwarehouse = 0, $notrigger = 0)
1201 {
1202 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1203 throw new RestException(403);
1204 }
1205 $result = $this->invoice->fetch($id);
1206 if (!$result) {
1207 throw new RestException(404, 'Invoice not found');
1208 }
1209
1210 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1211 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1212 }
1213
1214 $result = $this->invoice->validate(DolibarrApiAccess::$user, $force_number, $idwarehouse, $notrigger);
1215 if ($result == 0) {
1216 throw new RestException(304, 'Error nothing done. May be object is already validated');
1217 }
1218 if ($result < 0) {
1219 throw new RestException(500, 'Error when validating Invoice: '.$this->invoice->error);
1220 }
1221
1222 $result = $this->invoice->fetch($id);
1223 if (!$result) {
1224 throw new RestException(404, 'Invoice not found');
1225 }
1226
1227 // test already done
1228 // if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1229 // throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1230 // }
1231
1232 // copy from order
1233 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
1234 $this->invoice->online_payment_url = getOnlinePaymentUrl(0, 'invoice', (string) $this->invoice->ref);
1235
1236 return $this->_cleanObjectDatas($this->invoice);
1237 }
1238
1256 public function settopaid($id, $close_code = '', $close_note = '')
1257 {
1258 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1259 throw new RestException(403);
1260 }
1261 $result = $this->invoice->fetch($id);
1262 if (!$result) {
1263 throw new RestException(404, 'Invoice not found');
1264 }
1265
1266 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1267 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1268 }
1269
1270 $result = $this->invoice->setPaid(DolibarrApiAccess::$user, $close_code, $close_note);
1271 if ($result == 0) {
1272 throw new RestException(304, 'Error nothing done. May be object is already validated');
1273 }
1274 if ($result < 0) {
1275 throw new RestException(500, 'Error : '.$this->invoice->error);
1276 }
1277
1278
1279 $result = $this->invoice->fetch($id);
1280 if (!$result) {
1281 throw new RestException(404, 'Invoice not found');
1282 }
1283
1284 // test already done
1285 // if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1286 // throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1287 // }
1288
1289 return $this->_cleanObjectDatas($this->invoice);
1290 }
1291
1292
1308 public function settounpaid($id)
1309 {
1310 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1311 throw new RestException(403);
1312 }
1313 $result = $this->invoice->fetch($id);
1314 if (!$result) {
1315 throw new RestException(404, 'Invoice not found');
1316 }
1317
1318 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1319 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1320 }
1321
1322 $result = $this->invoice->setUnpaid(DolibarrApiAccess::$user);
1323 if ($result == 0) {
1324 throw new RestException(304, 'Nothing done');
1325 }
1326 if ($result < 0) {
1327 throw new RestException(500, 'Error : '.$this->invoice->error);
1328 }
1329
1330
1331 $result = $this->invoice->fetch($id);
1332 if (!$result) {
1333 throw new RestException(404, 'Invoice not found');
1334 }
1335
1336 // test already done
1337 // if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1338 // throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1339 // }
1340
1341 return $this->_cleanObjectDatas($this->invoice);
1342 }
1343
1354 public function getDiscount($id)
1355 {
1356 require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1357
1358 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
1359 throw new RestException(403);
1360 }
1361
1362 $result = $this->invoice->fetch($id);
1363 if (!$result) {
1364 throw new RestException(404, 'Invoice not found');
1365 }
1366
1367 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1368 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1369 }
1370
1371 $discountcheck = new DiscountAbsolute($this->db);
1372 $result = $discountcheck->fetch(0, $this->invoice->id);
1373
1374 if ($result == 0) {
1375 throw new RestException(404, 'Discount not found');
1376 }
1377 if ($result < 0) {
1378 throw new RestException(500, $discountcheck->error);
1379 }
1380
1381 return parent::_cleanObjectDatas($discountcheck);
1382 }
1383
1400 {
1401 require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1402
1403 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1404 throw new RestException(403);
1405 }
1406
1407 $result = $this->invoice->fetch($id);
1408 if (!$result) {
1409 throw new RestException(404, 'Invoice not found');
1410 }
1411
1412 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1413 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1414 }
1415
1416 if ($this->invoice->paye) { // TODO Replace by a test on status
1417 throw new RestException(500, 'Alreay paid');
1418 }
1419
1420 $this->invoice->fetch($id);
1421 $this->invoice->fetch_thirdparty();
1422
1423 // Check if there is already a discount (protection to avoid duplicate creation when resubmit post)
1424 $discountcheck = new DiscountAbsolute($this->db);
1425 $result = $discountcheck->fetch(0, $this->invoice->id);
1426
1427 $canconvert = 0;
1428 if ($this->invoice->type == Facture::TYPE_DEPOSIT && empty($discountcheck->id)) {
1429 $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)
1430 }
1431 if (($this->invoice->type == Facture::TYPE_CREDIT_NOTE || $this->invoice->type == Facture::TYPE_STANDARD) && $this->invoice->paye == 0 && empty($discountcheck->id)) {
1432 $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)
1433 }
1434 if ($canconvert) {
1435 $this->db->begin();
1436
1437 $amount_ht = $amount_tva = $amount_ttc = array();
1438 $multicurrency_amount_ht = $multicurrency_amount_tva = $multicurrency_amount_ttc = array();
1439 '
1440 @phan-var-force array<string,float> $amount_ht
1441 @phan-var-force array<string,float> $amount_tva
1442 @phan-var-force array<string,float> $amount_ttc
1443 @phan-var-force array<string,float> $multicurrency_amount_ht
1444 @phan-var-force array<string,float> $multicurrency_amount_tva
1445 @phan-var-force array<string,float> $multicurrency_amount_ttc
1446 ';
1447
1448 // Loop on each vat rate
1449 $i = 0;
1450 foreach ($this->invoice->lines as $line) {
1451 if ($line->product_type < 9 && $line->total_ht != 0) { // Remove lines with product_type greater than or equal to 9
1452 if (!array_key_exists($line->tva_tx, $amount_ht)) {
1453 $amount_ht[$line->tva_tx] = 0.0;
1454 $amount_tva[$line->tva_tx] = 0.0;
1455 $amount_ttc[$line->tva_tx] = 0.0;
1456 $multicurrency_amount_ht[$line->tva_tx] = 0.0;
1457 $multicurrency_amount_tva[$line->tva_tx] = 0.0;
1458 $multicurrency_amount_ttc[$line->tva_tx] = 0.0;
1459 }
1460 // no need to create discount if amount is null
1461 $amount_ht[$line->tva_tx] += $line->total_ht;
1462 $amount_tva[$line->tva_tx] += $line->total_tva;
1463 $amount_ttc[$line->tva_tx] += $line->total_ttc;
1464 $multicurrency_amount_ht[$line->tva_tx] += $line->multicurrency_total_ht;
1465 $multicurrency_amount_tva[$line->tva_tx] += $line->multicurrency_total_tva;
1466 $multicurrency_amount_ttc[$line->tva_tx] += $line->multicurrency_total_ttc;
1467 $i++;
1468 }
1469 }
1470
1471 // Insert one discount by VAT rate category
1472 $discount = new DiscountAbsolute($this->db);
1473 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1474 $discount->description = '(CREDIT_NOTE)';
1475 } elseif ($this->invoice->type == Facture::TYPE_DEPOSIT) {
1476 $discount->description = '(DEPOSIT)';
1477 } elseif ($this->invoice->type == Facture::TYPE_STANDARD || $this->invoice->type == Facture::TYPE_REPLACEMENT || $this->invoice->type == Facture::TYPE_SITUATION) {
1478 $discount->description = '(EXCESS RECEIVED)';
1479 } else {
1480 throw new RestException(500, 'Cant convert to reduc an Invoice of this type');
1481 }
1482
1483 $discount->fk_soc = $this->invoice->socid;
1484 $discount->socid = $this->invoice->socid;
1485 $discount->fk_facture_source = $this->invoice->id;
1486
1487 $error = 0;
1488
1489 if ($this->invoice->type == Facture::TYPE_STANDARD || $this->invoice->type == Facture::TYPE_REPLACEMENT || $this->invoice->type == Facture::TYPE_SITUATION) {
1490 // If we're on a standard invoice, we have to get excess received to create a discount in TTC without VAT
1491
1492 // Total payments
1493 $sql = 'SELECT SUM(pf.amount) as total_payments';
1494 $sql .= ' FROM '.MAIN_DB_PREFIX.'paiement_facture as pf, '.MAIN_DB_PREFIX.'paiement as p';
1495 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as c ON p.fk_paiement = c.id';
1496 $sql .= ' WHERE pf.fk_facture = '.((int) $this->invoice->id);
1497 $sql .= ' AND pf.fk_paiement = p.rowid';
1498 $sql .= ' AND p.entity IN ('.getEntity('invoice').')';
1499 $resql = $this->db->query($sql);
1500 if (!$resql) {
1501 dol_print_error($this->db);
1502 }
1503
1504 $res = $this->db->fetch_object($resql);
1505 $total_payments = $res->total_payments;
1506
1507 // Total credit note and deposit
1508 $total_creditnote_and_deposit = 0;
1509 $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
1510 $sql .= " re.description, re.fk_facture_source";
1511 $sql .= " FROM ".MAIN_DB_PREFIX."societe_remise_except as re";
1512 $sql .= " WHERE fk_facture = ".((int) $this->invoice->id);
1513 $resql = $this->db->query($sql);
1514 if (!empty($resql)) {
1515 while ($obj = $this->db->fetch_object($resql)) {
1516 $total_creditnote_and_deposit += $obj->amount_ttc;
1517 }
1518 } else {
1519 dol_print_error($this->db);
1520 }
1521
1522 $discount->amount_ht = $discount->amount_ttc = $total_payments + $total_creditnote_and_deposit - $this->invoice->total_ttc;
1523 $discount->total_ht = $discount->total_ttc = $total_payments + $total_creditnote_and_deposit - $this->invoice->total_ttc;
1524 $discount->amount_tva = 0;
1525 $discount->total_tva = 0;
1526 $discount->tva_tx = 0;
1527
1528 $result = $discount->create(DolibarrApiAccess::$user);
1529 if ($result < 0) {
1530 $error++;
1531 }
1532 }
1533 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE || $this->invoice->type == Facture::TYPE_DEPOSIT) {
1534 foreach ($amount_ht as $tva_tx => $xxx) {
1535 $discount->amount_ht = abs($amount_ht[$tva_tx]);
1536 $discount->amount_tva = abs($amount_tva[$tva_tx]);
1537 $discount->amount_ttc = abs($amount_ttc[$tva_tx]);
1538 $discount->total_ht = abs($amount_ht[$tva_tx]);
1539 $discount->total_tva = abs($amount_tva[$tva_tx]);
1540 $discount->total_ttc = abs($amount_ttc[$tva_tx]);
1541 $discount->multicurrency_amount_ht = abs($multicurrency_amount_ht[$tva_tx]);
1542 $discount->multicurrency_amount_tva = abs($multicurrency_amount_tva[$tva_tx]);
1543 $discount->multicurrency_amount_ttc = abs($multicurrency_amount_ttc[$tva_tx]);
1544 $discount->multicurrency_total_ht = abs($multicurrency_amount_ht[$tva_tx]);
1545 $discount->multicurrency_total_tva = abs($multicurrency_amount_tva[$tva_tx]);
1546 $discount->multicurrency_total_ttc = abs($multicurrency_amount_ttc[$tva_tx]);
1547 $discount->tva_tx = abs((float) $tva_tx);
1548
1549 $result = $discount->create(DolibarrApiAccess::$user);
1550 if ($result < 0) {
1551 $error++;
1552 break;
1553 }
1554 }
1555 }
1556
1557 if (empty($error)) {
1558 if ($this->invoice->type != Facture::TYPE_DEPOSIT) {
1559 // Set the invoice as paid
1560 $result = $this->invoice->setPaid(DolibarrApiAccess::$user);
1561 if ($result >= 0) {
1562 $this->db->commit();
1563 } else {
1564 $this->db->rollback();
1565 throw new RestException(500, 'Could not set paid');
1566 }
1567 } else {
1568 $this->db->commit();
1569 }
1570 } else {
1571 $this->db->rollback();
1572 throw new RestException(500, 'Discount creation error');
1573 }
1574 }
1575
1576 return $this->_cleanObjectDatas($this->invoice);
1577 }
1578
1597 public function useDiscount($id, $discountid)
1598 {
1599 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1600 throw new RestException(403);
1601 }
1602 if (empty($id)) {
1603 throw new RestException(400, 'Invoice ID is mandatory');
1604 }
1605 if (empty($discountid)) {
1606 throw new RestException(400, 'Discount ID is mandatory');
1607 }
1608
1609 if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1610 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1611 }
1612
1613 $result = $this->invoice->fetch($id);
1614 if (!$result) {
1615 throw new RestException(404, 'Invoice not found');
1616 }
1617
1618 $result = $this->invoice->insert_discount($discountid);
1619 if ($result < 0) {
1620 throw new RestException(405, $this->invoice->error);
1621 }
1622
1623 return $result;
1624 }
1625
1644 public function useCreditNote($id, $discountid)
1645 {
1646 require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1647
1648 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1649 throw new RestException(403);
1650 }
1651 if (empty($id)) {
1652 throw new RestException(400, 'Invoice ID is mandatory');
1653 }
1654 if (empty($discountid)) {
1655 throw new RestException(400, 'Credit ID is mandatory');
1656 }
1657
1658 if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1659 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1660 }
1661 $discount = new DiscountAbsolute($this->db);
1662 $result = $discount->fetch($discountid);
1663 if (!$result) {
1664 throw new RestException(404, 'Credit not found');
1665 }
1666
1667 $result = $discount->link_to_invoice(0, $id);
1668 if ($result < 0) {
1669 throw new RestException(405, $discount->error);
1670 }
1671
1672 return $result;
1673 }
1674
1692 public function getPayments($id)
1693 {
1694 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
1695 throw new RestException(403);
1696 }
1697 if (empty($id)) {
1698 throw new RestException(400, 'Invoice ID is mandatory');
1699 }
1700
1701 if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1702 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1703 }
1704
1705 $result = $this->invoice->fetch($id);
1706 if (!$result) {
1707 throw new RestException(404, 'Invoice not found');
1708 }
1709
1710 $result = $this->invoice->getListOfPayments();
1711 if (!is_array($result) && $result < 0) {
1712 throw new RestException(405, $this->invoice->error);
1713 }
1714
1715 return $result;
1716 }
1717
1718
1742 public function addPayment($id, $datepaye, $paymentid, $closepaidinvoices, $accountid, $num_payment = '', $comment = '', $chqemetteur = '', $chqbank = '')
1743 {
1744 require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
1745
1746 if (!DolibarrApiAccess::$user->hasRight('facture', 'paiement')) {
1747 throw new RestException(403);
1748 }
1749 if (empty($id)) {
1750 throw new RestException(400, 'Invoice ID is mandatory');
1751 }
1752
1753 if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1754 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1755 }
1756
1757 if (isModEnabled("bank")) {
1758 if (empty($accountid)) {
1759 throw new RestException(400, 'Account ID is mandatory');
1760 }
1761 }
1762
1763 if (empty($paymentid)) {
1764 throw new RestException(400, 'Payment ID or Payment Code is mandatory');
1765 }
1766
1767
1768 $result = $this->invoice->fetch($id);
1769 if (!$result) {
1770 throw new RestException(404, 'Invoice not found');
1771 }
1772
1773 // Calculate amount to pay
1774 $totalpaid = $this->invoice->getSommePaiement();
1775 $totalcreditnotes = $this->invoice->getSumCreditNotesUsed();
1776 $totaldeposits = $this->invoice->getSumDepositsUsed();
1777
1778 $this->db->begin();
1779
1780 $amounts = array();
1781 $multicurrency_amounts = array();
1782
1783 // Clean parameters amount if payment is for a credit note
1784 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1785 $resteapayer = price2num($this->invoice->total_ttc + $totalpaid - $totalcreditnotes - $totaldeposits, 'MT');
1786 $amounts[$id] = (float) price2num(-1 * abs((float) $resteapayer), 'MT');
1787 // Multicurrency
1788 $newvalue = price2num($this->invoice->multicurrency_total_ttc, 'MT');
1789 $multicurrency_amounts[$id] = (float) price2num(-1 * (float) $newvalue, 'MT');
1790 } else {
1791 $resteapayer = price2num($this->invoice->total_ttc - $totalpaid - $totalcreditnotes - $totaldeposits, 'MT');
1792 $amounts[$id] = (float) $resteapayer;
1793 // Multicurrency
1794 $newvalue = price2num($this->invoice->multicurrency_total_ttc, 'MT');
1795 $multicurrency_amounts[$id] = (float) $newvalue;
1796 }
1797
1798 // Creation of payment line
1799 $paymentobj = new Paiement($this->db);
1800 if (is_numeric($datepaye)) {
1801 $paymentobj->datepaye = $datepaye;
1802 } else {
1803 $paymentobj->datepaye = dol_stringtotime($datepaye);
1804 }
1805 $paymentobj->amounts = $amounts; // Array with all payments dispatching with invoice id
1806 $paymentobj->multicurrency_amounts = $multicurrency_amounts; // Array with all payments dispatching
1807 $paymentobj->paiementid = $paymentid;
1808 $paymentobj->paiementcode = (string) dol_getIdFromCode($this->db, (string) $paymentid, 'c_paiement', 'id', 'code', 1);
1809 $paymentobj->num_payment = $num_payment;
1810 $paymentobj->note_private = $comment;
1811
1812 $payment_id = $paymentobj->create(DolibarrApiAccess::$user, ($closepaidinvoices == 'yes' ? 1 : 0)); // This include closing invoices
1813 if ($payment_id < 0) {
1814 $this->db->rollback();
1815 throw new RestException(400, 'Payment error : '.$paymentobj->error);
1816 }
1817
1818 if (isModEnabled("bank")) {
1819 $label = '(CustomerInvoicePayment)';
1820
1821 if ($paymentobj->paiementcode == 'CHQ' && empty($chqemetteur)) {
1822 throw new RestException(400, 'Emetteur is mandatory when payment code is '.$paymentobj->paiementcode);
1823 }
1824 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1825 $label = '(CustomerInvoicePaymentBack)'; // Refund of a credit note
1826 }
1827 $result = $paymentobj->addPaymentToBank(DolibarrApiAccess::$user, 'payment', $label, $accountid, $chqemetteur, $chqbank);
1828 if ($result < 0) {
1829 $this->db->rollback();
1830 throw new RestException(400, 'Add payment to bank error : '.$paymentobj->error);
1831 }
1832 }
1833
1834 $this->db->commit();
1835
1836 return $payment_id;
1837 }
1838
1869 public function addPaymentDistributed($arrayofamounts, $datepaye, $paymentid, $closepaidinvoices, $accountid, $num_payment = '', $comment = '', $chqemetteur = '', $chqbank = '', $ref_ext = '', $accepthigherpayment = false)
1870 {
1871 require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
1872
1873 if (!DolibarrApiAccess::$user->hasRight('facture', 'paiement')) {
1874 throw new RestException(403);
1875 }
1876 foreach ($arrayofamounts as $id => $amount) {
1877 if (empty($id)) {
1878 throw new RestException(400, 'Invoice ID is mandatory. Fill the invoice id and amount into arrayofamounts parameter. For example: {"1": "99.99", "2": "10"}');
1879 }
1880 if (!DolibarrApi::_checkAccessToResource('facture', (int) $id)) {
1881 throw new RestException(403, 'Access not allowed on invoice ID '.$id.' for login '.DolibarrApiAccess::$user->login);
1882 }
1883 }
1884
1885 if (isModEnabled("bank")) {
1886 if (empty($accountid)) {
1887 throw new RestException(400, 'Account ID is mandatory');
1888 }
1889 }
1890 if (empty($paymentid)) {
1891 throw new RestException(400, 'Payment ID or Payment Code is mandatory');
1892 }
1893
1894 $this->db->begin();
1895
1896 $amounts = array();
1897 $multicurrency_amounts = array();
1898
1899 // Loop on each invoice to pay
1900 foreach ($arrayofamounts as $id => $amountarray) {
1901 $id = (int) $id; // Ensure $id is seen as int, required by function calls and array indexes.
1902 $result = $this->invoice->fetch($id);
1903 if (!$result) {
1904 $this->db->rollback();
1905 throw new RestException(404, 'Invoice ID '.$id.' not found');
1906 }
1907
1908 if (($amountarray["amount"] == "remain" || $amountarray["amount"] > 0) && ($amountarray["multicurrency_amount"] == "remain" || $amountarray["multicurrency_amount"] > 0)) {
1909 $this->db->rollback();
1910 throw new RestException(400, 'Payment in both currency '.$id.' ( amount: '.$amountarray["amount"].', multicurrency_amount: '.$amountarray["multicurrency_amount"].')');
1911 }
1912
1913 $is_multicurrency = 0;
1914 $total_ttc = $this->invoice->total_ttc;
1915
1916 if ($amountarray["multicurrency_amount"] > 0 || $amountarray["multicurrency_amount"] == "remain") {
1917 $is_multicurrency = 1;
1918 $total_ttc = $this->invoice->multicurrency_total_ttc;
1919 }
1920
1921 // Calculate amount to pay
1922 $totalpaid = $this->invoice->getSommePaiement($is_multicurrency);
1923 $totalcreditnotes = $this->invoice->getSumCreditNotesUsed($is_multicurrency);
1924 $totaldeposits = $this->invoice->getSumDepositsUsed($is_multicurrency);
1925 $remainstopay = $amount = (float) price2num($total_ttc - $totalpaid - $totalcreditnotes - $totaldeposits, 'MT');
1926
1927 if (!$is_multicurrency && $amountarray["amount"] != 'remain') {
1928 $amount = (float) price2num($amountarray["amount"], 'MT');
1929 }
1930
1931 if ($is_multicurrency && $amountarray["multicurrency_amount"] != 'remain') {
1932 $amount = (float) price2num($amountarray["multicurrency_amount"], 'MT');
1933 }
1934
1935 if (abs($amount) > abs($remainstopay) && !$accepthigherpayment) {
1936 $this->db->rollback();
1937 throw new RestException(400, 'Payment amount on invoice ID '.$id.' ('.$amount.') is higher than remain to pay ('.$remainstopay.')');
1938 }
1939
1940 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1941 $amount = (float) price2num(-1 * abs((float) $amount), 'MT');
1942 }
1943
1944 if ($is_multicurrency) {
1945 $amounts[$id] = null;
1946 // Multicurrency
1947 $multicurrency_amounts[$id] = (float) $amount;
1948 } else {
1949 $amounts[$id] = (float) $amount;
1950 // Multicurrency
1951 $multicurrency_amounts[$id] = null;
1952 }
1953 }
1954
1955 // Creation of payment line
1956 $paymentobj = new Paiement($this->db);
1957 if (is_numeric($datepaye)) {
1958 $paymentobj->datepaye = $datepaye;
1959 } else {
1960 $paymentobj->datepaye = dol_stringtotime($datepaye);
1961 }
1962 $paymentobj->amounts = $amounts; // Array with all payments dispatching with invoice id
1963 $paymentobj->multicurrency_amounts = $multicurrency_amounts; // Array with all payments dispatching
1964 $paymentobj->paiementid = $paymentid;
1965 $paymentobj->paiementcode = (string) dol_getIdFromCode($this->db, (string) $paymentid, 'c_paiement', 'id', 'code', 1);
1966 $paymentobj->num_payment = $num_payment;
1967 $paymentobj->note_private = $comment;
1968 $paymentobj->ref_ext = $ref_ext;
1969 $payment_id = $paymentobj->create(DolibarrApiAccess::$user, ($closepaidinvoices == 'yes' ? 1 : 0)); // This include closing invoices
1970 if ($payment_id < 0) {
1971 $this->db->rollback();
1972 throw new RestException(400, 'Payment error : '.$paymentobj->error);
1973 }
1974 if (isModEnabled("bank")) {
1975 $label = '(CustomerInvoicePayment)';
1976 if ($paymentobj->paiementcode == 'CHQ' && empty($chqemetteur)) {
1977 throw new RestException(400, 'Emetteur is mandatory when payment code is '.$paymentobj->paiementcode);
1978 }
1979 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1980 $label = '(CustomerInvoicePaymentBack)'; // Refund of a credit note
1981 }
1982 $result = $paymentobj->addPaymentToBank(DolibarrApiAccess::$user, 'payment', $label, $accountid, $chqemetteur, $chqbank);
1983 if ($result < 0) {
1984 $this->db->rollback();
1985 throw new RestException(400, 'Add payment to bank error : '.$paymentobj->error);
1986 }
1987 }
1988
1989 $this->db->commit();
1990
1991 return $payment_id;
1992 }
1993
2012 public function putPayment($id, $num_payment = '')
2013 {
2014 require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
2015
2016 if (!DolibarrApiAccess::$user->hasRight('facture', 'paiement')) {
2017 throw new RestException(403);
2018 }
2019 if (empty($id)) {
2020 throw new RestException(400, 'Payment ID is mandatory');
2021 }
2022
2023 $paymentobj = new Paiement($this->db);
2024 $result = $paymentobj->fetch($id);
2025
2026 if (!$result) {
2027 throw new RestException(404, 'Payment not found');
2028 }
2029
2030 if (!empty($num_payment)) {
2031 $result = $paymentobj->update_num($num_payment);
2032 if ($result < 0) {
2033 throw new RestException(500, 'Error when updating the payment num');
2034 }
2035 }
2036
2037 return [
2038 'success' => [
2039 'code' => 200,
2040 'message' => 'Payment updated'
2041 ]
2042 ];
2043 }
2044
2045 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
2055 protected function _cleanObjectDatas($object)
2056 {
2057 // phpcs:enable
2058 $object = parent::_cleanObjectDatas($object);
2059
2060 unset($object->note);
2061 unset($object->address);
2062 unset($object->barcode_type);
2063 unset($object->barcode_type_code);
2064 unset($object->barcode_type_label);
2065 unset($object->barcode_type_coder);
2066 unset($object->canvas);
2067
2068 return $object;
2069 }
2070
2079 private function _validate($data)
2080 {
2081 if ($data === null) {
2082 $data = array();
2083 }
2084 $invoice = array();
2085 foreach (Invoices::$FIELDS as $field) {
2086 if (!isset($data[$field])) {
2087 throw new RestException(400, "$field field missing");
2088 }
2089 $invoice[$field] = $data[$field];
2090 }
2091 return $invoice;
2092 }
2093
2094
2110 public function getTemplateInvoice($id, $contact_list = 1)
2111 {
2112 return $this->_fetchTemplateInvoice($id, '', '', $contact_list);
2113 }
2114
2115
2143 public function indexTemplateInvoices($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $thirdparty_ids = '', $status = '', $sqlfilters = '', $properties = '', $pagination_data = false, $loadlinkedobjects = 0, $withLines = true)
2144 {
2145 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
2146 throw new RestException(403);
2147 }
2148
2149 $obj_ret = array();
2150
2151 // case of external user, $thirdparty_ids param is ignored and replaced by user's socid
2152 $socids = DolibarrApiAccess::$user->socid ?: $thirdparty_ids;
2153
2154
2155 // If the internal user must only see his customers, force searching by him
2156 $search_sale = 0;
2157 if (!DolibarrApiAccess::$user->hasRight('societe', 'client', 'voir') && !$socids) {
2158 $search_sale = DolibarrApiAccess::$user->id;
2159 }
2160
2161 $sql = "SELECT t.rowid";
2162 $sql .= " FROM ".MAIN_DB_PREFIX."facture_rec AS t";
2163 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."societe AS s ON (s.rowid = t.fk_soc)";
2164 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facture_rec_extrafields AS ef ON (ef.fk_object = t.rowid)";
2165 $sql .= ' WHERE t.entity IN ('.getEntity('invoice').')';
2166 if ($socids) {
2167 $sql .= " AND t.fk_soc IN (".$this->db->sanitize($socids).")";
2168 }
2169
2170 // Search on sale representative
2171 if ($search_sale && $search_sale != '-1') {
2172 if ($search_sale == -2) {
2173 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux AS sc WHERE sc.fk_soc = t.fk_soc)";
2174 } elseif ($search_sale > 0) {
2175 $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).")";
2176 }
2177 }
2178
2179 // Filter by status
2180 if ($status == 'active') {
2181 $sql .= " AND t.suspended = 0 AND t.frequency IS NOT NULL";
2182 }
2183 if ($status == 'suspended') {
2184 $sql .= " AND t.suspended = 1 AND t.frequency IS NOT NULL";
2185 }
2186 if ($status == 'draft') {
2187 $sql .= " AND t.frequency IS NULL";
2188 }
2189 // add sql filters
2190 if ($sqlfilters) {
2191 $errormessage = '';
2192 $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
2193 if ($errormessage) {
2194 throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);
2195 }
2196 }
2197
2198 //this query will return total template invoices with the filters given
2199 $sqlTotals = str_replace('SELECT t.rowid', 'SELECT count(t.rowid) as total', $sql);
2200
2201 $sql .= $this->db->order($sortfield, $sortorder);
2202 if ($limit) {
2203 if ($page < 0) {
2204 $page = 0;
2205 }
2206 $offset = $limit * $page;
2207
2208 $sql .= $this->db->plimit($limit + 1, $offset);
2209 }
2210
2211 $result = $this->db->query($sql);
2212 if ($result) {
2213 $i = 0;
2214 $num = $this->db->num_rows($result);
2215 $min = min($num, ($limit <= 0 ? $num : $limit));
2216 while ($i < $min) {
2217 $obj = $this->db->fetch_object($result);
2218 $factureRec = new FactureRec($this->db);
2219 if ($factureRec->fetch($obj->rowid) > 0) {
2220 if ($loadlinkedobjects) {
2221 // retrieve linked objects
2222 $factureRec->fetchObjectLinked();
2223 }
2224
2225 if (!$withLines) {
2226 unset($factureRec->lines);
2227 }
2228
2229 $obj_ret[] = $this->_filterObjectProperties($this->_cleanTemplateObjectDatas($factureRec), $properties);
2230 }
2231 $i++;
2232 }
2233 } else {
2234 throw new RestException(503, 'Error when retrieving recurring invoice templates: '.$this->db->lasterror());
2235 }
2236
2237 //if $pagination_data is true the response will contain element data with all values and element pagination with pagination data(total,page,limit)
2238 if ($pagination_data) {
2239 $totalsResult = $this->db->query($sqlTotals);
2240 $total = $this->db->fetch_object($totalsResult)->total;
2241
2242 $tmp = $obj_ret;
2243 $obj_ret = array();
2244
2245 $obj_ret['data'] = $tmp;
2246 $obj_ret['pagination'] = array(
2247 'total' => (int) $total,
2248 'page' => $page,
2249 'page_count' => ceil((int) $total / $limit),
2250 'limit' => $limit
2251 );
2252 }
2253
2254 return $obj_ret;
2255 }
2256
2270 private function _fetchTemplateInvoice($id, $ref = '', $ref_ext = '', $contact_list = 1)
2271 {
2272 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
2273 throw new RestException(403);
2274 }
2275
2276 $result = $this->template_invoice->fetch($id, $ref, $ref_ext);
2277 if (!$result) {
2278 throw new RestException(404, 'Template invoice not found');
2279 }
2280
2281 if (!DolibarrApi::_checkAccessToResource('facturerec', $this->template_invoice->id)) {
2282 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
2283 }
2284
2285 // Add external contacts ids
2286 if ($contact_list > -1) {
2287 $tmparray = $this->template_invoice->liste_contact(-1, 'external', $contact_list);
2288 if (is_array($tmparray)) {
2289 $this->template_invoice->contacts_ids = $tmparray;
2290 }
2291 }
2292
2293 $this->template_invoice->fetchObjectLinked();
2294 return $this->_cleanTemplateObjectDatas($this->template_invoice);
2295 }
2296
2297
2298 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
2306 {
2307 // phpcs:enable
2308 $object = parent::_cleanObjectDatas($object);
2309
2310 unset($object->note);
2311 unset($object->address);
2312 unset($object->barcode_type);
2313 unset($object->barcode_type_code);
2314 unset($object->barcode_type_label);
2315 unset($object->barcode_type_coder);
2316 unset($object->canvas);
2317
2318 return $object;
2319 }
2320}
$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: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:435
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 '.
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.