Решение задач 1С:Предприятие 8 на примерах

Регистры в 1С: виды регистров и движения документа по регистру накопления

АлгоритмыОпубликовано

Регистры — основа учёта в 1С: на них строятся остатки товаров, взаиморасчёты, продажи, цены и зарплата. Здесь собрано, чем отличаются виды регистров и как выбрать нужный, а затем показано на готовом коде, как документ делает движения по регистру накопления, как при проведении проверить остатки и как прочитать регистр запросом через виртуальные таблицы.

Задача: выбрать регистр и корректно писать в него движения

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

Виды регистров в 1С и чем они отличаются

В 1С:Предприятии 8.3 четыре вида регистров. Каждый решает свою задачу, и подменять один другим неудобно: у них разный набор свойств и разный способ чтения данных.

Вид регистраЧто хранитСвязь с документамиТипичное применение
Регистр сведенийЗначения по ключу измерений, обычно на датуНезависимый или подчинённый регистраторуЦены, курсы валют, настройки, статусы
Регистр накопления, вид «Остатки»Приход и расход, остатки нарастающим итогомВсегда подчинён регистраторуТовары на складах, деньги, взаиморасчёты
Регистр накопления, вид «Обороты»Только обороты за периодВсегда подчинён регистраторуПродажи, выручка, затраты
Регистр бухгалтерииПроводки: дебет, кредит, суммаПодчинён регистратору, связан с планом счетовБухгалтерский учёт
Регистр расчётаНачисления и удержания с периодом действияПодчинён регистратору, связан с планом видов расчётаРасчёт зарплаты

Правило выбора простое. Нужна история значений на даты — регистр сведений (подробнее о записи в него — в статье про запись в регистр сведений программно). Нужно знать, сколько чего осталось на складе или на расчётном счёте, — регистр накопления «Остатки». Нужна только статистика продаж без остатков — регистр накопления «Обороты»: он занимает меньше места и работает быстрее, потому что не хранит итоги остатков.

Структура регистра накопления

Данные регистра накопления делятся на три группы. Измерения — признаки, по которым группируются данные (номенклатура, склад). Ресурсы — числовые значения, которые суммируются (количество, сумма). Реквизиты — дополнительная информация, по которой итоги не считаются. Кроме них у каждой записи есть период, регистратор и номер строки, а у регистров вида «Остатки» ещё и вид движения — приход или расход.

В примерах ниже используется регистр накопления ТоварыНаСкладах вида «Остатки» с измерениями Номенклатура и Склад и ресурсом Количество. Документ ПоступлениеТоваров имеет реквизит шапки Склад и табличную часть Товары с колонками Номенклатура и Количество. Чтобы документ мог делать движения, в его свойствах на вкладке «Движения» нужно отметить этот регистр.

Регистры остатков устроены так, что платформа дополнительно хранит предрассчитанные итоги по границам периодов. Благодаря этому остаток на любую дату собирается из готового итога и небольшого числа свежих записей, а не суммированием всей истории движений. Именно поэтому регистр накопления читают через виртуальные таблицы, а не обычным запросом по всем записям. Если итоги когда-либо расходятся с движениями, в конфигураторе есть служебная команда их пересчёта.

Движения документа по регистру: приход в ОбработкаПроведения

Движения пишут в обработчике ОбработкаПроведения модуля объекта документа. У документа есть коллекция Движения, в ней — набор записей каждого регистра. Порядок работы такой: включить запись набора, добавить строки, заполнить поля. Саму запись в базу платформа выполнит после завершения обработчика.

Процедура ОбработкаПроведения(Отказ, РежимПроведения)

    Движения.ТоварыНаСкладах.Записывать = Истина;

    Для Каждого СтрокаТовара Из Товары Цикл

        Движение = Движения.ТоварыНаСкладах.Добавить();
        Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
        Движение.Период = Дата;
        Движение.Номенклатура = СтрокаТовара.Номенклатура;
        Движение.Склад = Склад;
        Движение.Количество = СтрокаТовара.Количество;

    КонецЦикла;

КонецПроцедуры

Строка Движения.ТоварыНаСкладах.Записывать = Истина обязательна. Она не только включает запись, но и заставляет платформу при перепроведении удалить прежние движения документа и записать новые. Если забыть этот флаг, регистр не изменится, а при отмене проведения старые записи не удалятся. Поле ВидДвижения принимает значения ВидДвиженияНакопления.Приход и ВидДвиженияНакопления.Расход. Период обычно берут из даты документа.

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

