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

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

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

1. Базовые понятия: что такое регистр накопления и зачем он нужен

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

  • 📦 Учёта товарных остатков на складах
  • 💰 Контроля денежных средств в кассе или на расчётных счетах
  • 📊 Аналитики продаж по периодам, контрагентам или номенклатуре
  • 🔄 Отслеживания движения материалов в производстве

Каждый регистр накопления имеет:

  • Измерения — параметры, по которым ведётся аналитика (например, Склад, Номенклатура)
  • Ресурсы — количественные показатели (например, Количество, Сумма)
  • Реквизиты — дополнительные атрибуты (например, ВалютнаяСумма или СтавкаНДС)

Важно понимать, что регистры накопления бывают двух типов:

  1. Остатков — хранят текущее состояние (например, остатки товаров на складе).
  2. Оборотов — фиксируют движения за период (например, продажи за месяц).
⚠️ Внимание: Если в конфигурации используется управление по регистрам (например, в 1С:ERP или 1С:УТ), то неправильное обращение к регистрам может привести к расхождению данных с бухгалтерскими итогами. Всегда проверяйте логику работы регистров в Конфигураторе перед изменением кода.

2. Способ 1: Получение остатков через метод ПолучитьДанные()

Самый простой способ получить данные из регистра накопления — использовать встроенный метод ПолучитьДанные(). Он подходит для быстрого чтения остатков или оборотов без сложных фильтров. Синтаксис:

ДанныеРегистра = РегистрыНакопления.ИмяРегистра.ПолучитьДанные(

НачалоПериода,

КонецПериода,

ПараметрыОтбора,

Группировки

);

Пример: получим остатки товаров на складе "Основной" по состоянию на текущую дату:

ОстаткиТоваров = РегистрыНакопления.ТоварыНаСкладах.ПолучитьДанные(

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

КонецДня(ТекущаяДата()),

Новый Структура("Склад", Справочники.Склады.Основной)

);

Преимущества метода:

  • 🔹 Простота использования — не требует знания языка запросов.
  • 🔹 Быстрота для небольших объёмов данных.

Недостатки:

  • 🐢 Медленная работа при больших выборках (более 10 000 записей).
  • 🎯 Ограниченные возможности фильтрации и группировки.
⚠️ Внимание: Если вы используете ПолучитьДанные() в цикле (например, для перебора складов), это может привести к замедлению системы в 10-100 раз. В таких случаях лучше использовать запросы с виртуальными таблицами (см. раздел 4).
📊 Какой способ обращения к регистрам вы используете чаще?
ПолучитьДанные()
Запросы с виртуальными таблицами
Объектный метод (Выбрать())
Другое

3. Способ 2: Работа с регистром как с объектом (метод Выбрать())

Регистр накопления можно обрабатывать как объект базы данных, используя метод Выбрать(). Это даёт больше гибкости, чем ПолучитьДанные(), но требует ручной обработки выборки. Пример:

Выборка = РегистрыНакопления.ТоварыНаСкладах.Выбрать(

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

КонецДня(ТекущаяДата()),

"Склад = &Склад",

Новый Структура("Склад", Справочники.Склады.Основной)

);

Пока Выборка.Следующий() Цикл

Сообщить(СтрШаблон("Номенклатура: %1, Остаток: %2",

Выборка.Номенклатура,

Выборка.КоличествоОстаток

));

КонецЦикла;

Ключевые особенности метода:

  • 🔄 Позволяет обрабатывать данные построчно, не загружая всё в память.
  • 🔍 Поддерживает отбор по нескольким параметрам (например, по складу и номенклатуре).
  • 📉 Может быть медленнее запросов при больших объёмах данных.

Когда использовать Выбрать():

  • 🛠️ Нужна постраничная обработка данных (например, для фонаовых задач).
  • 🔄 Требуется модификация данных "на лету" (например, корректировка остатков).

Используйте индексы для полей отбора|Ограничьте период выборки|Избегайте вложенных циклов с Выбрать()|Закрывайте выборку после использования (Выборка.Закрыть())

