Почему документ не заполняется на основании сразу после проведения и как это исправить?

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

При автоматизации бизнес-процессов в 1С разработчики нередко сталкиваются с ситуацией: пользователь создает и проводит документ, сразу же пытается ввести на его основании подчиненный документ, однако новый объект открывается абсолютно пустым. Если закрыть форму и повторить попытку ввода через 5–10 секунд, документ успешно заполняется всеми данными. При этом в тестовой базе или при отладке поведение может не воспроизводиться. Разберем детально механизм возникновения этой плавающей ошибки и рассмотрим технические способы ее устранения.

Проанализируем механику работы ввода на основании. Когда в конфигурации создается связанный документ, в модуле объекта срабатывает процедура ОбработкаЗаполнения. Часто для заполнения табличных частей разработчики обращаются не к исходному документу-ссылке напрямую, а запрашивают виртуальные таблицы регистров накопления или сведений (например, остатки товаров по партиям, взаиморасчеты или срез последних цен), которые сформировал документ-основание при проведении.

Причина 1. Рассинхронизация системного времени между клиентом и сервером

В базах на обычных формах (толстый клиент) код нередко выполняется на стороне рабочей станции пользователя. Если для передачи параметра периода в запрос используется функция ТекущаяДата(), возвращается локальное системное время клиентского компьютера, а не сервера 1С или СУБД.

Представим сценарий:

  1. На сервере время составляет 12:00:15. Пользователь проводит документ-основание, и его движения записываются в базу с этой отметкой времени.
  2. Часы на компьютере пользователя отстают на 10 секунд — локальное время 12:00:05.
  3. Пользователь сразу нажимает кнопку создания подчиненного документа. В процедуре ОбработкаЗаполнения выполняется запрос к виртуальной таблице остатков на момент ТекущаяДата() (то есть на 12:00:05).
  4. Поскольку движения документа-основания в базе зарегистрированы более поздним моментом времени (12:00:15), запрос к регистру остатков на 12:00:05 физически не видит эти записи и возвращает пустой результат.
  5. Спустя 10 секунд часы рабочей станции доходят до отметки 12:00:15, и при повторной попытке данные наконец начинают считываться корректно.

Способ устранения 1. Настройка синхронизации времени и переход на серверные функции

Разберем по шагам, как исключить влияние рассинхронизации часов:

  1. Настроим единую службу синхронизации времени (NTP / W32Time) в домене или локальной сети, чтобы все рабочие станции, сервер 1С:Предприятия и сервер СУБД имели строго идентичное системное время.
  2. В программном коде откажемся от вызова ТекущаяДата() и заменим его на функцию ТекущаяДатаСеанса(), которая всегда ориентируется на серверное время:

// Не рекомендуется в толстом клиенте:
// Запрос.УстановитьПараметр("Период", ТекущаяДата());

// Рекомендуется:
Запрос.УстановитьПараметр("Период", ТекущаяДатаСеанса());

Причина 2. Некорректное позиционирование границы периода в запросе

Если в виртуальную таблицу регистра накопления передается дата без указания типа границы, платформа 1С по умолчанию применяет вид границы «Исключая». Из-за этого движения, созданные в ту же самую секунду, не попадают в итоговую выборку.

Также ошибку вызывает попытка получить срез или остатки на момент ЭтотОбъект.Дата для нового, еще не записанного документа, у которого реквизит даты содержит пустое значение 0001-01-01.

Способ устранения 2. Использование объекта «Граница»

Посмотрим на пример правильной передачи момента времени документа-основания в параметры запроса. Для гарантированного включения движений самого документа необходимо использовать объект Граница с флагом ВидГраницы.Включая:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
    |	ТоварыНаСкладахОстатки.КоличествоОстаток КАК Количество
    |ИЗ
    |	РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, Склад = &Склад) КАК ТоварыНаСкладахОстатки";

// Передаем границу, включающую момент времени документа-основания
ГраницаВыборки = Новый Граница(ДанныеЗаполнения.МоментВремени(), ВидГраницы.Включая);
Запрос.УстановитьПараметр("Период", ГраницаВыборки);
Запрос.УстановитьПараметр("Склад", ДанныеЗаполнения.Склад);

РезультатЗапроса = Запрос.Выполнить();

Если требуется получить актуальные остатки на текущий момент без привязки к конкретной секунде, параметр периода в виртуальной таблице можно опустить вовсе либо передать значение Неопределено.

Способ устранения 3. Прямое чтение данных из документа-основания

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


Процедура ОбработкаЗаполнения(ДанныеЗаполнения, ТекстЗаполнения, СтандартнаяОбработка)
	
	Если ТипЗнч(ДанныеЗаполнения) = Тип("ДокументСсылка.РеализацияТоваровУслуг") Тогда
		
		Запрос = Новый Запрос;
		Запрос.Текст = 
			"ВЫБРАТЬ
			|	Реализация.Ссылка КАК Ссылка,
			|	Реализация.Контрагент КАК Контрагент,
			|	Реализация.ДоговорКонтрагента КАК ДоговорКонтрагента,
			|	Реализация.Товары.(
			|		Номенклатура КАК Номенклатура,
			|		Количество КАК Количество,
			|		Цена КАК Цена,
			|		Сумма КАК Сумма
			|	)
			|ИЗ
			|	Документ.РеализацияТоваровУслуг КАК Реализация
			|ГДЕ
			|	Реализация.Ссылка = &ДокументОснование";
		
		Запрос.УстановитьПараметр("ДокументОснование", ДанныеЗаполнения);
		Выборка = Запрос.Выполнить().Выбрать();
		
		Если Выборка.Следующий() Тогда
			ЭтотОбъект.ДокументОснование = Выборка.Ссылка;
			ЭтотОбъект.Контрагент = Выборка.Контрагент;
			ЭтотОбъект.Договор = Выборка.ДоговорКонтрагента;
			
			ТабличнаяЧастьТовары = Выборка.Товары.Выгрузить();
			ЭтотОбъект.Товары.Загрузить(ТабличнаяЧастьТовары);
		КонецЕсли;
		
	КонецЕсли;

КонецПроцедуры

Такой подход полностью исключает зависимость логики заполнения от времени проведения, расчета итогов в регистрах и рассинхронизации системных часов.

← На главную