В практической работе с программой «1С:Управление нашей фирмой» (УНФ) разработчики часто сталкиваются с запросами пользователей, суть которых сводится к следующему: «Мы оформляем расходную накладную задним числом (например, вчерашней датой). Почему при нажатии на кнопку "Подобрать" система показывает нам актуальные остатки на текущий момент времени, а не остатки на дату документа? Сделайте так, чтобы мы видели остатки на вчера».
На первый взгляд задача кажется тривиальной, ведь достаточно в запросе получения остатков виртуальной таблицы указать параметр &Период. Однако за этой кажущейся простотой скрываются серьезные методологические проблемы, архитектурные риски и угроза существенного снижения производительности базы данных. В этой статье мы подробно разберем, почему фирма «1С» заблокировала вывод остатков на дату документа в стандартном подборе, к каким последствиям ведет обход этого ограничения и какие безопасные альтернативные решения мы можем предложить заказчику.
Проанализируем логику работы платформы «1С:Предприятие 8». В типовых конфигурациях УНФ при открытии формы подбора остатки товаров извлекаются на текущую дату (или на максимально возможную дату 01.11.3999). Это сделано разработчиками конфигурации намеренно.
Давайте разберем методологический пример. Представим следующую хронологию событий:
Если бы форма подбора показывала остатки на дату документа (на 11 мая), то пользователь увидел бы свободный остаток в количестве 5 штук. Он выбирает эти 5 штук, и документ от 11 мая успешно проводится. Но что происходит в учете на временной шкале дальше?
После проведения этого документа остаток на конец дня 11 мая действительно будет корректным (0 штук). Однако при переходе на 12 мая, где уже проведено списание еще 5 штук, остаток мгновенно превращается в минус 5 штук (-5 шт.). Возникает так называемый «скрытый» или «будущий» отрицательный остаток.
Чем опасны такие отрицательные остатки в управленческом учете:
Именно поэтому стандартный механизм подбора всегда ориентируется на актуальный текущий остаток: если товара нет физически сегодня, программа не позволяет продать его «вчера».
Рассмотрим также техническую сторону вопроса. Получение остатков в реальном времени для динамического списка формы подбора на произвольную дату (дату документа) требует выполнения тяжелых запросов с левым соединением таблиц остатков. Если документов много, а номенклатура исчисляется тысячами позиций, динамический список начнет критически тормозить при прокрутке.
---
Если пользователям жизненно необходимо периодически анализировать остатки на дату документа, лучшим и самым безопасным решением будет предоставление им удобного инструмента в один клик без вмешательства в алгоритмы проведения и подбора. Мы можем добавить на форму документа кнопку быстрого перехода к отчету «Остатки товаров» с предустановленными отборами.
Разберем по шагам, как это реализовать. В форму документа добавляем команду и кнопку «Показать остатки на дату документа». При нажатии на нее мы программно открываем отчет, передавая в него параметры из текущего документа.
Посмотрим на пример кода на встроенном языке 1С:
&НаКлиенте
Процедура ПоказатьОстаткиНаДату(Команда)
// Формируем параметры открытия отчета
ПараметрыОтчета = Новый Структура;
ПараметрыОтчета.Вставить("СформироватьПриОткрытии", Истина);
// Задаем настройки компоновки данных
Настройки = Новый НастройкиКомпоновкиДанных;
// Устанавливаем параметр "Период" (наша дата документа)
ПараметрПериод = Настройки.ПараметрыДанных.Элементы.Найти("КонецПериода");
Если ПараметрПериод <> Неопределено Тогда
ПараметрПериод.Значение = Объект.Дата;
ПараметрПериод.Использование = Истина;
КонецЕсли;
// Добавляем отбор по складу, если он указан в документе
Если ЗначениеЗаполнено(Объект.Склад) Тогда
ЭлементОтбора = Настройки.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Склад");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ЭлементОтбора.ПравоеЗначение = Объект.Склад;
ЭлементОтбора.Использование = Истина;
КонецЕсли;
ПараметрыОтчета.Вставить("ФиксированныеНастройки", Настройки);
// Открываем форму стандартного отчета "ОстаткиТоваров"
ОткрытьФорму("Отчет.ОстаткиТоваров.Форма", ПараметрыОтчета, ЭтотФорма);
КонецПроцедуры
Такой подход полностью соответствует стандартам разработки «1С:Совместимо» и логике БСП (Библиотеки стандартных подсистем). Пользователь получает оперативную информацию в привычном интерфейсе отчетов, при этом система не подвергается риску занесения некорректных данных.
---
Что делать, если заказчик категорически отказывается открывать отчеты, мотивируя это тем, что при подборе сотен позиций переключение окон отнимает слишком много времени? В этом случае мы можем разработать точечный интерактивный механизм.
Добавим на форму документа команду «Показать остаток по строке на дату». Пользователь позиционируется на строке табличной части с товаром, нажимает кнопку, и система в виде краткого всплывающего сообщения или декоративного поля на форме выводит точный остаток именно по этой позиции на дату документа.
Проанализируем программную реализацию этого метода. Нам потребуется легкий серверный запрос к регистру накопления Запасы.
&НаСервереБезКонтекста
Функция ПолучитьОстатокТовараНаДату(Знач Номенклатура, Знач Склад, Знач ДатаДокумента) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЗапасыОстатки.КоличествоОстаток КАК Остаток
|ИЗ
| РегистрНакопления.Запасы.Остатки(&Период, Номенклатура = &Номенклатура И СтруктурнаяЕдиница = &Склад) КАК ЗапасыОстатки";
Запрос.УстановитьПараметр("Период", Новый Граница(ДатаДокумента, ГраницаТипа.Включая));
Запрос.УстановитьПараметр("Номенклатура", Номенклатура);
Запрос.УстановитьПараметр("Склад", Склад);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Возврат Выборка.Остаток;
КонецЕсли;
Возврат 0;
КонецФункции
&НаКлиенте
Процедура УзнатьОстатокНаДатуДокумента(Команда)
ТекущиеДанные = Элементы.Запасы.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
ПоказатьПредупреждение(, "Выберите строку с товаром!");
Возврат;
КонецЕсли;
// Определяем склад (из строки или из шапки документа)
ТекущийСклад = ?(ЗначениеЗаполнено(ТекущиеДанные.Склад), ТекущиеДанные.Склад, Объект.Склад);
Если Не ЗначениеЗаполнено(ТекущийСклад) Тогда
ПоказатьПредупреждение(, "Не заполнен склад в документе или строке!");
Return;
КонецЕсли;
ОстатокНаДату = ПолучитьОстатокТовараНаДату(ТекущиеДанные.Номенклатура, ТекущийСклад, Объект.Date);
// Выводим информацию пользователю
ТекстСообщения = Шаблон"Остаток товара ""%1"" на дату %2 составляет %3 шт.";
ТекстСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстСообщения,
ТекущиеДанные.Номенклатура, Формат(Объект.Дата, "ДЛФ=D"), ОстатокНаДату);
ПоказатьОповещениеПользователя("Информация об остатках", , ТекстСообщения, БиблиотекаКартинок.Информация);
КонецПроцедуры
Преимущества данного метода очевидны: мы не перегружаем динамический список формы подбора тяжелыми соединениями, база данных не зависает. Запрос выполняется мгновенно, так как обращается к конкретной номенклатурной позиции по индексированным полям регистра. Пользователь получает нужную цифру «здесь и сейчас».
---
Если бизнес-процессы компании все же требуют вывода остатков на дату документа непосредственно в форму подбора, и руководство осознает все риски возникновения отрицательных остатков, задачу можно решить через создание расширения конфигурации.
Рассмотрим общую схему доработки программного кода:
&Вместо("ОткрытьПодборНоменклатуры")
Процедура Расш_ОткрытьПодборНоменклатуры()
ПараметрыОткрытия = Новый Структура;
ПараметрыОткрытия.Вставить("ДатаДокумента", Объект.Дата);
// Дополнительные стандартные параметры...
ОткрытьФорму("Обработка.ПодборНоменклатуры.Форма", ПараметрыОткрытия, ЭтотФорма);
КонецПроцедуры
ПодборНоменклатуры) перехватывается обработчик ПриСозданииНаСервере. Мы считываем переданный параметр даты и передаем его в компоновщик настроек динамического списка остатков, устанавливая значение параметра &Период.При использовании этого метода настоятельно рекомендуется настроить регулярное выполнение фонового задания, которое будет анализировать базу на предмет появления отрицательных остатков на конец каждого дня и присылать отчет администраторам или руководству для оперативного исправления документов.