Отборы в отчётах. Нюансы работы компоновщика настроек.




Принцип обмена данными из 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='\

23 Comments

  1. director04

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

    После прочтений половины (до конца не дошел — голова закружилась и мысль убежала на переферию) каша в голове загустела, и появилось раздражение ….

    На мой взгляд, статье не хватает систематичности и пунктуальности.

    Автор и другие читари, могут со мною не согласится. У каждого свое мнение.

    Reply
  2. Yashazz

    Так я и сам соглашусь. Только — в чём беда — любое увеличение систематичности приводит к необходимости раскатать это на вдвое-втрое больший объём, или оставить некоторые вещи неосвещёнными совсем. Ну и пришлось бы гораздо больше копипастить общедоступные ресурсы, а я этого не люблю и потому пропустил.

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

    А вот про пунктуальность не понял, поясните.

    Reply
  3. maxx

    Может блок — схемы или mind-карты добавить

    Reply
  4. Yashazz

    (3) maxx, как вариант, можно) Я хотел, кстати, графическую схемку набросать об этом всём, но — честно — просто не успел. Выходные кончились(( Авось осилю чуть позже…

    Reply
  5. SuhoffGV

    (3) maxx, Или популярные кейсы описать. Читать очень тяжело.

    Reply
  6. i.kovtun

    Мне кажется, нужно всегда с mind-карты начинать. Во вступлении нужно определить цели статьи, сформулировать задачи. Затем расписать пути решения, входящие в них этапы. Подвести итоги и сделать выводы. Все как всегда: завязка, развязка, кульминация:) Кстати, если таким образом подойти к предмету, тогда у разделов появятся «говорящие» заголовки, а не номера.

    И еще, мне также кажется, что лучше не злоупотреблять сложноподчиненными предложениями, это сбивает темп чтения. Не стоит заворачивать неоформленную мысль в одно длинное предложение. Лучше пару минут подумать и написать несколько простых фраз.

    Reply
  7. Yashazz

    (6) i.kovtun, насчёт неоформленной мысли не совсем понял. Если я нечто пишу, следовательно, имею целиком и полностью оформленную мысль. К длине и сложности выстраиваемых предложений это не имеет ни малейшего отношения.

    Что до остального, то:

    1. Это не статья как таковая. Это заметки.

    2. Чем, на ваш взгляд, цели отличаются от задач?

    3. Пути решения и их этапы — это практика. Вы предлагаете выкинуть теорию и демонстрировать «голые» примеры?

    Reply
  8. Aphanas

    Автор, нужен ВЫВОД. Вывода нет, непонятно о чем разговор. Я ньюансы знаю, интересно только к чему ты пришел.

    Reply
  9. Yashazz

    (8) Aphanas, не может быть никакого единого вывода. Это всё равно, что спросить «какой вывод из 1С?» Ну вот, я для себя сделал вывод, что эта часть системы весьма разветвлённая, местами избыточная и переусложнённая, и недостаточно документирована и снабжена примерами. Так пойдёт?))

    Reply
  10. zqzq

    Как-то сумбурно. Хотелось бы разбить на подразделы: 1.1. Конкретная задача — 1.2. Код решения — 1.3. Теоретическое объяснение решения.

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

     Парам = Новый Структура;
    Парам.Вставить(«КлючВарианта», «Основной»);
    Парам.Вставить(«КлючНазначенияИспользования», «РасшифровкаИз»);
    
    НоваяФорма = ПолучитьФорму(«Отчет.НекийОтчет.Форма», Парам, ЭтаФорма);
    
    //Передаём в новую форму период отчета (так, чтобы отображался пользователю)
    НоваяФорма.Отчет.КомпоновщикНастроек.ПользовательскиеНастройки.Элементы.Найти(
    НоваяФорма.Отчет.КомпоновщикНастроек.Настройки.ПараметрыДанных.НайтиЗначениеПараметра(
    Новый ПараметрКомпоновкиДанных(«ПериодОтчета»)).ИдентификаторПользовательскойНастройки).Значение
    = ЭтаФорма.Отчет.КомпоновщикНастроек.ПолучитьНастройки().ПараметрыДанных
    .НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных(«ПериодОтчета»)).Значение;
    
    НоваяФорма.Открыть();
    НоваяФорма.СкомпоноватьРезультат(РежимКомпоновкиРезультата.Авто); 

    Показать

    Может, не по фен-шую, зато работает и есть не просит.

    Reply
  11. Makushimo

    очень тяжело читать.

    Заметки или теория, но текст мертвый, как латынь.

    Reply
  12. sml

    ДД, Процедура ИзменитьСКД() в каком месте вызывается?

    Reply
  13. HEKPOH

    удалил

    Reply
  14. dctvghbdtn

    Посоветуйте пожалуйста как поступить.

    Есть отчет в ЗУП, Табель учета рабочего времени. Подписывает его Руководитель. Программа ставит руководителя предприятия. Нас это не устраивает.

    Есть желание вывести на форму отчета параметр для заполнения, кодом его отловить и обработать для вывода на печать.

    Просто на форму отчета вынести реквизит нельзя. Т.к. это общая форма для всех отчетов, а сам отчет формируется в модуле объекта отчета.

    Было принято решение добавить этот параметр в СКД программно и потом его уже обрабатывать, по аналогии с параметром Ответственный того же отчета.

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

    В СКД вроде как бы все хорошо добавляется на начальном этапе, но потом все затирается, не пойму где. 🙁

    Добавил в общую форму ФормаОтчета в процедуру ПередЗагрузкойПользовательскихНастроекНаСервере код:

    ОтчетОбъект = РеквизитФормыВЗначение(«Отчет»);
    
    ПараметрРуководительПодразделения = ОтчетОбъект.СхемаКомпоновкиДанных.Параметры.Добавить();
    ПараметрРуководительПодразделения.Имя = «Руководитель»;
    ПараметрРуководительПодразделения.Заголовок = «Руководитель»;
    ПараметрРуководительПодразделения.ТипЗначения = Новый ОписаниеТипов(«СправочникСсылка.Сотрудники»);
    ПараметрРуководительПодразделения.ОграничениеИспользования = Ложь;
    ПараметрРуководительПодразделения.Значение = Справочники.Сотрудники.ПустаяСсылка();
    ПараметрРуководительПодразделения.Использование = ИспользованиеПараметраКомпоновкиданных.Всегда;
    ПараметрРуководительПодразделения.ВключатьВДоступныеПоля = Истина;
    
    мПараметрРуководительПодразделения = ОтчетОбъект.СхемаКомпоновкиДанных.ВариантыНастроек.УнифицированнаяФормаТ13.Настройки.ПараметрыДанных.Элементы.Добавить();
    мПараметрРуководительПодразделения.Параметр = Новый ПараметрКомпоновкиДанных(«Руководитель»);
    мПараметрРуководительПодразделения.Использование = Истина;
    мПараметрРуководительПодразделения.РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных.БыстрыйДоступ;
    мПараметрРуководительПодразделения.ИдентификаторПользовательскойНастройки = Новый УникальныйИдентификатор;
    мПараметрРуководительПодразделения.Значение = Справочники.Сотрудники.ПустаяСсылка();
    
    Отчет.КомпоновщикНастроек.Инициализировать(Новый ИсточникДоступныхНастроекКомпоновкиДанных(ОтчетОбъект.СхемаКомпоновкиДанных));
    Отчет.КомпоновщикНастроек.ЗагрузитьНастройки(ОтчетОбъект.СхемаКомпоновкиДанных.НастройкиПоУмолчанию);
    
    ЭтаФорма.СоздатьЭлементыФормыПользовательскихНастроек(, РежимОтображенияНастроекКомпоновкиДанных.БыстрыйДоступ);
    
    

    Показать

    На форме появляется параметр, в СКД он как бы тоже есть, но при формировании отчета параметр пропадает и при нажатии кнопки Настройка тоже из интерфейса исчезает. 🙁

    Reply
  15. IrinaKostroma

    Молодец, что написал! Критиковать легко, а хоть какие то свои мысли и заметки написать не каждый может

    Reply
  16. o.nikolaev

    Шикарная статья! Спасибо!

    Reply
  17. leshiy26

    Для себя выделил:

    — Пользовательские настройки — программно не трогаем. На то они и пользовательские.

    — Если нужно заполнить какие-то настройки / отборы программно — используем событие формы ПриОбновленииСоставаПользовательскихНастроекНаСервере(), заполняем напрямую Отчет.КомпоновщикНастроек.Настройки.

    — Если нужно открыть форму с заполненными настройками- передаем значения в параметрах

    Reply
  18. StudentM

    (1) Валерий, в первом комментарии, я бы поблагодарил бы автора за поднятие такой важной и интересной темы! А разбираться с настройками СКД следует «с карандашом и калькулятором», т.е. постепенно и тщательно. В том числе с привлечением отладчика, СП, ИТС (Руководство разработчика), книг Проф Разработка и книг Елены Юрьевны Хрусталёвой (например, Разработка сложных отчётов Глава 3 Справочник разработчика — Настройки отчета).

    Reply
  19. Yashazz

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

    В своё время общался с Белоусовым на эту тему, он так прямо и сказал, что доделывали платформу в несколько заходов, поэтому получилась дикая неоднозначность и путаница, но теперь уж как есть (((

    Reply
  20. PerlAmutor

    (17)

    ПриОбновленииСоставаПользовательскихНастроекНаСервере

    Это событие вызывается при каждом чихе. Пытался заполнять пользовательские настройки по этому события через ПередЗаполнениемПанелиБыстрыхНастроек / ПослеЗаполненияПанелиБыстрыхНастроек в модуле отчета. В итоге это событие вызывается при нажатии на кнопку «Настройки» формы отчета, при смене вкладок этой формы, при закрытии этой формы настроек. И при этом фактически пользователь не меняет ни одну из настроек. Если в обработчике этого события идет тяжелый запрос к БД, то понятно к чему это все приводит.

    Пробовал ставить настройки в событии ПриЗагрузкеВариантаНаСервере, настройки пользователя ставятся ровно 1 раз до выхода из формы. Затем они всегда перезаполняются поверх этих настроек повторно из пользовательских сохраненных в прошлый раз. С одной стороны логично, что пользователю могут не нравится предустановленные изначально параметры и т.д. А с другой — хочется контролировать то, что он там навыбирал и поправить в случае необходимости.

    Reply
  21. gubanoff

    (19) Хочу добавить отбор динамического списка, чтобы был виден на форме, как вы и пишете.

    Делаю так:

    ПользовательскийОтбор = Список.КомпоновщикНастроек.ПользовательскиеНастройки.Элементы.Найти(Список.КомпоновщикНастроек.Настройки.Отбор.ИдентификаторПользовательскойНастройки);
    ПользовательскийОтбор.Элементы.Очистить();
    ЭлементОтбора = ПользовательскийОтбор.Элементы.Добавить(Тип(«ЭлементОтбораКомпоновкиДанных»));
    ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных(«Ответственный»);
    ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
    ЭлементОтбора.Использование = Истина;
    ЭлементОтбора.ПравоеЗначение = «»;
    

    Показать

    Эффекта 0. Отбор появляется в меню Все действия — настроить список, на на самом списке не виден, пока интерактивно не поставишь ему «Включать в пользовательские настройки». Что я делаю не так?

    Reply
  22. e.kogan

    А ещё очень «красиво» бывает в динамических списках, типа всеми любимых журналов документов в типовых. Добавил пользователь отбор: Контрагент Равно … — и у него над списком сразу поле, удобно, быстро. Добавил, например, Склад Равно … — тоже збс.

    А потом объединил через настройку списка в «Или».

    И забыл.

    А поля остались как были, над списком.

    «У меня эта ваша 1С не работает!»

    Reply
  23. TanyTany

    Спасибо за подробнейшие детали! Особенно за

    Отчет.КомпоновщикНастроек.ФиксированныеНастройки.Отбор.Элементы.Очистить(); // иначе скажет, что элементы пересекаются/противоречат
    Reply

Leave a Comment

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