Разрабатывая внешнюю обработку в 1С для автоматизации оповещений, мы часто сталкиваемся с задачами, требующими глубокого понимания платформы и принципов работы с данными. Давайте подробнее разберем процесс создания такой обработки, которая будет информировать руководителя о значимых заказах клиентов, отправляя отформатированный список по электронной почте.
Мы рассмотрим ключевые аспекты: от правильного формирования запросов и работы с датами до отправки табличных документов по email, сохранения пользовательских настроек и корректной обработки ошибок. Наша цель – создать надежный и удобный инструмент для конфигурации 1С:Управление торговлей 8, редакция 11.
Центральным элементом нашей обработки является запрос к базе данных, который выбирает документы «Заказ клиента», соответствующие заданным условиям по периоду и сумме. Рассмотрим, как правильно построить такой запрос, чтобы избежать ошибок и получить точные данные.
Изначально в запросе могут возникнуть сложности с передачей дат, если мы используем фиксированные значения или неверно определяем параметры. Важно, чтобы запрос принимал две даты — начало и конец периода, а также корректно обрабатывал их с учетом времени.
В тексте запроса нам необходимо использовать два отдельных параметра для начала и конца периода. Это позволит нам гибко управлять диапазоном выборки. Используйте оператор МЕЖДУ для определения диапазона дат.
"ВЫБРАТЬ
| ЗаказКлиента.Номер КАК Номер,
| ЗаказКлиента.Дата КАК Дата,
| ЗаказКлиента.Партнер.Наименование КАК Клиент, // Используем Наименование партнера
| ЗаказКлиента.СуммаДокумента КАК СуммаДокумента,
| ЗаказКлиента.Менеджер КАК Менеджер
|ИЗ
| Документ.ЗаказКлиента КАК ЗаказКлиента
|ГДЕ
| ЗаказКлиента.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
| И ЗаказКлиента.СуммаДокумента > &СуммаДокумента
|УПОРЯДОЧИТЬ ПО
| Дата, Номер"
После того как текст запроса определен, нам необходимо правильно установить его параметры. Если на форме обработки используется реквизит с типом СтандартныйПериод, мы можем извлечь из него ДатаНачала и ДатаОкончания. Для того чтобы обеспечить корректную выборку данных на начало и конец указанного дня с учетом времени, применяем функции НачалоДня() и КонецДня().
Для поля «Сумма», которое пользователь указывает на форме, мы просто передаем его значение.
// Предполагаем, что на форме есть реквизиты:
// ПериодВыборки (тип СтандартныйПериод)
// СуммаОтбора (тип Число)
Запрос = Новый Запрос;
Запрос.Текст = "
ВЫБРАТЬ
| ЗаказКлиента.Номер КАК Номер,
| ЗаказКлиента.Дата КАК Дата,
| ЗаказКлиента.Партнер.Наименование КАК Клиент,
| ЗаказКлиента.СуммаДокумента КАК СуммаДокумента,
| ЗаказКлиента.Менеджер КАК Менеджер
|ИЗ
| Документ.ЗаказКлиента КАК ЗаказКлиента
|ГДЕ
| ЗаказКлиента.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
| И ЗаказКлиента.СуммаДокумента >= &СуммаОтбора
|УПОРЯДОЧИТЬ ПО
| Дата, Номер";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("ДатаНачала", НачалоДня(ПериодВыборки.ДатаНачала));
Запрос.УстановитьПараметр("ДатаОкончания", КонецДня(ПериодВыборки.ДатаОкончания));
Запрос.УстановитьПараметр("СуммаОтбора", СуммаОтбора);
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Обратите внимание, что в запросе использовано ЗаказКлиента.СуммаДокумента >= &СуммаОтбора, чтобы включить заказы, сумма которых равна указанному значению, если это соответствует требованиям "свыше которой заказы являются интересными" (в примере письма "от 100 000 руб.").
Теперь, когда у нас есть выборка данных, нам необходимо сформировать из них табличный документ и отправить его по электронной почте. Это включает создание макета, заполнение его данными, сохранение во временный файл и прикрепление к письму.
Для того чтобы письмо содержало красиво оформленную таблицу, мы будем использовать макет. Макет — это шаблон табличного документа, который мы можем создать в конфигураторе. Он позволяет определить области для заголовка, шапки таблицы, детальных записей и подвала.
[Номер], [Дата], [Клиент], [СуммаДокумента], [Менеджер]).Код для заполнения табличного документа будет выглядеть следующим образом:
Макет = ВнешниеОбработки.ПолучитьМакет("МакетОтчета"); // Получаем наш макет
ТабДок = Новый ТабличныйДокумент;
ТабДок.Очистить();
// Выводим заголовок письма
ОбластьЗаголовок = Макет.ПолучитьОбласть("Заголовок");
ОбластьЗаголовок.Параметры.ТекстЗаголовка = "Здравствуйте, Иван Иванович!"; // Пример параметра
ОбластьЗаголовок.Параметры.ПериодТекст = "за период с " + Формат(ПериодВыборки.ДатаНачала, "ДФ=dd.MM.yyyy") + " по " + Формат(ПериодВыборки.ДатаОкончания, "ДФ=dd.MM.yyyy");
ОбластьЗаголовок.Параметры.СуммаОтбораТекст = "от " + Формат(СуммаОтбора, "ЧЦ=10; ЧДЦ=2") + " руб.";
ТабДок.Вывести(ОбластьЗаголовок);
// Выводим шапку таблицы
ОбластьШапкаТаблицы = Макет.ПолучитьОбласть("ШапкаТаблицы");
ТабДок.Вывести(ОбластьШапкаТаблицы);
// Выводим детальные записи
ОбластьДетальныхЗаписей = Макет.ПолучитьОбласть("Детали");
СуммаВсего = 0;
КоличествоЗаказов = 0;
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
ОбластьДетальныхЗаписей.Параметры.Заполнить(ВыборкаДетальныеЗаписи);
ОбластьДетальныхЗаписей.Параметры.Клиент = СокрЛП(ВыборкаДетальныеЗаписи.Клиент); // Пример обработки поля
ОбластьДетальныхЗаписей.Параметры.СуммаДокумента = Формат(ВыборкаДетальныеЗаписи.СуммаДокумента, "ЧЦ=10; ЧДЦ=2");
ТабДок.Вывести(ОбластьДетальныхЗаписей);
СуммаВсего = СуммаВсего + ВыборкаДетальныеЗаписи.СуммаДокумента;
КоличествоЗаказов = КоличествоЗаказов + 1;
КонецЦикла;
// Выводим подвал таблицы с итоговыми данными
ОбластьПодвалТаблицы = Макет.ПолучитьОбласть("ПодвалТаблицы");
ОбластьПодвалТаблицы.Параметры.КоличествоЗаказов = КоличествоЗаказов;
ОбластьПодвалТаблицы.Параметры.СуммаВсего = Формат(СуммаВсего, "ЧЦ=10; ЧДЦ=2");
ОбластьПодвалТаблицы.Параметры.СуммаПрописью = ЧислоПрописью(СуммаВсего, "рубль", "ru_RU");
ТабДок.Вывести(ОбластьПодвалТаблицы);
// Выводим общий подвал
ОбластьПодвал = Макет.ПолучитьОбласть("Подвал");
ТабДок.Вывести(ОбластьПодвал);
Для того чтобы прикрепить табличный документ к электронному письму, его необходимо сначала сохранить в файл. Мы можем выбрать различные форматы, например, PDF, HTML, XLS или XLSX. Выбор формата зависит от того, как руководитель предпочитает просматривать информацию.
Рассмотрим пример сохранения в формате HTML, который часто хорошо отображается непосредственно в теле письма, или PDF для универсальности.
// Создаем временное имя файла
ИмяВременногоФайлаHTML = ПолучитьИмяВременногоФайла(".html");
ИмяВременногоФайлаPDF = ПолучитьИмяВременногоФайла(".pdf");
// Сохраняем табличный документ
Попытка
ТабДок.Записать(ИмяВременногоФайлаHTML, ТипТабличногоДокумента.HTML);
// Или в PDF:
// ТабДок.Записать(ИмяВременногоФайлаPDF, ТипТабличногоДокумента.PDF);
Исключение
Сообщить("Ошибка при сохранении табличного документа: " + ОписаниеОшибки());
Возврат;
КонецПопытки;
После сохранения табличного документа в файл, мы можем приступить к отправке электронного письма по протоколу SMTP. Для этого нам потребуются объекты ИнтернетПочтовыйПрофиль и ИнтернетПочта.
Подключиться().ИнтернетПочтовоеСообщение, задайте отправителя, получателей, тему и тело письма.Вложения.Добавить() прикрепите ранее сохраненный временный файл.Послать().Отключиться().В качестве темы письма и текста мы используем динамически формируемые строки, включающие период и сумму отбора.
// Формируем тему письма
ТемаПисьма = "Крупные заказы за период с " + Формат(ПериодВыборки.ДатаНачала, "ДФ=dd.MM.yyyy") + " по " + Формат(ПериодВыборки.ДатаОкончания, "ДФ=dd.MM.yyyy");
// Настраиваем почтовый профиль
Профиль = Новый ИнтернетПочтовыйПрофиль;
Профиль.АдресСервераSMTP = "smtp.yandex.ru"; // или другой SMTP-сервер
Профиль.ПортSMTP = 587; // Для SSL/TLS обычно 465 или 587
Профиль.ИспользоватьSSL = Истина; // Рекомендуется для безопасности
Профиль.Пользователь = "ваш_логин@yandex.ru"; // Ваш почтовый ящик
Профиль.Пароль = "ваш_пароль"; // Пароль от почтового ящика
Профиль.АутентификацияSMTP = СпособSMTPАутентификации.Login;
Профиль.ПользовательSMTP = Профиль.Пользователь;
Профиль.ПарольSMTP = Профиль.Пароль;
Почта = Новый ИнтернетПочта;
Попытка
Почта.Подключиться(Профиль);
Исключение
Сообщить("Ошибка при подключении к почтовому серверу: " + ОписаниеОшибки(), СтатусСообщения.Важное);
Возврат;
КонецПопытки;
Сообщение = Новый ИнтернетПочтовоеСообщение;
Сообщение.Отправитель = "ваш_логин@yandex.ru";
Сообщение.Получатели.Добавить("rukovoditel@firma.ru"); // Адрес руководителя
Сообщение.Тема = ТемаПисьма;
Сообщение.Тексты.Добавить("Текст письма смотрите во вложении.", ТипТекстаПочтовогоСообщения.Простой);
// Прикрепляем файл с табличным документом
Если ФайлСуществует(ИмяВременногоФайлаHTML) Тогда
Сообщение.Вложения.Добавить(ИмяВременногоФайлаHTML, "КрупныеЗаказы.html");
// Или, если сохранили в PDF:
// Сообщение.Вложения.Добавить(ИмяВременногоФайлаPDF, "КрупныеЗаказы.pdf");
КонецЕсли;
Попытка
Почта.Послать(Сообщение);
Сообщить("Письмо успешно отправлено.", СтатусСообщения.Информация);
Исключение
Сообщить("Ошибка при отправке письма: " + ОписаниеОшибки(), СтатусСообщения.Важное);
КонецПопытки;
Почта.Отключиться();
// Удаляем временные файлы
УдалитьФайлы(ИмяВременногоФайлаHTML);
// УдалитьФайлы(ИмяВременногоФайлаPDF);
Важно помнить, что если вы хотите, чтобы табличный документ отображался непосредственно в теле письма, то его лучше сохранять в формате HTML и передавать как основной текст сообщения, а не вложение. Для этого используйте Сообщение.Тексты.Добавить(ПрочитатьТекстФайла(ИмяВременногоФайлаHTML), ТипТекстаПочтовогоСообщения.HTML).
Для удобства пользователя необходимо, чтобы значение поля «Сумма» запоминалось при повторном открытии обработки. Мы можем реализовать это, используя методы СохранитьЗначение() и ВосстановитьЗначение().
Эти методы позволяют сохранять и восстанавливать значения реквизитов формы в пользовательских настройках, привязанных к конкретной обработке.
ПриОткрытии восстанавливаем значение.ПриЗакрытии или ПриЗавершенииРаботыСистемы сохраняем текущее значение.
// В модуле формы:
// Процедура "ПриОткрытии"
&НаСервере
Процедура ПриОткрытииНаСервере()
// Восстанавливаем сохраненное значение суммы при открытии формы
ЗначениеСуммы = ВосстановитьЗначение("СуммаОтбора");
Если ЗначениеСуммы <> Неопределено Тогда
СуммаОтбора = ЗначениеСуммы;
Иначе
СуммаОтбора = 100000; // Значение по умолчанию, если ничего не сохранено
КонецЕсли;
КонецПроцедуры
// Процедура "ПриЗакрытии"
&НаКлиенте
Процедура ПриЗакрытии()
// Сохраняем текущее значение суммы при закрытии формы
СохранитьЗначение("СуммаОтбора", СуммаОтбора);
КонецПроцедуры
Не забудьте, что реквизит формы СуммаОтбора должен быть создан с соответствующим типом (Число).
Перед отправкой письма обязательно необходимо проверить, что пользователь заполнил все необходимые поля. Если поля не заполнены, мы должны вывести информативное сообщение, привязанное к соответствующему полю формы. Кроме того, поле «Сумма» не может быть меньше 100 000 руб., и это тоже следует контролировать.
Для проверки заполнения полей мы можем использовать метод Сообщить(), передавая вторым параметром имя элемента формы, к которому нужно привязать сообщение. Это обеспечит визуальное выделение незаполненного поля для пользователя.
Также, для реквизитов формы можно установить свойство ПроверкаЗаполнения в значение ВыдаватьОшибку, что обеспечит автоматическую проверку платформой.
// В обработчике кнопки "Отправить"
&НаКлиенте
Процедура ОтправитьНажатие(Команда)
Если Не ПроверитьЗаполнениеПолей() Тогда
Возврат;
КонецЕсли;
// Выполняем действия по отправке письма на сервере
ОтправитьПисьмоНаСервере();
КонецПроцедуры
&НаКлиенте
Функция ПроверитьЗаполнениеПолей()
РезультатПроверки = Истина;
// Проверка поля "Период"
Если ПериодВыборки.ДатаНачала = Дата(1,1,1) И ПериодВыборки.ДатаОкончания = Дата(1,1,1) Тогда
Сообщить("Поле 'Период' не заполнено!", СтатусСообщения.Важное, "ПериодВыборки"); // "ПериодВыборки" - имя элемента формы
РезультатПроверки = Ложь;
КонецЕсли;
// Проверка поля "Сумма"
Если СуммаОтбора = 0 Тогда // или любое другое значение, означающее незаполненность
Сообщить("Поле 'Сумма' не заполнено!", СтатусСообщения.Важное, "СуммаОтбора"); // "СуммаОтбора" - имя элемента формы
РезультатПроверки = Ложь;
КонецЕсли;
// Проверка значения суммы не менее 100 000 руб.
Если СуммаОтбора < 100000 Тогда
Сообщить("Поле 'Сумма' не может быть меньше 100 000 руб.", СтатусСообщения.Важное, "СуммаОтбора");
РезультатПроверки = Ложь;
КонецЕсли;
Возврат РезультатПроверки;
КонецФункции
&НаСервере
Процедура ОтправитьПисьмоНаСервере()
// Здесь размещаем весь код по формированию запроса,
// формированию табличного документа и отправке письма,
// который мы рассмотрели выше.
// ...
КонецПроцедуры
Для поля «Сумма» мы можем также настроить ограничение на ввод прямо в свойствах элемента формы, используя минимальное значение. Однако более надежным и интерактивным будет использование обработчика события ПриИзменении для этого поля.
// В модуле формы, для элемента формы "СуммаОтбора":
&НаКлиенте
Процедура СуммаОтбораПриИзменении(Элемент)
Если СуммаОтбора < 100000 Тогда
Сообщить("Сумма не может быть меньше 100 000 руб. Установлено минимальное значение.", СтатусСообщения.Важное, Элемент);
СуммаОтбора = 100000;
КонецЕсли;
КонецПроцедуры
Таким образом, мы не только проинформируем пользователя об ошибке, но и автоматически скорректируем значение, если оно не соответствует условию.
Правильное распределение кода между модулем формы и модулем объекта обработки является ключевым для поддержания порядка, удобства отладки и возможности переиспользования логики. Мы всегда стремимся к четкому разделению ответственности.
Модуль формы предназначен для всего, что непосредственно связано с пользовательским интерфейсом и взаимодействием с ним. Здесь размещаем:
ПроверитьЗаполнениеПолей().СохранитьЗначение() и ВосстановитьЗначение().Пример из нашей задачи:
ОтправитьНажатие(), ПроверитьЗаполнениеПолей(), СуммаОтбораПриИзменении().ПриОткрытии() и ПриЗакрытии(), содержащие логику сохранения и восстановления значения поля «Сумма».Модуль объекта — это место для размещения основной бизнес-логики, которая не зависит от конкретной формы представления. Все, что связано с работой с данными, выполнением сложных расчетов, взаимодействием с другими объектами метаданных или внешними системами (такими как почтовый сервер), должно находиться здесь.
Основные задачи модуля объекта:
ТабличныйДокумент).Пример из нашей задачи:
ТабличныйДокумент с отчетом.ОтправитьПисьмо(), которая принимает готовый табличный документ (или путь к файлу) и параметры для отправки, и выполняет всю работу с ИнтернетПочтовыйПрофиль и ИнтернетПочта.Вынося основную логику в модуль объекта, мы делаем код более модульным, тестируемым и легким для сопровождения. Из модуля формы мы просто вызываем необходимые процедуры и функции из модуля объекта, передавая им требуемые параметры.
// Пример взаимодействия между модулем формы и модулем объекта
// в упрощенном виде
// --- В модуле формы ---
&НаКлиенте
Процедура ОтправитьНажатие(Команда)
Если Не ПроверитьЗаполнениеПолей() Тогда
Возврат;
КонецЕсли;
// Вызываем серверную процедуру, которая может использовать методы модуля объекта
ОтправитьОтчетРуководителюНаСервере(ПериодВыборки, СуммаОтбора);
КонецПроцедуры
&НаСервере
Процедура ОтправитьОтчетРуководителюНаСервере(Период, Сумма)
// Здесь мы можем вызывать функции из модуля объекта обработки
ЭтаНаСервере.ОтправитьОтчетРуководителю(Период, Сумма);
КонецПроцедуры
// --- В модуле объекта (внутри объекта внешней обработки) ---
Процедура ОтправитьОтчетРуководителю(Период, Сумма) Экспорт
// 1. Формирование запроса и получение данных
Запрос = Новый Запрос;
Запрос.Текст = "..." // Текст запроса
Запрос.УстановитьПараметр("ДатаНачала", НачалоДня(Период.ДатаНачала));
Запрос.УстановитьПараметр("ДатаОкончания", КонецДня(Период.ДатаОкончания));
Запрос.УстановитьПараметр("СуммаОтбора", Сумма);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
// 2. Формирование ТабличногоДокумента
ТабДок = СформироватьТабличныйДокументОтчета(Выборка, Период, Сумма); // Вызываем вспомогательную функцию из этого же модуля объекта
// 3. Сохранение ТабличногоДокумента в файл
ИмяФайлаОтчета = ПолучитьИмяВременногоФайла(".html");
ТабДок.Записать(ИмяФайлаОтчета, ТипТабличногоДокумента.HTML);
// 4. Отправка письма
ОтправитьEmailСОтчетом(ИмяФайлаОтчета, Период, Сумма); // Вызываем вспомогательную процедуру
// 5. Удаление временного файла
УдалитьФайлы(ИмяФайлаОтчета);
КонецПроцедуры
// Вспомогательная функция для формирования ТабличногоДокумента
Функция СформироватьТабличныйДокументОтчета(Выборка, Период, Сумма)
// ... логика формирования ТабДок из пункта 2.1 ...
// Возвращает готовый ТабличныйДокумент
Возврат ТабДок;
КонецФункции
// Вспомогательная процедура для отправки Email
Процедура ОтправитьEmailСОтчетом(ПутьКФайлуОтчета, Период, Сумма)
// ... логика отправки Email из пункта 2.3 ...
// Использует ПутьКФайлуОтчета для вложения
КонецПроцедуры
При работе с 1С:Управление торговлей 8, редакция 11, всегда полезно знать о встроенном функционале, который может упростить или даже полностью заменить часть нашей разработки. Конфигурация УТ 11 предоставляет мощные инструменты для работы с отчетами и их рассылками.
ТабличныйДокумент вручную, мы могли бы создать внешний отчет на СКД. СКД предоставляет гибкие возможности для формирования отчетов, включая группировки, отборы, вывод итогов и даже форматирование. Результат такого отчета также можно сохранить в файл и отправить.Таким образом, мы рассмотрели подробный процесс создания внешней обработки для оповещения руководителя о крупных заказах. Применение этих подходов поможет вам не только решить текущую задачу, но и улучшить навыки разработки в 1С, следуя лучшим практикам.