Создаем и заполняем покупателей в инвойсах в 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 табличная часть товаров была неправильно спроектирована и я отложил пока эту часть до исправления структуры базы.
Свежие комментарии