При разработке в системе «1С:Предприятие» одной из базовых операций является программное создание документов. Логика работы платформы на первый взгляд прозрачна: пока мы не вызвали метод Записать(), документ существует исключительно в оперативной памяти компьютера в виде объекта и не может появиться в таблицах базы данных СУБД. Однако на практике разработчики часто сталкиваются с парадоксальной ситуацией: документ создается программно, заполняется, на строку записи устанавливается жесткое условие (которое не должно выполняться), но после работы алгоритма документ всё равно сохраняется в базе данных.
Давайте подробно проанализируем, почему так происходит, какие подводные камни скрывает в себе платформа 1С и как провести комплексный аудит кода, чтобы локализовать и устранить эту проблему.
Для начала зафиксируем фундаментальное правило работы платформы: вызов метода СоздатьДокумент() у менеджера документа возвращает новый, еще не записанный объект. На данном этапе у него нет уникального идентификатора в СУБД (UUID равен "00000000-0000-0000-0000-000000000000" или является ссылкой нового), и в базе данных физически не существует соответствующей строки.
Посмотрим на классический алгоритм, вызывающий вопросы у разработчиков:
// Создаем объект в оперативной памяти
ДокументНормыВыдачиСИЗ = Документы.НормыВыдачиСИЗ.СоздатьДокумент();
// Заполняем реквизиты документа
ДокументНормыВыдачиСИЗ.Дата = ТекущаяДата();
ДокументНормыВыдачиСИЗ.Организация = Справочники.Организации.ПустаяСсылка();
// Проверяем условие для записи
Если Отказ = Ложь Тогда
ДокументНормыВыдачиСИЗ.Записать();
КонецЕсли;
Если переменная Отказ принимает значение Истина, ветка с методом Записать() выполняться не должна. Если документ всё же записывается, значит, в нашей логике или окружении есть скрытый фактор. Разберем по шагам все возможные причины.
Это одна из самых частых иллюзий программиста 1С. Вы ставите точку останова на строке Если Отказ = Ложь Тогда, запускаете отладку, шаг за шагом нажимаете клавиши F10 или F11. Отладчик перескакивает через строку ДокументНормыВыдачиСИЗ.Записать() напрямую к КонецЕсли. Вы уверены, что метод записи не вызывался, но документ всё равно появляется в списке.
Выясним причину этого поведения:
-debug, точки останова внутри серверных процедур просто не будут срабатывать. При этом визуально в конфигураторе отладка может работать для клиентских процедур.Решение: Убедитесь, что в строке запуска службы сервера 1С (в реестре Windows или конфигурационном файле под Linux) прописан параметр запуска отладки. Проверьте тип вызова процедуры — если это фоновое задание, то для его отладки в параметрах конфигуратора должен быть включен автоматический запуск отладки фоновых заданий.
Проанализируем конструкцию сравнения Если Отказ = Ложь Тогда. В 1С динамическая типизация, что часто приводит к логическим ловушкам:
Отказ изначально не была явно объявлена или была передана как Неопределено, то результатом сравнения Неопределено = Ложь будет значение Ложь, и запись производиться не должна.Отказ было записано число 0 (например, при расчете количества ошибок или неуспешных проверок), то сравнение 0 = Ложь в 1С вернет Истина! Платформа неявно приведет число к булеву типу, и условие записи сработает.Рассмотрим более безопасный и лаконичный вариант написания этого условия:
// Избегайте прямого сравнения с Ложь, пишите отрицание явно
Если Не Отказ Тогда
ДокументНормыВыдачиСИЗ.Записать();
КонецЕсли;
Такой подход минимизирует риски ошибок типизации, но перед этим обязательно нужно гарантировать, что переменная Отказ имеет тип Булево.
Разработчик может быть абсолютно уверен, что запись происходит только в одном месте созданной им процедуры. Но это часто оказывается не так. Как проверить гипотезу о сторонней записи?
Воспользуемся пошаговой трассировкой. Когда отладчик останавливается на строке КонецПроцедуры, ни в коем случае не нажимайте клавишу продолжения выполнения (F5). Нажмите клавишу F11 (Шагнуть внутрь). Разберем, куда может уйти управление дальше:
ДокументНормыВыдачиСИЗ) мог быть передан по ссылке (как параметр) в другую процедуру или функцию, которая выполняется далее по стеку вызовов. Внутри той внешней процедуры разработчик мог вызвать метод Записать(), даже не подозревая об этом.ЗафиксироватьТранзакцию() или при автоматическом завершении транзакции проведения.Современные конфигурации 1С построены на базе БСП (Библиотеки стандартных подсистем). В них активно используются подписки на события объектов. Проанализируем этот аспект:
Если в процессе создания и заполнения документа вы вызываете методы БСП (например, заполняете штрихкоды, регистрируете документ в дополнительных свойствах или отправляете в подсистему версионирования объектов), эти универсальные механизмы могут принудительно инициировать запись объекта.
Зачем они это делают? Для привязки связанных сущностей (например, файлов, записей регистров сведений, истории изменений) платформе физически необходима ссылка на этот документ в базе данных. Метод УстановитьСсылкуНового() решает эту проблему лишь частично. Поэтому многие функции БСП перед выполнением своей логики принудительно вызывают метод Записать(), чтобы получить постоянный уникальный идентификатор.
Если Источник.ЭтоНовый() Тогда
Источник.Записать();
КонецЕсли;
В результате ваш объект оказывается записан в БД "в обход" вашей основной логики ветвления.
Если вы столкнулись с проблемой нежелательного появления документа в базе данных, выполните следующие шаги для локализации проблемы:
.Записать( (обязательно с открывающей скобкой) с фильтром по имени вашего документа НормыВыдачиСИЗ. Это поможет найти все места, где данный документ может быть записан физически.Записать(). Платформа покажет точную строку кода и модуль, в котором произошла запись.ДокументОбъект. Обратите внимание на события ПередЗаписью, ПриЗаписи и ОбработкаПроведения. Проверьте логику внутри их обработчиков.Рассмотрим шаблон надежного кода для программного создания документа с детальной валидацией данных и транзакционным контролем. Этот подход гарантирует, что при возникновении любых ошибок или невыполнении условий документ не попадет в базу данных, а транзакция будет полностью отменена.
// Открываем явную транзакцию для безопасности данных
НачатьТранзакцию();
Попытка
НовыйДокумент = Документы.НормыВыдачиСИЗ.СоздатьДокумент();
// Заполнение реквизитов
НовыйДокумент.Дата = ТекущаяДата();
НовыйДокумент.Организация = Справочники.Организации.НайтиПоРеквизиту("Код", "000000001");
// Валидация заполнения ключевых данных
Отказ = Ложь;
Если Не ЗначениеЗаполнено(НовыйДокумент.Организация) Тогда
ОбщегоНазначенияКлиентСервер.СообщитьПользователю("Не удалось заполнить организацию!");
Отказ = Истина;
КонецЕсли;
// Проверка условий записи
Если Не Отказ Тогда
// Запись объекта в транзакции
НовыйДокумент.Записать(РежимЗаписиДокумента.Запись);
// Фиксируем транзакцию — данные реально попадают в СУБД
ЗафиксироватьТранзакцию();
Сообщить("Документ успешно создан: " + НовыйДокумент.Ссылка);
Иначе
// Отменяем транзакцию — база данных откатывается к исходному состоянию
ОтменитьТранзакцию();
Сообщить("Создание документа отменено по причине некорректных данных.");
КонецЕсли;
Исключение
// В случае непредвиденных ошибок (блокировки СУБД, ошибки проведения и т.д.)
ОтменитьТранзакцию();
ВызватьИсключение КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
КонецПопытки;
Использование конструкции НачатьТранзакцию() / ЗафиксироватьТранзакцию() / ОтменитьТранзакцию() — это профессиональный стандарт разработки в 1С. Он защищает базу данных от появления частично заполненных, некорректных или ошибочно созданных объектов, даже если во внешних модулях или подписках на события сработает неявная запись.