Как программно начислить зарплату в 1С:ЗУП 3.1: Пошаговое руководство

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

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

В этой статье мы подробно разберем, как обойти эти ограничения, заглянем внутрь программных механизмов расчета ЗУП 3.1, научимся использовать встроенный менеджер расчета и напишем готовый рабочий код для программного заполнения и проведения документа НачислениеЗарплаты.

В чем главная сложность: Архитектурные особенности ЗУП 3.1

Давайте проанализируем ситуацию и выясним, почему обычный метод Заполнить() не решает задачу. В отличие от старой редакции 2.5, где расчеты происходили преимущественно в модуле объекта, в ЗУП 3.1 логика расчета распределена между общими модулями, обработкой МенеджерРасчетаЗарплаты и модулем управляемой формы документа. Это сделано для повышения интерактивности: пользователь на форме видит расчет «на лету».

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

Если мы просто заполним документ программно без учета этих связей, то:

  1. Не заполнятся показатели начислений, из-за чего в регистре сведений ЗначенияПоказателейНачисления не сформируются движения. Это сломает последующие отчеты и перерасчеты.
  2. Разойдутся идентификаторы строк между начислениями, НДФЛ и взносами. Это приведет к тому, что в отчетах (например, в Расчетном листке или Анализе начислений) отработанное время и суммы начнут отображаться некорректно.

Для решения этой задачи нам потребуется сымитировать работу формы программным путем на сервере.

Шаг 1. Получаем описание документа

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

Рассмотрим, как инициализировать параметры и подготовить описание документа программно:


ПараметрыОписания = Новый Структура;
ПараметрыОписания.Вставить("ИзменениеБезОграничений", Истина);
ПараметрыОписания.Вставить("РегистрацияНачисленийДоступна", Истина);
ПараметрыОписания.Вставить("СпособыРасчетаСреднегоЗаработка", Неопределено);

ОписаниеДокумента = КадровыйУчетРасширенныйКлиентСервер.ОписаниеДокумента(ПараметрыОписания);

Используя этот подход, мы гарантируем совместимость нашего кода с будущими релизами 1С:ЗУП.

Шаг 2. Подготовка параметров для заполнения

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

Давайте разберем по шагам заполнение этой структуры:


СтруктураПараметров = РасчетЗарплатыРасширенныйКлиентСервер.ПараметрыПолученияДанныхЗаполненияДокумента();
СтруктураПараметров.ОписаниеДокумента = ОписаниеДокумента;
СтруктураПараметров.Организация = ОбъектДокумент.Организация;
СтруктураПараметров.ДокументСсылка = ОбъектДокумент.Ссылка;
СтруктураПараметров.Подразделение = ОбъектДокумент.Подразделение;
СтруктураПараметров.МесяцНачисления = ОбъектДокумент.МесяцНачисления;
СтруктураПараметров.ОкончаниеПериода = КонецМесяца(ОбъектДокумент.МесяцНачисления);
СтруктураПараметров.РежимНачисления = Перечисления.РежимНачисленияЗарплаты.ОкончательныйРасчет;
СтруктураПараметров.ОкончательныйРасчетНДФЛ = Истина;
СтруктураПараметров.ПроверятьРегистрациюПроцентаЕНВД = Истина;
СтруктураПараметров.ДатаВыплаты = РасчетЗарплатыРасширенныйКлиентСервер.ПланируемаяДатаВыплатыЗарплаты(ОбъектДокумент.Организация, ОбъектДокумент.МесяцНачисления);

Шаг 3. Расчет данных через фоновые механизмы

После подготовки параметров мы вызываем основной метод заполнения табличных частей. Платформа 1С делает это через фоновые задания БСП с использованием временного хранилища. Мы воспользуемся методом ПодготовитьДанныеДляЗаполнения менеджера документа НачислениеЗарплаты:


АдресХранилища = ПоместитьВоВременноеХранилище(Неопределено, Новый УникальныйИдентификатор);
Документы.НачислениеЗарплаты.ПодготовитьДанныеДляЗаполнения(СтруктураПараметров, АдресХранилища);
РезультатЗаполнения = ПолучитьИзВременногоХранилища(АдресХранилища);

В переменной РезультатЗаполнения мы получаем структуру со всеми рассчитанными таблицами: начислениями, удержаниями, НДФЛ, взносами и показателями. Наша следующая задача — правильно перенести эти таблицы в создаваемый документ.

Шаг 4. Перенос табличных частей и связывание показателей

Проанализируем структуру полученного результата. Большинство таблиц можно загрузить напрямую в соответствующие табличные части документа с помощью метода Загрузить(). Однако с показателями расчетов все обстоит сложнее.

