Создаем и заполняем покупателей в инвойсах в Drupal из Zoho Books

После успехов в загрузке инвойсов в Drupal я решил попробовать загружать и создавать покупателей и товары в инвойсы. Изначально создавать товары не планировалось по ТЗ, но решено было, что отсутствующие товары надо создавать, в качестве ключа для поиска будет использоваться item_id из Zoho Books.

На всякий случай уточню, что инвойсы — это счета покупателям, содержащие товары и услуги.

Получение материала для обновления

Ранее я научился искать и создавать материал (node), теперь мне понадобилось обновлять материал.

Не скажу, что разобрался быстро, но все же нашел методику:

		if ($isFound) {
			$node = node_load('invoice', $nid);
			//print_r("- found: " . $presentation . " node " . var_dump_ret($node) . "<BR>" );
		}
		else			
		{
			//If not found, create Invoice
			$node = node_create('invoice', $ext_id, $presentation);
			print_r("- created: " . $presentation . "<BR>");
		}
 
		$node->set('field_ext_id', $ext_id);
		$node->set('title', $presentation);
 
		node_save($node);
 
function node_load($type, $nid) {
	//get node by nid
	$node = \Drupal::entityTypeManager()->getStorage('node')->load($nid);
	return $node;
}
 

Я даже разобрался как title обновлять, это такое же поле как и остальные.

Получение инвойса в цикле по инвойсам

Теперь по каждому инвойсу из списка получаем инвойс и будем его загружать.

Используем API Zoho: https://www.zoho.com/books/api/v3/invoices/#get-an-invoice

Код примерно такой:

	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;
		$nid = node_findby_ext_id('invoice', $ext_id);
		$isFound =  $nid != 0;
		print_r("- search result Is found: " . $isFound . "<BR>");
		//$isFound = true;
 
		if ($isFound) {
			$node = node_load('invoice', $nid);
			//print_r("- found: " . $presentation . " node " . var_dump_ret($node) . "<BR>" );
		}
		else			
		{
			//If not found, create Invoice
			$node = node_create('invoice', $ext_id, $presentation);
			print_r("- created: " . $presentation . "<BR>");
		}
 
		//$node->set('field_ext_id', $ext_id);
		//$node->set('title', $presentation);
 
		$ret_invoice = zoho_query_invoice($context, $invoice -> invoice_id);
		print_r("- invoice: " . $ret_invoice->invoice->invoice_id . "<BR>" );
 
 
 
		//print_r("- invoice: " . var_dump_ret($ret_invoice) . "<BR>" );
 
 
		node_save($node);
 
		//Update Invoice in Drupal from Data in Zoho-books
	}

Результат:

Загрузка покупателей

Задача — проставлять покупателя в шапку инвойса.

Обнаружил, что машинное поле должно быть уникальным, поэтому переименовал машинные поля внешних ключей в имя, в которое входит название типа материала:

Более-менее сделал универсальным код по поиску и созданию материалов. Пока только ищу, не создавая:

		$ret_invoice = zoho_query_invoice($context, $invoice -> invoice_id);
		print_r("- invoice: " . $ret_invoice->invoice->invoice_id . "<BR>" );
 
		//set field customer 
		$customer_nid = trade_find_object_byfields ('customer', [
			'ext_id' => $invoice -> customer_id,
			'title' => $invoice -> customer_name
			], true);
 
		print_r("- customer nid: " . $customer_nid . ' name: '. $invoice -> customer_name . "<BR>" );
 
		$invoice_node->set('field_customer_name', $customer_nid);
 
 
 
//=== LIB OF TRADE FUNCTIONS ===
function trade_create_object($type, $fields)
{
	//create item and return its nid
	$ext_id = $fields["ext_id"];
	if (!empty($ext_id)) return 0;
 
	$title = $fields["title"];
	if (!empty($title)) return 0;
 
	return 0; //Temporary
 
}
 