При отмене проведения писать код не нужно: платформа сама удаляет все движения документа по всем регистрам, в которые он делает записи. Исключение — собственные данные, которые вы записали мимо движений, например в независимый регистр сведений. Их очищают в обработчике ОбработкаУдаленияПроведения.

Расход и контроль остатков при проведении

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

Процедура ОбработкаПроведения(Отказ, РежимПроведения)

    // Блокировка не даёт параллельным документам списать те же остатки
    Блокировка = Новый БлокировкаДанных;
    ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.ТоварыНаСкладах");
    ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
    ЭлементБлокировки.УстановитьЗначение("Склад", Склад);
    Блокировка.Заблокировать();

    Движения.ТоварыНаСкладах.Записывать = Истина;

    Для Каждого СтрокаТовара Из Товары Цикл
        Движение = Движения.ТоварыНаСкладах.Добавить();
        Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
        Движение.Период = Дата;
        Движение.Номенклатура = СтрокаТовара.Номенклатура;
        Движение.Склад = Склад;
        Движение.Количество = СтрокаТовара.Количество;
    КонецЦикла;

    // Запись нужна, чтобы запрос увидел новые движения
    Движения.ТоварыНаСкладах.Записать();

    Запрос = Новый Запрос;
    Запрос.Текст =
    "ВЫБРАТЬ
    |    Остатки.Номенклатура КАК Номенклатура,
    |    Остатки.КоличествоОстаток КАК КоличествоОстаток
    |ИЗ
    |    РегистрНакопления.ТоварыНаСкладах.Остатки(
    |        ,
    |        Склад = &Склад
    |        И Номенклатура В (&Номенклатура)) КАК Остатки
    |ГДЕ
    |    Остатки.КоличествоОстаток < 0";
    Запрос.УстановитьПараметр("Склад", Склад);
    Запрос.УстановитьПараметр("Номенклатура", Товары.ВыгрузитьКолонку("Номенклатура"));

    Выборка = Запрос.Выполнить().Выбрать();
    Пока Выборка.Следующий() Цикл
        Сообщение = Новый СообщениеПользователю;
        Сообщение.Текст = СтрШаблон("Не хватает товара «%1»: после списания остаток составит %2.",
            Выборка.Номенклатура, Выборка.КоличествоОстаток);
        Сообщение.Сообщить();
        Отказ = Истина;
    КонецЦикла;

КонецПроцедуры

Установка Отказ = Истина отменяет проведение, а транзакция откатывается вместе с записанными движениями. Условие по складу и номенклатуре передано в параметры виртуальной таблицы, а не в ГДЕ: так платформа отбирает данные ещё при расчёте итогов и запрос выполняется быстрее. Если в конфигурации включён автоматический режим управления блокировками, явная блокировка не обязательна, но в управляемом режиме без неё параллельные проведения могут создать отрицательные остатки. Про параметры запросов и выполнение — в статье про запросы в 1С.

Чтение регистра: Остатки, Обороты, ОстаткиИОбороты

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

// Остатки на дату
ВЫБРАТЬ
    Остатки.Номенклатура КАК Номенклатура,
    Остатки.КоличествоОстаток КАК КоличествоОстаток
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки(&НаДату, Склад = &Склад) КАК Остатки

// Обороты за период по месяцам
ВЫБРАТЬ
    Обороты.ПериодМесяц КАК Месяц,
    Обороты.Номенклатура КАК Номенклатура,
    Обороты.КоличествоПриход КАК Приход,
    Обороты.КоличествоРасход КАК Расход
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Обороты(&Начало, &Конец, Месяц, Склад = &Склад) КАК Обороты

// Начальный остаток, обороты и конечный остаток
ВЫБРАТЬ
    ОстаткиИОбороты.Номенклатура КАК Номенклатура,
    ОстаткиИОбороты.КоличествоНачальныйОстаток КАК НачальныйОстаток,
    ОстаткиИОбороты.КоличествоПриход КАК Приход,
    ОстаткиИОбороты.КоличествоРасход КАК Расход,
    ОстаткиИОбороты.КоличествоКонечныйОстаток КАК КонечныйОстаток
ИЗ
    РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(&Начало, &Конец, , , Склад = &Склад) КАК ОстаткиИОбороты

Параметры Остатки: момент времени и условие. Если момент не указан, берётся текущее состояние. У Обороты параметры такие: начало и конец периода, периодичность и условие; без периодичности возвращается один итог за весь период. У ОстаткиИОбороты между периодичностью и условием стоит ещё метод добавления периодов. Имена полей строятся из имени ресурса и суффикса: Остаток, Приход, Расход, Оборот, НачальныйОстаток, КонечныйОстаток. Если период передают в виде даты, помните о границе дня — об этом в статье про дату в запросе.

