Bläddra i källkod

cross-shopping fixes

szollosil 1 år sedan
förälder
incheckning
8075985abe

+ 20 - 10
custom/bbus/class/api_bbus.class.php

@@ -466,22 +466,22 @@ class BBus extends DolibarrApi
 		global $user;
 		$helper = new ApiBBusHelper();
 		$facture_id = $helper->getFactureIdForInvoicePrinting($ref);
-		if(empty($facture_id)){
+		if (empty($facture_id)) {
 			ApiBbusLog::appLog("localDateHandler: facture_id because cross-shopping");
 			$printedCopies = $this->checkPrintedCopiesWithRef($ref);
-		}else{
+		} else {
 			$printedCopies = $this->checkPrintedCopies($facture_id);
 		}
 		// Leellenőrzöm, hogy a számla volt-e már nyomtatva
-		
+
 		ApiBbusLog::appLog("printedCopies: {$printedCopies}");
 
 
 		if ($printedCopies == 0) {
 			ApiBbusLog::appLog("printedCopies: no copies");
-			if(empty($facture_id)){
+			if (empty($facture_id)) {
 				$bbTicketsByFacture = $this->getTicketsByInvoiceNumber($ref);
-			}else{
+			} else {
 				$bbTicketsByFacture = $this->getTicketsByFactureId($facture_id);
 			}
 			foreach ($bbTicketsByFacture as $ticket) {
@@ -489,9 +489,9 @@ class BBus extends DolibarrApi
 				$helper->setPrintingInvoiceObject($user, $facture_id, $datetime, $datetime_timestamp, $ticket, $ref);
 			}
 		} else {
-			if(empty($facture_id)){
+			if (empty($facture_id)) {
 				$ticketIds = $helper->getTicketIdsForCrossShopping($ref);
-			}else{
+			} else {
 				$ticketIds = $helper->getTicketIdsByFactureId($facture_id);
 			}
 			$ticketObj = new stdClass();
@@ -506,7 +506,7 @@ class BBus extends DolibarrApi
 			return 'Multiprinting';
 		}
 	}
-/* 	private function localDateHandler($ref, $datetime, $datetime_timestamp, $now)
+	/* 	private function localDateHandler($ref, $datetime, $datetime_timestamp, $now)
 	{
 		global $user;
 		$helper = new ApiBBusHelper();
@@ -1722,7 +1722,7 @@ class BBus extends DolibarrApi
 	 * 
 	 * @url POST /invoice
 	 */
-	public function invoice(array $invoice, array $lines, array $payment, string $cardPaymentLog = '', string $sendId = '')
+	public function invoice(array $invoice, array $lines, array $payment, string $cardPaymentLog = '', string $sendId = '', $server_host_curl = false)
 	{
 		global $user;
 		$lockLabel = 'CREATE_INVOICE';
@@ -1810,6 +1810,16 @@ class BBus extends DolibarrApi
 		ApiBbusLog::appLog("{$sendId}####################################################################");
 		dol_syslog("{$sendId}####################################################################", LOG_INFO, 0);
 
+		#-------------- UPDATE BTICKET --------------
+		ApiBbusLog::appLog("{$sendId}: Update bbticket with curl");
+		$invoiceForCurl['id'] = $invoiceObj->id;
+		$invoiceForCurl['ref'] = $invoiceObj->ref;
+		//$array['fk_booking_history'] = null;
+		$array['invoice'] = $invoiceForCurl;
+		$updateBBticketPostFields = json_encode($array);
+		ApiBbusLog::appLog("{$updateBBticketPostFields}");
+		$this->curlRunner('bookingapi/curlUpdateBbticket', $updateBBticketPostFields, 'POST', true);
+
 		return [
 			'sendId' => $sendId,
 			'invoice' => [
@@ -2152,7 +2162,7 @@ class BBus extends DolibarrApi
 		if ($this->db->num_rows($result) < 1) {
 			return [];
 		} else {
-			while($row=$this->db->fetch_object($result)){
+			while ($row = $this->db->fetch_object($result)) {
 				return $row->datec;
 			}
 		}

+ 2178 - 2178
custom/bbus/class/api_bbus.class_old.php

@@ -1,2178 +1,2178 @@
-<?php
-/* Copyright (C) 2015   Jean-François Ferry     <jfefe@aternatik.fr>
-* Copyright (C) 2019   Cedric Ancelin          <icedo.anc@gmail.com>
-*
-* This program is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation; either version 3 of the License, or
-* (at your option) any later version.
-*
-program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-* GNU General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-
-use Luracast\Restler\RestException;
-use Sabre\Xml\Element;
-
-
-require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
-require_once DOL_DOCUMENT_ROOT . '/product/stock/class/productlot.class.php';
-require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
-require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
-require_once DOL_DOCUMENT_ROOT . '/variants/class/ProductAttribute.class.php';
-require_once DOL_DOCUMENT_ROOT . '/variants/class/ProductAttributeValue.class.php';
-require_once DOL_DOCUMENT_ROOT . '/variants/class/ProductCombination.class.php';
-require_once DOL_DOCUMENT_ROOT . '/variants/class/ProductCombination2ValuePair.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/sellproduct.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/apiuserhandler.class.php';
-require_once DOL_DOCUMENT_ROOT . '/compta/facture/class/facture.class.php';
-
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/jegy.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbloginlog.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbexchangerate.class.php';
-
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbticket.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbdevices.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbdevicesservicelocation.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbdevicesservicelocationproduct.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbticketinvoiceprinting.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/utils/globalconst.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/utils/emailtemplatehandler.php';
-include_once DOL_DOCUMENT_ROOT . '/core/class/CMailFile.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbticketnaplo.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbtickethandler.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/gpsposition.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/userloginnaplo.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/utils/countryhandler.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/utils/entityhandler.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/discountnaplo.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/settlements/class/groupusers.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/rollerstorage/class/packagehistory.class.php';
-require_once DOL_DOCUMENT_ROOT . '/product/inventory/class/inventory.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/apiproductlisthelper.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/apiinvoicehelper.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/ticket_checker.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/api_bbus_helper.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/api_bbus_log.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbapilock.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/exchangerateupdater.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbticketvalidationcoords.class.php';
-require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/api_curl.class.php';
-
-
-
-/**
- * API class for products
- *
- * @access protected
- * @class  DolibarrApiAccess {@requires user,external}
- */
-class BBus extends DolibarrApi
-{
-
-	use CurlApi;
-
-	private $code;
-	private $printingTime;
-	private $status = 0;
-	private $device_id;
-	private $service_location_id;
-	private $bbTicketRowId;
-	private $facture_id;
-	private $isValid = true;
-	private User $user;
-
-
-	const EMAIL_TEMPLATE = 'multiticketprinting';
-	const GLOBAL_CONF_SEND_TO_EMAIL = 'BBUS_INVOICE_PRINTING_ALERT_EMAIL';
-	const GLOBAL_CONF_EMAIL_FROM = 'BBUS_INVOICE_PRINTING_ALERT_EMAIL_FROM';
-
-	public function __construct()
-	{
-		global $db, $conf, $user;
-
-		$this->db = $db;
-		$this->user = $user;
-	}
-
-
-	#-------------------------------------------------
-	#	Vásárlási folyamat
-	#-------------------------------------------------
-
-	/**
-	 * Get app config
-	 *
-	 * Return an array with config params.
-	 *
-	 * @return array|mixed Data without useless information
-	 *
-	 * @url GET config
-	 *
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 */
-	public function config(): array
-	{
-		global $user, $conf;
-		$helper = new ApiBBusHelper();
-		// company
-		$companyData = $helper->getCompany();
-
-		$config = [
-			'exchange_rate' => ExchangeRateUpdater::getExchangeRate(ExchangeRateUpdater::EUR, $conf->entity),
-			'invoice' => [
-				'header' => [
-					'name' => $companyData['company_data']['NOM'],
-					'tax_number' => $companyData['company_data']['TVAINTRA'],
-					'address' => [
-						'zip' => $companyData['company_data']['ZIP'],
-						'city' => $companyData['company_data']['TOWN'],
-						'address' => $companyData['company_data']['ADDRESS']
-					]
-				],
-				'customer' => $helper->getAppCustomers(),
-				'vat_percent' => 27,
-				'entity' => $user->entity,
-				'account_id' => $helper->getAccountRowid($user->entity),
-				'currencies' => $helper->getCurrenciesRowid($user->entity),
-				'payments_mode' => $helper->getPaymentsMode($user->entity)
-			]
-		];
-		ApiBbusLog::appLog("User: {$user->firstname} {$user->lastname} (ID: {$user->id})");
-		ApiBbusLog::appLog(json_encode([
-			'config' => $config
-		]));
-
-
-
-		return $config;
-	}
-
-	/**
-	 * Check user and eventdetail type permissions
-	 *
-	 * Return a result.
-	 *
-	 * @return array|mixed Data without useless information
-	 *
-	 * @url GET checkPermission
-	 */
-	public function checkPermission()
-	{
-		global $user, $db;
-		$permissionArray = [];
-		$groupUsersObj = new GroupUsers($db);
-		$result = $groupUsersObj->fetchAll('desc', 'rowid', 1, 0, ["customsql" => "fk_user = {$user->id}"]);
-		if ($result == -1 || empty($result)) {
-			return $permissionArray;
-		}
-		dol_include_once('/eventwizard/class/eventdetails.class.php');
-		$sqlBasicService = "SELECT pe.basic_service FROM llx_product as p
-			INNER JOIN llx_product_extrafields as pe ON pe.fk_object = p.rowid
-			GROUP BY pe.basic_service";
-		$resultBasicServices = $db->query($sqlBasicService);
-		if ($db->num_rows($resultBasicServices) > 0) {
-			while ($row = $db->fetch_object($resultBasicServices)) {
-				$basicServices[] = $row->basic_service;
-			}
-		}
-		$sqlBasicServicesTable = "SELECT basic_service_id, ref FROM llx_bbus_basicServices ORDER BY basic_service_id ASC";
-		$resultBasicServicesObj = $db->query($sqlBasicServicesTable);
-		if ($db->num_rows($resultBasicServicesObj) > 0) {
-			while ($bsrow = $db->fetch_object($resultBasicServicesObj)) {
-				foreach ($basicServices as $item) {
-					if ($bsrow->basic_service_id == $item && $item != 2) {
-						$permissionArray[$item] = $bsrow->ref;
-					}
-				}
-			}
-		}
-		return $permissionArray;
-	}
-
-	/**
-	 * Products list with children
-	 *
-	 * @param  string $sortfield  			Sort field
-	 * @param  string $sortorder  			Sort order
-	 * @param  int    $limit      			Limit for list
-	 * @param  int    $page       			Page number
-	 * @param  int    $mode       			Use this param to filter list (0 for all, 1 for only product, 2 for only service)
-	 * @param  int    $category   			Use this param to filter list by category
-	 * @param  string $sqlfilters 			Other criteria to filter answers separated by a comma. Syntax example "(t.tobuy:=:0) and (t.tosell:=:1)"
-	 * @param  bool   $ids_only   			Return only IDs of product instead of all properties (faster, above all if list is long)
-	 * @param  int    $variant_filter   		Use this param to filter list (0 = all, 1=products without variants, 2=parent of variants, 3=variants only)
-	 * @param  bool   $pagination_data   	If this parameter is set to true the response will include pagination data. Default value is false. Page starts from 0
-	 * @param  int    $includestockdata		Load also information about stock (slower)
-	 * @return array                			Array of product objects
-	 *
-	 * @url GET /products
-	 *
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 */
-	public function products($sortfield = 't.ref', $sortorder = 'ASC', $limit = 100, $page = 0, $mode = 0, $category = 0, $sqlfilters = '', $ids_only = false, $variant_filter = 0, $pagination_data = false, $includestockdata = 0): array
-	{
-		if (!DolibarrApiAccess::$user->rights->produit->lire) {
-			throw new RestException(403);
-		}
-		//$sqlfilters .= " AND ef.basic_service < '3'";
-		return (new ApiProductListHelper)->list($sortfield, $sortorder, $limit, $page, $mode, $category, $sqlfilters, $ids_only, $variant_filter, $pagination_data, $includestockdata);
-	}
-
-	/**
-	 * Create invoice
-	 * 
-	 * @param  array 	$invoice			Invoice data
-	 * @param  array 	$lines				Invoice lines
-	 * @param  array 	$payment			Invoice payment
-	 * @param  string 	$cardPaymentLog		Card payment log data
-	 * 
-	 * @return array|mixed Data without useless information
-	 * 
-	 * @url POST /invoice
-	 */
-	public function invoice(array $invoice, array $lines, array $payment, string $cardPaymentLog = '', string $sendId = '')
-	{
-		global $user;
-		$lockLabel = 'CREATE_INVOICE';
-		if (empty($sendId)) {
-			$sendId = substr(md5(rand()), 0, 8);
-		}
-		/**
-		 * LOG SECTION
-		 */
-		ApiBbusLog::appLog("{$sendId} === NEW INVOICE ===");
-		dol_syslog("{$sendId} === NEW INVOICE ===", LOG_INFO, 0);
-		ApiBbusLog::appLog("{$sendId} REQUEST: {$sendId}");
-		dol_syslog("{$sendId} REQUEST: {$sendId}", LOG_INFO, 0);
-		ApiBbusLog::appLog("{$sendId} User: {$user->firstname} {$user->lastname} (ID: {$user->id})");
-		dol_syslog("{$sendId} User: {$user->firstname} {$user->lastname} (ID: {$user->id})", LOG_INFO, 0);
-		ApiBbusLog::appLog("{$sendId} " . json_encode([
-			'invoice' => $invoice,
-			//'lines' => $lines,
-			'payment' => $payment,
-			'cardPaymentLog' => $cardPaymentLog
-		]));
-
-		/**
-		 * CHECK RIGHTS
-		 */
-		if (!DolibarrApiAccess::$user->rights->facture->creer) {
-			ApiBbusLog::appLog("{$sendId} Insufficient rights");
-			throw new RestException(401, 'Insufficient rights');
-		}
-
-
-		$apiInvoiceHelper = new ApiInvoiceHelper;
-		/**
-		 * handle invoice basic data
-		 */
-		$invoiceObj = $apiInvoiceHelper->createInvoice($invoice, $sendId);
-		/**
-		 * handle invoice line(s)
-		 */
-		$products = [];
-
-		foreach ($lines as $line) {
-			$invoiceObj = $apiInvoiceHelper->addLineToInvoice($invoiceObj, $line, $sendId);
-
-			$product = $apiInvoiceHelper->loadProductToResult($line);
-			if (!empty($product)) {
-				$products[] = $product;
-			}
-		}
-
-		$invoiceObj->fetch_lines();
-
-		foreach ($products as &$row) {
-			foreach ($invoiceObj->lines as $line) {
-				if ($line->product_ref == $row['ref']) {
-					//$row['price'] = $line->total_ttc;
-					$row['price'] = $line->multicurrency_total_ttc;
-					$row['total_tva'] = $line->total_tva;
-					//$row['total_tva'] = $line->multicurrency_total_tva;
-				}
-			}
-		}
-
-		/**
-		 * validate
-		 */
-		$invoiceObj = $apiInvoiceHelper->validateInvoice($invoiceObj, $sendId);
-		/**
-		 * set payment
-		 */
-		$apiInvoiceHelper->setPayment($invoiceObj, $payment, $sendId);
-		/**
-		 * save card payment data
-		 */
-		if (!empty($cardPaymentLog)) {
-			$invoiceObj = $apiInvoiceHelper->saveCardPaymentLog($invoiceObj, $cardPaymentLog, $sendId);
-		}
-
-		ApiBbusLog::appLog("{$sendId} Invoice created. ID: {$invoiceObj->id} REF: {$invoiceObj->ref} REQ: {$sendId}");
-		dol_syslog("{$sendId} Invoice created. ID: {$invoiceObj->id} REF: {$invoiceObj->ref} REQ: {$sendId}", LOG_INFO, 0);
-		//$bbApiLock->delete($user);
-
-		ApiBbusLog::appLog("{$sendId}####################################################################");
-		dol_syslog("{$sendId}####################################################################", LOG_INFO, 0);
-
-		return [
-			'sendId' => $sendId,
-			'invoice' => [
-				'id' => $invoiceObj->id,
-				'ref' => $invoiceObj->ref,
-				'total' => $invoiceObj->multicurrency_total_ttc
-			],
-			'products' => $products,
-		];
-	}
-
-	/**
-	 * Post invoice ref and creation date for data recording
-	 *
-	 * Return an array with success information.
-	 *
-	 * @param  string $ref	Facture ref
-	 * @param  string $datetime	invoice creation date
-	 * @param  string $type_id	type_id
-	 *
-	 * @return array|mixed Data without useless information
-	 *
-	 * @url POST printdate
-	 *
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 */
-	public function dateHandler(string $ref, string $datetime, string $type_id = null)
-	{
-		global $user;
-		$helper = new ApiBBusHelper();
-
-
-		if (!DolibarrApiAccess::$user->rights->produit->lire) {
-			ApiBbusLog::appLog("!DolibarrApiAccess::user->rights->produit->lire");
-			throw new RestException(403);
-		}
-		if (empty($ref)) {
-			ApiBbusLog::appLog("Empty ref!");
-			throw new RestException(401, 'Empty ref!');
-		}
-		if (empty($datetime)) {
-			ApiBbusLog::appLog("Empty datetime!");
-			throw new RestException(401, 'Empty datetime!');
-		}
-
-		ApiBbusLog::appLog("=== dateHandler ===");
-		ApiBbusLog::appLog("Facture: {$ref}");
-		
-		$printDate = DateTime::createFromFormat('Y-m-d H:i:s', $datetime);
-		ApiBbusLog::appLog("User: {$user->firstname} {$user->lastname} (ID: {$user->id})");
-		$now = new DateTime('now');
-		$now = (clone $now)->modify('+2 minutes');
-		$validDate = (clone $now)->modify('-5 minutes');
-		$datetime = $printDate->format('Y-m-d H:i:s');
-		$datetime_timestamp = strtotime($datetime);
-
-		if ($validDate <= $printDate && $printDate <= $now) {
-			$server_host = $this->getServerHost($type_id);
-			# Keresztvásárlás
-			if ($server_host == 'excelia') {
-				$this->localDateHandler($ref, $datetime, $datetime_timestamp, $now);
-			} else {
-				ApiBbusLog::appLog("dateHandler: cURL");
-				$this->curlDateHandler($ref, $datetime);
-			}
-		} else {
-			ApiBbusLog::appLog("dateHandler: Invalid date");
-			throw new RestException(401, 'Invalid date');
-		}
-		return 'OK';
-	}
-
-	private function localDateHandler($ref, $datetime, $datetime_timestamp, $now)
-	{
-		global $user;
-		$helper = new ApiBBusHelper();
-		$facture_id = $helper->getFactureIdForInvoicePrinting($ref);
-		if(empty($facture_id)){
-			ApiBbusLog::appLog("localDateHandler: facture_id because cross-shopping");
-			$printedCopies = $this->checkPrintedCopiesWithRef($ref);
-		}else{
-			$printedCopies = $this->checkPrintedCopies($facture_id);
-		}
-		// Leellenőrzöm, hogy a számla volt-e már nyomtatva
-		
-		ApiBbusLog::appLog("printedCopies: {$printedCopies}");
-
-
-		if ($printedCopies == 0) {
-			ApiBbusLog::appLog("printedCopies: no copies");
-			if(empty($facture_id)){
-				$bbTicketsByFacture = $this->getTicketsByInvoiceNumber($ref);
-			}else{
-				$bbTicketsByFacture = $this->getTicketsByFactureId($facture_id);
-			}
-			foreach ($bbTicketsByFacture as $ticket) {
-				// Rogzitem a bbticketinvoiceprinting tablaba a rekordot
-				$helper->setPrintingInvoiceObject($user, $facture_id, $datetime, $datetime_timestamp, $ticket, $ref);
-			}
-		} else {
-			if(empty($facture_id)){
-				$ticketIds = $helper->getTicketIdsForCrossShopping($ref);
-			}else{
-				$ticketIds = $helper->getTicketIdsByFactureId($facture_id);
-			}
-			$ticketObj = new stdClass();
-			foreach ($ticketIds as $key => $value) {
-				// Rogzitem a bbticketinvoiceprinting tablaba a rekordot
-				$ticketObj->id = $key;
-				$ticketObj->ticket_id = $value;
-				$helper->setPrintingInvoiceObject($user, $facture_id, $datetime, $datetime_timestamp, $ticketObj, $ref);
-			}
-			// e-mail küldése
-			$helper->sendMail($facture_id, $datetime, $now->format('Y-m-d H:i:s'));
-			return 'Multiprinting';
-		}
-	}
-
-	private function getTicketsByFactureId($facture_id)
-	{
-		$bbticket = new BbTicket($this->db);
-		$bbTicketsByFacture = $bbticket->fetchAll('', '', 0, 0, ['customsql' => 'fk_facture = ' . intval($facture_id)]);
-		if ($bbTicketsByFacture < 1) {
-			throw new RestException(404, 'BBTicket not found');
-		}
-		return $bbTicketsByFacture;
-	}
-
-	private function getTicketsByInvoiceNumber($ref)
-	{
-		$bbticket = new BbTicket($this->db);
-		$bbTicketsByFacture = $bbticket->fetchAll('', '', 0, 0, ["customsql" => "invoice_number =  '" . $ref . "'"]);
-		if ($bbTicketsByFacture < 1) {
-			ApiBbusLog::appLog("getTicketsByInvoiceNumber: BBTicket not found");
-			throw new RestException(404, 'BBTicket not found');
-		}
-		ApiBbusLog::appLog("getTicketsByInvoiceNumber: I got it");
-		return $bbTicketsByFacture;
-	}
-
-	private function checkPrintedCopies($facture_id)
-	{
-		$bbticketinvoiceprinting = new BbTicketInvoicePrinting($this->db);
-		$copies = $bbticketinvoiceprinting->fetchAll('ASC', 'rowid', 0, 0, ['customsql' => 'fk_facture = ' . intval($facture_id)]);
-		return (is_array($copies)) ? count($copies) : 0;
-	}
-
-	private function checkPrintedCopiesWithRef($ref)
-	{
-		$bbticketinvoiceprinting = new BbTicketInvoicePrinting($this->db);
-		$copies = $bbticketinvoiceprinting->fetchAll('ASC', 'rowid', 0, 0, ["customsql" => "invoice_number = '" . $ref . "'"]);
-		return (is_array($copies)) ? count($copies) : 0;
-	}
-
-	private function curlDateHandler($ref, $datetime)
-	{
-		$params = compact('ref', 'datetime');
-		$datehandlerPostFields = json_encode($params);
-		ApiBbusLog::appLog("dateHandler: {$datehandlerPostFields}");
-		$this->curlRunner('bbus/printdate', $datehandlerPostFields, 'POST', true);
-	}
-
-	/**
-	 * Generate events	 
-	 *
-	 * @return array|mixed Data without useless information
-	 *
-	 * @url GET /getDatetime
-	 *
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 */
-	public function getDatetime()
-	{
-		$dateArray = [];
-		$now = dol_now();
-		$dateArray = ["timestamp" => $now, "date" => date("Y-m-d", $now), "datetime" => date("Y-m-d H:i:s", $now)];
-		return $dateArray;
-	}
-
-	#-------------------------------------------------
-	#	Egyéb
-	#-------------------------------------------------
-
-	/**
-	 * First ticket validation
-	 * 
-	 * @param  string $code
-	 * 
-	 * @return array|mixed Data without useless information
-	 * 
-	 * @url POST search
-	 */
-
-	public function search($code)
-	{
-		$this->checkUserReadPermission();
-		global $user;
-		$this->code = $code;
-		$this->facture_id = $this->getFactureRowIdByFactureId();
-
-		$sql = "SELECT bt.*, pr.ref as product_ref, pr.label as product_label, btp.printing_date, btp.printing_date_timestamp
-		FROM llx_bbus_bbticketinvoiceprinting as btp
-		INNER JOIN llx_bbus_bbticket AS bt ON bt.rowid = btp.ticket_id
-		INNER JOIN llx_product AS pr ON pr.rowid = bt.ticket_id
-		WHERE btp.fk_facture = {$this->facture_id}
-		  AND btp.printing_date = (
-			SELECT MIN(printing_date)
-			FROM llx_bbus_bbticketinvoiceprinting AS btp2
-			WHERE btp2.fk_facture = {$this->facture_id}
-		)";
-		$ticketsData = $this->db->query($sql);
-		while ($row = pg_fetch_assoc($ticketsData)) {
-			$now = date("Y-m-d H:i:s");
-			if ($row['usage'] == $row['usable_occasions'] && $row['usable_occasions'] != 0) {
-				$this->isValid = false;
-			}
-			if ($row['available_at'] < $now || ((!is_null($row['expire_at']) && !empty($row['expire_at'])) && $row['expire_at'] < $now)) {
-				$this->isValid = false;
-			}
-
-			$row['facture_ref'] = $code;
-			$row['isValid'] = $this->isValid;
-			$sub = [];
-			foreach ($row as $key => $value) {
-				$sub[$key] = $value;
-			}
-			$json[] = $sub;
-		}
-		return $json;
-	}
-
-	/**
-	 * Értékesítések
-	 * 
-	 * @param  date $date	Date
-	 * 
-	 *
-	 * @return array|mixed Data without useless information
-	 *
-	 * @url POST getsaleditems
-	 */
-	public function getsaleditems($date = null)
-	{
-		global $user;
-
-		if (!DolibarrApiAccess::$user->rights->produit->lire) {
-			throw new RestException(403);
-		}
-		//$date = isset($date) ? $date : date("Y-m-d");
-		$date = date("Y-m-d");
-
-		$apiBbusHelper = new ApiBBusHelper();
-		$facturesArray = $apiBbusHelper->getSaledItems($date);
-
-		return $apiBbusHelper->getSaledItemsArray($facturesArray);
-	}
-
-	/** LOG OK
-	 * Get properties of a product object by barcode
-	 *
-	 * Return an array with product information.
-	 *
-	 * @param  string $code	Facture ID and timestamp of printing
-	 *
-	 * @return array|mixed Data without useless information
-	 *
-	 * @url POST ticketinfos
-	 */
-	public function getinfos($code)
-	{
-		global $user;
-		$helper = new ApiBBusHelper();
-		ApiBbusLog::getinfosLog('=== NEW TICKETINFOS ===');
-		ApiBbusLog::getinfosLog("User: {$user->firstname} {$user->lastname} (ID: {$user->id})");
-		ApiBbusLog::getinfosLog("DOLAPIKEY: {$user->api_key}");
-		$ticketsFromBbticketinvoiceprinting = [];
-		$code_and_timstamp = explode('_', $code);
-		$this->code = $code_and_timstamp[0];
-		if (empty($this->code)) {
-			ApiBbusLog::getinfosLog('Code or mac is empty!');
-			ApiBbusLog::getinfosLog('=================');
-			throw new RestException(404, 'Code or mac is empty!');
-		}
-		$this->printingTime = $code_and_timstamp[1];
-		if (empty($this->printingTime)) {
-			ApiBbusLog::getinfosLog('Timestamp is empty!');
-			ApiBbusLog::getinfosLog('=================');
-			throw new RestException(404, 'Timestamp is empty!');
-		}
-		if (!DolibarrApiAccess::$user->rights->bbus->ticket->validation) {
-			ApiBbusLog::getinfosLog('No access: Ticket Validation!');
-			ApiBbusLog::getinfosLog('=================');
-			throw new RestException(403);
-		}
-		ApiBbusLog::getinfosLog("Code: {$this->code}");
-		ApiBbusLog::getinfosLog("Code: {$this->printingTime}");
-
-		//Kikeresem a facture_id-t
-		$facture = new Facture($this->db);
-		$sql = "SELECT * FROM " . $this->db->prefix() . $facture->table_element . " WHERE ref ILIKE '{$this->code}'";
-		$factureResult = $this->db->query($sql);
-		if (pg_num_rows($factureResult) < 1) {
-			ApiBbusLog::getinfosLog('Invoice not found!');
-			ApiBbusLog::getinfosLog('=================');
-			throw new RestException(404, 'Invoice not found!');
-		}
-		while ($row = pg_fetch_assoc($factureResult)) {
-			$selectedFacture_id = $row['rowid'];
-		}
-
-		ApiBbusLog::getinfosLog('Facture ID: ' . (string) $selectedFacture_id);
-
-
-		$sql = "SELECT rowid FROM " . $this->db->prefix() . $facture->table_element . " WHERE fk_facture_source = {$selectedFacture_id} AND type = 2";
-		$res = $this->db->query($sql);
-		if (pg_num_rows($res) > 0) {
-			ApiBbusLog::getinfosLog('Invoice has a Credit account!');
-			ApiBbusLog::getinfosLog('=================');
-			throw new RestException(404, 'Invoice has a Credit account.');
-		}
-
-		// Kikeresem az llx_bbus_bbticketinvoiceprinting táblából a timestamp-hez tartozo jegy Id(ka)t
-		$bbticketinvoiceprinting = new BbTicketInvoicePrinting($this->db);
-		$result = $bbticketinvoiceprinting->fetchAll('', '', 0, 0, ['customsql' => "printing_date_timestamp = '{$this->printingTime}' AND fk_facture = {$selectedFacture_id}"]);
-		$helper->checkResult($result, 'bbticketinvoiceprinting');
-		foreach ($result as $device) {
-			$ticketsFromBbticketinvoiceprinting[] = $device->ticket_id;
-		}
-		$inString = implode(', ', $ticketsFromBbticketinvoiceprinting);
-		//echo $inString;exit;
-		// A ticket_id alapjan, hogy a jegyek kozott szerepel-e es ervenyes-e
-		$bbticket = new BbTicket($this->db);
-		$sql = "SELECT tic.rowid as ticketrowid, tic.*, bbd.*, fac.* FROM " . $this->db->prefix() . $bbticket->table_element . " AS tic
-		INNER JOIN public.llx_product as bbd ON tic.ticket_id = bbd.rowid
-		INNER JOIN public.llx_facture as fac ON tic.fk_facture = fac.rowid
-		WHERE tic.rowid IN ({$inString})";
-		$statement = $this->db->query($sql);
-		while ($row = pg_fetch_assoc($statement)) {
-			$sub = [];
-			foreach ($row as $key => $value) {
-				$sub[$key] = $value;
-			}
-			$json[] = $sub;
-		}
-		ApiBbusLog::getinfosLog('Status: OK!');
-		ApiBbusLog::getinfosLog('=====================');
-		return $json;
-	}
-
-	/**
-	 * Get properties of a product object by barcode
-	 *
-	 * Return an array with product information.
-	 *
-	 *
-	 * @return array|mixed Data without useless information
-	 *
-	 * @url GET product
-	 */
-	public function getProducts()
-	{
-		global $user;
-		$productsArray = [];
-		$products = new Product($this->db);
-		$sql = "SELECT price, default_vat_code, rowid FROM " . $this->db->prefix() . $products->table_element . " WHERE fk_product_type = 1 ORDER BY rowid ASC";
-		$res = $this->db->query($sql);
-		while ($row = pg_fetch_assoc($res)) {
-			$productsArray[$row['rowid']] = $row;
-		}
-		return 'OK';
-	}
-
-	/** LOG OK
-	 * Validate a product bbticket id.
-	 *
-	 * Return an array with product validation data.
-	 *
-	 * @param  string $code	Facture ID and timestamp of printing
-	 * @param  string $ticketid	Ticketid (bbticket rowid)
-	 * @param  string $imei	IMEI (Mobile IMEI number)
-	 *
-	 * @return array|mixed Data without useless information
-	 *
-	 * @url POST ticketvalidationbyphone
-	 */
-	public function validateTicket($code, $ticketid, $imei)
-	{
-		global $user;
-		$logId = ApiBbusLog::getLogId();
-		ApiBbusLog::ticketvalidationByPhone("{$logId} === NEW TICKETVALIDATION BY PHONE ===");
-		ApiBbusLog::ticketvalidationByPhone("{$logId} User: {$user->firstname} {$user->lastname} (ID: {$user->id})");
-		ApiBbusLog::ticketvalidationByPhone("{$logId} DOLAPIKEY: {$user->api_key}");
-		ApiBbusLog::ticketvalidationByPhone("{$logId} code: {$code}");
-		$ticketChecker = new TicketChecker();
-
-
-		$ticketChecker->setCode($code);
-		$ticketChecker->setTimestamp($code, 'ticketvalidationByPhone', $logId);
-		$ticketChecker->SetTicketidFromPhone($ticketid, $logId);
-		$ticketChecker->setImei($imei, $logId);
-
-		$this->checkUserValidatePermission();
-
-		$ticketChecker->setFactureId('ticketvalidationByPhone', $logId);
-		$ticketChecker->checkBbTicketInvoicePrinting();
-		$selectedTicket = $ticketChecker->getDataOfTheSelectedTicket();
-
-		$ticketChecker->setBbTicketRowId($selectedTicket->id);
-
-		$ticketChecker->checkExceptionHandlers($selectedTicket, 'ticketvalidationByPhone', $logId);
-
-		$ticketChecker->setMergedTickets($selectedTicket, 'ticketvalidationByPhone', $logId);
-
-		$ticketChecker->saveData($selectedTicket);
-		ApiBbusLog::ticketvalidationByPhone("{$logId} Status: OK!");
-		ApiBbusLog::ticketvalidationByPhone("{$logId} =================");
-		return 'OK';
-	}
-
-	/** LOG OK
-	 * Get properties of a product object by barcode
-	 *
-	 * Return an array with product information.
-	 *
-	 * @param  string $code	Facture ID and timestamp of printing
-	 * @param  string $mac	MAC adress of the device
-	 *
-	 * @return array|mixed Data without useless information
-	 *
-	 * @url POST barcode
-	 *
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 */
-
-	public function getByBarcode(string $code, string $mac, string $lat = null, string $lon = null)
-	{
-		global $user;
-		$logId = ApiBbusLog::getLogId();
-		ApiBbusLog::getByBarcode("{$logId} === NEW OBU validation ===");
-		ApiBbusLog::getByBarcode("{$logId} User: {$user->firstname} {$user->lastname} (ID: {$user->id})");
-		ApiBbusLog::getByBarcode("{$logId} DOLAPIKEY: {$user->api_key}");
-		$bbticket = new BbTicket($this->db);
-		$ticketChecker = new TicketChecker();
-
-		if (empty($code) || empty($mac)) {
-			ApiBbusLog::getByBarcode("{$logId} Code or mac is empty!");
-			throw new RestException(404, 'Code or mac is empty!');
-		}
-
-		ApiBbusLog::getByBarcode("{$logId} Code: " . $code);
-		ApiBbusLog::getByBarcode("{$logId} MAC: " . $mac);
-		ApiBbusLog::getByBarcode("{$logId} Latitude: " . $lat);
-		ApiBbusLog::getByBarcode("{$logId} Longitude: " . $lon);
-		$ticketChecker->setMac($mac);
-		$ticketChecker->setCode($code);
-		$ticketChecker->setLat($lat);
-		$ticketChecker->setLon($lon);
-		$ticketChecker->setTimestamp($code, 'getByBarcode', $logId);
-
-		$this->checkUserReadPermission();
-
-		$ticketChecker->setFactureId('getByBarcode', $logId);
-		$ticketChecker->setFilsArray();
-
-		$ticketChecker->setTicketId($logId);
-		$selectedTicket = $ticketChecker->getDataOfTheSelectedTicket();
-
-		$ticketChecker->setBbTicketRowId($selectedTicket->id);
-		$ticketChecker->check5Minutes($logId);
-		$ticketChecker->checkExceptionHandlers($selectedTicket, 'getByBarcode', $logId);
-
-		$ticketChecker->setMergedTickets($selectedTicket, 'getByBarcode', $logId);
-		$ticketChecker->saveCoordinates($logId);
-		$ticketChecker->saveData($selectedTicket);
-
-		ApiBbusLog::getByBarcode("{$logId} Status: OK!");
-		ApiBbusLog::getByBarcode("{$logId} =================");
-		return 'OK';
-	}
-
-	/**
-	 * Save customer data (zip or countrycode) in facture table
-	 *
-	 * Return a result.
-	 *
-	 * @param  string $facture_id	Facture rowid
-	 * @param  string $customerdatazip	Customer data ZIP
-	 * @param  string $customerdatacountrycode	Customer data Countrycode
-	 *
-	 * @return array|mixed Data without useless information
-	 *
-	 * @url POST customerDataHandler
-	 *
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 */
-	public function customerDataHandler(string $facture_id, string $customerdatazip = null, string $customerdatacountrycode = null)
-	{
-		$helper = new ApiBBusHelper();
-		if (is_null($customerdatazip) && is_null($customerdatacountrycode)) {
-			dol_syslog("Nem sikerult a facture updateje. Ref: " . $this->facture_id, LOG_DEBUG | LOG_INFO | LOG_WARNING | LOG_ERR);
-			throw new RestException(404, 'Empty params');
-		}
-		$sql = "UPDATE " . $this->db->prefix() . "facture_extrafields SET ";
-		$sql .= !is_null($customerdatazip) ? "customer_data_zip = '{$customerdatazip}' " : "customer_data_countrycode = '{$customerdatacountrycode}' ";
-		$sql .= "WHERE fk_object = '{$facture_id}'";
-		$helper->factureUpdate($sql, $facture_id);
-		return "OK";
-	}
-
-	/**
-	 * Create naplo for userlogout
-	 *
-	 * Return a result.
-	 *
-	 * @return array|mixed Data without useless information
-	 *
-	 * @url POST logoutNaploCreator
-	 *
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 */
-	public function logoutNaploCreator()
-	{
-		global $user, $db;
-		$error = 0;
-		$db->begin();
-		$helper = new ApiBBusHelper();
-
-		$group_user_id = $helper->getGroupUserIdByUserId($user->id);
-		if ($group_user_id == -1) {
-			$error++;
-		}
-		$result = $helper->isLastStatusLogout($user);
-		if ($result !== '') {
-			return date('Y-m-d H:i:s', $result);
-		}
-
-		$userLoginNaplo = new UserLoginNaplo($db);
-		$userLoginNaplo->login_logout_status = 1;
-		$userLoginNaplo->user_id = $user->id;
-		$result = $userLoginNaplo->create($user);
-		if ($result < 0) {
-			$error++;
-		}
-
-		$userNaploObj = new UserNaplo($db);
-		$userNaploObj->user_id = $user->id;
-		$userNaploObj->group_user_id = $group_user_id;
-		$userNaploObj->status = 0;
-		$result = $userNaploObj->create($user);
-		if ($result < 0) {
-			$error++;
-		}
-
-		$groupUsersObj = new GroupUsers($db);
-		$sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . "settlements_groupusers WHERE fk_user = {$user->id}";
-		$data = $db->query($sql);
-		$dataArray = pg_fetch_assoc($data);
-		$selectedGroupId = $dataArray['rowid'];
-		if (isset($selectedGroupId)) {
-			$resultKickOff = $groupUsersObj->deleteLine($user, $selectedGroupId);
-			if (!$resultKickOff) {
-				$error++;
-			}
-		}
-
-		if ($error) {
-			$db->rollback();
-		}
-		$db->commit();
-		return 'OK';
-	}
-
-	/**
-	 * Get the discount unit From Product
-	 *
-	 * Return a result.
-	 *
-	 * @param  int $product_id			Product rowid
-	 * 
-	 * @return string discount unit
-	 *
-	 * @url POST getDiscountUnit
-	 *
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 */
-	public function getDiscountUnit(int $product_id)
-	{
-		global $db, $user;
-		$ProductObj = new Product($db);
-		$ProductObj->fetch($product_id);
-		return $ProductObj->array_options['discount_period'];
-	}
-
-	/**
-	 * Create naplo for Discount
-	 *
-	 * Return a result.
-	 *
-	 * @param  int $facture_id			Facture rowid
-	 * @param  int $product_id			Product rowid
-	 * @param  int $discount			Discount
-	 * @param  string $discount_unit	Discount unit
-	 * 
-	 * @return array|mixed Data without useless information
-	 *
-	 * @url POST createDiscountNaplo
-	 *
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 */
-	public function createDiscountNaplo(int $facture_id, int $product_id, int $discount, string $discount_unit)
-	{
-		global $db, $user;
-		$discountNaploObj = new DiscountNaplo($db);
-		$discountNaploObj->fk_facture = $facture_id;
-		$discountNaploObj->fk_product = $product_id;
-		$discountNaploObj->discount = $discount;
-		$discountNaploObj->discount_unit = $discount_unit;
-		$result = $discountNaploObj->create($user);
-		if ($result < 0) {
-			return false;
-		}
-		return $result;
-	}
-
-	/**
-	 * Get the products from Product
-	 *
-	 * Return a result.
-	 *
-	 * @param  int $entity	entity
-	 * 
-	 * @return array|mixed Data without useless information
-	 *
-	 * @url POST getHotelSalesProducts
-	 *
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 */
-	public function getHotelSalesProducts(int $entity)
-	{
-		global $db, $user;
-		$sql = "SELECT p.* FROM public.llx_product AS p 
-		INNER JOIN public.llx_product_extrafields as pe ON pe.fk_object = p.rowid
-		WHERE p.entity = {$entity} AND pe.hotelsales = 1";
-		$products = $this->db->query($sql);
-		$row = pg_fetch_all($products);
-		return $row;
-	}
-
-	private function checkUserReadPermission()
-	{
-		if (!DolibarrApiAccess::$user->rights->produit->lire) {
-			throw new RestException(403);
-		}
-	}
-
-	private function checkUserValidatePermission()
-	{
-		if (!DolibarrApiAccess::$user->rights->bbus->ticket->validation) {
-			throw new RestException(403);
-		}
-	}
-
-	private function getFactureRowIdByFactureId()
-	{
-		$facture = new Facture($this->db);
-		$sqlFacture = "SELECT rowid FROM public.llx_facture WHERE ref ILIKE '%{$this->code}%'";
-		$result = $this->db->query($sqlFacture);
-		if (pg_num_rows($result) > 0) {
-			while ($adatok = pg_fetch_assoc($result)) {
-				$factureRowid = $adatok['rowid'];
-			}
-		} else {
-			throw new RestException(404, 'Invoice not found.');
-		}
-		$sql = "SELECT rowid FROM llx_facture WHERE fk_facture_source = {$factureRowid} AND type = 2";
-		$res = $this->db->query($sql);
-		if (pg_num_rows($res) > 0) {
-			throw new RestException(404, 'Invoice has a Credit account.');
-		}
-
-		return $factureRowid;
-	}
-
-	private function saveBbTicketNaplo($user)
-	{
-		$bbTicketNaplo = new BbTicketNaplo($this->db);
-		$bbTicketNaplo->ticket_row_id = $this->bbTicketRowId;
-		$bbTicketNaplo->bbservicelocation_id = isset($this->service_location_id) ? $this->service_location_id : null;
-		$bbTicketNaplo->device_id = isset($this->device_id) ? $this->device_id : null;
-		$bbTicketNaplo->status = $this->status;
-
-
-		if ($bbTicketNaplo->create($user) < 0) {
-			dol_syslog('Nem sikerult menteni a bbticketNaplo tablaba a rekordot.', LOG_DEBUG | LOG_INFO | LOG_WARNING | LOG_ERR);
-			throw new RestException(500, 'Nem sikerult menteni a bbticketNaplo tablaba a rekordot.');
-		}
-	}
-
-	/**
-	 * @param  string $ref	Facture ref
-	 *
-	 * @return array|mixed Data without useless information
-	 *
-	 * @url POST getTicketsByFactureInvoiceNumber
-	 *
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 */
-	public function getTicketsByFactureInvoiceNumber($ref)
-	{
-		$bbTicketsByFacture = [];
-		$sql = "SELECT rowid as id, ticket_id FROM llx_bbus_bbticket WHERE invoice_number ILIKE '%{$ref}%'";
-		$result = $this->db->query($sql);
-		if ($this->db->num_rows($result) > 0) {
-			while ($row = $this->db->fetch_object($result)) {
-				$bbTicketsByFacture[] = $row;
-			}
-		} else {
-			throw new RestException(404, 'BBTicket not found');
-		}
-		return $bbTicketsByFacture;
-	}
-
-	/**
-	 * @param  string $facture_id	Facture ref
-	 *
-	 * @return array|mixed Data without useless information
-	 *
-	 * @url POST getRefFromFacture
-	 *
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 */
-	public function getRefFromFacture(string $facture_id)
-	{
-		global $user;
-		$facture = new Facture($this->db);
-		$sql = "SELECT ref FROM " . $this->db->prefix() . $facture->table_element . " WHERE rowid = " . $facture_id;
-		$res = $this->db->query($sql);
-		while ($adatok = pg_fetch_assoc($res)) {
-			print_r($adatok['ref']);
-			exit;
-		}
-
-		return 'OK';
-	}
-
-	/**
-	 * Get properties of a product object by barcode
-	 *
-	 * Return an array with product information.
-	 *
-	 * @param  string $barcode            Barcode of element
-	 * @param  int    $includestockdata   Load also information about stock (slower)
-	 * @param  bool   $includesubproducts Load information about subproducts
-	 * @param  bool   $includeparentid    Load also ID of parent product (if product is a variant of a parent product)
-	 * @param  bool   $includetrans		  Load also the translations of product label and description
-	 *
-	 * @return array|mixed Data without useless information
-	 *
-	 * @url GET profileimage/{id}
-	 *
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 */
-	public function getUserImage(int $id)
-	{
-		$image = null;
-		$dolApiKey = $_SERVER['HTTP_DOLAPIKEY'] ?? null;
-
-		if (empty($dolApiKey)) {
-			throw new RestException(401, 'Access not allowed');
-		}
-
-		$user = (new ApiUserHandler)->getUser($id, $dolApiKey);
-		if (empty($user->photo)) {
-			throw new RestException(404, 'Photo not found');
-		} else {
-			$originalFile = get_exdir(0, 0, 0, 0, $user, 'user') . 'photos/' . $user->photo;
-			$originalFile = DOL_DOCUMENT_ROOT . '/documents/users/' . $originalFile;
-
-			$filename = basename($originalFile);
-			$originalFileOsEncoded = dol_osencode($originalFile); // New file name encoded in OS encoding charset
-
-			$fileContent = file_get_contents($originalFileOsEncoded);
-			$image = [
-				'filename' => $filename,
-				'content-type' => dol_mimetype($filename),
-				'filesize' => filesize($originalFile),
-				'content' => base64_encode($fileContent),
-				'encoding' => 'base64'
-			];
-		}
-
-		return $image;
-	}
-
-	/**
-	 * Create invoices from the order
-	 *
-	 * @param int $id_order   		(Row)Id of the order
-	 * @param int $id_soc  			Id of 3th party
-	 * @return array                Response							  
-
-	 * @url POST invoicesFromOrder
-
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 */
-	public function invoicesFromOrder(int $id_order, int $id_soc = 0): array
-	{
-		// echo ini_get('memory_limit');
-		// exit;
-		if (!DolibarrApiAccess::$user->rights->facture->creer) {
-			throw new RestException(401, "Insuffisant rights");
-		}
-
-		require_once DOL_DOCUMENT_ROOT . '/compta/facture/class/facture.class.php';
-		// Standard or deposit invoice, not from a Predefined template invoice
-		// Si facture standard
-		$this->now = time();
-		$this->invoices = [];
-
-		$element == 'order';
-		$element = $subelement = 'commande';
-
-		//etalon obj:
-
-		$object = new Facture($this->db);
-		// $extrafields = new ExtraFields($db);
-		$defaultDevSocId = 1;
-		$this->defaultSocId = (isset($_ENV['DEFAULT_SOCID']) && (int) $_ENV['DEFAULT_SOCID']) ? (int) $_ENV['DEFAULT_SOCID'] : $defaultDevSocId;
-
-		$object->socid = (empty((int) $id_soc)) ? $this->defaultSocId : (int) $id_soc;
-		$object->type = null;
-		$object->ref = null;
-		$object->date = $this->now;
-		// $object->date_pointoftax 	= $date_pointoftax;
-		// $object->note_public		= trim(GETPOST('note_public', 'restricthtml'));
-		// $object->note_private = trim(GETPOST('note_private', 'restricthtml'));
-		// $object->ref_client			= GETPOST('ref_client');
-		$object->model_pdf = null;
-		// $object->fk_project			= GETPOST('projectid', 'int');
-		// $object->cond_reglement_id	= (GETPOST('type') == 3 ? 1 : GETPOST('cond_reglement_id'));
-		// $object->mode_reglement_id	= GETPOST('mode_reglement_id');
-		// $object->fk_account = GETPOST('fk_account', 'int');
-		// $object->amount = price2num(GETPOST('amount'));
-		// $object->remise_absolue		= price2num(GETPOST('remise_absolue'), 'MU');
-		// $object->remise_percent		= price2num(GETPOST('remise_percent'), '', 2);
-		// $object->fk_incoterms = GETPOST('incoterm_id', 'int');
-		// $object->location_incoterms = GETPOST('location_incoterms', 'alpha');
-		// $object->multicurrency_code = GETPOST('multicurrency_code', 'alpha');
-		// $object->multicurrency_tx   = GETPOST('originmulticurrency_tx', 'int');
-
-		$object->fetch_thirdparty();
-
-		// If creation from another object of another module (Example: origin=propal, originid=1)
-
-		$object->origin = $element;
-		$object->origin_id = $id_order;
-
-		// Possibility to add external linked objects with hooks
-		$object->linked_objects[$object->origin] = $object->origin_id;
-		// link with order if it is a shipping invoice
-
-		if (is_array($_POST['other_linked_objects']) && !empty($_POST['other_linked_objects'])) {
-			$object->linked_objects = array_merge($object->linked_objects, $_POST['other_linked_objects']);
-		}
-
-		//
-		dol_include_once('/' . $element . '/class/' . $subelement . '.class.php');
-		$classname = ucfirst($subelement);
-		$srcobject = new $classname($this->db);
-
-		$result = $srcobject->fetch($id_order);
-		if ($result > 0) {
-			$lines = $srcobject->lines;
-			if (empty($lines) && method_exists($srcobject, 'fetch_lines')) {
-				$srcobject->fetch_lines();
-				$lines = $srcobject->lines;
-			}
-			/*
-																																																													  // If we create a standard invoice with a percent, we change amount by changing the qty
-																																																													  if (is_array($lines)) {
-																																																													  foreach ($lines as $line) {
-																																																													  // We keep ->subprice and ->pa_ht, but we change the qty
-																																																													  $line->qty = price2num($line->qty * $valuestandardinvoice / 100, 'MS');
-																																																													  }
-																																																													  }
-																																																													  */
-			$fk_parent_line = 0;
-			$num = count($lines);
-			$invNum = 0;
-
-			for ($i = 0; $i < $num; $i++) {
-				// if (!in_array($lines[$i]->id, $selectedLines)) {
-				// continue; // Skip unselected lines
-				// }
-
-				//!!! create invoice by line:
-				$quantity = $lines[$i]->qty;
-				for ($j = 0; $j < $quantity; $j++) {
-					$this->invoices[$invNum] = clone $object;
-					if ($id = $this->invoices[$invNum]->create(DolibarrApiAccess::$user)) {
-						$this->invoices[$invNum]->update_price(1, 'auto', 0, $mysoc);
-
-						$label = (!empty($lines[$i]->label) ? $lines[$i]->label : '');
-						$desc = (!empty($lines[$i]->desc) ? $lines[$i]->desc : $lines[$i]->libelle);
-						if ($this->invoices[$invNum]->situation_counter == 1) {
-							$lines[$i]->situation_percent = 0;
-						}
-
-						if ($lines[$i]->subprice < 0 && empty($conf->global->INVOICE_KEEP_DISCOUNT_LINES_AS_IN_ORIGIN)) {
-							// Negative line, we create a discount line
-							require_once DOL_DOCUMENT_ROOT . '/core/class/discount.class.php';
-							$discount = new DiscountAbsolute($db);
-							$discount->fk_soc = $this->invoices[$invNum]->socid;
-							$discount->amount_ht = abs($lines[$i]->total_ht);
-							$discount->amount_tva = abs($lines[$i]->total_tva);
-							$discount->amount_ttc = abs($lines[$i]->total_ttc);
-							$discount->tva_tx = $lines[$i]->tva_tx;
-							$discount->fk_user = DolibarrApiAccess::$user->id;
-							$discount->description = $desc;
-							$discount->multicurrency_subprice = abs($lines[$i]->multicurrency_subprice);
-							$discount->multicurrency_amount_ht = abs($lines[$i]->multicurrency_total_ht);
-							$discount->multicurrency_amount_tva = abs($lines[$i]->multicurrency_total_tva);
-							$discount->multicurrency_amount_ttc = abs($lines[$i]->multicurrency_total_ttc);
-
-							$discountid = $discount->create(DolibarrApiAccess::$user);
-							if ($discountid > 0) {
-								$result = $this->invoices[$invNum]->insert_discount($discountid); // This include link_to_invoice
-							} else {
-								setEventMessages($discount->error, $discount->errors, 'errors');
-								$error++;
-								break;
-							}
-						} else {
-							// Positive line
-							$product_type = ($lines[$i]->product_type ? $lines[$i]->product_type : 0);
-
-							// Date start
-							$date_start = false;
-							if ($lines[$i]->date_debut_prevue) {
-								$date_start = $lines[$i]->date_debut_prevue;
-							}
-							if ($lines[$i]->date_debut_reel) {
-								$date_start = $lines[$i]->date_debut_reel;
-							}
-							if ($lines[$i]->date_start) {
-								$date_start = $lines[$i]->date_start;
-							}
-
-							// Date end
-							$date_end = false;
-							if ($lines[$i]->date_fin_prevue) {
-								$date_end = $lines[$i]->date_fin_prevue;
-							}
-							if ($lines[$i]->date_fin_reel) {
-								$date_end = $lines[$i]->date_fin_reel;
-							}
-							if ($lines[$i]->date_end) {
-								$date_end = $lines[$i]->date_end;
-							}
-
-							// Reset fk_parent_line for no child products and special product
-							if (($lines[$i]->product_type != 9 && empty($lines[$i]->fk_parent_line)) || $lines[$i]->product_type == 9) {
-								$fk_parent_line = 0;
-							}
-
-							// Extrafields
-							if (method_exists($lines[$i], 'fetch_optionals')) {
-								$lines[$i]->fetch_optionals();
-								$array_options = $lines[$i]->array_options;
-							}
-
-							$tva_tx = $lines[$i]->tva_tx;
-							if (!empty($lines[$i]->vat_src_code) && !preg_match('/\(/', $tva_tx)) {
-								$tva_tx .= ' (' . $lines[$i]->vat_src_code . ')';
-							}
-
-							// View third's localtaxes for NOW and do not use value from origin.
-							// TODO Is this really what we want ? Yes if source is template invoice but what if proposal or order ?
-							$localtax1_tx = get_localtax($tva_tx, 1, $this->invoices[$invNum]->thirdparty);
-							$localtax2_tx = get_localtax($tva_tx, 2, $this->invoices[$invNum]->thirdparty);
-
-							$result = $this->invoices[$invNum]->addline(
-								$desc,
-								$lines[$i]->subprice,
-								// $lines[$i]->qty,
-								1,
-								$tva_tx,
-								$localtax1_tx,
-								$localtax2_tx,
-								$lines[$i]->fk_product,
-								$lines[$i]->remise_percent,
-								$date_start,
-								$date_end,
-								0,
-								$lines[$i]->info_bits,
-								$lines[$i]->fk_remise_except,
-								'HT',
-								0,
-								$product_type,
-								$lines[$i]->rang,
-								$lines[$i]->special_code,
-								$this->invoices[$invNum]->origin,
-								$lines[$i]->rowid,
-								$fk_parent_line,
-								$lines[$i]->fk_fournprice,
-								$lines[$i]->pa_ht,
-								$label,
-								$array_options,
-								$lines[$i]->situation_percent,
-								$lines[$i]->fk_prev_id,
-								$lines[$i]->fk_unit,
-								0,
-								'',
-								1
-							);
-
-							if ($result > 0) {
-								$lineid = $result;
-							} else {
-								$lineid = 0;
-								$error++;
-								break;
-							}
-
-							// Defined the new fk_parent_line
-							if ($result > 0 && $lines[$i]->product_type == 9) {
-								$fk_parent_line = $result;
-							}
-						}
-					}
-
-					$invNum++;
-				}
-			}
-		} else {
-			setEventMessages($srcobject->error, $srcobject->errors, 'errors');
-			$error++;
-		}
-		$num = count($this->invoices);
-		$return = [
-			'validated' => []
-		];
-		// $used_mem = round(memory_get_usage(false) / 1024 / 1024); //mb
-		// $used_mem = round(memory_get_usage(true) / 1024 / 1024); //mb
-		// echo $used_mem. " / ".$allowed_mem; 
-		// echo ini_get('memory_limit');
-		// exit;
-
-		for ($i = 0; $i < $num; $i++) {
-
-			//!!! validation
-			// $idwarehouse = 0;
-			// $notrigger = 0;
-			// $result = $this->invoices[$i]->validate(DolibarrApiAccess::$user, '', $idwarehouse, $notrigger);
-			$id = $this->invoices[$i]->id;
-			$result = $this->invoices[$i]->validate(DolibarrApiAccess::$user, '', null);
-			if ($result >= 0) {
-				// Define output language
-				if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE)) {
-					global $langs;
-					$outputlangs = $langs;
-					$newlang = '';
-					if (!empty($conf->global->MAIN_MULTILANGS) && empty($newlang) && GETPOST('lang_id', 'aZ09')) {
-						$newlang = GETPOST('lang_id', 'aZ09');
-					}
-					if (!empty($conf->global->MAIN_MULTILANGS) && empty($newlang)) {
-						$newlang = $this->invoices[$i]->thirdparty->default_lang;
-					}
-					if (!empty($newlang)) {
-						$outputlangs = new Translate("", $conf);
-						$outputlangs->setDefaultLang($newlang);
-						$outputlangs->load('products');
-					}
-					$model = $this->invoices[$i]->model_pdf;
-
-					$ret = $this->invoices[$i]->fetch($id); // Reload to get new records
-					// PDF
-					$hidedetails = (!empty($conf->global->MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS)) ? 1 : 0;
-					$hidedesc = (!empty($conf->global->MAIN_GENERATE_DOCUMENTS_HIDE_DESC)) ? 1 : 0;
-					$hideref = (!empty($conf->global->MAIN_GENERATE_DOCUMENTS_HIDE_REF)) ? 1 : 0;
-					$result = $this->invoices[$i]->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
-					if ($result < 0) {
-						//...
-						// setEventMessages($this->invoices[$i]->error, $this->invoices[$i]->errors, 'errors');
-					}
-				}
-			} else {
-				// if (count($this->invoices[$i]->errors)) {
-				// setEventMessages(null, $this->invoices[$i]->errors, 'errors');
-				// } else {
-				// setEventMessages($this->invoices[$i]->error, $this->invoices[$i]->errors, 'errors');
-				// }
-			}
-
-			// if ($result == 0) {
-			// throw new RestException(304, 'Error nothing done. May be object is already validated');
-			// }
-			// if ($result < 0) {
-			// throw new RestException(500, 'Error when validating Invoice: '.$this->invoice->error);
-			// }
-
-			// $this->invoices[$i] = $this->invoicees[$i]->fetch($id);
-			if (!$this->invoices[$i]) {
-				throw new RestException(404, 'Invoice not found');
-			}
-
-			$this->invoices[$i] = parent::_cleanObjectDatas($this->invoices[$i]);
-			unset($this->invoices[$i]->note);
-			unset($this->invoices[$i]->address);
-			unset($this->invoices[$i]->barcode_type);
-			unset($this->invoices[$i]->barcode_type_code);
-			unset($this->invoices[$i]->barcode_type_label);
-			unset($this->invoices[$i]->barcode_type_coder);
-			unset($this->invoices[$i]->canvas);
-
-
-			if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
-				throw new RestException(401, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
-			}
-
-			$return['validated'][] = $this->invoices[$i]->ref;
-		}
-		return $return;
-	}
-
-	/**
-	 * Jegy generálás -> (order?->) invoice -> payment -> jegy -> qr on the bill
-	 *
-	 * Return an array with prcess information.
-	 *
-	 * @param  array $orderDetails			Order details
-	 *
-	 * @return array|mixed Data without useless information
-	 *
-	 * @url POST order
-	 *
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 */
-	public function jegy(array $orderDetails)
-	{
-		$return = [
-			"request" => $orderDetails
-		];
-		$Jegy = new Jegy();
-		$return['hash'] = [
-			$Jegy->generate_hash(),
-			$Jegy->generate_hash(1),
-			$Jegy->generate_hash(20),
-			$Jegy->generate_hash(50),
-			$Jegy->generate_hash(100),
-		];
-
-		return $return;
-	}
-
-	/**
-	 * Get groupId for REACT
-	 *
-	 * Return an ID.
-	 *
-	 * @return integer Data without useless information
-	 *
-	 * @url GET getGroupId
-	 *
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 */
-	public function getGroupId(): array
-	{
-		global $user, $db;
-		$sql = "SELECT fk_settlements_group FROM public.llx_settlements_groupusers WHERE fk_user = {$user->id} ORDER BY rowid DESC LIMIT 1";
-		$result = $db->query($sql);
-		while ($row = pg_fetch_assoc($result)) {
-			return $row;
-		}
-		return [];
-	}
-
-	/**
-	 * Insert a row into login log table
-	 *
-	 * @param  string $login		Login name
-	 *
-	 * @return array|mixed Data without useless information
-	 *
-	 * @url POST savelogin
-	 *
-	 * @throws RestException 404
-	 */
-	public function saveLogin(string $login)
-	{
-		$error = 0;
-		$this->db->begin();
-		$user = new User($this->db);
-		//$user->fetch(0, $login, 0, 0, 1);
-		$user->fetch(0, $login, 0, 0);
-		if (empty($user->id)) {
-			throw new RestException(404, 'User not found');
-		}
-
-		$log = new BbLoginLog($this->db);
-		$log->entity = 1;
-		$log->description = 'Login from App';
-		$id = $log->create($user);
-		if ($id < 1) {
-			$error++;
-			throw new RestException(500, 'Log failure');
-		}
-
-		$now = dol_now();
-
-		$userremoteip = getUserRemoteIP();
-
-		$sql = "UPDATE " . $this->db->prefix() . "user SET";
-		$sql .= " datepreviouslogin = datelastlogin,";
-		$sql .= " ippreviouslogin = iplastlogin,";
-		$sql .= " datelastlogin = '" . $this->db->idate($now) . "',";
-		$sql .= " iplastlogin = '" . $this->db->escape($userremoteip) . "',";
-		$sql .= " tms = tms"; // La date de derniere modif doit changer sauf pour la mise a jour de date de derniere connexion
-		$sql .= " WHERE rowid = " . ((int) $user->id);
-		$resql = $this->db->query($sql);
-		if (!$resql) {
-			$error++;
-			$this->error = $this->db->lasterror() . ' sql=' . $sql;
-		}
-		if (!$error) {
-			$this->db->commit();
-			return 'OK';
-		} else {
-			$this->db->rollback();
-		}
-	}
-
-	/**
-	 * Receive GPS coordinates of the device
-	 *
-	 * @param string  $licplate	License plate of the vehicle
-	 * @param string  $lat	GPS latitude coordinate
-	 * @param string  $lon	GPS longitude coordinate
-	 *
-	 * @return string|mixed Data without useless information
-	 *
-	 * @url GET /gps_coords
-	 *
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 * @throws RestException 500
-	 */
-	public function gpsCoords(string $licplate, string $lat, string $lon): string
-	{
-		if (!(new GpsPosition)->save($licplate, $lat, $lon)) {
-			throw new RestException(500, 'Save failure');
-		}
-
-		return 'OK';
-	}
-
-	/**
-	 * Receive GPS coordinates of the device
-	 *
-	 * @param string  $licplate	License plate of the vehicle
-	 * @param string  $lat	GPS latitude coordinate
-	 * @param string  $lon	GPS longitude coordinate
-	 *
-	 * @return string|mixed Data without useless information
-	 *
-	 * @url POST /gps_position
-	 *
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 * @throws RestException 500
-	 */
-	public function gpsPosition(string $licplate, string $lat, string $lon): string
-	{
-		if (!(new GpsPosition)->save($licplate, $lat, $lon)) {
-			throw new RestException(500, 'Save failure');
-		}
-
-		return 'OK';
-	}
-
-	/**
-	 * List of countries
-	 *
-	 * Return an array with country name and code.
-	 *
-	 * @param string $lang
-	 * @param string $search
-	 * 
-	 * @return array|mixed Data without useless information
-	 *
-	 * @url GET /countries
-	 *
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 */
-	public function countries(string $lang = 'hu', string $search = ''): array
-	{
-		return (new CountryHandler)->getCountries($lang, $search);
-	}
-
-	/**
-	 * List of entities
-	 *
-	 * Return an array with companies data.
-	 *
-	 * @return array|mixed Data without useless information
-	 *
-	 * @url GET /entities
-	 *
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 */
-	public function entities(): array
-	{
-		return (new EntityHandler)->getEntities();
-	}
-
-	/**
-	 * List of assets received
-	 *
-	 * Return an array with assets received.
-	 *
-	 * @return array|mixed Data without useless information
-	 *
-	 * @url GET /assets_received
-	 *
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 */
-	public function assetsReceived(): array
-	{
-		global $user, $db;
-
-		$packageHistory = new PackageHistory($db);
-		$inventoryObj = new Inventory($db);
-		$sql = "SELECT pt.device_id, i.ref, i.title, ie.unique_identifier, 
-		(SELECT ph.date_creation FROM " . $this->db->prefix() . $packageHistory->table_element . " as ph WHERE user_id = {$user->id} AND DATE(ph.date_creation) = CURRENT_DATE
-		ORDER BY ph.rowid DESC LIMIT 1) AS ph_date_creation
-		FROM llx_settlements_packagetool pt
-		INNER JOIN llx_inventory as i ON i.rowid = pt.device_id
-		INNER JOIN llx_inventory_extrafields as ie ON ie.fk_object = i.rowid
-		WHERE pt.package_id = (SELECT ph.package_id FROM public.llx_rollerstorage_packagehistory as ph WHERE user_id = {$user->id}
-		AND DATE(ph.date_creation) = CURRENT_DATE
-		ORDER BY ph.rowid DESC LIMIT 1)
-		ORDER BY pt.rowid DESC";
-		//print $sql;exit;
-		$result = $db->query($sql);
-		$changeSql = "SELECT ref, title FROM " . $this->db->prefix() . $inventoryObj->table_element . " WHERE ref ILIKE '%Change%'";
-		$resultCh = $db->query($changeSql);
-		if ($result > 0 && $resultCh > 0) {
-			while ($row = $db->fetch_object($result)) {
-				$row = (array) $row;
-				$date = $row['ph_date_creation'];
-				$device[] = [
-					"name" => isset($row['title']) ? $row['title'] : '',
-					"sku" => isset($row['unique_identifier']) ? $row['unique_identifier'] : ''
-				];
-			}
-			$change = [];
-			while ($rowCh = $db->fetch_object($resultCh)) {
-				$rowCh = (array) $rowCh;
-				$ChangeArray = explode('_', $rowCh['ref']);
-				$key = isset($ChangeArray[1]) ? strtolower($ChangeArray[1]) : '';
-				if (!empty($key)) {
-					$change[$key] = isset($rowCh['title']) ? $rowCh['title'] : '';
-				}
-			}
-		} else {
-			return [];
-		}
-
-		return [
-			'date' => $date,
-			'devices' => $device,
-			'change' => $change
-		];
-	}
-
-	/**
-	 * Save card payment log
-	 *
-	 * @param string $invoice_ref
-	 * @param string $data
-	 * 
-	 * @return array|mixed Data without useless information
-	 *
-	 * @url POST /cardpaymentlog
-	 *
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 */
-	public function cardPaymentLog(string $invoice_ref, string $data)
-	{
-		$result = 'OK';
-
-		$facture = new Facture($this->db);
-		if ($facture->fetch(0, $invoice_ref) > 0) {
-			$facture->array_options['options_payment_log'] = $data;
-			$facture->updateExtraField('payment_log');
-
-			$json = json_decode($data, true);
-			if (!empty($json['authorization_number'])) {
-				$sql = "
-					UPDATE " . MAIN_DB_PREFIX . "paiement 
-					SET num_paiement='" . $json['authorization_number'] . "' 
-					WHERE rowid=(
-						SELECT fk_paiement 
-						FROM " . MAIN_DB_PREFIX . "paiement_facture 
-						WHERE fk_facture={$facture->id}
-					)
-				";
-				$this->db->query($sql);
-			}
-		} else {
-			$result = 'Missing invoice';
-		}
-
-		return $result;
-	}
-
-	/**
-	 * Products list with children
-	 *
-	 * @param  string $sortfield  			Sort field
-	 * @param  string $sortorder  			Sort order
-	 * @param  int    $limit      			Limit for list
-	 * @param  int    $page       			Page number
-	 * @param  int    $mode       			Use this param to filter list (0 for all, 1 for only product, 2 for only service)
-	 * @param  int    $category   			Use this param to filter list by category
-	 * @param  string $sqlfilters 			Other criteria to filter answers separated by a comma. Syntax example "(t.tobuy:=:0) and (t.tosell:=:1)"
-	 * @param  bool   $ids_only   			Return only IDs of product instead of all properties (faster, above all if list is long)
-	 * @param  int    $variant_filter   		Use this param to filter list (0 = all, 1=products without variants, 2=parent of variants, 3=variants only)
-	 * @param  bool   $pagination_data   	If this parameter is set to true the response will include pagination data. Default value is false. Page starts from 0
-	 * @param  int    $includestockdata		Load also information about stock (slower)
-	 * @return array                			Array of product objects
-	 *
-	 * @url GET /productshotel
-	 *
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 */
-	public function productshotel($sortfield = 't.ref', $sortorder = 'ASC', $limit = 100, $page = 0, $mode = 0, $category = 0, $sqlfilters = '', $ids_only = false, $variant_filter = 0, $pagination_data = false, $includestockdata = 0): array
-	{
-		if (!DolibarrApiAccess::$user->rights->produit->lire) {
-			throw new RestException(403);
-		}
-
-		return (new ApiProductListHelper)->listhotel($sortfield, $sortorder, $limit, $page, $mode, $category, $sqlfilters, $ids_only, $variant_filter, $pagination_data, $includestockdata);
-	}
-
-	/**
-	 * Get the saled factures data by user
-	 * 
-	 * @param  string 	$date			Date
-	 * 
-	 * @return array|mixed Data without useless information
-	 * 
-	 * @url POST /saledfacturesbyuser
-	 */
-	function saledfacturesbyuser(string $date)
-	{
-		global $user, $db;
-		$date = date("Y-m-d", dol_now());
-		$fulldate = date("Y-m-d H:i:s", dol_now());
-		$apiBbusHelper = new ApiBBusHelper();
-		$from = $apiBbusHelper->getGroupLoginDate($date);
-		$to = $apiBbusHelper->getGroupLogout($date, $from);
-		$array = [];
-		if (strtotime($fulldate) > strtotime($to)) {
-			return $array;
-		}
-		$sql = "SELECT 
-		f.rowid as facture_rowid, 
-		f.ref AS facture_ref, 
-		f.datec, 
-		f.multicurrency_code, 
-		pa.libelle, 
-		pai.note AS transaction_id, 
-		fdet.fk_product AS product_id,  
-		pr.label AS product_label, 
-		pr.ref AS product_ref, 
-		pr.description, 
-		pass.fk_product_pere AS bundle_id, 
-		pr2.label AS bundle_label, 
-		pr2.ref AS bundle_ref, 
-		fdet.multicurrency_total_tva AS base_tva, 
-		fdet.multicurrency_total_ttc AS base_ttc, 
-		fdete.discount_percent, 
-		fdete.discount_hours, 
-		pr.default_vat_code, 
-		f.multicurrency_total_ttc AS bundle_ttc, 
-		(SELECT rate FROM llx_multicurrency_rate AS mc WHERE mc.fk_multicurrency = f.fk_multicurrency AND mc.entity = {$user->entity} ORDER BY mc.rowid DESC LIMIT 1) AS rate,
-		(SELECT date_start FROM llx_eventwizard_eventhistory AS evev WHERE evev.fk_facture = f.rowid) AS date_start
-				FROM llx_facture AS f
-				INNER JOIN llx_facture_extrafields AS fe ON fe.fk_object = f.rowid
-				INNER JOIN llx_c_paiement AS pa ON pa.id = f.fk_mode_reglement
-				INNER JOIN llx_paiement_facture AS pafa ON pafa.fk_facture = f.rowid
-				INNER JOIN llx_paiement AS pai ON pai.rowid = pafa.fk_paiement
-				INNER JOIN llx_facturedet AS fdet ON fdet.fk_facture = f.rowid
-				INNER JOIN llx_product AS pr ON pr.rowid = fdet.fk_product
-				left JOIN llx_product_association AS pass ON pass.fk_product_fils = pr.rowid
-				LEFT JOIN llx_product AS pr2 ON pr2.rowid = pass.fk_product_pere
-				LEFT JOIN llx_facturedet_extrafields AS fdete ON fdet.rowid = fdete.fk_object
-				WHERE f.datec BETWEEN '{$from}' AND '{$to}' 
-				AND f.fk_user_closing = $user->id
-				ORDER BY f.rowid DESC";
-		//print $sql;exit;
-		$data = $db->query($sql);
-		if ($db->num_rows($data > 0)) {
-
-			while ($row = $db->fetch_object($data)) {
-				$tmpArray = (array) $row;
-				if ($tmpArray['transaction_id'] != '') {
-					$jsonArray = json_decode($tmpArray['transaction_id']);
-					$row->transaction_id = $jsonArray->card_transaction_id;
-				}
-				$row->bundle_id = $row->bundle_id == '' ? $row->product_id : $row->bundle_id;
-				$row->bundle_label = $row->bundle_label == '' ? $row->product_label : $row->bundle_label;
-				$row->bundle_ref = $row->bundle_ref == '' ? $row->product_ref : $row->bundle_ref;
-				$array[$row->facture_rowid]['ref'] = $row->facture_ref;
-				$array[$row->facture_rowid]['bundle_name'] = $row->bundle_label;
-				$array[$row->facture_rowid]['currency'] = $row->multicurrency_code;
-				$array[$row->facture_rowid]['total_price'] = $row->bundle_ttc;
-				$array[$row->facture_rowid]['type'] = $row->libelle;
-				$array[$row->facture_rowid]['transaction_id'] = $row->transaction_id;
-				$array[$row->facture_rowid]['rate'] = $row->rate;
-				$array[$row->facture_rowid]['date'] = $row->datec;
-				$array[$row->facture_rowid]['lines'][$row->product_id] = (array) $row;
-			}
-			return $array;
-		} else {
-			return $array;
-		}
-	}
-
-	/**
-	 * List of assets received
-	 *
-	 * Return an array with assets received.
-	 *
-	 * @return array|mixed Data without useless information
-	 *
-	 * @url GET /getAllUsersOfTheGroup
-	 *
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 */
-	public function getAllUsersOfTheGroup(): array
-	{
-		global $user, $db;
-		$array = [];
-		$sql = "SELECT u.rowid, u.firstname, u.lastname, u.login, ue.nickname, u.email, u.office_phone, u.user_mobile, u.personal_mobile   FROM llx_settlements_groupusers AS gu
-		INNER JOIN llx_user_extrafields as ue ON ue.fk_object = gu.fk_user
-		INNER JOIN llx_user as u ON u.rowid = gu.fk_user
-		WHERE gu.fk_settlements_group = (SELECT gu2.fk_settlements_group FROM llx_settlements_groupusers as gu2 WHERE gu2.fk_user = {$user->id})";
-		$data = $db->query($sql);
-		while ($row = $db->fetch_object($data)) {
-			$array[] = [
-				'id' => $row->rowid,
-				'name' => $row->firstname . ' ' . $row->lastname,
-				'login' => $row->login,
-				'email' => $row->email,
-				'nickname' => $row->nickname ? $row->nickname : '',
-				'office_phone' => $row->office_phone,
-				'user_mobile' => $row->user_mobile,
-				'personal_mobile' => $row->personal_mobile
-			];
-		}
-		return $array;
-	}
-
-	#-------------------------------------------------
-	#	Event generator
-	#-------------------------------------------------
-
-	/**
-	 * Generate events	 
-	 *
-	 * @return array|mixed Data without useless information
-	 *
-	 * @url GET /genEvents
-	 *
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 */
-	public function genEvents()
-	{
-		$period = new DatePeriod(
-			new DateTime('2024-07-16'),
-			new DateInterval('P1D'),
-			new DateTime('2024-10-16')
-		);
-
-		$size = 30;
-		$buffer = 5;
-
-		$dates = [];
-
-		foreach ($period as $date) {
-			if (rand(0, 1)) {
-				$eventDate = $date->format('Y-m-d');
-				$eventTimes = array_rand(
-					array_flip([
-						'09:00',
-						'10:00',
-						'11:00',
-						'12:00',
-						'13:00',
-						'14:00',
-						'15:00',
-						'16:00',
-						'17:00',
-						'18:00',
-						'19:00',
-						'20:00'
-					]),
-					rand(1, 12)
-				);
-				/* 				print $eventDate;
-																													print "\r\n";
-																													print_r($eventTimes); */
-				if (is_array($eventTimes)) {
-					foreach ($eventTimes as $eventTime) {
-						$duration = rand(1, 3);
-						$dates[$eventDate][] = [
-							'start' => $eventTime,
-							'duration' => '0' . $duration . ':00:00',
-						];
-					}
-				}
-			}
-		}
-
-		//print_r($dates);exit;
-
-
-		$eventdetail_id = 1;
-		$dates = ['dates' => $dates, 'id_eventdetails' => $eventdetail_id];
-
-		dol_include_once('/eventwizard/class/eventdetails.class.php');
-
-		$object = new EventDetails($this->db);
-		$object->fetch($eventdetail_id);
-		$object->genEvents($dates, $eventdetail_id);
-
-		//print_r($dates);exit;
-
-		return [];
-	}
-
-	/**
-	 * Generate events	 
-	 *
-	 * @return array|mixed Data without useless information
-	 *
-	 * @url GET /updateeventextrafields
-	 *
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 */
-	public function updateEventsExtrafields()
-	{
-		$sql = "SELECT ac.id FROM llx_actioncomm as ac 
-		LEFT JOIN llx_actioncomm_extrafields as ace ON ace.fk_object = ac.id
-		WHERE ac.code = 'AC_EVENT'
-		AND ace.max_num IS NULL";
-		$result1 = $this->db->query($sql);
-		while ($row1 = $this->db->fetch_object($result1)) {
-			print_r($row1);
-			$participants = rand(1, 20);
-			$sql2 = "INSERT INTO llx_actioncomm_extrafields (fk_object, max_num, buffer, participants)
-			VALUES ({$row1->id}, 30, 5, $participants)";
-			$result = $this->db->query($sql2);
-		}
-	}
-
-	#-------------------------------------------------
-	#	Facture generator
-	#-------------------------------------------------
-
-	/**
-	 * Generate factures	 
-	 * 
-	 * @return array|mixed Data without useless information
-	 *
-	 * @url POST /generateFactures
-	 *
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 */
-	public function generateFactures()
-	{
-		$numberOfFactures = 1;
-
-		for ($i = 0; $i < $numberOfFactures; $i++) {
-			$currencyRand = rand(0, 1);
-			$currencyText = [0 => 'HUF', 1 => 'EUR'];
-			$currencyNumber = [0 => 3, 1 => 5];
-
-			$reglementRand = rand(14, 15);
-			//$reglementRand = rand(106, 107); ## STAGING
-			$mode_reglement_id = $reglementRand;
-			$paymentid = $reglementRand;
-			$accountid = [0 => 4, 1 => 5];
-
-			$deduction = rand(1, 5) == 3 ? 1 : null;
-			$storno = rand(1, 5) == 1 ? 1 : null;
-
-			$invoice = ["mode_reglement_id" => $mode_reglement_id, "array_options_app_facture" => 1, "array_options_commission_deduction" => $deduction, "array_options_marked_for_storno" => $storno, "multicurrency_code" => $currencyText[$currencyRand], "fk_multicurrency" => $currencyNumber[$currencyRand], "array_options_customer_data_zip" => null];
-			$linesHUFBundle = [
-				["desc" => "", "subprice" => "787.4015", "qty" => 1, "tva_tx" => "27.0", "localtax1_tx" => "0.000", "localtax2_tx" => "0.000", "fk_product" => "102", "remise_percent" => "0", "date_start" => "", "date_end" => "", "fk_code_ventilation" => 0, "info_bits" => "0", "fk_remise_except" => null, "product_type" => "1", "rang" => "-1", "special_code" => "0", "fk_parent_line" => null, "fk_fournprice" => null, "pa_ht" => "0.00000000", "label" => "", "array_options" => [], "situation_percent" => "100", "fk_prev_id" => null, "fk_unit" => null, "price_base_type" => "HT"],
-				["desc" => "", "subprice" => "13333.3333", "qty" => 1, "tva_tx" => "5.0", "localtax1_tx" => "0.000", "localtax2_tx" => "0.000", "fk_product" => "108", "remise_percent" => "0", "date_start" => "", "date_end" => "", "fk_code_ventilation" => 0, "info_bits" => "0", "fk_remise_except" => null, "product_type" => "1", "rang" => "-1", "special_code" => "0", "fk_parent_line" => null, "fk_fournprice" => null, "pa_ht" => "0.00000000", "label" => "", "array_options" => [], "situation_percent" => "100", "fk_prev_id" => null, "fk_unit" => null, "price_base_type" => "HT"]
-			];
-
-			$linesEURBundle = [
-				["desc" => "", "subprice" => "2.3622", "qty" => 1, "tva_tx" => "27.0", "localtax1_tx" => "0.000", "localtax2_tx" => "0.000", "fk_product" => "101", "remise_percent" => "0", "date_start" => "", "date_end" => "", "fk_code_ventilation" => 0, "info_bits" => "0", "fk_remise_except" => null, "product_type" => "1", "rang" => "-1", "special_code" => "0", "fk_parent_line" => null, "fk_fournprice" => null, "pa_ht" => "0.00000000", "label" => "", "array_options" => [], "situation_percent" => "100", "fk_prev_id" => null, "fk_unit" => null, "price_base_type" => "HT"],
-				["desc" => "", "subprice" => "33.3333", "qty" => 1, "tva_tx" => "5.0", "localtax1_tx" => "0.000", "localtax2_tx" => "0.000", "fk_product" => "107", "remise_percent" => "0", "date_start" => "", "date_end" => "", "fk_code_ventilation" => 0, "info_bits" => "0", "fk_remise_except" => null, "product_type" => "1", "rang" => "-1", "special_code" => "0", "fk_parent_line" => null, "fk_fournprice" => null, "pa_ht" => "0.00000000", "label" => "", "array_options" => [], "situation_percent" => "100", "fk_prev_id" => null, "fk_unit" => null, "price_base_type" => "HT"]
-			];
-
-			$linesEURSimple = [
-				["desc" => "", "subprice" => "12.5984", "qty" => 1, "tva_tx" => "27.0", "localtax1_tx" => "0.000", "localtax2_tx" => "0.000", "fk_product" => "132", "remise_percent" => "0", "date_start" => "", "date_end" => "", "fk_code_ventilation" => 0, "info_bits" => "0", "fk_remise_except" => null, "product_type" => "1", "rang" => "-1", "special_code" => "0", "fk_parent_line" => null, "fk_fournprice" => null, "pa_ht" => "0.00000000", "label" => "", "array_options" => [], "situation_percent" => "100", "fk_prev_id" => null, "fk_unit" => null, "price_base_type" => "HT"],
-			];
-
-			$linesArray = [
-				0 => $linesEURBundle,
-				1 => $linesEURSimple,
-				2 => $linesHUFBundle
-			];
-			if ($currencyRand == 1) {
-				$lineRand = rand(0, 1);
-				$lines = $linesArray[$lineRand];
-			} else {
-				$lines = $linesArray[2];
-			}
-
-			$payment = ["datepaye" => time(), "paymentid" => $paymentid, "closepaidinvoices" => "yes", "accountid" => $accountid[$currencyRand]];
-			$comment = "{\"timestamp\":\"1704241200\",\"merchant_id\":\"000000000014202\",\"acquirer_id\":\"88105000003\",\"operation_number\":\"000037\",\"termid\":\"36002058\",\"pan\":\"428312******1789\",\"exp\":\"****\",\"stan\":\"000007\",\"authorization_number\":\"282597\",\"trans_type\":\"CLI\",\"amount\":\"2.0\",\"cvm\":\"2\",\"billnumber\":\"AcTbHmcWsMGcF6Nf\",\"card_transaction_id\":\"AcTbHmcWsMGcF6Nf\"}";
-			$payment['comment'] = $reglementRand == 15 ? '' : $comment;
-			//$payment['comment'] = $reglementRand == 107 ? '' : $comment;  ## STAGING
-
-			$cardPaymentLog = "";
-
-			/*  			print_r($invoice);
-															print_r($lines);
-															print_r($payment);
-															exit; */
-
-			$invoiceNumber = $this->invoice($invoice, $lines, $payment, $cardPaymentLog, $sendId = '');
-			$ref = $invoiceNumber['invoice']['ref'];
-			$datetime = date("Y-m-d H:i:s", $payment['datepaye']);
-			$multiprint = rand(1, 5);
-			$this->dateHandler($ref, $datetime);
-			if ($multiprint == 2 || $multiprint == 3) {
-				$prints = rand(1, 5);
-				$dateofprint = $payment['datepaye'];
-				for ($i = 0; $i < $prints; $i++) {
-					$dateofprint = strtotime('+5 seconds', $dateofprint);
-					$datetime = date("Y-m-d H:i:s", $dateofprint);
-					$this->dateHandler($ref, $datetime);
-				}
-			}
-		}
-		return 'OK';
-	}
-
-	#-------------------------------------------------
-	#	CURL
-	#-------------------------------------------------
-
-	/**
-	 * Check Printed copies
-	 *
-	 * Return an array with details
-	 *
-	 * @return 	array|mixed data without useless information
-	 *
-	 * @param 	string 	$ref			//selected evet from llx_event
-	 * 
-	 * @url	POST curlCheckPrintedCopies
-	 * @throws RestException 401 Not allowed
-	 * @throws RestException 404 Not found
-	 */
-	public function curlCheckPrintedCopies(string $ref)
-	{
-		$bbticketinvoiceprinting = new BbTicketInvoicePrinting($this->db);
-		$copies = $bbticketinvoiceprinting->fetchAll('ASC', 'rowid', 0, 0, ["customsql" => "invoice_number ILIKE '%{$ref}%'"]);
-		return (is_array($copies)) ? count($copies) : 0;
-	}
-
-	/**
-	 * @param 	string 	$datetime
-	 * @param 	string 	$datetime_timestamp
-	 * @param 	array 	$ticket
-	 * @param 	string 	$ref
-	 * 
-	 * 
-	 * @return array|mixed Data without useless information
-	 *
-	 * @url POST curlSetPrintingInvoiceObject
-	 *
-	 * @throws RestException 401
-	 * @throws RestException 403
-	 * @throws RestException 404
-	 */
-	public function curlSetPrintingInvoiceObject(string $datetime, string $datetime_timestamp, array $ticket, string $ref)
-	{
-		global $user;
-		$helper = new ApiBBusHelper();
-		$helper->setPrintingInvoiceObject($user, null, $datetime, $datetime_timestamp, (object)$ticket, $ref);
-	}
-}
+<?php
+/* Copyright (C) 2015   Jean-François Ferry     <jfefe@aternatik.fr>
+* Copyright (C) 2019   Cedric Ancelin          <icedo.anc@gmail.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 3 of the License, or
+* (at your option) any later version.
+*
+program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use Luracast\Restler\RestException;
+use Sabre\Xml\Element;
+
+
+require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
+require_once DOL_DOCUMENT_ROOT . '/product/stock/class/productlot.class.php';
+require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
+require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
+require_once DOL_DOCUMENT_ROOT . '/variants/class/ProductAttribute.class.php';
+require_once DOL_DOCUMENT_ROOT . '/variants/class/ProductAttributeValue.class.php';
+require_once DOL_DOCUMENT_ROOT . '/variants/class/ProductCombination.class.php';
+require_once DOL_DOCUMENT_ROOT . '/variants/class/ProductCombination2ValuePair.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/sellproduct.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/apiuserhandler.class.php';
+require_once DOL_DOCUMENT_ROOT . '/compta/facture/class/facture.class.php';
+
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/jegy.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbloginlog.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbexchangerate.class.php';
+
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbticket.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbdevices.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbdevicesservicelocation.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbdevicesservicelocationproduct.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbticketinvoiceprinting.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/utils/globalconst.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/utils/emailtemplatehandler.php';
+include_once DOL_DOCUMENT_ROOT . '/core/class/CMailFile.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbticketnaplo.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbtickethandler.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/gpsposition.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/userloginnaplo.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/utils/countryhandler.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/utils/entityhandler.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/discountnaplo.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/settlements/class/groupusers.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/rollerstorage/class/packagehistory.class.php';
+require_once DOL_DOCUMENT_ROOT . '/product/inventory/class/inventory.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/apiproductlisthelper.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/apiinvoicehelper.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/ticket_checker.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/api_bbus_helper.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/api_bbus_log.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbapilock.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/exchangerateupdater.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbticketvalidationcoords.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/api_curl.class.php';
+
+
+
+/**
+ * API class for products
+ *
+ * @access protected
+ * @class  DolibarrApiAccess {@requires user,external}
+ */
+class BBus extends DolibarrApi
+{
+
+	use CurlApi;
+
+	private $code;
+	private $printingTime;
+	private $status = 0;
+	private $device_id;
+	private $service_location_id;
+	private $bbTicketRowId;
+	private $facture_id;
+	private $isValid = true;
+	private User $user;
+
+
+	const EMAIL_TEMPLATE = 'multiticketprinting';
+	const GLOBAL_CONF_SEND_TO_EMAIL = 'BBUS_INVOICE_PRINTING_ALERT_EMAIL';
+	const GLOBAL_CONF_EMAIL_FROM = 'BBUS_INVOICE_PRINTING_ALERT_EMAIL_FROM';
+
+	public function __construct()
+	{
+		global $db, $conf, $user;
+
+		$this->db = $db;
+		$this->user = $user;
+	}
+
+
+	#-------------------------------------------------
+	#	Vásárlási folyamat
+	#-------------------------------------------------
+
+	/**
+	 * Get app config
+	 *
+	 * Return an array with config params.
+	 *
+	 * @return array|mixed Data without useless information
+	 *
+	 * @url GET config
+	 *
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 */
+	public function config(): array
+	{
+		global $user, $conf;
+		$helper = new ApiBBusHelper();
+		// company
+		$companyData = $helper->getCompany();
+
+		$config = [
+			'exchange_rate' => ExchangeRateUpdater::getExchangeRate(ExchangeRateUpdater::EUR, $conf->entity),
+			'invoice' => [
+				'header' => [
+					'name' => $companyData['company_data']['NOM'],
+					'tax_number' => $companyData['company_data']['TVAINTRA'],
+					'address' => [
+						'zip' => $companyData['company_data']['ZIP'],
+						'city' => $companyData['company_data']['TOWN'],
+						'address' => $companyData['company_data']['ADDRESS']
+					]
+				],
+				'customer' => $helper->getAppCustomers(),
+				'vat_percent' => 27,
+				'entity' => $user->entity,
+				'account_id' => $helper->getAccountRowid($user->entity),
+				'currencies' => $helper->getCurrenciesRowid($user->entity),
+				'payments_mode' => $helper->getPaymentsMode($user->entity)
+			]
+		];
+		ApiBbusLog::appLog("User: {$user->firstname} {$user->lastname} (ID: {$user->id})");
+		ApiBbusLog::appLog(json_encode([
+			'config' => $config
+		]));
+
+
+
+		return $config;
+	}
+
+	/**
+	 * Check user and eventdetail type permissions
+	 *
+	 * Return a result.
+	 *
+	 * @return array|mixed Data without useless information
+	 *
+	 * @url GET checkPermission
+	 */
+	public function checkPermission()
+	{
+		global $user, $db;
+		$permissionArray = [];
+		$groupUsersObj = new GroupUsers($db);
+		$result = $groupUsersObj->fetchAll('desc', 'rowid', 1, 0, ["customsql" => "fk_user = {$user->id}"]);
+		if ($result == -1 || empty($result)) {
+			return $permissionArray;
+		}
+		dol_include_once('/eventwizard/class/eventdetails.class.php');
+		$sqlBasicService = "SELECT pe.basic_service FROM llx_product as p
+			INNER JOIN llx_product_extrafields as pe ON pe.fk_object = p.rowid
+			GROUP BY pe.basic_service";
+		$resultBasicServices = $db->query($sqlBasicService);
+		if ($db->num_rows($resultBasicServices) > 0) {
+			while ($row = $db->fetch_object($resultBasicServices)) {
+				$basicServices[] = $row->basic_service;
+			}
+		}
+		$sqlBasicServicesTable = "SELECT basic_service_id, ref FROM llx_bbus_basicServices ORDER BY basic_service_id ASC";
+		$resultBasicServicesObj = $db->query($sqlBasicServicesTable);
+		if ($db->num_rows($resultBasicServicesObj) > 0) {
+			while ($bsrow = $db->fetch_object($resultBasicServicesObj)) {
+				foreach ($basicServices as $item) {
+					if ($bsrow->basic_service_id == $item && $item != 2) {
+						$permissionArray[$item] = $bsrow->ref;
+					}
+				}
+			}
+		}
+		return $permissionArray;
+	}
+
+	/**
+	 * Products list with children
+	 *
+	 * @param  string $sortfield  			Sort field
+	 * @param  string $sortorder  			Sort order
+	 * @param  int    $limit      			Limit for list
+	 * @param  int    $page       			Page number
+	 * @param  int    $mode       			Use this param to filter list (0 for all, 1 for only product, 2 for only service)
+	 * @param  int    $category   			Use this param to filter list by category
+	 * @param  string $sqlfilters 			Other criteria to filter answers separated by a comma. Syntax example "(t.tobuy:=:0) and (t.tosell:=:1)"
+	 * @param  bool   $ids_only   			Return only IDs of product instead of all properties (faster, above all if list is long)
+	 * @param  int    $variant_filter   		Use this param to filter list (0 = all, 1=products without variants, 2=parent of variants, 3=variants only)
+	 * @param  bool   $pagination_data   	If this parameter is set to true the response will include pagination data. Default value is false. Page starts from 0
+	 * @param  int    $includestockdata		Load also information about stock (slower)
+	 * @return array                			Array of product objects
+	 *
+	 * @url GET /products
+	 *
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 */
+	public function products($sortfield = 't.ref', $sortorder = 'ASC', $limit = 100, $page = 0, $mode = 0, $category = 0, $sqlfilters = '', $ids_only = false, $variant_filter = 0, $pagination_data = false, $includestockdata = 0): array
+	{
+		if (!DolibarrApiAccess::$user->rights->produit->lire) {
+			throw new RestException(403);
+		}
+		//$sqlfilters .= " AND ef.basic_service < '3'";
+		return (new ApiProductListHelper)->list($sortfield, $sortorder, $limit, $page, $mode, $category, $sqlfilters, $ids_only, $variant_filter, $pagination_data, $includestockdata);
+	}
+
+	/**
+	 * Create invoice
+	 * 
+	 * @param  array 	$invoice			Invoice data
+	 * @param  array 	$lines				Invoice lines
+	 * @param  array 	$payment			Invoice payment
+	 * @param  string 	$cardPaymentLog		Card payment log data
+	 * 
+	 * @return array|mixed Data without useless information
+	 * 
+	 * @url POST /invoice
+	 */
+	public function invoice(array $invoice, array $lines, array $payment, string $cardPaymentLog = '', string $sendId = '')
+	{
+		global $user;
+		$lockLabel = 'CREATE_INVOICE';
+		if (empty($sendId)) {
+			$sendId = substr(md5(rand()), 0, 8);
+		}
+		/**
+		 * LOG SECTION
+		 */
+		ApiBbusLog::appLog("{$sendId} === NEW INVOICE ===");
+		dol_syslog("{$sendId} === NEW INVOICE ===", LOG_INFO, 0);
+		ApiBbusLog::appLog("{$sendId} REQUEST: {$sendId}");
+		dol_syslog("{$sendId} REQUEST: {$sendId}", LOG_INFO, 0);
+		ApiBbusLog::appLog("{$sendId} User: {$user->firstname} {$user->lastname} (ID: {$user->id})");
+		dol_syslog("{$sendId} User: {$user->firstname} {$user->lastname} (ID: {$user->id})", LOG_INFO, 0);
+		ApiBbusLog::appLog("{$sendId} " . json_encode([
+			'invoice' => $invoice,
+			//'lines' => $lines,
+			'payment' => $payment,
+			'cardPaymentLog' => $cardPaymentLog
+		]));
+
+		/**
+		 * CHECK RIGHTS
+		 */
+		if (!DolibarrApiAccess::$user->rights->facture->creer) {
+			ApiBbusLog::appLog("{$sendId} Insufficient rights");
+			throw new RestException(401, 'Insufficient rights');
+		}
+
+
+		$apiInvoiceHelper = new ApiInvoiceHelper;
+		/**
+		 * handle invoice basic data
+		 */
+		$invoiceObj = $apiInvoiceHelper->createInvoice($invoice, $sendId);
+		/**
+		 * handle invoice line(s)
+		 */
+		$products = [];
+
+		foreach ($lines as $line) {
+			$invoiceObj = $apiInvoiceHelper->addLineToInvoice($invoiceObj, $line, $sendId);
+
+			$product = $apiInvoiceHelper->loadProductToResult($line);
+			if (!empty($product)) {
+				$products[] = $product;
+			}
+		}
+
+		$invoiceObj->fetch_lines();
+
+		foreach ($products as &$row) {
+			foreach ($invoiceObj->lines as $line) {
+				if ($line->product_ref == $row['ref']) {
+					//$row['price'] = $line->total_ttc;
+					$row['price'] = $line->multicurrency_total_ttc;
+					$row['total_tva'] = $line->total_tva;
+					//$row['total_tva'] = $line->multicurrency_total_tva;
+				}
+			}
+		}
+
+		/**
+		 * validate
+		 */
+		$invoiceObj = $apiInvoiceHelper->validateInvoice($invoiceObj, $sendId);
+		/**
+		 * set payment
+		 */
+		$apiInvoiceHelper->setPayment($invoiceObj, $payment, $sendId);
+		/**
+		 * save card payment data
+		 */
+		if (!empty($cardPaymentLog)) {
+			$invoiceObj = $apiInvoiceHelper->saveCardPaymentLog($invoiceObj, $cardPaymentLog, $sendId);
+		}
+
+		ApiBbusLog::appLog("{$sendId} Invoice created. ID: {$invoiceObj->id} REF: {$invoiceObj->ref} REQ: {$sendId}");
+		dol_syslog("{$sendId} Invoice created. ID: {$invoiceObj->id} REF: {$invoiceObj->ref} REQ: {$sendId}", LOG_INFO, 0);
+		//$bbApiLock->delete($user);
+
+		ApiBbusLog::appLog("{$sendId}####################################################################");
+		dol_syslog("{$sendId}####################################################################", LOG_INFO, 0);
+
+		return [
+			'sendId' => $sendId,
+			'invoice' => [
+				'id' => $invoiceObj->id,
+				'ref' => $invoiceObj->ref,
+				'total' => $invoiceObj->multicurrency_total_ttc
+			],
+			'products' => $products,
+		];
+	}
+
+	/**
+	 * Post invoice ref and creation date for data recording
+	 *
+	 * Return an array with success information.
+	 *
+	 * @param  string $ref	Facture ref
+	 * @param  string $datetime	invoice creation date
+	 * @param  string $type_id	type_id
+	 *
+	 * @return array|mixed Data without useless information
+	 *
+	 * @url POST printdate
+	 *
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 */
+	public function dateHandler(string $ref, string $datetime, string $type_id = null)
+	{
+		global $user;
+		$helper = new ApiBBusHelper();
+
+
+		if (!DolibarrApiAccess::$user->rights->produit->lire) {
+			ApiBbusLog::appLog("!DolibarrApiAccess::user->rights->produit->lire");
+			throw new RestException(403);
+		}
+		if (empty($ref)) {
+			ApiBbusLog::appLog("Empty ref!");
+			throw new RestException(401, 'Empty ref!');
+		}
+		if (empty($datetime)) {
+			ApiBbusLog::appLog("Empty datetime!");
+			throw new RestException(401, 'Empty datetime!');
+		}
+
+		ApiBbusLog::appLog("=== dateHandler ===");
+		ApiBbusLog::appLog("Facture: {$ref}");
+		
+		$printDate = DateTime::createFromFormat('Y-m-d H:i:s', $datetime);
+		ApiBbusLog::appLog("User: {$user->firstname} {$user->lastname} (ID: {$user->id})");
+		$now = new DateTime('now');
+		$now = (clone $now)->modify('+2 minutes');
+		$validDate = (clone $now)->modify('-5 minutes');
+		$datetime = $printDate->format('Y-m-d H:i:s');
+		$datetime_timestamp = strtotime($datetime);
+
+		if ($validDate <= $printDate && $printDate <= $now) {
+			$server_host = $this->getServerHost($type_id);
+			# Keresztvásárlás
+			if ($server_host == 'excelia') {
+				$this->localDateHandler($ref, $datetime, $datetime_timestamp, $now);
+			} else {
+				ApiBbusLog::appLog("dateHandler: cURL");
+				$this->curlDateHandler($ref, $datetime);
+			}
+		} else {
+			ApiBbusLog::appLog("dateHandler: Invalid date");
+			throw new RestException(401, 'Invalid date');
+		}
+		return 'OK';
+	}
+
+	private function localDateHandler($ref, $datetime, $datetime_timestamp, $now)
+	{
+		global $user;
+		$helper = new ApiBBusHelper();
+		$facture_id = $helper->getFactureIdForInvoicePrinting($ref);
+		if(empty($facture_id)){
+			ApiBbusLog::appLog("localDateHandler: facture_id because cross-shopping");
+			$printedCopies = $this->checkPrintedCopiesWithRef($ref);
+		}else{
+			$printedCopies = $this->checkPrintedCopies($facture_id);
+		}
+		// Leellenőrzöm, hogy a számla volt-e már nyomtatva
+		
+		ApiBbusLog::appLog("printedCopies: {$printedCopies}");
+
+
+		if ($printedCopies == 0) {
+			ApiBbusLog::appLog("printedCopies: no copies");
+			if(empty($facture_id)){
+				$bbTicketsByFacture = $this->getTicketsByInvoiceNumber($ref);
+			}else{
+				$bbTicketsByFacture = $this->getTicketsByFactureId($facture_id);
+			}
+			foreach ($bbTicketsByFacture as $ticket) {
+				// Rogzitem a bbticketinvoiceprinting tablaba a rekordot
+				$helper->setPrintingInvoiceObject($user, $facture_id, $datetime, $datetime_timestamp, $ticket, $ref);
+			}
+		} else {
+			if(empty($facture_id)){
+				$ticketIds = $helper->getTicketIdsForCrossShopping($ref);
+			}else{
+				$ticketIds = $helper->getTicketIdsByFactureId($facture_id);
+			}
+			$ticketObj = new stdClass();
+			foreach ($ticketIds as $key => $value) {
+				// Rogzitem a bbticketinvoiceprinting tablaba a rekordot
+				$ticketObj->id = $key;
+				$ticketObj->ticket_id = $value;
+				$helper->setPrintingInvoiceObject($user, $facture_id, $datetime, $datetime_timestamp, $ticketObj, $ref);
+			}
+			// e-mail küldése
+			$helper->sendMail($facture_id, $datetime, $now->format('Y-m-d H:i:s'));
+			return 'Multiprinting';
+		}
+	}
+
+	private function getTicketsByFactureId($facture_id)
+	{
+		$bbticket = new BbTicket($this->db);
+		$bbTicketsByFacture = $bbticket->fetchAll('', '', 0, 0, ['customsql' => 'fk_facture = ' . intval($facture_id)]);
+		if ($bbTicketsByFacture < 1) {
+			throw new RestException(404, 'BBTicket not found');
+		}
+		return $bbTicketsByFacture;
+	}
+
+	private function getTicketsByInvoiceNumber($ref)
+	{
+		$bbticket = new BbTicket($this->db);
+		$bbTicketsByFacture = $bbticket->fetchAll('', '', 0, 0, ["customsql" => "invoice_number =  '" . $ref . "'"]);
+		if ($bbTicketsByFacture < 1) {
+			ApiBbusLog::appLog("getTicketsByInvoiceNumber: BBTicket not found");
+			throw new RestException(404, 'BBTicket not found');
+		}
+		ApiBbusLog::appLog("getTicketsByInvoiceNumber: I got it");
+		return $bbTicketsByFacture;
+	}
+
+	private function checkPrintedCopies($facture_id)
+	{
+		$bbticketinvoiceprinting = new BbTicketInvoicePrinting($this->db);
+		$copies = $bbticketinvoiceprinting->fetchAll('ASC', 'rowid', 0, 0, ['customsql' => 'fk_facture = ' . intval($facture_id)]);
+		return (is_array($copies)) ? count($copies) : 0;
+	}
+
+	private function checkPrintedCopiesWithRef($ref)
+	{
+		$bbticketinvoiceprinting = new BbTicketInvoicePrinting($this->db);
+		$copies = $bbticketinvoiceprinting->fetchAll('ASC', 'rowid', 0, 0, ["customsql" => "invoice_number = '" . $ref . "'"]);
+		return (is_array($copies)) ? count($copies) : 0;
+	}
+
+	private function curlDateHandler($ref, $datetime)
+	{
+		$params = compact('ref', 'datetime');
+		$datehandlerPostFields = json_encode($params);
+		ApiBbusLog::appLog("dateHandler: {$datehandlerPostFields}");
+		$this->curlRunner('bbus/printdate', $datehandlerPostFields, 'POST', true);
+	}
+
+	/**
+	 * Generate events	 
+	 *
+	 * @return array|mixed Data without useless information
+	 *
+	 * @url GET /getDatetime
+	 *
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 */
+	public function getDatetime()
+	{
+		$dateArray = [];
+		$now = dol_now();
+		$dateArray = ["timestamp" => $now, "date" => date("Y-m-d", $now), "datetime" => date("Y-m-d H:i:s", $now)];
+		return $dateArray;
+	}
+
+	#-------------------------------------------------
+	#	Egyéb
+	#-------------------------------------------------
+
+	/**
+	 * First ticket validation
+	 * 
+	 * @param  string $code
+	 * 
+	 * @return array|mixed Data without useless information
+	 * 
+	 * @url POST search
+	 */
+
+	public function search($code)
+	{
+		$this->checkUserReadPermission();
+		global $user;
+		$this->code = $code;
+		$this->facture_id = $this->getFactureRowIdByFactureId();
+
+		$sql = "SELECT bt.*, pr.ref as product_ref, pr.label as product_label, btp.printing_date, btp.printing_date_timestamp
+		FROM llx_bbus_bbticketinvoiceprinting as btp
+		INNER JOIN llx_bbus_bbticket AS bt ON bt.rowid = btp.ticket_id
+		INNER JOIN llx_product AS pr ON pr.rowid = bt.ticket_id
+		WHERE btp.fk_facture = {$this->facture_id}
+		  AND btp.printing_date = (
+			SELECT MIN(printing_date)
+			FROM llx_bbus_bbticketinvoiceprinting AS btp2
+			WHERE btp2.fk_facture = {$this->facture_id}
+		)";
+		$ticketsData = $this->db->query($sql);
+		while ($row = pg_fetch_assoc($ticketsData)) {
+			$now = date("Y-m-d H:i:s");
+			if ($row['usage'] == $row['usable_occasions'] && $row['usable_occasions'] != 0) {
+				$this->isValid = false;
+			}
+			if ($row['available_at'] < $now || ((!is_null($row['expire_at']) && !empty($row['expire_at'])) && $row['expire_at'] < $now)) {
+				$this->isValid = false;
+			}
+
+			$row['facture_ref'] = $code;
+			$row['isValid'] = $this->isValid;
+			$sub = [];
+			foreach ($row as $key => $value) {
+				$sub[$key] = $value;
+			}
+			$json[] = $sub;
+		}
+		return $json;
+	}
+
+	/**
+	 * Értékesítések
+	 * 
+	 * @param  date $date	Date
+	 * 
+	 *
+	 * @return array|mixed Data without useless information
+	 *
+	 * @url POST getsaleditems
+	 */
+	public function getsaleditems($date = null)
+	{
+		global $user;
+
+		if (!DolibarrApiAccess::$user->rights->produit->lire) {
+			throw new RestException(403);
+		}
+		//$date = isset($date) ? $date : date("Y-m-d");
+		$date = date("Y-m-d");
+
+		$apiBbusHelper = new ApiBBusHelper();
+		$facturesArray = $apiBbusHelper->getSaledItems($date);
+
+		return $apiBbusHelper->getSaledItemsArray($facturesArray);
+	}
+
+	/** LOG OK
+	 * Get properties of a product object by barcode
+	 *
+	 * Return an array with product information.
+	 *
+	 * @param  string $code	Facture ID and timestamp of printing
+	 *
+	 * @return array|mixed Data without useless information
+	 *
+	 * @url POST ticketinfos
+	 */
+	public function getinfos($code)
+	{
+		global $user;
+		$helper = new ApiBBusHelper();
+		ApiBbusLog::getinfosLog('=== NEW TICKETINFOS ===');
+		ApiBbusLog::getinfosLog("User: {$user->firstname} {$user->lastname} (ID: {$user->id})");
+		ApiBbusLog::getinfosLog("DOLAPIKEY: {$user->api_key}");
+		$ticketsFromBbticketinvoiceprinting = [];
+		$code_and_timstamp = explode('_', $code);
+		$this->code = $code_and_timstamp[0];
+		if (empty($this->code)) {
+			ApiBbusLog::getinfosLog('Code or mac is empty!');
+			ApiBbusLog::getinfosLog('=================');
+			throw new RestException(404, 'Code or mac is empty!');
+		}
+		$this->printingTime = $code_and_timstamp[1];
+		if (empty($this->printingTime)) {
+			ApiBbusLog::getinfosLog('Timestamp is empty!');
+			ApiBbusLog::getinfosLog('=================');
+			throw new RestException(404, 'Timestamp is empty!');
+		}
+		if (!DolibarrApiAccess::$user->rights->bbus->ticket->validation) {
+			ApiBbusLog::getinfosLog('No access: Ticket Validation!');
+			ApiBbusLog::getinfosLog('=================');
+			throw new RestException(403);
+		}
+		ApiBbusLog::getinfosLog("Code: {$this->code}");
+		ApiBbusLog::getinfosLog("Code: {$this->printingTime}");
+
+		//Kikeresem a facture_id-t
+		$facture = new Facture($this->db);
+		$sql = "SELECT * FROM " . $this->db->prefix() . $facture->table_element . " WHERE ref ILIKE '{$this->code}'";
+		$factureResult = $this->db->query($sql);
+		if (pg_num_rows($factureResult) < 1) {
+			ApiBbusLog::getinfosLog('Invoice not found!');
+			ApiBbusLog::getinfosLog('=================');
+			throw new RestException(404, 'Invoice not found!');
+		}
+		while ($row = pg_fetch_assoc($factureResult)) {
+			$selectedFacture_id = $row['rowid'];
+		}
+
+		ApiBbusLog::getinfosLog('Facture ID: ' . (string) $selectedFacture_id);
+
+
+		$sql = "SELECT rowid FROM " . $this->db->prefix() . $facture->table_element . " WHERE fk_facture_source = {$selectedFacture_id} AND type = 2";
+		$res = $this->db->query($sql);
+		if (pg_num_rows($res) > 0) {
+			ApiBbusLog::getinfosLog('Invoice has a Credit account!');
+			ApiBbusLog::getinfosLog('=================');
+			throw new RestException(404, 'Invoice has a Credit account.');
+		}
+
+		// Kikeresem az llx_bbus_bbticketinvoiceprinting táblából a timestamp-hez tartozo jegy Id(ka)t
+		$bbticketinvoiceprinting = new BbTicketInvoicePrinting($this->db);
+		$result = $bbticketinvoiceprinting->fetchAll('', '', 0, 0, ['customsql' => "printing_date_timestamp = '{$this->printingTime}' AND fk_facture = {$selectedFacture_id}"]);
+		$helper->checkResult($result, 'bbticketinvoiceprinting');
+		foreach ($result as $device) {
+			$ticketsFromBbticketinvoiceprinting[] = $device->ticket_id;
+		}
+		$inString = implode(', ', $ticketsFromBbticketinvoiceprinting);
+		//echo $inString;exit;
+		// A ticket_id alapjan, hogy a jegyek kozott szerepel-e es ervenyes-e
+		$bbticket = new BbTicket($this->db);
+		$sql = "SELECT tic.rowid as ticketrowid, tic.*, bbd.*, fac.* FROM " . $this->db->prefix() . $bbticket->table_element . " AS tic
+		INNER JOIN public.llx_product as bbd ON tic.ticket_id = bbd.rowid
+		INNER JOIN public.llx_facture as fac ON tic.fk_facture = fac.rowid
+		WHERE tic.rowid IN ({$inString})";
+		$statement = $this->db->query($sql);
+		while ($row = pg_fetch_assoc($statement)) {
+			$sub = [];
+			foreach ($row as $key => $value) {
+				$sub[$key] = $value;
+			}
+			$json[] = $sub;
+		}
+		ApiBbusLog::getinfosLog('Status: OK!');
+		ApiBbusLog::getinfosLog('=====================');
+		return $json;
+	}
+
+	/**
+	 * Get properties of a product object by barcode
+	 *
+	 * Return an array with product information.
+	 *
+	 *
+	 * @return array|mixed Data without useless information
+	 *
+	 * @url GET product
+	 */
+	public function getProducts()
+	{
+		global $user;
+		$productsArray = [];
+		$products = new Product($this->db);
+		$sql = "SELECT price, default_vat_code, rowid FROM " . $this->db->prefix() . $products->table_element . " WHERE fk_product_type = 1 ORDER BY rowid ASC";
+		$res = $this->db->query($sql);
+		while ($row = pg_fetch_assoc($res)) {
+			$productsArray[$row['rowid']] = $row;
+		}
+		return 'OK';
+	}
+
+	/** LOG OK
+	 * Validate a product bbticket id.
+	 *
+	 * Return an array with product validation data.
+	 *
+	 * @param  string $code	Facture ID and timestamp of printing
+	 * @param  string $ticketid	Ticketid (bbticket rowid)
+	 * @param  string $imei	IMEI (Mobile IMEI number)
+	 *
+	 * @return array|mixed Data without useless information
+	 *
+	 * @url POST ticketvalidationbyphone
+	 */
+	public function validateTicket($code, $ticketid, $imei)
+	{
+		global $user;
+		$logId = ApiBbusLog::getLogId();
+		ApiBbusLog::ticketvalidationByPhone("{$logId} === NEW TICKETVALIDATION BY PHONE ===");
+		ApiBbusLog::ticketvalidationByPhone("{$logId} User: {$user->firstname} {$user->lastname} (ID: {$user->id})");
+		ApiBbusLog::ticketvalidationByPhone("{$logId} DOLAPIKEY: {$user->api_key}");
+		ApiBbusLog::ticketvalidationByPhone("{$logId} code: {$code}");
+		$ticketChecker = new TicketChecker();
+
+
+		$ticketChecker->setCode($code);
+		$ticketChecker->setTimestamp($code, 'ticketvalidationByPhone', $logId);
+		$ticketChecker->SetTicketidFromPhone($ticketid, $logId);
+		$ticketChecker->setImei($imei, $logId);
+
+		$this->checkUserValidatePermission();
+
+		$ticketChecker->setFactureId('ticketvalidationByPhone', $logId);
+		$ticketChecker->checkBbTicketInvoicePrinting();
+		$selectedTicket = $ticketChecker->getDataOfTheSelectedTicket();
+
+		$ticketChecker->setBbTicketRowId($selectedTicket->id);
+
+		$ticketChecker->checkExceptionHandlers($selectedTicket, 'ticketvalidationByPhone', $logId);
+
+		$ticketChecker->setMergedTickets($selectedTicket, 'ticketvalidationByPhone', $logId);
+
+		$ticketChecker->saveData($selectedTicket);
+		ApiBbusLog::ticketvalidationByPhone("{$logId} Status: OK!");
+		ApiBbusLog::ticketvalidationByPhone("{$logId} =================");
+		return 'OK';
+	}
+
+	/** LOG OK
+	 * Get properties of a product object by barcode
+	 *
+	 * Return an array with product information.
+	 *
+	 * @param  string $code	Facture ID and timestamp of printing
+	 * @param  string $mac	MAC adress of the device
+	 *
+	 * @return array|mixed Data without useless information
+	 *
+	 * @url POST barcode
+	 *
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 */
+
+	public function getByBarcode(string $code, string $mac, string $lat = null, string $lon = null)
+	{
+		global $user;
+		$logId = ApiBbusLog::getLogId();
+		ApiBbusLog::getByBarcode("{$logId} === NEW OBU validation ===");
+		ApiBbusLog::getByBarcode("{$logId} User: {$user->firstname} {$user->lastname} (ID: {$user->id})");
+		ApiBbusLog::getByBarcode("{$logId} DOLAPIKEY: {$user->api_key}");
+		$bbticket = new BbTicket($this->db);
+		$ticketChecker = new TicketChecker();
+
+		if (empty($code) || empty($mac)) {
+			ApiBbusLog::getByBarcode("{$logId} Code or mac is empty!");
+			throw new RestException(404, 'Code or mac is empty!');
+		}
+
+		ApiBbusLog::getByBarcode("{$logId} Code: " . $code);
+		ApiBbusLog::getByBarcode("{$logId} MAC: " . $mac);
+		ApiBbusLog::getByBarcode("{$logId} Latitude: " . $lat);
+		ApiBbusLog::getByBarcode("{$logId} Longitude: " . $lon);
+		$ticketChecker->setMac($mac);
+		$ticketChecker->setCode($code);
+		$ticketChecker->setLat($lat);
+		$ticketChecker->setLon($lon);
+		$ticketChecker->setTimestamp($code, 'getByBarcode', $logId);
+
+		$this->checkUserReadPermission();
+
+		$ticketChecker->setFactureId('getByBarcode', $logId);
+		$ticketChecker->setFilsArray();
+
+		$ticketChecker->setTicketId($logId);
+		$selectedTicket = $ticketChecker->getDataOfTheSelectedTicket();
+
+		$ticketChecker->setBbTicketRowId($selectedTicket->id);
+		$ticketChecker->check5Minutes($logId);
+		$ticketChecker->checkExceptionHandlers($selectedTicket, 'getByBarcode', $logId);
+
+		$ticketChecker->setMergedTickets($selectedTicket, 'getByBarcode', $logId);
+		$ticketChecker->saveCoordinates($logId);
+		$ticketChecker->saveData($selectedTicket);
+
+		ApiBbusLog::getByBarcode("{$logId} Status: OK!");
+		ApiBbusLog::getByBarcode("{$logId} =================");
+		return 'OK';
+	}
+
+	/**
+	 * Save customer data (zip or countrycode) in facture table
+	 *
+	 * Return a result.
+	 *
+	 * @param  string $facture_id	Facture rowid
+	 * @param  string $customerdatazip	Customer data ZIP
+	 * @param  string $customerdatacountrycode	Customer data Countrycode
+	 *
+	 * @return array|mixed Data without useless information
+	 *
+	 * @url POST customerDataHandler
+	 *
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 */
+	public function customerDataHandler(string $facture_id, string $customerdatazip = null, string $customerdatacountrycode = null)
+	{
+		$helper = new ApiBBusHelper();
+		if (is_null($customerdatazip) && is_null($customerdatacountrycode)) {
+			dol_syslog("Nem sikerult a facture updateje. Ref: " . $this->facture_id, LOG_DEBUG | LOG_INFO | LOG_WARNING | LOG_ERR);
+			throw new RestException(404, 'Empty params');
+		}
+		$sql = "UPDATE " . $this->db->prefix() . "facture_extrafields SET ";
+		$sql .= !is_null($customerdatazip) ? "customer_data_zip = '{$customerdatazip}' " : "customer_data_countrycode = '{$customerdatacountrycode}' ";
+		$sql .= "WHERE fk_object = '{$facture_id}'";
+		$helper->factureUpdate($sql, $facture_id);
+		return "OK";
+	}
+
+	/**
+	 * Create naplo for userlogout
+	 *
+	 * Return a result.
+	 *
+	 * @return array|mixed Data without useless information
+	 *
+	 * @url POST logoutNaploCreator
+	 *
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 */
+	public function logoutNaploCreator()
+	{
+		global $user, $db;
+		$error = 0;
+		$db->begin();
+		$helper = new ApiBBusHelper();
+
+		$group_user_id = $helper->getGroupUserIdByUserId($user->id);
+		if ($group_user_id == -1) {
+			$error++;
+		}
+		$result = $helper->isLastStatusLogout($user);
+		if ($result !== '') {
+			return date('Y-m-d H:i:s', $result);
+		}
+
+		$userLoginNaplo = new UserLoginNaplo($db);
+		$userLoginNaplo->login_logout_status = 1;
+		$userLoginNaplo->user_id = $user->id;
+		$result = $userLoginNaplo->create($user);
+		if ($result < 0) {
+			$error++;
+		}
+
+		$userNaploObj = new UserNaplo($db);
+		$userNaploObj->user_id = $user->id;
+		$userNaploObj->group_user_id = $group_user_id;
+		$userNaploObj->status = 0;
+		$result = $userNaploObj->create($user);
+		if ($result < 0) {
+			$error++;
+		}
+
+		$groupUsersObj = new GroupUsers($db);
+		$sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . "settlements_groupusers WHERE fk_user = {$user->id}";
+		$data = $db->query($sql);
+		$dataArray = pg_fetch_assoc($data);
+		$selectedGroupId = $dataArray['rowid'];
+		if (isset($selectedGroupId)) {
+			$resultKickOff = $groupUsersObj->deleteLine($user, $selectedGroupId);
+			if (!$resultKickOff) {
+				$error++;
+			}
+		}
+
+		if ($error) {
+			$db->rollback();
+		}
+		$db->commit();
+		return 'OK';
+	}
+
+	/**
+	 * Get the discount unit From Product
+	 *
+	 * Return a result.
+	 *
+	 * @param  int $product_id			Product rowid
+	 * 
+	 * @return string discount unit
+	 *
+	 * @url POST getDiscountUnit
+	 *
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 */
+	public function getDiscountUnit(int $product_id)
+	{
+		global $db, $user;
+		$ProductObj = new Product($db);
+		$ProductObj->fetch($product_id);
+		return $ProductObj->array_options['discount_period'];
+	}
+
+	/**
+	 * Create naplo for Discount
+	 *
+	 * Return a result.
+	 *
+	 * @param  int $facture_id			Facture rowid
+	 * @param  int $product_id			Product rowid
+	 * @param  int $discount			Discount
+	 * @param  string $discount_unit	Discount unit
+	 * 
+	 * @return array|mixed Data without useless information
+	 *
+	 * @url POST createDiscountNaplo
+	 *
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 */
+	public function createDiscountNaplo(int $facture_id, int $product_id, int $discount, string $discount_unit)
+	{
+		global $db, $user;
+		$discountNaploObj = new DiscountNaplo($db);
+		$discountNaploObj->fk_facture = $facture_id;
+		$discountNaploObj->fk_product = $product_id;
+		$discountNaploObj->discount = $discount;
+		$discountNaploObj->discount_unit = $discount_unit;
+		$result = $discountNaploObj->create($user);
+		if ($result < 0) {
+			return false;
+		}
+		return $result;
+	}
+
+	/**
+	 * Get the products from Product
+	 *
+	 * Return a result.
+	 *
+	 * @param  int $entity	entity
+	 * 
+	 * @return array|mixed Data without useless information
+	 *
+	 * @url POST getHotelSalesProducts
+	 *
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 */
+	public function getHotelSalesProducts(int $entity)
+	{
+		global $db, $user;
+		$sql = "SELECT p.* FROM public.llx_product AS p 
+		INNER JOIN public.llx_product_extrafields as pe ON pe.fk_object = p.rowid
+		WHERE p.entity = {$entity} AND pe.hotelsales = 1";
+		$products = $this->db->query($sql);
+		$row = pg_fetch_all($products);
+		return $row;
+	}
+
+	private function checkUserReadPermission()
+	{
+		if (!DolibarrApiAccess::$user->rights->produit->lire) {
+			throw new RestException(403);
+		}
+	}
+
+	private function checkUserValidatePermission()
+	{
+		if (!DolibarrApiAccess::$user->rights->bbus->ticket->validation) {
+			throw new RestException(403);
+		}
+	}
+
+	private function getFactureRowIdByFactureId()
+	{
+		$facture = new Facture($this->db);
+		$sqlFacture = "SELECT rowid FROM public.llx_facture WHERE ref ILIKE '%{$this->code}%'";
+		$result = $this->db->query($sqlFacture);
+		if (pg_num_rows($result) > 0) {
+			while ($adatok = pg_fetch_assoc($result)) {
+				$factureRowid = $adatok['rowid'];
+			}
+		} else {
+			throw new RestException(404, 'Invoice not found.');
+		}
+		$sql = "SELECT rowid FROM llx_facture WHERE fk_facture_source = {$factureRowid} AND type = 2";
+		$res = $this->db->query($sql);
+		if (pg_num_rows($res) > 0) {
+			throw new RestException(404, 'Invoice has a Credit account.');
+		}
+
+		return $factureRowid;
+	}
+
+	private function saveBbTicketNaplo($user)
+	{
+		$bbTicketNaplo = new BbTicketNaplo($this->db);
+		$bbTicketNaplo->ticket_row_id = $this->bbTicketRowId;
+		$bbTicketNaplo->bbservicelocation_id = isset($this->service_location_id) ? $this->service_location_id : null;
+		$bbTicketNaplo->device_id = isset($this->device_id) ? $this->device_id : null;
+		$bbTicketNaplo->status = $this->status;
+
+
+		if ($bbTicketNaplo->create($user) < 0) {
+			dol_syslog('Nem sikerult menteni a bbticketNaplo tablaba a rekordot.', LOG_DEBUG | LOG_INFO | LOG_WARNING | LOG_ERR);
+			throw new RestException(500, 'Nem sikerult menteni a bbticketNaplo tablaba a rekordot.');
+		}
+	}
+
+	/**
+	 * @param  string $ref	Facture ref
+	 *
+	 * @return array|mixed Data without useless information
+	 *
+	 * @url POST getTicketsByFactureInvoiceNumber
+	 *
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 */
+	public function getTicketsByFactureInvoiceNumber($ref)
+	{
+		$bbTicketsByFacture = [];
+		$sql = "SELECT rowid as id, ticket_id FROM llx_bbus_bbticket WHERE invoice_number ILIKE '%{$ref}%'";
+		$result = $this->db->query($sql);
+		if ($this->db->num_rows($result) > 0) {
+			while ($row = $this->db->fetch_object($result)) {
+				$bbTicketsByFacture[] = $row;
+			}
+		} else {
+			throw new RestException(404, 'BBTicket not found');
+		}
+		return $bbTicketsByFacture;
+	}
+
+	/**
+	 * @param  string $facture_id	Facture ref
+	 *
+	 * @return array|mixed Data without useless information
+	 *
+	 * @url POST getRefFromFacture
+	 *
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 */
+	public function getRefFromFacture(string $facture_id)
+	{
+		global $user;
+		$facture = new Facture($this->db);
+		$sql = "SELECT ref FROM " . $this->db->prefix() . $facture->table_element . " WHERE rowid = " . $facture_id;
+		$res = $this->db->query($sql);
+		while ($adatok = pg_fetch_assoc($res)) {
+			print_r($adatok['ref']);
+			exit;
+		}
+
+		return 'OK';
+	}
+
+	/**
+	 * Get properties of a product object by barcode
+	 *
+	 * Return an array with product information.
+	 *
+	 * @param  string $barcode            Barcode of element
+	 * @param  int    $includestockdata   Load also information about stock (slower)
+	 * @param  bool   $includesubproducts Load information about subproducts
+	 * @param  bool   $includeparentid    Load also ID of parent product (if product is a variant of a parent product)
+	 * @param  bool   $includetrans		  Load also the translations of product label and description
+	 *
+	 * @return array|mixed Data without useless information
+	 *
+	 * @url GET profileimage/{id}
+	 *
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 */
+	public function getUserImage(int $id)
+	{
+		$image = null;
+		$dolApiKey = $_SERVER['HTTP_DOLAPIKEY'] ?? null;
+
+		if (empty($dolApiKey)) {
+			throw new RestException(401, 'Access not allowed');
+		}
+
+		$user = (new ApiUserHandler)->getUser($id, $dolApiKey);
+		if (empty($user->photo)) {
+			throw new RestException(404, 'Photo not found');
+		} else {
+			$originalFile = get_exdir(0, 0, 0, 0, $user, 'user') . 'photos/' . $user->photo;
+			$originalFile = DOL_DOCUMENT_ROOT . '/documents/users/' . $originalFile;
+
+			$filename = basename($originalFile);
+			$originalFileOsEncoded = dol_osencode($originalFile); // New file name encoded in OS encoding charset
+
+			$fileContent = file_get_contents($originalFileOsEncoded);
+			$image = [
+				'filename' => $filename,
+				'content-type' => dol_mimetype($filename),
+				'filesize' => filesize($originalFile),
+				'content' => base64_encode($fileContent),
+				'encoding' => 'base64'
+			];
+		}
+
+		return $image;
+	}
+
+	/**
+	 * Create invoices from the order
+	 *
+	 * @param int $id_order   		(Row)Id of the order
+	 * @param int $id_soc  			Id of 3th party
+	 * @return array                Response							  
+
+	 * @url POST invoicesFromOrder
+
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 */
+	public function invoicesFromOrder(int $id_order, int $id_soc = 0): array
+	{
+		// echo ini_get('memory_limit');
+		// exit;
+		if (!DolibarrApiAccess::$user->rights->facture->creer) {
+			throw new RestException(401, "Insuffisant rights");
+		}
+
+		require_once DOL_DOCUMENT_ROOT . '/compta/facture/class/facture.class.php';
+		// Standard or deposit invoice, not from a Predefined template invoice
+		// Si facture standard
+		$this->now = time();
+		$this->invoices = [];
+
+		$element == 'order';
+		$element = $subelement = 'commande';
+
+		//etalon obj:
+
+		$object = new Facture($this->db);
+		// $extrafields = new ExtraFields($db);
+		$defaultDevSocId = 1;
+		$this->defaultSocId = (isset($_ENV['DEFAULT_SOCID']) && (int) $_ENV['DEFAULT_SOCID']) ? (int) $_ENV['DEFAULT_SOCID'] : $defaultDevSocId;
+
+		$object->socid = (empty((int) $id_soc)) ? $this->defaultSocId : (int) $id_soc;
+		$object->type = null;
+		$object->ref = null;
+		$object->date = $this->now;
+		// $object->date_pointoftax 	= $date_pointoftax;
+		// $object->note_public		= trim(GETPOST('note_public', 'restricthtml'));
+		// $object->note_private = trim(GETPOST('note_private', 'restricthtml'));
+		// $object->ref_client			= GETPOST('ref_client');
+		$object->model_pdf = null;
+		// $object->fk_project			= GETPOST('projectid', 'int');
+		// $object->cond_reglement_id	= (GETPOST('type') == 3 ? 1 : GETPOST('cond_reglement_id'));
+		// $object->mode_reglement_id	= GETPOST('mode_reglement_id');
+		// $object->fk_account = GETPOST('fk_account', 'int');
+		// $object->amount = price2num(GETPOST('amount'));
+		// $object->remise_absolue		= price2num(GETPOST('remise_absolue'), 'MU');
+		// $object->remise_percent		= price2num(GETPOST('remise_percent'), '', 2);
+		// $object->fk_incoterms = GETPOST('incoterm_id', 'int');
+		// $object->location_incoterms = GETPOST('location_incoterms', 'alpha');
+		// $object->multicurrency_code = GETPOST('multicurrency_code', 'alpha');
+		// $object->multicurrency_tx   = GETPOST('originmulticurrency_tx', 'int');
+
+		$object->fetch_thirdparty();
+
+		// If creation from another object of another module (Example: origin=propal, originid=1)
+
+		$object->origin = $element;
+		$object->origin_id = $id_order;
+
+		// Possibility to add external linked objects with hooks
+		$object->linked_objects[$object->origin] = $object->origin_id;
+		// link with order if it is a shipping invoice
+
+		if (is_array($_POST['other_linked_objects']) && !empty($_POST['other_linked_objects'])) {
+			$object->linked_objects = array_merge($object->linked_objects, $_POST['other_linked_objects']);
+		}
+
+		//
+		dol_include_once('/' . $element . '/class/' . $subelement . '.class.php');
+		$classname = ucfirst($subelement);
+		$srcobject = new $classname($this->db);
+
+		$result = $srcobject->fetch($id_order);
+		if ($result > 0) {
+			$lines = $srcobject->lines;
+			if (empty($lines) && method_exists($srcobject, 'fetch_lines')) {
+				$srcobject->fetch_lines();
+				$lines = $srcobject->lines;
+			}
+			/*
+																																																													  // If we create a standard invoice with a percent, we change amount by changing the qty
+																																																													  if (is_array($lines)) {
+																																																													  foreach ($lines as $line) {
+																																																													  // We keep ->subprice and ->pa_ht, but we change the qty
+																																																													  $line->qty = price2num($line->qty * $valuestandardinvoice / 100, 'MS');
+																																																													  }
+																																																													  }
+																																																													  */
+			$fk_parent_line = 0;
+			$num = count($lines);
+			$invNum = 0;
+
+			for ($i = 0; $i < $num; $i++) {
+				// if (!in_array($lines[$i]->id, $selectedLines)) {
+				// continue; // Skip unselected lines
+				// }
+
+				//!!! create invoice by line:
+				$quantity = $lines[$i]->qty;
+				for ($j = 0; $j < $quantity; $j++) {
+					$this->invoices[$invNum] = clone $object;
+					if ($id = $this->invoices[$invNum]->create(DolibarrApiAccess::$user)) {
+						$this->invoices[$invNum]->update_price(1, 'auto', 0, $mysoc);
+
+						$label = (!empty($lines[$i]->label) ? $lines[$i]->label : '');
+						$desc = (!empty($lines[$i]->desc) ? $lines[$i]->desc : $lines[$i]->libelle);
+						if ($this->invoices[$invNum]->situation_counter == 1) {
+							$lines[$i]->situation_percent = 0;
+						}
+
+						if ($lines[$i]->subprice < 0 && empty($conf->global->INVOICE_KEEP_DISCOUNT_LINES_AS_IN_ORIGIN)) {
+							// Negative line, we create a discount line
+							require_once DOL_DOCUMENT_ROOT . '/core/class/discount.class.php';
+							$discount = new DiscountAbsolute($db);
+							$discount->fk_soc = $this->invoices[$invNum]->socid;
+							$discount->amount_ht = abs($lines[$i]->total_ht);
+							$discount->amount_tva = abs($lines[$i]->total_tva);
+							$discount->amount_ttc = abs($lines[$i]->total_ttc);
+							$discount->tva_tx = $lines[$i]->tva_tx;
+							$discount->fk_user = DolibarrApiAccess::$user->id;
+							$discount->description = $desc;
+							$discount->multicurrency_subprice = abs($lines[$i]->multicurrency_subprice);
+							$discount->multicurrency_amount_ht = abs($lines[$i]->multicurrency_total_ht);
+							$discount->multicurrency_amount_tva = abs($lines[$i]->multicurrency_total_tva);
+							$discount->multicurrency_amount_ttc = abs($lines[$i]->multicurrency_total_ttc);
+
+							$discountid = $discount->create(DolibarrApiAccess::$user);
+							if ($discountid > 0) {
+								$result = $this->invoices[$invNum]->insert_discount($discountid); // This include link_to_invoice
+							} else {
+								setEventMessages($discount->error, $discount->errors, 'errors');
+								$error++;
+								break;
+							}
+						} else {
+							// Positive line
+							$product_type = ($lines[$i]->product_type ? $lines[$i]->product_type : 0);
+
+							// Date start
+							$date_start = false;
+							if ($lines[$i]->date_debut_prevue) {
+								$date_start = $lines[$i]->date_debut_prevue;
+							}
+							if ($lines[$i]->date_debut_reel) {
+								$date_start = $lines[$i]->date_debut_reel;
+							}
+							if ($lines[$i]->date_start) {
+								$date_start = $lines[$i]->date_start;
+							}
+
+							// Date end
+							$date_end = false;
+							if ($lines[$i]->date_fin_prevue) {
+								$date_end = $lines[$i]->date_fin_prevue;
+							}
+							if ($lines[$i]->date_fin_reel) {
+								$date_end = $lines[$i]->date_fin_reel;
+							}
+							if ($lines[$i]->date_end) {
+								$date_end = $lines[$i]->date_end;
+							}
+
+							// Reset fk_parent_line for no child products and special product
+							if (($lines[$i]->product_type != 9 && empty($lines[$i]->fk_parent_line)) || $lines[$i]->product_type == 9) {
+								$fk_parent_line = 0;
+							}
+
+							// Extrafields
+							if (method_exists($lines[$i], 'fetch_optionals')) {
+								$lines[$i]->fetch_optionals();
+								$array_options = $lines[$i]->array_options;
+							}
+
+							$tva_tx = $lines[$i]->tva_tx;
+							if (!empty($lines[$i]->vat_src_code) && !preg_match('/\(/', $tva_tx)) {
+								$tva_tx .= ' (' . $lines[$i]->vat_src_code . ')';
+							}
+
+							// View third's localtaxes for NOW and do not use value from origin.
+							// TODO Is this really what we want ? Yes if source is template invoice but what if proposal or order ?
+							$localtax1_tx = get_localtax($tva_tx, 1, $this->invoices[$invNum]->thirdparty);
+							$localtax2_tx = get_localtax($tva_tx, 2, $this->invoices[$invNum]->thirdparty);
+
+							$result = $this->invoices[$invNum]->addline(
+								$desc,
+								$lines[$i]->subprice,
+								// $lines[$i]->qty,
+								1,
+								$tva_tx,
+								$localtax1_tx,
+								$localtax2_tx,
+								$lines[$i]->fk_product,
+								$lines[$i]->remise_percent,
+								$date_start,
+								$date_end,
+								0,
+								$lines[$i]->info_bits,
+								$lines[$i]->fk_remise_except,
+								'HT',
+								0,
+								$product_type,
+								$lines[$i]->rang,
+								$lines[$i]->special_code,
+								$this->invoices[$invNum]->origin,
+								$lines[$i]->rowid,
+								$fk_parent_line,
+								$lines[$i]->fk_fournprice,
+								$lines[$i]->pa_ht,
+								$label,
+								$array_options,
+								$lines[$i]->situation_percent,
+								$lines[$i]->fk_prev_id,
+								$lines[$i]->fk_unit,
+								0,
+								'',
+								1
+							);
+
+							if ($result > 0) {
+								$lineid = $result;
+							} else {
+								$lineid = 0;
+								$error++;
+								break;
+							}
+
+							// Defined the new fk_parent_line
+							if ($result > 0 && $lines[$i]->product_type == 9) {
+								$fk_parent_line = $result;
+							}
+						}
+					}
+
+					$invNum++;
+				}
+			}
+		} else {
+			setEventMessages($srcobject->error, $srcobject->errors, 'errors');
+			$error++;
+		}
+		$num = count($this->invoices);
+		$return = [
+			'validated' => []
+		];
+		// $used_mem = round(memory_get_usage(false) / 1024 / 1024); //mb
+		// $used_mem = round(memory_get_usage(true) / 1024 / 1024); //mb
+		// echo $used_mem. " / ".$allowed_mem; 
+		// echo ini_get('memory_limit');
+		// exit;
+
+		for ($i = 0; $i < $num; $i++) {
+
+			//!!! validation
+			// $idwarehouse = 0;
+			// $notrigger = 0;
+			// $result = $this->invoices[$i]->validate(DolibarrApiAccess::$user, '', $idwarehouse, $notrigger);
+			$id = $this->invoices[$i]->id;
+			$result = $this->invoices[$i]->validate(DolibarrApiAccess::$user, '', null);
+			if ($result >= 0) {
+				// Define output language
+				if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE)) {
+					global $langs;
+					$outputlangs = $langs;
+					$newlang = '';
+					if (!empty($conf->global->MAIN_MULTILANGS) && empty($newlang) && GETPOST('lang_id', 'aZ09')) {
+						$newlang = GETPOST('lang_id', 'aZ09');
+					}
+					if (!empty($conf->global->MAIN_MULTILANGS) && empty($newlang)) {
+						$newlang = $this->invoices[$i]->thirdparty->default_lang;
+					}
+					if (!empty($newlang)) {
+						$outputlangs = new Translate("", $conf);
+						$outputlangs->setDefaultLang($newlang);
+						$outputlangs->load('products');
+					}
+					$model = $this->invoices[$i]->model_pdf;
+
+					$ret = $this->invoices[$i]->fetch($id); // Reload to get new records
+					// PDF
+					$hidedetails = (!empty($conf->global->MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS)) ? 1 : 0;
+					$hidedesc = (!empty($conf->global->MAIN_GENERATE_DOCUMENTS_HIDE_DESC)) ? 1 : 0;
+					$hideref = (!empty($conf->global->MAIN_GENERATE_DOCUMENTS_HIDE_REF)) ? 1 : 0;
+					$result = $this->invoices[$i]->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
+					if ($result < 0) {
+						//...
+						// setEventMessages($this->invoices[$i]->error, $this->invoices[$i]->errors, 'errors');
+					}
+				}
+			} else {
+				// if (count($this->invoices[$i]->errors)) {
+				// setEventMessages(null, $this->invoices[$i]->errors, 'errors');
+				// } else {
+				// setEventMessages($this->invoices[$i]->error, $this->invoices[$i]->errors, 'errors');
+				// }
+			}
+
+			// if ($result == 0) {
+			// throw new RestException(304, 'Error nothing done. May be object is already validated');
+			// }
+			// if ($result < 0) {
+			// throw new RestException(500, 'Error when validating Invoice: '.$this->invoice->error);
+			// }
+
+			// $this->invoices[$i] = $this->invoicees[$i]->fetch($id);
+			if (!$this->invoices[$i]) {
+				throw new RestException(404, 'Invoice not found');
+			}
+
+			$this->invoices[$i] = parent::_cleanObjectDatas($this->invoices[$i]);
+			unset($this->invoices[$i]->note);
+			unset($this->invoices[$i]->address);
+			unset($this->invoices[$i]->barcode_type);
+			unset($this->invoices[$i]->barcode_type_code);
+			unset($this->invoices[$i]->barcode_type_label);
+			unset($this->invoices[$i]->barcode_type_coder);
+			unset($this->invoices[$i]->canvas);
+
+
+			if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
+				throw new RestException(401, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
+			}
+
+			$return['validated'][] = $this->invoices[$i]->ref;
+		}
+		return $return;
+	}
+
+	/**
+	 * Jegy generálás -> (order?->) invoice -> payment -> jegy -> qr on the bill
+	 *
+	 * Return an array with prcess information.
+	 *
+	 * @param  array $orderDetails			Order details
+	 *
+	 * @return array|mixed Data without useless information
+	 *
+	 * @url POST order
+	 *
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 */
+	public function jegy(array $orderDetails)
+	{
+		$return = [
+			"request" => $orderDetails
+		];
+		$Jegy = new Jegy();
+		$return['hash'] = [
+			$Jegy->generate_hash(),
+			$Jegy->generate_hash(1),
+			$Jegy->generate_hash(20),
+			$Jegy->generate_hash(50),
+			$Jegy->generate_hash(100),
+		];
+
+		return $return;
+	}
+
+	/**
+	 * Get groupId for REACT
+	 *
+	 * Return an ID.
+	 *
+	 * @return integer Data without useless information
+	 *
+	 * @url GET getGroupId
+	 *
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 */
+	public function getGroupId(): array
+	{
+		global $user, $db;
+		$sql = "SELECT fk_settlements_group FROM public.llx_settlements_groupusers WHERE fk_user = {$user->id} ORDER BY rowid DESC LIMIT 1";
+		$result = $db->query($sql);
+		while ($row = pg_fetch_assoc($result)) {
+			return $row;
+		}
+		return [];
+	}
+
+	/**
+	 * Insert a row into login log table
+	 *
+	 * @param  string $login		Login name
+	 *
+	 * @return array|mixed Data without useless information
+	 *
+	 * @url POST savelogin
+	 *
+	 * @throws RestException 404
+	 */
+	public function saveLogin(string $login)
+	{
+		$error = 0;
+		$this->db->begin();
+		$user = new User($this->db);
+		//$user->fetch(0, $login, 0, 0, 1);
+		$user->fetch(0, $login, 0, 0);
+		if (empty($user->id)) {
+			throw new RestException(404, 'User not found');
+		}
+
+		$log = new BbLoginLog($this->db);
+		$log->entity = 1;
+		$log->description = 'Login from App';
+		$id = $log->create($user);
+		if ($id < 1) {
+			$error++;
+			throw new RestException(500, 'Log failure');
+		}
+
+		$now = dol_now();
+
+		$userremoteip = getUserRemoteIP();
+
+		$sql = "UPDATE " . $this->db->prefix() . "user SET";
+		$sql .= " datepreviouslogin = datelastlogin,";
+		$sql .= " ippreviouslogin = iplastlogin,";
+		$sql .= " datelastlogin = '" . $this->db->idate($now) . "',";
+		$sql .= " iplastlogin = '" . $this->db->escape($userremoteip) . "',";
+		$sql .= " tms = tms"; // La date de derniere modif doit changer sauf pour la mise a jour de date de derniere connexion
+		$sql .= " WHERE rowid = " . ((int) $user->id);
+		$resql = $this->db->query($sql);
+		if (!$resql) {
+			$error++;
+			$this->error = $this->db->lasterror() . ' sql=' . $sql;
+		}
+		if (!$error) {
+			$this->db->commit();
+			return 'OK';
+		} else {
+			$this->db->rollback();
+		}
+	}
+
+	/**
+	 * Receive GPS coordinates of the device
+	 *
+	 * @param string  $licplate	License plate of the vehicle
+	 * @param string  $lat	GPS latitude coordinate
+	 * @param string  $lon	GPS longitude coordinate
+	 *
+	 * @return string|mixed Data without useless information
+	 *
+	 * @url GET /gps_coords
+	 *
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 * @throws RestException 500
+	 */
+	public function gpsCoords(string $licplate, string $lat, string $lon): string
+	{
+		if (!(new GpsPosition)->save($licplate, $lat, $lon)) {
+			throw new RestException(500, 'Save failure');
+		}
+
+		return 'OK';
+	}
+
+	/**
+	 * Receive GPS coordinates of the device
+	 *
+	 * @param string  $licplate	License plate of the vehicle
+	 * @param string  $lat	GPS latitude coordinate
+	 * @param string  $lon	GPS longitude coordinate
+	 *
+	 * @return string|mixed Data without useless information
+	 *
+	 * @url POST /gps_position
+	 *
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 * @throws RestException 500
+	 */
+	public function gpsPosition(string $licplate, string $lat, string $lon): string
+	{
+		if (!(new GpsPosition)->save($licplate, $lat, $lon)) {
+			throw new RestException(500, 'Save failure');
+		}
+
+		return 'OK';
+	}
+
+	/**
+	 * List of countries
+	 *
+	 * Return an array with country name and code.
+	 *
+	 * @param string $lang
+	 * @param string $search
+	 * 
+	 * @return array|mixed Data without useless information
+	 *
+	 * @url GET /countries
+	 *
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 */
+	public function countries(string $lang = 'hu', string $search = ''): array
+	{
+		return (new CountryHandler)->getCountries($lang, $search);
+	}
+
+	/**
+	 * List of entities
+	 *
+	 * Return an array with companies data.
+	 *
+	 * @return array|mixed Data without useless information
+	 *
+	 * @url GET /entities
+	 *
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 */
+	public function entities(): array
+	{
+		return (new EntityHandler)->getEntities();
+	}
+
+	/**
+	 * List of assets received
+	 *
+	 * Return an array with assets received.
+	 *
+	 * @return array|mixed Data without useless information
+	 *
+	 * @url GET /assets_received
+	 *
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 */
+	public function assetsReceived(): array
+	{
+		global $user, $db;
+
+		$packageHistory = new PackageHistory($db);
+		$inventoryObj = new Inventory($db);
+		$sql = "SELECT pt.device_id, i.ref, i.title, ie.unique_identifier, 
+		(SELECT ph.date_creation FROM " . $this->db->prefix() . $packageHistory->table_element . " as ph WHERE user_id = {$user->id} AND DATE(ph.date_creation) = CURRENT_DATE
+		ORDER BY ph.rowid DESC LIMIT 1) AS ph_date_creation
+		FROM llx_settlements_packagetool pt
+		INNER JOIN llx_inventory as i ON i.rowid = pt.device_id
+		INNER JOIN llx_inventory_extrafields as ie ON ie.fk_object = i.rowid
+		WHERE pt.package_id = (SELECT ph.package_id FROM public.llx_rollerstorage_packagehistory as ph WHERE user_id = {$user->id}
+		AND DATE(ph.date_creation) = CURRENT_DATE
+		ORDER BY ph.rowid DESC LIMIT 1)
+		ORDER BY pt.rowid DESC";
+		//print $sql;exit;
+		$result = $db->query($sql);
+		$changeSql = "SELECT ref, title FROM " . $this->db->prefix() . $inventoryObj->table_element . " WHERE ref ILIKE '%Change%'";
+		$resultCh = $db->query($changeSql);
+		if ($result > 0 && $resultCh > 0) {
+			while ($row = $db->fetch_object($result)) {
+				$row = (array) $row;
+				$date = $row['ph_date_creation'];
+				$device[] = [
+					"name" => isset($row['title']) ? $row['title'] : '',
+					"sku" => isset($row['unique_identifier']) ? $row['unique_identifier'] : ''
+				];
+			}
+			$change = [];
+			while ($rowCh = $db->fetch_object($resultCh)) {
+				$rowCh = (array) $rowCh;
+				$ChangeArray = explode('_', $rowCh['ref']);
+				$key = isset($ChangeArray[1]) ? strtolower($ChangeArray[1]) : '';
+				if (!empty($key)) {
+					$change[$key] = isset($rowCh['title']) ? $rowCh['title'] : '';
+				}
+			}
+		} else {
+			return [];
+		}
+
+		return [
+			'date' => $date,
+			'devices' => $device,
+			'change' => $change
+		];
+	}
+
+	/**
+	 * Save card payment log
+	 *
+	 * @param string $invoice_ref
+	 * @param string $data
+	 * 
+	 * @return array|mixed Data without useless information
+	 *
+	 * @url POST /cardpaymentlog
+	 *
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 */
+	public function cardPaymentLog(string $invoice_ref, string $data)
+	{
+		$result = 'OK';
+
+		$facture = new Facture($this->db);
+		if ($facture->fetch(0, $invoice_ref) > 0) {
+			$facture->array_options['options_payment_log'] = $data;
+			$facture->updateExtraField('payment_log');
+
+			$json = json_decode($data, true);
+			if (!empty($json['authorization_number'])) {
+				$sql = "
+					UPDATE " . MAIN_DB_PREFIX . "paiement 
+					SET num_paiement='" . $json['authorization_number'] . "' 
+					WHERE rowid=(
+						SELECT fk_paiement 
+						FROM " . MAIN_DB_PREFIX . "paiement_facture 
+						WHERE fk_facture={$facture->id}
+					)
+				";
+				$this->db->query($sql);
+			}
+		} else {
+			$result = 'Missing invoice';
+		}
+
+		return $result;
+	}
+
+	/**
+	 * Products list with children
+	 *
+	 * @param  string $sortfield  			Sort field
+	 * @param  string $sortorder  			Sort order
+	 * @param  int    $limit      			Limit for list
+	 * @param  int    $page       			Page number
+	 * @param  int    $mode       			Use this param to filter list (0 for all, 1 for only product, 2 for only service)
+	 * @param  int    $category   			Use this param to filter list by category
+	 * @param  string $sqlfilters 			Other criteria to filter answers separated by a comma. Syntax example "(t.tobuy:=:0) and (t.tosell:=:1)"
+	 * @param  bool   $ids_only   			Return only IDs of product instead of all properties (faster, above all if list is long)
+	 * @param  int    $variant_filter   		Use this param to filter list (0 = all, 1=products without variants, 2=parent of variants, 3=variants only)
+	 * @param  bool   $pagination_data   	If this parameter is set to true the response will include pagination data. Default value is false. Page starts from 0
+	 * @param  int    $includestockdata		Load also information about stock (slower)
+	 * @return array                			Array of product objects
+	 *
+	 * @url GET /productshotel
+	 *
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 */
+	public function productshotel($sortfield = 't.ref', $sortorder = 'ASC', $limit = 100, $page = 0, $mode = 0, $category = 0, $sqlfilters = '', $ids_only = false, $variant_filter = 0, $pagination_data = false, $includestockdata = 0): array
+	{
+		if (!DolibarrApiAccess::$user->rights->produit->lire) {
+			throw new RestException(403);
+		}
+
+		return (new ApiProductListHelper)->listhotel($sortfield, $sortorder, $limit, $page, $mode, $category, $sqlfilters, $ids_only, $variant_filter, $pagination_data, $includestockdata);
+	}
+
+	/**
+	 * Get the saled factures data by user
+	 * 
+	 * @param  string 	$date			Date
+	 * 
+	 * @return array|mixed Data without useless information
+	 * 
+	 * @url POST /saledfacturesbyuser
+	 */
+	function saledfacturesbyuser(string $date)
+	{
+		global $user, $db;
+		$date = date("Y-m-d", dol_now());
+		$fulldate = date("Y-m-d H:i:s", dol_now());
+		$apiBbusHelper = new ApiBBusHelper();
+		$from = $apiBbusHelper->getGroupLoginDate($date);
+		$to = $apiBbusHelper->getGroupLogout($date, $from);
+		$array = [];
+		if (strtotime($fulldate) > strtotime($to)) {
+			return $array;
+		}
+		$sql = "SELECT 
+		f.rowid as facture_rowid, 
+		f.ref AS facture_ref, 
+		f.datec, 
+		f.multicurrency_code, 
+		pa.libelle, 
+		pai.note AS transaction_id, 
+		fdet.fk_product AS product_id,  
+		pr.label AS product_label, 
+		pr.ref AS product_ref, 
+		pr.description, 
+		pass.fk_product_pere AS bundle_id, 
+		pr2.label AS bundle_label, 
+		pr2.ref AS bundle_ref, 
+		fdet.multicurrency_total_tva AS base_tva, 
+		fdet.multicurrency_total_ttc AS base_ttc, 
+		fdete.discount_percent, 
+		fdete.discount_hours, 
+		pr.default_vat_code, 
+		f.multicurrency_total_ttc AS bundle_ttc, 
+		(SELECT rate FROM llx_multicurrency_rate AS mc WHERE mc.fk_multicurrency = f.fk_multicurrency AND mc.entity = {$user->entity} ORDER BY mc.rowid DESC LIMIT 1) AS rate,
+		(SELECT date_start FROM llx_eventwizard_eventhistory AS evev WHERE evev.fk_facture = f.rowid) AS date_start
+				FROM llx_facture AS f
+				INNER JOIN llx_facture_extrafields AS fe ON fe.fk_object = f.rowid
+				INNER JOIN llx_c_paiement AS pa ON pa.id = f.fk_mode_reglement
+				INNER JOIN llx_paiement_facture AS pafa ON pafa.fk_facture = f.rowid
+				INNER JOIN llx_paiement AS pai ON pai.rowid = pafa.fk_paiement
+				INNER JOIN llx_facturedet AS fdet ON fdet.fk_facture = f.rowid
+				INNER JOIN llx_product AS pr ON pr.rowid = fdet.fk_product
+				left JOIN llx_product_association AS pass ON pass.fk_product_fils = pr.rowid
+				LEFT JOIN llx_product AS pr2 ON pr2.rowid = pass.fk_product_pere
+				LEFT JOIN llx_facturedet_extrafields AS fdete ON fdet.rowid = fdete.fk_object
+				WHERE f.datec BETWEEN '{$from}' AND '{$to}' 
+				AND f.fk_user_closing = $user->id
+				ORDER BY f.rowid DESC";
+		//print $sql;exit;
+		$data = $db->query($sql);
+		if ($db->num_rows($data > 0)) {
+
+			while ($row = $db->fetch_object($data)) {
+				$tmpArray = (array) $row;
+				if ($tmpArray['transaction_id'] != '') {
+					$jsonArray = json_decode($tmpArray['transaction_id']);
+					$row->transaction_id = $jsonArray->card_transaction_id;
+				}
+				$row->bundle_id = $row->bundle_id == '' ? $row->product_id : $row->bundle_id;
+				$row->bundle_label = $row->bundle_label == '' ? $row->product_label : $row->bundle_label;
+				$row->bundle_ref = $row->bundle_ref == '' ? $row->product_ref : $row->bundle_ref;
+				$array[$row->facture_rowid]['ref'] = $row->facture_ref;
+				$array[$row->facture_rowid]['bundle_name'] = $row->bundle_label;
+				$array[$row->facture_rowid]['currency'] = $row->multicurrency_code;
+				$array[$row->facture_rowid]['total_price'] = $row->bundle_ttc;
+				$array[$row->facture_rowid]['type'] = $row->libelle;
+				$array[$row->facture_rowid]['transaction_id'] = $row->transaction_id;
+				$array[$row->facture_rowid]['rate'] = $row->rate;
+				$array[$row->facture_rowid]['date'] = $row->datec;
+				$array[$row->facture_rowid]['lines'][$row->product_id] = (array) $row;
+			}
+			return $array;
+		} else {
+			return $array;
+		}
+	}
+
+	/**
+	 * List of assets received
+	 *
+	 * Return an array with assets received.
+	 *
+	 * @return array|mixed Data without useless information
+	 *
+	 * @url GET /getAllUsersOfTheGroup
+	 *
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 */
+	public function getAllUsersOfTheGroup(): array
+	{
+		global $user, $db;
+		$array = [];
+		$sql = "SELECT u.rowid, u.firstname, u.lastname, u.login, ue.nickname, u.email, u.office_phone, u.user_mobile, u.personal_mobile   FROM llx_settlements_groupusers AS gu
+		INNER JOIN llx_user_extrafields as ue ON ue.fk_object = gu.fk_user
+		INNER JOIN llx_user as u ON u.rowid = gu.fk_user
+		WHERE gu.fk_settlements_group = (SELECT gu2.fk_settlements_group FROM llx_settlements_groupusers as gu2 WHERE gu2.fk_user = {$user->id})";
+		$data = $db->query($sql);
+		while ($row = $db->fetch_object($data)) {
+			$array[] = [
+				'id' => $row->rowid,
+				'name' => $row->firstname . ' ' . $row->lastname,
+				'login' => $row->login,
+				'email' => $row->email,
+				'nickname' => $row->nickname ? $row->nickname : '',
+				'office_phone' => $row->office_phone,
+				'user_mobile' => $row->user_mobile,
+				'personal_mobile' => $row->personal_mobile
+			];
+		}
+		return $array;
+	}
+
+	#-------------------------------------------------
+	#	Event generator
+	#-------------------------------------------------
+
+	/**
+	 * Generate events	 
+	 *
+	 * @return array|mixed Data without useless information
+	 *
+	 * @url GET /genEvents
+	 *
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 */
+	public function genEvents()
+	{
+		$period = new DatePeriod(
+			new DateTime('2024-07-16'),
+			new DateInterval('P1D'),
+			new DateTime('2024-10-16')
+		);
+
+		$size = 30;
+		$buffer = 5;
+
+		$dates = [];
+
+		foreach ($period as $date) {
+			if (rand(0, 1)) {
+				$eventDate = $date->format('Y-m-d');
+				$eventTimes = array_rand(
+					array_flip([
+						'09:00',
+						'10:00',
+						'11:00',
+						'12:00',
+						'13:00',
+						'14:00',
+						'15:00',
+						'16:00',
+						'17:00',
+						'18:00',
+						'19:00',
+						'20:00'
+					]),
+					rand(1, 12)
+				);
+				/* 				print $eventDate;
+																													print "\r\n";
+																													print_r($eventTimes); */
+				if (is_array($eventTimes)) {
+					foreach ($eventTimes as $eventTime) {
+						$duration = rand(1, 3);
+						$dates[$eventDate][] = [
+							'start' => $eventTime,
+							'duration' => '0' . $duration . ':00:00',
+						];
+					}
+				}
+			}
+		}
+
+		//print_r($dates);exit;
+
+
+		$eventdetail_id = 1;
+		$dates = ['dates' => $dates, 'id_eventdetails' => $eventdetail_id];
+
+		dol_include_once('/eventwizard/class/eventdetails.class.php');
+
+		$object = new EventDetails($this->db);
+		$object->fetch($eventdetail_id);
+		$object->genEvents($dates, $eventdetail_id);
+
+		//print_r($dates);exit;
+
+		return [];
+	}
+
+	/**
+	 * Generate events	 
+	 *
+	 * @return array|mixed Data without useless information
+	 *
+	 * @url GET /updateeventextrafields
+	 *
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 */
+	public function updateEventsExtrafields()
+	{
+		$sql = "SELECT ac.id FROM llx_actioncomm as ac 
+		LEFT JOIN llx_actioncomm_extrafields as ace ON ace.fk_object = ac.id
+		WHERE ac.code = 'AC_EVENT'
+		AND ace.max_num IS NULL";
+		$result1 = $this->db->query($sql);
+		while ($row1 = $this->db->fetch_object($result1)) {
+			print_r($row1);
+			$participants = rand(1, 20);
+			$sql2 = "INSERT INTO llx_actioncomm_extrafields (fk_object, max_num, buffer, participants)
+			VALUES ({$row1->id}, 30, 5, $participants)";
+			$result = $this->db->query($sql2);
+		}
+	}
+
+	#-------------------------------------------------
+	#	Facture generator
+	#-------------------------------------------------
+
+	/**
+	 * Generate factures	 
+	 * 
+	 * @return array|mixed Data without useless information
+	 *
+	 * @url POST /generateFactures
+	 *
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 */
+	public function generateFactures()
+	{
+		$numberOfFactures = 1;
+
+		for ($i = 0; $i < $numberOfFactures; $i++) {
+			$currencyRand = rand(0, 1);
+			$currencyText = [0 => 'HUF', 1 => 'EUR'];
+			$currencyNumber = [0 => 3, 1 => 5];
+
+			$reglementRand = rand(14, 15);
+			//$reglementRand = rand(106, 107); ## STAGING
+			$mode_reglement_id = $reglementRand;
+			$paymentid = $reglementRand;
+			$accountid = [0 => 4, 1 => 5];
+
+			$deduction = rand(1, 5) == 3 ? 1 : null;
+			$storno = rand(1, 5) == 1 ? 1 : null;
+
+			$invoice = ["mode_reglement_id" => $mode_reglement_id, "array_options_app_facture" => 1, "array_options_commission_deduction" => $deduction, "array_options_marked_for_storno" => $storno, "multicurrency_code" => $currencyText[$currencyRand], "fk_multicurrency" => $currencyNumber[$currencyRand], "array_options_customer_data_zip" => null];
+			$linesHUFBundle = [
+				["desc" => "", "subprice" => "787.4015", "qty" => 1, "tva_tx" => "27.0", "localtax1_tx" => "0.000", "localtax2_tx" => "0.000", "fk_product" => "102", "remise_percent" => "0", "date_start" => "", "date_end" => "", "fk_code_ventilation" => 0, "info_bits" => "0", "fk_remise_except" => null, "product_type" => "1", "rang" => "-1", "special_code" => "0", "fk_parent_line" => null, "fk_fournprice" => null, "pa_ht" => "0.00000000", "label" => "", "array_options" => [], "situation_percent" => "100", "fk_prev_id" => null, "fk_unit" => null, "price_base_type" => "HT"],
+				["desc" => "", "subprice" => "13333.3333", "qty" => 1, "tva_tx" => "5.0", "localtax1_tx" => "0.000", "localtax2_tx" => "0.000", "fk_product" => "108", "remise_percent" => "0", "date_start" => "", "date_end" => "", "fk_code_ventilation" => 0, "info_bits" => "0", "fk_remise_except" => null, "product_type" => "1", "rang" => "-1", "special_code" => "0", "fk_parent_line" => null, "fk_fournprice" => null, "pa_ht" => "0.00000000", "label" => "", "array_options" => [], "situation_percent" => "100", "fk_prev_id" => null, "fk_unit" => null, "price_base_type" => "HT"]
+			];
+
+			$linesEURBundle = [
+				["desc" => "", "subprice" => "2.3622", "qty" => 1, "tva_tx" => "27.0", "localtax1_tx" => "0.000", "localtax2_tx" => "0.000", "fk_product" => "101", "remise_percent" => "0", "date_start" => "", "date_end" => "", "fk_code_ventilation" => 0, "info_bits" => "0", "fk_remise_except" => null, "product_type" => "1", "rang" => "-1", "special_code" => "0", "fk_parent_line" => null, "fk_fournprice" => null, "pa_ht" => "0.00000000", "label" => "", "array_options" => [], "situation_percent" => "100", "fk_prev_id" => null, "fk_unit" => null, "price_base_type" => "HT"],
+				["desc" => "", "subprice" => "33.3333", "qty" => 1, "tva_tx" => "5.0", "localtax1_tx" => "0.000", "localtax2_tx" => "0.000", "fk_product" => "107", "remise_percent" => "0", "date_start" => "", "date_end" => "", "fk_code_ventilation" => 0, "info_bits" => "0", "fk_remise_except" => null, "product_type" => "1", "rang" => "-1", "special_code" => "0", "fk_parent_line" => null, "fk_fournprice" => null, "pa_ht" => "0.00000000", "label" => "", "array_options" => [], "situation_percent" => "100", "fk_prev_id" => null, "fk_unit" => null, "price_base_type" => "HT"]
+			];
+
+			$linesEURSimple = [
+				["desc" => "", "subprice" => "12.5984", "qty" => 1, "tva_tx" => "27.0", "localtax1_tx" => "0.000", "localtax2_tx" => "0.000", "fk_product" => "132", "remise_percent" => "0", "date_start" => "", "date_end" => "", "fk_code_ventilation" => 0, "info_bits" => "0", "fk_remise_except" => null, "product_type" => "1", "rang" => "-1", "special_code" => "0", "fk_parent_line" => null, "fk_fournprice" => null, "pa_ht" => "0.00000000", "label" => "", "array_options" => [], "situation_percent" => "100", "fk_prev_id" => null, "fk_unit" => null, "price_base_type" => "HT"],
+			];
+
+			$linesArray = [
+				0 => $linesEURBundle,
+				1 => $linesEURSimple,
+				2 => $linesHUFBundle
+			];
+			if ($currencyRand == 1) {
+				$lineRand = rand(0, 1);
+				$lines = $linesArray[$lineRand];
+			} else {
+				$lines = $linesArray[2];
+			}
+
+			$payment = ["datepaye" => time(), "paymentid" => $paymentid, "closepaidinvoices" => "yes", "accountid" => $accountid[$currencyRand]];
+			$comment = "{\"timestamp\":\"1704241200\",\"merchant_id\":\"000000000014202\",\"acquirer_id\":\"88105000003\",\"operation_number\":\"000037\",\"termid\":\"36002058\",\"pan\":\"428312******1789\",\"exp\":\"****\",\"stan\":\"000007\",\"authorization_number\":\"282597\",\"trans_type\":\"CLI\",\"amount\":\"2.0\",\"cvm\":\"2\",\"billnumber\":\"AcTbHmcWsMGcF6Nf\",\"card_transaction_id\":\"AcTbHmcWsMGcF6Nf\"}";
+			$payment['comment'] = $reglementRand == 15 ? '' : $comment;
+			//$payment['comment'] = $reglementRand == 107 ? '' : $comment;  ## STAGING
+
+			$cardPaymentLog = "";
+
+			/*  			print_r($invoice);
+															print_r($lines);
+															print_r($payment);
+															exit; */
+
+			$invoiceNumber = $this->invoice($invoice, $lines, $payment, $cardPaymentLog, $sendId = '');
+			$ref = $invoiceNumber['invoice']['ref'];
+			$datetime = date("Y-m-d H:i:s", $payment['datepaye']);
+			$multiprint = rand(1, 5);
+			$this->dateHandler($ref, $datetime);
+			if ($multiprint == 2 || $multiprint == 3) {
+				$prints = rand(1, 5);
+				$dateofprint = $payment['datepaye'];
+				for ($i = 0; $i < $prints; $i++) {
+					$dateofprint = strtotime('+5 seconds', $dateofprint);
+					$datetime = date("Y-m-d H:i:s", $dateofprint);
+					$this->dateHandler($ref, $datetime);
+				}
+			}
+		}
+		return 'OK';
+	}
+
+	#-------------------------------------------------
+	#	CURL
+	#-------------------------------------------------
+
+	/**
+	 * Check Printed copies
+	 *
+	 * Return an array with details
+	 *
+	 * @return 	array|mixed data without useless information
+	 *
+	 * @param 	string 	$ref			//selected evet from llx_event
+	 * 
+	 * @url	POST curlCheckPrintedCopies
+	 * @throws RestException 401 Not allowed
+	 * @throws RestException 404 Not found
+	 */
+	public function curlCheckPrintedCopies(string $ref)
+	{
+		$bbticketinvoiceprinting = new BbTicketInvoicePrinting($this->db);
+		$copies = $bbticketinvoiceprinting->fetchAll('ASC', 'rowid', 0, 0, ["customsql" => "invoice_number ILIKE '%{$ref}%'"]);
+		return (is_array($copies)) ? count($copies) : 0;
+	}
+
+	/**
+	 * @param 	string 	$datetime
+	 * @param 	string 	$datetime_timestamp
+	 * @param 	array 	$ticket
+	 * @param 	string 	$ref
+	 * 
+	 * 
+	 * @return array|mixed Data without useless information
+	 *
+	 * @url POST curlSetPrintingInvoiceObject
+	 *
+	 * @throws RestException 401
+	 * @throws RestException 403
+	 * @throws RestException 404
+	 */
+	public function curlSetPrintingInvoiceObject(string $datetime, string $datetime_timestamp, array $ticket, string $ref)
+	{
+		global $user;
+		$helper = new ApiBBusHelper();
+		$helper->setPrintingInvoiceObject($user, null, $datetime, $datetime_timestamp, (object)$ticket, $ref);
+	}
+}

+ 4 - 1
custom/bbus/class/api_curl.class.php

@@ -38,9 +38,12 @@ trait CurlApi
 
 	private function curlRunner($route, $postFields, $request = 'POST', $decode = false)
 	{
+		ApiBbusLog::appLog("{$route}");
 		$curl = $this->curlInit($route, $postFields, $request);
 		$response = curl_exec($curl);
-		//ApiBbusLog::appLog("{$response}");
+		if($route != 'bookingapi/localavailableplaces'){
+			ApiBbusLog::appLog("{$response}");
+		}
 		curl_close($curl);
 		return $decode ? json_decode($response) : $response;
 	}

+ 10 - 0
custom/bbus/core/triggers/interface_99_modBBus_BBusTriggers.class.php

@@ -35,6 +35,8 @@ require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbtickethandler.class.php';
 require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/commissionhandler.class.php';
 require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/bbticket.class.php';
 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/booking/class/api_booking.class.php';
+
 
 
 
@@ -135,6 +137,14 @@ class InterfaceBBusTriggers extends DolibarrTriggers
 					ApiBbusLog::appLog("LINEBILL_INSERT->basic_service = " . $productObj->array_options['options_basic_service']);
 					$bbTicketHandler = new BbTicketHandler();
 					$bbTicketHandler->addTicket($object, $action);
+				} else if($productObj->array_options['options_basic_service'] == '1' || $productObj->array_options['options_basic_service'] == '2' || $productObj->array_options['options_basic_service'] == '5'){
+					ApiBbusLog::appLog("LINEBILL_INSERT->basic_service = " . $productObj->array_options['options_basic_service']);
+					$factureObj = new Facture($this->db);
+					$result = $factureObj->fetch($object->fk_facture);
+					$datec = date("Y-m-d H:i:s", $factureObj->date_creation);
+					$bookingApi = new BookingApi();
+					ApiBbusLog::appLog("curlCreateBBticket: START");
+					$bookingApi->curlCreateBBticket($object->fk_product, $datec, $factureObj->id);
 				}
 				break;
 

+ 64 - 64
custom/booking/booking_agenda_event_details_view.php

@@ -1,64 +1,64 @@
-<?php
-$res = 0;
-// Try main.inc.php into web root known defined into CONTEXT_DOCUMENT_ROOT (not always defined)
-if (!$res && !empty ($_SERVER["CONTEXT_DOCUMENT_ROOT"])) {
-    $res = @include $_SERVER["CONTEXT_DOCUMENT_ROOT"] . "/main.inc.php";
-}
-// Try main.inc.php into web root detected using web root calculated from SCRIPT_FILENAME
-$tmp = empty ($_SERVER['SCRIPT_FILENAME']) ? '' : $_SERVER['SCRIPT_FILENAME'];
-$tmp2 = realpath(__FILE__);
-$i = strlen($tmp) - 1;
-$j = strlen($tmp2) - 1;
-while ($i > 0 && $j > 0 && isset ($tmp[$i]) && isset ($tmp2[$j]) && $tmp[$i] == $tmp2[$j]) {
-    $i--;
-    $j--;
-}
-if (!$res && $i > 0 && file_exists(substr($tmp, 0, ($i + 1)) . "/main.inc.php")) {
-    $res = @include substr($tmp, 0, ($i + 1)) . "/main.inc.php";
-}
-if (!$res && $i > 0 && file_exists(dirname(substr($tmp, 0, ($i + 1))) . "/main.inc.php")) {
-    $res = @include dirname(substr($tmp, 0, ($i + 1))) . "/main.inc.php";
-}
-// Try main.inc.php using relative path
-if (!$res && file_exists("../main.inc.php")) {
-    $res = @include "../main.inc.php";
-}
-if (!$res && file_exists("../../main.inc.php")) {
-    $res = @include "../../main.inc.php";
-}
-if (!$res && file_exists("../../../main.inc.php")) {
-    $res = @include "../../../main.inc.php";
-}
-if (!$res) {
-    die ("Include of main fails");
-}
-$event = $_GET['event'];
-
-require_once DOL_DOCUMENT_ROOT . '/custom/booking/class/booking_agenda_helper.class.php';
-$bookingAgendaHelper = new BookingAgendaHelper($db);
-$bookingAgendaHelper->getSelectedEvents($event);
-?>
-<style>
-    .trheight {
-        height: 50px;
-    }
-
-    .elvalaszto {
-        background-color: grey;
-    }
-
-    .foglaltsag {
-        color: red;
-    }
-
-    .location {
-        color: black;
-    }
-
-    .firstcolumn {
-        background-color: lightgrey;
-        text-align: center;
-        font-weight: bold;
-    }
-</style>
-
+<?php
+$res = 0;
+// Try main.inc.php into web root known defined into CONTEXT_DOCUMENT_ROOT (not always defined)
+if (!$res && !empty ($_SERVER["CONTEXT_DOCUMENT_ROOT"])) {
+    $res = @include $_SERVER["CONTEXT_DOCUMENT_ROOT"] . "/main.inc.php";
+}
+// Try main.inc.php into web root detected using web root calculated from SCRIPT_FILENAME
+$tmp = empty ($_SERVER['SCRIPT_FILENAME']) ? '' : $_SERVER['SCRIPT_FILENAME'];
+$tmp2 = realpath(__FILE__);
+$i = strlen($tmp) - 1;
+$j = strlen($tmp2) - 1;
+while ($i > 0 && $j > 0 && isset ($tmp[$i]) && isset ($tmp2[$j]) && $tmp[$i] == $tmp2[$j]) {
+    $i--;
+    $j--;
+}
+if (!$res && $i > 0 && file_exists(substr($tmp, 0, ($i + 1)) . "/main.inc.php")) {
+    $res = @include substr($tmp, 0, ($i + 1)) . "/main.inc.php";
+}
+if (!$res && $i > 0 && file_exists(dirname(substr($tmp, 0, ($i + 1))) . "/main.inc.php")) {
+    $res = @include dirname(substr($tmp, 0, ($i + 1))) . "/main.inc.php";
+}
+// Try main.inc.php using relative path
+if (!$res && file_exists("../main.inc.php")) {
+    $res = @include "../main.inc.php";
+}
+if (!$res && file_exists("../../main.inc.php")) {
+    $res = @include "../../main.inc.php";
+}
+if (!$res && file_exists("../../../main.inc.php")) {
+    $res = @include "../../../main.inc.php";
+}
+if (!$res) {
+    die ("Include of main fails");
+}
+$event = $_GET['event'];
+
+require_once DOL_DOCUMENT_ROOT . '/custom/booking/class/booking_agenda_helper.class.php';
+$bookingAgendaHelper = new BookingAgendaHelper($db);
+$bookingAgendaHelper->getSelectedEvents($event);
+?>
+<style>
+    .trheight {
+        height: 50px;
+    }
+
+    .elvalaszto {
+        background-color: grey;
+    }
+
+    .foglaltsag {
+        color: red;
+    }
+
+    .location {
+        color: black;
+    }
+
+    .firstcolumn {
+        background-color: lightgrey;
+        text-align: center;
+        font-weight: bold;
+    }
+</style>
+

+ 67 - 67
custom/booking/booking_agenda_table_view.php

@@ -1,67 +1,67 @@
-<?php
-$res = 0;
-// Try main.inc.php into web root known defined into CONTEXT_DOCUMENT_ROOT (not always defined)
-if (!$res && !empty ($_SERVER["CONTEXT_DOCUMENT_ROOT"])) {
-    $res = @include $_SERVER["CONTEXT_DOCUMENT_ROOT"] . "/main.inc.php";
-}
-// Try main.inc.php into web root detected using web root calculated from SCRIPT_FILENAME
-$tmp = empty ($_SERVER['SCRIPT_FILENAME']) ? '' : $_SERVER['SCRIPT_FILENAME'];
-$tmp2 = realpath(__FILE__);
-$i = strlen($tmp) - 1;
-$j = strlen($tmp2) - 1;
-while ($i > 0 && $j > 0 && isset ($tmp[$i]) && isset ($tmp2[$j]) && $tmp[$i] == $tmp2[$j]) {
-    $i--;
-    $j--;
-}
-if (!$res && $i > 0 && file_exists(substr($tmp, 0, ($i + 1)) . "/main.inc.php")) {
-    $res = @include substr($tmp, 0, ($i + 1)) . "/main.inc.php";
-}
-if (!$res && $i > 0 && file_exists(dirname(substr($tmp, 0, ($i + 1))) . "/main.inc.php")) {
-    $res = @include dirname(substr($tmp, 0, ($i + 1))) . "/main.inc.php";
-}
-// Try main.inc.php using relative path
-if (!$res && file_exists("../main.inc.php")) {
-    $res = @include "../main.inc.php";
-}
-if (!$res && file_exists("../../main.inc.php")) {
-    $res = @include "../../main.inc.php";
-}
-if (!$res && file_exists("../../../main.inc.php")) {
-    $res = @include "../../../main.inc.php";
-}
-if (!$res) {
-    die ("Include of main fails");
-}
-$year = GETPOST('year', 'aZ09');
-$month = GETPOST('month', 'aZ09');
-$day = GETPOST('day', 'aZ09');
-
-require_once DOL_DOCUMENT_ROOT . '/custom/booking/class/booking_agenda_helper.class.php';
-$bookingAgendaHelper = new BookingAgendaHelper($db);
-$eventdayDates = $bookingAgendaHelper->getEventDayDates($year, $month, $day);
-$bookingAgendaHelper->showTable($eventdayDates, $selectedEvent);
-?>
-<style>
-    .trheight {
-        height: 50px;
-    }
-
-    .elvalaszto {
-        background-color: grey;
-    }
-
-    .foglaltsag {
-        color: red;
-    }
-
-    .location {
-        color: black;
-    }
-
-    .firstcolumn {
-        background-color: lightgrey;
-        text-align: center;
-        font-weight: bold;
-    }
-</style>
-
+<?php
+$res = 0;
+// Try main.inc.php into web root known defined into CONTEXT_DOCUMENT_ROOT (not always defined)
+if (!$res && !empty ($_SERVER["CONTEXT_DOCUMENT_ROOT"])) {
+    $res = @include $_SERVER["CONTEXT_DOCUMENT_ROOT"] . "/main.inc.php";
+}
+// Try main.inc.php into web root detected using web root calculated from SCRIPT_FILENAME
+$tmp = empty ($_SERVER['SCRIPT_FILENAME']) ? '' : $_SERVER['SCRIPT_FILENAME'];
+$tmp2 = realpath(__FILE__);
+$i = strlen($tmp) - 1;
+$j = strlen($tmp2) - 1;
+while ($i > 0 && $j > 0 && isset ($tmp[$i]) && isset ($tmp2[$j]) && $tmp[$i] == $tmp2[$j]) {
+    $i--;
+    $j--;
+}
+if (!$res && $i > 0 && file_exists(substr($tmp, 0, ($i + 1)) . "/main.inc.php")) {
+    $res = @include substr($tmp, 0, ($i + 1)) . "/main.inc.php";
+}
+if (!$res && $i > 0 && file_exists(dirname(substr($tmp, 0, ($i + 1))) . "/main.inc.php")) {
+    $res = @include dirname(substr($tmp, 0, ($i + 1))) . "/main.inc.php";
+}
+// Try main.inc.php using relative path
+if (!$res && file_exists("../main.inc.php")) {
+    $res = @include "../main.inc.php";
+}
+if (!$res && file_exists("../../main.inc.php")) {
+    $res = @include "../../main.inc.php";
+}
+if (!$res && file_exists("../../../main.inc.php")) {
+    $res = @include "../../../main.inc.php";
+}
+if (!$res) {
+    die ("Include of main fails");
+}
+$year = GETPOST('year', 'aZ09');
+$month = GETPOST('month', 'aZ09');
+$day = GETPOST('day', 'aZ09');
+
+require_once DOL_DOCUMENT_ROOT . '/custom/booking/class/booking_agenda_helper.class.php';
+$bookingAgendaHelper = new BookingAgendaHelper($db);
+$eventdayDates = $bookingAgendaHelper->getEventDayDates($year, $month, $day);
+$bookingAgendaHelper->showTable($eventdayDates, $selectedEvent);
+?>
+<style>
+    .trheight {
+        height: 50px;
+    }
+
+    .elvalaszto {
+        background-color: grey;
+    }
+
+    .foglaltsag {
+        color: red;
+    }
+
+    .location {
+        color: black;
+    }
+
+    .firstcolumn {
+        background-color: lightgrey;
+        text-align: center;
+        font-weight: bold;
+    }
+</style>
+

+ 86 - 32
custom/booking/class/api_booking.class.php

@@ -544,10 +544,9 @@ class BookingApi extends DolibarrApi
 	 */
 	public function firstEventStep(int $type_id, int $fk_event, int $reservations, int $product_id, string $sendId)
 	{
-		ApiBbusLog::appLog("firstEventStep");
-
+		ApiBbusLog::appLog("{$sendId}: firstEventStep");
 		if (!DolibarrApiAccess::$user->rights->facture->creer) {
-			ApiBbusLog::eventLog("{$sendId} Insufficient rights");
+			ApiBbusLog::appLog("{$sendId} Insufficient rights");
 			throw new RestException(401, 'Insufficient rights');
 		}
 		$this->createLog($sendId, $fk_event, $reservations, $product_id);
@@ -560,23 +559,42 @@ class BookingApi extends DolibarrApi
 			}
 			$createdPreOrderOBJ = $this->localcreatedpreorderobj($sendId, $fk_event, $reservations, $product_id);
 		} else {
+			ApiBbusLog::appLog("{$sendId}: firstEventStep: Bbus");
 			$params = compact('fk_event', 'reservations');
 			$postFields = json_encode($params);
-			$noavailableSpaces = json_decode($this->curlRunner('bookingapi/nolocalemptyspaces', $postFields, 'POST', true))->result;
+			$noavailableSpaces = json_decode($this->curlRunner('bookingapi/curlnoLocalEmptySpaces', $postFields, 'POST', true))->result;
 			if ($noavailableSpaces) {
+				ApiBbusLog::appLog("{$sendId}: firstEventStep: No available spaces");
 				throw new RestException(506, 'No available spaces');
 			}
 			$params = compact('sendId', 'fk_event', 'reservations', 'product_id');
 			$postFields = json_encode($params);
 			$createdPreOrderOBJ = $this->curlRunner('bookingapi/localcreatedpreorderobj', $postFields, 'POST', true);
 		}
