Как включить начисление Подарок в базу для расчета Премии в 1С: Enterprise 8.3

Программист 1С v8.3 (Управляемые формы) Управленческий учет IT и автоматизация бизнеса
← На главную

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

Постановка задачи и анализ структуры

Рассмотрим ситуацию: у нас есть основной вид расчета Оклад и два дополнительных начисления — Подарок и Премия. Наша цель — реализовать формулу: Премия = (Оклад + Подарок) * Процент / 100. Все три начисления вводятся одним документом. Для решения этой задачи нам необходимо работать с объектами ПланыВидовРасчета и РегистрыРасчета.

Первым делом проанализируем настройки метаданных. Чтобы система понимала связь между начислениями, в Плане видов расчета для Премии необходимо указать Оклад и Подарок в списке базовых видов расчета. Без этой настройки виртуальные таблицы базы просто не будут «собирать» суммы по этим начислениям.

Этап 1: Первичная запись движений (Оклад и Подарок)

Разберем по шагам логику проведения документа. Основная ошибка новичков заключается в попытке рассчитать все начисления в одном цикле. Выясним причину: виртуальная таблица базы строится на основании уже существующих записей в регистре. Следовательно, мы должны сначала рассчитать и записать те начисления, которые сами не зависят от базы (Оклад и Подарок), и только потом приступать к расчету Премии.

Рассмотрим пример кода для первой фазы записи:


// Устанавливаем флаг записи для наборов
Движения.ОсновныеНачисления.Записывать = Истина;
Движения.ДополнительныеНачисления.Записывать = Истина;

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

// 2. Формируем движения по Подарку (Дополнительные начисления)
Для Каждого ТекСтрокаДоп Из НачисленияДоп Цикл
    Если ТекСтрокаДоп.ВидНачисления = ПланыВидовРасчета.ДополнительныеНачисления.Подарок Тогда
        Движение = Движения.ДополнительныеНачисления.Добавить();
        ЗаполнитьЗначенияСвойств(Движение, ТекСтрокаДоп);
        Движение.ВидРасчета = ТекСтрокаДоп.ВидНачисления;
        Движение.ПериодРегистрации = НачалоМесяца(Дата);
        // Подарок вводится фиксированной суммой в колонку Размер
        Движение.Результат = ТекСтрокаДоп.Размер; 
    КонецЕсли;
КонецЦикла;

// ВАЖНО: Физически записываем движения в базу данных, чтобы виртуальные таблицы их увидели
Движения.ОсновныеНачисления.Записать();
Движения.ДополнительныеНачисления.Записать();

Этап 2: Проблема периодов регистрации и действия

Проанализируем ситуацию с периодами. Для дополнительных начислений крайне важен ПериодРегистрации. Если мы хотим, чтобы Премия за текущий месяц увидела Подарок за текущий месяц, их периоды регистрации должны совпадать (или подарок должен быть зарегистрирован раньше).

Также обратите внимание на БазовыйПериод. Для Премии необходимо четко задать границы БазовыйПериодНачало и БазовыйПериодКонец. Если премия рассчитывается за прошлый месяц, то и подарок должен быть в прошлом месяце. Если же расчет идет «месяц в месяц», установите границы на начало и конец текущего месяца регистрации.


// Пример настройки периодов для Премии
ДвижениеПремия = Движения.ДополнительныеНачисления.Добавить();
ДвижениеПремия.ВидРасчета = ПланыВидовРасчета.ДополнительныеНачисления.Премия;
ДвижениеПремия.ПериодРегистрации = НачалоМесяца(Дата);
// Устанавливаем базовый период равным текущему месяцу
ДвижениеПремия.БазовыйПериодНачало = НачалоМесяца(Дата);
ДвижениеПремия.БазовыйПериодКонец = КонецМесяца(Дата);

Этап 3: Расчет Премии через виртуальные таблицы

Теперь, когда базовые начисления записаны в регистр, мы можем использовать запрос к виртуальным таблицам БазаОсновныеНачисления и БазаДополнительныеНачисления. Посмотрим на пример запроса, который собирает данные по НомеруСтроки для корректной привязки к движениям документа.


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|	ДопНачисления.НомерСтроки КАК НомерСтроки,
|	ЕСТЬNULL(БазаОсн.РезультатБаза, 0) КАК СуммаБазыОклад,
|	ЕСТЬNULL(БазаДоп.РезультатБаза, 0) КАК СуммаБазыПодарок
|ИЗ
|	РегистрРасчета.ДополнительныеНачисления КАК ДопНачисления
|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрРасчета.ДополнительныеНачисления.БазаОсновныеНачисления(&Измерения, &Измерения, , Регистратор = &Ссылка) КАК БазаОсн
|		ПО ДопНачисления.НомерСтроки = БазаОсн.НомерСтроки
|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрРасчета.ДополнительныеНачисления.БазаДополнительныеНачисления(&Измерения, &Измерения, , Регистратор = &Ссылка) КАК БазаДоп
|		ПО ДопНачисления.НомерСтроки = БазаДоп.НомерСтроки
|ГДЕ
|	ДопНачисления.Регистратор = &Ссылка
|	И ДопНачисления.ВидРасчета = &ВидРасчетаПремия";

МассивИзмерений = Новый Массив;
МассивИзмерений.Добавить("Сотрудник");

Запрос.УстановитьПараметр("Измерения", МассивИзмерений);
Запрос.УстановитьПараметр("Ссылка", Ссылка);
Запрос.УстановитьПараметр("ВидРасчетаПремия", ПланыВидовРасчета.ДополнительныеНачисления.Премия);

РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

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

// Финальная запись рассчитанных сумм премии
Движения.ДополнительныеНачисления.Записать(, Истина);

Альтернативный метод: Использование ПолучитьБазу()

В некоторых случаях (особенно в учебных задачах или простых алгоритмах) допустимо использовать метод ПолучитьБазу() объекта записи регистра расчета. Разберем этот подход подробнее. Этот метод позволяет получить таблицу значений с результатами базы без написания сложного текста запроса.

Важные моменты при использовании ПолучитьБазу():

  1. Первым параметром передается массив имен ресурсов (например, "Результат").
  2. Вторым параметром передается структура измерений (например, "Сотрудник").
  3. Метод должен вызываться только после того, как в регистре созданы записи с установленными базовыми периодами.

Для Каждого Запись Из Движения.ДополнительныеНачисления Цикл
    Если Запись.ВидРасчета = ПланыВидовРасчета.ДополнительныеНачисления.Премия Тогда
        
        Ресурсы = Новый Массив;
        Ресурсы.Добавить("Результат");
        
        Измерения = Новый Структура("Сотрудник", "Сотрудник");
        
        // Получаем базу из двух регистров
        БазаОсн = Запись.ПолучитьБазу("ОсновныеНачисления", Ресурсы, Измерения);
        БазаДоп = Запись.ПолучитьБазу("ДополнительныеНачисления", Ресурсы, Измерения);
        
        СуммаБазы = БазаОсн.Итог("Результат") + БазаДоп.Итог("Результат");
        Запись.Результат = СуммаБазы * Запись.Размер / 100;
        
    КонецЕсли;
КонецЦикла;

Проанализировав оба метода, мы видим, что использование запроса более эффективно с точки зрения производительности при большом количестве строк в документе, в то время как ПолучитьБазу() удобнее для быстрой разработки небольших модулей. Однако в обоих случаях предварительная запись базовых начислений является обязательным условием успеха.

Резюме и советы

Выяснив причину отсутствия базы, мы можем сделать следующие выводы:

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

← На главную