-->

4. Способ 3: Виртуальные таблицы в запросах — оптимальный подход

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

Основные виртуальные таблицы регистров накопления:

Тип таблицы Описание Пример использования
Остатки Текущие остатки на дату Анализ товарных запасов на складе
Обороты Движения за период (приход/расход) Отчёт о продажах за месяц
ОстаткиИОбороты Остатки на начало/конец периода + обороты Анализ динамики остатков
СрезПоследних Последние записи по измерениям Актуальные цены номенклатуры

Пример запроса для получения остатков товаров на складе:

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,

| ТоварыНаСкладахОстатки.КоличествоОстаток КАК Остаток

|ИЗ

| РегистрНакопления.ТоварыНаСкладах.Остатки(&ДатаОтчета, ) КАК ТоварыНаСкладахОстатки

|ГДЕ

| ТоварыНаСкладахОстатки.Склад = &Склад";

Запрос.УстановитьПараметр("ДатаОтчета", ТекущаяДата());

Запрос.УстановитьПараметр("Склад", Справочники.Склады.Основной);

Результат = Запрос.Выполнить();

Преимущества виртуальных таблиц:

  • Максимальная производительность — платформа оптимизирует выполнение запроса.
  • 🔧 Гибкость — можно комбинировать с другими таблицами (справочниками, документами).
  • 📊 Поддержка агрегатных функций (СУММА, МАКСИМУМ).
⚠️ Внимание: При использовании виртуальной таблицы ОстаткиИОбороты с большим периодом (например, год) запрос может зависать. Разбивайте длинные периоды на более короткие интервалы (например, помесячно).
Что делать если запрос с виртуальной таблицей выполняется слишком долго?

Если запрос к регистру накопления выполняется более 30 секунд, проверьте:

1. Наличие индексов по полям отбора (в Конфигураторе).

2. Корректность указания периода (не используйте Дата = '' для "всех дат").

3. Возможность разбиения запроса на части (например, по месяцам).

4. Наличие блокировок в базе (через Администрирование → Тестирование и исправление).

5. Способ 4: Прямой SQL-запрос (для опытных пользователей)

В редких случаях, когда стандартные методы неэффективны, можно обратиться к регистру накопления через прямой SQL-запрос. Это требует знания структуры базы данных и должно использоваться с осторожностью.

Пример SQL-запроса для получения остатков из регистра ТоварыНаСкладах (для SQL Server):

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Т.Номенклатура КАК Номенклатура,

| Т.КоличествоОстаток КАК Остаток

|ИЗ

| dbo._AccumRg12345 КАК Т // 12345 - внутренний ID регистра

|ГДЕ

| Т.Period = @Дата

| И Т.Sklad = @Склад";

Запрос.УстановитьПараметр("Дата", Формат(ТекущаяДата(), "ДЛФ=DT"));

Запрос.УстановитьПараметр("Склад", Справочники.Склады.Основной.УникальныйИдентификатор());

Результат = Запрос.Выполнить();

Опасности прямого SQL:

  • 💥 Нарушение целостности данных при некорректных изменениях.
  • 🔄 Зависимость от версии платформы (внутренние таблицы могут меняться).
  • 🚫 Отсутствие поддержки со стороны .
⚠️ Внимание: Прямые SQL-запросы могут привести к сбою поддержки конфигурации со стороны фирмы . Используйте их только в крайних случаях и обязательно фиксируйте изменения в документации.
💡

Перед использованием SQL проверьте, можно ли решить задачу стандартными средствами. Например, виртуальные таблицы ОстаткиИОбороты часто покрывают 90% потребностей в аналитике.

6. Способ 5: Использование менеджера регистра

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

  • 📝 Создавать движения по регистру.
  • 🔄 Корректировать остатки.
  • 🗑️ Проводить очистку данных.

Пример корректировки остатков:

МенеджерРегистра = РегистрыНакопления.ТоварыНаСкладах;

НаборЗаписей = МенеджерРегистра.СоздатьНаборЗаписей();

