Почему записи подчиненного регистра сведений удаляются, а затем снова создаются при перепроведении документа в 1С?

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

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

Разберем исходную проблему и ее проявления

Автор проблемы описал следующий сценарий:

  1. Первое проведение документа: Записи регистра сведений успешно создаются.
  2. Второе проведение (перепроведение) документа: Записи по этому регистру удаляются.
  3. Третье проведение документа: Записи снова создаются.

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

Анализируем исходный код

Предположим, у нас есть процедура, формирующая движения, и вспомогательная процедура для удаления старых записей:


Процедура СформироватьДвиженияИсторииДополнительныхПоказателейШтатногоРасписания(Регистратор) Экспорт
    // Удаляем старые движения регистра
    УдалитьСтарыеЗаписиРегистра(Регистратор);

    НаборЗаписейДобавление = РегистрыСведений.ИсторияДополнительныхПоказателейШтатногоРасписания.СоздатьНаборЗаписей();
    НаборЗаписейДобавление.Отбор.Регистратор.Установить(Регистратор);
    // Определяем дату в зависимости от типа документа
    Если ТипЗнч(Регистратор) = Тип("ДокументСсылка.ИзменениеШтатногоРасписания") Тогда
        Дата = Регистратор.ДатаВступленияВСилу;
    ИначеЕсли ТипЗнч(Регистратор) = Тип("ДокументСсылка.УтверждениеШтатногоРасписания") Тогда
        Дата = Регистратор.МесяцВступленияВСилу;
    КонецЕсли;

    // Добавляем новые записи
    Для каждого СтрокаТЧ из Регистратор.Позиции Цикл
        НоваяЗапись = НаборЗаписейДобавление.Добавить();

        НоваяЗапись.Регистратор = Регистратор;
        НоваяЗапись.Дата = Дата;

        Если Не СтрокаТЧ.Позиция.Закрыта Тогда
            НоваяЗапись.Используется = Истина;
        Иначе
            НоваяЗапись.Используется = Ложь;
        КонецЕсли;

        НоваяЗапись.ПозицияШтатногоРасписания = СтрокаТЧ.Позиция;
        НоваяЗапись.КлассПозицииШтатногоРасписания = СтрокаТЧ.КлассПозицииШтатногоРасписания;
        НоваяЗапись.ПА_Категория = СтрокаТЧ.ПА_Категория;
        НоваяЗапись.ПА_Участок = СтрокаТЧ.ПА_Участок;
        НоваяЗапись.ПА_РазрядКатегорияИТР = СтрокаТЧ.ПА_РазрядКатегорияИТР;
    КонецЦикла;
    НаборЗаписейДобавление.Записать();
КонецПроцедуры

Процедура УдалитьСтарыеЗаписиРегистра(Регистратор)
    НаборЗаписейУдаления = РегистрыСведений.ИсторияДополнительныхПоказателейШтатногоРасписания.СоздатьНаборЗаписей();
    НаборЗаписейУдаления.Отбор.Регистратор.Установить(Регистратор);
    НаборЗаписейУдаления.Прочитать();
    НаборЗаписейУдаления.Записать();
КонецПроцедуры

Выясняем причину некорректного поведения

Давайте проанализируем ошибки в представленном коде:

  1. Неправильный способ создания набора записей: Основная проблема заключается в использовании метода РегистрыСведений.ИмяРегистра.СоздатьНаборЗаписей() для подчиненного регистра сведений. Для регистров, которые подчинены регистратору (документу), платформа 1С предоставляет специальный механизм работы через коллекцию движений документа.

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

  2. Ошибочная процедура удаления: Процедура УдалитьСтарыеЗаписиРегистра(Регистратор) также содержит логическую ошибку, которая усугубляет проблему. В ней мы создаем набор записей, устанавливаем отбор по регистратору, читаем записи (НаборЗаписейУдаления.Прочитать()) и сразу же их записываем (НаборЗаписейУдаления.Записать()). Если мы хотим удалить записи, то после Прочитать() необходимо явно очистить набор записей методом НаборЗаписейУдаления.Очистить(), а уже потом вызывать Записать().

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

  3. Несогласованность с механизмом платформы: Из-за того, что мы не используем коллекцию Регистратор.Движения, платформа при повторном проведении документа не "видит" наши изменения в движениях так, как должна, и не выполняет автоматическую очистку. Это приводит к наблюдаемой "чехарде" с записями.

