В процессе разработки конфигураций на платформе 1С:Предприятие 8.3 часто возникает задача автоматизации заполнения данных. Типичный сценарий: при выборе сотрудника в документе начисления зарплаты система должна сама рассчитать его премию, опираясь на показатели эффективности (например, объем продаж), зафиксированные в системе. В данной статье мы подробно разберем, как реализовать механизм, который при выборе сотрудника в табличной части проверяет его должность и, если он является баристой, высчитывает премию в размере 10% от суммы его личных продаж из регистра накопления.
Для начала проанализируем структуру метаданных, с которыми нам предстоит работать. У нас есть документ НачисленияСотрудникам с табличной частью Начисления. В этой табличной части присутствуют реквизиты: Сотрудник, Должность, Оклад и Премия. Информацию о продажах мы будем брать из оборотного регистра накопления Продажи, где измерением является Бариста (ссылка на справочник сотрудников), а ресурсом — Стоимость.
Основная логика должна срабатывать в момент интерактивного изменения реквизита Сотрудник в строке табличной части. Нам необходимо выполнить следующие действия:
Стоимость для данного сотрудника за определенный период.Премия.Разберем, где именно должен располагаться наш код. Поскольку мы работаем в управляемом приложении, нам важно соблюдать разделение контекста на клиентский и серверный. Основное событие, которое нас интересует, — это СотрудникПриИзменении. Оно создается для элемента формы, связанного с реквизитом табличной части.
Проанализируем пример кода для клиентской части. Здесь мы получаем текущую строку табличной части и вызываем серверную функцию, которая выполнит расчет. Использование серверной функции без контекста (&НаСервереБезКонтекста) является правилом хорошего тона, так как это ускоряет работу системы за счет передачи минимального объема данных.
&НаКлиенте
Процедура НачисленияСотрудникПриИзменении(Элемент)
СтрокаТЧ = Элементы.Начисления.ТекущиеДанные;
Если СтрокаТЧ <> Неопределено Тогда
// Вызываем функцию расчета на сервере
РезультатРасчета = РассчитатьПремиюНаСервере(СтрокаТЧ.Сотрудник, СтрокаТЧ.Должность);
СтрокаТЧ.Премия = РезультатРасчета;
КонецЕсли;
КонецПроцедуры
Теперь перейдем к самому ответственному этапу — получению данных из базы. Для этого мы будем использовать механизм запросов к виртуальным таблицам. Посмотрим на типичную ошибку начинающих разработчиков: попытку наложить отбор на результат запроса через условие ГДЕ, используя ссылку на сам документ. В случае с виртуальными таблицами оборотов это не только избыточно, но и может привести к получению пустой выборки.
Выясним причину, по которой запрос может возвращать пустой результат. Правильный подход заключается в передаче параметров отбора непосредственно в параметры виртуальной таблицы Обороты. Это позволяет платформе оптимизировать запрос на уровне СУБД.
Рассмотрим структуру запроса подробнее:
&НаСервереБезКонтекста
Функция РассчитатьПремиюНаСервере(Знач Сотрудник, Знач ДолжностьСотрудника)
// Проверяем должность. В реальной базе лучше использовать предопределенные
// значения или ссылки, а не сравнение строк.
Если СокрЛП(ДолжностьСотрудника) <> "Бариста" Тогда
Возврат 0;
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЕСТЬNULL(ПродажиОбороты.СтоимостьОборот, 0) КАК СтоимостьОборот
|ИЗ
| РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, , Бариста = &Бариста) КАК ПродажиОбороты";
// Устанавливаем границы периода (например, текущий месяц)
Запрос.УстановитьПараметр("НачалоПериода", НачалоМесяца(ТекущаяДата()));
Запрос.УстановитьПараметр("КонецПериода", КонецМесяца(ТекущаяДата()));
Запрос.УстановитьПараметр("Бариста", Сотрудник);
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
// Рассчитываем 10% от продаж
Возврат Выборка.СтоимостьОборот * 0.1;
КонецЕсли;
Возврат 0;
КонецФункции
Проанализируем ключевые моменты в приведенном коде. Во-первых, использование функции ЕСТЬNULL в запросе крайне важно. Если по сотруднику не было движений в регистре, виртуальная таблица может не вернуть строку, или значение ресурса будет NULL. Обработка таких случаев предотвращает ошибки при попытке выполнения арифметических операций.
Во-вторых, обратите внимание на параметры виртуальной таблицы Обороты. Мы передаем туда НачалоПериода и КонецПериода. Если этого не сделать, запрос соберет данные за все время существования базы, что вряд ли корректно для ежемесячного начисления зарплаты. Обычно период берется из даты самого документа.
Особенности работы с должностями: в нашем примере мы передаем Должность напрямую из строки ТЧ. Однако, если должность в справочнике Сотрудники является реквизитом, лучше получать её программно внутри серверной функции, чтобы исключить влияние человеческого фактора (если пользователь вручную изменил должность в строке, но не в карточке сотрудника).
Для простых задач 1С предоставляет объектный метод доступа к данным. Рассмотрим, как получить те же данные без написания текста запроса вручную, используя метод Обороты() менеджера регистра накопления.
// Пример использования объектного метода
Отбор = Новый Структура("Бариста", Сотрудник);
ТаблицаОборотов = РегистрыНакопления.Продажи.Обороты(НачалоМесяца, КонецМесяца, Отбор);
Если ТаблицаОборотов.Количество() > 0 Тогда
СуммаПродаж = ТаблицаОборотов[0].Стоимость;
Премия = СуммаПродаж * 0.1;
КонецЕсли;
Хотя этот способ кажется более коротким, профессиональные разработчики предпочитают Запрос. Он позволяет более гибко настраивать условия, объединять данные из нескольких таблиц и работает быстрее на больших массивах данных за счет того, что СУБД получает четкую инструкцию по выборке только необходимых полей.
При написании дипломных работ или реальных проектов стоит помнить о ссылочной целостности. Сравнение Должность.Наименование = "Бариста" считается плохой практикой ("магические строки"). Правильнее будет создать в конфигурации предопределенный элемент в справочнике должностей или использовать проверку через ссылку:
Если ДолжностьСотрудника = Справочники.Должности.Бариста Тогда...
Также не забывайте про округление. При расчете процентов могут возникнуть длинные дробные части. Используйте функцию Окр(Значение, 2), чтобы сумма премии соответствовала денежному формату.
Разберем ситуацию, когда данных в регистре нет. Если запрос вернул пустой результат, важно явно вернуть 0, иначе в поле Премия может остаться старое значение от предыдущего сотрудника, что приведет к финансовым ошибкам в учете. В нашем примере эта ситуация обработана возвратом значения по умолчанию в конце функции.
Таким образом, мы изучили комплексный подход к автоматической подстановке данных. Сочетание событийных обработчиков формы и эффективных запросов к виртуальным таблицам позволяет создавать отзывчивый и надежный интерфейс для пользователей системы 1С.