// Добавляем запись о корректировке

Запись = НаборЗаписей.Добавить();

Запись.Период = ТекущаяДата();

Запись.Склад = Справочники.Склады.Основной;

Запись.Номенклатура = Справочники.Номенклатура.Товар1;

Запись.Количество = 10; // Устанавливаем новый остаток

// Записываем изменения

НаборЗаписей.Записать();

Когда использовать менеджер регистра:

  • 🔧 Нужно программно создать движения (например, при загрузке данных из Excel).
  • 📉 Требуется массовая корректировка остатков (например, после инвентаризации).
  • 🗃️ Необходимо очистить регистр перед загрузкой новых данных.

Ограничения:

  • 🔒 Требуются права на изменение данных.
  • 📈 Может приводить к конфликтам блокировок в многопользовательском режиме.
💡

Менеджер регистра — единственный безопасный способ программно изменять остатки. Никогда не редактируйте данные регистра напрямую через SQL или консоль отладки!

7. Типичные ошибки и как их избежать

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

  • 🐢 Замедлению системы.
  • 📉 Некорректным отчётам.
  • 💥 Падению базы.

Рассмотрим самые распространённые проблемы:

Ошибка Последствия Как исправить
Использование ПолучитьДанные() в цикле по большому справочнику Зависание системы на 10+ минут Заменить на запрос с виртуальной таблицей
Отсутствие отбора по периоду в запросе Выборка всех движений за всю историю Всегда указывать Период МЕЖДУ ... И ...
Прямое изменение данных регистра через SQL Расхождение итогов, ошибки при проведении документов Использовать менеджер регистра
Игнорирование блокировок при массовых изменениях Конфликты между пользователями Использовать НачатьТранзакцию() и ЗафиксироватьТранзакцию()

Ещё одна частая ошибка — неправильная работа с виртуальными таблицами. Например, многие разработчики забывают, что таблица Остатки возвращает данные на конец периода, а не на начало. Если нужно получить остатки на начало дня, используйте:

ВЫБРАТЬ

РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата, Склад = &Склад) КАК Остатки

где &Дата — это начало дня (например, НачалоДня(ТекущаяДата())).

8. Оптимизация запросов к регистрам накопления

Чтобы запросы к регистрам накопления работали быстро, следуйте этим рекомендациям:

  • 📅 Ограничивайте период — никогда не запрашивайте данные за "всю историю" без необходимости.
  • 🔍 Используйте индексы — в Конфигураторе проверьте, что поля отбора проиндексированы.
  • 📊 Разбивайте сложные запросы — вместо одного большого запроса сделайте несколько маленьких.
  • 🔄 Избегайте вложенных циклов — если вам нужно получить данные по нескольким складам, используйте один запрос с параметром Склад В (&СписокСкладов).

Пример оптимизированного запроса для получения оборотов по нескольким складам:

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Обороты.Склад КАК Склад,

| Обороты.Номенклатура КАК Номенклатура,

| СУММА(Обороты.КоличествоПриход) КАК Приход,

| СУММА(Обороты.КоличествоРасход) КАК Расход

|ИЗ

| РегистрНакопления.ТоварыНаСкладах.Обороты(&НачалоПериода, &КонецПериода, ) КАК Обороты

|ГДЕ

| Обороты.Склад В (&СписокСкладов)

|СГРУППИРОВАТЬ ПО

| Обороты.Склад,

| Обороты.Номенклатура";

СписокСкладов = Новый Массив;

СписокСкладов.Добавить(Справочники.Склады.Основной);

СписокСкладов.Добавить(Справочники.Склады.Резервный);

Запрос.УстановитьПараметр("СписокСкладов", СписокСкладов);

Результат = Запрос.Выполнить();

Для дополнительной оптимизации:

  • 📈 Используйте ПЛАН ВЫПОЛНЕНИЯ в Консоли запросов, чтобы найти "узкие места".
  • 🗃️ Регулярно проводите Тестирование и исправление ИБ (через Администрирование).
  • 🔄 Обновляйте платформу — в новых версиях оптимизированы алгоритмы работы с регистрами.
