Как автоматически менять цены на сайте без загрузки и выгрузки Excel файла с прайсом

Сегодня на связи отдел разработки и поддержки сайтов Зекслер. Продолжаем рассказывать, как автоматизируем бизнес процессы клиентов (писали как автоматизировали создание коммерческих предложений в Битрикс 24), и в этот раз сделали синхронизацию цен из Google-таблицы (дабы не грузить Excel и прочее) и цен на сайте производителя гаражей и хозблоков SKOGGY (да, для многих производственников делаем полный цикл по маркетингу, продажам, автоматизации и управленческому учету).

Как автоматически менять цены на сайте без загрузки и выгрузки Excel файла с прайсом

В статье будут строчки кода и прочая внутрянка, но донесению сути она не должна помешать. А если всё же помешала, пиши в комментариях, учтём в следующих материалах.

Подписывайтесь на наш Телеграм-канал, где мы ежедневно публикуем посты по теме не только разработки, но и дизайна, маркетинга, продвижения и т.д. Будем рады новым подписчикам!

Возвращаемся к модулю. Задача состояла в разработке модуля для CMS Drupal, который синхронизирует данные из гугл документа формата Excel (Google Spreadsheet) в колонке с ценами в поля для цен на сайте.

Подготовка колонки с артикулами в Google таблице

Предварительно необходимо было задать связь записи продукта и таблицы. Для это в файле документа организовали колонку с уникальными полями (Артикулы), отвечающими за связь этой строки (продукта) с вариантом продукта в Drupal.

Как автоматически менять цены на сайте без загрузки и выгрузки Excel файла с прайсом

В нашем случае, это колонка C.

Колонка с ценами – это N.

Начальная строка 5, а конечная 105.

После добавления всех артикулов в таблицу и правильных цен, переходим к следующему этапу.

Создание приложения в Google консоли

Следующий шаг – создание приложения. Переходим в Google Cloud Console и создаем отдельное приложение. Подключаем к нему (ENABLE APIS AND SERVICES) Google Drive API.

Далее нужно получить доступ к приложению. Для этого переходим в credentials и нажимаем Create credentials. После заполнения и добавления тестового пользователя, нужно скачать Client secrets в формате json (справа в строке).

После этого необходимо дать разрешения для доменов Authorized JavaScript origins и Authorized redirect URIs. В нашем случае это localhost и localhost/state.

Сохраненный файл json нужен для получения токена, который будем получать с помощью php-файла в отдельном скрипте в консоле.

Получение токена для ClientApi Google

Теперь нужно организовать получение токена. Для этого создаем в папке микропроекта composer.json и добавим туда google/apiclient посредством команды:

composer require google/apiclient

Делаем файл php с подгрузкой require __DIR__ . '/vendor/autoload.php';

А скачанный файл credentials размещаем в той же папке composer.json

{ "name": "my/google_spreadsheet_api", "type": "my-api", "description": "Library for google api", "require": { "google/apiclient": "^2.0" }, "minimum-stability": "dev" }

Файл index.php:

