Подбор квалифицированного разработчика 1С уровня Middle — задача непростая. Часто работодатели предлагают кандидатам многочасовые тестовые задания, которые могут вызывать отторжение у востребованных специалистов. В рамках данной статьи мы подробно разберем практические задачи из реального тестового задания, проанализируем варианты их технического решения, а также рассмотрим альтернативный вариант оценки квалификации программиста с помощью глубоких устных вопросов.
Оцениваемое тестовое задание состоит из двух связанных технологических блоков, которые кандидат должен выполнить в среде демо-конфигурации (например, на базе Библиотеки стандартных подсистем — БСП):
Булево) для возможности множественного выбора строк пользователем. Главное условие — запрещено вносить изменения в метаданные конфигурации (нельзя добавлять реквизиты к документам или создавать вспомогательные регистры сведений).Давайте подробно проанализируем, как решить обе задачи оптимальными способами, не тратя на это лишнего времени.
---
Поскольку физически хранить признак «выбран» в базе данных нам запрещено, мы должны организовать его временное хранение на клиенте в оперативной памяти формы. Для этого отлично подойдет коллекция типа Соответствие или Массив, где ключами будут выступать ссылки на документы (или их уникальные идентификаторы), а значениями — признак выбора.
Разберем пошагово алгоритм реализации этой задачи на управляемой форме:
Шаг 1. Модификация запроса динамического списка. Нам необходимо добавить в схему компоновки данных динамического списка искусственное поле, которое изначально будет принимать значение Ложь. Откроем запрос динамического списка и добавим поле выбора:
ВЫБРАТЬ
ДокументРеализация.Ссылка КАК Ссылка,
ДокументРеализация.Номер КАК Номер,
ДокументРеализация.Дата КАК Дата,
ЛОЖЬ КАК Выбран
ИЗ
Документ.РеализацияТоваровУслуг КАК ДокументРеализация
Шаг 2. Создание коллекции для хранения состояния на форме. Добавим в реквизиты формы новый элемент ВыбранныеСтроки с типом Соответствие (или Массив). Этот реквизит будет хранить список реально отмеченных пользователем документов в течение сеанса работы с формой.
Шаг 3. Динамическое отображение флажков через Условное оформление. Для того чтобы при прокрутке списка отмеченные флажки не визуализировались хаотично и не сбрасывались, мы настроим условное оформление формы. В процедуре ПриСозданииНаСервере пропишем код, который будет связывать значение нашей коллекции с отображением флажка на форме:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Инициализируем структуру хранения отмеченных документов
ВыбранныеСтроки = Новый Соответствие;
// Программная настройка условного оформления для отображения флажка
ЭлементОформления = УсловноеОформление.Элементы.Добавить();
ПолеОформления = ЭлементОформления.Поля.Элементы.Добавить();
ПолеОформления.Поле = Новый ПолеКомпоновкиДанных("СписокВыбран");
// Условие: если ссылка находится в нашей коллекции отмеченных элементов
ЭлементОтбора = ЭлементОформления.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Ссылка");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.ВСписке;
// Значение отбора будем динамически обновлять при каждом клике пользователя
ЭлементОтбора.ПравоеЗначение = ПолучитьМассивВыбранныхСсылок();
ЭлементОформления.Оформление.УстановитьЗначениеПараметра("Текст", "✔");
КонецПроцедуры
Шаг 4. Обработка клика по флажку. Добавим обработчик события Выбор или ПриИзменении для поля флажка в таблице динамического списка. При нажатии на ячейку «Выбран» мы должны инвертировать состояние элемента в нашей коллекции и обновить условное оформление списка:
&НаКлиенте
Процедура СписокВыбор(Элемент, ВыбраннаяСтрока, Поле, СтандартнаяОбработка)
Если Поле.Имя = "СписокВыбран" Тогда
СтандартнаяОбработка = Ложь;
ПереключитьПометкуВыбора(ВыбраннаяСтрока);
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура ПереключитьПометкуВыбора(Знач СсылкаНаОбъект)
Если ВыбранныеСтроки.Получить(СсылкаНаОбъект) = Неопределено Тогда
ВыбранныеСтроки.Вставить(СсылкаНаОбъект, Истина);
Иначе
ВыбранныеСтроки.Удалить(СсылкаНаОбъект);
КонецЕсли;
// Обновляем параметры условного оформления
ОбновитьУсловноеОформлениеФормы();
КонецПроцедуры
Использование этого подхода позволяет обойтись без создания дополнительных объектов метаданных, сохраняя высокую скорость работы динамического списка и корректное поведение интерфейса при позиционировании и обновлении данных.
---
Вторая часть тестового задания требует от кандидата умения работать с механизмами интеграции и XML-структурами. Использование полноценной конфигурации «Конвертация данных 3.0» (КД3) для разовой локальной задачи избыточно. Опытный разработчик решит эту задачу напрямую через работу со схемами XDTO пакета EnterpriseData.
Рассмотрим, как правильно реализовать программную генерацию XML по схеме ED:
Шаг 1. Получение XSD-схемы формата. Для работы нам потребуется файл схемы нужной версии формата EnterpriseData (например, EnterpriseData_1_6_max.xsd). Его можно получить, выгрузив схему из любого существующего в типовом решении XDTO-пакета, поддерживающего нужную интеграцию, либо загрузить из каталога шаблонов ИТС.
Шаг 2. Инициализация Фабрики XDTO и Записи XML. Напишем процедуру, которая будет обходить выбранные на первом шаге документы, собирать их реквизиты, сериализовать в объекты XDTO по правилам схемы EnterpriseData и записывать в результирующий XML-файл:
&НаСервере
Процедура ВыгрузитьДокументыВФорматеEnterpriseData(МассивСсылок)
// 1. Создаем объект записи XML
ЗаписьXML = Новый ЗаписьXML;
// Формируем динамическое имя файла на основе контрагента первого документа
КонтрагентИмя = "";
Если МассивСсылок.Количество() > 0 Тогда
КонтрагентИмя = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(МассивСсылок[0], "Контрагент.Наименование");
КонтрагентИмя = ОбщегоНазначенияКлиентСервер.ЗаменитьНедопустимыеСимволыВИмениФайла(КонтрагентИмя, "_");
КонецЕсли;
ИмяФайла = ПолучитьИмяВременногоФайла("xml");
ЗаписьXML.ОткрытьФайл(ИмяФайла, "UTF-8");
// Пишем стандартный заголовок XML-документа
ЗаписьXML.ЗаписатьОбъявлениеXML();
// 2. Инициализируем фабрику XDTO на основе схемы EnterpriseData
// В типовых конфигурациях фабрика доступна через глобальный контекст ФабрикаXDTO
ПространствоИмен = "http://v8.1c.ru/edi/edi_st/EnterpriseData/1.6";
ЗаписьXML.ЗаписатьНачалоЭлемента("Message", ПространствоИмен);
ЗаписьXML.ЗаписатьСоответствиеПространствИмен("xs", "http://www.w3.org/2001/XMLSchema");
ЗаписьXML.ЗаписатьСоответствиеПространствИмен("xsi", "http://www.w3.org/2001/XMLSchema-instance");
// Записываем секцию заголовка сообщения (Header)
ТипHeader = ФабрикаXDTO.Тип(ПространствоИмен, "HeaderType");
ОбъектHeader = ФабрикаXDTO.Создать(ТипHeader);
ОбъектHeader.FormatVersion = "1.6";
ОбъектHeader.CreationDate = ТекущаяДатаСеанса();
ФабрикаXDTO.ЗаписатьXML(ЗаписьXML, ОбъектHeader, "Header", ПространствоИмен);
// Записываем тело сообщения (Body)
ЗаписьXML.ЗаписатьНачалоЭлемента("Body", ПространствоИмен);
Для Каждого СсылкаДок Из МассивСсылок Цикл
ВыгрузитьДокументРеализацииXDTO(ЗаписьXML, СсылкаДок, ПространствоИмен);
КонецЦикла;
ЗаписьXML.ЗаписатьКонецЭлемента(); // Body
ЗаписьXML.ЗаписатьКонецЭлемента(); // Message
ЗаписьXML.Закрыть();
// Передаем файл пользователю или сохраняем на диск
...
КонецПроцедуры
Шаг 3. Заполнение структуры документа по схеме ED. Вспомогательная процедура сериализует конкретный документ в структуру XDTO:
&НаСервере
Процедура ВыгрузитьДокументРеализацииXDTO(ЗаписьXML, СсылкаДок, ПространствоИмен)
ДанныеДокумента = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(СсылкаДок, "Номер, Дата, Контрагент, Организация, СуммаДокумента");
ТипДокумента = ФабрикаXDTO.Тип(ПространствоИмен, "Document.SalesInvoice");
ОбъектДокумента = ФабрикаXDTO.Создать(ТипДокумента);
ОбъектДокумента.Number = ДанныеДокумента.Номер;
ОбъектДокумента.Date = ДанныеДокумента.Дата;
// Заполняем структуру Контрагента (справочник) внутри документа
ТипКонтрагента = ФабрикаXDTO.Тип(ПространствоИмен, "Catalog.Counterparties");
ОбъектКонтрагента = ФабрикаXDTO.Создать(ТипКонтрагента);
ОбъектКонтрагента.Description = Строка(ДанныеДокумента.Contragent);
// Добавляем ИНН/КПП контрагента для прохождения валидации формата
ДанныеКонтрагента = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(ДанныеДокумента.Контрагент, "ИНН, КПП");
ОбъектКонтрагента.INN = ДанныеКонтрагента.ИНН;
ОбъектКонтрагента.KPP = ДанныеКонтрагента.КПП;
ОбъектДокумента.Counterparty = ОбъектКонтрагента;
// Записываем сформированный XDTO-объект документа в XML-поток
ФабрикаXDTO.ЗаписатьXML(ЗаписьXML, ОбъектДокумента, "Document.SalesInvoice", ПространствоИмен);
КонецПроцедуры
Этот способ позволяет кандидату наглядно продемонстрировать глубокое знание XML, механизмов сериализации 1С и работы с пространством имен XDTO без необходимости настраивать сложные правила синхронизации.
---
Многие ведущие специалисты сходятся во мнении, что 4-часовые тестовые задания дома часто заставляют сильных кандидатов отказываться от вакансии на раннем этапе. Качественная альтернатива — устное техническое интервью продолжительностью 20-30 минут с разбором ключевых тем.
Проанализируем подборку из 7 практических вопросов, которые гарантированно покажут реальный уровень знаний Middle-разработчика 1С:
Попросите кандидата проанализировать следующий код и ответить, какую задачу он решает:
Функция ПроверитьНаличиеСвойства(Объект, ИмяСвойства) Экспорт
УникальныйИдентификатор = Новый УникальныйИдентификатор;
Структура = Новый Структура(ИмяСвойства, УникальныйИдентификатор);
ЗаполнитьЗначенияСвойств(Структура, Объект);
Возврат НЕ Структура[ИмяСвойства] = УникальныйИдентификатор;
КонецФункции
Правильный ответ: Данная функция универсально проверяет, содержит ли переданный Объект (который может быть Структурой, СтрокойТаблицыЗначений, ОбъектомБД и т.д.) свойство с именем ИмяСвойства. Метод основан на заполнении структуры с заранее известным уникальным значением-маркером. Если свойство у объекта существует, метод ЗаполнитьЗначенияСвойств перезапишет маркер реальным значением свойства (которое гарантированно не совпадет со вновь созданным УникальнымИдентификатором). Если свойства у объекта нет, значение останется прежним. Это классическая функция БСП, демонстрирующая умение читать системный оптимизированный код.
Правильный ответ: Использование полей составного типа в секциях ПО при соединении таблиц или в условиях ГДЕ заставляет СУБД выполнять неявное приведение типов. На уровне SQL-запроса это приводит к созданию огромного количества соединений типа LEFT OUTER JOIN со всеми таблицами, входящими в составной тип. Результат: резкое падение производительности запроса, невозможность использования индексов и избыточная нагрузка на TempDB сервера СУБД.
Правильный ответ: Обращение вида СсылкаНаОбъект.Реквизит в коде на сервере заставляет платформу полностью считать объект базы данных из СУБД и кэшировать его в оперативной памяти (включая все его табличные части и тяжелые реквизиты). Если обращение происходит в цикле или к большим объектам, это приводит к деградации производительности. Избежать этого можно двумя способами:
ОбщегоНазначения.ЗначениеРеквизитаОбъекта() или ЗначенияРеквизитовОбъекта(), которые под капотом оптимально кэшируют запросы к СУБД.
Процедура ЗаполнитьТаблицуПоЦенам(ДатаЦен, Склад, ТаблицаЦен)
Запрос = Новый Запрос;
Запрос.УстановитьПараметр("Склад", Склад);
Запрос.УстановитьПараметр("ТекДата", ДатаЦен);
Запрос.Текст =
"ВЫБРАТЬ
| ЦеныСрезПоследних.Цена
|ИЗ
| РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&ТекДата, Номенклатура = &Номенклатура И Склад = &Склад) КАК ЦеныСрезПоследних";
Для Каждого ТекТовар Из ТаблицаЦен Цикл
Запрос.УстановитьПараметр("Номенклатура", ТекТовар.Номенклатура);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
ТекТовар.Цена = Выборка.Price;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Правильный ответ: В представленном фрагменте кода допущена классическая критическая ошибка — запрос выполняется в цикле. Для оптимизации необходимо передать всю таблицу ТаблицаЦен в качестве параметра запроса (предварительно типизировав ее колонки и поместив во временную таблицу), а затем связать ее в одном запросе с виртуальной таблицей СрезПоследних регистра сведений. Запрос выполнится на сервере ровно один раз.
Как определить в обработчиках событий ПриЗаписи или ОбработкаПроведения, был ли документ только что создан (это его первая запись), был ли он проведен или помечен на удаление до начала транзакции?
Правильный ответ: На этапе выполнения обработчика ПриЗаписи данные в базе уже изменены, поэтому прямое сравнение свойств объекта со свойствами его ссылки не покажет старое состояние. Чтобы обойти это ограничение, состояние объекта фиксируют на самом раннем этапе — в обработчике ПередЗаписью (где объект базы данных еще содержит старые значения), и передают эти флаги в транзакцию через свойство ДополнительныеСвойства объекта:
// В модуле документа
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Если ЭтоНовый() Тогда
ДополнительныеСвойства.Вставить("ЭтоНовыйДокумент", Истина);
КонецЕсли;
КонецПроцедуры
Процедура ПриЗаписи(Отказ)
Если ДополнительныеСвойства.Свойство("ЭтоНовыйДокумент") Тогда
// Выполняем логику первого сохранения
КонецЕсли;
КонецПроцедуры
Правильный ответ: Прямой поиск элементов в коде по коду или наименованию считается грубым нарушением стандартов разработки, так как пользователи могут легко изменить наименование или код элемента в режиме предприятия. Чтобы этого избежать, следует использовать:
Справочники.Валюты.Рубль).Правильный ответ: Фигурные скобки {} в текстах запросов СКД описывают секции необязательных полей компоновки (для выбора, отбора, группировки или порядка). Конструкции внутри фигурных скобок обрабатываются процессом компоновки данных динамически: если пользователь не задействовал соответствующее поле в настройках отчета, СКД вырежет эту часть из итогового SQL-запроса, что существенно снизит нагрузку на сервер СУБД.
---
Для качественной оценки кандидата на позицию Middle-разработчика 1С не обязательно заставлять его писать объемные бесплатные приложения. Тестовое задание с реализацией интерактивного флажка без метаданных и экспортом в EnterpriseData является отличным техническим тестом, но выполнять его лучше в рамках совместного live-coding сеанса или предлагать в качестве оплачиваемой мини-задачи. Альтернативный устный разбор 7 ключевых вопросов на понимание платформенных механизмов позволит вам за 20 минут оценить реальный опыт и архитектурное мышление специалиста, сохранив его лояльность к компании.