⚠️ Внимание: Если после оптимизации запросы всё равно выполняются медленно, проверьте фрагментацию индексов в базе данных (актуально для SQL Server). Используйте скрипт:
-- Пример SQL для проверки фрагментации (только для администраторов!)

SELECT * FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED')

WHERE avg_fragmentation_in_percent > 30;

FAQ: Ответы на частые вопросы

Как получить остатки на конкретную дату, если в регистре нет движений?

Если в регистре накопления не было движений на запрашиваемую дату, виртуальная таблица Остатки вернёт пустой результат. Чтобы получить "нулевые остатки", используйте конструкцию ЛЕВОЕ СОЕДИНЕНИЕ со справочником номенклатуры:

ВЫБРАТЬ

Номенклатура.Ссылка КАК Номенклатура,

ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток

ИЗ

Справочник.Номенклатура КАК Номенклатура

ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата, ) КАК Остатки

ПО Номенклатура.Ссылка = Остатки.Номенклатура

ГДЕ

Остатки.Склад = &Склад

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

Это может происходить по нескольким причинам:

  1. Неактуальный период — проверьте, что дата в запросе соответствует нужному моменту времени.
  2. Непроведённые документы — движения по регистру создаются только после проведения документов.
  3. Кэширование данных — в редких случаях помогает перезапуск сеанса или очистка кэша.
  4. Ошибки в конфигурации — проверьте логику записей в регистр через Конфигуратор → Регистры накопления → ИмяРегистра → Движения.
Как узнать внутреннее имя регистра накопления для SQL-запроса?

Внутреннее имя регистра (например, _AccumRg12345) можно узнать:

  1. Через Конфигуратор:
    1. Откройте регистр накопления в дереве конфигурации.
    2. Нажмите F5 (Свойства) и посмотрите поле Имя таблицы БД.
  2. Через запрос к метаданным:
    Запрос = Новый Запрос("ВЫБРАТЬ ИмяТаблицыБД ИЗ Метаданные.РегистрыНакопления ГДЕ Имя = ""ТоварыНаСкладах""");

⚠️ Внимание: Внутренние имена таблиц могут меняться при обновлении конфигурации!

Можно ли получить данные из регистра накопления без указания периода?

Технически да, но это крайне не рекомендуется. Если не указать период в виртуальной таблице Остатки или Обороты, платформа:

  • Для Остатки — вернёт остатки на текущую дату (что может быть неожиданно).
  • Для Обороты — просуммирует движения за всю историю (что почти всегда избыточно).

Пример некорректного запроса (без периода):

ВЫБРАТЬ * ИЗ РегистрНакопления.ТоварыНаСкладах.Остатки(,)

Это может привести к выборке миллионов записей и зависанию системы.

Как проверить, есть ли движения в регистре накопления за период?

Чтобы узнать, были ли движения в регистре за определённый период, используйте виртуальную таблицу Обороты с агрегатной функцией ЕСТЬNULL:

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| ЕСТЬNULL(СУММА(Обороты.КоличествоПриход), 0) +

| ЕСТЬNULL(СУММА(Обороты.КоличествоРасход), 0) КАК КоличествоДвижений

|ИЗ

| РегистрНакопления.ТоварыНаСкладах.Обороты(&Начало, &Конец, ) КАК Обороты";

Запрос.УстановитьПараметр("Начало", НачалоМесяца(ТекущаяДата()));

Запрос.УстановитьПараметр("Конец", КонецМесяца(ТекущаяДата()));

Результат = Запрос.Выполнить();

Если Результат.Пустой() Тогда

Сообщить("Движений нет!");

Иначе

Если Результат[0].КоличествоДвижений = 0 Тогда

Сообщить("Движений нет!");

Иначе

Сообщить(СтрШаблон("Найдено %1 движений", Результат[0].КоличествоДвижений));

КонецЕсли;

КонецЕсли;