<?php require __DIR__ .'/vendor/autoload.php'; if (php_sapi_name() != 'cli') { throw new Exception('This application must be run on the command line.'); } $pre_dir = __DIR__ . '/'; $relocation_url = 'http://localhost/state'; $path_to_credentials = $pre_dir.'credentials.json'; $path_to_result_token = $pre_dir. 'token.json'; use Google\Client; /** * Returns an authorized API client. * @return Client the authorized client object */ function getClient() { global $relocation_url, $path_to_credentials, $path_to_result_token; $client = new Google\Client(); $client->setApplicationName('Google Sheets API PHP Quickstart'); $client->setScopes('https://www.googleapis.com/auth/spreadsheets'); $client->setAuthConfig($path_to_credentials); $client->setAccessType('offline'); $client->setPrompt('select_account consent'); $client->setRedirectUri($relocation_url); // Load previously authorized token from a file, if it exists. // The file token.json stores the user's access and refresh tokens, and is // created automatically when the authorization flow completes for the first // time. $tokenPath = $path_to_result_token; if (file_exists($tokenPath)) { $accessToken = json_decode(file_get_contents($tokenPath), true); $client->setAccessToken($accessToken); } // If there is no previous token or it's expired. if ($client->isAccessTokenExpired()) { // Refresh the token if possible, else fetch a new one. if ($client->getRefreshToken()) { $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken()); } else { // Request authorization from the user. $authUrl = $client->createAuthUrl(); printf("Open the following link in your browser:\n%s\n", $authUrl); print 'Enter verification code: '; $authCode = trim(fgets(STDIN)); // Exchange authorization code for an access token. $accessToken = $client->fetchAccessTokenWithAuthCode($authCode); $client->setAccessToken($accessToken); // Check to see if there was an error. if (array_key_exists('error', $accessToken)) { throw new Exception(join(', ', $accessToken)); } } // Save the token to a file. if (!file_exists(dirname($tokenPath))) { mkdir(dirname($tokenPath), 0700, true); } file_put_contents($tokenPath, json_encode($client->getAccessToken())); } return $client; } // Get the API client and construct the service object. $client = getClient();

Показываем пример файла credentials.json

{"web":{"client_id":"...","project_id":"gpru-artdetox-driven-bulwark","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"...","redirect_uris":["http://localhost/state"],"javascript_origins":["http://localhost"]}}

Скрипт в index.php выполняется только в консоли php index.php

В результате выполнения скрипта в терминале (консоли) получим примерно следующее:

Как автоматически менять цены на сайте без загрузки и выгрузки Excel файла с прайсом

Копируем полученную ссылку и вводим в строку браузера. Сейчас нужно получить код авторизации для того адреса email, который будет работать с этим приложением.

После авторизации и получения доступа к приложению, к вашим файлам Excel будет перенаправление примерно по такой ссылке:

http://localhost/state?code=4/0AWyig&scope=https://www.googleapis.com/auth/spreadsheets

Копируем code и вставляем в терминал. После этого получим файл token.json.

Как автоматически менять цены на сайте без загрузки и выгрузки Excel файла с прайсом

Возможные проблемы получения токена

При получении токена могут возникнуть ошибки. Рассмотрим наиболее частые из них.

Проблема 1: Нет доступа к приложению или приложение не проверялось.

Решение: Добавьте тестового пользователя в разделе OAuth consent screen.

Проблема 2: Fatal error: Uncaught InvalidArgumentException: Redirect URI must be absolute in /var/www/vendor/google/auth/src/OAuth2.php:793

Как автоматически менять цены на сайте без загрузки и выгрузки Excel файла с прайсом

Решение: Эта ошибка связана с тем, что в разделе credentials (https://console.cloud.google.com/apis/credentials) проекта не указаны Authorized JavaScript origins и Authorized redirect URIs. Необходимо их заполнить и сохранить. При этом сохранение будет происходит в течение 5 минут.

Завершение настройки модуля

После проведения всех манипуляций, организация получения данных и отправление их на обработку в Batch-процесс выглядит так.

Важно! Файл с которым работаете должен быть доступен для тестового пользователя, либо должен быть открыт доступ по ссылке (чтение).

К примеру, SheetId с файла https://docs.google.com/spreadsheets/d/3xS34U5аZ8zVzU65ho-VFRVg/edit#gid=0 – это «3xS34U5аZ8zVzU65ho-VFRVg».

use Drupal\mw_google_spreadsheet\GetClient; use Google\Service\Sheets; /* Это колонка - например E. Колонка с ценами - например N Начальная строка 5, а конечная 105. */ /* Получаем доступ к гугл файлу */ $columnArticul = 'E'; $columnPrice = 'N'; $start = 5; $end = 105; /* Ид рабочей области - файла гугл, берётся со ссылки на файл. */ $spreadsheetId = '1xS34U5vZ8z5jyblSJOvLUUShINhgKEVzU35ho-VFRVg'; $client = new GetClient(); $cl = $client->get(); $service = new Sheets($cl); $range = $columnArticul . $start . ':' . $columnPrice . $end; try { $response = $service->spreadsheets_values->get($spreadsheetId, $range); $values = $response->getValues(); }catch (Exception $e){ \Drupal::messenger()->addMessage( 'Выброшено исключение: ', $e->getMessage(), "\n ", 'error', TRUE); // Displays nothing (at UID=235) die(); } $num_operations = count($values); $this->messenger()->addMessage($this->t('Creating an array of @num operations', ['@num' => $num_operations])); $operations = []; $i = 1; foreach ($values as $value){ $operations[] = [ 'mw_google_spreadsheet_op_1', [ $value, $this->t('(Operation @operation)', ['@operation' => $i]), ], ]; $i++; } $batch = [ 'title' => $this->t('Creating an array of @num operations', ['@num' => $num_operations]), 'operations' => $operations, 'finished' => 'mw_google_spreadsheet_finished', ]; return $batch;

Функция обработки каждой строки 'mw_google_spreadsheet_op_1' в Batch-процессе выглядит так (описание в комментариях):

/** * Batch operation for batch 1: one at a time. * * This is the function that is called on each operation in batch 1. */ function mw_google_spreadsheet_op_1($rowFromExcel, $operation_details, &$context) { /* Пример полученной строки из файла 0 => "КХС-03-П-Т-Б" 1 => "Контейнер" 2 => "Стандарт" 3 => "3,06м" 4 => "2,16" 5 => "2,06" 6 => "200" 7 => "Торцевая" 8 => "Плоская" 9 => "Пол OSB" 10 => "Цинк" 11 => "р.106 800" */ //название поля из настройки сущности вариантов продукта - field_excele_artikul /* получение всех записей вариантов для обработки по артикулу с колонки 0 */ $query = \Drupal::database()->select('commerce_product_variation__field_excele_artikul', 'cv_fea'); $query->fields('cv_fea', ['entity_id', 'revision_id', 'bundle', 'field_excele_artikul_value', 'langcode']); $query->where('field_excele_artikul_value = :name', [':name' => $rowFromExcel[0]]); $result = $query->execute()->fetchAll(); $ids = []; // все ид вариантов foreach ($result as $item){ $ids[] = $item->entity_id; } // загружаем все варианты $variations = \Drupal\commerce_product\Entity\ProductVariation::loadMultiple($ids); //считаем их количество $cont = count($rowFromExcel); //из колонки цены удаляем все записи (кстати цена должна быть без копеек) $price_new = preg_replace('/([^0-9]+)/','', $rowFromExcel[$cont - 1]); /** @var \Drupal\commerce_product\Entity\ProductVariation $variation */ foreach ($variations as &$variation){ // в полученную из коллекции сущностей по артиклу вариантов записываем цену в нужном формате // валюту можно брать из настроек магазина $variation->setPrice(new \Drupal\commerce_price\Price($price_new, 'RUB')); $variation->save(); } $context['results'][] = implode(',', $ids); // Optional message displayed under the progressbar. $context['message'] = t('Running Batch "@id" @details', ['@id' => implode(',', $ids), '@details' => $operation_details] ); }

В итоге получили, что каждая строка обрабатывает все записи вариантов по артикулу и присваивает им нужную цену с валютой RUB. Процесс отображается пользователю.

Можно менять строки начальной и конечной строки, не обновляя старые.

Как это работает для менеджера

Если цена меняется, менеджер исправляет ее в Google таблице. После этого в админке сайта необходимо выполнить одну простую команду «Обновить цены».

Как автоматически менять цены на сайте без загрузки и выгрузки Excel файла с прайсом

В результате на сайте автоматически цены будут обновлены.

Надеемся, статья была для вас полезна, и вы смогли по инструкции настроить самостоятельно синхронизацию цен и упростить работу себе и менеджерам. Подписывайтесь на наш Телеграм-канал, где мы рассказываем, как делаем дизайн, нейминг, продвижение, публикуем короткие инструкции и т.д. Будем рады новым подписчикам.

Пишите комментарии, если остались вопросы по настройкам.

55
4 комментария

а какое было красивое начало... про гугл таблицу))) Спасибо за возможность скопировать функции

После первых строк кода сразу начинается дикий скролл вниз)

Редакция VC крайне странные люди, публиковать гавно тоннами про chatGPT и прочую на фиг никому не нужную дичь (аля Илон Маск пошёл в туалет и что-то там сказал) - это пожалуйста, сколько угодно.

Выпускаешь нормальный контент - идите ребята в бан.

Странная позиция.

По такое теме уже плагины есть, в которых не нужно кнопку нажимать, а просто сразу информация из Google Sheets сразу на сайте отображается. Вы делали что-нибудь похожее с автоматизацией наценки на сайте в зависимости от времени суток и прочих факторов?