При разработке и поддержке конфигураций на платформе 1С:Предприятие 7.7 программисты часто сталкиваются со специфическим поведением регистров накопления. Одна из классических проблем — попытка получить остатки по регистрам на конец дня (или на любой момент времени «после» текущего документа) внутри модуля проведения при групповом перепроведении документов или восстановлении последовательностей.
В этой статье мы подробно разберем, почему стандартный код получения остатков на конец дня сбоит при групповых операциях, в чем заключается ограничение платформы 1С 7.7, и как правильно решить эту задачу с точки зрения архитектуры и программирования.
Рассмотрим типичный фрагмент кода, который разработчики используют в модуле проведения документа для получения остатков на конец дня:
ВремРегистры.Актуальность(1);
ГраницаРасчета = ДатаДок;
Если ПолучитьДатуТА() <= ДатаДок Тогда
ГраницаРасчета = ПолучитьПозициюТА();
КонецЕсли;
ВремРегистры.РассчитатьРегистрыПо(ГраницаРасчета);
Давайте разберем по шагам, что происходит при выполнении этого алгоритма в различных режимах проведения:
ТА) базы данных находится на самом последнем проведенном документе (например, в конце сегодняшнего дня или даже в будущем периоде). В этом случае функция ПолучитьДатуТА() возвращает дату, которая больше или равна дате проводимого документа (ДатаДок). Условие ПолучитьДатуТА() <= ДатаДок возвращает Ложь. Переменная ГраницаРасчета остается равной ДатаДок (что интерпретируется платформой как конец дня 23:59:59). Временные регистры успешно рассчитываются на конец дня, и документ видит все движения до конца суток.ТА временно устанавливается прямо на этот документ. Здесь поведение программы меняется кардинально:
ПолучитьДатуТА() теперь возвращает дату текущего проводимого документа, так как ТА временно находится на нем.ПолучитьДатуТА() <= ДатаДок превращается в равенство (например, 15.10.2023 <= 15.10.2023), которое возвращает Истина.ГраницаРасчета значение ПолучитьПозициюТА() (то есть позицию текущего документа со временем его проведения).РассчитатьРегистрыПо рассчитывает итоги на момент проведения документа, а не на конец дня. Все документы, проведенные в этот же день позже по времени, в расчет не попадают.Почему нельзя просто убрать условие проверки ТА и всегда рассчитывать на ДатаДок (конец дня)?
Дело в жестком ограничении ядра платформы 1С 7.7: запрещено рассчитывать временные регистры на момент времени, находящийся строго после Точки Актуальности. Если в процессе группового перепроведения (когда ТА стоит на текущем документе в 12:00) вы попытаетесь выполнить метод РассчитатьРегистрыПо(ДатаДок) (что означает расчет на конец дня — 23:59:59), платформа аварийно завершит работу системы с критической ошибкой базы данных.
Таким образом, стандартный механизм временных регистров физически не позволяет заглянуть «в будущее» за пределы текущей ТА во время транзакции проведения документа.
Самый правильный и надежный способ решить эту проблему — пересмотреть логику проведения документов. В профессиональной разработке учет строится так, чтобы документ принимал решения строго на момент своего проведения (на свою позицию на временной шкале).
Попытка заглянуть в конец дня нарушает базовые принципы последовательности учета:
Как сделать правильно:
Если бизнес-требования невозможно изменить, и вам обязательно нужно знать остатки на конец дня в процессе группового проведения, стандартные механизмы 1С 7.7 не помогут. В этом случае на помощь приходит использование прямых SQL-запросов к базе данных (при работе 1С 7.7 в SQL-режиме) с помощью внешней компоненты 1С++.
Прямой запрос обращается непосредственно к таблицам итогов (RG*) и движений (RA*) СУБД, полностью игнорируя виртуальные ограничения платформы 1С 7.7 на работу с ТА.
Рассмотрим пример логики построения такого запроса:
// Пример концепта прямого запроса через 1С++ для получения остатков на конец дня
// Запрос суммирует остатки на начало месяца и движения по указанную дату включительно
ТекстЗапроса = "
|SELECT
| Рег.Товар [Товар $Справочник.Товары],
| SUM(Рег.Остаток) ТоварОстаток
|FROM
| (
| // Получаем остатки на начало месяца
| SELECT Товар, Остаток FROM $РегистрИтоги.ОстаткиТоваров WHERE Период = :НачалоМесяца
| UNION ALL
| // Прибавляем движения с начала месяца по конец дня проводимого документа
| SELECT Товар, Расход * -1 + Приход FROM $Регистр.ОстаткиТоваров WHERE ДатаДок BETWEEN :НачалоМесяца AND :КонецДняДокумента
| ) Рег
|GROUP BY
| Рег.Товар
|";
Преимущества данного подхода:
Если использование внешних компонент нежелательно (например, база работает в файловом DBF-формате), можно применить двухпроходный алгоритм группового проведения через специальную обработку:
Запрос) и собирает все остатки на концы нужных дней за весь период перепроведения.Этот метод позволяет избежать фатальной ошибки «расчета после ТА» и значительно ускоряет работу групповой обработки, так как исключает тысячи однотипных расчетов регистров в цикле.