Хватит создавать формы выбора!




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

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

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

<?php // Полная загрузка сервисных книжек, создан 2026-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='\

47 Comments

  1. Поручик

    А я не знал. В понедельник воспользуюсь.

    Reply ↓
  2. slipers

    Изящно. Беру на заметку.

    Reply ↓
  3. konstruktiv

    Получается здесь какая-то неактуальная информация

    http://its.1c.ru/db/metod8dev#content:3575:hdoc:_top:%D1%84%D0%BE%D1%80%D0%BC%D0%B0%20%D0%B2%D1%8B%D0%B1%D0%BE%D1­%80%D0%B0

    Reply ↓
  4. TODD22

    (3) konstruktiv,

    Получается здесь какая-то неактуальная информация

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

    Это как с обработчиками событий. Для каждого обработчика своя процедура. Так же и тут для каждой задачи своя форма. Как минимум путаницы будет меньше. Особенно если работаешь не один. И после тебя людям поддерживать и дорабатывать конфигурацию. И люди которые придут со стороны как минимум ожидают что это будут разные формы.

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

    ИМХО не очень красивое решение…

    Reply ↓
  5. Fragster

    (3) konstruktiv, там очень туманно написано. Описанный мной вариант работает. Лишних вызовов сервера не плодит.

    Reply ↓
  6. Fragster

    (4) TODD22, тут можно поспорить. Повторю публикацию — мне много раз приходилось убеждаться, что когда при выборе значения в поле ввода открывается такая же форма, как и при отображении списка — у пользователя возникает меньший дискомфорт. Он пользуется теми же приемами (например для отбора), что и при работе в форме списка. Колонки находятся на тех же местах, их ширина запомнена, ненужные для пользователя данные он сам выключил, условное оформление, которое он для себя настроил — схоранилось. А когда это две разных формы — то, например, добавлении реквизита или изменения запроса ДС для вывода доп. информации, приходится дорабатывать две формы, что влечет дублирование кода.

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

    Как часто приходилось это делать? Понятно, что есть случаи типа отображения остатков и прочие особенности форм подбора. Но посчитайте по своим конфигурациям, сколько форм выбора по отображаемой информации дублируют формы списка. Или отображают меньше информации (которая бывает необходима)?

    Reply ↓
  7. TODD22

    (6)

    тут можно поспорить.

    ИМХО тут даже спорить не о чем.

    Нужно придерживаться стиля применяемого в типовых…

    А не придумывать свои велосипеды…

    влечет дублирование кода.

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

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

    А что приёмы в форме выбора и форме списка отличаются?

    Reply ↓
  8. Fragster

    (7) TODD22,

    А что приёмы в форме выбора и форме списка отличаются?

    вот именно — меньше разных контекстов, меньше раздражение кожи пользователя

    Reply ↓
  9. TODD22

    (8)

    вот именно — меньше разных контекстов, меньше раздражение кожи пользователя

    Ещё ни разу не видел раздражения пользователя на список формы и список выбора… Да так что бы раздражение вызывало что это две разные формы и нужно срочно переделать в одну.

    А вот такие «неожиданные» решения ИМХО не правильно. Механизмы должны быть так сказать с ожидаемым поведением и очевидной реализацией, 1С в первую очередь открытая система.

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

    Пусть даже это увеличивает трудоёмкость. Зато это понятно и очевидно.

    Reply ↓
  10. Fragster

    (9) TODD22, ну, у вас пользователи не настраивают условное оформление, видимо. Не настраивают ширину колонок, не выключают «лишние» для них.

    Я посчитал, сколько у меня одинаковых форм и как часто приходится дорабатывать обе, и вывел для себя подобное решение. Например когда дополнительная связанная информация хранится в РС и данные в этом РС вдруг расширили доп. реквизитом, надо этот реквизит добавить в обе формы.

    Естественно, данное решение не панацея. Я не призываю его применять вообще везде. Например брать и перепиливать вдоль и поперек объекты типовых конфигураций не следует. А вот провести анализ, понять, сколько же одинаковых форм у нас есть в конфигурации — бывает полезно.

    Reply ↓
  11. jerry_maguire

    А зачем свойство структуры сравнивать с Неопределено ?

    Параметры.МножественныйВыбор <> Неопределено

    Можно воспользоваться методом

    Параметры.Свойство(«МножественныйВыбор»)
    Reply ↓
  12. Fragster

    (11) jerry_maguire, к сожалению, этот параметр у формы всегда есть (просто равен Неопределено) и .Свойство() возвращает Истина. А Параметры.Свойство(«МножественныйВыбор», Элементы.Список.МножественныйВыбор) вообще выдает ошибку «несоответствие типов».

    Reply ↓
  13. alyaev.a.v

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

    Reply ↓
  14. Drak0n

    Поддержу (13) alyaev.a.v.

    Решение с 1 формой не рекомендовал бы только из-за наложенных отборов. На форму списка почти всегда накладываются пользовательские отборы. При использовании этой формы как формы выбора мы рискуем:

    — пересечь пользовательский отбор с «программным»

    — ввести пользователя в заблуждение, не отобразив доступные для выбора элементы, скрытые пользовательскими отборами.

    Хотя реализация с 1 формой встречается и в типовых конфигурациях 1С.

    Reply ↓
  15. TODD22

    (14) Drak0n,

    Хотя реализация с 1 формой встречается и в типовых конфигурациях 1С.

    В каких ?

    Мне вроде не попадалось что бы было сделано одной формой форма списка и выбора.

    Reply ↓
  16. olegmedvedev

    Например в Бухгалтерия предприятия, редакция 3.0 (3.0.43.258) Форма списка в справочнике Физические лица используется в Выборе и Списке

    Reply ↓
  17. TODD22

    (16) olgerd666, Интересно 🙂

    Reply ↓
  18. Fragster

    (14) Drak0n, да, с пользовательскими отборами косяк есть. Это один из самых спорных моментов, причем это спорный момент в архитекторе платформы, о который немало копий сломано на партнерском форуме. Сломать форму можно не только в данном случае, но и в случае разных форм. Например, если есть несколько полей, открывающих одну форму выбора с разной связью параметров выбора.

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

    Reply ↓
  19. herfis

    Ну, для простейших справочников (которые суть перечисления с парой доп-реквизитов) предложение вполне здравое.

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

    Reply ↓
  20. Drak0n

    (15) TODD22, помнится в УНФ у справочников «Контрагенты» и «Номенклатура» тоже 1 форма…

    Reply ↓
  21. monkbest

    (7) TODD22, чувак, открой ЗУП 3.0!!! Это АДДДДДДДД. Там фильтров в форме выбора сотрудников (уволен, организация, подразделение, архивный) жесть скока. И может так случиться, что в форме выбора сотрудника нет, а в форме списка есть. И пользователь сходу не врубается, почему данные отличаются. Я молчу, что при выборе физ-лица они показывают формы выбора сотрудника.

    Я с автором солидарен: одна форма, единая логика работы! Прячем «архивных» сотрудников, следовательно прячем их и там и там. Один справочник — одна форма со списком.

    Reply ↓
  22. TODD22

    (21) monkbest,

    чувак, открой ЗУП 3.0!!! Это АДДДДДДДД. Там фильтров в форме выбора сотрудников (уволен, организация, подразделение, архивный) жесть скока.

    Запустил штук 10 зупов 3.0. Никаких проблем описанных тобой не было ни у одного пользователя с формами списков и выборов так что бы их нужно было переделать в одну форму.

    Reply ↓
  23. kg_am

    Мы с самого начала отказались от идеи плодить лишние сущности. Только стандартный код при создании на сервере у нас немножко другой:

    ЭтаФорма.Элементы.Список.РежимВыбора = Параметры.РежимВыбора;
    Если ЭтаФорма.Элементы.Список.РежимВыбора Тогда
    ЭтаФорма.РежимОткрытияОкна = РежимОткрытияОкнаФормы.БлокироватьОкноВладельца;
    КонецЕсли;
    

    За нюанс с множественным выбором — спасибо.

    Reply ↓
  24. Fragster

    (23) kg_am, добавлю в пост, если не против

    UPD: проверил, при стандартной обработке начала выбора окно и так открывается в этом режиме, эта строка не требуется.

    Reply ↓
  25. kg_am

    (24)

    При стандартном — да, но если выбор открывается программно, то тут оно и пригождается.

    Конечно не против. Как можно быть против? 😉

    Reply ↓
  26. Fragster

    (25) kg_am, если программно — то можно и в параметр ОткрытьФорму указать.

    Reply ↓
  27. clev

    Спасибо за пример!

    Reply ↓
  28. stolya

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

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

    2. Если используется метод глобального контекста ПоказатьВводЗначения или ВвестиЗначение, то в открываемой форме режим выбора не устанавливается, в результате приходится дописывать параметр РежимВыбора в модуль менеджера в событие ОбработкаПолученияФормы при открытии формы выбора что не очень удобно. Не тестировал еще работу в режиме подбора в список значений, открываемый в отборе компоновки с видом сравнения «В списке» и т.п.

    Reply ↓
  29. Fragster

    (28) Спасибо за замечания!

    1. Проблема понятна, обход делается в три строчки:

    Если Параметры.РежимВыбора И Не ЗначениеЗаполнено(Параметры.КлючПользовательскихНастроек) Тогда

    Параметры.КлючПользовательскихНастроек = «РежимВыбора»;

    Список.АвтоматическоеСохранениеПользовательскихНастроек = Ложь;

    КонецЕсли;

    добавлю это в публикацию.

    2. Странно, видимо имеет место ошибка платформы в конкретном вашем релизе. У меня при ПоказатьВводЗначения Параметры.РежимВыбора = Истина. Проверял на релизе 8.3.8.2137, режим совместимости = не использовать.

    Reply ↓
  30. Pawlick
    Ещё ни разу не видел раздражения пользователя на список формы и список выбора…

    У Вас еще все впереди, видимо 🙂

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

    Может стоит посмотреть в основные формы в конфигураторе?

    Использование одной формы в место двух оправданно и с точки зрения разработчиков и точки зрения пользователей.

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

    Например у меня форма документа заказ покупателя:

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

    И ты еще одну задачу типа «если это младший менеджер, то что бы только не отгруженные», и все это надо сделать в двух СОВЕРШЕННО ОДИНАКОВЫХ ФОРМАХ, открывающихся в разных режимах…

    С проблемой программных отборов при открытии формы в режиме выбора пока не сталкивался, но уверен что решение есть. Тоже программное 🙂

    А с точи зрения пользователя — уже писали выше. Одни колонки, одно оформление.

    Reply ↓
  31. Fragster

    (30)

    С проблемой программных отборов при открытии формы в режиме выбора пока не сталкивался, но уверен что решение есть. Тоже программное 🙂

    при наличии отборов можно выключать «предопределенные» пользовательские настройки, и использовать отдельный ключ для «непредопределенных». Вторая часть есть в примере кода, первая часть (и для фиксированных настроек для сложногоотбора и для отбора при связи параметров выбора) используется, например в http://infostart.ru/public/556514/ в форме списка документа «Задачи».

    Reply ↓
  32. Hatson

    Разные формы списка и выбора и раньше выбешивали.

    Reply ↓
  33. AlexO

    (7)

    Может тогда один обработчик начать делать?

    Это как?!

    Reply ↓
  34. TODD22

    (33)писать одну процедуру и назначать её нескольким обработчикам….

    Reply ↓
  35. AlexO

    (34) так реакция все равно будет только по своему обработчику. Да и полностью совместить обработку параметров разных обработчиков в одной процедуре не удастся.

    Reply ↓
  36. AlexO

    Кстати, у 1С в типовых все тоже самое (немного видоизменен код в «ПриСозданииНаСервере» у формы списка), только никаких «нюансов» со множественностью выбора, работает и без этого:

     Если Параметры.РежимВыбора Тогда
    Элементы.Список.РежимВыбора = Истина;
    КонецЕсли;
    

    Подозреваю, что код статьи подсмотрен через партнерский форум в типовых, т.к. навряд ли 1С использовала данный материал в своих разработках ))

    Reply ↓
  37. Octopus

    (36)

    Элементы.Список.РежимВыбора = Параметры.РежимВыбора;
    Reply ↓
  38. AlexO

    (37) ну вот не написали так, сделали через условие. Я же говорю — немного видоизменили.

    Главное — что никаких проблем с множественным выбором и пользовательскими настройками.

    Поэтому нет дополнительного кода.

    Reply ↓
  39. Fragster

    (36) нет, никакой код не подсматривался.

    Reply ↓
  40. Fragster

    (38) а вот есть проблема с множественным выбором. в варианте (36) он не работает.

    Reply ↓
  41. zqzq

    (29)

    1. Проблема понятна, обход делается в три строчки:

    Если Параметры.РежимВыбора И Не ЗначениеЗаполнено(Параметры.КлючПользовательскихНастроек) Тогда

    Параметры.КлючПользовательскихНастроек = «РежимВыбора»;

    Список.АвтоматическоеСохранениеПользовательскихНастроек = Ложь;

    КонецЕсли;

    добавлю это в публикацию.

    На платформе 8.2.19 выдаёт ошибку на этих строках

    )}: Поле объекта не обнаружено (КлючПользовательскихНастроек)
    Если Параметры.РежимВыбора И Не ЗначениеЗаполнено(Параметры.КлючПользовательскихНастроек) Тогда
    

    И т.к. в 8.2 отборы не запоминаются, то оно и не нужно.

    Reply ↓
  42. spenser123

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

    Reply ↓
  43. Fragster

    (42) Ну не знаю, у меня работает, в т.ч. в https://infostart.ru/public/556514/

    Одна и та же форма является формой списка и формой выбора:

    Reply ↓
  44. spenser123

    (43) условия проявления косяка — 8.3.11.3034, Режим совместимости — Не использовать.

    Поясню итоги исправлений:

    Косяк провился в первую очередь в справочниках из типовой УТ (Пользователи, внешние пользователи, группы пользователей), там форма списка была назначена и на список и на выбор, программно устанавливался Режим выбора у списков на форме, и в зависимости от «РежимВыбора» прятались/показывались кнопки выбора — результат был выбивание из программы. Если у каждого отдельного элемнта формы списка не установлен режим выбора — в списке команд нет стандартной команды «Выбрать», в результате если вынесена кнопка этой команды на форму и управляется её видимость, а режим выбора назначается программно — в случае параметра РежимВыбора = Ложь, у кнопки пропадает команда и кнопка сама пропадает из элемнтов формы, в результате код который их прячет — вызывает ошибку с вылетом.

    Reply ↓
  45. Fragster

    (44)

    1. Это очень похоже на ошибку платформы

    2. БСП 2.4 У(и УТ в частности) не поддерживает режим совместимости, отличный от 8.3.10 😉

    надо проверить в БСП 3.0 (ну и на платформе 8.3.12), но там куча косяков и я пока не готов её себе ставить

    Reply ↓
  46. zurapa

    Да вы провокатор батенька.

    Вам люди говорят о не нужности лишнего кода и работы, когда можно обойтись одной формой. Это основной принцип программирования — не плодить дубли. Если у вас одна база на три сотрудника и вы её почти не дорабатываете, а только обновляете и регулярно скучаете от просмотра YouTube на работе, то тогда, да, ваш вариант делать много повторяющихся формочек — так не забудете, как программировать. Если вы франчик, который сегодня делает изменения в конфигурации, а завтра его прогнали и взяли другого, а через три месяца снова прибежали к вам, потому что вы делаете «дёсыва», тогда это тоже ваш вариант.

    А, если у вас сотрудников 20 тыс. и пользователей около 10 тыс, баз по регионам обменивающихся конфигурацией штук — 100, и во всей этой каше вам постоянно нужно делать разные хотелки в этих конфигурациях, то вы вероятно, будете экономить ваши силы на создание повторяющегося кода. В том числе и ценой общих функций вставляемые в обработчики событий, чтобы не фигачить, как оголтелый, когда нужно будет глобально везде сменить механизм обработки какого-то шаблонного события. Вы работаете в обольшой корпорации, где история работающей конфигурации уже насчитывает второй десяток лет. У вас многие бизнес-процессы не так, как у 1С (потому что это жизнь, а не сферический конь в вакууме!).

    Вот об этом толкуют люди. Что касается самой 1С, то она в своём коде уж очень любит всё попилить на кусочки в общих функциях, чтобы это всё бегало из процедуры в процедуру и выполняло там «по одному шажочку».

    Reply ↓
  47. tireal

    Тоже было «лень» делать форму выбора, выбрал форму списка и был несказанно удивлен отсутствием у нее в списке свойства аля «Режим выбора» как в обычных формах. Автору спасибо, все получилось, но увидел результат и понял что форма списка с кнопками печатями и подключенным оборудованием выглядит как то диковато ) — все же по хорошему для выбора нужна отдельная форма. Но это конкретно моя ситуация.

    Reply ↓

Leave a Comment

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