В структуре РезультатЗаполнения.ДанныеДляЗаполненияТаблицДокумента начисления лежат в виде таблицы значений. Внутри каждой строки этой таблицы в колонке Показатели содержится вложенная таблица значений со значениями конкретных показателей для данной строки начисления. Нам необходимо «развернуть» эти вложенные таблицы во флэт-структуру подчиненной ТЧ Показатели документа, при этом строго соблюдая равенство идентификаторов строк.

Выполним заполнение начислений и показателей с помощью следующего алгоритма:


// Очищаем существующие таблицы документа перед заполнением
ОбъектДокумент.Начисления.Очистить();
ОбъектДокумент.Показатели.Очистить();

// Переносим таблицы значений из результата расчета в ТЧ документа
Для Каждого КлючИЗначение Из РезультатЗаполнения.ДанныеДляЗаполненияТаблицДокумента Цикл
    Если ТипЗнч(КлючИЗначение.Value) = Тип("ТаблицаЗначений") Тогда
        // Проверяем наличие ТЧ в документе
        МетаданныеТЧ = ОбъектДокумент.Метаданные().ТабличныеЧасти.Найти(КлючИЗначение.Key);
        Если МетаданныеТЧ <> Неопределено Тогда
            ОбъектДокумент[КлючИЗначение.Key].Загрузить(КлючИЗначение.Value);
        КонецЕсли;
    КонецЕсли;
КонецЦикла;

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

Шаг 5. Решение проблемы с НДФЛ и взносами

В процессе тестирования можно заметить, что движения по НДФЛ и страховым взносам могут формироваться некорректно, если не заполнены идентификаторы строк в соответствующих ТЧ. В отличие от начислений, где идентификаторы уникальны и генерируются системой автоматически, для НДФЛ часто используется сквозная порядковая нумерация.

Если после заполнения вы обнаружили, что реквизит ИдентификаторСтроки в ТЧ НДФЛ остался пустым, выполните принудительное индексирование строк перед проведением документа:


ИндексСтроки = 1;
Для Каждого СтрокаНДФЛ Из ОбъектДокумент.НДФЛ Цикл
    Если СтрокаНДФЛ.ИдентификаторСтроки = 0 Тогда
        СтрокаНДФЛ.ИдентификаторСтроки = ИндексСтроки;
    КонецЕсли;
    ИндексСтроки = ИндексСтроки + 1;
КонецЦикла;

Полный пример рабочего кода для программного начисления зарплаты

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


