Загрузка инвойсов в Drupal из Zoho Books

Маленькие радости

К этому моменту админ меня порадовал — видимо, отключил кэширование, теперь мне достаточно было сохранить php-файл программы на сервер и запустить его, чтобы сразу увидеть результат и ошибки.

Я даже от радости проверил, действительно ли это так, добавил строчку кода в файл, сохранил:

И сразу увидел результат в браузере:

Конечно, было бы неплохо запускать всю цепочку нажатием одной клавиши — сохранение php-файла, обновление страницы и переключение на страницу в браузер. Но это пока подождет. В конце концов когда-то учась в БГУИР я бегал за распечаткой результата программы на общий принтер. Правда, тогда это была не коммерческая разработка.

Нужно найти какой-нибудь редактор, который элементарно проверяет синтаксис PHP, до сих пор хардкорно использую notepad++.

Также к этому моменту я открыл для себя возможность вывода дампа массивов с форматированием. Вот так не работало:

А вот так очень даже хорошо заработало:

Функцию var_dump_ret я случайно нашел на просторах интернета:

function var_dump_ret($mixed = null) {
  ob_start();
  var_dump($mixed);
  $content = ob_get_contents();
  ob_end_clean();
  return $content;
}

Тупняки

Не обошлось без тупняков из-за того, что php я основательно забыл и из-за нюансов Drupal.

Например, я пытался получить через -> значение по ключу из массива, на что PHP жестоко ругался. Пришлось переписать на квадратные скобки, а то мне уже начало чудиться, что по ключу в массив нельзя вставить массив:

Работа с датами тоже вызвала у меня затруднение. Я даже не мог объявить константы даты:

Оказывается имена классов в Drupal нужно начинать с бэкслеша, т.к. по умолчанию используется namespace Друпала.

Я пытался передать в функцию date объект типа DateTime, и ловил ошибку типов, после чего научился делать преобразование в Timestamp:

Получение инвойсов с фильтром по дате

И все же я победил все препятствия на пути и смог загрузить инвойсы с фильтром по дате.

	$date1 = new \DateTime('2023-02-01');
	$date2 = new \DateTime('2023-02-15');
 
	$headers = array(
    'Authorization: Zoho-oauthtoken ' . $resp['access_token'],
    'Content-Type: application/x-www-form-urlencoded;charset=UTF-8',
	);
 
	$context = [
		'organization_id' => $organization_id,
		'headers' => $headers,
		'date1' => $date1,
		'date2' => $date2
	];
 
	get_data_from_zoho($context);
 
function get_data_from_zoho($context) {
	//Load data from zoho
	get_invoices_from_zoho($context);
}
 
function get_invoices_from_zoho($context) {
	//Load invoices from zoho
	$prm = [
		'date_start' => zoho_date($context["date1"]), //"2013-11-17"
		'date_end' => zoho_date($context["date2"])
		];
	$ret = http_get(zoho_books_api('invoices'), $prm, $context);
 
	$invoices = $ret -> invoices;
 
	foreach ($invoices as $invoice) {
		print_r("Inv.No:" . $invoice -> invoice_number . " customer_name: " . $invoice -> customer_name . "<BR>");
	}
 
}
 
function zoho_date($date) {
	return date("Y-m-d", $date -> getTimestamp());
}
 
 
function zoho_books_api($item) {
	//item - f.e. invoices
	return "https://www.zohoapis.com/books/v3/" . $item;
}
 
function zoho_accounts_api($item) {
	//item - f.e. auth
	return "https://accounts.zoho.com/oauth/v2/" . $item;
}
 
 
 

В тестовой базе я создал вручную 4 инвойса:

С фильтром по дате я получил 3 инвойса:

Отложенные вопросы

