Задача: выгрузить табличную часть формы в файл формата XLSX. К сожалению на прямую это не сделать, но зато мы можем воспользоваться функционалом компонента ПостроительОтчета, который в свою очередь понимает на входе таблицу значений. В итоге нам остаётся лишь выгрузить табличную часть в таблицу значений, которую в свою оочередь скормить построителю отчетов. На выходе будет ТабличныйДокумент, который уже штатно может сохраняться в т.ч. и в формат xlsx. Код получается примерно такой::
&НаСервере
Функция ЭкспортВExcelНаСервере()
ТабДок = Новый ТабличныйДокумент;
Тз = Новый ТаблицаЗначений;
тз.Колонки.Добавить("Отделение");
тз.Колонки.Добавить("Подразделение");
тз.Колонки.Добавить("ЛС");
тз.Колонки.Добавить("КодСопоставления");
тз.Колонки.Добавить("ДатаРождения");
тз.Колонки.Добавить("МестоРождения");
тз.Колонки.Добавить("СерияПаспорта");
тз.Колонки.Добавить("НомерПаспорта");
тз.Колонки.Добавить("СерияНомерПаспорта");
тз.Колонки.Добавить("РезультатПроверки");
для каждого стр из объект.РезультатСопоставления цикл
нс=тз.Добавить();
нс.Отделение=стр.Отделение;
нс.Подразделение=стр.Подразделение;
нс.ЛС=стр.ЛС;
нс.КодСопоставления=стр.КодСопоставления;
нс.ДатаРождения=стр.ДатаРождения;
нс.МестоРождения=стр.МестоРождения;
нс.СерияПаспорта=стр.СерияПаспортаС;
нс.НомерПаспорта=стр.НомерПаспортаС;
нс.СерияНомерПаспорта=стр.СерияНомерПаспорта;
нс.РезультатПроверки=стр.РезультатПроверки;
конеццикла;
Построитель = Новый ПостроительОтчета;
Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(Тз);
Построитель.Выполнить();
Построитель.Вывести(ТабДок);
ИмяВременногоФайлаХар = ПолучитьИмяВременногоФайла(".xlsx");
ТабДок.Записать(ИмяВременногоФайлаХар,ТипФайлаТабличногоДокумента.XLSX);
сообщить(ИмяВременногоФайлаХар);
Двоичное=Новый ДвоичныеДанные(ИмяВременногоФайлаХар);
Адрес=ПоместитьВоВременноеХранилище(Двоичное,ЭтаФорма.УникальныйИдентификатор);
Возврат Адрес;
КонецФункции
&НаКлиенте
Процедура ЭкспортВExcel(Команда)
Адрес=ЭкспортВExcelНаСервере();
Описание=Новый ОписаниеПередаваемогоФайла(ПолучитьИмяВременногоФайла(".xlsx"),Адрес);
МассивОписаний=Новый Массив;
МассивОписаний.Добавить(Описание);
ПолучитьФайлы(МассивОписаний,,,Ложь);
Для Каждого фф Из МассивОписаний Цикл
ЗапуститьПриложение(фф.Имя);
КонецЦикла;
КонецПроцедуры
Пользователь нажимает кнопку «Экспорт в эксель», на сервере формируется файл и передаётся на клиент. На клиенте он открывается приложением по умолчанию для данного формата файла. Код получился кросс платформенный, что в наше время очень актуально.
Прилетела задача прочитать сообщение из почтового ящика по протоколу IMAP. Последнее из пришедших. В принципе в 1С есть штатный функционал для чтения почты, поэтому пример всё сам расскажет за себя:
Процедура ЧитнутьНаСервереImap()
Профиль = Новый ИнтернетПочтовыйПрофиль;
Профиль.АутентификацияPOP3 = Ложь;
Профиль.АутентификацияSMTP = Ложь;
// IMAP
Профиль.ИспользоватьSSLIMAP = Истина;
Профиль.АдресСервераIMAP = "цукацу-укацука.уцкацук.ru";
Профиль.ПортIMAP = "993";
Профиль.ПользовательIMAP = "укацу@укацукау.ru";
Профиль.ПарольIMAP = "цукацукацук";
Профиль.ТолькоЗащищеннаяАутентификацияIMAP=Ложь;
Почта = Новый ИнтернетПочта;
Сообщ = Новый СообщениеПользователю();
Почта.Подключиться(Профиль, ПротоколИнтернетПочты.IMAP); //ПротоколИнтернетПочты.POP3
заголовки=Почта.ПолучитьЗаголовки();
если заголовки.Количество()>0 тогда
ПоследнийЗаголовок=Новый Массив();
ПоследнийЗаголовок.Добавить(заголовки[заголовки.Количество()-1]);
МассивСообщений = Новый Массив;
МассивСообщений = Почта.Выбрать(Ложь,ПоследнийЗаголовок);
ТекстСообщения=МассивСообщений[0].тексты[0].Текст;
сообщить(ТекстСообщения);
конецесли;
Почта.Отключиться();
КонецПроцедуры
Из особенностей логики — сначала вычитываю заголовки, т.к. если почты много, то чтение писем будет ОЧЕНЬ долгим. Далее получаю последний по счету заголовок, и вычитываю текст конкретно уже этого письма.
Задача добавление рабочих дней к дате (добавить рабочие дни к дате) встречается довольно часто. И штатно должна решаться с помощью стандартного функционала БСП — «Производственный календарь». Например так:
Функция ДобавитьКДатеРабочиеДни(ДатаНач,ЧислоРабочихДней) экспорт
Запрос=Новый Запрос;
Запрос.Текст="ВЫБРАТЬ
| ВЫБОР
| КОГДА РегламентированныйПроизводственныйКалендарь.ВидДня = &РабочийДень
| ИЛИ РегламентированныйПроизводственныйКалендарь.ВидДня = &ПредпраздничныйДень
| ТОГДА 1
| ИНАЧЕ 0
| КОНЕЦ КАК ЧислоРабочихДней,
| РегламентированныйПроизводственныйКалендарь.ДатаКалендаря КАК ДатаКалендаря
|ПОМЕСТИТЬ ТЗ
|ИЗ
| РегистрСведений.РегламентированныйПроизводственныйКалендарь КАК РегламентированныйПроизводственныйКалендарь
|ГДЕ
| РегламентированныйПроизводственныйКалендарь.ДатаКалендаря МЕЖДУ &ДатаНач И &ДатаКон
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ТЗ.ДатаКалендаря,
| СУММА(ТЗ1.ЧислоРабочихДней) КАК ЧислоРабочихДней
|ПОМЕСТИТЬ ТЗНакопл
|ИЗ
| ТЗ КАК ТЗ
| ЛЕВОЕ СОЕДИНЕНИЕ ТЗ КАК ТЗ1
| ПО (ТЗ1.ДатаКалендаря <= ТЗ.ДатаКалендаря)
|
|СГРУППИРОВАТЬ ПО
| ТЗ.ДатаКалендаря
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| МАКСИМУМ(ТЗ.ДатаКалендаря) КАК ДатаКалендаря
|ИЗ
| ТЗНакопл КАК ТЗ
|ГДЕ
| ТЗ.ЧислоРабочихДней = &ЧислоРабочихДней";
Запрос.УстановитьПараметр("ДатаНач", ДатаНач);
ЧислоНедель = Цел(ЧислоРабочихДней/5+0.5);
ЧислоВыходныхДней = ЧислоНедель*2;
МаксимальноеКоличествоПраздничныхДнейПодряд = 8; //РождественскиеКаникулы
ДатаКон = ДатаНач+(ЧислоВыходныхДней+ЧислоРабочихДней+МаксимальноеКоличествоПраздничныхДнейПодряд)*24*60*60;
Запрос.УстановитьПараметр("ДатаКон",ДатаКон);
Запрос.УстановитьПараметр("ЧислоРабочихДней",ЧислоРабочихДней);
Запрос.УстановитьПараметр("РабочийДень", Перечисления.ВидыДнейПроизводственногоКалендаря.Рабочий);
Запрос.УстановитьПараметр("ПредпраздничныйДень", Перечисления.ВидыДнейПроизводственногоКалендаря.Предпраздничный);
Выборка = Запрос.Выполнить().Выбрать();
Результат = 0;
Если Выборка.Следующий() Тогда
Результат = КонецДня(Выборка.ДатаКалендаря)+1;
Конецесли;
Возврат Результат;
КонецФункции
Однако не всё же не все конфигурации содержат производственный календарь. Например почему-то обделено Делопроизводство 2.0. Что-бы не городить огород,, решил задачу «в лоб», без учёта праздничных дней — просто проверяю выпадение даты на субботу-воскресенье. В моём случае этого хватило.
Функция ДобавитьКДатеРабочиеДни(ДатаПроверки,дней)
добавлено=0;
пока добавлено<>дней цикл
ДатаПроверки=ДатаПроверки+86400;
Если ДеньНедели(ДатаПроверки) <> 6 ИЛИ ДеньНедели(ДатаПроверки) <> 7 Тогда
добавлено=добавлено+1;
конецесли;
конеццикла;
Возврат ДатаПроверки;
КонецФункции
Задача: Открыть файл прикрепленный к справочнику на клиенте приложением по умолчанию (передать файл с сервера на клиент)
Решение: любой файл который «прикреплен» к справочнику, если используется БСП , на самом деле находится в справочнике Файлы. Это для старых версий БСП. В новых версиях имя справочника носит шаблонное имя [ИмяСправочника]ПрисоединенныеФайлы. В обоих вариантах у этик справочников есть реквизит «Владелец файла». Вот по нему и получим самый первый прикрепленный файл.
&НаСервере
Функция ОткрытьФайлНаСервере()
фф="";
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Файлы.Ссылка КАК Ссылка,
| Файлы.ПолноеНаименование КАК ПолноеНаименование,
| Файлы.ТекущаяВерсияПутьКФайлу КАК ТекущаяВерсияПутьКФайлу,
| Файлы.ТекущаяВерсияТом.ПолныйПутьWindows КАК ТекущаяВерсияТомПолныйПутьWindows,
| Файлы.ГлавныйФайл КАК ГлавныйФайл,
| Файлы.ТекущаяВерсияРасширение КАК ТекущаяВерсияРасширение,
| Файлы.Наименование КАК Наименование
|ИЗ
| Справочник.Файлы КАК Файлы
|ГДЕ
| Файлы.ВладелецФайла = &ВладелецФайла
| И Файлы.ПометкаУдаления = ЛОЖЬ";
Запрос.УстановитьПараметр("ВладелецФайла", объект.ДокОб);
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
сообщить(ВыборкаДетальныеЗаписи.Ссылка);
фф=ВыборкаДетальныеЗаписи.ТекущаяВерсияТомПолныйПутьWindows+ВыборкаДетальныеЗаписи.ТекущаяВерсияПутьКФайлу;
КонецЦикла;
если фф<>"" тогда
Двоичное=Новый ДвоичныеДанные(фф);
Адрес=ПоместитьВоВременноеХранилище(Двоичное,ЭтаФорма.УникальныйИдентификатор);
Возврат Новый Структура("error,address,filename",false,Адрес,ВыборкаДетальныеЗаписи.Наименование+"."+ВыборкаДетальныеЗаписи.ТекущаяВерсияРасширение);
иначе
Возврат Новый Структура("error,address,filename",true);
конецесли
КонецФункции
На клиенте таким образом мы получим имя файла и адрес во временном хранилище. Остаётся сохранить файл во временный каталог и открыть его приложением по умолчанию:
&НаКлиенте
Процедура ОткрытьФайл(Команда)
рез=ОткрытьФайлНаСервере();
если рез.error=false тогда
Двоичное=ПолучитьИзВременногоХранилища(рез.address);
ИмяФайла = КаталогВременныхФайлов() + рез.filename;
Двоичное.Записать(ИмяФайла);
ЗапуститьПриложение(ИмяФайла);
конецесли;
КонецПроцедуры
Со временем, даже в небольших конфигурациях 1С количество пользователей чаще всего увеличивается. А если еще и компания достаточно большая, то возникает необходимость следить за тем, чтобы учётки вовремя отключались. А еще и были безопасны с точки зрения использования. Например: достаточно часто пользователь меняет пароль, включена ли у него доменная авторизация и т.п., Фактически полноценная проверка безопасности пользователей 1С
В моём случае возникла необходимость регулярно мониторить служебные учётные данные. Количество разработчиков большое. Количество http и web сервисов не большое, но постоянно растёт, т.к. мы придерживаюсь принципа: один сервис — одна учётка для доступа. Делается это для того чтобы было удобнее логировать и отслеживать кто что и как. Соответственно необходимо чтобы эти служебные пользователи имели строго определенные доступы, и не имели возможность непосредственного входа в 1С через толстый и тонкий клиенты.
После калькуляции требований безопасности я выявил следующие показатели для того чтобы считать учётную запись «неблагонадёжной»:
нет доменной авторизации
разрешен вход в 1с (галочка «Доступ к информационной базе разрешен» в справочнике Пользователи)
у пользователя включены роли ЗапускВебКлиента, ЗапускТолстогоКлиента, ЗапускВнешнегоСоединения
При ответе «Да» на все три условия, пользователя помещаю в соответствующий список.
Кроме того дополнительно провожу проверку, что в правах одной из ролей есть права: ЗапускВебКлиента, ЗапускТолстогоКлиента, ЗапускВнешнегоСоединения
В итоге родил такую обработку:
Процедура СК_ГР_ПроверкаБезопасностиПользователя() экспорт
УстановитьПривилегированныйРежим(Истина);
Выборка = ПользователиИнформационнойБазы.ПолучитьПользователей();
МассивДляОтчета=Новый Массив();
данные="";
бд=НСтр(СтрокаСоединенияИнформационнойБазы(), "Ref");
Для Каждого ЭлементМассива Из Выборка Цикл
если ЭлементМассива.АутентификацияОС=ложь тогда
если ЭлементМассива.АутентификацияСтандартная=истина тогда
инф=Новый Структура("полноеимя,имя,email,датапароля,показыватьвспискевыбора,роли,пользователь,ЗапускВебКлиента,ЗапускТолстогоКлиента,ЗапускТонкогоКлиента,ЗапускВнешнегоСоединения");
инф.полноеимя=ЭлементМассива.ПолноеИмя;
инф.имя=ЭлементМассива.Имя;
инф.email=ЭлементМассива.АдресЭлектроннойПочты;
инф.датапароля=ЭлементМассива.ДатаУстановкиПароля;
инф.показыватьвспискевыбора=ЭлементМассива.ПоказыватьВСпискеВыбора;
инф.роли="";
ЕстьПароль = ЭлементМассива.ПарольУстановлен;
Роли = ЭлементМассива.Роли;
Для Каждого Роль Из Роли Цикл
если ПравоДоступа("ВебКлиент",Метаданные,Роль) тогда
инф.роли=инф.роли+Роль.Имя+", ";
иначеесли ПравоДоступа("ВнешнееСоединение",Метаданные,Роль) тогда
инф.роли=инф.роли+Роль.Имя+", ";
иначеесли ПравоДоступа("ТолстыйКлиент",Метаданные,Роль) тогда
инф.роли=инф.роли+Роль.Имя+", ";
иначеесли ПравоДоступа("ТонкийКлиент",Метаданные,Роль) тогда
инф.роли=инф.роли+Роль.Имя+", ";
конецесли;
КонецЦикла;
поль=Справочники.Пользователи.НайтиПоРеквизиту("ИдентификаторПользователяИБ",ЭлементМассива.УникальныйИдентификатор);
ЗапускРоль="";
если поль<>Справочники.Пользователи.ПустаяСсылка() тогда
инф.ЗапускВебКлиента=УправлениеДоступом.ЕстьРоль("ЗапускВебКлиента",, поль);
если инф.ЗапускВебКлиента=истина тогда
ЗапускРоль=ЗапускРоль+"Веб, ";
конецесли;
инф.ЗапускТолстогоКлиента=УправлениеДоступом.ЕстьРоль("ЗапускТолстогоКлиента",, поль);
если инф.ЗапускТолстогоКлиента=истина тогда
ЗапускРоль=ЗапускРоль+"Толстый, ";
конецесли;
инф.ЗапускТонкогоКлиента=УправлениеДоступом.ЕстьРоль("ЗапускТонкогоКлиента",, поль);
если инф.ЗапускТонкогоКлиента=истина тогда
ЗапускРоль=ЗапускРоль+"Тонкий, ";
конецесли;
инф.ЗапускВнешнегоСоединения=УправлениеДоступом.ЕстьРоль("ЗапускВнешнегоСоединения",, поль);
если инф.ЗапускВнешнегоСоединения=истина тогда
ЗапускРоль=ЗапускРоль+"Внешние, ";
конецесли;
конецесли;
инф.пользователь=поль.Наименование;
МассивДляОтчета.Добавить(инф);
данные=данные+"<tr>";
данные=данные+" <td>"+инф.полноеимя+"</td>";
данные=данные+" <td>"+инф.имя+"</td>";
данные=данные+" <td>"+инф.пользователь+"</td>";
данные=данные+" <td>"+инф.email+"</td>";
данные=данные+" <td>"+инф.датапароля+"</td>";
данные=данные+" <td>"+инф.показыватьвспискевыбора+"</td>";
данные=данные+" <td>"+ЗапускРоль+"</td>";
данные=данные+" <td>"+инф.роли+"</td>";
данные=данные+"</tr>";
Конецесли;
Конецесли;
КонецЦикла;
если МассивДляОтчета.Количество()>0 тогда
тело="<style>td {border: 1px solid;} th{border: 1px solid;} table.table {word-wrap:true;border-collapse: collapse;border-spacing: 0;}table.table > thead > tr > th {font-size: 14px;font-weight: normal;padding-top: 7px;padding-bottom: 7px;}.table > thead > tr > th {vertical-align: bottom;border-bottom: 2px solid #ddd;}table.table th {background-color: #153e76;}</style>";
тело=тело+"Критерии попадания в этот список: отключена доменная авторизация, разрешен вход в 1С<br/>";
тело=тело+"<strong>Подозрительные учётки</strong>: <br/>";
тело=тело+"<table class='table'>";
тело=тело+" <thead>";
тело=тело+" <tr>";
тело=тело+" <th scope='col'> Полное имя</th>";
тело=тело+" <th scope='col'> Имя</th>";
тело=тело+" <th scope='col'> Пользователь</th>";
тело=тело+" <th scope='col'> Email</th>";
тело=тело+" <th scope='col'> Дата пароля</th>";
тело=тело+" <th scope='col'> В списке выбора</th>";
тело=тело+" <th scope='col'> Вид запуска</th>";
тело=тело+" <th scope='col'> Роли с доступом в 1С</th>";
тело=тело+" </tr>";
тело=тело+" </thead>";
тело=тело+" <tbody>";
тело=тело+данные;
тело=тело+" </tbody>";
тело=тело+"</table>";
emails=Новый Массив();
emails.Добавить("цкуаука@куауцкацук.ru");
для каждого email из emails цикл
если email<>"" тогда
попытка
ПараметрыПисьма = Новый Структура("Кому, Тема, Тело,ТипТекста", email, "Подозрительные учётки в БД: "+бд, тело,"HTML");
если бд="укму" тогда
УчетнаяЗаписьПочты = РаботаСПочтовымиСообщениями.ПолучитьСистемнуюУчетнуюЗапись();
РаботаСПочтовымиСообщениями.ОтправитьСообщение(УчетнаяЗаписьПочты, ПараметрыПисьма);
иначеесли бд="куепуке" тогда
УчетнаяЗаписьПочты = РаботаСПочтовымиСообщениями.ПолучитьСистемнуюУчетнуюЗапись();
РаботаСПочтовымиСообщениями.ОтправитьСообщение(УчетнаяЗаписьПочты, ПараметрыПисьма);
иначеесли бд="укепуке" тогда
РаботаСПочтовымиСообщениями.ОтправитьПочтовоеСообщение(РаботаСПочтовымиСообщениями.СистемнаяУчетнаяЗапись(),ПараметрыПисьма);
иначеесли бд="куепук" тогда
РаботаСПочтовымиСообщениями.ОтправитьПочтовоеСообщение(РаботаСПочтовымиСообщениями.СистемнаяУчетнаяЗапись(),ПараметрыПисьма);
конецесли;
исключение
ЗаписьЖурналаРегистрации("Ошибка", УровеньЖурналаРегистрации.Ошибка,,ОписаниеОшибки(),);
конецпопытки;
конецесли;
конеццикла;
конецесли;
УстановитьПривилегированныйРежим(ложь);
КонецПроцедуры
Обработкой я перебираю всех пользователей информационной базы, и ищу соответствующего пользователя из справочника «Пользователи». В том случае, если пользователя признаю потенциально не безопасным, то отправляю заинтересованному лицу уведомление на электронную почту.