При работе в программных продуктах на платформе «1С:Предприятие 8» пользователи и администраторы периодически сталкиваются с системными сообщениями, которые блокируют проведение документов и останавливают закрытие отчетных периодов. Одной из самых распространенных является ошибка: «Запись не верна: период не может быть пустым». Особенно часто она проявляется при проведении сложных регламентных документов распределения затрат, таких как Косвенные расходы.
В этой статье мы подробно разберем природу возникновения данной ошибки, рассмотрим по шагам методы её локализации с помощью встроенного отладчика в конфигураторе и выясним, как правильно исправить код и данные, чтобы восстановить штатную работу системы.
С технической точки зрения, в платформе 1С существуют периодические регистры (это могут быть регистры сведений, регистры накопления или регистры бухгалтерии). У таких регистров есть обязательное системное поле — Период. Оно отвечает за временную привязку записи в базе данных. Платформа 1С жестко контролирует, чтобы у каждой строки, записываемой в периодический регистр, это поле было заполнено корректной датой.
Если при попытке записи набора записей в базу данных поле Период хотя бы в одной строке принимает значение пустой даты (01.01.0001 00:00:00), СУБД отвергает транзакцию, а платформа выбрасывает критическую ошибку проведения, прерывая операцию.
Давайте проанализируем ситуацию. Документ Косвенные расходы формирует множество движений в различных регистрах для распределения затрат по подразделениям, номенклатурным группам или статьям расходов. Ошибка указывает на конкретный документ (например, с номером 01.00.0000001). Мы можем решить эту проблему, разделив процесс диагностики на несколько уровней: от простых пользовательских проверок до детального анализа кода.
Прежде чем привлекать разработчика и открывать конфигуратор, выполним базовые проверки данных в режиме «1С:Предприятие»:
Если визуальный осмотр документа в пользовательском режиме не принес результатов, нам необходимо заглянуть внутрь процесса проведения. Для этого воспользуемся конфигуратором и механизмом отладки.
Рассмотрим пошагово, как поймать ошибку «на горячем»:
КосвенныеРасходы (или соответствующий документ в вашей конфигурации).ОбработкаПроведения. Именно внутри нее формируются все движения по регистрам.Для более быстрого поиска можно воспользоваться механизмом «Остановка по ошибке» в конфигураторе. Для этого перейдите в меню Отладка -> Остановка по ошибке..., нажмите кнопку «Добавить» и укажите текст ошибки или просто включите остановку при возникновении любого исключения. В этом случае отладчик автоматически прервет выполнение кода ровно в той строке, которая вызвала сбой.
Когда выполнение программы остановится на точке прерывания перед записью движений, откройте окно вычисления выражений с помощью комбинации клавиш Shift + F9. Нам нужно проанализировать коллекцию Движения документа. Например, если документ делает записи в регистр накопления ПрочиеРасходы, введите в поле выражения:
Движения.ПрочиеРасходы.Выгрузить()
Эта команда выгрузит все сформированные движения в таблицу значений, которую вы сможете детально изучить. Пролистайте таблицу и найдите колонку Период. Обратите внимание на строки, в которых дата отображается как 01.01.0001 00:00:00. Наличие таких строк — это и есть прямая причина падения системы.
В случаях, когда документ формирует тысячи строк движений, ручной перебор строк в окне вычисления выражений может занять слишком много времени. В этой ситуации на помощь приходят условные точки останова. Давайте разберем, как их настроить:
Движения.Записать(); или цикл заполнения).СтрокаДвижения, условие может выглядеть так:
СтрокаДвижения.Период = '00010101000000'
Теперь отладчик сработает и прервет выполнение программы только тогда, когда условие выполнится — то есть когда система обнаружит ту самую строку с пустой датой. Это сэкономит вам массу времени и позволит мгновенно определить контекст возникновения ошибки (какая именно статья затрат или аналитика привела к сбою).
Давайте проанализируем, почему поле Период может оставаться пустым при программном формировании движений. Ниже приведены основные сценарии и способы их решения.
Иногда при ручном формировании движений разработчики забывают инициализировать поле периода для каждой строки набора записей. Рассмотрим неправильный пример кода:
НоваяЗапись = Движения.ПрочиеРасходы.Добавить();
НоваяЗапись.Организация = Объект.Организация;
НоваяЗапись.СтатьяРасходов = Выборка.СтатьяРасходов;
НоваяЗапись.Сумма = Выборка.СуммаРасходов;
// Поле НоваяЗапись.Период осталось пустым!
Чтобы исправить этот код, достаточно явно присвоить периоду дату документа или дату хозяйственной операции:
НоваяЗапись = Движения.ПрочиеРасходы.Добавить();
НоваяЗапись.Период = ЭтотОбъект.Дата; // Заполняем период датой документа
НоваяЗапись.Организация = Объект.Организация;
НоваяЗапись.СтатьяРасходов = Выборка.СтатьяРасходов;
НоваяЗапись.Сумма = Выборка.СуммаРасходов;
Часто в целях оптимизации движения формируются сначала во временной таблице значений, а затем загружаются в регистр одной командой:
Движения.ПрочиеРасходы.Загрузить(ТаблицаДвижений);
Если в исходной таблице значений ТаблицаДвижений колонка Период отсутствует вовсе или содержит пустые даты, то при вызове метода Загрузить платформа заполнит движения значениями по умолчанию (пустой датой). Решение здесь заключается в проверке структуры таблицы ТаблицаДвижений. Перед загрузкой убедитесь, что колонка добавлена и заполнена:
// Проверяем наличие колонки Период в таблице значений
Если ТаблицаДвижений.Колонки.Найти("Период") = Неопределено Тогда
ТаблицаДвижений.Колонки.Добавить("Период", Новый ОписаниеТипов("Дата"));
КонецЕсли;
Для Каждого СтрокаТЗ Из ТаблицаДвижений Цикл
Если Не ЗначениеЗаполнено(СтрокаТЗ.Период) Тогда
СтрокаТЗ.Период = ЭтотОбъект.Дата;
КонецЕсли;
КонецЦикла;
Движения.ПрочиеРасходы.Загрузить(ТаблицаДвижений);
При распределении косвенных расходов алгоритмы проведения очень часто обращаются к базе данных с помощью сложных запросов. Запросы могут соединять табличную часть документа или обороты затрат с регистрами сведений (например, методами распределения, долями стоимости или коэффициентами). Для перехвата и анализа таких алгоритмов можно использовать инструмент для отладки динамических запросов с временными таблицами и параметрами.
Рассмотрим классический пример опасного запроса:
ВЫБРАТЬ
ВТ_Затраты.СтатьяРасходов КАК СтатьяРасходов,
ВТ_Затраты.Сумма КАК Сумма,
ТаблицаНастроек.ДатаНачалаРаспределения КАК ПериодРаспределения
ИЗ
ВТ_Затраты КАК ВТ_Затраты
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НастройкиРаспределенияРасходов КАК ТаблицаНастроек
ПО ВТ_Затраты.Подразделение = ТаблицаНастроек.Подразделение
Если для какого-то подразделения из таблицы ВТ_Затраты в регистре НастройкиРаспределенияРасходов отсутствует соответствующая запись настроек, то в результате левого соединения поле ТаблицаНастроек.ДатаНачалаРаспределения вернет значение NULL. В дальнейшем при обходе выборки этого запроса значение NULL неявно преобразуется в пустую дату (01.01.0001 00:00:00), которая и запишется в движения регистра.
Как исправить такой запрос? Нам необходимо обязательно обрабатывать возможные значения NULL с помощью функции ЕСТЬNULL. Изменим наш запрос следующим образом:
ВЫБРАТЬ
ВТ_Затраты.СтатьяРасходов КАК СтатьяРасходов,
ВТ_Затраты.Сумма КАК Сумма,
// Если дата настроек не найдена, подставляем дату документа
ЕСТЬNULL(ТаблицаНастроек.ДатаНачалаРаспределения, &ДатаДокумента) КАК ПериодРаспределения
ИЗ
ВТ_Затраты КАК ВТ_Затраты
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НастройкиРаспределенияРасходов КАК ТаблицаНастроек
ПО ВТ_Затраты.Подразделение = ТаблицаНастроек.Подразделение
Благодаря использованию функции ЕСТЬNULL, мы страхуем систему от появления пустых дат в результирующей выборке. В качестве резервного значения (второго параметра функции) рекомендуется передавать параметр &ДатаДокумента, который мы предварительно устанавливаем перед выполнением запроса:
Запрос.УстановитьПараметр("ДатаДокумента", ЭтотОбъект.Дата);
Чтобы минимизировать риски возникновения подобных ошибок в будущем, придерживайтесь следующих правил при разработке конфигураций и администрировании баз данных 1С:
ЕСТЬNULL в запросах проведения для полей, которые выбираются из правых (присоединяемых) таблиц при левом соединении.ПередЗаписью модуля документа.Применяя эти несложные подходы к отладке и написанию кода, вы сможете быстро локализовать проблему с незаполненным периодом и обеспечить бесперебойную работу механизмов распределения расходов в вашей системе.