Как правильно указать момент времени для запроса по остаткам в форме документа?

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

При разработке алгоритмов автозаполнения табличных частей документа остатками номенклатуры разработчики часто переносят привычный код из модуля объекта в модуль формы. В модуле документа получение остатков до момента проведения обычно выглядит лаконично: разработчик обращается к системному методу МоментВремени() и оборачивает его в объект Граница с исключением движений текущего документа. Однако при попытке вызвать этот метод в модуле управляемой формы возникает ошибка времени выполнения. Разберем подробно, почему это происходит и как методологически верно рассчитать остатки из формы документа в платформе 1С:Предприятие 8.3.

Выясним причину ошибки: почему в форме недоступен метод МоментВремени()

В управляемом приложении основной реквизит формы документа (как правило, с именем Объект) не является объектом конфигурации типа ДокументОбъект. На клиенте и в серверном контексте формы он имеет специальный платформенный тип ДанныеФормыСтруктура. Этот тип служит легковесным хранилищем значений реквизитов для передачи между клиентом и сервером и не содержит прикладных методов бизнес-логики, таких как Записать(), ПроверитьЗаполнение() или МоментВремени().

Хотя разработчик может принудительно преобразовать данные формы в объект с помощью вызова РеквизитФормыВЗначение("Объект"), делать это исключительно ради получения позиции документа на оси времени неэффективно. Десериализация всей структуры формы в тяжелый серверный объект создает неоправданную нагрузку на систему. Посмотрим, как решить задачу стандартными средствами платформы без лишних операций.

Способ 1. Формирование объекта МоментВремени из реквизитов формы

Вспомним базовое определение платформы: момент времени в 1С представляет собой совокупность даты и ссылки на документ. Платформа предоставляет конструктор объекта МоментВремени, принимающий дату документа и его ссылку. Следовательно, мы можем получить нужную точку на оси времени напрямую из реквизитов Объект.Дата и Объект.Ссылка.

Рассмотрим серверную процедуру запроса остатков, где мы явно инициализируем момент времени и передаем его в виртуальную таблицу регистра накопления:


&НаСервере
Процедура ЗаполнитьОстаткиНаСервере()

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

	ТочкаВремени = Новый МоментВремени(Объект.Дата, Объект.Ссылка);
	ГраницаОстатков = Новый Граница(ТочкаВремени, ВидГраницы.Исключая);

	Запрос.УстановитьПараметр("Период", ГраницаОстатков);
	Запрос.УстановитьПараметр("Склад", Объект.Склад);

	РезультатЗапроса = Запрос.Выполнить();
	// Дальнейшее заполнение табличной части
	
КонецПроцедуры

В платформе 1С конструктор объекта Граница также умеет неявно определять момент времени непосредственно по ссылке на документ. Конструкция вида Новый Граница(Объект.Ссылка, ВидГраницы.Исключая) отработает аналогично, если документ уже существует в информационной базе.

Подводный камень: работа с новым незаписанным документом

Проанализируем критическую ситуацию: пользователь создал новый документ и нажал кнопку заполнения остатков до того, как нажал кнопку сохранения или проведения. В этот момент ссылка документа еще не сформирована в базе данных — свойство Объект.Ссылка.Пустая() вернет значение Истина.

Если передать в конструктор границы пустую ссылку, платформа не сможет корректно сопоставить документ с движениями регистров. Более того, при формировании Новый МоментВремени(Объект.Дата, Объект.Ссылка) пустая ссылка имеет минимальный GUID (нули), из-за чего созданный момент времени окажется самым первым среди всех документов на данную секунду, что может привести к искажению расчетных данных.

Разберем два правильных подхода к обработке незаписанных документов.

Способ 2. Ветвление логики для нового и записанного документа

Методологически верным решением без принудительной записи является проверка наличия ссылки. Если документ новый, у него заведомо нет собственных движений по регистрам, а значит, исключать его движения не требуется — достаточно рассчитать остатки на дату документа или на текущий момент времени сеанса.

Посмотрим на реализацию универсальной установки параметра периода:


&НаСервере
Функция ПолучитьГраницуДляОстатков()

	Если Объект.Ссылка.Пустая() Тогда
		// Для нового документа исключать нечего: используем дату документа или оперативную отметку
		Если Объект.Дата = '00010101' Тогда
			Возврат ТекущаяДатаСеанса();
		Иначе
			Возврат Объект.Дата;
		КонецЕсли;
	Иначе
		// Для записанного документа формируем исключающую границу
		ТочкаВремени = Новый МоментВремени(Объект.Дата, Объект.Ссылка);
		Возврат Новый Граница(ТочкаВремени, ВидГраницы.Исключая);
	КонецЕсли;

КонецФункции

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

Способ 3. Интерактивная запись документа перед выполнением операции

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

Разберем по шагам реализацию клиентской команды:

  1. В обработчике команды проверяем, модифицирован ли документ или является ли он новым.
  2. Вызываем стандартный метод формы Записать(). Если запись проходит успешно, форма получает реальную валидную ссылку.
  3. Вызываем серверную процедуру расчета остатков, будучи уверенными, что Объект.Ссылка существует.

&НаКлиенте
Процедура ЗаполнитьПоОстаткам(Команда)

	// Проверяем необходимость предварительной записи
	Если Объект.Ссылка.Пустая() Или Модифицированность Тогда
		ПараметрыЗаписи = Новый Структура("РежимЗаписи", РежимЗаписиДокумента.Запись);
		Если НЕ Записать(ПараметрыЗаписи) Тогда
			Возврат; // Пользователь отказался от записи или возникли ошибки проверки
		КонецЕсли;
	КонецЕсли;

	ЗаполнитьОстаткиНаСервере();

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

Рекомендации по стандартам разработки 1С

В соответствии со стандартами разработки типовых конфигураций (1С:ERP, 1С:УТ, 1С:КА), размещение тяжелых запросов непосредственно в коде формы считается нежелательным. Рекомендуется выносить алгоритмы расчета в модуль менеджера документа.

В модуль менеджера передается структура параметров (например, склад, дата, ссылка и список номенклатуры), а возвращается таблица значений с рассчитанными остатками. Это обеспечивает повторное использование кода: алгоритм можно будет вызывать как из формы документа, так и из внешних обработок или процедур регламентного фонового заполнения.

← На главную