В типовой конфигурации «Управление торговлей», редакция 10.3, специалисты часто сталкиваются с проблемой необоснованного сдвига границы последовательности (ГП) партионного учета. Ситуация выглядит так: пользователь открывает документ недельной давности, меняет второстепенный реквизит (например, комментарий или подразделение), нажимает «ОК», и граница последовательности мгновенно «улетает» назад на дату этого документа. В итоге системе требуется полное перепроведение партий за длительный период, хотя фактически данные о закупках и продажах не изменились.
В рамках данной статьи мы разберем внутренние механизмы 1С, отвечающие за это поведение, и разработаем алгоритм, позволяющий «заморозить» границу, если движения по регистрам остались прежними.
Для начала проанализируем, как именно платформа и код конфигурации управляют границей. В 1С существует два параллельных процесса, которые могут изменять ГП:
Последовательности.ПартионныйУчет.УстановитьГраницу().Рассмотрим подробнее программный метод. В УТ 10.3 основным «дирижером» партионного учета является общий модуль УправлениеЗапасамиПартионныйУчет. Если мы изучим функцию ДвижениеПартийТоваров, то увидим в финальной части следующий блок кода:
СдвигатьГраницу = ОпределитьНеобходимостьСдвигаГраницы(ДокументМоментВремени, "Упр");
Если СдвигатьГраницу Тогда
Последовательности.ПартионныйУчет.УстановитьГраницу(ДокументМоментВремени);
КонецЕсли;
Здесь система проверяет, является ли документ «оперативным» и нужно ли переместить ГП. Однако, как заметили участники обсуждения, многие документы (например, ПоступлениеТоваровУслуг без использования ордерной схемы) могут не вызывать эту функцию напрямую, но граница всё равно сдвигается.
Выясним причину скрытого сдвига. У каждого документа, включенного в последовательность, есть записи в системной таблице регистрации. Когда документ перепроводится, 1С выполняет следующие действия:
ПартииТоваровНаСкладах).Важный вывод: Чтобы граница не двигалась, нужно либо предотвратить запись набора записей регистра (если он идентичен старому), либо программно вернуть ГП на место сразу после записи.
Рассмотрим наиболее эффективный метод — сравнение новых движений с уже существующими в базе данных. Если движения идентичны, мы помечаем этот факт и блокируем изменение границы.
Для реализации нам понадобятся подписки на события. Разберем по шагам:
Создадим подписку на событие ПередЗаписью для регистра накопления ПартииТоваровНаСкладах. В обработчике мы должны сравнить то, что сейчас находится в памяти (новые движения), с тем, что уже записано в базе по этому регистратору.
Процедура ПередЗаписьюНабораПартий(Источник, Отказ, Замещать) Экспорт
// Источник — это НаборЗаписей регистра
Если Источник.ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
Регистратор = Источник.Отбор.Регистратор.Значение;
// Считываем текущие движения из БД
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ * ИЗ РегистрНакопления.ПартииТоваровНаСкладах ГДЕ Регистратор = &Регистратор";
Запрос.УстановитьПараметр("Регистратор", Регистратор);
ТаблицаБД = Запрос.Выполнить().Выгрузить();
ТаблицаНовая = Источник.Выгрузить();
// Сравниваем таблицы (нужно написать функцию сравнения составов)
Если ДвиженияИдентичны(ТаблицаБД, ТаблицаНовая) Тогда
// Если движения не изменились, ставим флаг в дополнительные свойства
Источник.ДополнительныеСвойства.Вставить("ДвиженияНеИзменились", Истина);
// Опционально: можно вообще отменить запись в БД, чтобы сэкономить ресурсы
// Источник.Записывать = Ложь;
КонецЕсли;
КонецПроцедуры
Проанализируем ситуацию: как передать информацию о том, что ГП трогать не нужно, из набора записей в модуль документа или в процедуру установки границы? Для этого идеально подходит коллекция ДополнительныеСвойства.
Поскольку при проведении документа последовательно вызываются события записи нескольких регистров, мы можем сохранять текущее состояние ГП в параметрах сеанса или передавать флаг через объект документа, если запись инициирована из него.
Теперь вернемся к коду в модуле УправлениеЗапасамиПартионныйУчет. Нам необходимо изменить логику функции ОпределитьНеобходимостьСдвигаГраницы или непосредственно место вызова УстановитьГраницу.
Посмотрим на пример модификации:
// В общем модуле, где происходит установка границы
Если СдвигатьГраницу Тогда
// Проверяем наш флаг
ДвиженияНеМенялись = Ложь;
Если ДокументОбъект.ДополнительныеСвойства.Свойство("ДвиженияНеИзменились") Тогда
ДвиженияНеМенялись = ДокументОбъект.ДополнительныеСвойства.ДвиженияНеИзменились;
КонецЕсли;
Если НЕ ДвиженияНеМенялись Тогда
Последовательности.ПартионныйУчет.УстановитьГраницу(ДокументМоментВремени);
КонецЕсли;
КонецЕсли;
Это критически важный момент. Чтобы избежать ошибок в учете, сравнение должно быть максимально строгим. Проанализируем, какие поля необходимо проверять:
Рекомендуем выгрузить оба набора в таблицы значений, свернуть их по всем значимым полям и сравнить количество строк и содержимое. Любое несовпадение должно приводить к ДвиженияНеМенялись = Ложь.
Существует еще один подход, который обсуждался коллегами. Если мы не хотим глубоко внедряться в логику сравнения таблиц, можно использовать «запоминание» границы:
ПередЗаписью документа или набора записей последовательности сохраняем текущую ГП в переменную или временное хранилище.ПриЗаписи (после того как платформа уже совершила свой автоматический сдвиг) проверяем, изменились ли данные.УстановитьГраницу(СтараяГраница), возвращая её в будущее.Однако этот метод менее надежен, так как программный возврат границы вперед может «перепрыгнуть» через реально измененные другими пользователями документы в многопользовательской среде.
Выяснив причины поведения системы, мы можем сделать следующие выводы:
ДополнительныеСвойства позволяет связать логику проведения документа с логикой регистрации в последовательностях без изменения структуры метаданных.Важный совет: Перед внедрением данных доработок на рабочую базу, тщательно протестируйте алгоритм сравнения движений. Если вы пропустите хотя бы одно поле (например, «Качество» или «Заказ»), система может оставить границу в будущем при фактическом изменении данных, что приведет к неверному расчету остатков и стоимости партий в последующих периодах.