Разберем типичную и крайне неприятную ошибку, возникающую в программе 1С:Зарплата и Управление Персоналом (начиная с релиза 3.1.38.92 и выше). При заполнении документа РегистрацияПереработок по конкретному сотруднику или списку сменщиков программа аварийно прерывает работу со следующим сообщением в стеке вызовов:
«Ошибка при вызове метода контекста (Выполнить) ... Ошибка во время выполнения встроенного языка по причине: {(31, 2)}: Таблица не найдена "ВТНормоВремяПоУчетуВремениСУчетомПредела"».
Выясним причину этой ошибки, рассмотрим механику работы с менеджером временных таблиц, проведем диагностику в отладчике и научимся правильно адаптировать доработки, чтобы обновления конфигурации не ломали расчет заработной платы.
Проанализируем цепочку формирования данных. В типовой конфигурации документ РегистрацияПереработок обращается к функции ПоказателиСуммированногоУчетаСотрудниковЗаПериод общего модуля РасчетЗарплатыРасширенный. Внутри этого модуля строится сложный пакетный запрос, использующий общий МенеджерВременныхТаблиц.
Подготовка временных таблиц по отработанному времени и нормам делегируется процедуре ДополнитьЗапросВТПоказателейПоУчетуВремени. В обновленном релизе поставщика схема пакета запросов изменилась: для расчета предельных объемов переработок разработчики добавили новую промежуточную таблицу ВТНормоВремяПоУчетуВремениСУчетомПредела. Последующие запросы пакета жестко рассчитывают на то, что данная таблица уже помещена в МенеджерВременныхТаблиц.
Если в информационной базе подключено расширение конфигурации, где процедура ДополнитьЗапросВТПоказателейПоУчетуВремени была переопределена разработчиками с помощью директивы &Вместо, то при обновлении основной конфигурации расширение продолжает подменять типовой код старой версией алгоритма. В результате старый код генерирует только таблицу ВТНормоВремяПоУчетуВремени, а новая таблица с учетом предела попросту не создается. В момент вызова Запрос.Выполнить() синтаксический анализатор платформы выдает исключение об отсутствии требуемой временной таблицы в МВТ.
Разберем по шагам, как выявить источник ошибки и убедиться, что виновником является устаревшее расширение:
/NoExtensions (или снимем активность расширений в конфигураторе). Если при таком запуске документ заполняется штатно, источник ошибки на 100% локализован в одном из расширений.РасчетЗарплатыРасширенный, перейдем к процедуре ПоказателиСуммированногоУчетаСотрудниковЗаПериод на строку вызова запроса (в районе строки 13956):
РезультатЗапроса = Запрос.Выполнить();
Запустим документ РегистрацияПереработок и добавим сотрудника с суммированным учетом времени.
Запрос.МенеджерВременныхТаблиц.Таблицы
Развернем список созданных таблиц. В корректно работающей базе среди прочих таблиц должна присутствовать ВТНормоВремяПоУчетуВремениСУчетомПредела. Если ее в списке нет, значит, предшествующая генерация запроса была нарушена.ДополнитьЗапросВТПоказателейПоУчетуВремени в коде общего модуля, нажмем правую кнопку мыши и выберем пункт меню «Показать переопределения метода в расширениях» либо выполним глобальный поиск по конфигурации и расширениям. Это покажет, какое именно расширение перехватывает управление.Обратите внимание: если отладчик не заходит внутрь модуля расширения, проверьте свойства расширения в дереве метаданных. При установленном флажке «Безопасный режим» платформа может блокировать отладку серверных модулей расширения в зависимости от настроек сеанса.
Посмотрим на фрагмент пакета запроса, который формируется в старой и новой редакциях процедуры ДополнитьЗапросВТПоказателейПоУчетуВремени. Старый вариант запроса, оставшийся в заброшенном расширении, заканчивался объединением графиков и календарей:
ВЫБРАТЬ
ЕСТЬNULL(ВТДанныеГрафика.Сотрудник, ВТДанныеПроизводственногоКалендаря.Сотрудник) КАК Сотрудник,
ЕСТЬNULL(ВТДанныеГрафика.НормаЧасов, 0) + ЕСТЬNULL(ВТДанныеПроизводственногоКалендаря.НормаЧасов, 0) КАК НормаЧасов
ПОМЕСТИТЬ ВТНормоВремяПоУчетуВремени
ИЗ
ВТДанныеПроизводственногоКалендаря КАК ВТДанныеПроизводственногоКалендаря
ПОЛНОЕ СОЕДИНЕНИЕ ВТДанныеГрафика КАК ВТДанныеГрафика
ПО ВТДанныеПроизводственногоКалендаря.Сотрудник = ВТДанныеГрафика.Сотрудник;
////////////////////////////////////////////////////////////////////////////////
УНИЧТОЖИТЬ ВТУчетРабочегоВремениСГрафиками
В обновленном релизе ЗУП после создания таблицы ВТНормоВремяПоУчетуВремени типовой алгоритм добавляет дополнительную ветку запроса, которая учитывает пределы переработок и формирует ВТНормоВремяПоУчетуВремениСУчетомПредела. Поскольку директива &Вместо полностью отсекает типовой код, новая таблица в менеджер не попадает вовсе.
Рассмотрим, как правильно устранить проблему и защитить информационную базу от подобных инцидентов при будущих обновлениях.
Если доработка в расширении действительно необходима, откроем модуль расширения и перенесем измененный текст запроса из актуального типового релиза ЗУП в расширенную процедуру. Обязательно убедимся, что все новые временные таблицы, присутствующие в типовом пакете (включая ВТНормоВремяПоУчетуВремениСУчетомПредела), корректно формируются и помещаются в МенеджерВременныхТаблиц с сохранением всех необходимых полей и группировок.
Проанализируем архитектурную ошибку применения директивы &Вместо. Использование &Вместо на процедурах расчета зарплаты крайне опасно: при выходе новых релизов или установке срочных патчей (исправлений EF) расширение продолжает слепо подменять алгоритм старым кодом. Система не предупреждает о конфликте, что приводит к незаметным ошибкам в расчетах или падению запросов.
Перепишем метод с использованием директивы &ИзменениеИКонтроль:
&Вместо на &ИзменениеИКонтроль("ДополнитьЗапросВТПоказателейПоУчетуВремени").#Вставить и #Удалить.Благодаря этому механизму при следующем обновлении типовой конфигурации платформа 1С автоматически выполнит сверку исходного метода. Если код метода в ЗУП снова изменится, расширение выдаст предупреждение о невозможности применения, защитив базу от некорректных расчетных данных и скрытых ошибок.
Если расширение уже обновлено или отключено, но ошибка продолжает воспроизводиться у отдельных пользователей, выполним очистку кэшированных данных:
%LOCALAPPDATA%\1C\1cv8 и %APPDATA%\1C\1cv8).