Как исправить ошибку «Поле объекта недоступно для записи» при изменении реквизитов в 1С

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

При написании обработок проведения или заполнении документов разработчики часто сталкиваются с ошибкой «Поле объекта недоступно для записи». Давайте подробно разберем, почему возникает эта ошибка, проанализируем реальный пример из практики, исправим неоптимальный код и выясним, почему простое техническое решение в лоб может полностью разрушить учет в базе данных.

Выясняем причину ошибки: Ссылка против Объекта

Для начала проанализируем исходный фрагмент кода, который вызвал сбой:


Если ДокументОснование.Дата > Дата("20190101") Тогда 
    Для Каждого Строка Из ДокументОснование.Товары Цикл
        Если Строка.СтавкаНДС = Перечисления.СтавкиНДС.НДС20 Тогда 
            Для Каждого СтрокиДок Из Материалы Цикл 
                Если СтрокиДок.НоменклатурнаяГруппа.СтавкаНДС = Перечисления.СтавкиНДС.НДС18 Тогда
                    
                    // Ошибка возникает на следующей строке:
                    СтрокиДок.НоменклатурнаяГруппа.СтавкаНДС = Перечисления.СтавкиНДС.НДС20;    
                    
                КонецЕсли;
            КонецЦикла;
        КонецЕсли;    
    КонецЦикла;
КонецЕсли;

Ошибка происходит в строке СтрокиДок.НоменклатурнаяГруппа.СтавкаНДС = Перечисления.СтавкиНДС.НДС20;.

Выясним причину этого поведения платформы «1С:Предприятие». Реквизит НоменклатурнаяГруппа в строке табличной части документа хранит тип данных СправочникСсылка.НоменклатурныеГруппы. В архитектуре 1С ссылки служат исключительно для идентификации элементов в базе данных и получения их значений «на чтение».

Важное правило разработки в 1С: Реквизиты объекта, полученные через точку от Ссылки (например, СсылкаНаСправочник.Реквизит), доступны только для чтения. Платформа физически блокирует попытки прямой записи в базу данных через ссылки для обеспечения целостности транзакций.

Решение 1: Быстрое техническое исправление ошибки (в лоб)

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

Разберем по шагам, как переписать этот участок кода с использованием метода ПолучитьОбъект():


// 1. Получаем объект справочника из ссылки
ОбъектНоменклатурнаяГруппа = СтрокиДок.НоменклатурнаяГруппа.ПолучитьОбъект();

Если ОбъектНоменклатурнаяГруппа <> Неопределено Тогда
    // 2. Меняем значение нужного реквизита
    ОбъектНоменклатурнаяГруппа.СтавкаНДС = Перечисления.СтавкиНДС.НДС20;
    
    // 3. Отключаем лишние проверки для ускорения записи (рекомендуется для обработок)
    ОбъектНоменклатурнаяГруппа.ОбменДанными.Загрузка = Истина;
    
    // 4. Записываем изменения в базу данных
    ОбъектНоменклатурнаяГруппа.Записать();
КонецЕсли;

Этот код успешно отработает без ошибок. Однако, прежде чем внедрять такое решение, давайте детально проанализируем бизнес-логику этой операции. Здесь кроется критическая архитектурная ловушка.

Критическая архитектурная ошибка: Искажение исторических данных

Рассмотрим, к чему приведет выполнение кода из Решения 1 на практике. Справочник НоменклатурныеГруппы является общим для всей информационной базы. Элементы этого справочника используются во множестве документов за прошлые периоды.

Если мы программно найдем элемент справочника (например, группу «Мебель») со ставкой НДС 18% и заменим в ней ставку на 20%, то:

  1. Эта группа изменится глобально во всей базе данных.
  2. Во всех документах прошлых лет (2018 год и ранее), где была указана эта группа, ставка НДС автоматически «превратится» в 20%.
  3. При первой же налоговой проверке или попытке перепровести закрытый период программа выдаст грубые ошибки, а финансовые показатели и отчетность по НДС за прошлые периоды будут безвозвратно искажены.

Вывод: Изменять реквизиты непосредственно внутри существующих элементов справочников для изменения логики текущих документов категорически запрещено!

Решение 2: Правильная замена ссылки в документе (Архитектурно верный путь)

Вместо того чтобы изменять сам элемент справочника, мы должны подставить в строку табличной части документа другую ссылку. Нам необходимо заранее создать в справочнике новую номенклатурную группу (например, «Мебель (НДС 20%)») и подставить ее в документ вместо старой.

