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 $invoiceline = new FactureLigne($this->db);
528 $result = $invoiceline->fetch($lineid);
529 if (!$result) {
530 throw new RestException(404, 'Invoice line not found');
531 }
532
533 if ($invoiceline->fk_facture != $id) {
534 throw new RestException(403, 'Line does not belong to this invoice');
535 }
536
537 $updateRes = $this->invoice->updateline(
538 $lineid,
539 $request_data->desc,
540 $request_data->subprice,
541 $request_data->qty,
542 $request_data->remise_percent,
543 $request_data->date_start,
544 $request_data->date_end,
545 $request_data->tva_tx,
546 $request_data->localtax1_tx,
547 $request_data->localtax2_tx,
548 $request_data->price_base_type ? $request_data->price_base_type : 'HT',
549 $request_data->info_bits,
550 $request_data->product_type,
551 $request_data->fk_parent_line,
552 0,
553 $request_data->fk_fournprice,
554 $request_data->pa_ht,
555 $request_data->label,
556 $request_data->special_code,
557 $request_data->array_options,
558 $request_data->situation_percent,
559 $request_data->fk_unit,
560 $request_data->multicurrency_subprice,
561 0,
562 $request_data->ref_ext,
563 $request_data->rang
564 );
565
566 if ($updateRes > 0) {
567 $result = $this->get($id);
568 unset($result->line);
569 return $this->_cleanObjectDatas($result);
570 } else {
571 throw new RestException(304, $this->invoice->error);
572 }
573 }
574
590 public function postContact($id, $contactid, $type)
591 {
592 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
593 throw new RestException(403);
594 }
595
596 $result = $this->invoice->fetch($id);
597
598 if (!$result) {
599 throw new RestException(404, 'Invoice not found');
600 }
601
602 if (!in_array($type, array('BILLING', 'SHIPPING', 'CUSTOMER'), true)) {
603 throw new RestException(500, 'Availables types: BILLING, SHIPPING OR CUSTOMER');
604 }
605
606 if (!DolibarrApi::_checkAccessToResource('invoice', $this->invoice->id)) {
607 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
608 }
609
610 $result = $this->invoice->add_contact($contactid, $type, 'external');
611
612 if (!$result) {
613 throw new RestException(500, 'Error when added the contact');
614 }
615
616 return array(
617 'success' => array(
618 'code' => 200,
619 'message' => 'Contact linked to the invoice'
620 )
621 );
622 }
623
638 public function deleteContact($id, $contactid, $type)
639 {
640 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
641 throw new RestException(403);
642 }
643
644 $result = $this->invoice->fetch($id);
645
646 if (!$result) {
647 throw new RestException(404, 'Invoice not found');
648 }
649
650 if (!DolibarrApi::_checkAccessToResource('invoice', $this->invoice->id)) {
651 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
652 }
653
654 $contacts = $this->invoice->liste_contact();
655
656 foreach ($contacts as $contact) {
657 if ($contact['id'] == $contactid && $contact['code'] == $type) {
658 $result = $this->invoice->delete_contact($contact['rowid']);
659
660 if (!$result) {
661 throw new RestException(500, 'Error when deleted the contact');
662 }
663 }
664 }
665
666 return $this->_cleanObjectDatas($this->invoice);
667 }
668
683 public function deleteLine($id, $lineid)
684 {
685 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
686 throw new RestException(403);
687 }
688 if (empty($lineid)) {
689 throw new RestException(400, 'Line ID is mandatory');
690 }
691
692 if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
693 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
694 }
695
696 $result = $this->invoice->fetch($id);
697 if (!$result) {
698 throw new RestException(404, 'Invoice not found');
699 }
700
701 $updateRes = $this->invoice->deleteLine($lineid, $id);
702 if ($updateRes > 0) {
703 return $this->get($id);
704 } else {
705 throw new RestException(405, $this->invoice->error);
706 }
707 }
708
718 public function put($id, $request_data = null)
719 {
720 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
721 throw new RestException(403);
722 }
723
724 $result = $this->invoice->fetch($id);
725 if (!$result) {
726 throw new RestException(404, 'Invoice not found');
727 }
728
729 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
730 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
731 }
732
733 foreach ($request_data as $field => $value) {
734 if ($field == 'id') {
735 continue;
736 }
737 if ($field === 'caller') {
738 // 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
739 $this->invoice->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
740 continue;
741 }
742 if ($field == 'array_options' && is_array($value)) {
743 foreach ($value as $index => $val) {
744 $this->invoice->array_options[$index] = $this->_checkValForAPI($field, $val, $this->invoice);
745 }
746 continue;
747 }
748
749 $this->invoice->$field = $this->_checkValForAPI($field, $value, $this->invoice);
750
751 // If cond reglement => update date lim reglement
752 if ($field == 'cond_reglement_id') {
753 $this->invoice->date_lim_reglement = $this->invoice->calculate_date_lim_reglement();
754 }
755 }
756
757 // update bank account
758 if (!empty($this->invoice->fk_account)) {
759 if ($this->invoice->setBankAccount($this->invoice->fk_account) == 0) {
760 throw new RestException(400, $this->invoice->error);
761 }
762 }
763
764 if ($this->invoice->update(DolibarrApiAccess::$user) > 0) {
765 return $this->get($id);
766 } else {
767 throw new RestException(500, $this->invoice->error);
768 }
769 }
770
779 public function delete($id)
780 {
781 if (!DolibarrApiAccess::$user->hasRight('facture', 'supprimer')) {
782 throw new RestException(403);
783 }
784 $result = $this->invoice->fetch($id);
785 if (!$result) {
786 throw new RestException(404, 'Invoice not found');
787 }
788
789 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
790 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
791 }
792
793 $result = $this->invoice->delete(DolibarrApiAccess::$user);
794 if ($result < 0) {
795 throw new RestException(500, 'Error when deleting invoice');
796 } elseif ($result == 0) {
797 throw new RestException(403, 'Invoice not erasable');
798 }
799
800 return array(
801 'success' => array(
802 'code' => 200,
803 'message' => 'Invoice deleted'
804 )
805 );
806 }
807
833 public function postLine($id, $request_data = null)
834 {
835 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
836 throw new RestException(403);
837 }
838
839 $result = $this->invoice->fetch($id);
840 if (!$result) {
841 throw new RestException(404, 'Invoice not found');
842 }
843
844 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
845 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
846 }
847
848 $request_data = (object) $request_data;
849
850 $request_data->desc = sanitizeVal($request_data->desc, 'restricthtml');
851 $request_data->label = sanitizeVal($request_data->label);
852
853 // Reset fk_parent_line for no child products and special product
854 if (($request_data->product_type != 9 && empty($request_data->fk_parent_line)) || $request_data->product_type == 9) {
855 $request_data->fk_parent_line = 0;
856 }
857
858 // calculate pa_ht
859 $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);
860 $pa_ht = $marginInfos[0];
861
862 $updateRes = $this->invoice->addline(
863 $request_data->desc,
864 $request_data->subprice,
865 $request_data->qty,
866 $request_data->tva_tx,
867 $request_data->localtax1_tx,
868 $request_data->localtax2_tx,
869 $request_data->fk_product,
870 $request_data->remise_percent,
871 $request_data->date_start,
872 $request_data->date_end,
873 $request_data->fk_code_ventilation,
874 $request_data->info_bits,
875 $request_data->fk_remise_except,
876 $request_data->price_base_type ? $request_data->price_base_type : 'HT',
877 $request_data->subprice,
878 $request_data->product_type,
879 $request_data->rang,
880 $request_data->special_code,
881 $request_data->origin,
882 $request_data->origin_id,
883 $request_data->fk_parent_line,
884 empty($request_data->fk_fournprice) ? null : $request_data->fk_fournprice,
885 $pa_ht,
886 $request_data->label,
887 $request_data->array_options,
888 $request_data->situation_percent,
889 $request_data->fk_prev_id,
890 $request_data->fk_unit,
891 0,
892 $request_data->ref_ext
893 );
894
895 if ($updateRes < 0) {
896 throw new RestException(400, 'Unable to insert the new line. Check your inputs. '.$this->invoice->error);
897 }
898
899 return $updateRes;
900 }
901
920 public function addContact($id, $fk_socpeople, $type_contact, $source, $notrigger = 0)
921 {
922 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
923 throw new RestException(403);
924 }
925 $result = $this->invoice->fetch($id);
926 if (!$result) {
927 throw new RestException(404, 'Invoice not found');
928 }
929
930 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
931 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
932 }
933
934 $result = $this->invoice->add_contact($fk_socpeople, $type_contact, $source, $notrigger);
935 if ($result < 0) {
936 throw new RestException(500, 'Error : '.$this->invoice->error);
937 }
938
939 $result = $this->invoice->fetch($id);
940 if (!$result) {
941 throw new RestException(404, 'Invoice not found');
942 }
943
944 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
945 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
946 }
947
948 return $this->_cleanObjectDatas($this->invoice);
949 }
950
951
952
967 public function settodraft($id, $idwarehouse = -1)
968 {
969 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
970 throw new RestException(403);
971 }
972 $result = $this->invoice->fetch($id);
973 if (!$result) {
974 throw new RestException(404, 'Invoice not found');
975 }
976
977 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
978 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
979 }
980
981 $result = $this->invoice->setDraft(DolibarrApiAccess::$user, $idwarehouse);
982 if ($result == 0) {
983 throw new RestException(304, 'Nothing done.');
984 }
985 if ($result < 0) {
986 throw new RestException(500, 'Error : '.$this->invoice->error);
987 }
988
989 $result = $this->invoice->fetch($id);
990 if (!$result) {
991 throw new RestException(404, 'Invoice not found');
992 }
993
994 return $this->_cleanObjectDatas($this->invoice);
995 }
996
997
1015 public function validate($id, $force_number = '', $idwarehouse = 0, $notrigger = 0)
1016 {
1017 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1018 throw new RestException(403);
1019 }
1020 $result = $this->invoice->fetch($id);
1021 if (!$result) {
1022 throw new RestException(404, 'Invoice not found');
1023 }
1024
1025 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1026 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1027 }
1028
1029 $result = $this->invoice->validate(DolibarrApiAccess::$user, $force_number, $idwarehouse, $notrigger);
1030 if ($result == 0) {
1031 throw new RestException(304, 'Error nothing done. May be object is already validated');
1032 }
1033 if ($result < 0) {
1034 throw new RestException(500, 'Error when validating Invoice: '.$this->invoice->error);
1035 }
1036
1037 $result = $this->invoice->fetch($id);
1038 if (!$result) {
1039 throw new RestException(404, 'Invoice not found');
1040 }
1041
1042 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1043 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1044 }
1045
1046 // copy from order
1047 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
1048 $this->invoice->online_payment_url = getOnlinePaymentUrl(0, 'invoice', $this->invoice->ref);
1049
1050 return $this->_cleanObjectDatas($this->invoice);
1051 }
1052
1068 public function settopaid($id, $close_code = '', $close_note = '')
1069 {
1070 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1071 throw new RestException(403);
1072 }
1073 $result = $this->invoice->fetch($id);
1074 if (!$result) {
1075 throw new RestException(404, 'Invoice not found');
1076 }
1077
1078 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1079 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1080 }
1081
1082 $result = $this->invoice->setPaid(DolibarrApiAccess::$user, $close_code, $close_note);
1083 if ($result == 0) {
1084 throw new RestException(304, 'Error nothing done. May be object is already validated');
1085 }
1086 if ($result < 0) {
1087 throw new RestException(500, 'Error : '.$this->invoice->error);
1088 }
1089
1090
1091 $result = $this->invoice->fetch($id);
1092 if (!$result) {
1093 throw new RestException(404, 'Invoice not found');
1094 }
1095
1096 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1097 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1098 }
1099
1100 return $this->_cleanObjectDatas($this->invoice);
1101 }
1102
1103
1117 public function settounpaid($id)
1118 {
1119 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1120 throw new RestException(403);
1121 }
1122 $result = $this->invoice->fetch($id);
1123 if (!$result) {
1124 throw new RestException(404, 'Invoice not found');
1125 }
1126
1127 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1128 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1129 }
1130
1131 $result = $this->invoice->setUnpaid(DolibarrApiAccess::$user);
1132 if ($result == 0) {
1133 throw new RestException(304, 'Nothing done');
1134 }
1135 if ($result < 0) {
1136 throw new RestException(500, 'Error : '.$this->invoice->error);
1137 }
1138
1139
1140 $result = $this->invoice->fetch($id);
1141 if (!$result) {
1142 throw new RestException(404, 'Invoice not found');
1143 }
1144
1145 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1146 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1147 }
1148
1149 return $this->_cleanObjectDatas($this->invoice);
1150 }
1151
1160 public function getDiscount($id)
1161 {
1162 require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1163
1164 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
1165 throw new RestException(403);
1166 }
1167
1168 $result = $this->invoice->fetch($id);
1169 if (!$result) {
1170 throw new RestException(404, 'Invoice not found');
1171 }
1172
1173 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1174 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1175 }
1176
1177 $discountcheck = new DiscountAbsolute($this->db);
1178 $result = $discountcheck->fetch(0, $this->invoice->id);
1179
1180 if ($result == 0) {
1181 throw new RestException(404, 'Discount not found');
1182 }
1183 if ($result < 0) {
1184 throw new RestException(500, $discountcheck->error);
1185 }
1186
1187 return parent::_cleanObjectDatas($discountcheck);
1188 }
1189
1204 {
1205 require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1206
1207 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1208 throw new RestException(403);
1209 }
1210
1211 $result = $this->invoice->fetch($id);
1212 if (!$result) {
1213 throw new RestException(404, 'Invoice not found');
1214 }
1215
1216 if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
1217 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1218 }
1219
1220 if ($this->invoice->paye) {
1221 throw new RestException(500, 'Alreay paid');
1222 }
1223
1224 $this->invoice->fetch($id);
1225 $this->invoice->fetch_thirdparty();
1226
1227 // Check if there is already a discount (protection to avoid duplicate creation when resubmit post)
1228 $discountcheck = new DiscountAbsolute($this->db);
1229 $result = $discountcheck->fetch(0, $this->invoice->id);
1230
1231 $canconvert = 0;
1232 if ($this->invoice->type == Facture::TYPE_DEPOSIT && empty($discountcheck->id)) {
1233 $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)
1234 }
1235 if (($this->invoice->type == Facture::TYPE_CREDIT_NOTE || $this->invoice->type == Facture::TYPE_STANDARD) && $this->invoice->paye == 0 && empty($discountcheck->id)) {
1236 $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)
1237 }
1238 if ($canconvert) {
1239 $this->db->begin();
1240
1241 $amount_ht = $amount_tva = $amount_ttc = array();
1242 $multicurrency_amount_ht = $multicurrency_amount_tva = $multicurrency_amount_ttc = array();
1243 '
1244 @phan-var-force array<string,float> $amount_ht
1245 @phan-var-force array<string,float> $amount_tva
1246 @phan-var-force array<string,float> $amount_ttc
1247 @phan-var-force array<string,float> $multicurrency_amount_ht
1248 @phan-var-force array<string,float> $multicurrency_amount_tva
1249 @phan-var-force array<string,float> $multicurrency_amount_ttc
1250 ';
1251
1252 // Loop on each vat rate
1253 $i = 0;
1254 foreach ($this->invoice->lines as $line) {
1255 if ($line->product_type < 9 && $line->total_ht != 0) { // Remove lines with product_type greater than or equal to 9
1256 if (!array_key_exists($line->tva_tx, $amount_ht)) {
1257 $amount_ht[$line->tva_tx] = 0.0;
1258 $amount_tva[$line->tva_tx] = 0.0;
1259 $amount_ttc[$line->tva_tx] = 0.0;
1260 $multicurrency_amount_ht[$line->tva_tx] = 0.0;
1261 $multicurrency_amount_tva[$line->tva_tx] = 0.0;
1262 $multicurrency_amount_ttc[$line->tva_tx] = 0.0;
1263 }
1264 // no need to create discount if amount is null
1265 $amount_ht[$line->tva_tx] += $line->total_ht;
1266 $amount_tva[$line->tva_tx] += $line->total_tva;
1267 $amount_ttc[$line->tva_tx] += $line->total_ttc;
1268 $multicurrency_amount_ht[$line->tva_tx] += $line->multicurrency_total_ht;
1269 $multicurrency_amount_tva[$line->tva_tx] += $line->multicurrency_total_tva;
1270 $multicurrency_amount_ttc[$line->tva_tx] += $line->multicurrency_total_ttc;
1271 $i++;
1272 }
1273 }
1274
1275 // Insert one discount by VAT rate category
1276 $discount = new DiscountAbsolute($this->db);
1277 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1278 $discount->description = '(CREDIT_NOTE)';
1279 } elseif ($this->invoice->type == Facture::TYPE_DEPOSIT) {
1280 $discount->description = '(DEPOSIT)';
1281 } elseif ($this->invoice->type == Facture::TYPE_STANDARD || $this->invoice->type == Facture::TYPE_REPLACEMENT || $this->invoice->type == Facture::TYPE_SITUATION) {
1282 $discount->description = '(EXCESS RECEIVED)';
1283 } else {
1284 throw new RestException(500, 'Cant convert to reduc an Invoice of this type');
1285 }
1286
1287 $discount->fk_soc = $this->invoice->socid;
1288 $discount->socid = $this->invoice->socid;
1289 $discount->fk_facture_source = $this->invoice->id;
1290
1291 $error = 0;
1292
1293 if ($this->invoice->type == Facture::TYPE_STANDARD || $this->invoice->type == Facture::TYPE_REPLACEMENT || $this->invoice->type == Facture::TYPE_SITUATION) {
1294 // If we're on a standard invoice, we have to get excess received to create a discount in TTC without VAT
1295
1296 // Total payments
1297 $sql = 'SELECT SUM(pf.amount) as total_payments';
1298 $sql .= ' FROM '.MAIN_DB_PREFIX.'paiement_facture as pf, '.MAIN_DB_PREFIX.'paiement as p';
1299 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as c ON p.fk_paiement = c.id';
1300 $sql .= ' WHERE pf.fk_facture = '.((int) $this->invoice->id);
1301 $sql .= ' AND pf.fk_paiement = p.rowid';
1302 $sql .= ' AND p.entity IN ('.getEntity('invoice').')';
1303 $resql = $this->db->query($sql);
1304 if (!$resql) {
1305 dol_print_error($this->db);
1306 }
1307
1308 $res = $this->db->fetch_object($resql);
1309 $total_payments = $res->total_payments;
1310
1311 // Total credit note and deposit
1312 $total_creditnote_and_deposit = 0;
1313 $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
1314 $sql .= " re.description, re.fk_facture_source";
1315 $sql .= " FROM ".MAIN_DB_PREFIX."societe_remise_except as re";
1316 $sql .= " WHERE fk_facture = ".((int) $this->invoice->id);
1317 $resql = $this->db->query($sql);
1318 if (!empty($resql)) {
1319 while ($obj = $this->db->fetch_object($resql)) {
1320 $total_creditnote_and_deposit += $obj->amount_ttc;
1321 }
1322 } else {
1323 dol_print_error($this->db);
1324 }
1325
1326 $discount->amount_ht = $discount->amount_ttc = $total_payments + $total_creditnote_and_deposit - $this->invoice->total_ttc;
1327 $discount->amount_tva = 0;
1328 $discount->tva_tx = 0;
1329
1330 $result = $discount->create(DolibarrApiAccess::$user);
1331 if ($result < 0) {
1332 $error++;
1333 }
1334 }
1335 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE || $this->invoice->type == Facture::TYPE_DEPOSIT) {
1336 foreach ($amount_ht as $tva_tx => $xxx) {
1337 $discount->amount_ht = abs($amount_ht[$tva_tx]);
1338 $discount->amount_tva = abs($amount_tva[$tva_tx]);
1339 $discount->amount_ttc = abs($amount_ttc[$tva_tx]);
1340 $discount->multicurrency_amount_ht = abs($multicurrency_amount_ht[$tva_tx]);
1341 $discount->multicurrency_amount_tva = abs($multicurrency_amount_tva[$tva_tx]);
1342 $discount->multicurrency_amount_ttc = abs($multicurrency_amount_ttc[$tva_tx]);
1343 $discount->tva_tx = abs((float) $tva_tx);
1344
1345 $result = $discount->create(DolibarrApiAccess::$user);
1346 if ($result < 0) {
1347 $error++;
1348 break;
1349 }
1350 }
1351 }
1352
1353 if (empty($error)) {
1354 if ($this->invoice->type != Facture::TYPE_DEPOSIT) {
1355 // Set the invoice as paid
1356 $result = $this->invoice->setPaid(DolibarrApiAccess::$user);
1357 if ($result >= 0) {
1358 $this->db->commit();
1359 } else {
1360 $this->db->rollback();
1361 throw new RestException(500, 'Could not set paid');
1362 }
1363 } else {
1364 $this->db->commit();
1365 }
1366 } else {
1367 $this->db->rollback();
1368 throw new RestException(500, 'Discount creation error');
1369 }
1370 }
1371
1372 return $this->_cleanObjectDatas($this->invoice);
1373 }
1374
1391 public function useDiscount($id, $discountid)
1392 {
1393 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1394 throw new RestException(403);
1395 }
1396 if (empty($id)) {
1397 throw new RestException(400, 'Invoice ID is mandatory');
1398 }
1399 if (empty($discountid)) {
1400 throw new RestException(400, 'Discount ID is mandatory');
1401 }
1402
1403 if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1404 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1405 }
1406
1407 $result = $this->invoice->fetch($id);
1408 if (!$result) {
1409 throw new RestException(404, 'Invoice not found');
1410 }
1411
1412 $result = $this->invoice->insert_discount($discountid);
1413 if ($result < 0) {
1414 throw new RestException(405, $this->invoice->error);
1415 }
1416
1417 return $result;
1418 }
1419
1436 public function useCreditNote($id, $discountid)
1437 {
1438 require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1439
1440 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1441 throw new RestException(403);
1442 }
1443 if (empty($id)) {
1444 throw new RestException(400, 'Invoice ID is mandatory');
1445 }
1446 if (empty($discountid)) {
1447 throw new RestException(400, 'Credit ID is mandatory');
1448 }
1449
1450 if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1451 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1452 }
1453 $discount = new DiscountAbsolute($this->db);
1454 $result = $discount->fetch($discountid);
1455 if (!$result) {
1456 throw new RestException(404, 'Credit not found');
1457 }
1458
1459 $result = $discount->link_to_invoice(0, $id);
1460 if ($result < 0) {
1461 throw new RestException(405, $discount->error);
1462 }
1463
1464 return $result;
1465 }
1466
1482 public function getPayments($id)
1483 {
1484 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
1485 throw new RestException(403);
1486 }
1487 if (empty($id)) {
1488 throw new RestException(400, 'Invoice ID is mandatory');
1489 }
1490
1491 if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1492 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1493 }
1494
1495 $result = $this->invoice->fetch($id);
1496 if (!$result) {
1497 throw new RestException(404, 'Invoice not found');
1498 }
1499
1500 $result = $this->invoice->getListOfPayments();
1501 if (!is_array($result) && $result < 0) {
1502 throw new RestException(405, $this->invoice->error);
1503 }
1504
1505 return $result;
1506 }
1507
1508
1530 public function addPayment($id, $datepaye, $paymentid, $closepaidinvoices, $accountid, $num_payment = '', $comment = '', $chqemetteur = '', $chqbank = '')
1531 {
1532 require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
1533
1534 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1535 throw new RestException(403);
1536 }
1537 if (empty($id)) {
1538 throw new RestException(400, 'Invoice ID is mandatory');
1539 }
1540
1541 if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
1542 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1543 }
1544
1545 if (isModEnabled("bank")) {
1546 if (empty($accountid)) {
1547 throw new RestException(400, 'Account ID is mandatory');
1548 }
1549 }
1550
1551 if (empty($paymentid)) {
1552 throw new RestException(400, 'Payment ID or Payment Code is mandatory');
1553 }
1554
1555
1556 $result = $this->invoice->fetch($id);
1557 if (!$result) {
1558 throw new RestException(404, 'Invoice not found');
1559 }
1560
1561 // Calculate amount to pay
1562 $totalpaid = $this->invoice->getSommePaiement();
1563 $totalcreditnotes = $this->invoice->getSumCreditNotesUsed();
1564 $totaldeposits = $this->invoice->getSumDepositsUsed();
1565 $resteapayer = price2num($this->invoice->total_ttc - $totalpaid - $totalcreditnotes - $totaldeposits, 'MT');
1566
1567 $this->db->begin();
1568
1569 $amounts = array();
1570 $multicurrency_amounts = array();
1571
1572 // Clean parameters amount if payment is for a credit note
1573 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1574 $resteapayer = price2num($resteapayer, 'MT');
1575 $amounts[$id] = (float) price2num(-1 * abs((float) $resteapayer), 'MT');
1576 // Multicurrency
1577 $newvalue = price2num($this->invoice->multicurrency_total_ttc, 'MT');
1578 $multicurrency_amounts[$id] = (float) price2num(-1 * (float) $newvalue, 'MT');
1579 } else {
1580 $resteapayer = price2num($resteapayer, 'MT');
1581 $amounts[$id] = (float) $resteapayer;
1582 // Multicurrency
1583 $newvalue = price2num($this->invoice->multicurrency_total_ttc, 'MT');
1584 $multicurrency_amounts[$id] = (float) $newvalue;
1585 }
1586
1587 // Creation of payment line
1588 $paymentobj = new Paiement($this->db);
1589 if (is_numeric($datepaye)) {
1590 $paymentobj->datepaye = $datepaye;
1591 } else {
1592 $paymentobj->datepaye = dol_stringtotime($datepaye);
1593 }
1594 $paymentobj->amounts = $amounts; // Array with all payments dispatching with invoice id
1595 $paymentobj->multicurrency_amounts = $multicurrency_amounts; // Array with all payments dispatching
1596 $paymentobj->paiementid = $paymentid;
1597 $paymentobj->paiementcode = (string) dol_getIdFromCode($this->db, (string) $paymentid, 'c_paiement', 'id', 'code', 1);
1598 $paymentobj->num_payment = $num_payment;
1599 $paymentobj->note_private = $comment;
1600
1601 $payment_id = $paymentobj->create(DolibarrApiAccess::$user, ($closepaidinvoices == 'yes' ? 1 : 0)); // This include closing invoices
1602 if ($payment_id < 0) {
1603 $this->db->rollback();
1604 throw new RestException(400, 'Payment error : '.$paymentobj->error);
1605 }
1606
1607 if (isModEnabled("bank")) {
1608 $label = '(CustomerInvoicePayment)';
1609
1610 if ($paymentobj->paiementcode == 'CHQ' && empty($chqemetteur)) {
1611 throw new RestException(400, 'Emetteur is mandatory when payment code is '.$paymentobj->paiementcode);
1612 }
1613 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1614 $label = '(CustomerInvoicePaymentBack)'; // Refund of a credit note
1615 }
1616 $result = $paymentobj->addPaymentToBank(DolibarrApiAccess::$user, 'payment', $label, $accountid, $chqemetteur, $chqbank);
1617 if ($result < 0) {
1618 $this->db->rollback();
1619 throw new RestException(400, 'Add payment to bank error : '.$paymentobj->error);
1620 }
1621 }
1622
1623 $this->db->commit();
1624
1625 return $payment_id;
1626 }
1627
1656 public function addPaymentDistributed($arrayofamounts, $datepaye, $paymentid, $closepaidinvoices, $accountid, $num_payment = '', $comment = '', $chqemetteur = '', $chqbank = '', $ref_ext = '', $accepthigherpayment = false)
1657 {
1658 require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
1659
1660 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1661 throw new RestException(403);
1662 }
1663 foreach ($arrayofamounts as $id => $amount) {
1664 if (empty($id)) {
1665 throw new RestException(400, 'Invoice ID is mandatory. Fill the invoice id and amount into arrayofamounts parameter. For example: {"1": "99.99", "2": "10"}');
1666 }
1667 if (!DolibarrApi::_checkAccessToResource('facture', (int) $id)) {
1668 throw new RestException(403, 'Access not allowed on invoice ID '.$id.' for login '.DolibarrApiAccess::$user->login);
1669 }
1670 }
1671
1672 if (isModEnabled("bank")) {
1673 if (empty($accountid)) {
1674 throw new RestException(400, 'Account ID is mandatory');
1675 }
1676 }
1677 if (empty($paymentid)) {
1678 throw new RestException(400, 'Payment ID or Payment Code is mandatory');
1679 }
1680
1681 $this->db->begin();
1682
1683 $amounts = array();
1684 $multicurrency_amounts = array();
1685
1686 // Loop on each invoice to pay
1687 foreach ($arrayofamounts as $id => $amountarray) {
1688 $result = $this->invoice->fetch((int) $id);
1689 if (!$result) {
1690 $this->db->rollback();
1691 throw new RestException(404, 'Invoice ID '.$id.' not found');
1692 }
1693
1694 if (($amountarray["amount"] == "remain" || $amountarray["amount"] > 0) && ($amountarray["multicurrency_amount"] == "remain" || $amountarray["multicurrency_amount"] > 0)) {
1695 $this->db->rollback();
1696 throw new RestException(400, 'Payment in both currency '.$id.' ( amount: '.$amountarray["amount"].', multicurrency_amount: '.$amountarray["multicurrency_amount"].')');
1697 }
1698
1699 $is_multicurrency = 0;
1700 $total_ttc = $this->invoice->total_ttc;
1701
1702 if ($amountarray["multicurrency_amount"] > 0 || $amountarray["multicurrency_amount"] == "remain") {
1703 $is_multicurrency = 1;
1704 $total_ttc = $this->invoice->multicurrency_total_ttc;
1705 }
1706
1707 // Calculate amount to pay
1708 $totalpaid = $this->invoice->getSommePaiement($is_multicurrency);
1709 $totalcreditnotes = $this->invoice->getSumCreditNotesUsed($is_multicurrency);
1710 $totaldeposits = $this->invoice->getSumDepositsUsed($is_multicurrency);
1711 $remainstopay = $amount = (float) price2num($total_ttc - $totalpaid - $totalcreditnotes - $totaldeposits, 'MT');
1712
1713 if (!$is_multicurrency && $amountarray["amount"] != 'remain') {
1714 $amount = (float) price2num($amountarray["amount"], 'MT');
1715 }
1716
1717 if ($is_multicurrency && $amountarray["multicurrency_amount"] != 'remain') {
1718 $amount = (float) price2num($amountarray["multicurrency_amount"], 'MT');
1719 }
1720
1721 if (abs($amount) > abs($remainstopay) && !$accepthigherpayment) {
1722 $this->db->rollback();
1723 throw new RestException(400, 'Payment amount on invoice ID '.$id.' ('.$amount.') is higher than remain to pay ('.$remainstopay.')');
1724 }
1725
1726 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1727 $amount = (float) price2num(-1 * abs((float) $amount), 'MT');
1728 }
1729
1730 if ($is_multicurrency) {
1731 $amounts[$id] = null;
1732 // Multicurrency
1733 $multicurrency_amounts[$id] = (float) $amount;
1734 } else {
1735 $amounts[$id] = (float) $amount;
1736 // Multicurrency
1737 $multicurrency_amounts[$id] = null;
1738 }
1739 }
1740
1741 // Creation of payment line
1742 $paymentobj = new Paiement($this->db);
1743 if (is_numeric($datepaye)) {
1744 $paymentobj->datepaye = $datepaye;
1745 } else {
1746 $paymentobj->datepaye = dol_stringtotime($datepaye);
1747 }
1748 $paymentobj->amounts = $amounts; // Array with all payments dispatching with invoice id
1749 $paymentobj->multicurrency_amounts = $multicurrency_amounts; // Array with all payments dispatching
1750 $paymentobj->paiementid = $paymentid;
1751 $paymentobj->paiementcode = (string) dol_getIdFromCode($this->db, (string) $paymentid, 'c_paiement', 'id', 'code', 1);
1752 $paymentobj->num_payment = $num_payment;
1753 $paymentobj->note_private = $comment;
1754 $paymentobj->ref_ext = $ref_ext;
1755 $payment_id = $paymentobj->create(DolibarrApiAccess::$user, ($closepaidinvoices == 'yes' ? 1 : 0)); // This include closing invoices
1756 if ($payment_id < 0) {
1757 $this->db->rollback();
1758 throw new RestException(400, 'Payment error : '.$paymentobj->error);
1759 }
1760 if (isModEnabled("bank")) {
1761 $label = '(CustomerInvoicePayment)';
1762 if ($paymentobj->paiementcode == 'CHQ' && empty($chqemetteur)) {
1763 throw new RestException(400, 'Emetteur is mandatory when payment code is '.$paymentobj->paiementcode);
1764 }
1765 if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
1766 $label = '(CustomerInvoicePaymentBack)'; // Refund of a credit note
1767 }
1768 $result = $paymentobj->addPaymentToBank(DolibarrApiAccess::$user, 'payment', $label, $accountid, $chqemetteur, $chqbank);
1769 if ($result < 0) {
1770 $this->db->rollback();
1771 throw new RestException(400, 'Add payment to bank error : '.$paymentobj->error);
1772 }
1773 }
1774
1775 $this->db->commit();
1776
1777 return $payment_id;
1778 }
1779
1796 public function putPayment($id, $num_payment = '')
1797 {
1798 require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
1799
1800 if (!DolibarrApiAccess::$user->hasRight('facture', 'creer')) {
1801 throw new RestException(403);
1802 }
1803 if (empty($id)) {
1804 throw new RestException(400, 'Payment ID is mandatory');
1805 }
1806
1807 $paymentobj = new Paiement($this->db);
1808 $result = $paymentobj->fetch($id);
1809
1810 if (!$result) {
1811 throw new RestException(404, 'Payment not found');
1812 }
1813
1814 if (!empty($num_payment)) {
1815 $result = $paymentobj->update_num($num_payment);
1816 if ($result < 0) {
1817 throw new RestException(500, 'Error when updating the payment num');
1818 }
1819 }
1820
1821 return [
1822 'success' => [
1823 'code' => 200,
1824 'message' => 'Payment updated'
1825 ]
1826 ];
1827 }
1828
1829 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1836 protected function _cleanObjectDatas($object)
1837 {
1838 // phpcs:enable
1839 $object = parent::_cleanObjectDatas($object);
1840
1841 unset($object->note);
1842 unset($object->address);
1843 unset($object->barcode_type);
1844 unset($object->barcode_type_code);
1845 unset($object->barcode_type_label);
1846 unset($object->barcode_type_coder);
1847 unset($object->canvas);
1848
1849 return $object;
1850 }
1851
1860 private function _validate($data)
1861 {
1862 if ($data === null) {
1863 $data = array();
1864 }
1865 $invoice = array();
1866 foreach (Invoices::$FIELDS as $field) {
1867 if (!isset($data[$field])) {
1868 throw new RestException(400, "$field field missing");
1869 }
1870 $invoice[$field] = $data[$field];
1871 }
1872 return $invoice;
1873 }
1874
1875
1889 public function getTemplateInvoice($id, $contact_list = 1)
1890 {
1891 return $this->_fetchTemplateInvoice($id, '', '', $contact_list);
1892 }
1893
1907 private function _fetchTemplateInvoice($id, $ref = '', $ref_ext = '', $contact_list = 1)
1908 {
1909 if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
1910 throw new RestException(403);
1911 }
1912
1913 $result = $this->template_invoice->fetch($id, $ref, $ref_ext);
1914 if (!$result) {
1915 throw new RestException(404, 'Template invoice not found');
1916 }
1917
1918 if (!DolibarrApi::_checkAccessToResource('facturerec', $this->template_invoice->id)) {
1919 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1920 }
1921
1922 // Add external contacts ids
1923 if ($contact_list > -1) {
1924 $tmparray = $this->template_invoice->liste_contact(-1, 'external', $contact_list);
1925 if (is_array($tmparray)) {
1926 $this->template_invoice->contacts_ids = $tmparray;
1927 }
1928 }
1929
1930 $this->template_invoice->fetchObjectLinked();
1931 return $this->_cleanTemplateObjectDatas($this->template_invoice);
1932 }
1933
1934
1935 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1943 {
1944 // phpcs:enable
1945 $object = parent::_cleanObjectDatas($object);
1946
1947 unset($object->note);
1948 unset($object->address);
1949 unset($object->barcode_type);
1950 unset($object->barcode_type_code);
1951 unset($object->barcode_type_label);
1952 unset($object->barcode_type_coder);
1953 unset($object->canvas);
1954
1955 return $object;
1956 }
1957}
$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 lines.
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.