При кастомизации и доработке документов расчета в программном продукте «1С:Зарплата и управление персоналом» (ЗУП 3.1) разработчики нередко сталкиваются со специфическим поведением интерфейсных механизмов. Классический сценарий: при первом заполнении табличной части документа (например, «Разовое начисление» или «Премия») через форму подбора сотрудников все показатели рассчитываются корректно и отображаются на форме. Однако при повторном вызове подбора новые строки добавляются без заполнения ключевых показателей, а значения у ранее добавленных сотрудников могут неожиданно исказиться.
В этой статье мы подробно разберем природу данной проблемы, проанализируем логическую ошибку в коде, изучим внутреннюю архитектуру хранения показателей в ЗУП 3.1 и рассмотрим современные варианты решения, включая типовые возможности системы.
Для начала выясним причину сбоя в алгоритме заполнения. Проанализируем исходный фрагмент кода, который разработчик использовал для обработки результатов подбора сотрудников:
Для Индекс = 0 По ВыбранноеЗначение.Количество - 1 Цикл
Для Итератор = 1 По 100 Цикл
Если Не объект.Начисления.Количество = 0 Тогда
Если объект.Начисления[Индекс].Свойство("Показатель" + Итератор) Тогда
Если Строка(объект.Начисления[Индекс]["Показатель" + Итератор]) = "Среднее количество часов в месяце" Тогда
объект.Начисления[Индекс]["Значение" + Итератор] = Доработки.ПечатнаяФормаГрафиковРаботы(объект.Начисления[Индекс].ГрафикРаботы, Год(Объект.ДатаНачала));
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецЦикла;
Разберем детально по шагам, что происходит в оперативной памяти и структурах данных при выполнении этого алгоритма.
Шаг 1: Первичное заполнение документа.
Пользователь открывает пустой документ, нажимает кнопку подбора и выбирает, например, 5 сотрудников. Платформа возвращает коллекцию ВыбранноеЗначение, содержащую 5 элементов. Соответственно, переменная ВыбранноеЗначение.Количество принимает значение 5.
В табличную часть Начисления добавляется 5 строк (с индексами от 0 до 4).
Запускается внешний цикл: Для Индекс = 0 По ВыбранноеЗначение.Количество - 1, который совершает итерации от 0 до 4.
Внутри цикла код успешно обращается к строкам по индексу: объект.Начисления[0], объект.Начисления[1] ... объект.Начисления[4]. Показатели для первых пяти сотрудников рассчитываются и выводятся на форму без замечаний.
Шаг 2: Повторный запуск подбора.
Пользователь решает добавить в этот же документ еще 3 сотрудников. Он снова открывает подбор и выбирает новые элементы. В коллекцию ВыбранноеЗначение теперь попадает только 3 элемента (новые добавленные сотрудники). Соответственно, переменная ВыбранноеЗначение.Количество теперь равна 3.
При этом в табличной части Начисления уже содержатся ранее добавленные сотрудники, и теперь общее количество строк равно 8 (5 старых и 3 новых). Новые сотрудники физически записаны в конец таблицы и имеют индексы 5, 6 и 7.
Запускается цикл: Для Индекс = 0 По ВыбранноеЗначение.Количество - 1, который в данном случае будет итерироваться от 0 до 2.
В результате программа начинает обрабатывать строки объект.Начисления[0], объект.Начисления[1] и объект.Начисления[2]. Это строки **первых трех ранее добавленных сотрудников**! Их показатели рассчитываются заново и перезаписываются. При этом строки с новыми сотрудниками (индексы 5, 6, 7) вообще не обрабатываются кодом, так как переменная Индекс никогда не достигнет этих значений. Именно поэтому значения показателей у новых сотрудников остаются пустыми.
Чтобы исправить эту проблему раз и навсегда, необходимо отказаться от привязки к индексам коллекции результатов подбора при заполнении строк табличной части. Вместо этого следует использовать универсальный обход коллекции Для Каждого.
Рассмотрим правильный вариант кода:
Для Каждого Стр Из Объект.Начисления Цикл
Для Итератор = 1 По ЭтаФорма.Показатели.Количество Цикл
Если Стр.Свойство("Показатель" + Итератор) Тогда
Если Строка(Стр["Показатель" + Итератор]) = "Среднее количество часов в месяце" Тогда
Стр["Значение" + Итератор] = Доработки.ПечатнаяФормаГрафиковРаботы(Стр.ГрафикРаботы, Год(Объект.ДатаНачала));
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецЦикла;
Проанализируем преимущества данного подхода:
Для Каждого Стр Из Объект.Начисления гарантирует, что программа обойдет абсолютно все строки, присутствующие в документе на момент выполнения кода, независимо от того, когда и каким образом они были туда добавлены.ЭтаФорма.Показатели.Количество. Это избавляет систему от выполнения сотен лишних 'холостых' циклов и предотвращает возможные ошибки выхода за границы диапазона.Для того чтобы писать надежный код, способный пережить будущие обновления конфигурации, важно понимать, как устроена архитектура хранения показателей в ЗУП 3.1.
Многие начинающие разработчики совершают методологическую ошибку, пытаясь напрямую работать с динамическими колонками формы вида Значение1, Значение2 и т. д. Давайте разберем, почему это тупиковый путь при разработке сложных алгоритмов:
Начисления. Они генерируются платформой динамически непосредственно в момент создания и отображения формы на клиенте. Их состав полностью зависит от того, какое начисление выбрано в шапке и какие показатели в него входят. Попытка обратиться к свойству Значение1 на уровне модуля объекта (например, при проведении или в фоновом задании) неизбежно вызовет критическую ошибку.Показатели. Каждая строка этой таблицы содержит:
ИдентификаторСтроки — числовой ключ связи со строкой табличной части Начисления;Показатель — ссылка на элемент справочника ПоказателиРасчетаЗарплаты;Значение — числовое значение данного показателя.Начисления имеет свой реквизит ИдентификаторСтроки. Именно по этому идентификатору система сопоставляет конкретного сотрудника, вид его расчета и значения показателей из скрытой ТЧ.Рассмотрим пример правильного программного заполнения показателей на уровне объекта данных. Этот код будет стабильно работать как на форме, так и в серверных фоновых заданиях или при программном создании документов из внешних источников:
// Пример правильного программного заполнения показателей на уровне объекта метаданных
Для Каждого СтрокаНачисления Из Объект.Начисления Цикл
// Получаем уникальный идентификатор строки для установки связи
ИдентификаторСвязи = СтрокаНачисления.ИдентификаторСтроки;
// Определяем нужный нам показатель расчета зарплаты
СсылкаНаПоказатель = Справочники.ПоказателиРасчетаЗарплаты.НайтиПоНаименованию("Среднее количество часов в месяце");
Если ЗначениеЗаполнено(СсылкаНаПоказатель) Тогда
// Пытаемся найти уже существующую строку показателя для данного начисления
ПараметрыПоиска = Новый Структура;
ПараметрыПоиска.Вставить("ИдентификаторСтроки", ИдентификаторСвязи);
ПараметрыПоиска.Вставить("Показатель", СсылкаНаПоказатель);
НайденныеСтроки = Объект.Показатели.НайтиСтроки(ПараметрыПоиска);
Если НайденныеСтроки.Количество() > 0 Тогда
СтрокаПоказателя = НайденныеСтроки[0];
Иначе
// Если строка не найдена, создаем новую и связываем ее
СтрокаПоказателя = Объект.Показатели.Добавить();
СтрокаПоказателя.ИдентификаторСтроки = ИдентификаторСвязи;
СтрокаПоказателя.Показатель = СсылкаНаПоказатель;
КонецЕсли;
// Производим расчет значения и его запись в ТЧ "Показатели"
СтрокаПоказателя.Значение = Доработки.ПечатнаяФормаГрафиковРаботы(СтрокаНачисления.ГрафикРаботы, Год(Объект.ДатаНачала));
КонецЕсли;
КонецЦикла;
Этот подход является архитектурно верным для ЗУП 3.1. Он не зависит от контекста формы, выполняется на сервере и гарантирует полную сохранность связей между начислениями сотрудников и их показателями.
Прежде чем приступать к написанию собственного программного кода для автоматического заполнения показателей, проанализируем возможности современных версий конфигурации «1С:Зарплата и управление персоналом».
Начиная с релизов 3.1.27.111 и 3.1.28.11, фирма «1С» добавила в документы «Премия» и «Разовое начисление» мощный стандартный инструмент — кнопку «Заполнить показатели». Во многих случаях этот функционал полностью исключает необходимость ручных программных доработок.
Возможности типового механизма:
Ctrl или Shift).Использование этого механизма снижает совокупную стоимость владения системой, так как типовая конфигурация остается на стандартной поддержке без необходимости постоянного обновления кастомного кода при выходе новых релизов от фирмы «1С».