среда, 21 декабря 2011 г.

Изменение пароля под тонким веб-клиентом

С началом работы пользователей в веб-клиенте появилась нужда изменять пароль пользователя на текущем клиенте, но стандаратных средств нет. Собственно изменить пароль не так и сложно - сложнее идентифицировать пользователя перед выполнением операции. Расшифровывать текущий хеш пароля (sha завернутое в base64) или шифровать его (у меня не получилось) тоже будет не самым оптимальным решением по соотношению затраченное время/полученный результат.

Предлагаю свой вариант алгоритма изменения пароля.

// Если хеш подтверждения пароля совпадает со старым хешем тогда изменяем пароль
ПользовательИнформационнойБазы = ПользователиИнформационнойБазы.ТекущийПользователь();
ХешАвторизации = ПользовательИнформационнойБазы.СохраняемоеЗначениеПароля;

ПользовательИнформационнойБазы.Пароль = Пароль;
ПользовательИнформационнойБазы.Записать();

Пользователь = ПользователиИнформационнойБазы.НайтиПоИмени(ИмяПользователя());
ХешНовогоПароля = Пользователь.СохраняемоеЗначениеПароля;

Если ХешНовогоПароля = ХешАвторизации Тогда
    ПользовательИнформационнойБазы.Пароль = НовыйПароль;
    ПользовательИнформационнойБазы.Записать();
Иначе
    ПользовательИнформационнойБазы.СохраняемоеЗначениеПароля = ХешАвторизации;
    ПользовательИнформационнойБазы.Записать();
КонецЕсли;
 
Скачать обработку 

среда, 5 октября 2011 г.

Пауза в 1С

Столкнулся с проблемою записи табличного документа в pdf файл – виртуальный принтер Bullzip PDF Printer записывает в файл, но при отправлении по почте обработка файл не находит. Оказывается принтер имеет некоторую задержку – нам говорит что файл создан, а на самом деле еще дозаписывает файл. В таких случаях необходимо сделать пазу в коде. Так как 1С своими методами не умеет делать паузу я воспользовался методом Sleep объекта операционной системы Windows Script Host.

Процедура Пауза(ИнтервалОжидания) Экспорт

    Скрипт = Новый ТекстовыйДокумент();
    Скрипт.УстановитьТекст
    (
    "if (WScript.Arguments.Count()==0)
    |    WScript.Quit();
    |else
    |    if (isNaN(parseInt(WScript.Arguments(0))))
    |        WScript.Quit();
    |WScript.Sleep(WScript.Arguments(0));"
    );
    Скрипт.Записать(КаталогВременныхФайлов()+"sleep.js", КодировкаТекста.ANSI);

    WshShell = Новый COMОбъект("WScript.Shell");
    WshShell.Run("WScript.exe """ + КаталогВременныхФайлов() + "sleep.js"" " + Формат(ИнтервалОжидания, "ЧГ=0"), 0, -1);

    УдалитьФайлы(КаталогВременныхФайлов() + "sleep.js");

КонецПроцедуры // Пауза

среда, 28 сентября 2011 г.

Использование СКД вне отчета

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

1. Необходимо получить нужный макет СКД

СхемаКомпоновкиДанных = ПолучитьМакет(ИмяМакетаСКД);

2. Проинициализировать компоновщик настроек.

КомпоновщикНастроек = Новый КомпоновщикНастроекКомпоновкиДанных
    КомпоновщикНастроек.ЗагрузитьНастройки(СхемаКомпоновкиДанных.НастройкиПоУмолчанию);
    ИсточникНастроек = Новый ИсточникДоступныхНастроекКомпоновкиДанных(СхемаКомпоновкиДанных);
    КомпоновщикНастроек.Инициализировать(ИсточникНастроек);

Можно обойтись и без компоновщика, передавая параметры напрямую, но тогда в настройках необходимо их скрыть, и присваивать напрямую
СхемаКомпоновкиДанных.Параметры.Период.Значение = Период

3. Установить параметры компоновщика настроек

Параметр = КомпоновщикНастроек.Настройки.ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("Период"));
    Если Не Параметр = Неопределено Тогда
        Параметр.Использование = Истина;
        Параметр.Значение = ЗначениеПараметра;
    КонецЕсли;

4. Получить макет компоновки