Влияет ли создание регистра в расширении? Факт создания регистра сведений в расширении сам по себе не является причиной описанного поведения. Проблема связана именно с некорректным использованием объекта для работы с движениями, а не с местоположением регистра.

Правильный подход к работе с подчиненными регистрами сведений

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

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

  2. Целостность данных: Использование коллекции Движения документа обеспечивает, что все операции выполняются в рамках транзакции проведения документа.

  3. "Новая методика проведения": В современных конфигурациях при "новой методике проведения" документов новые движения при записи затрут старые. Это особенно актуально для сложных систем учета, где применяется, например, новая методика учета периодических возвратных регистров сведений.

Решение проблемы: Корректируем код

Мы выяснили, что ключ к решению проблемы — это использование объекта Регистратор.Движения. Чтобы ускорить процесс внесения изменений и тестирования, можно применить помощник отладки, который минимизирует время на настройку среды при каждом запуске.

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

  2. Изменяем создание набора записей: Вместо прямого обращения к менеджеру регистра, мы будем использовать коллекцию движений регистратора.

Вот как будет выглядеть исправленная процедура СформироватьДвиженияИсторииДополнительныхПоказателейШтатногоРасписания:


Процедура СформироватьДвиженияИсторииДополнительныхПоказателейШтатногоРасписания(Регистратор) Экспорт
    // Платформа 1С сама очистит старые движения документа перед вызовом этой процедуры

    // Корректное обращение к набору записей подчиненного регистра сведений
    НаборЗаписейДобавление = Регистратор.Движения.ИсторияДополнительныхПоказателейШтатногоРасписания;
    // Отбор по регистратору устанавливается автоматически при таком подходе
    НаборЗаписейДобавление.Очистить();

    // Определяем дату в зависимости от типа документа
    Перем Дата;
    Если ТипЗнч(Регистратор) = Тип("ДокументСсылка.ИзменениеШтатногоРасписания") Тогда
        Дата = Регистратор.ДатаВступленияВСилу;
    ИначеЕсли ТипЗнч(Регистратор) = Тип("ДокументСсылка.УтверждениеШтатногоРасписания") Тогда
        Дата = Регистратор.МесяцВступленияВСилу;
    КонецЕсли;

    // Добавляем новые записи
    Для каждого СтрокаТЧ из Регистратор.Позиции Цикл
        НоваяЗапись = НаборЗаписейДобавление.Добавить();

        НоваяЗапись.Регистратор = Регистратор; 
        НоваяЗапись.Дата = Дата;

        Если Не СтрокаТЧ.Позиция.Закрыта Тогда
            НоваяЗапись.Используется = Истина;
        Иначе
            НоваяЗапись.Используется = Ложь;
        КонецЕсли;

        НоваяЗапись.ПозицияШтатногоРасписания = СтрокаТЧ.Позиция;
        НоваяЗапись.КлассПозицииШтатногоРасписания = СтрокаТЧ.КлассПозицииШтатногоРасписания;
        НоваяЗапись.ПА_Категория = СтрокаТЧ.ПА_Категория;
        НоваяЗапись.ПА_Участок = СтрокаТЧ.ПА_Участок;
        НоваяЗапись.ПА_РазрядКатегорияИТР = СтрокаТЧ.ПА_РазрядКатегорияИТР;
    КонецЦикла;

    // Записываем сформированные движения.
    НаборЗаписейДобавление.Записать();
КонецПроцедуры

Обратите внимание на ключевые изменения:

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

← На главную