В практике разработки на платформе 1С:Предприятие часто встречается ситуация: новый документ при проведении исправно записывает данные в регистры, но стоит пользователю создать документ копированием, как движения исчезают. При этом визуально документ заполнен верно, а система не выдает никаких ошибок. Разберем эту проблему на конкретном примере из практики разработки самописной конфигурации.
Рассмотрим типовой алгоритм, представленный в процедуре ОбработкаПроведения. Программист использует запрос для получения данных из табличных частей самого документа, чтобы затем на их основе сформировать записи в регистре накопления ПланПоБюджетуФинансированияПоПлатежам. На первый взгляд, логика прозрачна: данные выбираются из базы по ссылке текущего документа.
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
Отказ = СуммыППиПОСовпадают(); // Проверка логики
ДатаПроведения = ТекущаяДатаСеанса();
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| ПланФинансированияРасходыПоПлатежам.ФКР,
| ПланФинансированияРасходыПоПлатежам.ЭКР,
| СУММА(ПланФинансированияРасходыПоПлатежам.Январь) КАК Январь,
| ...
|ИЗ
| Документ.ИзмененияПланаФинансированияПоРасходам.ТЧРасходыПоПлатежам КАК ПланФинансированияРасходыПоПлатежам
|ГДЕ
| ПланФинансированияРасходыПоПлатежам.Ссылка = &Ссылка
| И ПланФинансированияРасходыПоПлатежам.Январь + ... + ПланФинансированияРасходыПоПлатежам.Декабрь <> 0";
Запрос.УстановитьПараметр("Ссылка", Ссылка);
ВыборкаПоПлатежам = Запрос.Выполнить().Выбрать();
Движения.ПланПоБюджетуФинансированияПоПлатежам.Записывать = Истина;
Пока ВыборкаПоПлатежам.Следующий() Цикл
// Формирование движений
Движение = Движения.ПланПоБюджетуФинансированияПоПлатежам.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
// ... заполнение остальных полей
КонецЦикла;
КонецПроцедуры
Проанализируем условие ГДЕ в тексте запроса. Программист добавил проверку, чтобы строки с нулевой суммой за все месяцы не попадали в регистр: ПланФинансированияРасходыПоПлатежам.Январь + ... + ПланФинансированияРасходыПоПлатежам.Декабрь <> 0.
В чем заключается ловушка: При копировании документа пользователь может изменить только часть данных или оставить некоторые поля пустыми. Если в результате копирования или программной обработки в полях сумм оказывается значение NULL (что возможно при сложных соединениях или некорректном программном создании объекта), то результат математического сложения с NULL всегда будет NULL. В языке запросов 1С условие NULL <> 0 не является истинным, и такая строка будет проигнорирована запросом.
Даже если поля содержат физические нули, логика «не записывать пустые строки» может сработать против разработчика, если в скопированном документе данные еще не успели обновиться или пересчитаться в экранных формах. Проанализируем ситуацию: если запрос возвращает пустую выборку, цикл Пока ВыборкаПоПлатежам.Следующий() не выполнится ни разу, и коллекция движений останется пустой.
Это самая критичная архитектурная особенность проведения в 1С. Рассмотрим строку Запрос.УстановитьПараметр("Ссылка", Ссылка);. В этот момент запрос обращается к физической таблице на SQL-сервере: Документ.ИзмененияПланаФинансированияПоРасходам.ТЧРасходыПоПлатежам.
Разберем ситуацию по шагам:
Ссылке.Если к моменту выполнения запроса данные документа еще не были физически записаны в таблицу базы данных, запрос вернет пустой результат. Для новых (включая скопированные) документов крайне важно понимать режим записи. Если в конфигурации не используется автоматическое удаление движений или если процедура вызывается в контексте, где данные еще «не долетели» до таблиц БД, мы получим пустую выборку. Именно поэтому для получения данных «самого себя» в процедуре проведения рекомендуется передавать в запрос табличную часть объекта как параметр, а не обращаться к таблицам БД по ссылке.
Чтобы избежать зависимости от того, записан документ в базу или нет, проанализируем следующий подход. Мы выгружаем табличную часть во временную таблицу и работаем с ней. Это гарантирует, что запрос увидит те данные, которые пользователь видит на экране прямо сейчас, даже если это первый запуск проведения для скопированного объекта.
// Вместо обращения к Ссылка в запросе
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Данные ИЗ &ТЧ КАК ТЧ;
|ВЫБРАТЬ ... ИЗ ВТ_Данные КАК Данные ГДЕ ...";
Запрос.УстановитьПараметр("ТЧ", ЭтотОбъект.ТЧРасходыПоПлатежам.Выгрузить());
Выясним причину, связанную с предопределенными процедурами. В модуле объекта может существовать процедура ПриКопировании(ОбъектКопирования). Часто разработчики очищают в ней ключевые реквизиты, чтобы они не дублировались (например, дату, номер или специфические статусы). Если в этой процедуре случайно очищается реквизит, участвующий в связи параметров или измерениях регистров (например, Организация или ФКР), то запрос в ОбработкаПроведения также не найдет нужных соответствий.
Важный момент: Проверьте, не очищаются ли в вашем коде суммы или аналитика при копировании. Если в скопированном документе табличная часть оказывается пустой или суммы обнуляются программно, движений не будет по вполне законным причинам.
В тексте сообщения 2 мы видим строку: Отказ = СуммыППиПОСовпадают();. Рассмотрим подробнее, что здесь происходит. Это функция, которая, скорее всего, проверяет равенство сумм по платежам и обязательствам. Если в скопированном документе из-за особенностей округления или неполного переноса данных суммы не совпали хотя бы на копейку, функция вернет Истина в переменную Отказ.
В 1С, если в процедуре проведения Отказ = Истина, система прерывает транзакцию. Однако, если внутри функции СуммыППиПОСовпадают() не прописан вывод сообщения пользователю (Сообщить()), то внешне это выглядит так: вы нажали «Провести», форма закрылась или осталась открытой, но записи в регистрах не появились. Всегда проверяйте возвращаемые значения подобных проверок.
Подводя итог, если вы столкнулись с отсутствием движений у скопированного документа, выполните следующие шаги:
Выборка.Следующий() возвращает Истина. Если выборка пуста — проблема в условиях ГДЕ или в том, что данные еще не записаны в БД.<> 0. Если движения появились — значит, логика суммирования в запросе некорректна для скопированных данных.ЕСТЬNULL: В запросах всегда оборачивайте числовые поля в функцию ЕСТЬNULL(Поле, 0), особенно при сложении нескольких колонок.Соблюдение этих правил позволит сделать механизм проведения надежным и независимым от способа создания документа в системе.