При работе с обменом данными между различными базами 1С, например, между типовой конфигурацией "Бухгалтерия предприятия 3.0" и самописной базой, часто возникает необходимость выборочной миграции данных. Представим ситуацию, когда нам требуется переносить не всех контрагентов и их договоры, а только тех, которые помечены специальным образом. В типовых конфигурациях существует механизм дополнительных реквизитов, который позволяет добавлять произвольные поля к объектам без изменения конфигурации. Возникает логичный вопрос: можно ли использовать такой дополнительный реквизит для отбора объектов в правилах регистрации обмена данными в "Конвертации данных 2.0"?
Мы столкнулись с задачей, когда из "Бухгалтерии 3.0" в самописную базу нужно перенести только часть контрагентов и их договоров. Чтобы не переносить всех, мы решили добавить в справочники "Контрагенты" и "Договоры контрагентов" дополнительный реквизит, например, "Участвует в обмене ИмяСамописнойБазы" типа "Булево". Цель этого реквизита — служить флагом для регистрации объектов к обмену.
На первый взгляд кажется, что это просто. Однако, при попытке настроить отбор в правилах регистрации по свойствам объекта (ПРОБ) мы сталкиваемся с несколькими ограничениями:
Эти ограничения означают, что прямой способ использования дополнительного реквизита в стандартных настройках правил регистрации не всегда применим. Но это не повод отказываться от нашей идеи. Давайте рассмотрим, как мы можем обойти эти ограничения и добиться желаемого результата.
Самым гибким и мощным способом реализации сложной логики отбора является использование программного кода непосредственно в правилах регистрации обмена данными. Механизм "Конвертации данных 2.0" позволяет писать код, который выполняется в момент регистрации объекта к обмену.
Мы можем использовать обработчик события, которое выполняется при регистрации объекта. В этом обработчике мы получаем доступ к объекту, который регистрируется, и можем проанализировать значение его дополнительного реквизита. Если дополнительный реквизит указывает, что объект не должен участвовать в обмене, мы устанавливаем параметр Отказ = Истина. Это прервет регистрацию текущего объекта.
Рассмотрим пример кода, который можно поместить в событие "При регистрации объекта" (или аналогичное, в зависимости от версии КД и конкретного правила):
// Предполагаем, что имя нашего дополнительного реквизита "УчаствуетВОбмене"
// и он имеет тип Булево.
// Получим Объект, который регистрируется.
// Например, в событии "При регистрации объекта" переменная Объект уже доступна.
// Поиск дополнительного реквизита по наименованию (лучше по ссылке на ПВХ)
// Но для демонстрации используем наименование.
// Важно: имя реквизита может храниться в ПВХ "ДополнительныеРеквизитыИСведения".
ЗначениеДопРеквизита = Ложь;
Для Каждого ДопРеквизитИзКоллекции Из Объект.ДополнительныеРеквизиты Цикл
Если ДопРеквизитИзКоллекции.Свойство.Имя = "УчаствуетВОбмене" Тогда // Пример имени, замените на фактическое
ЗначениеДопРеквизита = ДопРеквизитИзКоллекции.Значение;
Прервать;
КонецЕсли;
КонецЦикла;
Если ЗначениеДопРеквизита = Ложь Тогда
Отказ = Истина;
КонецЕсли;
В этом коде мы перебираем коллекцию дополнительных реквизитов объекта, находим наш реквизит по имени и проверяем его значение. Если оно Ложь (то есть объект не участвует в обмене), мы устанавливаем Отказ = Истина.
При первой попытке реализации этого подхода мы столкнулись с интересным поведением. После установки значения "Истина" в дополнительном реквизите "Участвует в обмене" и нажатия кнопки "Записать" в элементе справочника, объект не регистрировался к обмену. Однако, если ничего не меняя, повторно нажать "Записать", объект регистрировался. Это навело на мысль, что обработчик в правилах регистрации выполняется раньше, чем происходит фактическая запись нового значения дополнительного реквизита в базу данных.
Действительно, такая ситуация возможна. Механизм регистрации изменений в 1С может срабатывать на уровне оперативной памяти или кеша до того, как изменения будут полностью зафиксированы в БД. Если код в правилах регистрации пытается прочитать данные через запрос к БД (например, через универсальные методы получения дополнительных реквизитов), он может получить старое, еще не записанное значение.
Решение оказалось в способе получения значения дополнительного реквизита. Вместо запроса или других косвенных методов, мы обратились к дополнительному реквизиту напрямую через точку, используя коллекцию Объект.ДополнительныеРеквизиты. Этот подход позволяет получить значение из текущего состояния объекта в оперативной памяти, которое уже содержит внесенные изменения, даже если они еще не зафиксированы в БД.
Давайте посмотрим на исправленный и рабочий вариант кода, который использовал пользователь:
// В обработчике правил регистрации, например, "При регистрации объекта"
// Переменная "Объект" уже содержит текущий регистрируемый объект
// Имя нашего дополнительного реквизита, например, "УчаствуетВОбмене"
Отказ = Ложь; // По умолчанию регистрируем
// Найдем сам элемент ПВХ "ДополнительныеРеквизитыИСведения" по имени
// Это более надежный способ, чем строковое сравнение имени свойства
// В реальном коде лучше сохранить ссылку на нужный элемент ПВХ в константе или параметре
// или получить ее один раз при инициализации.
ИмяРеквизита = "УчаствуетВОбмене..."; // Полное имя реквизита из плана видов характеристик
// Получаем ссылку на элемент ПВХ, который описывает наш доп. реквизит
СсылкаНаДопРеквизит = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоРеквизиту("Имя", ИмяРеквизита);
Если СсылкаНаДопРеквизит <> Неопределено Тогда
// Получаем коллекцию дополнительных реквизитов объекта
// и ищем в ней наш реквизит по найденной ссылке
ДопРеквизитКоллекции = Объект.ДополнительныеРеквизиты.Найти(СсылкаНаДопРеквизит);
Если ДопРеквизитКоллекции <> Неопределено Тогда
// Проверяем значение доп. реквизита
Если НЕ ДопРеквизитКоллекции.Значение Тогда // Если значение Ложь, то не участвует
Отказ = Истина;
КонецЕсли;
Иначе
// Если доп. реквизит отсутствует (например, не добавлен для объекта),
// можно принять решение о регистрации или отказе по умолчанию.
// В нашем случае, если реквизита нет, считаем, что объект не должен участвовать.
Отказ = Истина;
КонецЕсли;
Иначе
// Если описание доп. реквизита не найдено в ПВХ,
// это может быть ошибка или признак того, что объект не требует отбора.
// Примем решение не регистрировать по умолчанию, чтобы избежать неконтролируемого переноса.
Отказ = Истина;
КонецЕсли;
```
В этом коде мы сначала находим сам элемент ПланаВидовХарактеристик ДополнительныеРеквизитыИСведения по имени нашего дополнительного реквизита. Затем, используя этот элемент, обращаемся к коллекции Объект.ДополнительныеРеквизиты.Найти(СсылкаНаДопРеквизит). Этот подход гарантирует, что мы получаем актуальное значение дополнительного реквизита, даже если оно было только что изменено пользователем и еще не до конца записано в базу данных. Таким образом, проблема с несвоевременным чтением значения была успешно решена.
Решение 2: Использование расширений конфигурации
Альтернативным способом, который позволяет избежать изменения основной конфигурации (и, соответственно, снятия ее с поддержки), является использование расширений конфигурации. Расширения — это мощный инструмент для доработки типовых решений 1С, который становится все более гибким с каждой новой версией платформы.
Добавление реквизита через расширение
Мы можем создать расширение для нашей типовой "Бухгалтерии 3.0" и в этом расширении добавить новый, обычный реквизит к нужным справочникам (например, "Контрагенты" и "Договоры контрагентов"). Этот реквизит будет храниться в таблицах расширения, но при этом будет доступен как обычный реквизит для объектов основной конфигурации.
- Создаем новое расширение в конфигураторе.
- Добавляем в расширение нужные справочники (например, "Справочник.Контрагенты").
- К добавленным справочникам в расширении добавляем новый реквизит, например, "МойРеквизитУчаствуетВОбмене" типа "Булево".
- Для того чтобы пользователи могли заполнять этот реквизит, нам необходимо добавить в расширение формы этих справочников и вынести новый реквизит на форму. Это может потребовать некоторой работы с формами расширений, включая их синхронизацию при обновлении основной конфигурации, о чем справедливо упоминается в обсуждении. Однако современные версии платформы 1С значительно упрощают работу с расширениями форм.
Использование реквизита из расширения в правилах обмена
После того как реквизит добавлен через расширение и доступен для заполнения, мы можем использовать его в правилах выгрузки данных. Поскольку реквизит, добавленный через расширение, ведет себя как обычный реквизит объекта, к нему можно обращаться напрямую.
В событии "Перед выгрузкой" для соответствующего правила конвертации объектов (ПКО) мы можем написать следующий код:
// В событии "Перед выгрузкой" для ПКО "Контрагенты"
// Переменная "Объект" ссылается на выгружаемый объект "Контрагент"
// Предполагаем, что наш реквизит называется "МойРеквизитУчаствуетВОбмене"
Если Объект.МойРеквизитУчаствуетВОбмене = Ложь Тогда
Отказ = Истина; // Отказываем в выгрузке, если реквизит установлен в Ложь
КонецЕсли;
// Или более кратко:
// Отказ = (Объект.МойРеквизитУчаствуетВОбмене = Ложь);
Преимущество этого подхода в том, что мы не модифицируем типовую конфигурацию, сохраняем ее на поддержке, и обновления проходят гораздо проще. Реквизит, добавленный расширением, будет восприниматься правилами конвертации данных как часть объекта. С платформы 8.3.18 и выше функциональность расширений стала еще шире, позволяя даже более глубокие изменения без снятия с поддержки.
Самым рекомендуемым и наименее инвазивным способом для добавления новых полей в типовые конфигурации 1С (основанных на Библиотеке Стандартных Подсистем – БСП) является использование встроенного механизма "Дополнительные реквизиты и сведения". Этот механизм позволяет создавать дополнительные реквизиты прямо в режиме "1С:Предприятие", без необходимости заходить в конфигуратор.
Дополнительный реквизит, созданный через механизм БСП, является частью объекта и хранится в нем. Поэтому мы можем использовать его точно так же, как в Решении 1, обращаясь к коллекции Объект.ДополнительныеРеквизиты. Код для отбора будет аналогичным:
// В обработчике правил регистрации или "Перед выгрузкой" ПКО
// Предполагаем, что наш реквизит называется "УчаствуетВОбмене"
// и он имеет тип Булево.
Отказ = Ложь;
// Получаем ссылку на элемент ПВХ, который описывает наш доп. реквизит
СсылкаНаДопРеквизит = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоРеквизиту("Имя", "УчаствуетВОбмене");
Если СсылкаНаДопРеквизит <> Неопределено Тогда
ДопРеквизитКоллекции = Объект.ДополнительныеРеквизиты.Найти(СсылкаНаДопРеквизит);
Если ДопРеквизитКоллекции <> Неопределено Тогда
Если НЕ ДопРеквизитКоллекции.Значение Тогда
Отказ = Истина;
КонецЕсли;
Иначе
Отказ = Истина;
КонецЕсли;
Иначе
Отказ = Истина;
КонецЕсли;
Этот способ является наиболее предпочтительным для типовых конфигураций, поскольку он не требует навыков программирования для создания реквизита и минимизирует риски при обновлении, так как является штатным функционалом системы.
Мы рассмотрели несколько подходов к использованию дополнительных реквизитов для отбора объектов при регистрации обмена данными в 1С:
Объект.ДополнительныеРеквизиты. Это самый прямой и гибкий способ, если дополнительный реквизит уже создан (например, через БСП) и требуется сложная логика отбора. Важно учитывать нюансы момента записи и чтения данных.Выбор конкретного решения зависит от специфики задачи, версии платформы, используемых конфигураций и требуемого уровня гибкости. В большинстве случаев для типовых конфигураций мы рекомендуем начинать с Решения 3, так как оно наименее затратно и наиболее безопасно с точки зрения поддержки. Если его функционала недостаточно, следует рассмотреть Решение 1 с использованием кода для уже существующих дополнительных реквизитов или Решение 2 с расширениями для более глубокой кастомизации.