Подстановка и добавление в отчет на СКД произвольных значений, которые нельзя получить запросом




Принцип обмена данными из 1С с сайтом (на MySQL) и выдачи (публикации) этих данных по запросу.
PHP-Скрипт автоматической загрузки данных из файла данных в формате CSV в базу данных сайта работающего на WordPress.

В продолжение моей темы: 1С:Альфа-Авто Автосалон Автосервис: обмен с сайтом.
С помощью данного скрипта можно загружать в автоматическом режиме, по расписанию, данные сервисных книжек (ремонтов авто) из 1С:Альфа-Авто Автосалон Автосервис.
Также можно загружать данные в ручном режиме: для этого делается скрытая страница, где размещается специальная кнопка.
Комментарии размещенные внутри скрипта разъяснят логику и порядок действия.
Комментарии с "/////    echo" использовались для отладки.
Дополнительно создана таблица для журналирования результатов загрузки данных.
Скрипт включает в себя защиту от SQL инъекций (думаю безопасность соблюдена в полной мере).
В кратце:
1. Пишется скрипт, который запускает этот.
2. Создается регламентное задание в WordPress, по которому запускается скрипт из п.1. 
3. Этот скрипт осуществляет проверку на существование файла обмена в папке.
4. Если данные не новые, загрузка не производится.
5. Если данные новые, очищается таблица сервисных книжек.
6. Загружаются новые данные.

Собственно сам скрипт:

<?php // Полная загрузка сервисных книжек, создан 2024-01-05 12:44:55

global $wpdb2;
global $failure;
global $file_hist;

/////  echo '<H2><b>Старт загрузки</b></H2><br>';

$failure=FALSE;
//подключаемся к базе
$wpdb2 = include_once 'connection.php'; ; // подключаемся к MySQL
// если не удалось подключиться, и нужно оборвать PHP с сообщением об этой ошибке
if (!empty($wpdb2->error))
{
/////   echo '<H2><b>Ошибка подключения к БД, завершение.</b></H2><br>';
$failure=TRUE;
wp_die( $wpdb2->error );
}

$m_size_file=0;
$m_mtime_file=0;
$m_comment='';
/////проверка существования файлов выгрузки из 1С
////файл выгрузки сервисных книжек
$file_hist = ABSPATH.'/_1c_alfa_exchange/AA_hist.csv';
if (!file_exists($file_hist))
{
/////   echo '<H2><b>Файл обмена с сервисными книжками не существует.</b></H2><br>';
$m_comment='Файл обмена с сервисными книжками не существует';
$failure=TRUE;
}

/////инициируем таблицу лога
/////если не существует файла то возврат и ничего не делаем
if ($failure){
///включает защиту от SQL инъекций и данные можно передавать как есть, например: $_GET['foo']
/////   echo '<H2><b>Попытка вставить запись в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>$m_comment));
wp_die();
/////    echo '<H2><b>Возврат в начало.</b></H2><br>';
return $failure;
}
/////проверка лога загрузки, что бы не загружать тоже самое
$masiv_data_file=stat($file_hist);   ////передаем в массив свойство файла
$m_size_file=$masiv_data_file[7];    ////получаем размер файла
$m_mtime_file=$masiv_data_file[9];   ////получаем дату модификации файла
////создаем запрос на получение последней удачной загрузки
////выбираем по штампу времени создания (редактирования) файла загрузки AA_hist.csv, $m_mtime_file

/////   echo '<H2><b>Размер файла: '.$m_size_file.'</b></H2><br>';
/////   echo '<H2><b>Штамп времени файла: '.$m_mtime_file.'</b></H2><br>';
/////   echo '<H2><b>Формирование запроса на выборку из лога</b></H2><br>';
////препарируем запрос
$text_zaprosa=$wpdb2->prepare("SELECT * FROM `vin_logs` WHERE `last_mtime_upload` = %s", $m_mtime_file);
$results=$wpdb2->get_results($text_zaprosa);

