Почему при подборе в 1С на текущую дату подставляются будущие цены и как это исправить?

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

В процессе работы со старыми типовыми конфигурациями «1С:Предприятие 8» (такими как «Комплексная автоматизация 1.1», «Управление производственным предприятием 1.3» или «Управление торговлей 10.3») пользователи и разработчики часто сталкиваются с крайне странным поведением системы. При создании документа сегодняшним числом в табличную часть при подборе подтягиваются цены из будущих документов установки цен (например, запланированных на начало следующего года или месяца). При этом, если изменить дату документа всего на один день назад или вперед, автоподбор начинает работать абсолютно корректно.

В данной статье мы подробно разберем, почему возникает этот классический «глюк», проанализируем логику работы платформы с виртуальными таблицами регистров сведений и найдем способ навсегда устранить эту ошибку в коде конфигурации.

Анализ проблемы на практическом примере

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

  1. В информационной базе зарегистрировано два документа Установка цен номенклатуры: первый от 12.12.2018 (с ценой, например, 100 рублей), второй — от 01.01.2019 (с новой ценой 150 рублей).
  2. Текущая рабочая дата на компьютере и в параметрах программы — 14.12.2018.
  3. Мы создаем документ продажи (например, Реализация товаров и услуг) с датой 14.12.2018. Открываем форму подбора номенклатуры. Программа предлагает нам цену 150 рублей (из будущего документа от 01.01.2019). Это явная ошибка, так как на 14 декабря новые цены еще не вступили в силу.
  4. Мы меняем дату документа реализации на 13.12.2018 (вчерашний день) или на 15.12.2018 (завтрашний день). Открываем подбор заново — программа корректно устанавливает цену 100 рублей от 12.12.2018.

На первый взгляд такое поведение кажется мистическим. Почему равенство даты документа текущей дате ломает логику среза цен? Для ответа на этот вопрос нам придется заглянуть в конфигуратор.

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

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


ДатаРасчетов = ?(НачалоДня(ДатаДокумента) = НачалоДня(ТекущаяДата()), Неопределено, ДатаДокумента);

Рассмотрим подробнее логику, которой руководствовались разработчики 1С при написании этого условия:

Замысел разработчиков состоял в том, чтобы при работе «сегодняшним» числом база данных не тратила ресурсы на избыточную фильтрацию по дате, предполагая, что текущие актуальные цены — это просто самые последние записи в базе данных.

Однако здесь кроется ключевая архитектурная ловушка платформы «1С:Предприятие». Запрос, который отвечает за получение цен для формы подбора, обращается к виртуальной таблице среза последних:


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

Согласно спецификации платформы 1С, поведение виртуальной таблицы СрезПоследних напрямую зависит от значения первого параметра (Период):

Именно поэтому при оформлении документа от 14.12.2018 (сегодняшнее число) параметр периода заменяется на Неопределено, запрос «пробивает» временную границу текущего дня и извлекает из базы данных будущие цены от 01.01.2019.

Связь с интерфейсом подбора: Анализ надписи «Дата расчетов»

Подтверждение этой логики легко найти в коде управления элементами интерфейса формы подбора. Обратим внимание на следующее выражение, управляющее видимостью информационной надписи:


ЭлементыФормы.НадписьДатаРасчетов.Видимость = Не (ПодборПоСправочнику или НЕ ЗначениеЗаполнено(ДатаРасчетов));

Проанализируем работу этого выражения по шагам для случая, когда документ открыт сегодняшней датой:

  1. В параметр ДатаРасчетов при открытии передано значение Неопределено.
  2. Системная функция ЗначениеЗаполнено(Неопределено) возвращает результат Ложь.
  3. Оператор НЕ Ложь преобразует выражение в Истина.
  4. Логическое сложение (ПодборПоСправочнику или Истина) в любом случае дает результат Истина.
  5. Внешнее отрицание Не (Истина) превращается в Ложь.

В результате свойство Видимость для надписи «Дата расчетов» принимает значение Ложь, и поле скрывается с формы. Программа считает, что дата расчетов «не задана» (так как передано пустое значение), хотя фактически пользователь ожидает расчеты именно на текущую дату компьютера.

Пошаговое руководство по исправлению ошибки

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

Шаг 1. Локализация проблемного участка кода

Откроем конфигурацию в режиме «Конфигуратор». Ошибка чаще всего кроется в общем модуле, отвечающем за работу с торговыми диалогами (например, РаботаСДиалогами, ОбработкаТабличныхЧастей) или непосредственно в модуле формы документа (например, ФормаДокумента реализации или заказа покупателя) в процедуре вызова подбора.

Воспользуемся глобальным поиском по конфигурации (сочетание клавиш Ctrl + Shift + F) и найдем строку инициализации переменной даты расчетов. Искать следует текст:

НачалоДня(ТекущаяДата())

Шаг 2. Корректировка кода

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

Рассмотрим, как выглядел код ДО изменения:


// Старый некорректный вариант:
ВременнаяДатаРасчетов = ?(НачалоДня(Дата) = НачалоДня(ТекущаяДата()), Неопределено, Дата);
СтруктураПараметровПодбора.Вставить("ДатаРасчетов", ВременнаяДатаРасчетов);

Перепишем этот участок кода так, чтобы дата документа передавалась в подбор всегда, без каких-либо исключений:


// Новый исправленный вариант:
ВременнаяДатаРасчетов = Дата;
СтруктураПараметровПодбора.Вставить("ДатаРасчетов", ВременнаяДатаРасчетов);

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

Шаг 3. Сохранение и проверка результатов

После внесения изменений выполним следующие действия:

  1. Сохраним конфигурацию базы данных (клавиша F7).
  2. Запустим систему в режиме «1С:Предприятие».
  3. Создадим новый документ продажи текущим днем (14.12.2018).
  4. Откроем обработку подбора товаров. На форме должна появиться надпись «Дата расчетов», указывающая на текущее число.
  5. Попробуем подобрать товар, для которого зарегистрированы будущие цены от 01.01.2019. Система должна проигнорировать их и подставить актуальные цены от 12.12.2018.

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

← На главную