При работе с программным заполнением документа «Табель» в 1С:ЗУП 3.1 часто возникает задача добавления дополнительного вида времени для сотрудника на уже заполненный день. Если в более ранних версиях, например, в ЗУП 2.5, такие задачи решались относительно просто, то в ЗУП 3.1, с ее более сложной и гибкой структурой учета времени, могут возникнуть сложности. Давайте разберем, как корректно программно добавить дополнительный вид времени, например, «ПП 1», в табель сотрудника, так чтобы он отображался "второй строчкой" для соответствующего дня.
Прежде всего, важно понимать, что 1С:ЗУП 3.1 значительно переработана по сравнению с версией 2.5 в части учета рабочего времени. Ключевое отличие заключается в возможности указывать несколько видов времени для одного сотрудника на одну и ту же дату. Эта функциональность позволяет более гибко отражать сложное рабочее время, например, когда сотрудник часть дня работает по основному графику, а часть — выполняет дополнительные работы, требующие отдельного вида времени.
Для корректной работы с несколькими видами времени в табеле, нам необходимо убедиться, что в программе активированы соответствующие настройки. Перейдите в раздел «Настройка» – «Расчет зарплаты». Здесь обратите внимание на следующие параметры:
Также может быть важна настройка «Проверять соответствие фактического времени плановому», находящаяся в «Настройка» – «Расчет зарплаты» – «Прочие настройки расчета зарплаты». Если вы добавляете виды времени, которые не соответствуют плановому графику (например, дополнительный вид времени в выходной день), и эта опция активна, программа может не дать провести табель. В таких случаях, возможно, придется временно отключить эту проверку или обеспечить соответствие данных.
Ключ к решению нашей задачи лежит в понимании, как документ «Табель» хранит данные в своей табличной части ДанныеОВремени. В отличие от других документов, которые могут хранить каждую запись о времени на отдельной физической строке, в документе «Табель» каждая строка табличной части ДанныеОВремени обычно соответствует одному сотруднику за один конкретный день периода. Если на этот день у сотрудника есть несколько видов времени, они хранятся в пределах этой же физической строки, но в разных колонках:
ВидВремени1, Часов1ВидВремени2, Часов2ВидВремени3, Часов3Таким образом, когда мы говорим о добавлении вида времени "второй строчкой", мы имеем в виду заполнение полей ВидВремени2 и Часов2 для соответствующего дня в уже существующей строке табличной части ДанныеОВремени.
В процессе поиска решения многие разработчики, привыкшие к другим структурам данных, могут столкнуться с несколькими распространенными ошибками. Давайте рассмотрим их и выясним, почему они не работают:
МассивКорр = Результат.ДанныеОВремени.НайтиСтроки(Новый Структура("Сотрудник", СтрокаТаблицы.Сотрудник));
Если МассивКорр.Количество > 0 Тогда
НовСтрока = МассивКорр[0];
ИндексВставки = 1;
НовСтрока["Часов" + Постфикс] = Число ("1");
НовСтрока["ВидВремени" + Постфикс] = ПП;
МассивКорр.Вставить(ИндексВставки, НовСтрока);
КонецЕсли;
В этом примере вы сначала находите первую строку для сотрудника (МассивКорр[0]), затем пытаетесь ее изменить и вставить копию этой же строки в массив. Проблема здесь в том, что:
НовСтрока после присвоения МассивКорр[0] по сути становится ссылкой на ту же строку. Любые изменения в НовСтрока отразятся и в МассивКорр[0].Вставить() работает с коллекцией (в данном случае, с МассивКорр, который является коллекцией найденных строк), но не изменяет исходную табличную часть Результат.ДанныеОВремени. Вы просто манипулируете временным массивом. Это не приведет к добавлению новой строки или обновлению данных в документе.ВидВремениN).
НоваяСтрокаТЧ = Результат.ДанныеОВремени.Добавить();
НоваяСтрокаТЧ.Сотрудник = СтрокаТаблицы.Сотрудник; // Попытка присвоить сотрудника
НоваяСтрокаТЧ.ВидВремениN = ПП;
НоваяСтрокаТЧ.ЧасовN = 1;
Этот подход также вызывает проблемы. Когда мы используем Результат.ДанныеОВремени.Добавить(), мы создаем новую физическую строку в табличной части. Если затем мы попытаемся присвоить этой новой строке уже существующего в других строках табличной части сотрудника, система может воспринять это как попытку создать дублирующую запись для того же сотрудника на ту же дату. В результате:
Сотрудник может остаться пустым, как это было отмечено в ходе обсуждения.Такое поведение подтверждает, что для добавления второго вида времени для того же сотрудника на ту же дату необходимо работать с уже существующей строкой, используя поля ВидВремениN и ЧасовN.
Итак, давайте рассмотрим правильный подход к программному добавлению дополнительного вида времени. Наша задача — найти соответствующую строку в табличной части ДанныеОВремени для конкретного сотрудника и дня, а затем заполнить в ней поля ВидВремени2 и Часов2.
Предположим, что документ «Табель» уже создан и заполнен основными данными по графику. Нам нужна обработка, которая будет проходить по нужным нам сотрудникам и дням, чтобы добавить дополнительный вид времени. В качестве примера будем считать, что у нас есть переменная ДокументОбъект, которая ссылается на объект документа «Табель», и ВидВремениПП — это ссылка на элемент справочника «Виды использования рабочего времени» с видом «ПП».
Мы будем работать напрямую с табличной частью документа или с ее копией, если это часть более сложной обработки, которая потом загрузит данные обратно в документ.
// Предполагаем, что ДокументОбъект - это уже существующий и заполненный объект документа "Табель".
// Получим табличную часть для работы
ПереносимыеДанныеОВремени = ДокументОбъект.ДанныеОВремени;
// Определим наш дополнительный вид времени
// Например, это может быть предопределенный элемент или найденный по наименованию
ВидВремениПП = Справочники.ВидыИспользованияРабочегоВремени.НайтиПоНаименованию("ПП");
// Если ВидВремениПП не найден, нужно обработать эту ситуацию
Если ВидВремениПП = Неопределено Тогда
Сообщить("Вид времени 'ПП' не найден! Проверьте справочник 'Виды использования рабочего времени'.");
Возврат;
КонецЕсли;
// Определим постфикс для второй строчки
ПостфиксДляВторогоВида = "2"; // Для ВидВремени2 и Часов2
```
Нам необходимо пройти по всем строкам табличной части ДанныеОВремени, чтобы найти нужного сотрудника и день. Важно помнить, что каждая строка в этой табличной части уже содержит информацию о сотруднике за определенный день (или период, если табель заполняется с детализацией по периодам, но чаще всего это дневная детализация).
Мы будем перебирать строки табличной части и для каждой строки проверять, соответствует ли она нашему целевому сотруднику или условиям, при которых нужно добавить вид времени «ПП».
// Пример: Допустим, нам нужно добавить вид времени ПП для всех сотрудников на все дни,
// где уже есть Явка (по умолчанию ВидВремени1)
// Итерируем по всем строкам табличной части ДанныеОВремени
Для Каждого СтрокаТабеля Из ПереносимыеДанныеОВремени Цикл
// Проверим, что это строка для нужного нам сотрудника и дня
// Например, если мы хотим добавить ПП1 только для тех дней, где есть Явка (ВидВремени1)
Если СтрокаТабеля.ВидВремени1 = Справочники.ВидыИспользованияРабочегоВремени.Явка Тогда // Пример условия
// Дополнительные условия могут быть по дате, подразделению и т.д.
// Пример условия по сотруднику (если у нас есть внешний список сотрудников):
// Если СписокЦелевыхСотрудников.Найти(СтрокаТабеля.Сотрудник) <> Неопределено Тогда
// ... Продолжаем в следующем шаге
КонецЕсли;
КонецЦикла;
```
Как только мы нашли нужную строку (для целевого сотрудника и дня), мы можем обновить ее поля, относящиеся ко второму виду времени. Используем динамические имена полей, чтобы сослаться на ВидВремени2 и Часов2.
// Продолжение цикла из Шага 2:
Для Каждого СтрокаТабеля Из ПереносимыеДанныеОВремени Цикл
// Условие для обработки строки (например, где уже есть явка)
Если СтрокаТабеля.ВидВремени1 = Справочники.ВидыИспользованияРабочегоВремени.Явка Тогда
// Проверяем, что ВидВремени2 еще не заполнен
// Это важно, чтобы не перезаписать уже существующие данные
Если СтрокаТабеля["ВидВремени" + ПостфиксДляВторогоВида] = Неопределено
ИЛИ СтрокаТабеля["ВидВремени" + ПостфиксДляВторогоВида] = Справочники.ВидыИспользованияРабочегоВремени.ПустаяСсылка Тогда
// Устанавливаем наш дополнительный вид времени
СтрокаТабеля["ВидВремени" + ПостфиксДляВторогоВида] = ВидВремениПП;
// Устанавливаем количество часов (в данном случае "1")
СтрокаТабеля["Часов" + ПостфиксДляВторогоВида] = 1; // Или любое другое нужное количество часов
Иначе
// Если ВидВремени2 уже заполнен, можно вывести сообщение или пропустить
Сообщить("Для сотрудника " + СтрокаТабеля.Сотрудник.Наименование
+ " на дату (подразумевается из строки табеля) поле ВидВремени" + ПостфиксДляВторогоВида
+ " уже заполнено. Пропускаем.");
КонецЕсли;
КонецЕсли;
КонецЦикла;
```
Обратите внимание, что в реальном коде вам, скорее всего, понадобится более сложная логика для определения конкретных дней и сотрудников, на которые нужно добавить «ПП». Возможно, вы будете передавать список сотрудников и дат в вашу обработку.
Также важно предусмотреть, что если вы работаете не с объектом документа напрямую, а с копией его табличной части, то после всех изменений необходимо загрузить обновленные данные обратно в документ. Если же вы работаете с ДокументОбъект.ДанныеОВремени напрямую, то изменения применяются немедленно.
После того как все необходимые изменения в табличной части ДанныеОВремени внесены, необходимо записать документ, чтобы сохранить эти изменения в базе данных.
// Записать измененный документ
Попытка
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение); // Или РежимЗаписиДокумента.Запись
Сообщить("Табель успешно обновлен.");
Исключение
Сообщить("Ошибка при обновлении табеля: " + ОписаниеОшибки());
КонецПопытки;
```
Если ваша обработка работает не с самим ДокументОбъект.ДанныеОВремени, а с временной таблицей (например, если вы выгружали данные в ТЗ, а потом обратно), то вам может потребоваться сначала загрузить эти данные в табличную часть документа, а затем записать документ:
// Пример, если вы работали с копией табличной части
// ДокументОбъект.ДанныеОВремени.Загрузить(ВашаВременнаяТаблица);
// ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
```
Давайте соберем все в единый пример, демонстрирующий, как может выглядеть фрагмент кода в вашей обработке:
// Предположим, что мы находимся в модуле объекта обработки или внешней обработки
// и у нас есть ссылка на документ Табель, который нужно модифицировать.
// Для примера, пусть ДокументТабель будет передан в функцию.
Функция ЗаполнитьДополнительныйВидВремениВТабеле(ДокументТабель) Экспорт
// --- Шаг 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, мы можем корректно программно добавлять дополнительные виды времени в табель.