При работе с системой 1С:Предприятие версии 8.3, особенно в конфигурациях с обычными формами, таких как УТ 10, мы часто сталкиваемся с необходимостью формирования движений по регистрам накопления. Однако иногда возникает задача создания нескольких записей в одном и том же регистре накопления в рамках одного документа. Рассмотрим ситуацию, когда к уже формируемой основной записи нам требуется добавить еще одну, дополнительную запись, при этом используя часть измерений и ресурсов, а часть оставляя незаполненной или указывая отличные значения.
Автору вопроса требовалось в обработчике события ОбработкаПроведения документа добавить возможность формирования дополнительной записи в регистр накопления. Основная запись уже создавалась по стандартному механизму. Задача состояла в том, чтобы при определенных условиях в этот же регистр, помимо первой записи, добавить еще одну. При этом дополнительная запись могла использовать не все измерения и ресурсы, и даже одно из общих измерений могло иметь разные значения по сравнению с основной записью. Казалось бы, это разные записи и движения, но, как выяснилось, стандартные подходы не позволяли создать эту дополнительную запись, так как она постоянно терялась или замещалась.
Давайте разберем подробнее, почему возникала такая проблема и как ее корректно решить.
В 1С:Предприятии регистры накопления предназначены для хранения информации, которая может быть суммирована и детализирована по комбинации измерений. Примерами таких данных могут быть остатки товаров на складах, объемы продаж, взаиморасчеты с контрагентами и так далее. Каждая запись в регистре накопления всегда привязана к оси времени и обязательно подчинена регистратору, то есть документу, который эту запись создал.
Ключевой момент, который был причиной непонимания автора и является важным для всех разработчиков, заключается в следующем: для одного регистратора (документа) в 1С создается общий набор записей для каждого конкретного регистра. Это означает, что все движения, которые формируются одним документом в один и тот же регистр накопления, будут частью одного и того же набора записей и будут иметь сквозную нумерацию строк в пределах этого регистратора. Мы не можем "разбить" этот единый набор на несколько независимых наборов, записываемых по отдельности в рамках одной транзакции проведения документа.
Автор вопроса изначально пытался добавить дополнительную запись следующим образом:
НаборДвижений = РегистрыНакопления.НазваниеРегистраНакопления.СоздатьНаборЗаписей();
НаборДвижений.Отбор.Регистратор.Установить(Ссылка);
НаборДвижений.Период = Дата;
// Заполнение полей для дополнительной записи
НаборДвижений.ТаблицаСоВсемиПолямиРегистра = ТаблицаСоВсемиЗначениямиДляВсехПолейРегистра; // Тут автор использовал свою таблицу
НаборДвижений.Записать();
При таком подходе возникала ситуация, когда после вызова НаборДвижений.Записать(), формировалась только основная запись, а дополнительная терялась. Давайте выясним причину этого поведения.
Когда мы работаем в обработчике проведения документа (ОбработкаПроведения), платформа 1С предоставляет специальный объект Движения, который является коллекцией всех движений, которые будут записаны документом по всем регистрам. Объект Движения.<ИмяРегистра> — это фактически ссылка на набор записей для конкретного регистра, связанного с текущим документом-регистратором.
Использование РегистрыНакопления.НазваниеРегистраНакопления.СоздатьНаборЗаписей() создает новый, независимый объект "Набор записей". Хотя мы и устанавливаем для него отбор по регистратору, этот набор существует отдельно от того набора движений, который собирает сам документ через свойство Движения.<ИмяРегистра>. Когда обработчик проведения завершает свою работу, стандартный механизм проведения документа автоматически записывает (или перезаписывает) все движения, собранные в объекте Движения. Если вы пытаетесь записать свой независимый набор записей до того, как сработает стандартный механизм, он будет удален и перезаписан стандартными движениями документа.
Например, если стандартная процедура проведения документа очищает и затем заполняет движения по регистру, то любые записи, сделанные вами через РегистрыНакопления.<ИмяРегистра>.СоздатьНаборЗаписей(), будут удалены и заменены набором, который формирует сам документ.
Чтобы корректно добавить дополнительную запись в тот же регистр накопления, в рамках того же документа, нам следует работать с тем же набором записей, который формируется самим документом. Для этого мы используем свойство Движения.<ИмяРегистраНакопления> объекта документа.
Рассмотрим по шагам, как это сделать:
Записывать = Истина. Если движения по регистру обычно формируются автоматически, этот флаг может быть уже установлен. Если вы добавляете движения по регистру, который по умолчанию не формирует их данным документом, этот шаг обязателен.Добавить() объекта Движения.<ИмяРегистраНакопления>, чтобы получить новую, пустую запись движения.Посмотрим на пример кода:
// В обработчике события "ОбработкаПроведения" документа
// 1. Убеждаемся, что движения по регистру будут записываться.
// Если регистр уже формирует движения, эта строка может быть необязательна,
// но для ясности и гарантии лучше ее указывать.
Движения.НазваниеРегистраНакопления.Записывать = Истина;
// Здесь происходит стандартное формирование основной записи(ей) регистра
// ... (ваш существующий код для основной записи) ...
// Пример формирования основной записи (если не сделано стандартным механизмом)
ДвижениеОсновное = Движения.НазваниеРегистраНакопления.Добавить();
ДвижениеОсновное.ВидДвижения = ВидДвиженияНакопления.Приход; // Или Расход
ДвижениеОсновное.Период = Дата;
ДвижениеОсновное.Измерение1 = "Значение1Осн";
ДвижениеОсновное.Измерение2 = "Значение2Осн";
ДвижениеОсновное.Измерение3 = "Значение3Осн";
ДвижениеОсновное.Измерение4 = "Значение4Осн";
ДвижениеОсновное.Измерение5 = "Значение5Осн";
ДвижениеОсновное.Измерение6 = "Значение6Осн";
ДвижениеОсновное.Измерение7 = "Значение7Осн";
ДвижениеОсновное.Ресурс1 = 100;
ДвижениеОсновное.Ресурс2 = 50;
// 2. Добавляем ДОПОЛНИТЕЛЬНУЮ запись
// Предположим, мы добавляем ее при выполнении некоторого условия
Если УсловиеДляДополнительнойЗаписи Тогда
ДвижениеДополнительное = Движения.НазваниеРегистраНакопления.Добавить();
ДвижениеДополнительное.ВидДвижения = ВидДвиженияНакопления.Приход; // Или Расход, может отличаться от основной
ДвижениеДополнительное.Период = Дата;
// Заполняем 4 измерения и 1 ресурс, как того требует задача
ДвижениеДополнительное.Измерение1 = "Значение1Доп"; // Это измерение может иметь другое значение
ДвижениеДополнительное.Измерение2 = "Значение2Доп";
ДвижениеДополнительное.Измерение3 = "Значение3Доп";
ДвижениеДополнительное.Измерение4 = "Значение4Доп";
// Измерения 5, 6, 7 остаются незаполненными (пустыми) или содержат значения по умолчанию
ДвижениеДополнительное.Ресурс1 = 25; // Заполняем только один ресурс
// Ресурс2 остается незаполненным (нулевым)
// Если измерения 5,6,7 для этой записи не используются, они будут иметь значения по умолчанию
// (например, Неопределено для ссылочных типов, пустая строка для строк, 0 для чисел)
КонецЕсли;
// Все эти записи будут объединены в один набор и записаны вместе
// при завершении проведения документа.
Таким образом, использование объекта Движения.<ИмяРегистраНакопления> гарантирует, что все созданные движения будут объединены в единый набор записей, который будет связан с текущим документом и записан как единое целое в конце процедуры проведения. Это позволяет избежать проблем с перезаписью или потерей данных.
Как мы уже упоминали, для одного регистратора в одном регистре создается общий набор записей. Это означает, что если вы добавите несколько движений через Движения.<ИмяРегистраНакопления>.Добавить(), они получат сквозную нумерацию строк. Например, если основная запись имеет номер 1, то дополнительная запись, добавленная после нее, получит номер 2, и так далее.
При перепроведении документа платформа 1С автоматически удаляет все старые записи движений, которые были связаны с этим регистратором, а затем записывает новые движения, сформированные в текущем цикле проведения. Это стандартное поведение платформы, которое обеспечивает консистентность данных и исключает дублирование при многократном перепроведении одного и того же документа.
При работе с регистрами накопления, особенно при больших объемах данных, важно учитывать вопросы производительности:
Движения.<ИмяРегистра> изначально спроектирован для этого, собирая все движения в одну коллекцию, которая затем записывается однократно.РежимЗамещения.Обновление. Этот режим позволяет производить только обновление существующих записей, совпадающих по ключевым полям, без добавления новых. Это может значительно ускорить процесс записи в определенных сценариях, но требует внимательного использования и понимания его механики.Важно помнить, что регистры накопления отличаются от регистров сведений. Регистры накопления всегда подчинены регистратору и предназначены для учета остатков или оборотов. Регистры сведений же могут быть как подчиненными регистратору, так и независимыми, и обычно используются для хранения произвольной справочной информации или периодических данных, которые не подлежат суммированию в таком же смысле, как в регистрах накопления.
Поле "Реквизит" в регистрах накопления не влияет на значения ресурсов и не используется для расчета итогов. Оно служит для хранения дополнительной информации или для аналитического анализа конкретных движений. Это может быть полезно, если вы хотите добавить какие-то комментарии или классификаторы к каждой записи движения, которые не являются частью измерений или ресурсов.
Таким образом, основная идея заключается в том, что внутри ОбработкаПроведения мы всегда работаем с единым объектом Движения.<ИмяРегистраНакопления>. Добавляя в него записи, мы гарантируем, что они будут корректно сохранены документом как часть его движений, получат сквозную нумерацию и будут участвовать в автоматической обработке при перепроведении.