-
-
+		ApiBbusLog::appLog("{$sendId}: " . json_encode($createdPreOrderOBJ));
 		return $createdPreOrderOBJ;
 	}
 
+	/**
+	 * Get all preorders
+	 *
+	 * Return an array with details
+	 *
+	 * @return 	array|mixed data without useless information
+	 *
+	 * @param 	string 	sendId
+	 * @param 	int 	fk_event
+	 * @param 	int 	reservations
+	 * @param 	int 	product_id
+	 * 
+	 * @url	POST localcreatedpreorderobj
+	 * @access protected
+	 * @throws RestException 401 Not allowed
+	 * @throws RestException 404 Not found
+	 */
 	function localcreatedpreorderobj($sendId, $fk_event, $reservations, $product_id)
 	{
+		global $user;
 		ApiBbusLog::appLog("{$sendId}: localcreatedpreorderobj");
 
 		$apiInvoiceHelper = new ApiInvoiceHelper;
@@ -592,14 +610,16 @@ class BookingApi extends DolibarrApi
 				$preOrderObj->ref = $this->generateRandomString();
 				$preOrderObj->fk_event = $fk_event;
 				$preOrderObj->fk_product = $product_id;
-				$result = $preOrderObj->create($this->user);
+				$result = $preOrderObj->create($user);
 				if ($result > 0) {
 					$createdPreOrderOBJ[] = [
 						'id' => $preOrderObj->id,
 						'ref' => $preOrderObj->ref,
 					];
 				} else {
-					ApiBbusLog::appLog("{$sendId}: " . json_encode(['error' => $preOrderObj->errors]));
+					foreach ($preOrderObj->errors as $error) {
+						ApiBbusLog::appLog("Error: " . $error);
+					}
 					ApiBbusLog::appLog("{$sendId}: Unsaved event: " . $fk_event);
 					throw new RestException(401, 'Unsaved event: ' . $fk_event);
 				}
@@ -648,21 +668,23 @@ class BookingApi extends DolibarrApi
 	 * @return 	array|mixed data without useless information
 	 *
 	 * @param 	int 	$fk_event			//selected evet from llx_event
-	 * @param 	int 	$fk_facture			//facture rowid from llx_facture
-	 * @param 	int 	$fk_eventproduct	//eventproduct rowid from llx_eventwizard_eventproduct
-	 * @param 	string 	$ref				//ref
 	 * 
 	 * @url	POST saveeventdata
 	 * @access protected
 	 * @throws RestException 401 Not allowed
 	 * @throws RestException 404 Not found
 	 */
-	public function saveEventData(int $fk_event, int $fk_facture = null, int $fk_eventproduct = null, string $ref = null )
+	public function saveEventData($fk_event, $fk_facture = null, $fk_eventproduct = null, $ref = null)
 	{
+
+		ApiBbusLog::appLog("saveEventData");
+
 		dol_include_once('/custom/booking/class/bookinghistory.class.php');
 		dol_include_once('/comm/action/class/actioncomm.class.php');
 		$actionCommObj = new ActionComm($this->db);
 		$sql = "SELECT fk_element, datep, datep2 FROM " . MAIN_DB_PREFIX . $actionCommObj->table_element . " WHERE id = {$fk_event}";
+		ApiBbusLog::appLog("{$sql}");
+
 		$result = $this->db->query($sql);
 		if ($this->db->num_rows($result) > 0) {
 			$row = $this->db->fetch_object($result);
@@ -676,8 +698,13 @@ class BookingApi extends DolibarrApi
 			$BookingHistory->invoice_number = $ref;
 			$result = $BookingHistory->create($this->user);
 			if ($result > 0) {
+				ApiBbusLog::appLog("saveEventData: OK {$BookingHistory->id}");
 				return $BookingHistory->id;
 			} else {
+				foreach ($BookingHistory->errors as $error) {
+					ApiBbusLog::appLog("saveEventData: {$error}");
+				}
+				ApiBbusLog::appLog("saveEventData: ERROR");
 				throw new RestException(401, 'Unsaved');
 			}
 		} else {
@@ -709,32 +736,44 @@ class BookingApi extends DolibarrApi
 	 */
 	public function validateandsaveinvoice(int $type_id, string $sendId, array $payment, array $preorder, array $invoice, string $cardPaymentLog = '')
 	{
+		ApiBbusLog::appLog("validateandsaveinvoice");
+
 		global $db;
 		$server_host = $this->getServerHost($type_id);
-		$createdInvoiceArray = $this->localValidateAndSaveInvoice($invoice, $preorder, $payment, $cardPaymentLog, $sendId, $server_host);
-		//print_r($createdInvoiceArray);exit;
+		$createdInvoiceArray = $this->localValidateAndSaveInvoice($invoice, $preorder, $payment, $cardPaymentLog, $sendId, $server_host, $type_id);
 		if ($server_host == 'excelia') {
 			ApiBbusLog::appLog("validateandsaveinvoice: Excelia");
 			ApiBbusLog::appLog(json_encode($preorder));
-			//exit;
 			foreach ($preorder as $order) {
 				$sql = "SELECT fk_product, fk_event  FROM llx_booking_preorder WHERE rowid = {$order}";
 				ApiBbusLog::appLog("{$sql}");
 				$data = $db->query($sql);
 				if ($db->num_rows($data) > 0) {
 					while ($row = $db->fetch_object($data)) {
+						ApiBbusLog::appLog("while");
 						$bookingHistory = $this->saveEventData((int)$row->fk_event, $createdInvoiceArray[0]['invoice']['id']);
-						$this->updateBbticket($bookingHistory, $createdInvoiceArray[0]['invoice']);
+
+						ApiBbusLog::appLog("______________________________________________________________________________");
+						$this->updateBbticket($createdInvoiceArray[0]['invoice'], $bookingHistory);
 						$this->deletePreorder($preorder);
 					}
 				}
 			}
 		} else {
+			ApiBbusLog::appLog("validateandsaveinvoice: bbus");
+			/* #-------------- UPDATE BTICKET --------------
+			ApiBbusLog::appLog("{$sendId}: Update bbticket with curl");
+			$invoiceForCurl['id'] = $createdInvoiceArray[0]['invoice']['id'];
+			$invoiceForCurl['ref'] = $createdInvoiceArray[0]['invoice']['ref'];
+			$array['fk_booking_history'] = null;
+			$array['invoice'] = $invoiceForCurl;
+			$updateBBticketPostFields = json_encode($array);
+			ApiBbusLog::appLog("{$updateBBticketPostFields}");
+			$this->curlRunner('bookingapi/curlUpdateBbticket', $updateBBticketPostFields, 'POST', true); */
 			foreach ($preorder as $rowid) {
-				ApiBbusLog::appLog("validateandsaveinvoice: Buziiiiiiii");
 				$params = compact('rowid');
 				$postFields = json_encode($params);
-				$preorderArray = $this->curlRunner('bookingapi/localpreorderarray', $postFields, 'POST', true);
+				$preorderArray = $this->curlRunner('bookingapi/curllocalpreorderarray', $postFields, 'POST', true);
 				$saveEventdataPostFields = '{"fk_event":"' . (int)$preorderArray->fk_event . '","ref":"' . $createdInvoiceArray[0]['invoice']['ref'] . '","fk_facture":"' . null . '","fk_eventproduct":"' . null . '"}';
 				$bookingHistory = $this->curlRunner('bookingapi/saveEventData', $saveEventdataPostFields, 'POST', true);
 				$array['fk_booking_history'] = $bookingHistory;
@@ -750,9 +789,12 @@ class BookingApi extends DolibarrApi
 		return $createdInvoiceArray;
 	}
 
-	public function localValidateAndSaveInvoice($invoice, $preorder, $payment, $cardPaymentLog, $sendId, $server_host)
+	public function localValidateAndSaveInvoice($invoice, $preorder, $payment, $cardPaymentLog, $sendId, $server_host, $type_id)
 	{
+		ApiBbusLog::appLog("localValidateAndSaveInvoice");
+
 		global $user, $db;
+		$server_host_curl = false;
 		$createdInvoiceData = [];
 		$apiInvoiceHelper = new ApiInvoiceHelper;
 		$bbusApi = new BBus();
@@ -763,11 +805,15 @@ class BookingApi extends DolibarrApi
 				ApiBbusLog::appLog("localValidateAndSaveInvoice: Excelia");
 				$preorderArray = $this->localpreorderarray($rowid);
 			} else {
-				$preorderArray = $this->curlRunner('bookingapi/localpreorderarray', $postFields, 'POST', true);
+				ApiBbusLog::appLog("localValidateAndSaveInvoice: bbus");
+				if ($type_id != 5 || $type_id != 3 || $type_id != 4) {
+					$server_host_curl = true;
+				}
+				$preorderArray = $this->curlRunner('bookingapi/curllocalpreorderarray', $postFields, 'POST', true);
 			}
 			$lines = $apiInvoiceHelper->getProductLinesWithoutDiscount($preorderArray->fk_product);
 			//$lines = $apiInvoiceHelper->createLinesForCrossShopping($preorderArray->fk_product);
-			$createdInvoiceArray = $bbusApi->invoice($invoice, $lines, $payment, $cardPaymentLog = '', $sendId = '');
+			$createdInvoiceArray = $bbusApi->invoice($invoice, $lines, $payment, $cardPaymentLog = '', $sendId = '', $server_host_curl);
 			$createdInvoiceData[] = $createdInvoiceArray;
 		}
 		ApiBbusLog::appLog("Visszaküld");
@@ -822,11 +868,13 @@ class BookingApi extends DolibarrApi
 		return $result;
 	}
 
-	public function updateBbticket($fk_booking_history, $invoice)
+	public function updateBbticket($invoice, $fk_booking_history = null)
 	{
+		ApiBbusLog::appLog("updateBbticket_________START");
+
 		global $user;
 		$sql = "SELECT rowid FROM llx_bbus_bbticket WHERE fk_facture = {$invoice['id']}";
-		//print $sql;exit;
+		ApiBbusLog::appLog("{$sql}");
 		$data = $this->db->query($sql);
 		if (!empty($this->db->error())) {
 			ApiBbusLog::appLog("{$this->db->error()}");
@@ -837,10 +885,12 @@ class BookingApi extends DolibarrApi
 				$sql = "UPDATE llx_bbus_bbticket
 					SET booking_history_id = {$fk_booking_history}, invoice_number = '{$invoice['ref']}'
 					WHERE rowid = {$row->rowid}";
+				ApiBbusLog::appLog("{$sql}");
 				$this->db->query($sql);
 			}
 		} else {
-			ApiBbusLog::appLog("api_booking: updateBbticket error");
+			ApiBbusLog::appLog("{$this->db->error()}");
+			ApiBbusLog::appLog("api_booking (Excelia): updateBbticket error");
 			throw new RestException(401, 'api_booking: updateBbticket error');
 		}
 	}
@@ -851,7 +901,6 @@ class BookingApi extends DolibarrApi
 	 * Return an array with details
 	 *
 	 *
-	 * @param  	int 	$fk_booking_history			preorder
 	 * @param  	array 	$invoice					invoice
 	 * 
 	 * @url	POST curlupdateBbticket
@@ -862,26 +911,30 @@ class BookingApi extends DolibarrApi
 	 * @throws RestException 506 No available spaces
 	 * 
 	 */
-	public function curlupdateBbticket(int $fk_booking_history, array $invoice)
+	public function curlupdateBbticket(array $invoice, $fk_booking_history = null)
 	{
 		global $user;
-		$sql = "SELECT rowid FROM llx_bbus_bbticket WHERE invoice_number = '{$invoice['id']}'";
+		ApiBbusLog::appLog("curlupdateBbticket______START");
+		$sql = "SELECT rowid FROM llx_bbus_bbticket WHERE invoice_number = '{$invoice['id']}' OR invoice_number = '{$invoice['ref']}'";
+		ApiBbusLog::appLog("{$sql}");
 		$data = $this->db->query($sql);
 		if (!empty($this->db->error())) {
 			ApiBbusLog::appLog("{$this->db->error()}");
 			throw new RestException(401, "{$this->db->error()}");
 		}
-		$data = $this->db->query($sql);
 		if ($this->db->num_rows($data) > 0) {
 			while ($row = $this->db->fetch_object($data)) {
+				$booking_history_id = is_null($fk_booking_history) ? 'booking_history_id = null,' : 'booking_history_id = ' . $fk_booking_history . ',';
 				$sql = "UPDATE llx_bbus_bbticket
-					SET booking_history_id = {$fk_booking_history}, invoice_number = '{$invoice['ref']}'
+					SET {$booking_history_id} invoice_number = '{$invoice['ref']}'
 					WHERE rowid = {$row->rowid}";
+				ApiBbusLog::appLog("{$sql}");
 				$this->db->query($sql);
 			}
 		} else {
-			ApiBbusLog::appLog("api_booking: updateBbticket error");
-			throw new RestException(401, 'api_booking: updateBbticket error');
+			ApiBbusLog::appLog("api_booking: curlupdateBbticket error");
+			ApiBbusLog::appLog("{$this->db->error()}");
+			throw new RestException(401, 'api_booking: curlupdateBbticket error');
 		}
 	}
 
@@ -1116,9 +1169,10 @@ class BookingApi extends DolibarrApi
 	{
 		$server_host = $this->getServerHost($type_id);
 		if ($server_host == 'excelia') {
-			ApiBbusLog::appLog("getAvailableSpaces");
+			ApiBbusLog::appLog("getAvailableSpaces: excelia");
 			return $this->localAvailablePlaces($date_from, $date_to, $product_id, $participant_number);
 		} else {
+			ApiBbusLog::appLog("getAvailableSpaces: bbus");
 			$params = compact('date_from', 'date_to', 'product_id', 'participant_number');
 			$postFields = json_encode($params);
 			return $this->curlRunner('bookingapi/localavailableplaces', $postFields, 'POST', true);

+ 1375 - 0
custom/booking/class/api_booking.class.php.bak

@@ -0,0 +1,1375 @@
+<?php
+/* Copyright (C) 2015   Jean-François Ferry     <jfefe@aternatik.fr>
+ * Copyright (C) 2023 Deák Ferenc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+use Luracast\Restler\RestException;
+
+// dol_include_once('/booking/class/bookinglog.class.php');
+
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/api_bbus.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/api_bbus_log.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/apiinvoicehelper.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/apiproductlisthelper.class.php';
+require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
+require_once DOL_DOCUMENT_ROOT . '/compta/facture/class/facture.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/api_curl.class.php';
+require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/basicservices.class.php';
+
+/**
+ * \file    booking/class/api_booking.class.php
+ * \ingroup booking
+ * \brief   File for API management of bookinglog.
+ */
+
+/**
+ * API class for booking bookinglog
+ *
+ * @class  DolibarrApiAccess {@requires user,external}
+ */
+class BookingApi extends DolibarrApi
+{
+	use CurlApi;
+
+	/**
+	 * @var BookingLog $bookinglog {@type BookingLog}
+	 */
+	public $bookinglog;
+	private User $user;
+
+	/**
+	 * Constructor
+	 *
+	 * @url     GET /
+	 *
+	 */
+	public function __construct()
+	{
+		global $db, $user;
+		$this->db = $db;
+		$this->user = $user;
+		// $this->bookinglog = new BookingLog($this->db);
+	}
+
+	//P4NR6zhWHage
+	//!!!types:
+
+	/**
+	 * Get types of a bookable events
+	 *
+	 * Return an array with bookable events type list
+	 *
+	 * @return 	array|mixed data without useless information
+	 *
+	 * @url	GET types
+	 *
+	 * @throws RestException 401 Not allowed
+	 * @throws RestException 404 Not found
+	 */
+	public function types()
+	{
+		dol_include_once('/eventwizard/class/eventdetails.class.php');
+		$EventDetails = new EventDetails($this->db);
+		return $EventDetails->fields['type']['arrayofkeyval'];
+		if (!DolibarrApiAccess::$user->rights->booking->types->read) {
+			throw new RestException(401);
+		}
+
+		// dol_include_once('/booking/class/bookinglog.class.php');
+
+		// $result = $this->bookinglog->fetch($id);
+		// if (!$result) {
+		// throw new RestException(404, 'BookingLog not found');
+		// }
+
+		// if (!DolibarrApi::_checkAccessToResource('bookinglog', $this->bookinglog->id, 'booking_bookinglog')) {
+		// throw new RestException(401, 'Access to instance id='.$this->bookinglog->id.' of object not allowed for login '.DolibarrApiAccess::$user->login);
+		// }
+
+		// return $this->_cleanObjectDatas($this->bookinglog);
+	}
+
+	/**
+	 * Get bookable events
+	 *
+	 * Return an array with bookable events list
+	 *
+	 * @return 	array|mixed data without useless information
+	 * 
+	 * @param 	int 		$onlyBookable 
+	 * @param 	int 		$withDetails 
+	 * @param 	int 		$withDates 
+	 * @param 	int 		$withAviability
+	 * @url	GET events
+	 *
+	 * @throws RestException 401 Not allowed
+	 * @throws RestException 404 Not found
+	 */
+	public function events($onlyBookable = 1, $withDetails = 0, $withDates = 0, $withAviability = 0)
+	{
+		global $db, $conf;
+		// if (!DolibarrApiAccess::$user->rights->booking->events->read) {
+		// throw new RestException(401);
+		// }
+
+		$obj_ret = [];
+
+		// if (!DolibarrApiAccess::$user->rights->agenda->myactions->read) {
+		// throw new RestException(401, "Insufficient rights to read events");
+		// }
+
+		$sql = "SELECT DISTINCT ed.rowid as id";
+		if (!empty($conf->societe->enabled)) {
+			// if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) {
+			// $sql .= ", sc.fk_soc, sc.fk_user"; // We need these fields in order to filter by sale (including the case where the user can only see his prospects)
+			// }
+		}
+		$sql .= " ,ed.label ";
+		$sql .= " ,eld.label as from"; //... ??
+		$sql .= " ,ela.label as to"; //...
+
+		$sql .= " FROM " . MAIN_DB_PREFIX . "actioncomm as t";
+		$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "eventwizard_eventdetails ed ON ed.rowid = t.fk_element";
+		$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "eventwizard_eventlocation eld ON eld.rowid = ed.fk_elventlocation_departure";
+		$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "eventwizard_eventlocation ela ON ela.rowid = ed.fk_elventlocation_arrival";
+		$sql .= " WHERE code = 'AC_EVENT' ";
+		$sql .= " AND t.elementtype = 'eventdetails@eventwizard'";
+		if ($onlyBookable) {
+			$sql .= " AND t.datep >  CURRENT_TIMESTAMP";
+		}
+		$result = $this->db->query($sql);
+
+		if ($result) {
+			$i = 0;
+			// $num = $this->db->num_rows($result);
+			// $min = min($num, ($limit <= 0 ? $num : $limit));
+			while ($event = $this->db->fetch_object($result)) {
+				if ($withDetails) {
+					$event->details = $this->details($event->id);
+				}
+				if ($withDates) {
+					//... $withAviability
+					$event->dates = $this->dates($event->id);
+				}
+				$obj_ret[] = $event;
+			}
+		} else {
+			throw new RestException(503, 'Error when retrieve Agenda Event list : ' . $this->db->lasterror());
+		}
+		if (!count($obj_ret)) {
+			throw new RestException(404, 'No Agenda Event found');
+		}
+		return $obj_ret;
+	}
+
+	/**
+	 * Get event dates
+	 *
+	 * Return an array with event dates list
+	 *
+	 * @return 	array|mixed data without useless information
+	 *
+	 * @param 	int 		$id 
+	 * @param 	int 		$onlyBookable 
+	 * @param 	string 		$json 
+	 * @url	GET dates
+	 *
+	 * @throws RestException 401 Not allowed
+	 * @throws RestException 404 Not found
+	 */
+	public function dates($id, $onlyBookable = 1, $json = null)
+	{
+		global $db, $conf;
+		// if (!DolibarrApiAccess::$user->rights->booking->events->read) {
+		// throw new RestException(401);
+		// }
+
+		$obj_ret = [];
+
+		// if (!DolibarrApiAccess::$user->rights->agenda->myactions->read) {
+		// throw new RestException(401, "Insufficient rights to read events");
+		// }
+
+		$sql = "SELECT t.id ";
+		if (!empty($conf->societe->enabled)) {
+			// if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) {
+			// $sql .= ", sc.fk_soc, sc.fk_user"; // We need these fields in order to filter by sale (including the case where the user can only see his prospects)
+			// }
+		}
+		$sql .= " ,ed.label, t.note ";
+		$sql .= " ,t.datep as start, t.datep2 as end";
+		$sql .= " ,eld.label as from"; //... ??
+		$sql .= " ,ela.label as to"; //...
+
+		$sql .= " FROM " . MAIN_DB_PREFIX . "actioncomm as t";
+		// if (!empty($conf->societe->enabled)) {
+		// if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) {
+		// $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; // We need this table joined to the select in order to filter by sale
+		// }
+		// }
+		$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "eventwizard_eventdetails ed ON ed.rowid = t.fk_element";
+		$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "eventwizard_eventlocation eld ON eld.rowid = ed.fk_elventlocation_departure";
+		$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "eventwizard_eventlocation ela ON ela.rowid = ed.fk_elventlocation_arrival";
+		// $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."eventwizard_eventoption eo ON el.rowid = ed.fk_element";
+		// $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."eventwizard_eventproduct eo ON el.rowid = ed.fk_element";
+		// $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."eventwizard_ eo ON el.rowid = ed.fk_element";
+		// $sql .= ' WHERE t.entity IN ('.getEntity('agenda').')';
+		$sql .= " WHERE code = 'AC_EVENT' ";
+		$sql .= " AND t.fk_element = " . (int) $this->db->escape($id);
+		$sql .= " AND t.elementtype = 'eventdetails@eventwizard'";
+		$sql .= " AND t.datep >  CURRENT_TIMESTAMP";
+		// if (!empty($conf->societe->enabled)) {
+		// if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) {
+		// $sql .= " AND t.fk_soc = sc.fk_soc";
+		// }
+		// }
+		// if ($user_ids) {
+		// $sql .= " AND t.fk_user_action IN (".$this->db->sanitize($user_ids).")";
+		// }
+		// if ($socid > 0) {
+		// $sql .= " AND t.fk_soc = ".((int) $socid);
+		// }
+		// Insert sale filter
+		// if ($search_sale > 0) {
+		// $sql .= " AND sc.fk_user = ".((int) $search_sale);
+		// }
+		// Add sql filters
+		// if ($sqlfilters) {
+		// $errormessage = '';
+		// if (!DolibarrApi::_checkFilters($sqlfilters, $errormessage)) {
+		// throw new RestException(503, 'Error when validating parameter sqlfilters -> '.$errormessage);
+		// }
+		// $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)';
+		// $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")";
+		// }
+
+		// $sql .= $this->db->order($sortfield, $sortorder);
+		// if ($limit) {
+		// if ($page < 0) {
+		// $page = 0;
+		// }
+		// $offset = $limit * $page;
+
+		// $sql .= $this->db->plimit($limit + 1, $offset);
+		// }
+		$result = $this->db->query($sql);
+
+		if ($result) {
+			$i = 0;
+			$num = $this->db->num_rows($result);
+			// $min = min($num, ($limit <= 0 ? $num : $limit));
+			while ($i < $num) {
+				$obj_ret[] = $this->db->fetch_object($result);
+				$i++;
+			}
+		} else {
+			throw new RestException(503, 'Error when retrieve Agenda Event list : ' . $this->db->lasterror());
+		}
+		if (!count($obj_ret)) {
+			throw new RestException(404, 'No Agenda Event found');
+		}
+		return $obj_ret;
+	}
+
+	/**
+	 * Post booking (draft)
+	 *
+	 * Return an array with draft bookings list
+	 *
+	 * @return 	array|mixed data without useless information
+	 *
+	 * @param 	int 	$event
+	 * @param 	array 	$details 
+	 * @url	POST set
+	 *
+	 * @access protected
+	 * @throws RestException 401 Not allowed
+	 * @throws RestException 404 Not found
+	 */
+	public function set($event, $details)
+	{
+		if (!DolibarrApiAccess::$user->rights->booking->events->write) {
+			throw new RestException(401);
+		}
+	}
+
+	/**
+	 * Post booking validation
+	 *
+	 * Return an array with bookings list
+	 *
+	 * @return 	array|mixed data without useless information
+	 *
+	 * @param 	int 	$event
+	 * @param 	array 	$drafts 
+	 * @url	POST validate
+	 *
+	 * @access protected
+	 * @throws RestException 401 Not allowed
+	 * @throws RestException 404 Not found
+	 */
+	public function validate($event, $drafts)
+	{
+		if (!DolibarrApiAccess::$user->rights->booking->events->write) {
+			throw new RestException(401);
+		}
+	}
+
+	/**
+	 * Get bookings list
+	 *
+	 * Return an array with the user's bookings list
+	 *
+	 * @return 	array|mixed data without useless information
+	 *
+	 * @url	POST bookings
+	 *
+	 * @access protected
+	 * @throws RestException 401 Not allowed
+	 * @throws RestException 404 Not found
+	 */
+	public function bookings()
+	{
+		if (!DolibarrApiAccess::$user->rights->booking->events->write) {
+			throw new RestException(401);
+		}
+	}
+
+	/**
+	 * Get cron to clean booking draftInvoices
+	 *
+	 * Return an array with cleaned list
+	 *
+	 * @return 	array|mixed data without useless information
+	 *
+	 * @url	GET cron
+	 *
+	 * @access protected
+	 * @throws RestException 401 Not allowed
+	 * @throws RestException 404 Not found
+	 */
+	public function cron()
+	{
+		if (!DolibarrApiAccess::$user->rights->booking->events->write) {
+			throw new RestException(401);
+		}
+	}
+
+	/**
+	 * Get details of a bookable event
+	 *
+	 * Return an array with details
+	 *
+	 * @return 	array|mixed data without useless information
+	 *
+	 * @param 	int 	$id
+	 * @url	GET details
+	 *
+	 * @access protected
+	 * @throws RestException 401 Not allowed
+	 * @throws RestException 404 Not found
+	 */
+	public function details($id)
+	{
+		$details = [
+			'tmp' => [],
+			'product' => [],
+			'min' => 0,
+			'id_event' => $id,
+			// 'variants' => []
+		];
+		$variants = [];
+		dol_include_once('/eventwizard/class/eventdetails.class.php');
+		$EventDetails = new EventDetails($this->db);
+		if ($EventDetails->fetch($id)) {
+			$sql = "SELECT ep.rowid ,ep.fk_eventdetails, ep.fk_product, ep.type, ep.amount, ep.qty ";
+			$sql .= " , p.label, p.price "; //price	price_ttc	price_min	price_min_ttc
+			$sql .= " FROM " . MAIN_DB_PREFIX . "eventwizard_eventproduct ep ";
+			$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "product p ON p.rowid = ep.fk_product ";
+			// $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_attribute_combination ac ON ac.fk_product_parent = ep.fk_product ";
+			// $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."eventwizard_eventoption eo ON eo.rowid = ep.fk_eventoption ";
+			// $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."eventwizard_eventdetails ed ON ed.rowid = ep.fk_eventdetails ";
+
+			$sql .= " WHERE ep.fk_eventdetails = " . (int) $this->db->escape($id);
+			// return $sql;
+			$result = $this->db->query($sql);
+			if ($result) {
+				$variants_ids = [];
+				while ($d = $this->db->fetch_object($result)) {
+					$variants_ids[] = $d->fk_product;
+					// if(is_null($d->fk_product_child)){}
+					$d->variants = [];
+					// $d->minP = (is_null($d->amount))?(float)$d->price:(float)$d->amount;
+					$details['tmp']['' . $d->fk_product] = $d;
+				}
+
+				$sql2 = "SELECT p.rowid, p.label, p.price ";
+				$sql2 .= ", ac.fk_product_parent, ac.fk_product_child, ac.variation_price, ac.variation_price_percentage "; //	variation_price	variation_price_percentage	variation_weight	variation_ref_ext	entity
+				$sql2 .= "FROM " . MAIN_DB_PREFIX . "product_attribute_combination ac ";
+				$sql2 .= " LEFT JOIN " . MAIN_DB_PREFIX . "product p ON ac.fk_product_child = p.rowid ";
+				$sql2 .= " WHERE ac.fk_product_parent IN (" . implode(",", $variants_ids) . ") ";
+
+				$result2 = $this->db->query($sql2);
+				if ($result2) {
+					while ($v = $this->db->fetch_object($result2)) {
+						$d = $details['tmp']['' . $v->fk_product_parent];
+						$d->variants[] = $this->product2out($v);
+						if (!is_null($d->amount)) { //... - ??? %?
+							$d->minP = (float) $d->amount;
+						} elseif (!isset($d->minP)) {
+							$d->minP = $v->price;
+						} elseif ($d->minP > $v->price) {
+							$d->minP = $v->price;
+						}
+						$details['tmp']['' . $v->fk_product_parent] = $d;
+					}
+				}
+				foreach ($details['tmp'] as $k => $v) {
+					$p = $this->product2out($v);
+					$p["minP"] = (is_null($v->amount)) ? (!isset($d->minP)) ? (float) $v->price : (float) $v->minP : (float) $v->amount;
+					$p["variants"] = $v->variants;
+					$details['product'][] = $p;
+					if ($p["type"] == 1) {
+						$details['min'] += $p["minP"];
+					}
+				}
+				unset($details['tmp']);
+			} else {
+				throw new RestException(503, 'Error when retrieve Agenda Event list : ' . $this->db->lasterror());
+			}
+			// $EventDetails->getLinesArray();
+			// print_r($EventDetails->lines);
+			return $details;
+
+			//$variations
+			// $variations = 
+
+		}
+	}
+
+	protected function product2out($product)
+	{
+		return [
+			"id" => $product->fk_product,
+			"type" => $product->type, //1,2,3
+			"fixed_price" => $product->amount,
+			"max" => $product->qty,
+			"label" => $product->label,
+			"price" => $product->price,
+		];
+	}
+
+	/**
+	 * Get all products by type
+	 *
+	 * Return an array with details
+	 *
+	 * @return 	array|mixed data without useless information
+	 *
+	 * @param 	int 	$type_id
+	 * @param 	string 	$from_date
+	 * @param 	string 	$to_date
+	 * 
+	 * @url	POST getallavailableproducts
+	 *
+	 * @throws RestException 401 Not allowed
+	 * @throws RestException 404 Not found
+	 */
+	public function getAllAvailableProducts(int $type_id, string $from_date, string $to_date)
+	{
+		$date = new DateTime($to_date);
+		$date->modify('+1 day');
+		$to_date = $date->format('Y-m-d');
+		$TMPArray = [];
+		$productsArray = [];
+		$sql = "SELECT 
+		pr.rowid
+		from llx_actioncomm as ac 
+		LEFT JOIN llx_actioncomm_extrafields as ace ON ace.fk_object = ac.id
+		INNER JOIN llx_eventwizard_eventdetails as ed ON ed.rowid = ac.fk_element
+		INNER JOIN llx_eventwizard_eventproduct as ep ON ep.fk_eventdetails = ed.rowid
+		INNER JOIN llx_product as pr ON pr.rowid = ep.fk_product
+		where ac.datep > '{$from_date} 00:00:00' AND ac.datep2 < '{$to_date} 00:00:00'
+		AND ac.code = 'AC_EVENT'
+		AND ed.type = {$type_id}
+		AND ace.max_num - COALESCE(ace.participants, 0) > 1
+		GROUP BY pr.rowid";
+		$result = $this->db->query($sql);
+		if ($this->db->num_rows($result) > 0) {
+			while ($row = $this->db->fetch_object($result)) {
+				$TMPArray[] = $row->rowid;
+			}
+			foreach ($TMPArray as $productItem) {
+				$ApiProductListHelper = new ApiProductListHelper();
+				$array = $ApiProductListHelper->list('t.ref', 'ASC', '', '', '', '', "(t.rowid:=:{$productItem})");
+				$productsArray[] = $array[0];
+			}
+		}
+
+		return $productsArray;
+	}
+
+	/**
+	 * First step of the eventhandling
+	 *
+	 * Return an array with details
+	 *
+	 * @return 	array|mixed data without useless information
+	 *
+	 * @param 	int 	$type_id			//selected evet from llx_event
+	 * @param 	int 	$fk_event			//selected evet from llx_event
+	 * @param 	int 	$reservations		//numbers of reservations
+	 * @param 	int 	$product_id			//Product
+	 * @param 	string	$sendId				//ID
+	 * 
+	 * @url	POST firsteventstep
+	 * @access protected
+	 * @throws RestException 401 Not allowed
+	 * @throws RestException 404 Not found
+	 * @throws RestException 506 No available spaces
+	 * 
+	 */
+	public function firstEventStep(int $type_id, int $fk_event, int $reservations, int $product_id, string $sendId)
+	{
+		ApiBbusLog::appLog("{$sendId}: firstEventStep");
+		if (!DolibarrApiAccess::$user->rights->facture->creer) {
+			ApiBbusLog::appLog("{$sendId} Insufficient rights");
+			throw new RestException(401, 'Insufficient rights');
+		}
+		$this->createLog($sendId, $fk_event, $reservations, $product_id);
+
+
+		$server_host = $this->getServerHost($type_id);
+		if ($server_host == 'excelia') {
+			if ($this->noLocalEmptySpaces($fk_event, $reservations)) {
+				throw new RestException(506, 'No available spaces');
+			}
+			$createdPreOrderOBJ = $this->localcreatedpreorderobj($sendId, $fk_event, $reservations, $product_id);
+		} else {
+			ApiBbusLog::appLog("{$sendId}: firstEventStep: Bbus");
+			$params = compact('fk_event', 'reservations');
+			$postFields = json_encode($params);
+			$noavailableSpaces = json_decode($this->curlRunner('bookingapi/curlnoLocalEmptySpaces', $postFields, 'POST', true))->result;
+			if ($noavailableSpaces) {
+				ApiBbusLog::appLog("{$sendId}: firstEventStep: No available spaces");
+				throw new RestException(506, 'No available spaces');
+			}
+			$params = compact('sendId', 'fk_event', 'reservations', 'product_id');
+			$postFields = json_encode($params);
+			$createdPreOrderOBJ = $this->curlRunner('bookingapi/localcreatedpreorderobj', $postFields, 'POST', true);
+		}
+		ApiBbusLog::appLog("{$sendId}: " . json_encode($createdPreOrderOBJ));
+		return $createdPreOrderOBJ;
+	}
+
+	function localcreatedpreorderobj($sendId, $fk_event, $reservations, $product_id)
+	{
+		ApiBbusLog::appLog("{$sendId}: localcreatedpreorderobj");
+
+		$apiInvoiceHelper = new ApiInvoiceHelper;
+		$createdPreOrderOBJ = [];
+		for ($i = 1; $i <= $reservations; $i++) {
+			dol_include_once('/comm/action/class/actioncomm.class.php');
+			dol_include_once('/custom/booking/class/preorder.class.php');
+			$apiInvoiceHelper->increaseParticipant($fk_event);
+
+			$lines = $apiInvoiceHelper->getProductLines($product_id);
+			foreach ($lines as $line) {
+				$preOrderObj = new PreOrder($this->db);
+				$preOrderObj->ref = $this->generateRandomString();
+				$preOrderObj->fk_event = $fk_event;
+				$preOrderObj->fk_product = $product_id;
+				$result = $preOrderObj->create($this->user);
+				if ($result > 0) {
+					$createdPreOrderOBJ[] = [
+						'id' => $preOrderObj->id,
+						'ref' => $preOrderObj->ref,
+					];
+				} else {
+					ApiBbusLog::appLog("{$sendId}: " . json_encode(['error' => $preOrderObj->errors]));
+					ApiBbusLog::appLog("{$sendId}: Unsaved event: " . $fk_event);
+					throw new RestException(401, 'Unsaved event: ' . $fk_event);
+				}
+			}
+		}
+		ApiBbusLog::appLog(json_encode($createdPreOrderOBJ));
+		return $createdPreOrderOBJ;
+	}
+
+	function createLog($sendId, $fk_event, $reservations, $product_id)
+	{
+		/**
+		 * LOG SECTION
+		 */
+		ApiBbusLog::eventLog("{$sendId} === NEW INVOICE ===");
+		dol_syslog("{$sendId} === NEW INVOICE ===", LOG_INFO, 0);
+		ApiBbusLog::eventLog("{$sendId} REQUEST: {$sendId}");
+		dol_syslog("{$sendId} REQUEST: {$sendId}", LOG_INFO, 0);
+		ApiBbusLog::eventLog("{$sendId} User: {$this->user->firstname} {$this->user->lastname} (ID: {$this->user->id})");
+		dol_syslog("{$sendId} User: {$this->user->firstname} {$this->user->lastname} (ID: {$this->user->id})", LOG_INFO, 0);
+		ApiBbusLog::eventLog("{$sendId} " . json_encode([
+			'fk_event' => $fk_event,
+			'reservations' => $reservations,
+			'product_id' => $product_id,
+			'sendId' => $sendId,
+		]));
+	}
+
+	function generateRandomString($length = 10)
+	{
+		$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+		$charactersLength = strlen($characters);
+		$randomString = '';
+		for ($i = 0; $i < $length; $i++) {
+			$randomString .= $characters[rand(0, $charactersLength - 1)];
+		}
+
+		return $randomString;
+	}
+
+	/**
+	 * Save event data in Bookinghistory
+	 *
+	 * Return an array with details
+	 *
+	 * @return 	array|mixed data without useless information
+	 *
+	 * @param 	int 	$fk_event			//selected evet from llx_event
+	 * @param 	int 	$fk_facture			//facture rowid from llx_facture
+	 * @param 	int 	$fk_eventproduct	//eventproduct rowid from llx_eventwizard_eventproduct
+	 * @param 	string 	$ref				//ref
+	 * 
+	 * @url	POST saveeventdata
+	 * @access protected
+	 * @throws RestException 401 Not allowed
+	 * @throws RestException 404 Not found
+	 */
+	public function saveEventData(int $fk_event, int $fk_facture = null, int $fk_eventproduct = null, string $ref = null)
+	{
+
+		ApiBbusLog::appLog("saveEventData");
+
+		dol_include_once('/custom/booking/class/bookinghistory.class.php');
+		dol_include_once('/comm/action/class/actioncomm.class.php');
+		$actionCommObj = new ActionComm($this->db);
+		$sql = "SELECT fk_element, datep, datep2 FROM " . MAIN_DB_PREFIX . $actionCommObj->table_element . " WHERE id = {$fk_event}";
+		ApiBbusLog::appLog("{$sql}");
+
+		$result = $this->db->query($sql);
+		if ($this->db->num_rows($result) > 0) {
+			$row = $this->db->fetch_object($result);
+			$BookingHistory = new BookingHistory($this->db);
+			$BookingHistory->fk_event = $fk_event;
+			$BookingHistory->fk_facture = $fk_facture;
+			$BookingHistory->fk_event_detail = $row->fk_element;
+			$BookingHistory->fk_eventproduct = $fk_eventproduct;
+			$BookingHistory->date_start = strtotime($row->datep);
+			$BookingHistory->date_end = strtotime($row->datep2);
+			$BookingHistory->invoice_number = $ref;
+			$result = $BookingHistory->create($this->user);
+			if ($result > 0) {
+				ApiBbusLog::appLog("saveEventData: OK {$BookingHistory->id}");
+				return $BookingHistory->id;
+			} else {
+				foreach ($BookingHistory->errors as $error) {
+					ApiBbusLog::appLog("saveEventData: {$error}");
+				}
+				ApiBbusLog::appLog("saveEventData: ERROR");
+				throw new RestException(401, 'Unsaved');
+			}
+		} else {
+			throw new RestException(401, 'No Event record');
+		}
+	}
+
+	/**
+	 * Validate and save invoice
+	 *
+	 * Return an array with details
+	 *
+	 * @return 	array|mixed data without useless information
+	 *
+	 * @param 	int 	$type_id			Type_id
+	 * @param 	string	$sendId				sendID
+	 * @param  	array 	$payment			Invoice payment
+	 * @param  	array 	$preorder			preorder
+	 * @param  	array 	$invoice			Invoice
+	 * @param  	string 	$cardPaymentLog		Card payment log data
+	 * 
+	 * @url	POST validateandsaveinvoice
+	 * @access protected
+
+	 * @throws RestException 401 Not allowed
+	 * @throws RestException 404 Not found
+	 * @throws RestException 506 No available spaces
+	 * 
+	 */
+	public function validateandsaveinvoice(int $type_id, string $sendId, array $payment, array $preorder, array $invoice, string $cardPaymentLog = '')
+	{
+		ApiBbusLog::appLog("validateandsaveinvoice");
+
+		global $db;
+		$server_host = $this->getServerHost($type_id);
+		$createdInvoiceArray = $this->localValidateAndSaveInvoice($invoice, $preorder, $payment, $cardPaymentLog, $sendId, $server_host);
+		if ($server_host == 'excelia') {
+			ApiBbusLog::appLog("validateandsaveinvoice: Excelia");
+			ApiBbusLog::appLog(json_encode($preorder));
+			foreach ($preorder as $order) {
+				$sql = "SELECT fk_product, fk_event  FROM llx_booking_preorder WHERE rowid = {$order}";
+				ApiBbusLog::appLog("{$sql}");
+				$data = $db->query($sql);
+				if ($db->num_rows($data) > 0) {
+					while ($row = $db->fetch_object($data)) {
+						ApiBbusLog::appLog("while");
+						$bookingHistory = $this->saveEventData((int)$row->fk_event, $createdInvoiceArray[0]['invoice']['id']);
+						$this->updateBbticket($bookingHistory, $createdInvoiceArray[0]['invoice']);
+						$this->deletePreorder($preorder);
+					}
+				}
+			}
+		} else {
+			ApiBbusLog::appLog("validateandsaveinvoice: bbus");
+			foreach ($preorder as $rowid) {
+				$params = compact('rowid');
+				$postFields = json_encode($params);
+				$preorderArray = $this->curlRunner('bookingapi/localpreorderarray', $postFields, 'POST', true);
+				$saveEventdataPostFields = '{"fk_event":"' . (int)$preorderArray->fk_event . '","ref":"' . $createdInvoiceArray[0]['invoice']['ref'] . '","fk_facture":"' . null . '","fk_eventproduct":"' . null . '"}';
+				$bookingHistory = $this->curlRunner('bookingapi/saveEventData', $saveEventdataPostFields, 'POST', true);
+				$array['fk_booking_history'] = $bookingHistory;
+				$array['invoice'] = $createdInvoiceArray[0]['invoice'];
+				$updateBBticketPostFields = json_encode($array);
+				$this->curlRunner('bookingapi/curlUpdateBbticket', $updateBBticketPostFields, 'POST', true);
+				ApiBbusLog::appLog("validateandsaveinvoice: {$postFields}");
+				$this->curlRunner('bookingapi/curlDeletePreorder', $postFields, 'POST', true);
+			}
+		}
+		ApiBbusLog::appLog("validateandsaveinvoice: END");
+
+		return $createdInvoiceArray;
+	}
+
+	public function localValidateAndSaveInvoice($invoice, $preorder, $payment, $cardPaymentLog, $sendId, $server_host)
+	{
+		ApiBbusLog::eventLog("localValidateAndSaveInvoice");
+
+		global $user, $db;
+		$server_host_curl = false;
+		$createdInvoiceData = [];
+		$apiInvoiceHelper = new ApiInvoiceHelper;
+		$bbusApi = new BBus();
+		foreach ($preorder as $rowid) {
+			$params = compact('rowid');
+			$postFields = json_encode($params);
+			if ($server_host == 'excelia') {
+				ApiBbusLog::appLog("localValidateAndSaveInvoice: Excelia");
+				$preorderArray = $this->localpreorderarray($rowid);
+			} else {
+				ApiBbusLog::appLog("localValidateAndSaveInvoice: bbus");
+				$server_host_curl = true;
+				$preorderArray = $this->curlRunner('bookingapi/localpreorderarray', $postFields, 'POST', true);
+			}
+			$lines = $apiInvoiceHelper->getProductLinesWithoutDiscount($preorderArray->fk_product);
+			//$lines = $apiInvoiceHelper->createLinesForCrossShopping($preorderArray->fk_product);
+			$createdInvoiceArray = $bbusApi->invoice($invoice, $lines, $payment, $cardPaymentLog = '', $sendId = '', $server_host_curl);
+			$createdInvoiceData[] = $createdInvoiceArray;
+		}
+		ApiBbusLog::appLog("Visszaküld");
+		$asd = json_encode($createdInvoiceData);
+		ApiBbusLog::appLog("{$asd}");
+
+		return $createdInvoiceData;
+	}
+
+	public function localpreorderarray(int $rowid)
+	{
+		global $user, $db;
+		$result = [];
+		$sql = "SELECT fk_product, fk_event  FROM llx_booking_preorder WHERE rowid = {$rowid}";
+		$data = $db->query($sql);
+		if ($db->num_rows($data) > 0) {
+			while ($row = $db->fetch_object($data)) {
+				$result = $row;
+			}
+		}
+		return $result;
+	}
+
+	/**
+	 * curllocalpreorderarray
+	 *
+	 * Return an array with details
+	 *
+	 * @return 	array|mixed data without useless information
+	 *
+	 * @param 	int 	$rowid			
+	 * 
+	 * @url	POST curllocalpreorderarray
+	 * @access protected
+
+	 * @throws RestException 401 Not allowed
+	 * @throws RestException 404 Not found
+	 * @throws RestException 506 No available spaces
+	 * 
+	 */
+	public function curllocalpreorderarray(int $rowid)
+	{
+		global $user, $db;
+		$result = [];
+		$sql = "SELECT fk_product, fk_event  FROM llx_booking_preorder WHERE rowid = {$rowid}";
+		$data = $db->query($sql);
+		if ($db->num_rows($data) > 0) {
+			while ($row = $db->fetch_object($data)) {
+				$result = $row;
+			}
+		}
+		return $result;
+	}
+
+	public function updateBbticket($invoice, $fk_booking_history = null)
+	{
+		ApiBbusLog::appLog("updateBbticket_________START");
+
+		global $user;
+		$sql = "SELECT rowid FROM llx_bbus_bbticket WHERE fk_facture = {$invoice['id']}";
+		$data = $this->db->query($sql);
+		if (!empty($this->db->error())) {
+			ApiBbusLog::appLog("{$this->db->error()}");
+			throw new RestException(401, "{$this->db->error()}");
+		}
+		if ($this->db->num_rows($data) > 0) {
+			while ($row = $this->db->fetch_object($data)) {
+				$sql = "UPDATE llx_bbus_bbticket
+					SET booking_history_id = {$fk_booking_history}, invoice_number = '{$invoice['ref']}'
+					WHERE rowid = {$row->rowid}";
+				ApiBbusLog::appLog("{$sql}");
+				$this->db->query($sql);
+			}
+		} else {
+			ApiBbusLog::appLog("{$this->db->error()}");
+			ApiBbusLog::appLog("api_booking (Excelia): updateBbticket error");
+			throw new RestException(401, 'api_booking: updateBbticket error');
+		}
+	}
+
+	/**
+	 * curlupdateBbticket
+	 *
+	 * Return an array with details
+	 *
+	 *
+	 * @param  	array 	$invoice					invoice
+	 * 
+	 * @url	POST curlupdateBbticket
+	 * @access protected
+
+	 * @throws RestException 401 Not allowed
+	 * @throws RestException 404 Not found
+	 * @throws RestException 506 No available spaces
+	 * 
+	 */
+	public function curlupdateBbticket(array $invoice, string $fk_booking_history = null)
+	{
+		ApiBbusLog::appLog("curlupdateBbticket______START");
+		global $user;
+		$sql = "SELECT rowid FROM llx_bbus_bbticket WHERE invoice_number = '{$invoice['id']}'";
+		$data = $this->db->query($sql);
+		if (!empty($this->db->error())) {
+			ApiBbusLog::appLog("{$this->db->error()}");
+			throw new RestException(401, "{$this->db->error()}");
+		}
+		$data = $this->db->query($sql);
+		if ($this->db->num_rows($data) > 0) {
+			while ($row = $this->db->fetch_object($data)) {
+				$booking_history_id = is_null($fk_booking_history) ? 'booking_history_id = null,' : '';
+				$sql = "UPDATE llx_bbus_bbticket
+					SET {$booking_history_id} invoice_number = '{$invoice['ref']}'
+					WHERE rowid = {$row->rowid}";
+				ApiBbusLog::appLog("{$sql}");
+				$this->db->query($sql);
+			}
+		} else {
+			ApiBbusLog::appLog("api_booking: curlupdateBbticket error");
+			ApiBbusLog::appLog("{$this->db->error()}");
+			throw new RestException(401, 'api_booking: curlupdateBbticket error');
+		}
+	}
+
+	public function deletePreorder($preorder)
+	{
+		foreach ($preorder as $item) {
+			$sql = "DELETE FROM llx_booking_preorder WHERE rowid = {$item}";
+			$this->db->query($sql);
+		}
+	}
+
+	/**
+	 * curldeletePreorder
+	 *
+	 * Return an array with details
+	 *
+	 *
+	 * @param  	array 	$preorder			preorder
+	 * 
+	 * @url	POST curldeletePreorder
+	 * @access protected
+
+	 * @throws RestException 401 Not allowed
+	 * @throws RestException 404 Not found
+	 * @throws RestException 506 No available spaces
+	 * 
+	 */
+	public function curldeletePreorder($preorder)
+	{
+		foreach ($preorder as $item) {
+			$sql = "DELETE FROM llx_booking_preorder WHERE rowid = {$item}";
+			$this->db->query($sql);
+		}
+	}
+
+	public function eventErasuer()
+	{
+		global $user;
+		$limitDate = date("Y-m-d H:i:s", dol_now() - 1200);
+		$sql = "SELECT * FROM llx_booking_preorder WHERE date_creation < '{$limitDate}'";
+		//print $sql;exit;
+		$data = $this->db->query($sql);
+		if ($this->db->num_rows($data) > 0) {
+			while ($row = $this->db->fetch_object($data)) {
+				//print_r($row);
+				$actioncommObj = new ActionComm($this->db);
+				$resultActionComm = $actioncommObj->fetch($row->fk_event);
+				$actioncommObj->array_options['options_participants'] = $actioncommObj->array_options['options_participants'] - 1;
+				$actioncommObj->update($user);
+			}
+			//exit;
+			$sqlDelete = "DELETE FROM llx_booking_preorder WHERE date_creation < '{$limitDate}'";
+			$dataDelete = $this->db->query($sqlDelete);
+		}
+	}
+
+	/**
+	 * Increase Participants
+	 *
+	 * Return an array with details
+	 *
+	 * @return 	array|mixed data without useless information
+	 *
+	 * @param 	int 	$event_id			//rowid of evet from llx_event
+	 * 
+	 * @url	POST increaseparticipant
+	 *
+	 * @throws RestException 401 Not allowed
+	 * @throws RestException 404 Not found
+	 */
+	public function increaseParticipant(int $event_id)
+	{
+		dol_include_once('/comm/action/class/actioncomm.class.php');
+		global $user;
+		$actionComObj = new ActionComm($this->db);
+		$actionComObj->fetch($event_id);
+		$max_num = (int) $actionComObj->array_options['options_max_num'];
+		$buffer = (int) $actionComObj->array_options['options_buffer'];
+		$participants = (int) $actionComObj->array_options['options_participants'];
+		$increasedParticipants = $participants + 1;
+		if ($increasedParticipants > ($max_num + $buffer)) {
+			return 'full';
+		} elseif (($increasedParticipants > $max_num) && ($increasedParticipants <= ($max_num + $buffer))) {
+			$actionComObj->array_options['options_participants'] = $increasedParticipants;
+			$actionComObj->update($user);
+			return 'Buffer';
+		} else {
+			$actionComObj->array_options['options_participants'] = $increasedParticipants;
+			$actionComObj->update($user);
+			return 'OK';
+		}
+	}
+
+	/**
+	 * Reduce Participants
+	 *
+	 * Return an array with details
+	 *
+	 * @return 	array|mixed data without useless information
+	 *
+	 * @param 	int 	$event_id			//rowid of evet from llx_event
+	 * 
+	 * @url	POST reduceparticipant
+	 *
+	 * 
+	 * @throws RestException 401 Not allowed
+	 * @throws RestException 404 Not found
+	 */
+	public function reduceParticipant(int $event_id)
+	{
+		global $user;
+		dol_include_once('/comm/action/class/actioncomm.class.php');
+		$actionComObj = new ActionComm($this->db);
+		$actionComObj->fetch($event_id);
+		$participants = (int) $actionComObj->array_options['options_participants'];
+		$reducedParticipants = $participants - 1;
+		$actionComObj->array_options['options_participants'] = $reducedParticipants < 0 ? 0 : $reducedParticipants;
+		$actionComObj->update($user);
+		return 'OK';
+	}
+
+	/**
+	 * Create invoice
+	 * 
+	 * @param  array 	$invoice			Invoice data
+	 * @param  array 	$lines				Invoice lines
+	 * @param  array 	$payment			Invoice payment
+	 * @param  string 	$eventid			Event ID
+	 * @param  string 	$cardPaymentLog		Card payment log data
+	 * 
+	 * @access protected
+	 * @return array|mixed Data without useless information
+	 * 
+	 * @url POST /invoice
+	 */
+	public function invoice(array $invoice, array $lines, array $payment, string $cardPaymentLog = '', string $eventid = '', string $sendId = '')
+	{
+		if (empty($sendId)) {
+			$sendId = substr(md5(rand()), 0, 8);
+		}
+
+		/**
+		 * LOG SECTION
+		 */
+		ApiBbusLog::appLog("{$sendId} === NEW INVOICE ===");
+		dol_syslog("{$sendId} === NEW INVOICE ===", LOG_INFO, 0);
+		ApiBbusLog::appLog("{$sendId} REQUEST: {$sendId}");
+		dol_syslog("{$sendId} REQUEST: {$sendId}", LOG_INFO, 0);
+		ApiBbusLog::appLog("{$sendId} User: {$this->user->firstname} {$this->user->lastname} (ID: {$this->user->id})");
+		dol_syslog("{$sendId} User: {$this->user->firstname} {$this->user->lastname} (ID: {$this->user->id})", LOG_INFO, 0);
+		ApiBbusLog::appLog("{$sendId} " . json_encode([
+			'invoice' => $invoice,
+			'lines' => $lines,
+			'payment' => $payment,
+			'eventid' => $eventid,
+			'cardPaymentLog' => $cardPaymentLog
+		]));
+
+		/**
+		 * CHECK RIGHTS
+		 */
+		if (!DolibarrApiAccess::$user->rights->facture->creer) {
+			ApiBbusLog::appLog("{$sendId} Insufficient rights");
+			throw new RestException(401, 'Insufficient rights');
+		}
+
+
+		$apiInvoiceHelper = new ApiInvoiceHelper;
+		/**
+		 * handle invoice basic data
+		 */
+		$invoiceObj = $apiInvoiceHelper->createInvoice($invoice, $sendId);
+		/**
+		 * handle invoice line(s)
+		 */
+		$products = [];
+
+		//print $eventid;exit;
+
+		foreach ($lines as $line) {
+			$invoiceObj = $apiInvoiceHelper->addLineToInvoice($invoiceObj, $line, $sendId);
+			$this->increaseParticipant((int) $eventid);
+			$product = $apiInvoiceHelper->loadProductToResult($line);
+			if (!empty($product)) {
+				$products[] = $product;
+			}
+		}
+
+		$invoiceObj->fetch_lines();
+
+		foreach ($products as &$row) {
+			foreach ($invoiceObj->lines as $line) {
+				if ($line->product_ref == $row['ref']) {
+					//$row['price'] = $line->total_ttc;
+					$row['price'] = $line->multicurrency_total_ttc;
+					$row['total_tva'] = $line->total_tva;
+					//$row['total_tva'] = $line->multicurrency_total_tva;
+				}
+			}
+		}
+
+		return [
+			'sendId' => $sendId,
+			'invoice' => [
+				'id' => $invoiceObj->id,
+				'ref' => $invoiceObj->ref,
+				'total' => $invoiceObj->multicurrency_total_ttc
+			],
+			'products' => $products,
+		];
+	}
+
+	/**
+	 * Get all free spaces on a selected date
+	 *
+	 * Return an array with details
+	 *
+	 * @return 	array|mixed data without useless information
+	 *
+	 * @param 	int 	type_id
+	 * @param 	string 	date_from
+	 * @param 	string 	date_to
+	 * @param 	int 	product_id
+	 * @param 	int 	participant_number
+	 * 
+	 * @url	POST getavailablespaces
+	 *
+	 * @throws RestException 401 Not allowed
+	 * @throws RestException 404 Not found
+	 */
+	public function getAvailableSpaces(int $type_id, string $date_from, string $date_to, int $product_id, int $participant_number)
+	{
+		$server_host = $this->getServerHost($type_id);
+		if ($server_host == 'excelia') {
+			ApiBbusLog::appLog("getAvailableSpaces: bbus");
+			return $this->localAvailablePlaces($date_from, $date_to, $product_id, $participant_number);
+		} else {
+			$params = compact('date_from', 'date_to', 'product_id', 'participant_number');
+			$postFields = json_encode($params);
+			return $this->curlRunner('bookingapi/localavailableplaces', $postFields, 'POST', true);
+		}
+	}
+
+	/* 	private function getServerHost($type_id)
+	{
+		$basicServices = new BasicServices($this->db);
+		$resultBS = $basicServices->fetch($type_id);
+		return $basicServices->server_host;
+	} */
+
+	/**
+	 * Get all free spaces on a selected date
+	 *
+	 * Return an array with details
+	 *
+	 * @return 	array|mixed data without useless information
+	 *
+	 * @param 	string 	date_from
+	 * @param 	string 	date_to
+	 * @param 	int 	product_id
+	 * @param 	int 	participant_number
+	 * 
+	 * @url	POST localavailableplaces
+	 *
+	 * @throws RestException 401 Not allowed
+	 * @throws RestException 404 Not found
+	 */
+	public function localavailableplaces($date_from, $date_to, $product_id, $participant_number)
+	{
+		ApiBbusLog::appLog("localAvailablePlaces");
+		$array = [];
+		$date = new DateTime($date_to);
+		$date->modify('+1 day');
+		$date_to = $date->format('Y-m-d');
+		$sql = "SELECT 
+		ac.id, 
+		ac.datep, 
+		ace.max_num, 
+		ace.participants,
+		ace.buffer
+		FROM llx_actioncomm as ac 
+		LEFT JOIN llx_actioncomm_extrafields as ace ON ace.fk_object = ac.id
+		INNER JOIN llx_eventwizard_eventdetails as ed ON ac.fk_element = ed.rowid
+		INNER JOIN llx_eventwizard_eventproduct as ep ON ep.fk_eventdetails = ac.fk_element
+		WHERE ac.code = 'AC_EVENT'
+		AND ep.fk_product = {$product_id}
+		AND ac.datep > '{$date_from} 00:00:00' 
+		AND ac.datep2 < '{$date_to} 00:00:00'
+		ORDER BY ac.datep ASC";
+		$result = $this->db->query($sql);
+		while ($row = $this->db->fetch_object($result)) {
+			$max_num = $row->max_num;
+			$buffer = $row->buffer;
+			$participants = $row->participants;
+			$date = strtotime($row->datep);
+			$element['event_id'] = $row->id;
+			//$element['date'] = date("Y-m-d", $date);
+			//$element['time'] = date("H:i", $date);
+			$element['participants'] = is_null($participants) ? 0 : (int) $participants;
+			$element['max_num'] = $max_num;
+			$element['buffer'] = $buffer;
+			if ($max_num > $participants) {
+				if ($max_num + $buffer > $participants + $participant_number) {
+					$array[date("Y-m-d", $date)][date("H:i", $date)] = $element;
+				}
+			}
+		}
+		return $array;
+	}
+	private function noLocalEmptySpaces($event_id, $reservations)
+	{
+		$sql = "SELECT ace.max_num, ace.buffer, ace.participants FROM llx_actioncomm as ac
+        INNER JOIN llx_actioncomm_extrafields as ace ON ace.fk_object = ac.id
+        WHERE ac.id = {$event_id}";
+		$result = $this->db->query($sql);
+		if ($this->db->num_rows($result) == 0) {
+			return true;
+		}
+		while ($row = $this->db->fetch_object($result)) {
+			if ($row->participants >= $row->max_num) {
+				return true;
+			}
+			if ($row->participants + $reservations > $row->max_num + $row->buffer) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * curlnoLocalEmptySpaces
+	 *
+	 * Return an array with details
+	 *
+	 * @return 	array|mixed data without useless information
+	 *
+	 * @param 	int 	$fk_event			
+	 * @param 	int 	$reservations		
+	 * 
+	 * @url	POST curlnoLocalEmptySpaces
+	 * @access protected
+	 * @throws RestException 401 Not allowed
+	 * @throws RestException 404 Not found
+	 * @throws RestException 506 No available spaces
+	 * 
+	 */
+	public function curlnoLocalEmptySpaces($fk_event, $reservations)
+	{
+		ApiBbusLog::appLog("curlnoLocalEmptySpaces");
+
+		$sql = "SELECT ace.max_num, ace.buffer, ace.participants FROM llx_actioncomm as ac
+        INNER JOIN llx_actioncomm_extrafields as ace ON ace.fk_object = ac.id
+        WHERE ac.id = {$fk_event}";
+		$result = $this->db->query($sql);
+		if ($this->db->num_rows($result) == 0) {
+			return true;
+		}
+		while ($row = $this->db->fetch_object($result)) {
+			if ($row->participants >= $row->max_num) {
+				return true;
+			}
+			if ($row->participants + $reservations > $row->max_num + $row->buffer) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * curlcreatedpreorderobj
+	 *
+	 * Return an array with details
+	 *
+	 * @return 	array|mixed data without useless information
+	 *
+	 * @param 	string 	$sendId			
+	 * @param 	int 	$fk_event			
+	 * @param 	int 	$reservations		
+	 * @param 	int 	$product_id			
+	 * 
+	 * @url	POST curlcreatedpreorderobj
+	 * @access protected
+	 * @throws RestException 401 Not allowed
+	 * @throws RestException 404 Not found
+	 * @throws RestException 506 No available spaces
+	 * 
+	 */
+	function curlcreatedpreorderobj($sendId, $fk_event, $reservations, $product_id)
+	{
+		ApiBbusLog::appLog("curlcreatedpreorderobj");
+		$apiInvoiceHelper = new ApiInvoiceHelper;
+		$createdPreOrderOBJ = [];
+		for ($i = 1; $i <= $reservations; $i++) {
+			dol_include_once('/comm/action/class/actioncomm.class.php');
+			dol_include_once('/custom/booking/class/preorder.class.php');
+			$apiInvoiceHelper->increaseParticipant($fk_event);
+
+			$lines = $apiInvoiceHelper->getProductLines($product_id);
+			foreach ($lines as $line) {
+				$preOrderObj = new PreOrder($this->db);
+				$preOrderObj->ref = $this->generateRandomString();
+				$preOrderObj->fk_event = $fk_event;
+				$preOrderObj->fk_product = $product_id;
+				$result = $preOrderObj->create($this->user);
+				if ($result > 0) {
+					$createdPreOrderOBJ[] = [
+						'id' => $preOrderObj->id,
+						'ref' => $preOrderObj->ref,
+					];
+				} else {
+					ApiBbusLog::appLog(json_encode(['error' => $preOrderObj->errors]));
+					ApiBbusLog::appLog("Unsaved event: " . $fk_event);
+					throw new RestException(401, 'Unsaved event: ' . $fk_event);
+				}
+			}
+		}
+		ApiBbusLog::appLog(json_encode($createdPreOrderOBJ));
+		return $createdPreOrderOBJ;
+	}
+
+	/**
+	 * Create BBticket record
+	 *
+	 * Return an array with details
+	 *
+	 * @return 	array|mixed data without useless information
+	 *
+	 * @param 	string		$product_id
+	 * @param 	string		$datec
+	 * @param 	string		$facture_id
+	 * 
+	 * @url	POST createbbticket
+	 * @access protected
+
+	 * @throws RestException 401 Not allowed
+	 * @throws RestException 404 Not found
+	 * @throws RestException 506 No available spaces
+	 * 
+	 */
+	public function createbbticket(string $product_id, string $datec, string $facture_id)
+	{
+		global $db, $conf;
+		$sql = "SELECT validperiod, occasions FROM " . $this->db->prefix() . "product_extrafields WHERE fk_object = " . $product_id;
+		$result = $db->query($sql);
+		while ($sqlDataResult = $db->fetch_array($result)) {
+			$validperiod = $sqlDataResult['validperiod'] ? $sqlDataResult['validperiod'] : 366;
+			$occasions = $sqlDataResult['occasions'];
+		}
+		$ticket = new BbTicket($db);
+		$ticket->fk_facture = null;
+		$ticket->bundle_id = $product_id;
+		$ticket->usable_occasions = $occasions;
+		$ticket->usage = '0';
+		$ticket->available_at = $this->getAvailableAtDate($datec, $validperiod);
+		$ticket->ticket_id = $product_id;
+		$ticket->fk_settlements_group_id = $conf->global->CURL_GROUP_ID;
+		$ticket->invoice_number = $facture_id;
+		if ($ticket->create($this->user) == -1) {
+			dol_syslog("Nem sikerult a ticketek mentese");
+		}
+		return true;
+	}
+
+	private function getAvailableAtDate($date, $validperiod)
+	{
+		$available_at = date('Y-m-d H:i:s', strtotime($date . ' +' . $validperiod . ' days'));
+		return $available_at;
+	}
+}