if ($results)
{   foreach ( $results as $r)
{
////если штамп времени и размер файла совпадают, возврат
if (($r->last_mtime_upload==$m_mtime_file) && ($r->last_size_upload==$m_size_file))
{////echo '<H2><b>Возврат в начало, т.к. найдена запись в логе.</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>'Загрузка отменена, новых данных нет, т.к. найдена запись в логе.'));
wp_die();
return $failure;
}
}
}
////если данные новые, пишем в лог запись о начале загрузки
/////echo '<H2><b>Попытка вставить запись о начале загрузки в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>0, 'last_size_upload'=>$m_size_file, 'comment'=>'Начало загрузки'));

////очищаем таблицу
$clear_tbl_zap=$wpdb2->prepare("TRUNCATE TABLE %s", 'vin_history');
$clear_tbl_zap_repl=str_replace("'","`",$clear_tbl_zap);
$results=$wpdb2->query($clear_tbl_zap_repl);
/////   echo '<H2><b>Очистка таблицы сервисных книжек</b></H2><br>';
if (empty($results))
{
/////   echo '<H2><b>Ошибка очистки таблицы книжек, завершение.</b></H2><br>';
//// если очистка не удалась, возврат
$failure=TRUE;
wp_die();
return $failure;
}

////загружаем данные
$table='vin_history';         // Имя таблицы для импорта
//$file_hist Имя CSV файла, откуда берется информация     // (путь от корня web-сервера)
$delim=';';          // Разделитель полей в CSV файле
$enclosed='"';      // Кавычки для содержимого полей
$escaped='\

