Как оптимизировать загрузку табеля из Excel в 1С:ЗУП 3.1 при сложном формате строк

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

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

Анализ проблемы и выбор стратегии

Рассмотрим типичную ситуацию: в файле Excel табельный номер указан в объединенной ячейке, а справа от него идут колонки по дням месяца. При этом данные организованы парами строк: нечетная строка содержит коды видов времени (например, «Я/Н»), а четная строка — соответствующее количество часов (например, «8/4»).

Основными точками оптимизации здесь являются:

  1. Отказ от запросов в цикле: поиск сотрудника методом НайтиПоКоду на каждой итерации крайне негативно сказывается на производительности.
  2. Правильный обход строк: вместо инкремента индекса внутри цикла лучше использовать шаг или четкую логику разделения на пары.
  3. Парсинг составных значений: обработка строк вида «Я/Н» должна быть универсальной, чтобы корректно сопоставлять виды времени и часы.

Шаг 1: Подготовка данных и массовый поиск сотрудников

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


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

// 2. Находим сотрудников одним запросом и помещаем в Соответствие
СоответствиеСотрудников = Новый Соответствие;
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Сотрудники.Ссылка КАК Ссылка,
    |	Сотрудники.Код КАК Код
    |ИЗ
    |	Справочник.Сотрудники КАК Сотрудники
    |ГДЕ
    |	Сотрудники.Код В(&МассивТН)";
Запрос.УстановитьПараметр("МассивТН", МассивТН);

Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    СоответствиеСотрудников.Вставить(Выборка.Код, Выборка.Ссылка);
КонецЦикла;

Использование объекта Соответствие (Map) позволяет мгновенно получать ссылку на сотрудника по его коду без обращения к серверу БД на каждом шаге цикла.

Шаг 2: Эффективный обход пар строк

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


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

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

Шаг 3: Универсальный парсинг видов времени и часов

Рассмотрим подробнее ситуацию, когда в одной ячейке указано несколько видов времени (например, «Я/Н» в верхней строке и «8/4» в нижней). Для корректного разбора необходимо использовать функцию СтрРазделить. Это позволит превратить строку в массив и сопоставить элементы по индексам. Посмотрим на пример функции обработки:


Процедура ОбработатьДанныеСотрудника(Сотрудник, СтрокаВидов, СтрокаЧасов)
    
    Для НомерДня = 1 По 31 Цикл
        ИмяКолонки = "День" + НомерДня; // Предположим, колонки называются так
        
        ТекстВидов = СтрокаВидов[ИмяКолонки];
        ТекстЧасов = СтрокаЧасов[ИмяКолонки];
        
        Если ПустаяСтрока(ТекстВидов) Тогда
            Продолжить;
        КонецЕсли;
        
        // Разбиваем строки по разделителю "/"
        МассивВидов = СтрРазделить(ТекстВидов, "/");
        МассивЧасов = СтрРазделить(ТекстЧасов, "/");
        
        // Важно: количество видов времени должно совпадать с количеством значений часов
        Для Инд = 0 По МассивВидов.ВГраница() Цикл
            ВидВремениКод = СокрЛП(МассивВидов[Инд]);
            Часы = ?(МассивЧасов.ВГраница() >= Инд, Число(МассивЧасов[Инд]), 0);
            
            // Здесь мы записываем данные в промежуточную ТЗ для заполнения табеля
            НоваяСтрока = ТЗРезультат.Добавить();
            НоваяСтрока.Сотрудник = Сотрудник;
            НоваяСтрока.Дата = НачалоМесяца(Объект.ПериодРегистрации) + (НомерДня - 1) * 86400;
            НоваяСтрока.ВидВремени = ПолучитьВидВремениПоКоду(ВидВремениКод);
            НоваяСтрока.Часы = Часы;
        КонецЦикла;
    КонецЦикла;
    
КонецПроцедуры

Шаг 4: Свертка данных и заполнение документа

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

Вместо ручного заполнения ВидВремени1, ВидВремени2 и так далее, рекомендуется:

  1. Сгруппировать полученную таблицу по сотрудникам.
  2. Использовать встроенные механизмы документа для перезаполнения строк.
  3. Если вы создаете новый документ, программно заполните его через Объект.ДанныеОВремени.Добавить(), помня, что в ЗУП 3.1 для одного сотрудника в табеле может быть несколько строк, если у него в течение месяца менялись условия труда или графики.

Важный совет: Для ускорения работы с Excel на стороне сервера (без установленного Microsoft Office) используйте метод ТабличныйДокумент.Прочитать(ПутьКФайлу, СпособЧтенияТабличногоДокумента.XLSX). Это работает значительно быстрее, чем COMОбъект, и не зависит от стороннего ПО.

Таким образом, комбинируя работу с Соответствием для поиска объектов, потоковое чтение файла и парсинг строк через СтрРазделить, мы получаем производительный и легко читаемый код, который справится с загрузкой табеля любой сложности.

← На главную