В практике разработчика 1С часто возникает задача предварительного расчета заработной платы или формирования прогнозных отчетов по ФОТ (фонду оплаты труда). Особенность таких задач заключается в том, что нам не нужно создавать реальные записи в регистре расчета ОсновныеНачисления или ДополнительныеНачисления. Нам требуется получить результат «на лету», используя штатные формулы видов расчета, но подставляя в них свои значения показателей (например, среднегодовую норму времени вместо фактической).
В этой статье мы подробно разберем, как реализовать такой механизм в конфигурации «1С:Зарплата и управление персоналом 3.1», рассмотрим несколько подходов — от простого парсинга формул до использования мощного внутреннего движка системы.
Проанализируем самый гибкий способ, который позволяет использовать уже настроенные в системе формулы. В 1С:ЗУП 3.1 формулы хранятся в строковом виде в справочнике Начисления. Мы можем извлечь эту строку, подготовить структуру показателей и вычислить результат программно.
Рассмотрим пошагово, как это реализовать:
Начисления и получить значение реквизита Формула.Оклад, ВремяВЧасах), а значениями — наши планируемые цифры.ОбщегоНазначения.ВычислитьВБезопасномРежиме.Посмотрим на пример кода, реализующего этот подход:
// Определим начисление и сотрудника
НачислениеСсылка = Справочники.Начисления.НайтиПоНаименованию("Оклад");
Формула = НачислениеСсылка.Формула;
// Подготовим структуру показателей (наши планируемые значения)
ИсходныеДанные = Новый Структура;
ИсходныеДанные.Вставить("Оклад", 50000);
ИсходныеДанные.Вставить("ВремяВЧасах", 160); // Наша среднегодовая норма
ИсходныеДанные.Вставить("НормаВремениВЧасах", 160);
// Важный нюанс: если в формуле используются системные префиксы,
// их нужно заменить для корректной работы парсера 1С
ФормулаДляРасчета = СтрЗаменить(Формула, "ИсходныеДанные.", "Параметры.");
Попытка
// Вычисляем результат, передавая структуру данных
Результат = ОбщегоНазначения.ВычислитьВБезопасномРежиме(ФормулаДляРасчета, ИсходныеДанные);
Сообщить("Рассчитанная сумма: " + Результат);
Исключение
Инфо = ИнформацияОбОшибке();
Сообщить("Ошибка при расчете: " + Инфо.Описание);
КонецПопытки;
Преимущества метода: Мы не зависим от регистра расчета и можем подставлять абсолютно любые значения «от балды», что идеально подходит для планирования «что если».
Важные ограничения: Этот метод плохо справляется со сложными функциями ЗУП, такими как ОценитьПоШкале или специфическими запросами к стажу, так как ВычислитьВБезопасномРежиме — это чистый интерпретатор выражений 1С, он не знает контекста предметной области зарплаты.
Если нам нужно рассчитать сложные начисления, которые зависят от стажа, графиков или имеют сложные логические условия в формулах, лучше использовать механизм МенеджерРасчетаЗарплаты. Этот объект является «сердцем» ЗУП и умеет выполнять расчеты максимально корректно.
Основная хитрость здесь заключается в том, что менеджер может работать с наборами данных, которые мы загрузим в него программно, не записывая их в базу данных. Мы создаем временную таблицу в памяти, имитирующую записи регистра расчета, и просим систему их «пересчитать».
Разберем основные шаги работы с менеджером:
МенеджерРасчетаЗарплаты.Рассчитать().Этот способ сложнее в реализации, так как требует заполнения множества служебных полей, но он гарантирует, что результат будет копейка в копейку совпадать с тем, что выдал бы документ «Начисление зарплаты и взносов».
Проанализируем ситуацию, когда нам нужно рассчитать плановую зарплату сразу по 10 000 сотрудников. Программный перебор в цикле через ВычислитьВБезопасномРежиме будет работать медленно. В этом случае на помощь приходят запросы.
Если начисления первичные (например, оклады, надбавки фиксированной суммой или процентом), мы можем реализовать логику формулы прямо в тексте запроса. Для планирования по среднегодовой норме мы можем заранее рассчитать эту норму и передать её в запрос как параметр.
Рассмотрим структуру такого запроса:
ВЫБРАТЬ
Сотрудники.Ссылка КАК Сотрудник,
&СреднегодоваяНорма КАК Норма,
&СреднегодоваяНорма КАК Отработано,
ЗначенияПоказателей.Значение КАК Оклад,
(ЗначенияПоказателей.Значение / &СреднегодоваяНорма) * &СреднегодоваяНорма КАК Результат
ИЗ
Справочник.Сотрудники КАК Сотрудники
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЗначенияПериодическихПоказателейРасчетаЗарплатыСрезПоследних(&Период, ) КАК ЗначенияПоказателей
ПО Сотрудники.Ссылка = ЗначенияПоказателей.Сотрудник
ГДЕ
Сотрудники.ВАрхиве = ЛОЖЬ
Выясним причину, почему это работает быстро: 1С на уровне SQL-сервера выполняет математические операции над выборкой, что в десятки раз быстрее любого объектного метода. Однако помните, что при таком подходе вам придется вручную «дублировать» логику формул из конфигурации в ваш код/запрос. Если расчетчик изменит формулу в пользовательском режиме, ваш отчет об этом не узнает.
При реализации расчета «вне регистра» мы должны учитывать несколько критических моментов, которые часто упускают из виду:
ВремяВЧасах и НормаВремениВЧасах — это предопределенные сущности. При программном расчете через парсер формул (Метод 1) обязательно убедитесь, что вы передали эти значения в структуру ИсходныеДанные, иначе формула вернет 0 из-за деления на неопределенное значение.ВычислитьВБезопасномРежиме предпочтительнее, чем Выполнить(), так как оно предотвращает выполнение потенциально опасного кода, который пользователь мог случайно или намеренно вписать в поле формулы.Подведем итог: Для быстрого и разового расчета небольшого количества сотрудников оптимально использовать подмену параметров и ОбщегоНазначения.ВычислитьВБезопасномРежиме. Если же задача стоит масштабно (бюджетирование предприятия на год), стоит смотреть в сторону МенеджераРасчетаЗарплаты или оптимизированных запросов с предварительной подготовкой данных в таблицах значений.