Как программно зарегистрировать начисление при создании отпуска без сохранения оплаты в 1С:ЗУП 3.1 и избежать ошибки «Документ не рассчитан»

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

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

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

Выясняем причину ошибки: архитектурные особенности ЗУП 3.1

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

Это разделение накладывает отпечаток на структуру метаданных и поведение объектов системы:

  1. Скрытые табличные части. На управляемой форме документа ОтпускБезСохраненияОплаты пользователь видит только базовые реквизиты: сотрудника, период отпуска, вид отпуска. Однако в структуре самого документа существуют скрытые табличные части, ключевой из которых является ТЧ Начисления. Даже если отпуск предоставляется «за свой счет» и не подлежит оплате, для системы это все равно расчетная операция, фиксирующая нулевое начисление и регистрирующая специальный вид времени (например, «НЕОПЛ» или «ОДОЗ»).
  2. Автоматический расчет на форме. Когда пользователь интерактивно заполняет документ на форме, при изменении дат отпуска или сотрудника срабатывают обработчики событий управляемой формы. Они обращаются к серверным процедурам расчета, которые автоматически заполняют скрытую табличную часть Начисления расчетной строкой, определяют количество оплачиваемых дней/часов и подтягивают соответствующий вид расчета из плана видов расчета Начисления. При обычном программном создании объекта без использования контекста формы эта табличная часть остается абсолютно пустой. Из-за этого при проведении не формируются движения по регистрам расчета (например, по регистру «Начисления удержания по сотрудникам») и регистрам учета времени.
  3. Реквизиты утверждения расчета. Чтобы документ считался завершенным и готовым к проведению, система проверяет заполнение специализированных реквизитов, отвечающих за статус расчета. К ним относятся Рассчитал (тип СправочникСсылка.Пользователи) и ПерерасчетВыполнен (тип Булево). При интерактивной работе под пользователем с правами расчетчика эти реквизиты заполняются автоматически в момент расчета на форме. При программной записи они остаются незаполненными, что и инициирует вывод ошибки «Документ не рассчитан».

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

Анализ некорректного кода

Рассмотрим пример исходного программного кода, который часто пишут начинающие разработчики и который гарантированно приводит к возникновению рассматриваемой ошибки:


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

В данном фрагменте кода пропущены три важнейших шага:

Ниже мы подробно рассмотрим три альтернативных способа решения этой задачи.

Способ 1. Программное заполнение скрытых реквизитов и табличной части (Рекомендуемый прямой метод)

Этот способ идеально подходит для выполнения кода в фоновых заданиях, внешних обработках или веб-сервисах, где нет возможности использовать контекст управляемой формы клиента.

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


Процедура ПрограммноеСозданиеОтпускаБезОплаты(СотрудникСсылка, ДатаНачала, ДатаКонца) Экспорт

    // 1. Инициализируем новый документ
    ОтпускДок = Документы.ОтпускБезСохраненияОплаты.СоздатьДокумент();
    ОтпускДок.Дата = ТекущаяДата();
    ОтпускДок.ПериодРегистрации = НачалоМесяца(ДатаНачала);
    
    // Получаем организацию сотрудника на дату начала отпуска
    ОрганизацияСотрудника = КадровыйУчет.ОрганизацияСотрудника(СотрудникСсылка, ДатаНачала);
    ОтпускДок.Организация = ОрганизацияСотрудника;
    
    ОтпускДок.Сотрудник = СотрудникСсылка;
    ОтпускДок.ДатаНачала = ДатаНачала;
    ОтпускДок.ДатаОкончания = ДатаКонца;
    
    // Находим вид отпуска (рекомендуется использовать предопределенные элементы или поиск по ТК РФ)
    ОтпускДок.ВидОтпуска = Справочники.ВидыОтпусков.НайтиПоНаименованию("Отпуск без оплаты в соотв. с частью 1 статьи 128 ТК РФ");
    
    // Находим соответствующий вид расчета в плане видов расчета Начисления
    ВидРасчета = ПланыВидовРасчета.Начисления.НайтиПоКоду("ОТПДО"); 
    Если ВидРасчета.Пустая() Тогда
        // Альтернативный поиск по предопределенному имени или наименованию
        ВидРасчета = ПланыВидовРасчета.Начисления.НайтиПоНаименованию("Отпуск без сохранения оплаты");
    КонецЕсли;
    
    ОтпускДок.ВидРасчета = ВидРасчета;
    
    // 2. Обязательно заполняем скрытую табличную часть "Начисления"
    СтрокаНачисления = ОтпускДок.Начисления.Добавить();
    СтрокаНачисления.Сотрудник = СотрудникСсылка;
    СтрокаНачисления.Начисление = ВидРасчета;
    СтрокаНачисления.ДатаНачала = ДатаНачала;
    СтрокаНачисления.ДатаОкончания = ДатаКонца;
    
    // Рассчитываем количество дней отпуска
    КоличествоДней = (КонецДня(ДатаКонца) - НачалоДня(ДатаНачала)) / 86400 + 1;
    // Заполняем показатели времени, если это требуется для корректного отражения в табеле
    // В простейших случаях платформа сама рассчитает дни при проведении, если ТЧ заполнена
    
    // 3. Заполняем реквизиты готовности расчета ("Утверждаем" расчет)
    ОтпускДок.Рассчитал = Пользователи.ТекущийПользователь();
    ОтпускДок.ПерерасчетВыполнен = Истина;
    
    // Дополнительные кадровые реквизиты для корректности печатных форм
    ОтпускДок.ОсвобождатьСтавку = Ложь;
    ОтпускДок.Основание = "Личное заявление сотрудника";
    
    // 4. Запись и проведение документа
    Попытка
        ОтпускДок.Записать(РежимЗаписиДокумента.Проведение);
        Сообщить("Документ " + ОтпускДок.Ссылка + " успешно создан и проведен.");
    Исключение
        ВызватьИсключение "Ошибка при проведении документа: " + ОписаниеОшибки();
    КонецПопытки;

КонецПроцедуры

Способ 2. Эмуляция интерактивного заполнения через форму документа

Поскольку бизнес-логика расчета и распределения времени в ЗУП 3.1 исторически сильно завязана на программный код управляемой формы, самым надежным способом создания документа без дублирования алгоритмов расчета является использование клиентского контекста и инициализация формы документа.

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


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

Преимущества данного метода: Полная гарантия того, что документ будет заполнен абсолютно идентично тому, как если бы его вносил расчетчик вручную. Все внутренние зависимости, распределение вытеснения видов времени и расчет стажа ПФР отработают на 100% корректно.

Способ 3. Использование программного Менеджера расчета ЗУП (Продвинутый уровень)

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

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


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

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

Проверка важной настройки: «Выполнять предварительный расчет документов»

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

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

Резюме и чек-лист для разработчика

Для того чтобы успешно создавать кадровые и расчетные документы отклонений в 1С:ЗУП 3.1 программным путем, всегда проверяйте выполнение следующих условий:

Применяя данные подходы, вы сможете создавать надежные интеграционные решения и автоматизировать массовый ввод отпусков за свой счет без ошибок проведения.

← На главную