Почему движения документа в регистре накопления не учитывают предыдущие остатки?

Программист Управленческий учет
← На главную

В практике разработки на платформе 1С:Предприятие 8 иногда возникает ситуация, которую трудно объяснить логикой обычного программного кода. Представьте: вы проводите документ прихода, но вместо того чтобы увеличиться, остаток по регистру становится равен сумме этого документа. То есть накопления не происходит — каждая новая запись будто затирает историю или игнорирует предыдущие итоги. Рассмотрим, почему это происходит и как исправить подобные аномалии.

Разбор симптомов проблемы

Типичная картина выглядит так:

  1. Был остаток в регистре, например, 5 единиц.
  2. Провели документ прихода на 3 единицы. Ожидаемый итог — 8, но фактический — 3.
  3. При запросе остатков с детализацией по регистратору нарушается хронология: документы за более поздние даты могут отображаться раньше документов за предыдущие периоды.
  4. Стандартные процедуры обслуживания (Тестирование и исправление, пересчет итогов) не приносят результата.

Проанализируем ситуацию по шагам, двигаясь от простых ошибок в коде к сложным проблемам на уровне структуры базы данных.

Шаг 1: Проверка логики формирования движений

Первым делом изучим код, который формирует записи в регистр. Часто причина кроется в банальной опечатке при установке вида движения. Рассмотрим пример кода из сообщения пользователя:


// Фрагмент кода ДокПриход
Движение = Движения.Бегунки.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Расход; // ОШИБКА! В приходном документе
Движение.Период = Дата;
Движение.Количество = ТекСтрокаСостав.Количество;

Выясним причину: если в документе, который логически должен делать Приход, жестко прописан Расход, то при расчете остатков система будет вычитать значения. Однако это не объясняет, почему остаток становится равен сумме документа. Если мы видим результат, равный значению документа, значит, система по какой-то причине считает текущую запись точкой отсчета, игнорируя всё, что было до нее.

Шаг 2: Анализ использования Регистратора как измерения

В некоторых самописных конфигурациях программисты добавляют в регистр накопления измерение с типом ДокументСсылка (например, Бегунок или УпаковочныйЛист), куда записывают ссылку на сам документ-регистратор. Рассмотрим, почему это опасно:

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

Проанализируем фрагмент кода, вызывающий подозрение:


Движение = Движения.УпаковочныеЛисты.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
Движение.Регистратор = Ссылка; // Системное поле, заполняется автоматически
Движение.Бегунок = Ссылка;     // Измерение, дублирующее регистратор

Решение: Если вам не требуется видеть остатки в разрезе конкретных документов-оснований (партий), удалите измерение, содержащее ссылку на регистратор. Если же это необходимо (например, для партионного учета), убедитесь, что при Расходе вы четко указываете ссылку на тот Приход, который нужно закрыть.

Шаг 3: Исправление поврежденных индексов и кэша метаданных

Если код проверен, а ВидДвижения установлен верно, но проблема сохраняется — мы имеем дело с нарушением целостности структуры метаданных. В 1С каждый объект имеет внутренний идентификатор (GUID). Иногда при обновлении конфигурации или сбоях СУБД индексы таблиц итогов (_AccumRegTotals) для конкретного типа документа могут повредиться.

Рассмотрим, почему помогает копирование документа:

  1. При создании копии документа (ДокПриход1) платформа генерирует для него новый GUID типа объекта.
  2. Для нового объекта в базе данных создаются новые ветки в индексах и новые записи в служебных таблицах.
  3. Все старые "фантомные" связи и битые индексы, привязанные к старому объекту, перестают влиять на процесс записи и расчета.

Как выполнить "чистое" пересоздание:

Шаг 4: Настройка режима удаления движений

Еще одна причина странных остатков — свойство документа Удаление движений. Разберем два варианта:

  1. Удалять автоматически: Перед проведением платформа сама очищает записи регистра по данному регистратору. Это наиболее безопасный режим.
  2. Не удалять автоматически: Программист должен сам вызвать Движения.ИмяРегистра.Очистить(). Если этого не сделать или вызвать метод некорректно, в базе могут остаться старые "хвосты" движений, которые будут конфликтовать с новыми и выдавать непредсказуемый результат в виртуальных таблицах Остатки.

Посмотрим на правильный паттерн в модуле документа:


Процедура ОбработкаПроведения(Отказ, Режим)
    // Очищаем старые движения, если стоит режим "Не удалять автоматически"
    Движения.УпаковочныеЛисты.Записывать = Истина;
    Движения.УпаковочныеЛисты.Очистить();
    
    // Формируем новые движения
    Для Каждого Строка Из Состав Цикл
        Движение = Движения.УпаковочныеЛисты.Добавить();
        // Заполнение полей...
    КонецЦикла;
КонецПроцедуры

Шаг 5: Проблема Момента времени и сортировки

Если в запросах по регистру документы за 18 июля идут раньше документов за 30 июня, это явный признак того, что 1С не может корректно определить МоментВремени. МоментВремени состоит из Даты и Ссылки.

Выясним причину: если записи в регистре были созданы напрямую набором записей без привязки к хронологии или если в конфигурации нарушена логика работы с итогами, виртуальная таблица ОстаткиИОбороты может "сходить с ума". В таких случаях помогает полное отключение итогов регистра в конфигураторе (снятие флага "Использовать итоги"), сохранение конфигурации БД, а затем повторное включение. Это принудительно пересоздаст физические таблицы итогов в SQL.

Итоговые рекомендации

Для решения проблемы с "нескладывающимися" остатками придерживайтесь следующего алгоритма:

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