Как программно изменить периодический реквизит в 1С 7.7 с привязкой к документу без полного перепроведения?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

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

Давайте подробно разберем, почему возникает эта проблема, и рассмотрим два эффективных способа ее решения: через прямое редактирование базы данных (SQL/DBF) и с помощью безопасного "технического" проведения документа без изменения его основных движений по регистрам.

Выясняем причину проблемы: почему не работает стандартный код?

Посмотрим на типичную попытку решения задачи через стандартный объект Периодический, которую часто используют начинающие разработчики:


СпрТовары = СоздатьОбъект("Справочник.Товары");
СпрТовары.НайтиЭлемент(ВыбТовар);

Пер = СоздатьОбъект("Периодический");
Пер.ИспользоватьОбъект("ЦенаУч", СпрТовары);
Пер.ВыбратьЗначения();
Пока Пер.ПолучитьЗначение() = 1 Цикл
    // Попытка напрямую записать документ-регистратор вызовет ошибку!
    Пер.ТекущийДокумент() = ТЗ_Форма.Документ; 
    Пер.НомерСтроки() = ТЗ_Форма.НомСтроки;
    Пер.Записать();
КонецЦикла;

При выполнении этого кода система выдаст ошибку времени выполнения. Дело в том, что методы ТекущийДокумент() и НомерСтроки() у объекта Периодический работают только на чтение. Платформа 1С 7.7 спроектирована так, что связать периодическое значение с документом-регистратором можно исключительно в момент проведения этого документа с помощью встроенной функции УстановитьРеквизитСправочника().

Если нам категорически нельзя перепроводить документ (чтобы не сдвинуть точку актуальности, не нарушить границу последовательности и не перезаписать движения по регистрам остатков), нам придется прибегнуть к альтернативным методам.

---

Способ 1. Прямая запись в таблицу периодических реквизитов (SQL / DBF)

Этот способ является самым быстрым и не требует изменения метаданных конфигурации. Мы запишем необходимые связи напрямую в базу данных. Вся история периодических реквизитов в 1С 7.7 хранится в одной таблице — `_1SCONST` (для SQL-версии) или в файле `1SCONST.DBF` (для файловой версии).

Проанализируем структуру таблицы _1SCONST:

Рассмотрим пример кода на встроенном языке с использованием внешней компоненты 1С++ для прямого выполнения SQL-запроса, который свяжет существующее "ручное" периодическое значение с документом прихода:


// Инициализируем объект для работы с прямыми запросами SQL
ЗапросSQL = СоздатьОбъект("ODBCRecordset");

// Получаем внутренний ID документа и товара
ВнутрIDТовара = ЗначениеВСтрокуВнутр(ВыбТовар);
// Извлекаем чистый 9-символьный ID элемента справочника
IDТовара = Сред(ВнутрIDТовара, 11, 9); 

ВнутрIDДок = ЗначениеВСтрокуВнутр(ВыбДокумент);
// Извлекаем чистый 9-символьный IDDOC документа
IDDoc = Сред(ВнутрIDДок, 11, 9);

// ID периодического реквизита "ЦенаУч" из файла 1Cv7.DD (например, 412)
IDРеквизита = "  412"; 

// Текст SQL-запроса для обновления записи
ТекстЗапроса = "
|UPDATE _1SCONST
|SET 
|   DOCID = :IDDoc,
|   LINENO_ = :НомерСтроки
|WHERE 
|   OBJID = :IDТовара 
|   AND ID = :IDРеквизита 
|   AND DATE = :ДатаПараметр
|";

ЗапросSQL.УстановитьТекстовыйПараметр("IDDoc", IDDoc);
ЗапросSQL.УстановитьТекстовыйПараметр("НомерСтроки", ТЗ_Форма.НомСтроки);
ЗапросSQL.УстановитьТекстовыйПараметр("IDТовара", IDТовара);
ЗапросSQL.УстановитьТекстовыйПараметр("IDРеквизита", IDРеквизита);
ЗапросSQL.УстановитьТекстовыйПараметр("ДатаПараметр", ВыбДата);

Попытка
    ЗапросSQL.ВыполнитьИнструкцию(ТекстЗапроса);
    Предупреждение("Связь с документом успешно установлена!");
Исключение
    Сообщить("Ошибка выполнения прямого запроса: " + ОписаниеОшибки(), "!");
КонецПопытки;