К этому моменту я накопил определенный технический долг, но решил, что сначала напишу «рыбу» кода, а потом уже буду заниматься декорациями. Как видно, мне нужно будет доработать:

  1. Схему авторизации, чтобы пользователю показывался переход на ссылку согласия на авторизацию, если нужно. Чтобы авторизация работала автоматом.
  2. Форма настроек сайта, куда нужно прописать ClientID, ClientSecret, OrganizationID и прочие параметры для подключения к ZB.
  3. Форму ввода даты начала и окончания и сохранения выбранных дат в настройках пользователя (не сессии и не в общих настройках).
  4. Переход с одной формы на другую по мере нажатия пользователем определенных кнопок. Остановился, что буду вызывать одну и ту же страницу с разными параметрами, в зависимости от которых отрисовывать новое содержимое формы.
  5. Сохранение в общих настройках сайта полученных refresh и access токенов.
  6. Инвойсы выдаются постранично, нужно учесть это в коде, чтобы обрабатывать всю выдачу.
  7. Работа PHP-скрипта должна быть ограничена во времени, поэтому нужно использовать Batch API или лучше очереди.

Поиск и создание инвойсов в Drupal — основы

Вначале я просто создал инвойсы, без их поиска:

Ну что же, создались:

Правда, не удаляются, т.к. важные поля не заполнены.

И не редактируются, при попытке открыть node возникает ошибка Call to a member function getPreviewMode() on null in Drupal\node\NodeForm->actions() (line 218 of core/modules/node/src/NodeForm.php):

Далее попробовал поиск:

Попытался создавать, заполняя field_ext_id:

Но поиск все равно не находил эти инвойсы. Возникла ошибка — что неправильно работает — поиск или создание? Просмотреть значение поля ext_id у инвойса я не могу.

Тут мне на помощь пришел админ и подсказал, что нужно использовать машинное имя типа материала, с учетом регистра, я писал Invoice, а надо invoice:

Одновременно он включил модуль views, теперь через админку можно удалять ненужные материалы, чем я и воспользовался:

В редакторе я посмотрел созданный материал и увидел, что поле ext_id пустое:

Значит, неправильно его заполняю. Ну что же, надо разбираться, как заполнить. Нагуглил тут и сделал установку полей в явном виде:

После этого материалы стали искаться по ID и не создаваться заново:

В редакторе я увидел, что поле ext_id установлено:

Далее я немного облагородил код, выделив участки кода в функции для применения с разными типами материалов и даже использовал функцию array_first для получения первого элемента массива:

	foreach ($invoices as $invoice) { 
		$presentation = "Inv.No:" . $invoice -> invoice_number . " customer_name: " . $invoice -> customer_name .  " date: " . $invoice -> date .  " ID: " . $invoice -> invoice_id ;
		print_r($presentation . "<BR>");
		//Search invoice in Drupal
		$isFound = false;
 
		$ext_id = $invoice -> invoice_id;
 
		$isFound = node_findby_ext_id('invoice', $ext_id) != 0;
		print_r("- search result Is found: " . $isFound . "<BR>");
		//$isFound = true;
 
		if (!$isFound) {
 
			//If not found, create Invoice
			$node = node_create('invoice', $ext_id, $presentation);
			node_save($node);
			print_r("- created: " . $presentation . "<BR>");
 
		}
 
		//Update Invoice in Drupal from Data in Zoho-books
	}
 
 
 
 
function node_findby_ext_id($type, $ext_id) {
	$ret = \Drupal::entityQuery('node')
		->condition('type', $type) //'invoice'
		-> condition('field_ext_id', $ext_id)
		-> accessCheck(FALSE)
		-> execute();
 
	print_r("Search: " . $type . " " . $ext_id . ' : ' . var_dump_ret($ret) . "<BR>");
 
	if (count($ret) != 0) return array_first($ret);
 
	return 0; //if not found
}
 
function node_create($type, $ext_id, $presentation = "") {
	// Create node object with attached file.
	$node = Node::create([
		'type'        => $type,
		'title'       => $presentation,
	]);
	$node->set('field_ext_id', $ext_id);
	return $node; 
}
 
function node_save($node){
	$node->save();
}
 
function array_first($array, $default = null)
{
   foreach ($array as $item) {
       return $item;
   }
   return $default;
}
 

Протокол выглядел так:

fixin

Программирую на 1С с 1999 года. В 1С просто Гений. В 2020 году ушел из офиса на вольные хлеба фриланса. Принимаю заказы.

Читайте также:

1 комментарий

  1. 03.03.2023

    […] успехов в загрузке инвойсов в Drupal я решил попробовать загружать и создавать […]

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *