Как программно добавить дополнительный вид времени во вторую строку табеля для сотрудника в 1С:ЗУП 3.1?

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

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

Основные отличия 1С:ЗУП 3.1 от 2.5 в учете времени

Прежде всего, важно понимать, что 1С:ЗУП 3.1 значительно переработана по сравнению с версией 2.5 в части учета рабочего времени. Ключевое отличие заключается в возможности указывать несколько видов времени для одного сотрудника на одну и ту же дату. Эта функциональность позволяет более гибко отражать сложное рабочее время, например, когда сотрудник часть дня работает по основному графику, а часть — выполняет дополнительные работы, требующие отдельного вида времени.

Важные настройки программы 1С:ЗУП 3.1

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

  1. «Используется несколько видов времени в графике работы»: Эта опция должна быть установлена. Она разрешает планировать и учитывать разные виды рабочего времени.
  2. «Максимальное количество видов времени на одну дату»: Этот переключатель (его видимость может зависеть от других настроек, таких как учетная политика или использование почасовой оплаты) определяет, сколько видов времени (например, явка, отпуск, больничный, дополнительный вид) система позволит отразить на одну и ту же дату для одного сотрудника. Если в вашей задаче требуется "вторая строчка", убедитесь, что это значение не равно 1.

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

Структура табличной части «ДанныеОВремени» в 1С:ЗУП 3.1

Ключ к решению нашей задачи лежит в понимании, как документ «Табель» хранит данные в своей табличной части ДанныеОВремени. В отличие от других документов, которые могут хранить каждую запись о времени на отдельной физической строке, в документе «Табель» каждая строка табличной части ДанныеОВремени обычно соответствует одному сотруднику за один конкретный день периода. Если на этот день у сотрудника есть несколько видов времени, они хранятся в пределах этой же физической строки, но в разных колонках:

Таким образом, когда мы говорим о добавлении вида времени "второй строчкой", мы имеем в виду заполнение полей ВидВремени2 и Часов2 для соответствующего дня в уже существующей строке табличной части ДанныеОВремени.

Анализ некорректных подходов

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

1. Использование метода «Вставить» для существующей строки


МассивКорр = Результат.ДанныеОВремени.НайтиСтроки(Новый Структура("Сотрудник", СтрокаТаблицы.Сотрудник));
Если МассивКорр.Количество > 0 Тогда
    НовСтрока = МассивКорр[0];
    ИндексВставки = 1;
    НовСтрока["Часов" + Постфикс] = Число ("1");
    НовСтрока["ВидВремени" + Постфикс] = ПП;
    МассивКорр.Вставить(ИндексВставки, НовСтрока);
КонецЕсли;

В этом примере вы сначала находите первую строку для сотрудника (МассивКорр[0]), затем пытаетесь ее изменить и вставить копию этой же строки в массив. Проблема здесь в том, что:

2. Добавление новой строки через «Результат.ДанныеОВремени.Добавить» с повторным указанием сотрудника


НоваяСтрокаТЧ = Результат.ДанныеОВремени.Добавить();
НоваяСтрокаТЧ.Сотрудник = СтрокаТаблицы.Сотрудник; // Попытка присвоить сотрудника
НоваяСтрокаТЧ.ВидВремениN = ПП;
НоваяСтрокаТЧ.ЧасовN = 1;

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

Такое поведение подтверждает, что для добавления второго вида времени для того же сотрудника на ту же дату необходимо работать с уже существующей строкой, используя поля ВидВремениN и ЧасовN.

Корректное решение: Обновление существующей строки табеля

Итак, давайте рассмотрим правильный подход к программному добавлению дополнительного вида времени. Наша задача — найти соответствующую строку в табличной части ДанныеОВремени для конкретного сотрудника и дня, а затем заполнить в ней поля ВидВремени2 и Часов2.

Шаг 1: Инициализация и подготовка

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

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


// Предполагаем, что ДокументОбъект - это уже существующий и заполненный объект документа "Табель".
// Получим табличную часть для работы
ПереносимыеДанныеОВремени = ДокументОбъект.ДанныеОВремени;

// Определим наш дополнительный вид времени
// Например, это может быть предопределенный элемент или найденный по наименованию
ВидВремениПП = Справочники.ВидыИспользованияРабочегоВремени.НайтиПоНаименованию("ПП"); 

// Если ВидВремениПП не найден, нужно обработать эту ситуацию
Если ВидВремениПП = Неопределено Тогда
    Сообщить("Вид времени 'ПП' не найден! Проверьте справочник 'Виды использования рабочего времени'.");
    Возврат;
КонецЕсли;

// Определим постфикс для второй строчки
ПостфиксДляВторогоВида = "2"; // Для ВидВремени2 и Часов2
```

Шаг 2: Итерация по сотрудникам и дням

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

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


// Пример: Допустим, нам нужно добавить вид времени ПП для всех сотрудников на все дни, 
// где уже есть Явка (по умолчанию ВидВремени1)

// Итерируем по всем строкам табличной части ДанныеОВремени
Для Каждого СтрокаТабеля Из ПереносимыеДанныеОВремени Цикл
    // Проверим, что это строка для нужного нам сотрудника и дня
    // Например, если мы хотим добавить ПП1 только для тех дней, где есть Явка (ВидВремени1)
    Если СтрокаТабеля.ВидВремени1 = Справочники.ВидыИспользованияРабочегоВремени.Явка Тогда // Пример условия
        // Дополнительные условия могут быть по дате, подразделению и т.д.
        // Пример условия по сотруднику (если у нас есть внешний список сотрудников):
        // Если СписокЦелевыхСотрудников.Найти(СтрокаТабеля.Сотрудник) <> Неопределено Тогда

        // ... Продолжаем в следующем шаге
    КонецЕсли;
КонецЦикла;
```

Шаг 3: Обновление полей «ВидВремениN» и «ЧасовN»

Как только мы нашли нужную строку (для целевого сотрудника и дня), мы можем обновить ее поля, относящиеся ко второму виду времени. Используем динамические имена полей, чтобы сослаться на ВидВремени2 и Часов2.


// Продолжение цикла из Шага 2:
Для Каждого СтрокаТабеля Из ПереносимыеДанныеОВремени Цикл
    // Условие для обработки строки (например, где уже есть явка)
    Если СтрокаТабеля.ВидВремени1 = Справочники.ВидыИспользованияРабочегоВремени.Явка Тогда
        // Проверяем, что ВидВремени2 еще не заполнен
        // Это важно, чтобы не перезаписать уже существующие данные
        Если СтрокаТабеля["ВидВремени" + ПостфиксДляВторогоВида] = Неопределено 
           ИЛИ СтрокаТабеля["ВидВремени" + ПостфиксДляВторогоВида] = Справочники.ВидыИспользованияРабочегоВремени.ПустаяСсылка Тогда

            // Устанавливаем наш дополнительный вид времени
            СтрокаТабеля["ВидВремени" + ПостфиксДляВторогоВида] = ВидВремениПП;
            // Устанавливаем количество часов (в данном случае "1")
            СтрокаТабеля["Часов" + ПостфиксДляВторогоВида] = 1; // Или любое другое нужное количество часов
        Иначе
            // Если ВидВремени2 уже заполнен, можно вывести сообщение или пропустить
            Сообщить("Для сотрудника " + СтрокаТабеля.Сотрудник.Наименование 
                     + " на дату (подразумевается из строки табеля) поле ВидВремени" + ПостфиксДляВторогоВида 
                     + " уже заполнено. Пропускаем.");
        КонецЕсли;
    КонецЕсли;
КонецЦикла;
```

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

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

Шаг 4: Применение изменений в документе

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


// Записать измененный документ
Попытка
    ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение); // Или РежимЗаписиДокумента.Запись
    Сообщить("Табель успешно обновлен.");
Исключение
    Сообщить("Ошибка при обновлении табеля: " + ОписаниеОшибки());
КонецПопытки;
```

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


// Пример, если вы работали с копией табличной части
// ДокументОбъект.ДанныеОВремени.Загрузить(ВашаВременнаяТаблица);
// ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
```

Пример полного кода обработки

Давайте соберем все в единый пример, демонстрирующий, как может выглядеть фрагмент кода в вашей обработке:


// Предположим, что мы находимся в модуле объекта обработки или внешней обработки
// и у нас есть ссылка на документ Табель, который нужно модифицировать.
// Для примера, пусть ДокументТабель будет передан в функцию.

Функция ЗаполнитьДополнительныйВидВремениВТабеле(ДокументТабель) Экспорт

    // --- Шаг 1: Инициализация и подготовка ---

    // Проверяем тип документа
    Если НЕ ТипЗнч(ДокументТабель) = Тип("ДокументОбъект.Табель") Тогда
        Сообщить("Передан некорректный объект. Ожидается документ 'Табель'.");
        Возврат Ложь;
    КонецЕсли;

    // Получаем ссылку на табличную часть ДанныеОВремени
    // Будем работать с ней напрямую, чтобы изменения сразу применялись к объекту
    ТабличнаяЧастьДанныеОВремени = ДокументТабель.ДанныеОВремени;

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

    Если ВидВремениПП = Неопределено Тогда
        Сообщить("Вид времени 'ПП' не найден! Проверьте справочник 'Виды использования рабочего времени'.");
        Возврат Ложь;
    КонецЕсли;

    // Определяем постфикс для второй строчки (т.е., для ВидВремени2 и Часов2)
    ПостфиксДляВторогоВида = "2"; 

    // --- Шаг 2 и 3: Итерация по строкам табеля и обновление полей ---

    КоличествоОбновленныхСтрок = 0;

    Для Каждого СтрокаТабеля Из ТабличнаяЧастьДанныеОВремени Цикл

        // Здесь можно добавить условия для фильтрации строк, которые нужно изменить.
        // Например, если нужно добавить "ПП" только для определенных сотрудников или на определенные даты.
        // Для примера: добавляем "ПП" на все дни, где уже есть "Явка"
        Если СтрокаТабеля.ВидВремени1 = Справочники.ВидыИспользованияРабочегоВремени.Явка Тогда

            // Проверяем, не заполнен ли уже ВидВремени2, чтобы не перезаписать данные
            Если СтрокаТабеля["ВидВремени" + ПостфиксДляВторогоВида] = Неопределено 
               ИЛИ СтрокаТабеля["ВидВремени" + ПостфиксДляВторогоВида] = Справочники.ВидыИспользованияРабочегоВремени.ПустаяСсылка Тогда

                СтрокаТабеля["ВидВремени" + ПостфиксДляВторогоВида] = ВидВремениПП;
                СтрокаТабеля["Часов" + ПостфиксДляВторогоВида] = 1; // Устанавливаем 1 час, как в задаче
                КоличествоОбновленныхСтрок = КоличествоОбновленныхСтрок + 1;
            Иначе
                // Сообщаем, если поле уже заполнено и мы не хотим его перезаписывать
                // Сообщить("Для сотрудника " + СтрокаТабеля.Сотрудник.Наименование 
                //          + " на " + Формат(СтрокаТабеля.Дата, "ДФ=dd.MM.yyyy") // Добавление даты для большей информативности
                //          + " поле ВидВремени" + ПостфиксДляВторогоВида 
                //          + " уже заполнено (" + СтрокаТабеля["ВидВремени" + ПостфиксДляВторогоВида] + "). Пропускаем.");
            КонецЕсли;
        КонецЕсли;

    КонецЦикла;

    // --- Шаг 4: Применение изменений в документе ---

    Если КоличествоОбновленныхСтрок > 0 Тогда
        Попытка
            ДокументТабель.Записать(РежимЗаписиДокумента.Проведение); // Проводим документ
            Сообщить("Табель '" + ДокументТабель.Номер + "' от " + Формат(ДокументТабель.Дата, "ДФ=dd.MM.yyyy") 
                     + " успешно обновлен. Добавлено видов времени ПП: " + КоличествоОбновленныхСтрок);
            Возврат Истина;
        Исключение
            Сообщить("Ошибка при проведении табеля '" + ДокументТабель.Номер + "' от " + Формат(ДокументТабель.Дата, "ДФ=dd.MM.yyyy") 
                     + ": " + ОписаниеОшибки());
            Возврат Ложь;
        КонецПопытки;
    Иначе
        Сообщить("Не найдено строк для обновления в табеле '" + ДокументТабель.Номер + "' от " + Формат(ДокументТабель.Дата, "ДФ=dd.MM.yyyy") + ".");
        Возврат Ложь;
    КонецЕсли;

КонецФункции

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

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

Таким образом, понимая внутреннюю структуру хранения данных в 1С:ЗУП 3.1 и используя соответствующие поля ВидВремениN и ЧасовN, мы можем корректно программно добавлять дополнительные виды времени в табель.

← На главную