Функция ПрограммноеНачислениеЗарплаты(Организация, МесяцНачисления) Экспорт
    
    // 1. Создаем новый документ
    ОбъектДокумент = Документы.НачислениеЗарплаты.СоздатьДокумент();
    ОбъектДокумент.Дата = ТекущаяДата();
    ОбъектДокумент.МесяцНачисления = НачалоМесяца(МесяцНачисления);
    ОбъектДокумент.Организация = Организация;
    
    // Заполняем первоначальные значения (ответственный, подразделение и т.д.)
    ЗначенияДляЗаполнения = Новый Структура("Организация, Ответственный", Организация, ПараметрыСеанса.ТекущийПользователь);
    ЗарплатаКадры.ЗаполнитьПервоначальныеЗначенияВФорме(ОбъектДокумент, ЗначенияДляЗаполнения);
    
    // Записываем документ, чтобы получить ссылку (необходимо для расчетов)
    ОбъектДокумент.Записать(РежимЗаписиДокумента.Запись);
    
    // 2. Формируем описание документа
    ПараметрыОписания = Новый Структура;
    ПараметрыОписания.Вставить("ИзменениеБезОграничений", Истина);
    ПараметрыОписания.Вставить("РегистрацияНачисленийДоступна", Истина);
    ПараметрыОписания.Вставить("СпособыРасчетаСреднегоЗаработка", Неопределено);
    
    ОписаниеДокумента = КадровыйУчетРасширенныйКлиентСервер.ОписаниеДокумента(ПараметрыОписания);
    
    // 3. Настраиваем структуру параметров расчета
    СтруктураПараметров = РасчетЗарплатыРасширенныйКлиентСервер.ПараметрыПолученияДанныхЗаполненияДокумента();
    СтруктураПараметров.ОписаниеДокумента = ОписаниеДокумента;
    СтруктураПараметров.Организация = ОбъектДокумент.Organзация;
    СтруктураПараметров.ДокументСсылка = ОбъектДокумент.Ссылка;
    СтруктураПараметров.МесяцНачисления = ОбъектДокумент.МесяцНачисления;
    СтруктураПараметров.ОкончаниеПериода = КонецМесяца(ОбъектДокумент.МесяцНачисления);
    СтруктураПараметров.РежимНачисления = Перечисления.РежимНачисленияЗарплаты.ОкончательныйРасчет;
    СтруктураПараметров.ОкончательныйРасчетНДФЛ = Истина;
    СтруктураПараметров.ПроверятьРегистрациюПроцентаЕНВД = Истина;
    СтруктураПараметров.ДатаВыплаты = РасчетЗарплатыРасширенныйКлиентСервер.ПланируемаяДатаВыплатыЗарплаты(Организация, МесяцНачисления);
    
    // 4. Запускаем фоновый расчет данных
    АдресХранилища = ПоместитьВоВременноеХранилище(Неопределено, Новый УникальныйИдентификатор);
    Документы.НачислениеЗарплаты.ПодготовитьДанныеДляЗаполнения(СтруктураПараметров, АдресХранилища);
    РезультатЗаполнения = ПолучитьИзВременногоХранилища(АдресХранилища);
    
    Если РезультатЗаполнения = Неопределено Тогда
        ВызватьИсключение "Не удалось получить данные для заполнения документа начисления зарплаты.";
    КонецЕсли;
    
    // 5. Загружаем основные таблицы
    Для Каждого КлючИЗначение Из РезультатЗаполнения.ДанныеДляЗаполненияТаблицДокумента Цикл
        Если ТипЗнч(КлючИЗначение.Value) = Тип("ТаблицаЗначений") Тогда
            МетаданныеТЧ = ОбъектДокумент.Метаданные().ТабличныеЧасти.Найти(КлючИЗначение.Key);
            Если МетаданныеТЧ <> Неопределено Тогда
                ОбъектДокумент[КлючИЗначение.Key].Загрузить(КлючИЗначение.Value);
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;
    
    // 6. Переносим показатели начислений
    ОбъектДокумент.Показатели.Очистить();
    Для Каждого СтрокаНачисления Из РезультатЗаполнения.ДанныеДляЗаполненияТаблицДокумента.Начисления Цикл
        Для Каждого СтрокаПоказателя Из СтрокаНачисления.Показатели Цикл
            НоваяСтрокаПоказателей = ОбъектДокумент.Показатели.Добавить();
            ЗаполнитьЗначенияСвойств(НоваяСтрокаПоказателей, СтрокаПоказателя);
            НоваяСтрокаПоказателей.ИдентификаторСтрокиВидаРасчета = СтрокаНачисления.ИдентификаторСтроки;
        КонецЦикла;
    КонецЦикла;
    
    // 7. Корректируем идентификаторы НДФЛ
    ИндексСтроки = 1;
    Для Каждого СтрокаНДФЛ Из ОбъектДокумент.НДФЛ Цикл
        СтрокаНДФЛ.ИдентификаторСтроки = ИндексСтроки;
        ИндексСтроки = IndexСтроки + 1;
    КонецЦикла;
    
    // 8. Проводим готовый документ
    ОбъектДокумент.Записать(РежимЗаписиДокумента.Проведение);
    
    Возврат ОбъектДокумент.Ссылка;
    
КонецФункции

Альтернативный вариант: Использование Менеджера расчета зарплаты напрямую

Для более тонких и кастомных расчетов можно использовать объект МенеджерРасчетаЗарплаты напрямую, минуя процедуры подготовки данных документа. Это полезно, если вам нужно рассчитать зарплату, не создавая сам документ в базе данных (например, для построения прогнозных отчетов или интеграции со сторонними CRM-системами).

Проанализируем пример такого использования:


МенеджерРасчета = Обработки.МенеджерРасчетаЗарплаты.Создать();
МенеджерРасчета.Инициализировать(МесяцНачисления, Организация);

// Настраиваем состав расчетов
МенеджерРасчета.НастройкиРасчета.РассчитыватьНачисления = Истина;
МенеджерРасчета.НастройкиРасчета.РассчитыватьУдержания = Истина;
МенеджерРасчета.НастройкиРасчета.РассчитыватьНДФЛ = Истина;

// Выполняем расчет в оперативной памяти
МенеджерРасчета.РассчитатьЗарплату();

// Анализируем результаты расчета
ТаблицаРезультатов = МенеджерРасчета.Зарплата.Начисления;
// Здесь можно обработать полученную таблицу значений

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

Рекомендации по внедрению и стабильности

Поскольку ЗУП 3.1 постоянно обновляется в связи с частыми изменениями в законодательстве (НДФЛ, взносы, ЕФС-1 и т.д.), при написании программного кода автоматизации важно следовать нескольким правилам:

Рассмотренный нами алгоритм позволяет решить задачу программного начисления зарплаты красиво, быстро и с соблюдением всех стандартов разработки фирмы «1С».

← На главную