Рассмотрим распространенную и коварную ситуацию при разработке в платформе 1С: программный код внешне отрабатывает абсолютно успешно, отладчик проходит по строкам с вызовом метода Записать(), исключений и ошибок в интерактивном режиме не возникает, однако при открытии формы элемента или списка справочника измененные данные отсутствуют.
С подобной ситуацией часто сталкиваются программисты при создании обработок загрузки номенклатуры, контрагентов или прайс-листов из внешних файлов (Excel, CSV, XML). Проанализируем причины такого поведения платформы 1С и разберем, как правильно выстроить работу с транзакциями и записью объектов.
Выясним главную причину, по которой модифицированный или созданный объект справочника (например, СправочникОбъект.Номенклатура) не сохраняет внесенные изменения в физических таблицах базы данных. В платформе 1С манипуляции с данными происходят в рамках механизмов СУБД, управлением которыми выступают транзакции.
Если в коде обработки вызывается метод НачатьТранзакцию(), платформа открывает явную транзакцию. В случае, если до выхода из процедуры или функции не был явным образом вызван метод ЗафиксироватьТранзакцию(), платформа 1С автоматически выполняет неявный откат (ROLLBACK) всех изменений, произведенных в базе данных с момента старта транзакции. При этом метод Записать() объекта отрабатывает без вызова ошибки в точке выполнения, так как на момент вызова оператора запись во временный контекст транзакции прошла успешно.
Проанализируем фрагмент кода, содержащий эту распространенную ошибку. Код выполняет чтение таблицы Excel через COM-объект, заполняет локальную таблицу значений и обновляет данные элементов справочника Номенклатура.
Посмотрим на ошибочный вариант построения процедуры:
// Открываем COM-соединение с Excel
Excel = Новый COMОбъект("Excel.Application");
ОткрытьФайл = Excel.WorkBooks.Open(ПутьКФайлу);
ОткрытьФайл = ОткрытьФайл.WorkSheets(1);
xlCellTypeLastCell = 11;
ExcelПоследняяСтрока = ОткрытьФайл.Cells.SpecialCells(xlCellTypeLastCell).Row;
ТзД = Новый ТаблицаЗначений;
ТзД.Колонки.Добавить("КодИнструмента");
ТзД.Колонки.Добавить("Инструмент");
// ОШИБКА: Открываем транзакцию перед циклом чтения
НачатьТранзакцию();
Для Строка = 2 По ExcelПоследняяСтрока Цикл
СтрокаТ = ТзД.Добавить();
СтрокаТ.КодИнструмента = СокрЛП(ОткрытьФайл.Cells(Строка,1).Value);
СтрокаТ.Инструмент = СокрЛП(ОткрытьФайл.Cells(Строка,4).Value);
КонецЦикла;
Excel.WorkBooks.Close();
Excel.Quit();
// Попытка модифицировать номенклатуру
л_Ном = Справочники.Номенклатура.НайтиПоКоду("72458");
Если Не л_Ном.Пустая() Тогда
Ном = л_Ном.ПолучитьОбъект();
СтрокаНом = Ном.ТорговыеНазвания.Добавить();
СтрокаНом.Номенклатура = л_Ном;
Ном.Записать(); // Метод отработает, но изменения откатятся!
КонецЕсли;
// ОШИБКА: ЗафиксироватьТранзакцию() отсутствуют в конце процедуры!
В приведенном выше примере метод НачатьТранзакцию() был случайно скопирован при вставке кода чтения файла. Из-за отсутствия ЗафиксироватьТранзакцию() в конце процедуры все записи объектов, выполненные методом Ном.Записать(), были аннулированы платформой сразу после завершения выполнения процедуры.
Для предотвращения потерь данных и корректной обработки ошибок всегда используем безопасный шаблон записи с обработкой исключений и явной фиксацией транзакции. Разберем правильное написание данного участка кода:
НачатьТранзакцию();
Попытка
Для Каждого СтрокаТ Из ТзД Цикл
л_Код = СокрЛП(СтрЗаменить(СтрокаТ.КодИнструмента, Символы.НПП, ""));
Если ПустаяСтрока(л_Код) Тогда
Продолжить;
КонецЕсли;
л_Номенклатура = Справочники.Номенклатура.НайтиПоКоду(л_Код);
Если л_Номенклатура.Пустая() Тогда
// Создание нового элемента справочника
НовыйОбъект = Справочники.Номенклатура.СоздатьЭлемент();
НовыйОбъект.Код = л_Код;
НовыйОбъект.Наименование = СтрокаТ.Инструмент;
НовыйОбъект.Записать();
л_Номенклатура = НовыйОбъект.Ссылка;
КонецЕсли;
// Изменение существующего объекта
л_Объект = л_Номенклатура.ПолучитьОбъект();
л_Объект.НаименованиеПолное = СокрЛП(л_Объект.Наименование);
// Модификация табличной части
НоваяСтрока = л_Объект.ТорговыеНазвания.Добавить();
НоваяСтрока.Номенклатура = л_Номенклатура;
// Фиксируем изменения конкретного объекта
л_Объект.Записать();
КонецЦикла;
// Обязательный вызов фиксации транзакции
ЗафиксироватьТранзакцию();
Сообщить("Данные успешно сохранены!");
Исключение
// В случае ошибки отменяем транзакцию и выводим описание
ОтменитьТранзакцию();
ИнформацияОбОшибке = ИнформацияОбОшибке();
Сообщить("Ошибка при обработке данных: " + ИнформацияОбОшибке.Описание);
КонецПопытки;
Рассмотрим дополнительные инструменты и подходы, которые помогают быстро диагностировать проблемы при программном изменении элементов справочников:
ТранзакцияАктивна() можно проверить, находится ли исполняемый код внутри незавершенного транзакционного блока. Это предотвращает случайное создание незафиксированных транзакций.ОбменДанными.Загрузка:
л_Объект = л_Ссылка.ПолучитьОбъект();
л_Объект.ОбменДанными.Загрузка = Истина;
л_Объект.Записать();
Excel.Quit() и обнуляйте переменную Excel = Неопределено, чтобы процесс EXCEL.EXE не оставался в оперативной памяти сервера или клиента.ОповеститьОбИзменении(СсылкаНаОбъект) для принудительного обновления кэша форм.