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

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

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

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

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

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

ПОДГОТОВКА КОЛОНКИ С АРТИКУЛАМИ В GOOGLE ТАБЛИЦЕ

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

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

В нашем случае, это колонка 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:

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

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

Результат выполнения скрипта в консоли

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

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

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

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

Файл token.json

ВОЗМОЖНЫЕ ПРОБЛЕМЫ ПОЛУЧЕНИЯ ТОКЕНА

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

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

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

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

Ошибки при получении токена

Решение: Эта ошибка связана с тем, что в разделе 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 таблице. После этого в админке сайта необходимо выполнить одну простую команду «Обновить цены».

Обновление цен в админке

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

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

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

Полный текст статьи читайте на CMS Magazine