dolibarr 22.0.5
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 Frédéric France <frederic.france@free.fr>
6 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22use Luracast\Restler\RestException;
23
24require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
25require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture-rec.class.php';
26
27
34class Invoices extends DolibarrApi
35{
39 public static $FIELDS = array(
40 'socid',
41 );
42
46 private $invoice;
47
51 private $template_invoice;
52
53
57 public function __construct()
58 {
59 global $db;
60 $this->db = $db;
61 $this->invoice = new Facture($this->db);
62 $this->template_invoice = new FactureRec($this->db);
63 }
64
76 public function get($id, $contact_list = 1)
77 {
78 return $this->_fetch($id, '', '', $contact_list);
79 }
80
94 public function getByRef($ref, $contact_list = 1)
95 {
96 return $this->_fetch(0, $ref, '', $contact_list);
97 }
98
112 public function getByRefExt($ref_ext, $contact_list = 1)
113 {
114 return $this->_fetch(0, '', $ref_ext, $contact_list);
115 }
116
130 private function _fetch($id, $ref = '', $ref_ext = '', $contact_list = 1)
131 {
132 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
133 throw new RestException(403);
134 }
135
136 $result = $this->invoice->fetch($id, $ref, $ref_ext);
137 if (!$result) {
138 throw new RestException(404, 'Invoice not found');
139 }
140
141 // Get payment details
142 $this->invoice->totalpaid = $this->invoice->getSommePaiement();
143 $this->invoice->totalcreditnotes = $this->invoice->getSumCreditNotesUsed();
144 $this->invoice->totaldeposits = $this->invoice->getSumDepositsUsed();
145 $this->invoice->remaintopay = price2num($this->invoice->total_ttc - $this->invoice->totalpaid - $this->invoice->totalcreditnotes - $this->invoice->totaldeposits, 'MT');
146
147 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
148 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
149 }
150
151 // Retrieve credit note ids
152 $this->invoice->getListIdAvoirFromInvoice();
153
154 // Add external contacts ids
155 if ($contact_list > -1) {
156 $tmparray = $this->invoice->liste_contact(-1, 'external', $contact_list);
157 if (is_array($tmparray)) {
158 $this->invoice->contacts_ids = $tmparray;
159 }
160 $tmparray = $this->invoice->liste_contact(-1, 'internal', $contact_list);
161 if (is_array($tmparray)) {
162 $this->invoice->contacts_ids = $tmparray;
163 }
164 }
165
166 $this->invoice->fetchObjectLinked();
167
168 // Add online_payment_url, copied from order
169 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
170 $this->invoice->online_payment_url = getOnlinePaymentUrl(0, 'invoice', $this->invoice->ref);
171
172 return $this->_cleanObjectDatas($this->invoice);
173 }
174
197 public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $thirdparty_ids = '', $status = '', $sqlfilters = '', $properties = '', $pagination_data = false, $loadlinkedobjects = 0)
198 {
199 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
200 throw new RestException(403);
201 }
202
203 $obj_ret = array();
204
205 // case of external user, $thirdparty_ids param is ignored and replaced by user's socid
206 $socids = DolibarrApiAccess::$user->socid ?: $thirdparty_ids;
207
208 // If the internal user must only see his customers, force searching by him
209 $search_sale = 0;
210 if (!DolibarrApiAccess::$user->hasRight('societe', 'client', 'voir') && !$socids) {
211 $search_sale = DolibarrApiAccess::$user->id;
212 }
213
214 $sql = "SELECT t.rowid";
215 $sql .= " FROM ".MAIN_DB_PREFIX."facture AS t";
216 $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
217 $sql .= ' WHERE t.entity IN ('.getEntity('invoice').')';
218 if ($socids) {
219 $sql .= " AND t.fk_soc IN (".$this->db->sanitize($socids).")";
220 }
221 // Search on sale representative
222 if ($search_sale && $search_sale != '-1') {
223 if ($search_sale == -2) {
224 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = t.fk_soc)";
225 } elseif ($search_sale > 0) {
226 $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).")";
227 }
228 }
229 // Filter by status
230 if ($status == 'draft') {
231 $sql .= " AND t.fk_statut IN (0)";
232 }
233 if ($status == 'unpaid') {
234 $sql .= " AND t.fk_statut IN (1)";
235 }
236 if ($status == 'paid') {
237 $sql .= " AND t.fk_statut IN (2)";
238 }
239 if ($status == 'cancelled') {
240 $sql .= " AND t.fk_statut IN (3)";
241 }
242 // Add sql filters
243 if ($sqlfilters) {
244 $errormessage = '';
245 $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
246 if ($errormessage) {
247 throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);
248 }
249 }
250
251 //this query will return total invoices with the filters given
252 $sqlTotals = str_replace('SELECT t.rowid', 'SELECT count(t.rowid) as total', $sql);
253
254 $sql .= $this->db->order($sortfield, $sortorder);
255 if ($limit) {
256 if ($page < 0) {
257 $page = 0;
258 }
259 $offset = $limit * $page;
260
261 $sql .= $this->db->plimit($limit + 1, $offset);
262 }
263
264 $result = $this->db->query($sql);
265 if ($result) {
266 $i = 0;
267 $num = $this->db->num_rows($result);
268 $min = min($num, ($limit <= 0 ? $num : $limit));
269 while ($i < $min) {
270 $obj = $this->db->fetch_object($result);
271 $invoice_static = new Facture($this->db);
272 if ($invoice_static->fetch($obj->rowid) > 0) {
273 // Get payment details
274 $invoice_static->totalpaid = $invoice_static->getSommePaiement();
275 $invoice_static->totalcreditnotes = $invoice_static->getSumCreditNotesUsed();
276 $invoice_static->totaldeposits = $invoice_static->getSumDepositsUsed();
277 $invoice_static->remaintopay = price2num($invoice_static->total_ttc - $invoice_static->totalpaid - $invoice_static->totalcreditnotes - $invoice_static->totaldeposits, 'MT');
278
279 // Retrieve credit note ids
280 $invoice_static->getListIdAvoirFromInvoice();
281
282 // Add external contacts ids
283 $tmparray = $invoice_static->liste_contact(-1, 'external', 1);
284 if (is_array($tmparray)) {
285 $invoice_static->contacts_ids = $tmparray;
286 }
287
288 if ($loadlinkedobjects) {
289 // retrieve linked objects
290 $invoice_static->fetchObjectLinked();
291 }
292
293 // Add online_payment_url, copied from order
294 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
295 $invoice_static->online_payment_url = getOnlinePaymentUrl(0, 'invoice', $invoice_static->ref);
296
297 $obj_ret[] = $this->_filterObjectProperties($this->_cleanObjectDatas($invoice_static), $properties);
298 }
299 $i++;
300 }
301 } else {
302 throw new RestException(503, 'Error when retrieve invoice list : '.$this->db->lasterror());
303 }
304
305 //if $pagination_data is true the response will contain element data with all values and element pagination with pagination data(total,page,limit)
306 if ($pagination_data) {
307 $totalsResult = $this->db->query($sqlTotals);
308 $total = $this->db->fetch_object($totalsResult)->total;
309
310 $tmp = $obj_ret;
311 $obj_ret = [];
312
313 $obj_ret['data'] = $tmp;
314 $obj_ret['pagination'] = [
315 'total' => (int) $total,
316 'page' => $page, //count starts from 0
317 'page_count' => ceil((int) $total / $limit),
318 'limit' => $limit
319 ];
320 }
321
322 return $obj_ret;
323 }
324
333 public function post($request_data = null)
334 {
335 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
336 throw new RestException(403, "Insuffisant rights");
337 }
338
339 if (!is_array($request_data)) {
340 $request_data = array();
341 }
342
343 // Check mandatory fields (not using output, only possible exception is important)
344 $this->_validate($request_data);
345
346 foreach ($request_data as $field => $value) {
347 if ($field === 'caller') {
348 // 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
349 $this->invoice->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
350 continue;
351 }
352
353 $this->invoice->$field = $this->_checkValForAPI($field, $value, $this->invoice);
354 }
355 if (!array_key_exists('date', $request_data)) {
356 $this->invoice->date = dol_now();
357 }
358 /* We keep lines as an array
359 if (isset($request_data["lines"])) {
360 $lines = array();
361 foreach ($request_data["lines"] as $line) {
362 array_push($lines, (object) $line);
363 }
364 $this->invoice->lines = $lines;
365 }*/
366
367 if ($this->invoice->create(DolibarrApiAccess::$user, 0, (empty($request_data["date_lim_reglement"]) ? 0 : $request_data["date_lim_reglement"])) < 0) {
368 throw new RestException(500, "Error creating invoice", array_merge(array($this->invoice->error), $this->invoice->errors));
369 }
370 return ((int) $this->invoice->id);
371 }
372
387 public function createInvoiceFromOrder($orderid)
388 {
389 require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
390
391 if (!DolibarrApiAccess::$user->hasRight('commande', 'lire')) {
392 throw new RestException(403);
393 }
394 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
395 throw new RestException(403);
396 }
397 if (empty($orderid)) {
398 throw new RestException(400, 'Order ID is mandatory');
399 }
400 if (!DolibarrApi::_checkAccessToResource('commande', $orderid)) {
401 throw new RestException(403, 'Access not allowed on order for login '.DolibarrApiAccess::$user->login);
402 }
403
404 $order = new Commande($this->db);
405 $result = $order->fetch($orderid);
406 if (!$result) {
407 throw new RestException(404, 'Order not found');
408 }
409
410 $result = $this->invoice->createFromOrder($order, DolibarrApiAccess::$user);
411 if ($result < 0) {
412 throw new RestException(405, $this->invoice->error);
413 }
414 $this->invoice->fetchObjectLinked();
415 return $this->_cleanObjectDatas($this->invoice);
416 }
417
431 public function createInvoiceFromContract($contractid)
432 {
433 require_once DOL_DOCUMENT_ROOT.'/contrat/class/contrat.class.php';
434
435 if (!DolibarrApiAccess::$user->hasRight('contrat', 'lire')) {
436 throw new RestException(403);
437 }
438 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
439 throw new RestException(403);
440 }
441 if (empty($contractid)) {
442 throw new RestException(400, 'Contract ID is mandatory');
443 }
444
445 $contract = new Contrat($this->db);
446 $result = $contract->fetch($contractid);
447 if (!$result) {
448 throw new RestException(404, 'Contract not found');
449 }
450
451 $result = $this->invoice->createFromContract($contract, DolibarrApiAccess::$user);
452 if ($result < 0) {
453 throw new RestException(405, $this->invoice->error);
454 }
455 $this->invoice->fetchObjectLinked();
456 return $this->_cleanObjectDatas($this->invoice);
457 }
458
469 public function getLines($id)
470 {
471 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
472 throw new RestException(403);
473 }
474
475 $result = $this->invoice->fetch($id);
476 if (!$result) {
477 throw new RestException(404, 'Invoice not found');
478 }
479
480 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
481 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
482 }
483 $this->invoice->getLinesArray();
484 $result = array();
485 foreach ($this->invoice->lines as $line) {
486 array_push($result, $this->_cleanObjectDatas($line));
487 }
488 return $result;
489 }
490
507 public function putLine($id, $lineid, $request_data = null)
508 {
509 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
510 throw new RestException(403);
511 }
512
513 $result = $this->invoice->fetch($id);
514 if (!$result) {
515 throw new RestException(404, 'Invoice not found');
516 }
517
518 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
519 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
520 }
521
522 $request_data = (object) $request_data;
523
524 $request_data->desc = sanitizeVal($request_data->desc, 'restricthtml');
525 $request_data->label = sanitizeVal($request_data->label);
526
527 $updateRes = $this->invoice->updateline(
528 $lineid,
529 $request_data->desc,
530 $request_data->subprice,
531 $request_data->qty,
532 $request_data->remise_percent,
533 $request_data->date_start,
534 $request_data->date_end,
535 $request_data->tva_tx,
536 $request_data->localtax1_tx,
537 $request_data->localtax2_tx,
538 $request_data->price_base_type ? $request_data->price_base_type : 'HT',
539 $request_data->info_bits,
540 $request_data->product_type,
541 $request_data->fk_parent_line,
542 0,
543 $request_data->fk_fournprice,
544 $request_data->pa_ht,
545 $request_data->label,
546 $request_data->special_code,
547 $request_data->array_options,
548 $request_data->situation_percent,
549 $request_data->fk_unit,
550 $request_data->multicurrency_subprice,
551 0,
552 $request_data->ref_ext,
553 $request_data->rang
554 );
555
556 if ($updateRes > 0) {
557 $result = $this->get($id);
558 unset($result->line);
559 return $this->_cleanObjectDatas($result);
560 } else {
561 throw new RestException(304, $this->invoice->error);
562 }
563 }
564
580 public function postContact($id, $contactid, $type)
581 {
582 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
583 throw new RestException(403);
584 }
585
586 $result = $this->invoice->fetch($id);
587
588 if (!$result) {
589 throw new RestException(404, 'Invoice not found');
590 }
591
592 if (!in_array($type, array('BILLING', 'SHIPPING', 'CUSTOMER'), true)) {
593 throw new RestException(500, 'Availables types: BILLING, SHIPPING OR CUSTOMER');
594 }
595
596 if (!DolibarrApi::_checkAccessToResource('invoice', $this->invoice->id)) {
597 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
598 }
599
600 $result = $this->invoice->add_contact($contactid, $type, 'external');
601
602 if (!$result) {
603 throw new RestException(500, 'Error when added the contact');
604 }
605
606 return array(
607 'success' => array(
608 'code' => 200,
609 'message' => 'Contact linked to the invoice'
610 )
611 );
612 }
613
628 public function deleteContact($id, $contactid, $type)
629 {
630 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
631 throw new RestException(403);
632 }
633
634 $result = $this->invoice->fetch($id);
635
636 if (!$result) {
637 throw new RestException(404, 'Invoice not found');
638 }
639
640 if (!DolibarrApi::_checkAccessToResource('invoice', $this->invoice->id)) {
641 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
642 }
643
644 $contacts = $this->invoice->liste_contact();
645
646 foreach ($contacts as $contact) {
647 if ($contact['id'] == $contactid && $contact['code'] == $type) {
648 $result = $this->invoice->delete_contact($contact['rowid']);
649
650 if (!$result) {
651 throw new RestException(500, 'Error when deleted the contact');
652 }
653 }
654 }
655
656 return $this->_cleanObjectDatas($this->invoice);
657 }
658
673 public function deleteLine($id, $lineid)
674 {
675 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
676 throw new RestException(403);
677 }
678 if (empty($lineid)) {
679 throw new RestException(400, 'Line ID is mandatory');
680 }
681
682 if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
683 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
684 }
685
686 $result = $this->invoice->fetch($id);
687 if (!$result) {
688 throw new RestException(404, 'Invoice not found');
689 }
690
691 $updateRes = $this->invoice->deleteLine($lineid, $id);
692 if ($updateRes > 0) {
693 return $this->get($id);
694 } else {
695 throw new RestException(405, $this->invoice->error);
696 }
697 }
698
708 public function put($id, $request_data = null)
709 {
710 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
711 throw new RestException(403);
712 }
713
714 $result = $this->invoice->fetch($id);
715 if (!$result) {
716 throw new RestException(404, 'Invoice not found');
717 }
718
719 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
720 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
721 }
722
723 foreach ($request_data as $field => $value) {
724 if ($field == 'id') {
725 continue;
726 }
727 if ($field === 'caller') {
728 // 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
729 $this->invoice->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
730 continue;
731 }
732 if ($field == 'array_options' && is_array($value)) {
733 foreach ($value as $index => $val) {
734 $this->invoice->array_options[$index] = $this->_checkValForAPI($field, $val, $this->invoice);
735 }
736 continue;
737 }
738
739 $this->invoice->$field = $this->_checkValForAPI($field, $value, $this->invoice);
740
741 // If cond reglement => update date lim reglement
742 if ($field == 'cond_reglement_id') {
743 $this->invoice->date_lim_reglement = $this->invoice->calculate_date_lim_reglement();
744 }
745 }
746
747 // update bank account
748 if (!empty($this->invoice->fk_account)) {
749 if ($this->invoice->setBankAccount($this->invoice->fk_account) == 0) {
750 throw new RestException(400, $this->invoice->error);
751 }
752 }
753
754 if ($this->invoice->update(DolibarrApiAccess::$user) > 0) {
755 return $this->get($id);
756 } else {
757 throw new RestException(500, $this->invoice->error);
758 }
759 }
760
769 public function delete($id)
770 {
771 if (!DolibarrApiAccess::$user->hasRight('facture', 'supprimer')) {
772 throw new RestException(403);
773 }
774 $result = $this->invoice->fetch($id);
775 if (!$result) {
776 throw new RestException(404, 'Invoice not found');
777 }
778
779 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
780 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
781 }
782
783 $result = $this->invoice->delete(DolibarrApiAccess::$user);
784 if ($result < 0) {
785 throw new RestException(500, 'Error when deleting invoice');
786 } elseif ($result == 0) {
787 throw new RestException(403, 'Invoice not erasable');
788 }
789
790 return array(
791 'success' => array(
792 'code' => 200,
793 'message' => 'Invoice deleted'
794 )
795 );
796 }
797
823 public function postLine($id, $request_data = null)
824 {
825 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
826 throw new RestException(403);
827 }
828
829 $result = $this->invoice->fetch($id);
830 if (!$result) {
831 throw new RestException(404, 'Invoice not found');
832 }
833
834 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
835 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
836 }
837
838 $request_data = (object) $request_data;
839
840 $request_data->desc = sanitizeVal($request_data->desc, 'restricthtml');
841 $request_data->label = sanitizeVal($request_data->label);
842
843 // Reset fk_parent_line for no child products and special product
844 if (($request_data->product_type != 9 && empty($request_data->fk_parent_line)) || $request_data->product_type == 9) {
845 $request_data->fk_parent_line = 0;
846 }
847
848 // calculate pa_ht
849 $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);
850 $pa_ht = $marginInfos[0];
851
852 $updateRes = $this->invoice->addline(
853 $request_data->desc,
854 $request_data->subprice,
855 $request_data->qty,
856 $request_data->tva_tx,
857 $request_data->localtax1_tx,
858 $request_data->localtax2_tx,
859 $request_data->fk_product,
860 $request_data->remise_percent,
861 $request_data->date_start,
862 $request_data->date_end,
863 $request_data->fk_code_ventilation,
864 $request_data->info_bits,
865 $request_data->fk_remise_except,
866 $request_data->price_base_type ? $request_data->price_base_type : 'HT',
867 $request_data->subprice,
868 $request_data->product_type,
869 $request_data->rang,
870 $request_data->special_code,
871 $request_data->origin,
872 $request_data->origin_id,
873 $request_data->fk_parent_line,
874 empty($request_data->fk_fournprice) ? null : $request_data->fk_fournprice,
875 $pa_ht,
876 $request_data->label,
877 $request_data->array_options,
878 $request_data->situation_percent,
879 $request_data->fk_prev_id,
880 $request_data->fk_unit,
881 0,
882 $request_data->ref_ext
883 );
884
885 if ($updateRes < 0) {
886 throw new RestException(400, 'Unable to insert the new line. Check your inputs. '.$this->invoice->error);
887 }
888
889 return $updateRes;
890 }
891
910 public function addContact($id, $fk_socpeople, $type_contact, $source, $notrigger = 0)
911 {
912 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
913 throw new RestException(403);
914 }
915 $result = $this->invoice->fetch($id);
916 if (!$result) {
917 throw new RestException(404, 'Invoice not found');
918 }
919
920 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
921 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
922 }
923
924 $result = $this->invoice->add_contact($fk_socpeople, $type_contact, $source, $notrigger);
925 if ($result < 0) {
926 throw new RestException(500, 'Error : '.$this->invoice->error);
927 }
928
929 $result = $this->invoice->fetch($id);
930 if (!$result) {
931 throw new RestException(404, 'Invoice not found');
932 }
933
934 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
935 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
936 }
937
938 return $this->_cleanObjectDatas($this->invoice);
939 }
940
941
942
957 public function settodraft($id, $idwarehouse = -1)
958 {
959 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
960 throw new RestException(403);
961 }
962 $result = $this->invoice->fetch($id);
963 if (!$result) {
964 throw new RestException(404, 'Invoice not found');
965 }
966
967 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
968 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
969 }
970
971 $result = $this->invoice->setDraft(DolibarrApiAccess::$user, $idwarehouse);
972 if ($result == 0) {
973 throw new RestException(304, 'Nothing done.');
974 }
975 if ($result < 0) {
976 throw new RestException(500, 'Error : '.$this->invoice->error);
977 }
978
979 $result = $this->invoice->fetch($id);
980 if (!$result) {
981 throw new RestException(404, 'Invoice not found');
982 }
983
984 return $this->_cleanObjectDatas($this->invoice);
985 }
986
987
1005 public function validate($id, $force_number = '', $idwarehouse = 0, $notrigger = 0)
1006 {
1007 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1008 throw new RestException(403);
1009 }
1010 $result = $this->invoice->fetch($id);
1011 if (!$result) {
1012 throw new RestException(404, 'Invoice not found');
1013 }
1014
1015 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1016 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1017 }
1018
1019 $result = $this->invoice->validate(DolibarrApiAccess::$user, $force_number, $idwarehouse, $notrigger);
1020 if ($result == 0) {
1021 throw new RestException(304, 'Error nothing done. May be object is already validated');
1022 }
1023 if ($result < 0) {
1024 throw new RestException(500, 'Error when validating Invoice: '.$this->invoice->error);
1025 }
1026
1027 $result = $this->invoice->fetch($id);
1028 if (!$result) {
1029 throw new RestException(404, 'Invoice not found');
1030 }
1031
1032 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1033 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1034 }
1035
1036 // copy from order
1037 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
1038 $this->invoice->online_payment_url = getOnlinePaymentUrl(0, 'invoice', $this->invoice->ref);
1039
1040 return $this->_cleanObjectDatas($this->invoice);
1041 }
1042
1058 public function settopaid($id, $close_code = '', $close_note = '')
1059 {
1060 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1061 throw new RestException(403);
1062 }
1063 $result = $this->invoice->fetch($id);
1064 if (!$result) {
1065 throw new RestException(404, 'Invoice not found');
1066 }
1067
1068 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1069 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1070 }
1071
1072 $result = $this->invoice->setPaid(DolibarrApiAccess::$user, $close_code, $close_note);
1073 if ($result == 0) {
1074 throw new RestException(304, 'Error nothing done. May be object is already validated');
1075 }
1076 if ($result < 0) {
1077 throw new RestException(500, 'Error : '.$this->invoice->error);
1078 }
1079
1080
1081 $result = $this->invoice->fetch($id);
1082 if (!$result) {
1083 throw new RestException(404, 'Invoice not found');
1084 }
1085
1086 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1087 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1088 }
1089
1090 return $this->_cleanObjectDatas($this->invoice);
1091 }
1092
1093
1107 public function settounpaid($id)
1108 {
1109 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1110 throw new RestException(403);
1111 }
1112 $result = $this->invoice->fetch($id);
1113 if (!$result) {
1114 throw new RestException(404, 'Invoice not found');
1115 }
1116
1117 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1118 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1119 }
1120
1121 $result = $this->invoice->setUnpaid(DolibarrApiAccess::$user);
1122 if ($result == 0) {
1123 throw new RestException(304, 'Nothing done');
1124 }
1125 if ($result < 0) {
1126 throw new RestException(500, 'Error : '.$this->invoice->error);
1127 }
1128
1129
1130 $result = $this->invoice->fetch($id);
1131 if (!$result) {
1132 throw new RestException(404, 'Invoice not found');
1133 }
1134
1135 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1136 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1137 }
1138
1139 return $this->_cleanObjectDatas($this->invoice);
1140 }
1141
1150 public function getDiscount($id)
1151 {
1152 require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1153
1154 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
1155 throw new RestException(403);
1156 }
1157
1158 $result = $this->invoice->fetch($id);
1159 if (!$result) {
1160 throw new RestException(404, 'Invoice not found');
1161 }
1162
1163 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1164 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1165 }
1166
1167 $discountcheck = new DiscountAbsolute($this->db);
1168 $result = $discountcheck->fetch(0, $this->invoice->id);
1169
1170 if ($result == 0) {
1171 throw new RestException(404, 'Discount not found');
1172 }
1173 if ($result < 0) {
1174 throw new RestException(500, $discountcheck->error);
1175 }
1176
1177 return parent::_cleanObjectDatas($discountcheck);
1178 }
1179
1194 {
1195 require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1196
1197 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1198 throw new RestException(403);
1199 }
1200
1201 $result = $this->invoice->fetch($id);
1202 if (!$result) {
1203 throw new RestException(404, 'Invoice not found');
1204 }
1205
1206 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1207 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1208 }
1209
1210 if ($this->invoice->paye) {
1211 throw new RestException(500, 'Alreay paid');
1212 }
1213
1214 $this->invoice->fetch($id);
1215 $this->invoice->fetch_thirdparty();
1216
1217 // Check if there is already a discount (protection to avoid duplicate creation when resubmit post)
1218 $discountcheck = new DiscountAbsolute($this->db);
1219 $result = $discountcheck->fetch(0, $this->invoice->id);
1220
1221 $canconvert = 0;
1222 if ($this->invoice->type == Facture::TYPE_DEPOSIT && empty($discountcheck->id)) {
1223 $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)
1224 }
1225 if (($this->invoice->type == Facture::TYPE_CREDIT_NOTE || $this->invoice->type == Facture::TYPE_STANDARD) && $this->invoice->paye == 0 && empty($discountcheck->id)) {
1226 $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)
1227 }
1228 if ($canconvert) {
1229 $this->db->begin();
1230
1231 $amount_ht = $amount_tva = $amount_ttc = array();
1232 $multicurrency_amount_ht = $multicurrency_amount_tva = $multicurrency_amount_ttc = array();
1233 '
1234 @phan-var-force array<string,float> $amount_ht
1235 @phan-var-force array<string,float> $amount_tva
1236 @phan-var-force array<string,float> $amount_ttc
1237 @phan-var-force array<string,float> $multicurrency_amount_ht
1238 @phan-var-force array<string,float> $multicurrency_amount_tva
1239 @phan-var-force array<string,float> $multicurrency_amount_ttc
1240 ';
1241
1242 // Loop on each vat rate
1243 $i = 0;
1244 foreach ($this->invoice->lines as $line) {
1245 if ($line->product_type < 9 && $line->total_ht != 0) { // Remove lines with product_type greater than or equal to 9
1246 if (!array_key_exists($line->tva_tx, $amount_ht)) {
1247 $amount_ht[$line->tva_tx] = 0.0;
1248 $amount_tva[$line->tva_tx] = 0.0;
1249 $amount_ttc[$line->tva_tx] = 0.0;
1250 $multicurrency_amount_ht[$line->tva_tx] = 0.0;
1251 $multicurrency_amount_tva[$line->tva_tx] = 0.0;
1252 $multicurrency_amount_ttc[$line->tva_tx] = 0.0;
1253 }
1254 // no need to create discount if amount is null
1255 $amount_ht[$line->tva_tx] += $line->total_ht;
1256 $amount_tva[$line->tva_tx] += $line->total_tva;
1257 $amount_ttc[$line->tva_tx] += $line->total_ttc;
1258 $multicurrency_amount_ht[$line->tva_tx] += $line->multicurrency_total_ht;
1259 $multicurrency_amount_tva[$line->tva_tx] += $line->multicurrency_total_tva;
1260 $multicurrency_amount_ttc[$line->tva_tx] += $line->multicurrency_total_ttc;
1261 $i++;
1262 }
1263 }
1264
1265 // Insert one discount by VAT rate category
1266 $discount = new DiscountAbsolute($this->db);
1267 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1268 $discount->description = '(CREDIT_NOTE)';
1269 } elseif ($this->invoice->type == Facture::TYPE_DEPOSIT) {
1270 $discount->description = '(DEPOSIT)';
1271 } elseif ($this->invoice->type == Facture::TYPE_STANDARD || $this->invoice->type == Facture::TYPE_REPLACEMENT || $this->invoice->type == Facture::TYPE_SITUATION) {
1272 $discount->description = '(EXCESS RECEIVED)';
1273 } else {
1274 throw new RestException(500, 'Cant convert to reduc an Invoice of this type');
1275 }
1276
1277 $discount->fk_soc = $this->invoice->socid;
1278 $discount->socid = $this->invoice->socid;
1279 $discount->fk_facture_source = $this->invoice->id;
1280
1281 $error = 0;
1282
1283 if ($this->invoice->type == Facture::TYPE_STANDARD || $this->invoice->type == Facture::TYPE_REPLACEMENT || $this->invoice->type == Facture::TYPE_SITUATION) {
1284 // If we're on a standard invoice, we have to get excess received to create a discount in TTC without VAT
1285
1286 // Total payments
1287 $sql = 'SELECT SUM(pf.amount) as total_payments';
1288 $sql .= ' FROM '.MAIN_DB_PREFIX.'paiement_facture as pf, '.MAIN_DB_PREFIX.'paiement as p';
1289 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as c ON p.fk_paiement = c.id';
1290 $sql .= ' WHERE pf.fk_facture = '.((int) $this->invoice->id);
1291 $sql .= ' AND pf.fk_paiement = p.rowid';
1292 $sql .= ' AND p.entity IN ('.getEntity('invoice').')';
1293 $resql = $this->db->query($sql);
1294 if (!$resql) {
1295 dol_print_error($this->db);
1296 }
1297
1298 $res = $this->db->fetch_object($resql);
1299 $total_payments = $res->total_payments;
1300
1301 // Total credit note and deposit
1302 $total_creditnote_and_deposit = 0;
1303 $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
1304 $sql .= " re.description, re.fk_facture_source";
1305 $sql .= " FROM ".MAIN_DB_PREFIX."societe_remise_except as re";
1306 $sql .= " WHERE fk_facture = ".((int) $this->invoice->id);
1307 $resql = $this->db->query($sql);
1308 if (!empty($resql)) {
1309 while ($obj = $this->db->fetch_object($resql)) {
1310 $total_creditnote_and_deposit += $obj->amount_ttc;
1311 }
1312 } else {
1313 dol_print_error($this->db);
1314 }
1315
1316 $discount->amount_ht = $discount->amount_ttc = $total_payments + $total_creditnote_and_deposit - $this->invoice->total_ttc;
1317 $discount->amount_tva = 0;
1318 $discount->tva_tx = 0;
1319
1320 $result = $discount->create(DolibarrApiAccess::$user);
1321 if ($result < 0) {
1322 $error++;
1323 }
1324 }
1325 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE || $this->invoice->type == Facture::TYPE_DEPOSIT) {
1326 foreach ($amount_ht as $tva_tx => $xxx) {
1327 $discount->amount_ht = abs($amount_ht[$tva_tx]);
1328 $discount->amount_tva = abs($amount_tva[$tva_tx]);
1329 $discount->amount_ttc = abs($amount_ttc[$tva_tx]);
1330 $discount->multicurrency_amount_ht = abs($multicurrency_amount_ht[$tva_tx]);
1331 $discount->multicurrency_amount_tva = abs($multicurrency_amount_tva[$tva_tx]);
1332 $discount->multicurrency_amount_ttc = abs($multicurrency_amount_ttc[$tva_tx]);
1333 $discount->tva_tx = abs((float) $tva_tx);
1334
1335 $result = $discount->create(DolibarrApiAccess::$user);
1336 if ($result < 0) {
1337 $error++;
1338 break;
1339 }
1340 }
1341 }
1342
1343 if (empty($error)) {
1344 if ($this->invoice->type != Facture::TYPE_DEPOSIT) {
1345 // Set the invoice as paid
1346 $result = $this->invoice->setPaid(DolibarrApiAccess::$user);
1347 if ($result >= 0) {
1348 $this->db->commit();
1349 } else {
1350 $this->db->rollback();
1351 throw new RestException(500, 'Could not set paid');
1352 }
1353 } else {
1354 $this->db->commit();
1355 }
1356 } else {
1357 $this->db->rollback();
1358 throw new RestException(500, 'Discount creation error');
1359 }
1360 }
1361
1362 return $this->_cleanObjectDatas($this->invoice);
1363 }
1364
1381 public function useDiscount($id, $discountid)
1382 {
1383 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1384 throw new RestException(403);
1385 }
1386 if (empty($id)) {
1387 throw new RestException(400, 'Invoice ID is mandatory');
1388 }
1389 if (empty($discountid)) {
1390 throw new RestException(400, 'Discount ID is mandatory');
1391 }
1392
1393 if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1394 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1395 }
1396
1397 $result = $this->invoice->fetch($id);
1398 if (!$result) {
1399 throw new RestException(404, 'Invoice not found');
1400 }
1401
1402 $result = $this->invoice->insert_discount($discountid);
1403 if ($result < 0) {
1404 throw new RestException(405, $this->invoice->error);
1405 }
1406
1407 return $result;
1408 }
1409
1426 public function useCreditNote($id, $discountid)
1427 {
1428 require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1429
1430 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1431 throw new RestException(403);
1432 }
1433 if (empty($id)) {
1434 throw new RestException(400, 'Invoice ID is mandatory');
1435 }
1436 if (empty($discountid)) {
1437 throw new RestException(400, 'Credit ID is mandatory');
1438 }
1439
1440 if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1441 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1442 }
1443 $discount = new DiscountAbsolute($this->db);
1444 $result = $discount->fetch($discountid);
1445 if (!$result) {
1446 throw new RestException(404, 'Credit not found');
1447 }
1448
1449 $result = $discount->link_to_invoice(0, $id);
1450 if ($result < 0) {
1451 throw new RestException(405, $discount->error);
1452 }
1453
1454 return $result;
1455 }
1456
1472 public function getPayments($id)
1473 {
1474 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
1475 throw new RestException(403);
1476 }
1477 if (empty($id)) {
1478 throw new RestException(400, 'Invoice ID is mandatory');
1479 }
1480
1481 if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1482 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1483 }
1484
1485 $result = $this->invoice->fetch($id);
1486 if (!$result) {
1487 throw new RestException(404, 'Invoice not found');
1488 }
1489
1490 $result = $this->invoice->getListOfPayments();
1491 if (!is_array($result) && $result < 0) {
1492 throw new RestException(405, $this->invoice->error);
1493 }
1494
1495 return $result;
1496 }
1497
1498
1520 public function addPayment($id, $datepaye, $paymentid, $closepaidinvoices, $accountid, $num_payment = '', $comment = '', $chqemetteur = '', $chqbank = '')
1521 {
1522 require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
1523
1524 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1525 throw new RestException(403);
1526 }
1527 if (empty($id)) {
1528 throw new RestException(400, 'Invoice ID is mandatory');
1529 }
1530
1531 if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1532 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1533 }
1534
1535 if (isModEnabled("bank")) {
1536 if (empty($accountid)) {
1537 throw new RestException(400, 'Account ID is mandatory');
1538 }
1539 }
1540
1541 if (empty($paymentid)) {
1542 throw new RestException(400, 'Payment ID or Payment Code is mandatory');
1543 }
1544
1545
1546 $result = $this->invoice->fetch($id);
1547 if (!$result) {
1548 throw new RestException(404, 'Invoice not found');
1549 }
1550
1551 // Calculate amount to pay
1552 $totalpaid = $this->invoice->getSommePaiement();
1553 $totalcreditnotes = $this->invoice->getSumCreditNotesUsed();
1554 $totaldeposits = $this->invoice->getSumDepositsUsed();
1555 $resteapayer = price2num($this->invoice->total_ttc - $totalpaid - $totalcreditnotes - $totaldeposits, 'MT');
1556
1557 $this->db->begin();
1558
1559 $amounts = array();
1560 $multicurrency_amounts = array();
1561
1562 // Clean parameters amount if payment is for a credit note
1563 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1564 $resteapayer = price2num($resteapayer, 'MT');
1565 $amounts[$id] = (float) price2num(-1 * abs((float) $resteapayer), 'MT');
1566 // Multicurrency
1567 $newvalue = price2num($this->invoice->multicurrency_total_ttc, 'MT');
1568 $multicurrency_amounts[$id] = (float) price2num(-1 * (float) $newvalue, 'MT');
1569 } else {
1570 $resteapayer = price2num($resteapayer, 'MT');
1571 $amounts[$id] = (float) $resteapayer;
1572 // Multicurrency
1573 $newvalue = price2num($this->invoice->multicurrency_total_ttc, 'MT');
1574 $multicurrency_amounts[$id] = (float) $newvalue;
1575 }
1576
1577 // Creation of payment line
1578 $paymentobj = new Paiement($this->db);
1579 if (is_numeric($datepaye)) {
1580 $paymentobj->datepaye = $datepaye;
1581 } else {
1582 $paymentobj->datepaye = dol_stringtotime($datepaye);
1583 }
1584 $paymentobj->amounts = $amounts; // Array with all payments dispatching with invoice id
1585 $paymentobj->multicurrency_amounts = $multicurrency_amounts; // Array with all payments dispatching
1586 $paymentobj->paiementid = $paymentid;
1587 $paymentobj->paiementcode = (string) dol_getIdFromCode($this->db, (string) $paymentid, 'c_paiement', 'id', 'code', 1);
1588 $paymentobj->num_payment = $num_payment;
1589 $paymentobj->note_private = $comment;
1590
1591 $payment_id = $paymentobj->create(DolibarrApiAccess::$user, ($closepaidinvoices == 'yes' ? 1 : 0)); // This include closing invoices
1592 if ($payment_id < 0) {
1593 $this->db->rollback();
1594 throw new RestException(400, 'Payment error : '.$paymentobj->error);
1595 }
1596
1597 if (isModEnabled("bank")) {
1598 $label = '(CustomerInvoicePayment)';
1599
1600 if ($paymentobj->paiementcode == 'CHQ' && empty($chqemetteur)) {
1601 throw new RestException(400, 'Emetteur is mandatory when payment code is '.$paymentobj->paiementcode);
1602 }
1603 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1604 $label = '(CustomerInvoicePaymentBack)'; // Refund of a credit note
1605 }
1606 $result = $paymentobj->addPaymentToBank(DolibarrApiAccess::$user, 'payment', $label, $accountid, $chqemetteur, $chqbank);
1607 if ($result < 0) {
1608 $this->db->rollback();
1609 throw new RestException(400, 'Add payment to bank error : '.$paymentobj->error);
1610 }
1611 }
1612
1613 $this->db->commit();
1614
1615 return $payment_id;
1616 }
1617
1646 public function addPaymentDistributed($arrayofamounts, $datepaye, $paymentid, $closepaidinvoices, $accountid, $num_payment = '', $comment = '', $chqemetteur = '', $chqbank = '', $ref_ext = '', $accepthigherpayment = false)
1647 {
1648 require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
1649
1650 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1651 throw new RestException(403);
1652 }
1653 foreach ($arrayofamounts as $id => $amount) {
1654 if (empty($id)) {
1655 throw new RestException(400, 'Invoice ID is mandatory. Fill the invoice id and amount into arrayofamounts parameter. For example: {"1": "99.99", "2": "10"}');
1656 }
1657 if (!DolibarrApi::_checkAccessToResource('facture', (int) $id)) {
1658 throw new RestException(403, 'Access not allowed on invoice ID '.$id.' for login '.DolibarrApiAccess::$user->login);
1659 }
1660 }
1661
1662 if (isModEnabled("bank")) {
1663 if (empty($accountid)) {
1664 throw new RestException(400, 'Account ID is mandatory');
1665 }
1666 }
1667 if (empty($paymentid)) {
1668 throw new RestException(400, 'Payment ID or Payment Code is mandatory');
1669 }
1670
1671 $this->db->begin();
1672
1673 $amounts = array();
1674 $multicurrency_amounts = array();
1675
1676 // Loop on each invoice to pay
1677 foreach ($arrayofamounts as $id => $amountarray) {
1678 $result = $this->invoice->fetch((int) $id);
1679 if (!$result) {
1680 $this->db->rollback();
1681 throw new RestException(404, 'Invoice ID '.$id.' not found');
1682 }
1683
1684 if (($amountarray["amount"] == "remain" || $amountarray["amount"] > 0) && ($amountarray["multicurrency_amount"] == "remain" || $amountarray["multicurrency_amount"] > 0)) {
1685 $this->db->rollback();
1686 throw new RestException(400, 'Payment in both currency '.$id.' ( amount: '.$amountarray["amount"].', multicurrency_amount: '.$amountarray["multicurrency_amount"].')');
1687 }
1688
1689 $is_multicurrency = 0;
1690 $total_ttc = $this->invoice->total_ttc;
1691
1692 if ($amountarray["multicurrency_amount"] > 0 || $amountarray["multicurrency_amount"] == "remain") {
1693 $is_multicurrency = 1;
1694 $total_ttc = $this->invoice->multicurrency_total_ttc;
1695 }
1696
1697 // Calculate amount to pay
1698 $totalpaid = $this->invoice->getSommePaiement($is_multicurrency);
1699 $totalcreditnotes = $this->invoice->getSumCreditNotesUsed($is_multicurrency);
1700 $totaldeposits = $this->invoice->getSumDepositsUsed($is_multicurrency);
1701 $remainstopay = $amount = (float) price2num($total_ttc - $totalpaid - $totalcreditnotes - $totaldeposits, 'MT');
1702
1703 if (!$is_multicurrency && $amountarray["amount"] != 'remain') {
1704 $amount = (float) price2num($amountarray["amount"], 'MT');
1705 }
1706
1707 if ($is_multicurrency && $amountarray["multicurrency_amount"] != 'remain') {
1708 $amount = (float) price2num($amountarray["multicurrency_amount"], 'MT');
1709 }
1710
1711 if (abs($amount) > abs($remainstopay) && !$accepthigherpayment) {
1712 $this->db->rollback();
1713 throw new RestException(400, 'Payment amount on invoice ID '.$id.' ('.$amount.') is higher than remain to pay ('.$remainstopay.')');
1714 }
1715
1716 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1717 $amount = (float) price2num(-1 * abs((float) $amount), 'MT');
1718 }
1719
1720 if ($is_multicurrency) {
1721 $amounts[$id] = null;
1722 // Multicurrency
1723 $multicurrency_amounts[$id] = (float) $amount;
1724 } else {
1725 $amounts[$id] = (float) $amount;
1726 // Multicurrency
1727 $multicurrency_amounts[$id] = null;
1728 }
1729 }
1730
1731 // Creation of payment line
1732 $paymentobj = new Paiement($this->db);
1733 if (is_numeric($datepaye)) {
1734 $paymentobj->datepaye = $datepaye;
1735 } else {
1736 $paymentobj->datepaye = dol_stringtotime($datepaye);
1737 }
1738 $paymentobj->amounts = $amounts; // Array with all payments dispatching with invoice id
1739 $paymentobj->multicurrency_amounts = $multicurrency_amounts; // Array with all payments dispatching
1740 $paymentobj->paiementid = $paymentid;
1741 $paymentobj->paiementcode = (string) dol_getIdFromCode($this->db, (string) $paymentid, 'c_paiement', 'id', 'code', 1);
1742 $paymentobj->num_payment = $num_payment;
1743 $paymentobj->note_private = $comment;
1744 $paymentobj->ref_ext = $ref_ext;
1745 $payment_id = $paymentobj->create(DolibarrApiAccess::$user, ($closepaidinvoices == 'yes' ? 1 : 0)); // This include closing invoices
1746 if ($payment_id < 0) {
1747 $this->db->rollback();
1748 throw new RestException(400, 'Payment error : '.$paymentobj->error);
1749 }
1750 if (isModEnabled("bank")) {
1751 $label = '(CustomerInvoicePayment)';
1752 if ($paymentobj->paiementcode == 'CHQ' && empty($chqemetteur)) {
1753 throw new RestException(400, 'Emetteur is mandatory when payment code is '.$paymentobj->paiementcode);
1754 }
1755 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1756 $label = '(CustomerInvoicePaymentBack)'; // Refund of a credit note
1757 }
1758 $result = $paymentobj->addPaymentToBank(DolibarrApiAccess::$user, 'payment', $label, $accountid, $chqemetteur, $chqbank);
1759 if ($result < 0) {
1760 $this->db->rollback();
1761 throw new RestException(400, 'Add payment to bank error : '.$paymentobj->error);
1762 }
1763 }
1764
1765 $this->db->commit();
1766
1767 return $payment_id;
1768 }
1769
1786 public function putPayment($id, $num_payment = '')
1787 {
1788 require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
1789
1790 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1791 throw new RestException(403);
1792 }
1793 if (empty($id)) {
1794 throw new RestException(400, 'Payment ID is mandatory');
1795 }
1796
1797 $paymentobj = new Paiement($this->db);
1798 $result = $paymentobj->fetch($id);
1799
1800 if (!$result) {
1801 throw new RestException(404, 'Payment not found');
1802 }
1803
1804 if (!empty($num_payment)) {
1805 $result = $paymentobj->update_num($num_payment);
1806 if ($result < 0) {
1807 throw new RestException(500, 'Error when updating the payment num');
1808 }
1809 }
1810
1811 return [
1812 'success' => [
1813 'code' => 200,
1814 'message' => 'Payment updated'
1815 ]
1816 ];
1817 }
1818
1819 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1826 protected function _cleanObjectDatas($object)
1827 {
1828 // phpcs:enable
1829 $object = parent::_cleanObjectDatas($object);
1830
1831 unset($object->note);
1832 unset($object->address);
1833 unset($object->barcode_type);
1834 unset($object->barcode_type_code);
1835 unset($object->barcode_type_label);
1836 unset($object->barcode_type_coder);
1837 unset($object->canvas);
1838
1839 return $object;
1840 }
1841
1850 private function _validate($data)
1851 {
1852 if ($data === null) {
1853 $data = array();
1854 }
1855 $invoice = array();
1856 foreach (Invoices::$FIELDS as $field) {
1857 if (!isset($data[$field])) {
1858 throw new RestException(400, "$field field missing");
1859 }
1860 $invoice[$field] = $data[$field];
1861 }
1862 return $invoice;
1863 }
1864
1865
1879 public function getTemplateInvoice($id, $contact_list = 1)
1880 {
1881 return $this->_fetchTemplateInvoice($id, '', '', $contact_list);
1882 }
1883
1897 private function _fetchTemplateInvoice($id, $ref = '', $ref_ext = '', $contact_list = 1)
1898 {
1899 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
1900 throw new RestException(403);
1901 }
1902
1903 $result = $this->template_invoice->fetch($id, $ref, $ref_ext);
1904 if (!$result) {
1905 throw new RestException(404, 'Template invoice not found');
1906 }
1907
1908 if (!DolibarrApi::_checkAccessToResource('facturerec', $this->template_invoice->id)) {
1909 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1910 }
1911
1912 // Add external contacts ids
1913 if ($contact_list > -1) {
1914 $tmparray = $this->template_invoice->liste_contact(-1, 'external', $contact_list);
1915 if (is_array($tmparray)) {
1916 $this->template_invoice->contacts_ids = $tmparray;
1917 }
1918 }
1919
1920 $this->template_invoice->fetchObjectLinked();
1921 return $this->_cleanTemplateObjectDatas($this->template_invoice);
1922 }
1923
1924
1925 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1933 {
1934 // phpcs:enable
1935 $object = parent::_cleanObjectDatas($object);
1936
1937 unset($object->note);
1938 unset($object->address);
1939 unset($object->barcode_type);
1940 unset($object->barcode_type_code);
1941 unset($object->barcode_type_label);
1942 unset($object->barcode_type_coder);
1943 unset($object->canvas);
1944
1945 return $object;
1946 }
1947}
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:48
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:67
Class to manage customers orders.
Class to manage absolute discounts.
Class for API REST v1.
Definition api.class.php:33
_filterObjectProperties($object, $properties)
Filter properties that will be returned on object.
static _checkAccessToResource($resource, $resource_id=0, $dbtablename='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid')
Check access by user to a given resource.
_checkValForAPI($field, $value, $object)
Check and convert a string depending on its type/name.
Definition api.class.php:98
Class to manage invoices.
const TYPE_REPLACEMENT
Replacement invoice.
const TYPE_STANDARD
Standard invoice.
const TYPE_SITUATION
Situation invoice.
const TYPE_DEPOSIT
Deposit invoice.
const TYPE_CREDIT_NOTE
Credit note invoice.
Class to manage invoice templates.
postContact($id, $contactid, $type)
Add a contact type of given invoice.
putPayment($id, $num_payment='')
Update a payment.
addContact($id, $fk_socpeople, $type_contact, $source, $notrigger=0)
Adds a contact to an invoice.
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.
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.
post($request_data=null)
Create invoice object.
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.
_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.
index($sortfield="t.rowid", $sortorder='ASC', $limit=100, $page=0, $thirdparty_ids='', $status='', $sqlfilters='', $properties='', $pagination_data=false, $loadlinkedobjects=0)
List invoices.
deleteLine($id, $lineid)
Deletes a line of a given invoice.
Class to manage payments of customer invoices.
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:431
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_now($mode='auto')
Return date for now.
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.
getMarginInfos($pv_ht, $remise_percent, $tva_tx, $localtax1_tx, $localtax2_tx, $fk_pa, $pa_ht)
Return an array with margins information of a line.