Регистр оборотов

У регистра накопления вида «Обороты» нет остатков и нет вида движения: запись просто добавляется со своими ресурсами. Это удобно для продаж и других данных, где нужна лишь статистика за период.

Движения.Продажи.Записывать = Истина;

Для Каждого СтрокаТовара Из Товары Цикл
    Движение = Движения.Продажи.Добавить();
    Движение.Период = Дата;
    Движение.Номенклатура = СтрокаТовара.Номенклатура;
    Движение.Контрагент = Контрагент;
    Движение.Количество = СтрокаТовара.Количество;
    Движение.Сумма = СтрокаТовара.Сумма;
КонецЦикла;

Читают такой регистр только виртуальной таблицей Обороты, а поля называются КоличествоОборот и СуммаОборот. Обращение к Остатки для регистра оборотов завершится ошибкой, что таблица не найдена.

Регистры бухгалтерии и регистры расчёта

Регистр бухгалтерии хранит проводки. Движение заполняют счетом дебета, счетом кредита и суммой; учёт по субконто и количеству ведут через дополнительные поля, зависящие от плана счетов.

Проводка = Движения.Хозрасчетный.Добавить();
Проводка.Период = Дата;
Проводка.СчетДт = ПланыСчетов.Хозрасчетный.НайтиПоКоду("41.01");
Проводка.СчетКт = ПланыСчетов.Хозрасчетный.НайтиПоКоду("60.01");
Проводка.Сумма = СуммаДокумента;

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

Начисление = Движения.Начисления.Добавить();
Начисление.ПериодРегистрации = НачалоМесяца(Дата);
Начисление.ВидРасчета = ПланыВидовРасчета.Начисления.Оклад;
Начисление.ПериодДействияНачало = НачалоМесяца(Дата);
Начисление.ПериодДействияКонец = КонецМесяца(Дата);
Начисление.Сотрудник = Сотрудник;
Начисление.Результат = Сумма;

Типичные ошибки

  • Не установлен флаг Записывать — движения не записываются, а при перепроведении остаются прежние.
  • Документ не отмечен в регистре на вкладке «Движения» — обращение к Движения.Регистр вызывает ошибку.
  • Проверка остатков до записи движений: запрос не видит расход текущего документа и пропускает отрицательный остаток.
  • Условия по измерениям вынесены в ГДЕ вместо параметров виртуальной таблицы — запрос по большому регистру работает заметно медленнее.
  • Попытка читать остатки из регистра вида «Обороты» или писать ВидДвижения в такой регистр.
  • Запись движений в ПриЗаписи вместо ОбработкаПроведения: движения появятся у непроведённого документа.

Актуальность для новых версий

Все описанные регистры и приёмы работают во всех версиях платформы 8.3, включая 8.3.2x. Функция СтрШаблон() доступна начиная с 8.3.6 — в более старых версиях собирайте текст сообщения конкатенацией. Имена регистров, измерений и ресурсов в примерах (ТоварыНаСкладах, Продажи) условные: подставьте свои. Модель блокировок (автоматическая или управляемая) задаётся в свойствах конфигурации, и от неё зависит, нужна ли явная блокировка данных при проведении.

Частые вопросы

Чем регистр накопления отличается от регистра сведений?

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

Зачем нужно Движения.Регистр.Записывать = Истина?

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

Как проверить остатки перед списанием в ОбработкаПроведения?

Установите блокировку данных, добавьте движения расхода, вызовите Движения.Регистр.Записать() и запросом к виртуальной таблице Остатки найдите отрицательные остатки. Если они есть, выдайте сообщение и поставьте Отказ = Истина.

В чём разница между виртуальными таблицами Остатки, Обороты и ОстаткиИОбороты?

Остатки возвращают состояние регистра на момент времени, Обороты — суммы прихода и расхода за период, а ОстаткиИОбороты — начальный и конечный остаток вместе с оборотами по периодам. Таблицы Остатки и ОстаткиИОбороты есть только у регистров вида «Остатки».

Расширение конфигурации 1С: создание, установка и перехват методов

Алгоритмы

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

Запись в регистр сведений 1С программно: менеджер записи, набор записей и удаление

Алгоритмы

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

Таблица значений в 1С: создание, поиск строк, передача в запрос и на форму

Алгоритмы

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

Хеширование HMAC-SHA1 и HMAC-SHA256 средствами 1С

Алгоритмы

Готовая функция на языке 1С, которая считает HMAC-SHA1 и HMAC-SHA256 и возвращает результат в Hex и Base64. Пригодится для подписи запросов к сторонним API без внешних компонент.