Почему при проведении документа текущей датой меняются остатки на вчерашний день в 1С?

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

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

Проанализируем архитектуру регистров накопления

Для начала разберем, как 1С хранит данные. Регистр накопления состоит не из одной таблицы, а как минимум из двух (в SQL-варианте): таблицы движений (где хранятся записи всех документов с их периодами) и таблицы итогов (где хранятся рассчитанные остатки на начало каждого месяца). Когда мы формируем отчет на «вчера», система не всегда пересчитывает все движения с начала времен. Она берет ближайшие рассчитанные итоги и прибавляет/вычитает к ним движения за короткий промежуток времени.

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

Причина 1: Механизм «Разделения итогов» (Split Totals)

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

Рассмотрим проблему подробнее: в некоторых релизах платформы (включая ветку 8.3.12) наблюдались ошибки, когда при массовой записи документов система некорректно «схлопывала» эти записи или временно теряла одну из них из-за неверного хэширования. В результате остаток по измерению (например, по конкретному договору) мог внезапно «исчезать» из итогов.

Что мы можем сделать:

  1. Найдем в конфигураторе нужный регистр накопления.
  2. Перейдем на закладку «Прочее».
  3. Найдем свойство Разрешить разделение итогов.
  4. Попробуем временно отключить его и проверить, сохраняется ли проблема. Если после отключения остатки перестали «прыгать», значит, мы столкнулись с багом платформы в механизме оптимизации.

Причина 2: Неуправляемые блокировки и «грязное чтение»

Если наша база работает в режиме автоматических (неуправляемых) блокировок, то при проведении документа СУБД (например, MS SQL Server) может блокировать не отдельные строки, а целые страницы данных в таблице итогов. В этот момент другой процесс (отчет или консоль запросов) может попытаться прочитать данные.

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

Решение: переведем конфигурацию на режим управляемых блокировок. Это позволит системе более точечно блокировать данные и исключит чтение «фантомных» записей.

Причина 3: Рассинхронизация таблиц и пересчет итогов

Иногда физическая таблица итогов просто «разваливается» — данные в ней перестают соответствовать данным в таблице движений. Это может произойти из-за аварийного завершения работы сервера SQL или сбоев в кэше сервера 1С.

Посмотрим на алгоритм исправления:

  1. Запустим «Тестирование и исправление» информационной базы.
  2. Выберем только пункт «Пересчет итогов».
  3. Дождемся завершения. Платформа полностью очистит таблицы итогов и заново рассчитает их на основе таблицы движений. Это гарантированно исправит ситуацию, если проблема была в накопленной ошибке данных.

Причина 4: Ошибки в коде самописных конфигураций

Разберем типичную ошибку программиста при записи движений вручную или через обработки. Если мы используем объект НаборЗаписей, крайне важно правильно устанавливать фильтры. Рассмотрим пример ошибочного кода:


Набор = РегистрыНакопления.Взаиморасчеты.СоздатьНаборЗаписей();
// Забыли установить отбор по регистратору!
Набор.Записать(); // Это может очистить данные, которые не должны были пострадать

Хотя автор темы утверждает, что «задним числом» документы не пишутся, стоит проверить, не меняется ли Период движений внутри модуля проведения программно. Выясним, нет ли в коде конструкций, которые принудительно устанавливают период движения, отличный от даты документа:


Для Каждого Движение Из Движения.Взаиморасчеты Цикл
    Движение.Период = НачалоДня(Дата) - 1; // ОШИБКА: Движение пишется вчерашним числом
КонецЦикла;

Причина 5: Проблемы на уровне SQL-статистики

В некоторых случаях данные в базе верны, но SQL-оптимизатор строит кривой план запроса. Это происходит из-за устаревшей статистики индексов. Когда мы запрашиваем остатки через виртуальную таблицу, SQL может использовать неоптимальный индекс, который «пропускает» часть записей. Проанализируем состояние базы через консоль SQL:

  1. Выполним обновление статистики (UPDATE STATISTICS) для таблиц регистра.
  2. Выполним реиндексацию (REINDEX) таблиц.

Выводы и рекомендации

Если мы столкнулись с такой проблемой на платформе 8.3.12, наиболее вероятным решением будет обновление платформы до более свежих релизов (например, 8.3.15 или актуальных версий 8.3.22+), так как в версии 8.3.12.1567 было зафиксировано множество ошибок, связанных именно с расчетом виртуальных таблиц в условиях многопользовательской работы. В качестве оперативной меры всегда начинаем с полного пересчета итогов и проверки свойства «Разделение итогов».

← На главную