В практике разработки на платформе 1С:Предприятие 8.3 часто встречается ситуация, когда идеально работающий в файловой базе код «отказывается» выполнять свои функции после переноса на клиент-серверный вариант (SQL). Особенно остро эта проблема встает, когда на сервере по техническим причинам отключена отладка, и мы не можем пошагово проследить выполнение алгоритма. Проанализируем типичный случай: автоматическое заполнение подразделений в табличной части документа.
Рассмотрим код, который вызывает затруднения. На клиенте инициируется процесс, который вызывает серверную процедуру для обхода строк табличной части НачисленнаяЗарплатаИВзносы:
&НаСервере
Процедура ЗаполнитьПодразделение()
Для Каждого Стр Из Объект.НачисленнаяЗарплатаИВзносы Цикл
Если ТипЗнч(Стр.СпособОтраженияЗарплатыВБухучете.АналитикаРасходов) = Тип("СправочникСсылка.СтруктураПредприятия") Тогда
Стр.ПодразделениеПредприятия = Стр.СпособОтраженияЗарплатыВБухучете.АналитикаРасходов;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Проблема заключается в том, что в файловой базе условие Если ТипЗнч(...) выполняется, и реквизиты заполняются, а в клиент-серверной — цикл проходит, ошибок нет, но данные в Объект остаются прежними. Выясним причины такого поведения и найдем решения.
Проанализируем конструкцию Стр.СпособОтраженияЗарплатыВБухучете.АналитикаРасходов. В клиент-серверном режиме обращение «через точку» к реквизитам ссылочного типа работает иначе, чем в файловом. При таком обращении система неявно выполняет SQL-запрос к базе данных.
Если у текущего пользователя настроены ограничения прав на уровне записей (RLS), то при обращении к промежуточному объекту (СпособОтраженияЗарплатыВБухучете) или его реквизиту (АналитикаРасходов), система может вернуть Неопределено или пустую ссылку, не вызывая исключения. В файловой базе RLS часто отрабатывает менее строго или может быть вовсе не настроен.
Что сделать: Попробуйте выполнить этот же код под пользователем с полными правами. Если под полными правами всё заработает — проблема в настройках доступа к справочникам, участвующим в цепочке обращения.
Когда мы проверяем ТипЗнч(Стр.СпособОтраженияЗарплатыВБухучете.АналитикаРасходов), мы полагаемся на то, что платформа корректно определит тип значения, хранящегося в базе. В клиент-серверном варианте, если поле АналитикаРасходов имеет составной тип, SQL-сервер может хранить там значения, которые 1С интерпретирует как «битые» ссылки или специфические пустые значения, если объект был удален или некорректно записан.
Рассмотрим более надежный способ проверки на сервере:
&НаСервере
Процедура ЗаполнитьПодразделение()
Для Каждого Стр Из Объект.НачисленнаяЗарплатаИВзносы Цикл
// Сначала получаем значение во временную переменную, чтобы избежать многократных обращений через точку
ТекАналитика = Стр.СпособОтраженияЗарплатыВБухучете.АналитикаРасходов;
// Проверяем тип через ссылку, а не через прямое обращение
Если Справочники.СтруктураПредприятия.ТипВсеСсылки().СодержитТип(ТипЗнч(ТекАналитика)) Тогда
Если ЗначениеЗаполнено(ТекАналитика) Тогда
Стр.ПодразделениеПредприятия = ТекАналитика;
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Если проблема действительно в правах доступа (RLS), а бизнес-логика требует заполнения этих данных для всех пользователей, мы можем принудительно поднять права на время выполнения серверного кода. Выясним, поможет ли нам УстановитьПривилегированныйРежим.
&НаСервере
Процедура ЗаполнитьПодразделение()
УстановитьПривилегированныйРежим(Истина);
Для Каждого Стр Из Объект.НачисленнаяЗарплатаИВзносы Цикл
// Теперь чтение АналитикаРасходов пройдет успешно даже при наличии ограничений у пользователя
Стр.ПодразделениеПредприятия = Стр.СпособОтраженияЗарплатыВБухучете.АналитикаРасходов;
КонецЦикла;
УстановитьПривилегированныйРежим(Ложь);
КонецПроцедуры
Поскольку отладка на сервере недоступна, применим метод, предложенный участниками сообщества: намеренное создание ошибки. Это позволит нам понять, доходит ли поток выполнения до нужной строки или условие Если всегда возвращает Ложь.
Добавим в код деление на ноль:
Для Каждого Стр Из Объект.НачисленнаяЗарплатаИВзносы Цикл
А = 1 / 0; // Если выполнение доходит сюда, база выдаст ошибку
Если ТипЗнч(Стр.СпособОтраженияЗарплатыВБухучете.АналитикаРасходов) = Тип("СправочникСсылка.СтруктураПредприятия") Тогда
Б = 1 / 0; // Если выполнение доходит сюда, значит условие истинно
Стр.ПодразделениеПредприятия = Стр.СпособОтраженияЗарплатыВБухучете.АналитикаРасходов;
КонецЕсли;
КонецЦикла;
Если при запуске процедуры вы видите окно с ошибкой «Деление на 0», значит код исполняется. Если ошибки нет — процедура либо не вызывается, либо цикл по коллекции НачисленнаяЗарплатаИВзносы пуст.
В клиент-серверном режиме иногда возникает рассинхронизация между данными в памяти сервера и тем, что отображается на клиенте. Если перед вызовом ЗаполнитьПодразделение() вызывалась другая процедура (например, ЗаполнитьТаблицуФрагмент), которая могла изменить структуру таблицы или ее состояние, сервер может «запутаться» в индексах строк.
Рекомендуется: объединить заполнение данных и проставление подразделений в один серверный вызов. Это гарантирует, что все изменения произойдут в рамках одного обращения к серверу и будут переданы на клиент единым пакетом (ДанныеФормы).
Не стоит забывать про банальную, но частую причину — кэш метаданных. В клиент-серверном режиме сервер 1С кэширует структуру объектов. Если вы недавно добавили реквизит ПодразделениеПредприятия, сервер может продолжать работать со старой версией метаданных, где этого поля еще «не существует» для программного обращения, хотя в интерфейсе оно уже есть.
snccntx и reg_1541 (за исключением файлов конфигурации сервера).Подведем итог: чаще всего проблема кроется в неявном получении NULL при обращении через точку в условиях RLS или в неверном сравнении типов для составных полей. Используя проверку через ЗначениеЗаполнено() и Привилегированный режим, вы с высокой вероятностью решите проблему работы кода в клиент-серверной базе.