ДанныеРасшифровки = Новый ДанныеРасшифровкиКомпоновкиДанных;
    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    Настройки = КомпоновщикНастроек.Настройки;
    МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);

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

ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки,, ДанныеРасшифровки);

    ЭлементыФормы.ДокументРезультат.Очистить();

    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    ПроцессорВывода.УстановитьДокумент(ЭлементыФормы.ДокументРезультат);

    ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных);

среда, 27 июля 2011 г.

Работа с элементами формы полей отбора СКД

... на самом деле для работы с элементом формы отбора СКД нам нужно всего 3 поля - Использование, ЛевоеЗначение И ПравоеЗначениеДляКраткогоОтображенияЭлемента. 
Когда необходимо назначить назначить свои обработчики проще всего это делать динамически

Процедура ПриОткрытии()

    // Определение типов данных
    // Так как метод Получить() работает только спо индексу элемента необходимо использовать перебор
    Для Каждого ЭлементОтбора Из КомпоновщикНастроек.Настройки.Отбор.Элементы Цикл
        Если Строка(ЭлементОтбора.ЛевоеЗначение) = "Дирекция" Тогда
            Элемент.ОграничениеТипа = Новый ОписаниеТипов("СправочникСсылка.ЗначенияСвойствОбъектов");
            Элемент.Значение = Элемент.ОграничениеТипа.ПривестиЗначение(Неопределено);
        КонецЕсли;
    КонецЦикла;

    // Установление обработчика колонке
    КолонкаПравоеЗначение = ЭлементыФормы.Отбор.Колонки.Найти("ПравоеЗначениеДляКраткогоОтображенияЭлемента");

    ДействиеНачалоВыбора = Новый Действие("ОтборПравоеЗначениеДляКраткогоОтображенияЭлементаНачалоВыбора");
    Колонка.ЭлементУправления.УстановитьДействие("НачалоВыбора", ДействиеНачалоВыбора);

    ДействиеОкончаниеВвода = Новый Действие("ОтборПравоеЗначениеДляКраткогоОтображенияЭлементаОкончаниеВводаТекста")
    Колонка.ЭлементУправления.УстановитьДействие("ОкончаниеВводаТекста", ДействиеОкончаниеВвода);

КонецПроцедуры

Процедура ОтборПравоеЗначениеДляКраткогоОтображенияЭлементаНачалоВыбора(Элемент, СтандартнаяОбработка)

    ПолеКомпоновкиДанных = ЭлементыФормы.Отбор.ТекущаяСтрока.ЛевоеЗначение;

    Если Строка(ПолеКомпоновкиДанных) = "Дирекция" Тогда
        Элемент.ВыборПоВладельцу = ПВХДирекция;
    КонецЕсли;

КонецПроцедуры

Процедура ОтборПравоеЗначениеДляКраткогоОтображенияЭлементаОкончаниеВводаТекста(Элемент, Текст, Значение, СтандартнаяОбработка)

    ПолеКомпоновкиДанных = ЭлементыФормы.Отбор.ТекущаяСтрока.ЛевоеЗначение;
    Если Строка(ПолеКомпоновкиДанных) = "Дирекция" Тогда
        СтандартнаяОбработка = Ложь;
        ОбработкаОкончанияВводаТекста(Элемент, Текст, Значение, СтандартнаяОбработка);
    КонецЕсли;

КонецПроцедуры

