Введение
После создания новой версии каркасной конфигурации, для разработки web-приложений на основе http-сервисов OneScript, необходимо было на практике пощупать новые функции и оценить ее применимость для создания бизнес-ориентированых web-приложений. В качестве web-приложения, был выбран сайт небольшого ресторана или кафе. Как показал анализ выдачи гугла, этот вид приложений достаточно распространен в мире и как правило, представляет собой одностраничный сайт, позволяющий рассказать о ресторане, просмотреть меню и забронировать столик. Поскольку в 90% случаев при создании сайтов используются готовые шаблоны, я решил поступить таким-же образом. В готовых решениях Битрикс, был найден подходящий шаблон сайта (RestoPesto). Я обратился в компанию Novastar, которая является автором вышеуказанного решения, с просьбой предоставить мне этот шаблон для миграции на OneScript. Антон Яшин любезно предоставил мне ссылку на исходный шаблон, за что ему огромное спасибо. Как оказалось - это html шаблон на основе библиотеки bootstrap, который не содержит кода php.
О том, что получилось - вы можете прочитать ниже или посмотреть, нажав кнопку "Показать демо".
Системные требования
Компьютер под управлением Windows 7 или более поздней версии.
Установленный .NET Framework версии 4.5.2 или более поздней.
Платформа 1С:Предприятие версии не ниже 8.3.6.1977.
Первый тест
Файловая структура шаблона, после скачивания и распаковки имеет следующий вид:
Для начала экспериментов, создадим новую информационную базу, на основе каркасной конфигурации, как описано в этой статье. В отличие от предыдущей версии, при настройке пула приложений выберем интегрированный пул.
Создадим общий макет, типа ТекстовыйДокумент, и поместим в него содержимое файла index.html.
Также создадим http-сервис, с корневым url – index.os, добавив функцию-обработчик GET-запроса нижеследующего содержания:
Функция ОбработкаВызоваHTTPСервиса(Запрос) Экспорт
Тело = ПолучитьОбщийМакет("ГлавнаяСтраница").ПолучитьТекст();
Ответ = Новый HTTPСервисОтвет(200);
Ответ.УстановитьТелоИзСтроки(Тело);
Возврат Ответ;
КонецФункции
Добавим созданные объекты в подсистему ОбъектыКонфигурацииWebПриложения.
Выгрузим конфигурацию, затем в режиме предприятие создадим и обновим web-приложение.
Скопируем папки изображений, js-скриптов etc. В папку web-приложения, таким образом, что файловая структура web-приложения будет иметь нижеследующий вид:
Для доступа к локальным файловым ресурсам из 1С:Предприятие, для web-публикации 1С:Предприятие создадим правила url rewrite.
Протестируем наше web-приложение и убедимся, что его поведение не отличается от поведения демонстрацинного сайта-источника.
Внешний вид сайта
Рассмотрим более подробно визуальную часть нашего сайта. Как можно увидеть, страница состоит из шапки, которая включает в себя логотип и меню, шесть функциональных разделов, по количеству пунктов меню, а также подвала, содержащего контактную информацию и информацию о рабочих часах.
Шапка
Содержит логотип компании и главное меню для навигации по разделам страницы. Обычно, эта информация остается неизменной.
Приветствие (Welcome)
Фактически - это начальный экран, на который попадает пользователь при открытии сайта. В верхней части находится слайдер изображений, с поясняющим текстом для каждого изображения (Заголовок, Заголовок1, Текст ). В нижней части - текст с кратким описанием заведения, фотография, а также заголовки к данному функциональному разделу. Обычно данная информация постоянна, либо меняется достаточно редко.
Специальные блюда (Specialties)
Данная секция содержит фоновое изображение, заголовки, соответствующие названию секции, а также список специальных блюд. Каждое блюдо содержит изображение, наименование, описание и цену. Обычно, изображение и заголовки меняются нечасто, однако список специальных блюд может меняться до нескольких раз в день. В дальнейшем мы более подробно остановимся на динамическом формировании этого списка.
Меню (Menu)
Данная секция, как и предыдущая, содержит фоновое изображение, заголовки секции, а также список блюд, составляющих меню ресторана. Каждое блюдо имеет изображение, наименование, ингридиенты и цену. Как и в случае со списком специальных блюд, мы полагаем, что фоновое изображение и заголовки меняются редко, в то время как меню может формироваться каждый день. В дальнейшем мы более подробно остановимся на динамическом формировании меню.
Резервирование столика (Reservation)
Данная секция содержит фоновое изображение, заголовки секции, а также форму заказа столика. Информация в этой секции может считаться условно-постоянной.
События ресторана (Events)
Данная секция содержит информацию о предстоящих событиях, которые будут происходить в заведении. Это могут быть дни каких-либо блюд, праздники etc. Также как и предыдущая, данная секция содержит фоновое изображение и заголовки секции, которые можно считать условно-постоянными, а также список событий, который может обновляться достаточно часто. В дальнейшем, мы более подробно остановимся на динамической генерации списка событий.
Контакты (Contact)
Данная секция содержит форму обратной связи. Информацию в этой секции можно считать условно постоянной, однако, необходимо регистрировать обращения клиентов в учетной системе.
Подвал
Данная секция содержит информацию об адресе заведения, а также часах работы. Данная информация может считаться условно-постоянной.
Условно-постоянная информация
Поскольку хранение условно-постоянной информации в информационной базе учетной системы нецелесообразно и в то же время необходимо обеспечить возможность ее настройки, в качестве своеобразного хранилища различных заголовков, ссылок на изображения etc. Используем макет типа ТекстовыйДокумент, где данная информация будет представлена в формате yaml.
Для работы с данными в формате yaml, добавим методом сравнения/объединения (не забывая объединить состав подсистем) в нашу конфигурацию соответствующую библиотеку. Также, заимствуем методом Ctrl+C, Ctrl+V общий модуль, с функциями для работы с шаблонами из этой демонстрационной конфигурации.
Cоздадим общий макет типа ТекстовыйДокумент с именем ОформлениеСайта и поместим его (и общий модуль Макеты) в подсистему ОбъектыКонфигурацииWebПриложения.
Импортируем прочие файлы приложения, затем добавим полученный архив в макет ПрочиеФайлы, а сам макет в подсистему ПрочиеФайлыWebПриложения.
Отредактируем исходный код страницы, разделив его на области вывода, заменим необходимый текст на параметры и внесем параметры в макет ОформлениеСайта. При этом, на данном этапе оставим области, отвечающие за вывод специальных блюд, меню и событий как есть.
Также отредактируем код нашего http-сервиса с учетом вывода областей. При этом содержимое макета ОформлениеСайта и код http-сервиса примут нижеследующий вид:
---
# Файл настроек оформления сайта
Страница:
ИмяСайта: Yep Resto
Описание: Демонстрационный сайт ресторана на OneScript
КлючевыеСлова: http-сервисы OneScript, OneScript, Yep Resto, Yep
ТекстКопирайт: 2017 <a href="/redirect.php?url=aHR0cHM6Ly91aWNvb2tpZXMuY29tLw==">uiCookies:Resto</a>. All Rights Reserved. Images by <a href="/redirect.php?url=aHR0cHM6Ly9ncmFwaGljYnVyZ2VyLmNvbS8=">GraphicBurger</a> & <a href="/redirect.php?url=aHR0cHM6Ly91bnNwbGFzaC5jb20v">Unsplash</a>
# Наименования пунктов главного меню сайта
МенюСайта:
СекцияWelcome: Welcome
СекцияSpecialties: Specialties
СекцияMenu: Menu
СекцияReservation: Reservation
СекцияEvents: Events
СекцияContact: Contact
# Слайды на начальной странице
Слайды:
- Изображение: img/hero_bg_1.jpg
Заголовок1: Welcome
Заголовок2: The Resto
Текст: A free bootstrap website template by <a href="/redirect.php?url=aHR0cHM6Ly91aWNvb2tpZXMuY29t">uicookies.com</a>
- Изображение: img/hero_bg_2.jpg
Заголовок1: Dine
Заголовок2: With Us
Текст: A free bootstrap website template by <a href="/redirect.php?url=aHR0cHM6Ly91aWNvb2tpZXMuY29t">uicookies.com</a>
- Изображение: img/hero_bg_3.jpg
Заголовок1: Enjoy
Заголовок2: With Us
Текст: A free bootstrap website template by <a href="/redirect.php?url=aHR0cHM6Ly91aWNvb2tpZXMuY29t">uicookies.com</a>
# Секция описания ресторана
ОписаниеРесторана:
Заголовок1: Discover
Заголовок2: Our Store
Текст: Voluptatibus quaerat laboriosam fugit non Ut consequatur animi est molestiae enim a voluptate totam natus modi debitis dicta impedit voluptatum quod sapiente illo saepe explicabo quisquam perferendis labore et illum suscipit
ТекстСсылки: About Us
Изображение: img/img_1.jpg
АльтернативныйТекстИзображения: Free Bootstrap Template by uicookies.com
# Шапка секции Специальные блюда
ШапкаСпециальныеБлюда:
Изображение: img/hero_bg_2.jpg
Заголовок1: Discover
Заголовок2: Our Specialties
# Шапка секции Меню ресторана
ШапкаМенюРесторана:
Изображение: img/hero_bg_4.jpg
Заголовок1: Discover
Заголовок2: Our Menu
# Секция резервирования столика
РезервированиеСтолика:
# Шапка секции
Изображение: img/hero_bg_5.jpg
Заголовок1: Booking
Заголовок2: Reserve A Table
# Форма резервирования. Наименования полей формы
КоличествоПерсон: How Many People
ДатаРезервирования: Date
ВремяРезервирования: Time
ИмяКлиента: Name
ИмяКлиентаПодсказка: Your full name
АдресЭлектроннойПочты: Email
АдресЭлектроннойПочтыПодсказка: Your email address
Телефон: Phone
ТелефонПодсказка: Your phone
КнопкаПодтвердить: Submit
# Варианты резервирования столиков
ВариантыРезервирования:
- Наименование: 1 people
- Наименование: 2 people
- Наименование: 3 people
- Наименование: 4+ people
# Шапка секции События ресторана
ШапкаСобытия:
Изображение: img/hero_bg_4.jpg
Заголовок1: Upcoming
Заголовок2: Our Events
# Секция обратная связь
ОбратнаяСвязь:
# Шапка секции
Заголовок1: Contact
Заголовок2: Let's Chat
Текст: Voluptatibus quaerat laboriosam fugit non Ut consequatur animi est molestiae enim a voluptate totam natus modi debitis dicta impedit voluptatum quod sapiente illo saepe explicabo quisquam perferendis labore et illum suscipit
# Форма обратной связи. Наименования полей формы
ИмяКлиента: Your Name
АдресЭлектроннойПочты: Your Email
Сообщение: Your Message
КнопкаОтправить: Send Message
# Секция подвал
Подвал:
ЗаголовокАдрес: Locations
Адрес1: 198 West 21th Street, Suite 721
Адрес2: New York NY 10016
Адрес3: 198 West 21th Street, Suite 721
Адрес4: New York NY 10016
ЗаголовокЧасыРаботы: Open Hours
ЧасыРаботы1: Monday - Thursday
ЧасыРаботы2: 5:30pm - 10:00pm
ЧасыРаботы3: Friday - Sunday
ЧасыРаботы4: 5:30pm - 10:00pm
Текст1: Available for Catering
Текст2: Email or Call Us
Функция ОбработкаВызоваHTTPСервиса(Запрос) Экспорт
МакетТекст = ПолучитьОбщийМакет("ГлавнаяСтраница").ПолучитьТекст();
ОформлениеТекст = ПолучитьОбщийМакет("ОформлениеСайта").ПолучитьТекст();
ПроцессорYaml = Обработки.YamlПроцессор.Создать();
ПараметрыОформления = ПроцессорYaml.ПрочитатьYaml(ОформлениеТекст);
ПараметрыОформления["Страница"].Вставить("Слайды", ВывестиСлайды(МакетТекст, ПараметрыОформления));
ПараметрыОформления["Страница"].Вставить("МенюСайта", ВывестиМенюСайта(МакетТекст, ПараметрыОформления));
ПараметрыОформления["Страница"].Вставить("ОписаниеРесторана", ВывестиОписаниеРесторана(МакетТекст, ПараметрыОформления));
ПараметрыОформления["Страница"].Вставить("ШапкаСпециальныеБлюда", ВывестиШапкуСпециальныеБлюда(МакетТекст, ПараметрыОформления));
ПараметрыОформления["Страница"].Вставить("СпециальныеБлюда", ВывестиСписокСпециальныхБлюд(МакетТекст, ПараметрыОформления));
ПараметрыОформления["Страница"].Вставить("ШапкаМенюРесторана", ВывестиШапкуМенюРесторана(МакетТекст, ПараметрыОформления));
ПараметрыОформления["Страница"].Вставить("МенюРесторана", ВывестиМенюРесторана(МакетТекст, ПараметрыОформления));
ПараметрыОформления["Страница"].Вставить("РезервированиеСтолика", ВывестиСекциюРезервированиеСтолика(МакетТекст, ПараметрыОформления));
ПараметрыОформления["Страница"].Вставить("ШапкаСобытия", ВывестиШапкуСобытия(МакетТекст, ПараметрыОформления));
ПараметрыОформления["Страница"].Вставить("События", ВывестиСобытия(МакетТекст, ПараметрыОформления));
ПараметрыОформления["Страница"].Вставить("ОбратнаяСвязь", ВывестиОбратнаяСвязь(МакетТекст, ПараметрыОформления));
ПараметрыОформления["Страница"].Вставить("Подвал", ВывестиПодвал(МакетТекст, ПараметрыОформления));
ОбластьСтраница = Макеты.ПолучитьОбласть(МакетТекст, "<!--Страница-->", "<!--Страница-->");
ТелоСтрока = Макеты.ЗаполнитьПараметры(ОбластьСтраница.Текст, ПараметрыОформления["Страница"]);
Ответ = Новый HTTPСервисОтвет(200);
Ответ.УстановитьТелоИзСтроки(ТелоСтрока);
Возврат Ответ;
КонецФункции
// Формирует html код элементов главного меню сайта
//
Функция ВывестиМенюСайта(МакетТекст, ПараметрыОформления)
ОбластьМеню = Макеты.ПолучитьОбласть(МакетТекст, "<!--МенюСайта-->", "<!--МенюСайта-->");
Возврат Макеты.ЗаполнитьПараметры(ОбластьМеню.Текст, ПараметрыОформления["МенюСайта"]);
КонецФункции
// Формирует html-код слайдов для слайдера
//
Функция ВывестиСлайды(МакетТекст, ПараметрыОформления)
ОбластьСлайд = Макеты.ПолучитьОбласть(МакетТекст, "<!--Слайд-->", "<!--Слайд-->");
ТекстСлайды = "";
Для каждого ПараметрыСлайд Из ПараметрыОформления["Слайды"] Цикл
ТекстСлайды = ТекстСлайды + Макеты.ЗаполнитьПараметры(ОбластьСлайд.Текст, ПараметрыСлайд);
КонецЦикла;
Возврат ТекстСлайды;
КонецФункции
// Формирует html-код секции описания ресторана
//
Функция ВывестиОписаниеРесторана(МакетТекст, ПараметрыОформления)
ОбластьОписаниеРесторана = Макеты.ПолучитьОбласть(МакетТекст, "<!--ОписаниеРесторана-->", "<!--ОписаниеРесторана-->");
Возврат Макеты.ЗаполнитьПараметры(ОбластьОписаниеРесторана.Текст, ПараметрыОформления["ОписаниеРесторана"]);
КонецФункции
// Формирует html-код шапки секции специальные блюда
//
Функция ВывестиШапкуСпециальныеБлюда(МакетТекст, ПараметрыОформления)
ОбластьШапкаСпециальныеБлюда = Макеты.ПолучитьОбласть(МакетТекст, "<!--ШапкаСпециальныеБлюда-->", "<!--ШапкаСпециальныеБлюда-->");
Возврат Макеты.ЗаполнитьПараметры(ОбластьШапкаСпециальныеБлюда.Текст, ПараметрыОформления["ШапкаСпециальныеБлюда"]);
КонецФункции
// Формирует список специальных блюд
//
Функция ВывестиСписокСпециальныхБлюд(МакетТекст, ПараметрыОформления)
ОбластьСпециальныеБлюда = Макеты.ПолучитьОбласть(МакетТекст, "<!--СпециальныеБлюда-->", "<!--СпециальныеБлюда-->");
Возврат ОбластьСпециальныеБлюда.Текст;
КонецФункции
// Формирует html-код шапки секции специальные блюда
//
Функция ВывестиШапкуМенюРесторана(МакетТекст, ПараметрыОформления)
ОбластьШапкаМенюРесторана = Макеты.ПолучитьОбласть(МакетТекст, "<!--ШапкаМенюРесторана-->", "<!--ШапкаМенюРесторана-->");
Возврат Макеты.ЗаполнитьПараметры(ОбластьШапкаМенюРесторана.Текст, ПараметрыОформления["ШапкаМенюРесторана"]);
КонецФункции
// Формирует список специальных блюд
//
Функция ВывестиМенюРесторана(МакетТекст, ПараметрыОформления)
ОбластьМенюРесторана = Макеты.ПолучитьОбласть(МакетТекст, "<!--МенюРесторана-->", "<!--МенюРесторана-->");
Возврат ОбластьМенюРесторана.Текст;
КонецФункции
// Формирует секцию Резервирование столика
//
Функция ВывестиСекциюРезервированиеСтолика(МакетТекст, ПараметрыОформления)
ПараметрыОформления["РезервированиеСтолика"].Вставить("ВариантыРезервирования", ВывестиВариантыРезервирования(МакетТекст, ПараметрыОформления));
ОбластьРезервированиеСтолика = Макеты.ПолучитьОбласть(МакетТекст, "<!--РезервированиеСтолика-->", "<!--РезервированиеСтолика-->");
Возврат Макеты.ЗаполнитьПараметры(ОбластьРезервированиеСтолика.Текст, ПараметрыОформления["РезервированиеСтолика"]);
КонецФункции
// Формирует список вариантов резервирования
//
Функция ВывестиВариантыРезервирования(МакетТекст, ПараметрыОформления)
ОбластьВариантРезервирования = Макеты.ПолучитьОбласть(МакетТекст, "<!--ВариантРезервирования-->", "<!--ВариантРезервирования-->");
ТекстВариантыРезервирования = "";
Для каждого ПараметрыВариантРезервирования Из ПараметрыОформления["ВариантыРезервирования"] Цикл
ТекстВариантыРезервирования = ТекстВариантыРезервирования + Макеты.ЗаполнитьПараметры(ОбластьВариантРезервирования.Текст, ПараметрыВариантРезервирования);
КонецЦикла;
Возврат ТекстВариантыРезервирования;
КонецФункции
// Формирует шапку секции События
//
Функция ВывестиШапкуСобытия(МакетТекст, ПараметрыОформления)
ОбластьШапкаСобытия = Макеты.ПолучитьОбласть(МакетТекст, "<!--ШапкаСобытия-->", "<!--ШапкаСобытия-->");
Возврат Макеты.ЗаполнитьПараметры(ОбластьШапкаСобытия.Текст, ПараметрыОформления["ШапкаСобытия"]);
КонецФункции
// Формирует список событий
//
Функция ВывестиСобытия(МакетТекст, ПараметрыОформления)
ОбластьСобытия = Макеты.ПолучитьОбласть(МакетТекст, "<!--События-->", "<!--События-->");
Возврат ОбластьСобытия.Текст;
КонецФункции
// Формирует html-код секции обратная связь
//
Функция ВывестиОбратнаяСвязь(МакетТекст, ПараметрыОформления)
ОбластьОбратнаяСвязь = Макеты.ПолучитьОбласть(МакетТекст, "<!--ОбратнаяСвязь-->", "<!--ОбратнаяСвязь-->");
Возврат Макеты.ЗаполнитьПараметры(ОбластьОбратнаяСвязь.Текст, ПараметрыОформления["ОбратнаяСвязь"]);
КонецФункции
// Формирует html-код секции подвал
//
Функция ВывестиПодвал(МакетТекст, ПараметрыОформления)
ОбластьПодвал = Макеты.ПолучитьОбласть(МакетТекст, "<!--Подвал-->", "<!--Подвал-->");
Возврат Макеты.ЗаполнитьПараметры(ОбластьПодвал.Текст, ПараметрыОформления["Подвал"]);
КонецФункции
Обновим наше web-приложение и протестируем его.
Как можно увидеть, наше web-приложение аналогично оригиналу и имеет возможность изменения оформления.
Вывод списка специальных блюд
Поскольку в учетной системе уже имеется информация о блюдах, предлагаемых рестораном, а также их стоимости, вполне разумным, будет использовать эту информацию на сайте.
Объекты в учетной системе
Для эмуляции соответствующих объектов учетной системы, создадим справочники Номенклатура, ВидыНоменклатуры, а также документ УстановкаЦенНоменклатуры. Эти объекты будут содержать информацию, соответственно о блюдах (Номенклатура), категориях блюд (ВидыНоменклатуры), а также их стоимости (УстановкаЦенНоменклатуры).
Также, создадим нестандартный документ СпециальныеБлюда, который будет содержать список блюд, которые мы будем считать специальными и выводить их в соответствующем разделе сайта.
Объекты на стороне сайта
Для хранения необходимых данных на стороне сайта используем СУБД SQLite. Изображения блюд будем хранить в отдельных файлах.
Для удобства дальнейших действий, создадим некое подобие ER-диаграммы, а также http-сервис, в который впоследствии поместим скрипт создания БД.
Также, в папке web-приложения создадим папку db, где будет расположен файл БД сайта, и добавим разрешения на запись аккаунту, из под которого выполняется наше web-приложение.
Создадим параметры соединения с СУБД
Создадим запрос на создание таблицы category и выполним его.
В процессе выполнения команды возникнет исключительная ситуация, которая обусловлена ошибкой в коде и не влияет на выполнение команды.
Проверим наличие таблицы, выполнив запрос на чтение из этой таблицы.
Сформируем код запроса, скопируем его и вставим в http-сервис создания БД.
Аналогичным образом создадим другие таблицы и сформируем код создания БД, а также саму БД.
При этом код создания нашей БД (обработчик http-сервиса createdb.os) будет выглядеть примерно следующим образом
Функция ОбработкаВызоваHTTPСервиса(Запрос) Экспорт
// Соединение
Соединение = Обработки.СоединениеСУБД.Создать();
Соединение.СтрокаСоединения = Обработки.ПараметрыСоединенияСУБД.ПолучитьСтрокуСоединения("TestConn");
Соединение.ТипСУБД = Обработки.ПараметрыСоединенияСУБД.ПолучитьТипСУБД("TestConn");
Соединение.Открыть();
// Удаление таблиц
// Удалить special_product
Попытка
Запрос = Соединение.СоздатьЗапрос();
Запрос.Текст = "
| DROP TABLE special_product
|";
Запрос.ВыполнитьКоманду();
Исключение
КонецПопытки;
// Удалить special_products
Попытка
Запрос = Соединение.СоздатьЗапрос();
Запрос.Текст = "
| DROP TABLE special_products
|";
Запрос.ВыполнитьКоманду();
Исключение
КонецПопытки;
// Удалить price
Попытка
Запрос = Соединение.СоздатьЗапрос();
Запрос.Текст = "
| DROP TABLE product_price
|";
Запрос.ВыполнитьКоманду();
Исключение
КонецПопытки;
// Удалить product
Попытка
Запрос = Соединение.СоздатьЗапрос();
Запрос.Текст = "
| DROP TABLE product
|";
Запрос.ВыполнитьКоманду();
Исключение
КонецПопытки;
// Удалить category
Попытка
Запрос = Соединение.СоздатьЗапрос();
Запрос.Текст = "
| DROP TABLE category
|";
Запрос.ВыполнитьКоманду();
Исключение
КонецПопытки;
// Создание таблиц
// Создание category
Запрос = Соединение.СоздатьЗапрос();
Запрос.Текст = "
| CREATE TABLE category (
| id nvarchar(50) PRIMARY KEY,
| code nchar(9) NOT NULL,
| name nvarchar(50) NOT NULL,
| deleted boolean NOT NULL
| );
|";
Запрос.ВыполнитьКоманду();
// Создание product
Запрос = Соединение.СоздатьЗапрос();
Запрос.Текст = "
| CREATE TABLE product (
| id nvarchar(50) PRIMARY KEY,
| code nchar(9) NOT NULL,
| name nvarchar(100) NOT NULL,
| descr nvarchar(150) NOT NULL,
| category_id nvarchar(50) NOT NULL,
| fext nvarchar(4) NOT NULL,
| deleted boolean NOT NULL
| );
|";
Запрос.ВыполнитьКоманду();
// Создание product_price
Запрос = Соединение.СоздатьЗапрос();
Запрос.Текст = "
| CREATE TABLE product_price (
| id nvarchar(50) NOT NULL,
| from_date datetime NOT NULL,
| product_id nvarchar(50) NOT NULL,
| price decimal(10,2) NOT NULL
| );
|";
Запрос.ВыполнитьКоманду();
// Создание special_products
Запрос = Соединение.СоздатьЗапрос();
Запрос.Текст = "
| CREATE TABLE special_products (
| id nvarchar(50) PRIMARY KEY,
| number nchar(9) NOT NULL,
| from_date datetime NOT NULL
| );
|";
Запрос.ВыполнитьКоманду();
// Создание special_product
Запрос = Соединение.СоздатьЗапрос();
Запрос.Текст = "
| CREATE TABLE special_product (
| id nvarchar(50) REFERENCES special_products(id) ON DELETE CASCADE,
| row_index integer NOT NULL,
| product_id nvarchar(50) NOT NULL
| );
|";
Запрос.ВыполнитьКоманду();
Соединение.Закрыть();
Ответ = Новый HTTPСервисОтвет(200);
Возврат Ответ;
КонецФункции
Данный код будет прекрасно работать в тестовой среде, однако будет непригоден для развертывания в производственной среде. Проблема заключается в том, что при создании строки соединения с СУБД мы указали физический путь к файлу БД. Так как мы заранее не знаем, на каком диске, в какой папке и под управлением какой операционной системы будет выполняться наше приложение, указание прямого физического пути к файлу скорее всего приведет к проблемам. В нашем случае, наиболее универсальным будет метод, использующий пути в стиле web. В нашем случае - это ~/db/, однако провайдеры СУБД не работают с такими форматами путей, поэтому разумным подходом будет хранение пути к СУБД в вышеуказанном формате, с последующим преобразованием в физический путь во время выполнения.
Для решения этой задачи, используем библиотеку HttpMeans, которая не входит в каркасную конфигурацию, поэтому подключим эту библиотеку "вручную".
Для этого, создадим подсистему БиблиотекаСредстваHTTP, общий макет типа ДвоичныеДанные с именем ИсполняемыеФайлыБиблиотекаСредстваHTTP, поместив его в вышеуказанную подсистему, а также в подсистему ИсполняемыеФайлыWebПриложения.
Скачаем последнюю версию библиотек c github'а, удалим из архива все dll, кроме HttpMeans.dll и поместим получившийся архив в макет.
Создадим общий макет типа ТекстовыйДокумент с именем appSettingsHttpMeans и добавим его в подсистемы БиблиотекаСредстваHTTP и СекцияAppSettingsФайлаWebConfig.
В соответствии с документацией, добавим в содержимое макета необходимый ключ, для регистрации библиотеки в OneScript.
Поскольку данная библиотека пока не имеет кросплатформенной реализации, создадим общий модуль ПлатформозависимыеФункции, куда мы будем помещать все платформозависимые функции (в нашем случае - это преобразование виртуального пути в физический), а также модуль СлужебныеФункции, куда мы будем помещать различные вспомогательные функции (в нашем случае - это функция создающая соединение с БД) и поместим их в подсистему ОбъектыКонфигурацииWebПриложения.
Реализуем функцию, возвращающую открытое соединение к БД, примерно следующим образом
// Создает соединение с БД
//
Функция НовыйСоединениеБД() Экспорт
Соединение = Обработки.СоединениеСУБД.Создать();
Соединение.ТипСУБД = Обработки.ПараметрыСоединенияСУБД.ПолучитьТипСУБД("DbConn");
СтрокаСоединения = Обработки.ПараметрыСоединенияСУБД.ПолучитьСтрокуСоединения("DbConn");
ФизическийПуть = ПлатформозависимыеФункции.ПолучитьФизическийПутьИзВиртуального("~/db/") + "localdb.db";
Соединение.СтрокаСоединения = СтрЗаменить(СтрокаСоединения, "{{ПутьКФайлуБД}}", ФизическийПуть);
Соединение.Открыть();
Возврат Соединение;
КонецФункции
А также функцию, преобразующую виртуальный путь в физический
// Получает физический путь файловой системы, соответствующий виртуальному
//
Функция ПолучитьФизическийПутьИзВиртуального(ВиртуальныйПуть) Экспорт
//<1C>
Если СтрНачинаетсяС(ВиртуальныйПуть, "/") Тогда
ФизическийПуть = Константы.КаталогПриложения.Получить() + ВиртуальныйПуть;
ИначеЕсли СтрНачинаетсяС(ВиртуальныйПуть, "~") Тогда
ФизическийПуть = Константы.КаталогПриложения.Получить() + Прав(ВиртуальныйПуть, СтрДлина(ВиртуальныйПуть) - 1);
ИначеЕсли ВиртуальныйПуть = "" Тогда
ФизическийПуть = Константы.КаталогПриложения.Получить();
Иначе
ФизическийПуть = Константы.КаталогПриложения.Получить() + "\" + ВиртуальныйПуть;
КонецЕсли;
Возврат СтрЗаменить(ФизическийПуть, "/", "\");
//<!1C>
//<OneScript>
//СредстваHTTP = Новый СредстваHTTP;
//Возврат СредстваHTTP.ПолучитьФизическийПутьИзВиртуального(ВиртуальныйПуть);
//<!OneScript>
КонецФункции
Создадим новые параметры соединения с СУБД с кодом DbConn, копированием параметров TestConn и заменим физический путь на строку {{ПутьКФайлуБД}}.
Сгенерируем необходимые ключи и вставим их в соответствующие макеты.
В нашем случае - это макеты connectionStringsПоУмолчанию и appSettingsПоУмолчанию.
Также изменим код обработчика http-сервиса createdb таким образом, чтобы он для создания соединения с СУБД использовал функцию ПлатформозависимыеФункции.НовыйСоединениеБД.
В дальнейшем, мы будем использовать ее во всех наших функциях, реализующих работу с БД.
Обмен данными между 1С:Предприятие и сайтом
Для обмена между учетной системой и сайтом используем механизм, аналогичный тому, что использовался в демонстрационной конфигурации интернет-магазина. Суть механизма обмена учетная система->сайт можно представить в виде последовательности нижеследующих действий:
У каждого объекта конфигурации, учавствующего в обмене (в данном случае - это справочники ВидыНоменклатуры, Номенклатура, документы - УстановкаЦенНоменклатуры, а также СпециальныеБлюда) создаются подписки на события ПриЗаписи, ПередУдалением для справочников и ОбработкаПроведения, ОбработкаОтменаПроведения для документов.
При измененении объектов, обработчики подписок формируют данные, необходимые для обмена в формате json и помещают их в очередь изменений.
Элемент очереди изменений представляет собой документ, который содержит признак вида изменений, а также данные об изменяемом объекте в формате json.
Обмен осуществляется по расписанию, с использованием регламентного задания. Обработчик регламентного задания, на основании данных, содержащихся в документах-элементах очереди осуществляет вызов соответствующих http-сервисов на стороне сайта и передает необходимую информацию. В случае успешного завершения операции обновления, соответствующий элемент очереди помечается как выполненный.
В случае неудачи, к примеру временного отсутствия связи с сайтом, попытка обновления будет предпринята при следующем запуске регламентного задания.
Для реализации данного механизма, методом Ctrl+C, Ctrl+V перенесем необходимые объекты в нашу конфигурацию и добавим их в подсистему RestoОбъекты1С.
Создадим общий модуль ОбменССайтом, куда поместим необходимые обработчики подписок на события, а также http-сервисы, которые будут обновлять данные на стороне сайта.
Пример создания обмена с сайтом
В качестве примера, рассмотрим процесс реализации обмена справочника ВидыНоменклатуры.
Создадим подписки на события с именами ОбновитьВидНоменклатурыНаСайте и УдалитьВидНоменклатурыНаСайте.
В качестве источника событий, укажем справочник ВидыНоменклатуры.
Первая подписка будет обрабатывать событие ПриЗаписи, которое возникает при записи нового элемента справочника или обновлении существующего элемента. Вторая подписка будет обрабатывать событие ПередУдалением, которое возникает перед удалением элемента справочника. Код обработчиков подписок представлен ниже:
Процедура ОбновитьВидНоменклатурыНаСайтеПриЗаписи(Источник, Отказ) Экспорт
// Вставить содержимое обработчика.
Док = Документы.ОбновленияДляСайта.СоздатьДокумент();
Док.Дата = ТекущаяДата();
// Заполняем данные
// Обновляем категорию
Док.ВидОбновления = "uc";
Данные = Новый Соответствие;
Идентификатор = Строка(Источник.Ссылка.УникальныйИдентификатор());
Данные.Вставить("id", Идентификатор);
Данные.Вставить("name", Источник.Наименование);
Данные.Вставить("code", Источник.Код);
Данные.Вставить("deleted", Источник.ПометкаУдаления);
Док.ТелоЗапроса = JSON.Записать(Данные);
// Записываем документ
Док.Записать(РежимЗаписиДокумента.Запись);
КонецПроцедуры
Процедура УдалитьВидНоменклатурыНаСайтеПередУдалением(Источник, Отказ) Экспорт
// Вставить содержимое обработчика.
Док = Документы.ОбновленияДляСайта.СоздатьДокумент();
Док.Дата = ТекущаяДата();
// Заполняем данные
// Обновляем категорию
Док.ВидОбновления = "dc";
Данные = Новый Соответствие;
Идентификатор = Строка(Источник.Ссылка.УникальныйИдентификатор());
Данные.Вставить("id", Идентификатор);
Док.ТелоЗапроса = JSON.Записать(Данные);
// Записываем документ
Док.Записать(РежимЗаписиДокумента.Запись);
КонецПроцедуры
Как можно увидеть, обработчики создают элементы очереди обновлений и заполняют их необходимыми данными. В дальнейшем, элементы очереди будут использоваться для отправки соответствующих изменений на сайт в обработчике регламентного задания ВыполнитьОбменССайтом. Код обработчика представлен ниже.
// Выполняет обмен с сайтом в один поток
// Обновляет цены, товары, категории на сайте
//
Процедура ВыполнитьОбменССайтом() Экспорт
// Вставить содержимое обработчика.
Запрос = Новый Запрос("ВЫБРАТЬ
| ОбновленияДляСайта.Ссылка
|ИЗ
| Документ.ОбновленияДляСайта КАК ОбновленияДляСайта
|ГДЕ
| ОбновленияДляСайта.ПометкаУдаления = &ПометкаУдаления
| И ОбновленияДляСайта.Проведен = &Проведен
|
|УПОРЯДОЧИТЬ ПО
| ОбновленияДляСайта.Номер");
Запрос.УстановитьПараметр("ПометкаУдаления", Ложь);
Запрос.УстановитьПараметр("Проведен", Ложь);
Выборка = Запрос.Выполнить().Выбрать();
СуффиксСтраниц = Константы.СуффиксСтраницОбмена.Получить();
ОтносительныйURL = Константы.ОтносительныйURLСайта.Получить();
Если СуффиксСтраниц = Неопределено Тогда
СуффиксСтраниц = "";
КонецЕсли;
Соединение = Новый HTTPСоединение(
Константы.ИмяХостаСайта.Получить(), // сервер (хост)
80, // порт, по умолчанию для http используется 80, для https 443
, // пользователь для доступа к серверу (если он есть)
, // пароль для доступа к серверу (если он есть)
, // здесь указывается прокси, если он есть
, // таймаут в секундах, 0 или пусто - не устанавливать
// защищенное соединение, если используется https
);
Пока Выборка.Следующий() Цикл
Запрос = Новый HTTPЗапрос(ОтносительныйURL + Выборка.Ссылка.ВидОбновления + СуффиксСтраниц + ".os");
Запрос.УстановитьТелоИзСтроки(Выборка.Ссылка.ТелоЗапроса);
Результат = Соединение.ОтправитьДляОбработки(Запрос);
Если Не Результат.КодСостояния = 200 Тогда
ВызватьИсключение "Error: " + Строка(Результат.КодСостояния);
КонецЕсли;
Док = Выборка.Ссылка.ПолучитьОбъект();
// Можно просто удалить документ
Док.Записать(РежимЗаписиДокумента.Проведение, РежимПроведенияДокумента.Неоперативный);
КонецЦикла;
КонецПроцедуры
Как можно увидеть, суть работы этого обработчика состоит в последовательной отправке POST запросов к соответствующим http-сервисам сайта. Соответственно тело запросов содержит необходимую информацию об изменениях.
Для выполнения обновлений на стороне сайта, создадим http-сервисы uc.os и dc.os, которые будут соответственно создавать новые или обновлять существующие категории, а также удалять категории, которые были удалены в учетной системе.
Для облегчения работы, в консоли работы с СУБД, создадим запрос, который создает новый элемент таблицы category, которая является представлением справочника ВидыНоменклатуры на сайте.
И выполним его.
Для просмотра результатов выполнения - создадим запрос, который получает запись из таблицы категорий, по определенному id
Как можно увидеть, запись с указанными нами параметрами была успешно добавлена в БД.
Для удаления категорий, создадим запрос, который удаляет запись в таблице с определенным Id
Установим значение параметра id таким же как при добавлении и выполним запрос.
Для проверки откроем запрос получения записи по id и выполним его.
Как можно увидеть, запись была успешно удалена.
Создадим обработчики http-сервисов uc.os и dc.os, используя автоматически-генерируемый код ранее созданных запросов.
Функция ОбработкаВызоваHTTPСервиса(Запрос) Экспорт
// Проверяем корректность метода запроса
Если Не Запрос.HTTPМетод = "POST" Тогда
Возврат Новый HTTPСервисОтвет(404);
КонецЕсли;
// Получаем данные, необходимые для создания или обновления
СтрокаТело = Запрос.ПолучитьТелоКакСтроку();
Данные = JSON.Прочитать(СтрокаТело);
// Создаем соединение с БД
Соединение = СлужебныеФункции.НовыйСоединениеБД();
// Удаляем существующую запись, если есть
Запрос = Соединение.СоздатьЗапрос();
Запрос.Текст = "
| DELETE FROM category
| WHERE id = @id
|";
Запрос.УстановитьПараметр("id", Данные["id"]);
Запрос.ВыполнитьКоманду();
// Добавляем новую запись
Запрос = Соединение.СоздатьЗапрос();
Запрос.Текст = "
| INSERT INTO category (id, code, name, deleted)
| VALUES (@id, @code, @name, @deleted)
|";
Запрос.УстановитьПараметр("id", Данные["id"]);
Запрос.УстановитьПараметр("code", Данные["code"]);
Запрос.УстановитьПараметр("name", Данные["name"]);
Запрос.УстановитьПараметр("deleted", Данные["deleted"]);
Запрос.ВыполнитьКоманду();
// Закрываем соединение
Соединение.Закрыть();
Ответ = Новый HTTPСервисОтвет(200);
Возврат Ответ;
КонецФункции
Функция ОбработкаВызоваHTTPСервиса(Запрос) Экспорт
// Проверяем корректность метода
Если Не Запрос.HTTPМетод = "POST" Тогда
Возврат Новый HTTPСервисОтвет(404);
КонецЕсли;
// Получаем данные из тела запроса
СтрокаТело = Запрос.ПолучитьТелоКакСтроку();
Данные = JSON.Прочитать(СтрокаТело);
// Создаем новое соединение
Соединение = СлужебныеФункции.НовыйСоединениеБД();
// Удаляем существующую запись, если есть
Запрос = Соединение.СоздатьЗапрос();
Запрос.Текст = "
| DELETE FROM category
| WHERE id = @id
|";
Запрос.УстановитьПараметр("id", Данные["id"]);
Запрос.ВыполнитьКоманду();
// Закрываем соединение
Соединение.Закрыть();
Ответ = Новый HTTPСервисОтвет(200);
Возврат Ответ;
КонецФункции
Обновим web-публикацию 1С:Предприятие, добавив созданные http-сервисы.
В режиме предприятия, заполним значения констант ИмяХостаСайта (в нашем случае - localhost), а также ОтносительныйUrlСайта (в нашем случае - /Resto2/hs/).
Создадим элемент справочника ВидыНоменклатуры и через некоторое время посмотрим на очередь обновлений.
Проверим наличие категории на сайте.
Изменим наименование, а также, пометим элемент справочника к удалению.
Вновь просмотрим очередь обновлений и состяние объекта в БД.
Как можно увидеть, изменения в учетной системе успешно реплицируются на сайт.
Удалим элемент справочника и убедимся, что он исчез из БД сайта.
Аналогичным образом запрограммируем обмен для остальных объектов учетной системы и протестируем работоспособность.
Вывод списка
Получение данных
Как вы помните, за список специальных блюд в учетной системе отвечает документ СпециальныеБлюда, а на сайте соответственно таблицы special_products и special_product. Данные объекты однозначно определяют список специальных блюд, который необходимо отображать на сайте, начиная с некоторой даты и времени, которые определяются датой и временем документа. Поскольку в реальной жизни эти списки могут меняться, к тому же мы можем составлять такие списки будующей датой (скажем через неделю у нас начинается акция, которая продлится две недели), для начала необходимо определить документ-список, который действует в настоящий момент времени.
Для тестирования нашего запроса создадим два документа, один будет прошедшей датой, а другой - будующей.
Дождемся завершения репликации данных на сайт и приступим к написанию запроса.
В справочнике запросов создадим группу Специальные блюда, куда будем помещать все запросы, относящиеся к выводу списка специальных блюд. Затем, создадим новый запрос, определяющий идентификатор документа актуального списка.
Поскольку в списке специальных блюд должны быть представлены цены, создадим запрос, который получает актуальные цены номенклатуры. Также как и в предыдущем случае, цены могут меняться со времененем, а также в системе могут быть документы, которые устанавливают цены будующей датой.
Для тестирования запроса, создадим три документа установка цен номенклатуры, один из которых будет будующим числом.
Затем, создадим и протестируем соответствующий запрос
Исходя из визуального представления списка специальных блюд, в нем присутствует информация о наименовании блюда, его описании, изображение, а также актуальная цена.
Создадим и протестируем запрос, возвращающий вышеуказанную информацию для актуального документа-списка, скомбинировав ранее созданные запросы.
Используя генератор кода запроса, создадим в модуле http-сервиса, выводящего главную страницу функцию ПолучитьСписокСпециальныхБлюд, которая будет возвращать таблицу значений, содержащую информацию, необходимую для вывода.
// Получает список специальных блюд для вывода
//
Функция ПолучитьСписокСпециальныхБлюд()
// Определение идентификатора актуального списка
// Соединение
Соединение = СлужебныеФункции.НовыйСоединениеБД();
// Имя переменной: АктуальнаяДата
// Имя параметра: current_date
//
АктуальнаяДата = ТекущаяДата();
Запрос = Соединение.СоздатьЗапрос();
Запрос.Текст = "
| SELECT t1.id
| FROM special_products AS t1
| INNER JOIN
| (
| SELECT MAX(from_date) AS from_date
| FROM special_products
| WHERE from_date <= @current_date
| ) AS t2
| ON
| t2.from_date = t1.from_date
|";
Запрос.УстановитьПараметр("current_date", АктуальнаяДата);
Результаты = Запрос.ВыполнитьЗапрос().Выгрузить();
Если Результаты.Количество() = 0 Тогда
Возврат Новый ТаблицаЗначений;
КонецЕсли;
// Имя переменной: ИдентификаторАктуальногоСписка
// Имя параметра: list_id
//
ИдентификаторАктуальногоСписка = Результаты[0].id;
// Имя переменной: ПометкаУдаленияПродукт
// Имя параметра: product_deleted
//
ПометкаУдаленияПродукт = Ложь;
Запрос = Соединение.СоздатьЗапрос();
Запрос.Текст = "
| SELECT sp.row_index AS row_index, p.id AS id, p.fext AS fext, p.name AS name, p.descr AS descr, pr.price AS price
| FROM
| product AS p
| INNER JOIN
| special_product AS sp
| ON
| p.id = sp.product_id
| INNER JOIN
| (
| SELECT t1.product_id AS product_id, t1.price AS price
| FROM
| product_price AS t1
| INNER JOIN
| (
| SELECT product_id, MAX(from_date) as from_date
| FROM
| product_price
| WHERE
| from_date <= @current_date
| GROUP BY product_id
| ) AS t2
| ON
| t1.product_id = t2.product_id
| AND
| t1.from_date = t2.from_date
| ) AS pr
| ON
| sp.product_id = pr.product_id
| WHERE
| p.deleted = @product_deleted
| AND
| sp.id = @list_id
| ORDER BY sp.row_index ASC
|";
Запрос.УстановитьПараметр("current_date", АктуальнаяДата);
Запрос.УстановитьПараметр("list_id", ИдентификаторАктуальногоСписка);
Запрос.УстановитьПараметр("product_deleted", ПометкаУдаленияПродукт);
Результаты = Запрос.ВыполнитьЗапрос().Выгрузить();
Соединение.Закрыть();
Возврат Результаты;
КонецФункции
Отображение на странице
В макете главной страницы, отредактируем область СпециальныеБлюда так, что она примет нижеследующий вид:
<!--ШапкаСпециальныеБлюда-->
<section class="probootstrap-section-bg overlay" style="background-image: url({{Изображение}});" data-stellar-background-ratio="0.5" data-section="specialties">
<div class="container">
<div class="row">
<div class="col-md-12 text-center probootstrap-animate">
<div class="probootstrap-heading">
<h2 class="primary-heading">{{Заголовок1}}</h2>
<h3 class="secondary-heading">{{Заголовок2}}</h3>
</div>
</div>
</div>
</div>
</section>
<!--ШапкаСпециальныеБлюда-->
<!--СпециальныеБлюда-->
<!-- probootstrap-bg-white -->
<section class="probootstrap-section">
<div class="container">
<div class="row">
<div class="probootstrap-cell-retro">
<div class="half">
{{СпециальныеБлюдаСлева}}
</div>
<div class="half">
{{СпециальныеБлюдаСправа}}
</div>
</div>
</div>
</div>
</section>
<!--СпециальныеБлюда-->
<!--СпециальноеБлюдоЧетнаяСтрока-->
<div class="probootstrap-cell probootstrap-animate" data-animate-effect="fadeIn">
<div class="image" style="background-image: url({{Изображение}});"></div>
<div class="text text-center">
<h3>{{Наименование}}</h3>
<p>{{Описание}}</p>
<p class="price">{{Цена}}</p>
</div>
</div>
<!--СпециальноеБлюдоЧетнаяСтрока-->
<!--СпециальноеБлюдоНечетнаяСтрока-->
<div class="probootstrap-cell reverse probootstrap-animate" data-animate-effect="fadeIn">
<div class="image" style="background-image: url({{Изображение}});"></div>
<div class="text text-center">
<h3>{{Наименование}}</h3>
<p>{{Описание}}</p>
<p class="price">{{Цена}}</p>
</div>
</div>
<!--СпециальноеБлюдоНечетнаяСтрока-->
В модуле http-сервиса, отредактируем функцию ВывестиСписокСпециальныхБлюд, в соответствии с внесенными изменениями:
// Формирует список специальных блюд
//
Функция ВывестиСписокСпециальныхБлюд(МакетТекст, ПараметрыОформления)
ОбластьСпециальныеБлюда = Макеты.ПолучитьОбласть(МакетТекст, "<!--СпециальныеБлюда-->", "<!--СпециальныеБлюда-->");
ОбластьСпециальноеБлюдоНечетнаяСтрока = Макеты.ПолучитьОбласть(МакетТекст, "<!--СпециальноеБлюдоНечетнаяСтрока-->", "<!--СпециальноеБлюдоНечетнаяСтрока-->");
ОбластьСпециальноеБлюдоЧетнаяСтрока = Макеты.ПолучитьОбласть(МакетТекст, "<!--СпециальноеБлюдоЧетнаяСтрока-->", "<!--СпециальноеБлюдоЧетнаяСтрока-->");
СписокСпециальныхБлюд = ПолучитьСписокСпециальныхБлюд();
ТекстСтрокиСпециальныеБлюда = "";
ТекстСпециальныеБлюдаСлева = "";
ТекстСпециальныеБлюдаСправа = "";
Индекс = 0;
Для каждого СпециальноеБлюдо Из СписокСпециальныхБлюд Цикл
Параметры = Новый Соответствие;
Параметры.Вставить("Изображение", "img/" + СпециальноеБлюдо.id + СпециальноеБлюдо.fext);
Параметры.Вставить("Наименование", СпециальноеБлюдо.name);
Параметры.Вставить("Описание", СпециальноеБлюдо.descr);
Параметры.Вставить("Цена", СпециальноеБлюдо.price);
Если Индекс % 2 = 0 Тогда
Если Цел(Индекс/2) % 2 = 0 Тогда
ТекстСпециальныеБлюдаСлева = ТекстСпециальныеБлюдаСлева + Макеты.ЗаполнитьПараметры(ОбластьСпециальноеБлюдоЧетнаяСтрока.Текст, Параметры);
Иначе
ТекстСпециальныеБлюдаСлева = ТекстСпециальныеБлюдаСлева + Макеты.ЗаполнитьПараметры(ОбластьСпециальноеБлюдоНечетнаяСтрока.Текст, Параметры);
КонецЕсли;
Иначе
Если Цел(Индекс/2) % 2 = 0 Тогда
ТекстСпециальныеБлюдаСправа = ТекстСпециальныеБлюдаСправа + Макеты.ЗаполнитьПараметры(ОбластьСпециальноеБлюдоЧетнаяСтрока.Текст, Параметры);
Иначе
ТекстСпециальныеБлюдаСправа = ТекстСпециальныеБлюдаСправа + Макеты.ЗаполнитьПараметры(ОбластьСпециальноеБлюдоНечетнаяСтрока.Текст, Параметры);
КонецЕсли;
КонецЕсли;
Индекс = Индекс + 1;
КонецЦикла;
Параметры = Новый Соответствие;
Параметры.Вставить("СпециальныеБлюдаСлева", ТекстСпециальныеБлюдаСлева);
Параметры.Вставить("СпециальныеБлюдаСправа", ТекстСпециальныеБлюдаСправа);
Возврат Макеты.ЗаполнитьПараметры(ОбластьСпециальныеБлюда.Текст, Параметры);
КонецФункции
Обновим конфигурацию и протестируем вывод.
Вывод меню ресторана
Фактически, меню ресторана - это список товаров (блюд), которые ресторан предлагает к продаже. Конечно, этот список может меняться со временем. Также, возможны такие ситуации, когда меню формируется на предстоящий период заранее. Как правило, меню группируется по видам (напитки, первые блюда, закуски etc.). Соответственно при выводе меню на сайте необходимо предусмотреть данную группировку.
Объекты в учетной системе
Для реализации данного функционала, создадим в учетной системе документ - МенюРесторана, содержит список блюд, предлагаемых к продаже. Поскольку функционал данного документа практически идентичен документу СпециальныеБлюда, мы создадим его простым копированием этого документа. В качестве категорий блюд будем использовать справочник ВидыНоменклатуры, созданный нами ранее.
Объекты на стороне сайта
В БД сайта, создадим таблицы product_lists и product_list аналогично тому, как это было сделано для документа СпециальныеБлюда (таблицы special_products и special_product).
Обмен данными между 1С:Предприятие и сайтом
Для организации обмена, создадим подписки обработки проведения и обработки отмены проведения аналогично тому, как это было сделано для документа СпециальныеБлюда. Также, создадим http-сервисы upl и dpl, аналогично тому, как это было сделано для других объектов.
Затем, необходимо опубликовать новые http-сервисы и обновить переопределения Url.
Вывод меню
Для простоты, порядок вывода категорий блюд, будет определяться кодом соответствующих элементов справочника ВидыНоменклатуры.
Получение данных
Получение данных из БД аналогично получению данных списка специальных блюд. Создадим и соответствующий запрос в консоли запросов, а затем перенесем его в функцию ПолучитьМенюРесторана.
// Получает список специальных блюд для вывода
//
Функция ПолучитьМенюРесторана()
// Соединение
Соединение = СлужебныеФункции.НовыйСоединениеБД();
// Имя переменной: АктуальнаяДата
// Имя параметра: current_date
//
АктуальнаяДата = ТекущаяДата();
Запрос = Соединение.СоздатьЗапрос();
Запрос.Текст = "
| SELECT t1.id
| FROM product_lists AS t1
| INNER JOIN
| (
| SELECT MAX(from_date) AS from_date
| FROM product_lists
| WHERE from_date <= @current_date
| ) AS t2
| ON
| t2.from_date = t1.from_date
|";
Запрос.УстановитьПараметр("current_date", АктуальнаяДата);
Результаты = Запрос.ВыполнитьЗапрос().Выгрузить();
Если Результаты.Количество() = 0 Тогда
Возврат Новый ТаблицаЗначений;
КонецЕсли;
// Имя переменной: ИдентификаторАктуальногоМеню
// Имя параметра: list_id
//
ИдентификаторАктуальногоМеню = Результаты[0].id;
// Имя переменной: ПометкаУдаления
// Имя параметра: is_deleted
//
ПометкаУдаления = Ложь;
Запрос = Соединение.СоздатьЗапрос();
Запрос.Текст = "
| SELECT c.code AS cat_code, c.name AS cat_name, p.id AS id, p.fext AS fext, p.name AS name, p.descr AS descr, p.ingridients AS ingridients, pr.price AS price
| FROM
| product AS p
| INNER JOIN
| product_list AS pl
| ON
| p.id = pl.product_id
| INNER JOIN
| category AS c
| ON
| c.id = p.category_id
| INNER JOIN
| (
| SELECT t1.product_id AS product_id, t1.price AS price
| FROM
| product_price AS t1
| INNER JOIN
| (
| SELECT product_id, MAX(from_date) as from_date
| FROM
| product_price
| WHERE
| from_date <= @current_date
| GROUP BY product_id
| ) AS t2
| ON
| t1.product_id = t2.product_id
| AND
| t1.from_date = t2.from_date
| ) AS pr
| ON
| pl.product_id = pr.product_id
| WHERE
| p.deleted = @is_deleted
| AND
| c.deleted = @is_deleted
| AND
| pl.id = @list_id
| ORDER BY cat_code, name ASC
|";
Запрос.УстановитьПараметр("current_date", АктуальнаяДата);
Запрос.УстановитьПараметр("list_id", ИдентификаторАктуальногоМеню);
Запрос.УстановитьПараметр("is_deleted", ПометкаУдаления);
Результаты = Запрос.ВыполнитьЗапрос().Выгрузить();
Соединение.Закрыть();
Возврат Результаты;
КонецФункции
Отображение на странице
Преобразуем область МенюРесторана в макете главной страницы таким образом, чтобы он принял нижеследующий вид:
<!--ШапкаМенюРесторана-->
<section class="probootstrap-section-bg overlay" style="background-image: url({{Изображение}});" data-stellar-background-ratio="0.5" data-section="menu">
<div class="container">
<div class="row">
<div class="col-md-12 text-center probootstrap-animate">
<div class="probootstrap-heading">
<h2 class="primary-heading">{{Заголовок1}}</h2>
<h3 class="secondary-heading">{{Заголовок2}}</h3>
</div>
</div>
</div>
</div>
</section>
<!--ШапкаМенюРесторана-->
<!--МенюРесторана-->
<section class="probootstrap-section probootstrap-bg-white">
<div class="container">
<div class="row">
{{КатегорииМеню}}
</div>
</div>
</section>
<!--МенюРесторана-->
<!--КатегорияМеню-->
<div class="col-md-12 text-center probootstrap-animate">
<div class="probootstrap-heading">
<h2 class="primary-heading">{{НаименованиеКатегории}}</h2>
</div>
</div>
<div class="col-md-6">
<ul class="menus">
{{ЭлементыМенюСлева}}
</ul>
</div>
<div class="col-md-6">
<ul class="menus">
{{ЭлементыМенюСправа}}
</ul>
</div>
<!--КатегорияМеню-->
<!--ЭлементМеню-->
<li>
<figure class="image"><img src="{{Изображение}}" alt="{{Наименование}}" style="width: 100%"></figure>
<div class="text">
<span class="price">{{Цена}}</span>
<h3>{{Наименование}}</h3>
<p>{{Ингридиенты}}</p>
</div>
</li>
<!--ЭлементМеню-->
Обновим конфигурацию, опубликуем вновь созданные http-сервисы, создадим несколько видов номенклатуры и распределим ранее созданные товары между ними. Затем, протестируем сделанные изменения. Меню ресторана будет отображаться примерно следующим образом:
Заказ столика
Секция заказ столика представляет собой форму ввода, в которую клиент вводит информацию, необходимую для заказа и нажимает кнопку подтверждения заказа. На основании этих данных в учетной системе должен быть создан предварительный заказ, на основании которого менеджер ресторана связывается с клиентом, уточняет детали и производит резервирование.
Схема обмена данными
Поскольку возможно наш сайт будет находиться на хостинге, а учетная система в офисе ресторана, возможны случаи перебоев со связью, между рестораном и сайтом. Поэтому мы будем сохранять заявки, созданные клиентами непосредственно на сайте. Также, с целью экономии лицензий и упрощения инфраструктуры мы не будем публиковать какие-либо сервисы учетной системы во внешний мир. Учетная система будет периодически (при помощи регламентного задания) обращаться к сайту и получать новые заявки. Таким образом, алгоритм обмена заявками на бронирования будет примерно следующим:
Клиент заполняет данные формы и нажимает кнопку подтверждения заявки.
Заявка сохраняется в локальной БД сайта.
Клиент получает сообщение о том, что его заявка принята.
Учетная система запрашивает информацию о заявках и формирует соответствующие документы.
После того, как документы созданы, учетная система сообщает сайту информацию о заявках, которые можно удалить из локальной БД сайта.
Соответствующий сервис сайта удаляет заявки, которые уже есть в учетной системе.
Создание заказа на основании данных web-формы
В исходном шаблоне, передача данных введенных клиентом осуществляется посредством отпраки post запроса с данными формы. Данный метод является стандартным и достаточно простым, однако при его использовании страница будет перерисована заново. Также, будут некоторые неудобства, связанные с выводом сообщения об успешном или неуспешном создании заявки. В нашем случае, при нажатии кнопки подтверждения мы будем формировать ajax запрос к http-сервису сайта, который будет создавать заявку в локальной БД сайта и возвращать результат этой операции. После выполнения запроса, пользователю будет выведено диалоговое окно с соответствующим сообщением. В серьезных проектах, данный функционал должны реализовывать разработчики frontend части, а мы должны пердоставить соответствующие http-сервисы, однако поскольку таких разработчиков под рукой не оказалось - я позаимствовал идею и основу кода у коллег из Novastar.
Отредактируем шаблон нашей страницы нижеследующим образом:
Создадим блок, соответствующий диалоговому окну
<div id="reservationModal" class="modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="icon-cross"></i></button>
<h4 class="modal-title">
<center></center>
</h4>
</div>
<div class="modal-body"></div>
<div class="modal-footer">
<center>
<button type="button" class="btn btn-primary" data-dismiss="modal">{{ТекстКнопкиЗакрыть}}</button>
</center>
</div>
</div>
</div>
</div>
Подключим jQuery и добавим обработчик нажатия кнопки подтверждения заказа, который будет отправлять запрос с данными формы в формате JSON и выводиить соответствующее диалоговое окно.
<script src="https://cdnjs.cloudflare.com/ajax/libs/smoothscroll/1.4.4/SmoothScroll.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.matchHeight/0.7.2/jquery.matchHeight-min.js"></script>
<script>
$(document).ready(function () {
$('.matchheight').matchHeight();
$('#reservation').submit(function (e) {
var fd = {
people: $('#people').val(),
name: $('#name').val(),
date: $('#date').val(),
time: $('#time').val(),
email: $('#email').val(),
phone: $('#phone').val()
};
var myJSON = JSON.stringify(fd);
e.preventDefault();
$.ajax({
url: 'co.os',
type: 'POST',
dataType: "json",
data: myJSON,
success: function (data) {
if (!data.errorstatus) {
$('.modal-body').html("{{ТекстЗаявкаУспешноСозданаДетали}}");
$('.modal-title center').text("{{ТекстЗаявкаУспешноСоздана}}");
} else {
$('.modal-body').html("{{ТекстЗаявкаНеСозданаДетали}}");
$('.modal-title center').text("{{ТекстЗаявкаНеСоздана}}");
}
$("#reservationModal").modal('show');
},
error: function() {
$('.modal-title center').text("{{ТекстЗаявкаНеСоздана}}");
$('.modal-body').html("{{ТекстЗаявкаНеСозданаДетали}}");
$("#reservationModal").modal('show');
}
});
});
$('#feedback').submit(function (e) {
var fd = {
name: $('#c_name').val(),
email: $('#c_email').val(),
message: $('#c_message').val()
};
var myJSON = JSON.stringify(fd);
e.preventDefault();
$.ajax({
url: 'cf.os',
type: 'POST',
dataType: "json",
data: myJSON,
success: function (data) {
if (!data.errorstatus) {
$('.modal-title center').text("{{ТекстОбращениеУспешноСоздано}}");
$('.modal-body').html("{{ТекстОбращениеУспешноСозданоДетали}}");
} else {
$('.modal-title center').text("{{ТекстОбращениеНеСоздано}}");
$('.modal-body').html("{{ТекстОбращениеНеСозданоДетали}}");
}
$("#reservationModal").modal('show');
},
error: function() {
$('.modal-title center').text("{{ТекстОбращениеНеСоздано}}");
$('.modal-body').html("{{ТекстОбращениеНеСозданоДетали}}");
$("#reservationModal").modal('show');
}
});
});
});
</script>
Объекты на сайте
При помощи консоли запросов, создадим в БД таблицу order, которая будет отвечать за хранение заявок на резервирование.
Объекты в учетной системе
Для реализации данного функционала создадим документ ЗаявкаБронирования, который будет содержать информацию, введенную клиентом на сайте, а также идентификатор заявки на сайте.
Обмен данными
Создадим http-сервис no.os, который будет принимать введенные данные и сохранять в локальной БД.
Функция ОбработкаВызоваHTTPСервиса(Запрос) Экспорт
Текст = Запрос.ПолучитьТелоКакСтроку();
Данные = JSON.Прочитать(Текст);
// Соединение
Соединение = СлужебныеФункции.НовыйСоединениеБД();
// Имя переменной: Идентификатор
// Имя параметра: id
//
Идентификатор = Строка(Новый УникальныйИдентификатор);
// Имя переменной: ДатаСоздания
// Имя параметра: creation_date
//
ДатаСоздания = ТекущаяДата();
// Имя переменной: ДатаРезервирования
// Имя параметра: reservation_date
//
СтруктураДатаВремя = ПолучитьДатуИзСтроки(Данные["date"]);
СтруктураДатаВремя = ПолучитьВремяИзСтроки(Данные["time"], СтруктураДатаВремя);
ДатаРезервирования = Дата(
СтруктураДатаВремя.Год,
СтруктураДатаВремя.Месяц,
СтруктураДатаВремя.День,
СтруктураДатаВремя.Час,
СтруктураДатаВремя.Минута,
0
);
// Имя переменной: КоличествоМест
// Имя параметра: people
//
КоличествоМест = Число(Данные["people"]);
// Имя переменной: ИмяКлиента
// Имя параметра: name
//
ИмяКлиента = Данные["name"];
// Имя переменной: Телефон
// Имя параметра: phone
//
Телефон = Данные["phone"];
// Имя переменной: АдресЭлПочты
// Имя параметра: email
//
АдресЭлПочты = Данные["email"];
Запрос = Соединение.СоздатьЗапрос();
Запрос.Текст = "
| INSERT INTO order_request (id, creation_date, reservation_date, people, name, phone, email)
| VALUES
| (@id, @creation_date, @reservation_date, @people, @name, @phone, @email)
|";
Запрос.УстановитьПараметр("id", Идентификатор);
Запрос.УстановитьПараметр("creation_date", ДатаСоздания);
Запрос.УстановитьПараметр("reservation_date", ДатаРезервирования);
Запрос.УстановитьПараметр("people", КоличествоМест);
Запрос.УстановитьПараметр("name", ИмяКлиента);
Запрос.УстановитьПараметр("phone", Телефон);
Запрос.УстановитьПараметр("email", АдресЭлПочты);
Запрос.ВыполнитьКоманду();
Соединение.Закрыть();
Ответ = Новый HTTPСервисОтвет(200);
Ответ.УстановитьТелоИзСтроки("{}");
Возврат Ответ;
КонецФункции
// Преобразует строку вида mm/dd/yyyy в структуру, содержащую Год, Месяц, Число
//
Функция ПолучитьДатуИзСтроки(СтрокаДата, СтруктураДата = Неопределено)
Если СтруктураДата = Неопределено Тогда
СтруктураДата = Новый Структура;
КонецЕсли;
СтрокаДата = СтрЗаменить(СтрокаДата, " ", "");
Индекс = Найти(СтрокаДата, "/");
СтрокаМесяц = Лев(СтрокаДата, Индекс - 1);
СтрокаДата = Сред(СтрокаДата, Индекс + 1);
Индекс = Найти(СтрокаДата, "/");
СтрокаДень = Лев(СтрокаДата, Индекс - 1);
СтрокаГод = Сред(СтрокаДата, Индекс + 1);
СтруктураДата.Вставить("Год", Число(СтрокаГод));
СтруктураДата.Вставить("Месяц", Число(СтрокаМесяц));
СтруктураДата.Вставить("День", Число(СтрокаДень));
Возврат СтруктураДата;
КонецФункции
// Преобразует строку вида HH:MMpm(am) в части времени
Функция ПолучитьВремяИзСтроки(СтрокаВремя, СтруктураВремя = Неопределено)
Если СтруктураВремя = Неопределено Тогда
СтруктураВремя = Новый Структура;
КонецЕсли;
СтрокаВремя = СтрЗаменить(СтрокаВремя, " ", "");
Индекс = Найти(СтрокаВремя, ":");
СтрокаЧасы = Лев(СтрокаВремя, Индекс - 1);
СтрокаВремя = Сред(СтрокаВремя, Индекс + 1);
ДоПослеПолудня = НРег(Прав(СтрокаВремя, 2));
СтрокаМинуты = Лев(СтрокаВремя, СтрДлина(СтрокаВремя) - 2);
Часы = Число(СтрокаЧасы);
Минуты = Число(СтрокаМинуты);
Если ДоПослеПолудня = "am" И Часы = 12 Тогда
Часы = 0;
ИначеЕсли ДоПослеПолудня = "pm" И Часы = 12 Тогда
Часы = 12;
ИначеЕсли ДоПослеПолудня = "pm" Тогда
Часы = Часы + 12;
КонецЕсли;
СтруктураВремя.Вставить("Час", Часы);
СтруктураВремя.Вставить("Минута", Минуты);
Возврат СтруктураВремя;
КонецФункции
Создадим http-сервис go.os, который будет возвращать заказы из БД сайта в учетную систему для импорта
Функция ОбработкаВызоваHTTPСервиса(Запрос) Экспорт
// Получить все заявки бронирования
// Соединение
Соединение = СлужебныеФункции.НовыйСоединениеБД();
Запрос = Соединение.СоздатьЗапрос();
Запрос.Текст = "
| SELECT *
| FROM
| order_request
|";
ЗаявкиБронирования = Запрос.ВыполнитьЗапрос().Выгрузить();
Соединение.Закрыть();
СписокЗаявок = Новый Массив;
Для каждого ЗаявкаБронирования Из ЗаявкиБронирования Цикл
ДанныеЗаявка = Новый Соответствие;
ДанныеЗаявка.Вставить("id", ЗаявкаБронирования.id);
ДанныеЗаявка.Вставить("creation_date", Формат(ЗаявкаБронирования.creation_date, "ДФ=""ггггММддЧЧммсс"""));
ДанныеЗаявка.Вставить("reservation_date", Формат(ЗаявкаБронирования.reservation_date, "ДФ=""ггггММддЧЧммсс"""));
ДанныеЗаявка.Вставить("people", ЗаявкаБронирования.people);
ДанныеЗаявка.Вставить("name", ЗаявкаБронирования.name);
ДанныеЗаявка.Вставить("email", ЗаявкаБронирования.email);
ДанныеЗаявка.Вставить("phone", ЗаявкаБронирования.phone);
СписокЗаявок.Добавить(ДанныеЗаявка);
КонецЦикла;
ТекстТело = JSON.Записать(СписокЗаявок);
Ответ = Новый HTTPСервисОтвет(200);
Ответ.УстановитьТелоИзСтроки(ТекстТело);
Возврат Ответ;
КонецФункции
Создадим http-сервис do.os, который будет удалять заказ с определенным идентификатором, после появления его в учетной системе
Функция ОбработкаВызоваHTTPСервиса(Запрос) Экспорт
// УдалитьЗаявкуБронирования
// Соединение
Соединение = СлужебныеФункции.НовыйСоединениеБД();
Данные = JSON.Прочитать(Запрос.ПолучитьТелоКакСтроку());
// Имя переменной: Идентификатор
// Имя параметра: id
//
Идентификатор = Данные["id"];
Запрос = Соединение.СоздатьЗапрос();
Запрос.Текст = "
| DELETE FROM order_request
| WHERE
| id = @id
|";
Запрос.УстановитьПараметр("id", Идентификатор);
Запрос.ВыполнитьКоманду();
Соединение.Закрыть();
Ответ = Новый HTTPСервисОтвет(200);
Возврат Ответ;
КонецФункции
Создадим регламентное задание ПолучитьЗаявкиССайта, с нижеследующим обработчиком:
Процедура ПолучитьЗаявкиССайта() Экспорт
// Вставить содержимое обработчика.
СуффиксСтраниц = Константы.СуффиксСтраницОбмена.Получить();
ОтносительныйURL = Константы.ОтносительныйURLСайта.Получить();
Если СуффиксСтраниц = Неопределено Тогда
СуффиксСтраниц = "";
КонецЕсли;
Соединение = Новый HTTPСоединение(
Константы.ИмяХостаСайта.Получить(), // сервер (хост)
80, // порт, по умолчанию для http используется 80, для https 443
, // пользователь для доступа к серверу (если он есть)
, // пароль для доступа к серверу (если он есть)
, // здесь указывается прокси, если он есть
, // таймаут в секундах, 0 или пусто - не устанавливать
// защищенное соединение, если используется https
);
Запрос = Новый HTTPЗапрос(ОтносительныйURL + "go" + СуффиксСтраниц + ".os");
Ответ = Соединение.Получить(Запрос);
Если Не Ответ.КодСостояния = 200 Тогда
Возврат;
КонецЕсли;
МассивЗаявок = JSON.Прочитать(Ответ.ПолучитьТелоКакСтроку());
Для каждого Заявка Из МассивЗаявок Цикл
// Проверяем наличие заказа в ИБ, если есть - пропускаем
Если Не Документы.ЗаявкаБронирования.НайтиПоРеквизиту("ИдентификаторЗаявкиНаСайте", Заявка["id"]) = Документы.ЗаявкаБронирования.ПустаяСсылка() Тогда
Продолжить;
КонецЕсли;
ДокЗаявка = Документы.ЗаявкаБронирования.СоздатьДокумент();
ДокЗаявка.Дата = ТекущаяДата();
ДокЗаявка.ДатаБронирования = Дата(Заявка["reservation_date"]);
ДокЗаявка.КоличествоМест = Заявка["people"];
ДокЗаявка.ИмяКлиента = Заявка["name"];
ДокЗаявка.Телефон = Заявка["phone"];
ДокЗаявка.АдресЭлПочты = Заявка["email"];
ДокЗаявка.ИдентификаторЗаявкиНаСайте = Заявка["id"];
ДокЗаявка.ДатаСозданияНаСайте = Дата(Заявка["creation_date"]);
ДокЗаявка.Записать(РежимЗаписиДокумента.Запись);
УдалитьЗаявкуССайта(Заявка["id"]);
КонецЦикла;
КонецПроцедуры
Обновим конфигурацию ИБ, опубликуем новые http-сервисы и протестируем функционал.
События ресторана
Создание галереи событий ресторана аналогично созданию списка специальных блюд. В учетной системе, события представлены справочником СобытиеРесторана, который аналогичен справочнику Номенклатура. Список событий на сайте формируется документом УстановкаСобытийРесторана. На стороне сайта, этим объектам соответствуют таблицы event, event_lists и event_list. Для обмена данными используются http-сервисы ue.os, de.os, uel.os, del.os.
Обратная связь
Реализация этого функционала аналогична реализации приема заявок на резервирование. На стороне учетной системы, обращения клиента представлены документом ОбращениеКлиента. На стороне сайта, данный объект представлен таблицей feedback. Для обмена данными используются http-сервисы cf.os, gf.os и df.os. Для периодического получения обращений клиентов используется регламентное задание ПолучитьОбращенияКлиентов.
Итоговая структура БД сайта представлена на рисунке ниже.
Создание web-приложения OneScript
Поскольку в процессе создания и тестирования приложения мы добавили строку соединения с СУБД, а также библиотеку СредстваHTTP, нам необходимо полностью перевыгрузить приложение. Однако, поскольку учетная система и БД сайта уже содержат некоторые данные, перед полной выгрузкой приложения сформируем архив "прочие файлы" и поместим его в одноименный макет. Обновим конфигурацию и выгрузим ее в файлы. Затем, в режиме предприятия на всякий случай создадим архив нашего приложения. Далее обновим приложение с установленным чекбоксом Обновить приложение. Также необходимо заново добавить права на запись для папок db и img пользователю IUSR.
Протестируем созданное приложение.
Развертывание web-приложения.
Для развертывания в различных средах, могут быть использованы методики, описанные в статьях:
В нашем случае - развернем приложение на web-хостинге.
Заключение
Вот таким вот нехитрым образом, почти не покидая конфигуратор, мы создали настраиваемое бизнес-ориентированное web-приложение, которое интегрировано с 1С:Предприятие. Думаю, что интеграция с какой-либо типовой конфигурацией не должно вызвать каких-либо проблем.
Теперь о впечатлениях, полученных в процессе создания данной демонстрационной конфигурации:
Код является практически полностью переносимым. Непереносимой является единственная функция, преобразующая виртуальный путь в физический :
// Получает физический путь файловой системы, соответствующий виртуальному
//
Функция ПолучитьФизическийПутьИзВиртуального(ВиртуальныйПуть) Экспорт
//<1C>
Если СтрНачинаетсяС(ВиртуальныйПуть, "/") Тогда
ФизическийПуть = Константы.КаталогПриложения.Получить() + ВиртуальныйПуть;
ИначеЕсли СтрНачинаетсяС(ВиртуальныйПуть, "~") Тогда
ФизическийПуть = Константы.КаталогПриложения.Получить() + Прав(ВиртуальныйПуть, СтрДлина(ВиртуальныйПуть) - 1);
ИначеЕсли ВиртуальныйПуть = "" Тогда
ФизическийПуть = Константы.КаталогПриложения.Получить();
Иначе
ФизическийПуть = Константы.КаталогПриложения.Получить() + "\" + ВиртуальныйПуть;
КонецЕсли;
Возврат СтрЗаменить(ФизическийПуть, "/", "\");
//<!1C>
//<OneScript>
//СредстваHTTP = Новый СредстваHTTP;
//Возврат СредстваHTTP.ПолучитьФизическийПутьИзВиртуального(ВиртуальныйПуть);
//<!OneScript>
КонецФункции
Инструменты для работы с БД оказались достаточно удобными и вместе с автогенерацией кода сэкономили массу времени при разработке и отладке.
Механизм сравнения/объединения оказался удобным, для включения библиотек.
Механизм макетов также оказался кстати, так как не приходилось думать о размещении файлов.
Хранение конфигурационных файлов в формате yaml также оказалось очень удобным, ввиду возможности динамического добавления элементов и читаемого вида.
Конечно для создания web-приложений необходимы хотя-бы базовые знания html, css и javascript, поскольку небольшие правки практически неизбежны. Альтернативой является наличие отдельного специалиста по frontend.
Неудобным является необходимость программировать обмен для каждого объекта информационной базы, который должен быть отображен на сайте. В результате появляется большое количество http-сервисов с похожим кодом. Хотя этот код несложен и быстро создается, особенно с использованием автогенерации, все-таки это отнимает время и его создание является достаточно нудным занятием. По всей видимости эту проблему можно решить, создав подсистему, в которой будет реализован обмен "типовых" справочников и документов, которую можно включать сравнением/объединением в новые проекты.