function trade_find_object_byfields($type, $fields, $create = false) {
	//find & create (if need) object of type with fields
	//fields - ext_id, title
 
	$ext_id = $fields["ext_id"];
 
	$nid = 0;
 
	if (!empty($ext_id))
		$nid = node_findby_ext_id($type, $ext_id);
	if (!empty($nid)) return $nid;
 
	//continue sear by title
	$title = $fields["title"];
	if (!empty($title))
		$nid = node_findby_title($type, $title);
	if (!empty($nid)) return $nid;
 
	if ($create) 
		return trade_create_object($type, $fields);
 
	return 0;
 
}
 
 
//=== LIB OF DRUPAL FUNCTIONS ===
 
 
 
function node_findby_ext_id($type, $ext_id) {
	$ret = \Drupal::entityQuery('node')
		->condition('type', $type) //'invoice'
		-> condition('field_' . $type .  '_ext_id', $ext_id)
		-> accessCheck(FALSE)
		-> execute();
 
	print_r("Search: " . $type . " " . $ext_id . ' : ' . var_dump_ret($ret) . "<BR>");
 
	return array_first($ret, 0); //if not found returns 0
 
}
 
function node_findby_title($type, $title) {
	$ret = \Drupal::entityQuery('node')
		->condition('type', $type) //'item'
		-> condition('title', $title)
		-> accessCheck(FALSE)
		-> execute();
 
	print_r("Search " . $type . " by title: " . $title . ' : ' . var_dump_ret($ret) . "<BR>");
 
	return array_first($ret, 0); //if not found returns 0
 
}
 
 
 

В протоколе видно, как находятся и подставляются существующие покупатели:

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

Создание покупателя в случае его отсутствия

Если покупателя нет, его надо создать с ext_id и наименованием (title).

Для проверки удалим покупателя Alpha:

Я столкнулся с ошибкой 500:

С помощью трассировки определил, что проблему вызывала попытка сделать дамп созданного материала:

Пришлось выяснить, как получить nid созданного материала, оказывается у Node есть метод id. Только он возвращается почему-то в виде строки, пришлось приводить его к числовому формату оператором преобразования типов (int). Этот метод доступен даже для незаписанного материала.

Получился вот такой код создания материла при его осутствии:

function trade_create_object($type, $fields)
{
	//create item and return its nid
	//print_r("-  step create start <BR>");
 
	$ext_id = $fields["ext_id"];
	if (empty($ext_id)) return 0;
 
	//print_r("-  step id <BR>");
 
	$title = $fields["title"];
	if (empty($title)) return 0;
 
	//print_r("-  step title <BR>");  
 
	//return 0; //Temporary
 
	$object_node = node_create($type, $ext_id, $title);
	//print_r("-  step node " . var_dump_ret($object_node) ." <BR>");
 
	$nid = node_save($object_node);
	print_r("-  create node " . var_dump_ret($nid) ." <BR>");
 
	//print_r("- created " . $type . " nid: " . $nid . $title . "<BR>");
 
 
	return $nid; //Temporary
 
}
 
 
 
function node_create($type, $ext_id, $presentation = "") {
	// Create node object with attached file.
	$node = Node::create([
		'type'        => $type,
		'title'       => $presentation,
	]);
	$node->set('field_' . $type .  '_ext_id', $ext_id);
	return $node; 
}
 
function node_save($node){
	$node->save();
	return (int) ($node-> id());
}
 
function node_nid($node){
	return (int) ($node-> id());
}
 

Протокол:

Работа с табличной частью товаров инвойса

Теперь будем загружать товары из инвойса, а при их отсутствии — создавать.

Код по поиску товаров аналогичный:

$item_nid = trade_find_object_byfields ('item', [
				'ext_id' => $line_item -> item_id,
				'title' => $line_item -> name
			], true);

Возникает вопрос — а куда сохранять товары, как получить доступ в табличную часть?

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

fixin

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

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

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

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