В платформе 1С:Предприятие 7.7 периодические реквизиты справочников представляют собой удобный механизм для хранения истории изменений данных (например, цен номенклатуры, курсов валют или ставок налогов). Однако при работе с ними разработчики часто сталкиваются с жестким ограничением платформы: связать значение периодического реквизита с конкретным документом (чтобы в истории отображались поля ТекущийДокумент, НомерДок и НомерСтроки) стандартным кодом записи периодического значения невозможно. Такие поля доступны только для чтения.
Давайте подробно разберем, почему возникает эта проблема, и рассмотрим два эффективных способа ее решения: через прямое редактирование базы данных (SQL/DBF) и с помощью безопасного "технического" проведения документа без изменения его основных движений по регистрам.
Посмотрим на типичную попытку решения задачи через стандартный объект Периодический, которую часто используют начинающие разработчики:
СпрТовары = СоздатьОбъект("Справочник.Товары");
СпрТовары.НайтиЭлемент(ВыбТовар);
Пер = СоздатьОбъект("Периодический");
Пер.ИспользоватьОбъект("ЦенаУч", СпрТовары);
Пер.ВыбратьЗначения();
Пока Пер.ПолучитьЗначение() = 1 Цикл
// Попытка напрямую записать документ-регистратор вызовет ошибку!
Пер.ТекущийДокумент() = ТЗ_Форма.Документ;
Пер.НомерСтроки() = ТЗ_Форма.НомСтроки;
Пер.Записать();
КонецЦикла;
При выполнении этого кода система выдаст ошибку времени выполнения. Дело в том, что методы ТекущийДокумент() и НомерСтроки() у объекта Периодический работают только на чтение. Платформа 1С 7.7 спроектирована так, что связать периодическое значение с документом-регистратором можно исключительно в момент проведения этого документа с помощью встроенной функции УстановитьРеквизитСправочника().
Если нам категорически нельзя перепроводить документ (чтобы не сдвинуть точку актуальности, не нарушить границу последовательности и не перезаписать движения по регистрам остатков), нам придется прибегнуть к альтернативным методам.
---
Этот способ является самым быстрым и не требует изменения метаданных конфигурации. Мы запишем необходимые связи напрямую в базу данных. Вся история периодических реквизитов в 1С 7.7 хранится в одной таблице — `_1SCONST` (для SQL-версии) или в файле `1SCONST.DBF` (для файловой версии).
Проанализируем структуру таблицы _1SCONST:
1Cv7.DD или 1Cv7.DDS).LINENO в DBF) — номер строки табличной части документа, к которой привязывается значение.Рассмотрим пример кода на встроенном языке с использованием внешней компоненты 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 по индексам и изменения соответствующих полей записи.
---
Если вы не хотите использовать прямые запросы к СУБД из-за рисков нарушения ссылочной целостности, мы можем использовать абсолютно штатный механизм 1С. Основная идея заключается в том, чтобы провести документ в особом "техническом" режиме, при котором реальные движения по регистрам (остатки, партии) записываться не будут, а запишется только периодический реквизит.
Выполним решение по шагам:
Шаг 1. Отключение автоматического удаления движений.
В конфигураторе найдите ваш документ прихода товара. Откройте его свойства и найдите пункт "Автоудаление движений". Переключите его в состояние "Не удалять" (или "Не очищать автоматически"). Это критически важно: теперь при повторном проведении документа система не будет автоматически очищать его существующие движения по регистрам накопления.
Шаг 2. Модификация процедуры ОбработкаПроведения().
Откроем модуль документа и перепишем логику проведения так, чтобы документ умел проводить только периодические реквизиты, не затрагивая регистры учета. Для этого мы будем использовать дополнительный параметр проведения:
Процедура ОбработкаПроведения(ВидПроведения)
// Проверяем, запущен ли документ в режиме обновления периодики
Если ВидПроведения = "ТолькоПериодика" Тогда
// Очищаем только историю периодических реквизитов, созданную этим документом
// (Остальные движения по регистрам остаются нетронутыми!)
ОчиститьДвижения();
// Перебираем строки табличной части и обновляем реквизит
ВыбратьСтроки();
Пока ПолучитьСтроку() = 1 Цикл
// Используем штатный метод связи реквизита с документом
УстановитьРеквизитСправочника(Товар, "ЦенаУч", НоваяЦена, ДатаДок);
КонецЦикла;
// Завершаем процедуру проведения, чтобы не выполнять движения по регистрам
Возврат;
КонецЕсли;
// --- НАЧАЛО СТАНДАРТНОГО АЛГОРИТМА ПРОВЕДЕНИЯ ---
// Здесь располагается ваш стандартный код проведения документа:
// Регистрация движений по регистрам "ОстаткиТоваров", "Партии" и т.д.
// ...
// --- КОНЕЦ СТАНДАРТНОГО АЛГОРИТМА ПРОВЕДЕНИЯ ---
КонецПроцедуры
Шаг 3. Запуск технического проведения.
Теперь напишем внешнюю обработку или код, который будет запускать наше "легкое" техническое проведение для нужного документа:
Док = СоздатьОбъект("Документ.Приходный");
Если Док.НайтиДокумент(ВыбДокумент) = 1 Тогда
// Передаем строковый параметр в метод Провести()
Если Док.Провести("ТолькоПериодика") = 1 Тогда
Сообщить("Периодический реквизит успешно связан с документом " + Док.ТекущийДокумент());
Иначе
Сообщить("Не удалось провести документ в техническом режиме!", "!");
КонецЕсли;
КонецЕсли;
---
При работе с периодическими значениями в 1С 7.7 необходимо всегда помнить о внутренней архитектуре платформы:
Выбор метода зависит от ваших задач: если документов очень много, а базу монопольно заблокировать нельзя — используйте безопасный штатный метод технического проведения (Способ 2). Если же вам нужно быстро исправить единичные исторические данные "на лету" без доработки метаданных конфигурации — отлично подойдет прямой SQL-апдейт (Способ 1).