Давайте посмотрим на пример реализации такого подхода:


// Предположим, мы заранее нашли или создали правильную номенклатурную группу
СсылкаНаНовуюГруппу = Справочники.НоменклатурныеГруппы.НайтиПоНаименованию("Номенклатурная группа НДС 20%");

Если ЗначениеЗаполнено(СсылкаНаНовуюГруппу) Тогда
    // Мы перезаписываем реквизит строки ТЧ документа, а не сам справочник!
    СтрокиДок.НоменклатурнаяГруппа = СсылкаНаНовуюГруппу;
КонецЕсли;

В этом случае старые документы за прошлые годы сохранят ссылку на старую группу (с НДС 18%), а новые документы получат ссылку на новую группу (с НДС 20%). Баланс и отчетность не пострадают.

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

Теперь обратим внимание на качество кода нашего исходного примера. Мы видим классическую проблему производительности — вложенные циклы (сложность алгоритма $O(N \times M)$).


Для Каждого Строка Из ДокументОснование.Товары Цикл
    Если Строка.СтавкаНДС = Перечисления.СтавкиНДС.НДС20 Тогда 
        Для Каждого СтрокиДок Из Материалы Цикл 
            // Полезное действие
        КонецЦикла;
    КонецЕсли;    
КонецЦикла;

Если в документе-основании содержится 100 строк с товарами и у большинства из них ставка НДС равна 20%, программа будет обходить всю табличную часть Материалы (в которой тоже может быть 100 строк) ровно 100 раз. В итоге мы получим 10 000 итераций цикла на ровном месте!

Давайте оптимизируем этот алгоритм. Нам не нужно перебирать все товары построчно. Нам достаточно лишь один раз убедиться, что в документе-основании есть хотя бы один товар со ставкой 20%. Сделать это можно моментально с помощью метода табличной части Найти() или НайтиСтроки().

Рассмотрим оптимизированный и безопасный вариант кода:


Если ДокументОснование.Дата > Дата("20190101") Тогда 
    
    // Структура для быстрого поиска строки с НДС 20%
    ПараметрыПоиска = Новый Структура;
    ПараметрыПоиска.Вставить("СтавкаНДС", Перечисления.СтавкиНДС.НДС20);
    
    НайденныеСтроки = ДокументОснование.Товары.НайтиСтроки(ПараметрыПоиска);
    
    // Если в документе-основании есть хотя бы одна строка со ставкой 20%
    Если НайденныеСтроки.Количество() > 0 Тогда 
        
        // Получаем ссылки на старую и новую группы заранее, вне циклов
        СтараяГруппа = Справочники.НоменклатурныеГруппы.НайтиПоНаименованию("Номенклатурная группа 18%");
        НоваяГруппа  = Справочники.НоменклатурныеГруппы.НайтиПоНаименованию("Номенклатурная группа 20%");
        
        Если ЗначениеЗаполнено(НоваяГруппа) Тогда
            // Проходим по материалам ОДИН раз
            Для Каждого СтрокаМатериалы Из Материалы Цикл 
                Если СтрокаМатериалы.НоменклатурнаяГруппа = СтараяГруппа Тогда
                    // Производим безопасную замену ссылки в документе
                    СтрокаМатериалы.НоменклатурнаяГруппа = НоваяГруппа;
                КонецЕсли;
            КонецЦикла;
        КонецЕсли;
        
    КонецЕсли;
    
КонецЕсли;

Использование штатных инструментов 1С

Если вам требуется массово изменить реквизиты или ставки НДС в документах и справочниках за определенный период, писать собственный код в модулях конфигурации требуется далеко не всегда. Фирма «1С» предоставляет мощные готовые инструменты:

  1. Групповое изменение реквизитов: Стандартная обработка, доступная в разделе «Администрирование» -> «Обслуживание» -> «Корректировка данных». Она позволяет отобрать нужные документы по сложным фильтрам и массово заменить в их табличных частях старые номенклатурные группы на новые, автоматически запустив корректное проведение документов.
  2. Помощники перехода на новые ставки НДС: В типовых конфигурациях (например, 1С:Бухгалтерия или 1С:ERP) встроены специальные обработки для корректного перехода со ставки 18% на 20%, разработанные с учетом всех нюансов законодательства. Они не портят исторические данные и обновляют только актуальные настройки учета.

Используйте рассмотренные нами приемы работы с объектами базы данных, пишите производительный код без вложенных циклов и всегда помните о сохранности исторических данных в информационных базах ваших пользователей!

← На главную