Процедура ОбработкаОкончанияВводаТекста(Элемент, Текст, Значение, СтандартнаяОбработка)

    Запрос = Новый Запрос;
    Запрос.Текст =
    "ВЫБРАТЬ
    |   ЗначенияСвойствОбъектов.Ссылка
    |ИЗ
    |   Справочник.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
    |ГДЕ
    |   ЗначенияСвойствОбъектов.Владелец = &Владелец
    |   И ЗначенияСвойствОбъектов.Наименование ПОДОБНО &Наименование + ""%""";

    Запрос.УстановитьПараметр("Владелец", ПВХДирекция);
    Запрос.УстановитьПараметр("Наименование", Текст);

    Результат = Запрос.Выполнить();
    Если НЕ Результат.Пустой() Тогда
        Список = Новый СписокЗначений;
        Список.ЗагрузитьЗначения(Результат.Выгрузить().ВыгрузитьКолонку("Ссылка"));
        ЭлСписка = ЭтаФорма.ВыбратьИзСписка(Список, Элемент);
        Если ЭлСписка <> Неопределено Тогда
            Значение = ЭлСписка.Значение;
        КонецЕсли;
    Иначе
        Элемент.ОграничениеТипа = Новый ОписаниеТипов("СправочникСсылка.ЗначенияСвойствОбъектов");
        Элемент.Значение = Элемент.ОграничениеТипа.ПривестиЗначение(Неопределено);
    КонецЕсли;

КонецПроцедуры

Динамическое добавление полей во внешнем наборе СКД

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

Процедура ВывестиНаборДанных(НаборДанных);

    ВнешниеНаборыДанных = Новый Структура;
    ВнешниеНаборыДанных.Вставить("НаборДанных", НаборДанных);

    СхемаКомпоновкиДанныхДляВывода = ПолучитьМакет("МакетДляВывода");
    Настройки = СхемаКомпоновкиДанныхДляВывода.НастройкиПоУмолчанию;

    // {{ динамическое добавление колонок валютных сумм
    Валюты = НаборДанных.Скопировать();
    Валюты.Свернуть("Валюта", "СуммаУпр");
    Валюты.Сортировать("СуммаУпр Убыв");

    Для Каждого Валюта Из Валюты Цикл
        ИмяПоля         = "Сумма" + Валюта.Валюта.Наименование;
        ЗаголовокПоля   = "Сумма, " + Валюта.Валюта.Наименование;

        // добавить в набор данных
        НоваяКолонка = НаборДанных.Колонки.Добавить(ИмяПоля, ОМОбщегоНазначения.ПолучитьОписаниеТиповЧисла(10, 2));

        // добавить в поля данных
        ПоляДанных = СхемаКомпоновкиДанныхДляВывода.НаборыДанных[0].Поля;
        НовоеПолеДанных = ПоляДанных.Добавить(Тип("ПолеНабораДанныхСхемыКомпоновкиДанных"));
        НовоеПолеДанных.Поле = ИмяПоля;
        НовоеПолеДанных.Заголовок = ЗаголовокПоля;
        НовоеПолеДанных.ТипЗначения = ОМОбщегоНазначения.ПолучитьОписаниеТиповЧисла(10, 2);

        // добавить в ресурсы
        НовоеПолеИтога = СхемаКомпоновкиДанныхДляВывода.ПоляИтога.Добавить();
        НовоеПолеИтога.ПутьКданным = ИмяПоля;
        НовоеПолеИтога.Выражение   = "Сумма(" + ИмяПоля + ")";

        // добавить в настройки
        НовоеПоле = СхемаКомпоновкиДанныхДляВывода.НастройкиПоУмолчанию.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
        НовоеПоле.Поле = Новый ПолеКомпоновкиДанных(ИмяПоля);
        НовоеПоле.Заголовок = ЗаголовокПоля;
        НовоеПоле.Использование = Истина;
    КонецЦикла;

    // переместить ресурсы по порядку
    ЭлементыНастройки = СхемаКомпоновкиДанныхДляВывода.НастройкиПоУмолчанию.Выбор.Элементы;
    Для Каждого ПолеНастройка Из ЭлементыНастройки Цикл
        Если Строка(ПолеНастройка.Поле) = "КатегорияА" Тогда
            ПолеКатегорияА = ПолеНастройка;
        ИначеЕсли Строка(ПолеНастройка.Поле) = "КатегорияВ" Тогда
            ПолеКатегорияВ = ПолеНастройка;
        ИначеЕсли Строка(ПолеНастройка.Поле) = "КатегорияС" Тогда
            ПолеКатегорияС = ПолеНастройка;
        КонецЕсли;
    КонецЦикла;

    ЭлементыНастройки.Сдвинуть(ПолеКатегорияА, ЭлементыНастройки.Количество() - ЭлементыНастройки.Индекс(ПолеКатегорияА));
    ЭлементыНастройки.Сдвинуть(ПолеКатегорияВ, ЭлементыНастройки.Количество() - ЭлементыНастройки.Индекс(ПолеКатегорияВ));
    ЭлементыНастройки.Сдвинуть(ПолеКатегорияС, ЭлементыНастройки.Количество() - ЭлементыНастройки.Индекс(ПолеКатегорияС));

    // заполнить валюты
    Для Каждого Строка Из НаборДанных Цикл
        Строка["Сумма" + Строка.Валюта.Наименование] = Строка.Сумма;
    КонецЦикла;
    // }} динамическое добавление колонок валютных сумм

    ДанныеРасшифровки = Новый ДанныеРасшифровкиКомпоновкиДанных;
    ДанныеРасшифровки.Настройки = Настройки;

    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанныхДляВывода, Настройки, ДанныеРасшифровки);

    ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки);

    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    ПроцессорВывода.УстановитьДокумент(ЭлементыФормы.ДокументРезультат);
    ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных);

КонецПроцедуры

Вывод параметров и результата в разных СКД

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

Процедура СформироватьОтчет()

    НаборДанных = ПолучитьНаборДанных();
    ВывестиНаборДанных(НаборДанных);

КонецПроцедуры
 
Функция ПолучитьНаборДанных()

    // Установка параметров формирования
    УстановитьПараметр("НачалоПериода", НачалоДня(НачалоПериода));
    УстановитьПараметр("КонецПериода", КонецДня(КонецПериода));

    // Вывод результата в таблицу значений
    КомпоновщикНастроек.Настройки.Структура[0].Использование = Истина;

    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;

    НаборДанных = Новый ТаблицаЗначений;
    ПроцессорВывода.УстановитьОбъект(НаборДанных);

    МакетКомпоновкиДанных = КомпоновщикМакета.Выполнить(ОтчетОбъект.СхемаКомпоновкиДанных,
        ЭтотОбъект.КомпоновщикНастроек.Настройки,,,Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));

    ПроцессорКомпоновки.Инициализировать(МакетКомпоновкиДанных);
    ПроцессорВывода.Вывести(ПроцессорКомпоновки);

    // {{ вывод результата в табличный документ
    КомпоновщикНастроек.Настройки.Структура[0].Использование = Ложь; 
    Настройки = ЭтотОбъект.КомпоновщикНастроек.Настройки;

    ДанныеРасшифровки = Новый ДанныеРасшифровкиКомпоновкиДанных;
    МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);

    ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки,, ДанныеРасшифровки);

    ЭлементыФормы.ДокументРезультат.Очистить();

    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    ПроцессорВывода.УстановитьДокумент(ЭлементыФормы.ДокументРезультат);
    ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных);
    // }} вывод результата в табличный документ

    Возврат НаборДанных;

КонецФункции


Процедура ВывестиНаборДанных(НаборДанных);

    ВнешниеНаборыДанных = Новый Структура;
    ВнешниеНаборыДанных.Вставить("НаборДанных", НаборДанных);

    СхемаКомпоновкиДанныхДляВывода = ПолучитьМакет("МакетДляВывода");
    Настройки = СхемаКомпоновкиДанныхДляВывода.НастройкиПоУмолчанию;

    ДанныеРасшифровки = Новый ДанныеРасшифровкиКомпоновкиДанных;
    ДанныеРасшифровки.Настройки = Настройки;

    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанныхДляВывода, Настройки, ДанныеРасшифровки);

    ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки);

    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    ПроцессорВывода.УстановитьДокумент(ЭлементыФормы.ДокументРезультат);
    ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных);

КонецПроцедуры

понедельник, 18 апреля 2011 г.

"Черная книга менеджера". Слава Панкратов.

Рекомендую для прочтения не только руководителям проектов но и рядовым программистам - в книге в доходчивой форме объясняются основные моменты возникновения конфликтных ситуаций, отбрасывается меланхолия, предрассудки и прочие тараканы. Так же, перейдя от часного к общему, содержимое книги можно применить почти в любой из форм трудовых, и не только трудовых отношений. В книге мало букв, но все они концентрированые и много с них матерных, но, говорят что матерится полезно для психики.

Прочтениние займет максимум один час времени, но оно того стоит.
http://www.it4business.ru/mbb

суббота, 16 апреля 2011 г.

1с v8.2 ХранилищеЗначений в Табличной части объекта

В табличных частях объектов 8.2 имеется возможность создавать реквизиты типа ХранилищеЗначений но сохранеие этих реквизитов в тонком клиенте отрабатывается некорректно, разве что каждый раз после присваивания вызывать метод записи объекта Записать(), что не очень то удобно использовать каждый раз при изменении отдельной строки. Для корректной работы с реквизитами такого типа предлагаю сохранять значения в соответствия, которое в свой черед помещается в реквизит формы типа ХранилищеЗначения. Ключем соответствия является идентификатор строки табличной части.

////////////////////////////////////////////////////////////////////////
/////////////////////// Обработчик событий на клиенте

&НаКлиенте
Процедура ПрикрепленныеФайлыПередУдалением(Элемент, Отказ)
    ТекущаяСтрока = Элементы.ПрикрепленныеФайлы.ТекущаяСтрока;
    ДанныеСтроки = Элементы.ПрикрепленныеФайлы.ДанныеСтроки(ТекущаяСтрока);

    УдалитьДанныеИзСоответствия(ДанныеСтроки.НомерСтроки);
КонецПроцедуры

////////////////////////////////////////////////////////////////////////
/////////////////////// Обработчик событий на сервере

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    Хранилище = Новый ХранилищеЗначения(Новый Соответствие);

    Индекс = -1;
    Для Каждого Строка Из Объект.ПрикрепленныеФайлы Цикл
        Индекс = Макс(Индекс, Строка.ПолучитьИдентификатор());
    КонецЦикла;
КонецПроцедуры

&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
    Соответствие = Хранилище.Получить();

    Для Каждого Строка Из Объект.ПрикрепленныеФайлы Цикл
        ИндексТекущейСтроки = Строка.ПолучитьИдентификатор();
        Если Не Соответствие[ИндексТекущейСтроки] = Неопределено Тогда
            // Записать файл
            ТекущийОбъект.ПрикрепленныеФайлы[Строка.НомерСтроки-1].Файл = Соответствие[ИндексТекущейСтроки];
            // Удаление соответствия
            Соответствие.Удалить(ИндексТекущейСтроки);
        КонецЕсли;
    КонецЦикла;

    Хранилище = Новый ХранилищеЗначения(Соответствие);
КонецПроцедуры

////////////////////////////////////////////////////////////////////////
/////////////////////// Серверные процедуры и функции

&НаСервере
Процедура ПоместитьФайлыИзВременногоХранилищаВТЗ(МассивФайлов)
    СправочникОбъект = РеквизитФормыВЗначение("Объект");
    // ... тело модуля
    Индекс = Индекс + 1;
    НоваяСтрокаВложения = СправочникОбъект.ПрикрепленныеФайлы.Добавить();

    Соответствие = Хранилище.Получить();
    Соответствие.Вставить(Индекс, Новый ХранилищеЗначения(Файл));
    Хранилище = Новый ХранилищеЗначения(Соответствие);

    ЗначениеВРеквизитФормы(СправочникОбъект, "Объект");
КонецПроцедуры

&НаСервере
Процедура УдалитьДанныеИзСоответствия(ТекущаяСтрока);
    ИндексТекущейСтроки = Объект.ПрикрепленныеФайлы[ТекущаяСтрока-1].ПолучитьИдентификатор();

    Соответствие = Хранилище.Получить();
    Соответствие.Удалить(ИндексТекущейСтроки);
    Хранилище = Новый ХранилищеЗначения(Соответствие);
КонецПроцедуры

пятница, 8 апреля 2011 г.

Ограничение доступности параметра СКД

 В запросах СКД часто встречаются конструкции типа "... Когда &Параметр = Значение(Справочник.ИмяСправочника.ПустаяСсылка)..." при выводе параметров не совсем красиво отображать пустое значение, а когда запретить вывод параметра поставив ограничение доступности не будут отображатся заполненые значения. Решением есть устанавливать ограничение доступности в СКД при компоновке в зависимости от заполнения параметра пользовательских настроек.

Настройки = ЭтотОбъект.КомпоновщикНастроек.Настройки;
Параметр = Настройки.ПараметрыДанных.Элементы.Найти(ИмяПараметра);
ПараметрСКД = ЭтотОбъект.СхемаКомпоновкиДанных.Параметры.Найти(ИмяПараметра);
Если Не (Параметр = Неопределено Или ПараметрСКД = Неопределено) Тогда
    ПараметрСКД.ОграничениеИспользования = Не ЗначениеЗаполнено(Параметр.Значение)
КонецЕсли;

Дата рождения - 08 апреля

В блоге буду публиковатся интересные методы решения задач и описание нестандартного поведения платформы :)