26 Comments

  1. MadMix

    Непонятно, зачем вообще все это нужно. В СКД можно вызывать функции общих модулей — то есть обработка полей может быть насколько угодно сложной. Единственный минус — нужно вносить изменения в конфигурацию.

    Reply
  2. slazzy

    (1) MadMix, собственно плюсую вопрос ) непонятно зачем это надо, в смысле понятно, что бывают такие ситуации, когда надо. Но в 95% ситуаций можно сделать всё в СКД, а тут в примерах костыль на костыле

    Reply
  3. sandybaev

    согласен, да можно все попросить в общем модуле. и читабельность получится лучше.

    но пусть +

    Reply
  4. AnderWonder

    А зачем в первом подходе использовать две схемы? Можно же сразу формировать нужную таблицу значений полностью программно, в т.ч. на основании какого-либо запроса и передавать её как источник данных для второй схемы. Т.е. первая схема не нужна.

    Reply
  5. TMV

    (4) AnderWonder, на мой взгляд проще передать параметры и условия от второй схеме первой, нежели от схемы запросу, да и код будет получше выглядеть, имхо.

    Reply
  6. echo77

    (0) Я не вижу в представленном примере ни одной фишки, которую нельзя было бы сделать в одной схеме, используя функционал только СКД.

    Составить строку с длиной строки в скобках пожалуйста:

    Функция языка выражения СКД ДлинаСтроки

    Наименование +» [«+ДлинаСтроки(Наименование))+»]»

    Вывести уникальный идентификатор(UUID) ссылки — пожалуйста:

    XMLСтрока(Ссылка)

    Reply
  7. Godman

    (1) MadMix, (2) slazzy,

    А вы пробовали использовать глобальные функции в СКД-отчете с хорошим количеством строк и парой временных таблиц? А я пробовал. Окончания формирования не дождался. А дело в том, что, если в функции общего модуля идёт обращение к базе данных, то это обращение происходит после того, как получены все данные для отчёта. По сути для каждой строки запрос будет выполняться ещё раз.

    Кроме того, действительно, случаются ситуации, когда нельзя изменять конфигурацию, и уж тем более выносить некие функции в глобальный модуль, отрывая их от «родных» общих модулей. И еще не очень понятно как быть в ситуации, когда в такую глобальную функцию нужно передать структуру параметров и т.д.

    Т.е. ситуаций бывает много, наверное мне стоило вынести эти ситуации в начало поста.

    Reply
  8. Godman

    (6) echo77,

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

    Однако и ваш пример

    Наименование +» [«+ДлинаСтроки(Наименование))+»]»

    не работает. Более того, функция ДлинаСтроки появилась только в 8.2.14, а у меня есть базы на 13.219

    А вот за XMLСтрока() спасибо, как-то не задумывался что эта функция глобального контекста

    Reply
  9. echo77

    (8) ошибка в тексте выражения.

    Не вижу смысла сидеть на 8.2 и использовать древние релизы платформы.

    Кстати, в 8.3 еще больше интересностей в СКД появилось, но 8.3 пока не пользуется большой популярностью.

    Reply
  10. Godman

    (9) echo77,

    Я имел в виду что пример в принципе не работает, даже так:

    «»+ДлинаСтроки(Наименование)

    Ну не нравится ему конкатенация вместе с функциями.

    Reply
  11. slazzy

    (9), простите я точно попал в 2015 год? Просто я уже как года 2 практически не видел 8.2, абсолютно у всех стоит 8.3 и уже очень давно. Более того вряд ли вообще существуют хоть сколько-то адекватные причины не переходить на 8.3

    (7), я прекрасно знаю как вызываются эти строки. Я написал всего лишь о том, что вообще крайне редки ситуации, в которых может понадобиться вручную добавлять какую-то колонку и придется делать это программно вне СКД. Почти наверняка это на самом деле можно сделать в СКД и сделать удобно, просто вероятно человек не знает все особенности СКД или не хочет/не может писать сложные запросы.

    Если же реально нужна какая-то хитрая и сложная колонка, то что мешает изначально БЕЗ использования второго СКД просто собрать ТЗ и загрузить её в СКД уже с этой колонкой?

    В Вашем же примере всё равно используется цикличное обращение к базе данных для каждой строки.

      ЭлементРезультата.ЗначенияПараметров[ИмяПараметра_НоваяКолонка].Значение =
    ПолучитьСсылкуИзПредставления(ЭлементРезультата.ЗначенияПараметров[ИмяПараметра_СсылкаПредставление].Значение).УникальныйИдентификатор(

    Так что тоже не совсем понятно в чем тут выигрыш.

    Reply
  12. echo77

    (10) Да, я рукожоп. Чтобы это заработало, необходимо привести число к типу строка.

    Правильное выражение выглядит так:

    [Наименование] + » [» + Строка(ДлинаСтроки([Наименование])) + «]»

    (11) Собрать ТЗ иногда надо на основании отборов, которые установил пользователь. Как я понял, автор об этом хочет сказать

    Reply
  13. Поручик

    (9) Ну ты ржачно загнул, caro collega. БП 3.0 и УТ 11.1 уже почти год требуют исключительно 8.3.

    Reply
  14. kuzyara

    Спасибо за публикацию. Пытался самостоятельно реализовать оба подхода на практике — получились костыли, несовместимые с жизнью.

    Reply
  15. Serg3141

    (11) у меня на работе используется 8.2 да еще и в режиме совместимости. Конфа настолько перековеркана сторонними программистами, что страшно рассказывать. В связи с чем ни кто не стремится переходить на что-либо новое. И меняющееся законодательство не пугает.

    Reply
  16. jobkostya1c8

    (1) MadMix, минус в том, что не всегда эти самые общие модули конфигурации могут быть открыты. Если конфа базовая — все, дело труба. А если даже нетиповая, то приходится спрашивать разрешение у руководителя проекта на добавление-модификацию общих модулей…

    В любом случае универсальность теряется.

    Из минусов подхода

    какую-то цифру или значение не получается получить в запросе, а требуются вызовы каких-то процедур и функций

    чаще всего обычно строку из дат и чисел не получается. Решение то с подобной идеей хорошая, но пользоваться нужно в крайнем случае, когда очень надо чтоб отчет СКД был универсальным и зависел только от релиза конфигурации. Трудоемкость на час-два минимум возрастет только на изучение и подгонку.

    Reply
  17. Godman

    (11) slazzy, Про повсеместность 8.3 вы зря. Типовые базы типа бух 3 и торг 11.1, да крутятся на отдельных серверах с 8.3. Но есть целый зоопарк отраслевых решений, типа альфа-авто, ликероводочный завод на обычных интерфейсах со своими доработками которые то ли будут работать на 8.3, то ли нет — проверять себе дороже. Но это вообще темы не касается.

    Что касается перебора строк при выводе результата, то в методе «подстановка» никакой цикличности нет, просто идет вывод результата построчно. В «двух схемах» выгрузка в ТЗ и загрузка из нее съедают основное время. А вот когда мы используем глобальную функцию, то боюсь в этом случае запрос выполняется действительно циклично: столько раз, сколько нужно рассчитать значение функции. Я, правда, не уверен на 100%, что это так и есть, но на практике с глобальной функцией получалось неприемлемо долго.

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

    (12) echo77, Да, действительно, две схемы используются потому, что на них легко «накинуть» один и тот же пользовательский отбор.

    Reply
  18. Lancelot-2M

    Регулярно использую вариант 2 схемы, но параметры и отборы доступные пользователю нужно делать по первой схеме чтобы ограничить выборку данных и не затягивать формирование отчета. По поводу — нафига ваще — а ряд формул расчета значений в строках оперируют итоговыми значениями других ресурсов

    Reply
  19. Godman

    (18) Lancelot-2M,

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

    Reply
  20. alest

    (17)

    Что касается перебора строк при выводе результата, то в методе «подстановка» никакой цикличности нет

    Т.е. вызов глобальной функции плохо, а обращение к БД в цикле методом НайтиПоНаименованию — ОК?

    Имеется ввиду, что вы в цикле обращаетесь каждый раз к базе. Таким макаром СКД и запросы использовать вообще нет смысла.

    Пока Выборка.Следующий() Цикл
    Сообщить(Выборка.Ссылка.Наименование);

    ?

    Reply
  21. Godman

    (20) alest, При вызове глобальной функции будет как минимум такое же количество обращений к базе данных. Более того, это толком нигде не описано, но у меня есть большое подозрение, что весь запрос может выполняться столь раз, сколько будет обращений к глобальной функции. Есть еще ряд ограничений, которые накладывает вызов глобальной функции. Они описаны выше. Могу и повторить: 1. Не всегда возможно добавлять в конфигурацию глобальный модуль со своей функцией. 2. В эту функцию мы не сможем передать сложные параметры вроде структур. 3. Собственноручное наблюдение: если глобальная функция не очень простая, то с ней отчет формируется ооочень медленно.

    Reply
  22. pbazeliuk

    (1) MadMix, действительно костыль на костыле. Но можно развить идею, например, создать свой ПроцессорВывода, который выводит не только в ДеревоЗначений, ТаблицуЗначений и ТабличныйДокумент, а и в другие прикладные типы.

    Reply
  23. Aleksey_Gold

    (19)

    Добрый день. Возможно ли получить данные с веб сервиса (таблицу значений), при помощи данной схемы?

    Reply
  24. Godman

    (23) Никаких проблем. Только в ТЗ понятно должны быть текст и числа. Ссылки только в виде GUID или 1С-ссылок в текстовом формате.

    Reply
  25. user1252432

    Добрый день!Попробовал применить первый подход для своего отчета. В первую схему в Вычисляемое поле получаю ТаблицуЗначений готовой функцией общего модуля, далее беру из нее значение определенной ячейки, предварительно сохранив результат схемы компоновки в ТаблицуЗначений1, добавляю в ТаблицуЗначений1 новую колонку и вывожу в нее строку из ячейки, а колонку ТаблицаЗначений (Вычисляемое поле) удаляю, она мне больше не нужна, но отчет пустой. Подскажите, в чем может быть ошибка?

    Reply
  26. Hans

    Автор ввел в заблуждение.

    Понятно что во втором случае любой дурак может сделать

    Наименование +» [«+СтрДлина(СокрЛП(Наименование))+»]»;

    но как заменить цифру в отчете на основе текущих группировок он не объяснил. А этим вторым способом — никак.

    Reply

Leave a Comment

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