Загрузка инвойсов в 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 инвойса:
Отложенные вопросы
К этому моменту я накопил определенный технический долг, но решил, что сначала напишу «рыбу» кода, а потом уже буду заниматься декорациями. Как видно, мне нужно будет доработать:
- Схему авторизации, чтобы пользователю показывался переход на ссылку согласия на авторизацию, если нужно. Чтобы авторизация работала автоматом.
- Форма настроек сайта, куда нужно прописать ClientID, ClientSecret, OrganizationID и прочие параметры для подключения к ZB.
- Форму ввода даты начала и окончания и сохранения выбранных дат в настройках пользователя (не сессии и не в общих настройках).
- Переход с одной формы на другую по мере нажатия пользователем определенных кнопок. Остановился, что буду вызывать одну и ту же страницу с разными параметрами, в зависимости от которых отрисовывать новое содержимое формы.
- Сохранение в общих настройках сайта полученных refresh и access токенов.
- Инвойсы выдаются постранично, нужно учесть это в коде, чтобы обрабатывать всю выдачу.
- Работа 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; }
Протокол выглядел так:
1 комментарий
[…] успехов в загрузке инвойсов в Drupal я решил попробовать загружать и создавать […]