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