Важное примечание: Если вы используете файловую версию базы данных (DBF), вам потребуется использовать объект XBase для ручного позиционирования на нужной строке файла 1SCONST.DBF по индексам и изменения соответствующих полей записи.

---

Способ 2. "Техническое проведение" документа (Штатный метод)

Если вы не хотите использовать прямые запросы к СУБД из-за рисков нарушения ссылочной целостности, мы можем использовать абсолютно штатный механизм 1С. Основная идея заключается в том, чтобы провести документ в особом "техническом" режиме, при котором реальные движения по регистрам (остатки, партии) записываться не будут, а запишется только периодический реквизит.

Выполним решение по шагам:

Шаг 1. Отключение автоматического удаления движений.
В конфигураторе найдите ваш документ прихода товара. Откройте его свойства и найдите пункт "Автоудаление движений". Переключите его в состояние "Не удалять" (или "Не очищать автоматически"). Это критически важно: теперь при повторном проведении документа система не будет автоматически очищать его существующие движения по регистрам накопления.

Шаг 2. Модификация процедуры ОбработкаПроведения().
Откроем модуль документа и перепишем логику проведения так, чтобы документ умел проводить только периодические реквизиты, не затрагивая регистры учета. Для этого мы будем использовать дополнительный параметр проведения:


Процедура ОбработкаПроведения(ВидПроведения)
    
    // Проверяем, запущен ли документ в режиме обновления периодики
    Если ВидПроведения = "ТолькоПериодика" Тогда
        
        // Очищаем только историю периодических реквизитов, созданную этим документом
        // (Остальные движения по регистрам остаются нетронутыми!)
        ОчиститьДвижения(); 
        
        // Перебираем строки табличной части и обновляем реквизит
        ВыбратьСтроки();
        Пока ПолучитьСтроку() = 1 Цикл
            // Используем штатный метод связи реквизита с документом
            УстановитьРеквизитСправочника(Товар, "ЦенаУч", НоваяЦена, ДатаДок);
        КонецЦикла;
        
        // Завершаем процедуру проведения, чтобы не выполнять движения по регистрам
        Возврат;
        
    КонецЕсли;
    
    // --- НАЧАЛО СТАНДАРТНОГО АЛГОРИТМА ПРОВЕДЕНИЯ ---
    // Здесь располагается ваш стандартный код проведения документа:
    // Регистрация движений по регистрам "ОстаткиТоваров", "Партии" и т.д.
    // ...
    // --- КОНЕЦ СТАНДАРТНОГО АЛГОРИТМА ПРОВЕДЕНИЯ ---
    
КонецПроцедуры

Шаг 3. Запуск технического проведения.
Теперь напишем внешнюю обработку или код, который будет запускать наше "легкое" техническое проведение для нужного документа:


Док = СоздатьОбъект("Документ.Приходный");
Если Док.НайтиДокумент(ВыбДокумент) = 1 Тогда
    // Передаем строковый параметр в метод Провести()
    Если Док.Провести("ТолькоПериодика") = 1 Тогда
        Сообщить("Периодический реквизит успешно связан с документом " + Док.ТекущийДокумент());
    Иначе
        Сообщить("Не удалось провести документ в техническом режиме!", "!");
    КонецЕсли;
КонецЕсли;

---

Анализируем риски и важные особенности работы с периодикой

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

  1. Опасность отмены проведения: Если вы связали периодический реквизит в таблице базы данных с документом (неважно — прямым SQL-запросом или техническим проведением), помните: при любой отмене проведения этого документа платформа автоматически удалит все связанные с ним строки в таблице `_1SCONST`. Это поведение "зашито" в ядро 1С 7.7.
  2. Влияние утилиты "Тестирование и исправление" (ТиИС): Если вы связали периодический реквизит справочника с документом напрямую через SQL, но в конфигураторе в свойствах этого периодического реквизита не установлен флаг "Изменяется документами", то при первом же запуске штатного тестирования и исправления базы данных эти связи будут признаны некорректными и удалены платформой. Перед работой всегда проверяйте настройки реквизита в конфигураторе.

Выбор метода зависит от ваших задач: если документов очень много, а базу монопольно заблокировать нельзя — используйте безопасный штатный метод технического проведения (Способ 2). Если же вам нужно быстро исправить единичные исторические данные "на лету" без доработки метаданных конфигурации — отлично подойдет прямой SQL-апдейт (Способ 1).

← На главную