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

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

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

Однако при проектировании такого движка возникает сложная техническая задача: как динамически определять аналитику (субконто) и суммы проводок, если логика их получения зависит от множества условий? Например, если в строке документа заполнено ОтделениеСтрока, то в проводку должно транслироваться именно оно, а если пусто — значение по умолчанию из шапки документа (Отделение), либо аналитика вовсе зависит от сочетания реквизитов Контрагент и ТМЦ. В этой статье мы подробно разберем, как решить эту проблему различными методами.

Архитектурная концепция гибкого движка проводок

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

Основная сложность заключается в вычислении «правил определения субконто». Рассмотрим пошагово четыре практических метода реализации этой логики.

Решение 1. Динамическая компиляция кода через 1С++ (класс «ВыполняемыйМодуль»)

Это наиболее мощный и профессиональный метод для платформы 1С 7.7. Поскольку штатного метода Выполнить() в семерке нет, для динамической компиляции многострочного кода из справочника мы будем использовать внешнюю компоненту 1С++ и ее класс ВыполняемыйМодуль.

Этот подход позволяет хранить полноценный программный код на встроенном языке 1С непосредственно в реквизите справочника (тип «Строка» неограниченной длины) и выполнять его в контексте проведения документа.

Разберем пример реализации этого механизма. В модуле проведения документа мы пишем универсальный обработчик, который считывает формулу из карточки настройки субконто и выполняет ее:


// Переменные для формирования динамического модуля
Перем ЗаголовокМодуля, КонецМодуля, РС;

РС = РазделительСтрок;    
ЗаголовокМодуля = "Перем Документ;" + РС + "Перем ТекущийДокумент;" + РС + "Перем Отказ;" + РС + "Перем Параметры;" + РС + "Процедура ВыполнитьУУ()" + РС;
КонецМодуля = РС + "КонецПроцедуры" + РС + "Отказ=0;" + РС + "Параметры=СоздатьОбъект(""СписокЗначений"");" + РС;    

// Считываем код настройки субконто из справочника проводок
КодНастройки = СокрЛП(СпрПроводка.Код1С);

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

В самом справочнике проводок в поле Код1С пользователь или внедренец может написать гибкий алгоритм любой сложности:


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

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

Недостатки: Требуется наличие зарегистрированной внешней компоненты 1cpp.dll на рабочих станциях. Ошибки в коде справочника могут привести к аварийному завершению проведения.

Решение 2. Использование штатной функции Шаблон()

Если мы не хотим использовать внешние компоненты, можно задействовать встроенную функцию платформы 1С 7.7 Шаблон(). Эта функция вычисляет выражения, заключенные в квадратные скобки. Мы можем хранить простые формулы-условия прямо в текстовом поле справочника.

Например, в поле настройки источника субконто справочника мы записываем текстовую строку:

"?[ПустоеЗначение(ОтделениеСтрока)=0, ОтделениеСтрока, Отделение]"

В движке проведения мы обрабатываем эту строку следующим образом:


// Вычисляем значение формулы с помощью штатного механизма шаблонов
ТекстФормулы = СокрЛП(СпрПроводка.ФормулаСубконто); // Строка вида: "?(Врач.Выбран()=1, Врач, Контрагент)"
Попытка
    ВычисленноеСубконто = Шаблон("[" + ТекстФормулы + "]");
Исключение
    Сообщить("Ошибка вычисления шаблона субконто: " + ОписаниеОшибки(), "!");
    ВычисленноеСубконто = ПолучитьПустоеЗначение();
КонецПопытки;

Важное ограничение метода: Функция Шаблон() возвращает строго текстовое представление результата вычислений. Если результатом формулы является элемент справочника (объектная ссылка), то Шаблон() превратит его в строку представления. Чтобы обойти это ограничение и вернуть объект, в шаблоне нужно вызывать глобальную функцию-посредник, которая будет записывать объект во временную переменную общего назначения, откуда движок сможет его забрать.

Решение 3. Декларативный метод «Цепочки приоритетов» (Fallback)

Чтобы избавить пользователей от необходимости писать код (даже простой) в справочнике, мы можем применить декларативный подход. Вместо поля ввода формул мы создаем подчиненный справочник или табличную часть ИсточникиАналитики для каждой настройки субконто.

В этой таблице пользователь просто указывает список реквизитов документа, из которых последовательно нужно пытаться получить значение аналитики, в порядке приоритета:

  1. Приоритет 1: Реквизит табличной части ОтделениеСтрока.
  2. Приоритет 2: Реквизит шапки Отделение.
  3. Приоритет 3: Свойство склада ОсновноеОтделение (связанный реквизит).

Алгоритм движка в цикле обходит эти настройки:


ВычисленноеСубконто = ПолучитьПустоеЗначение();

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

Преимущества: Полная безопасность и простота использования. Бухгалтер или аналитик настраивает логику мышкой, выбирая реквизиты из списка. Исключены синтаксические ошибки программирования.

Недостатки: Ограниченность настроек. Сложнейшие логические ветвления (например, зависимость от третьих справочников) описать таким образом затруднительно.

Решение 4. Архитектура «Комплексной конфигурации» (Трансляция из регистров)

Проанализируем опыт разработчиков типовых решений фирмы 1С (в частности, «Комплексной конфигурации 7.7»). Там задача гибких проводок решалась через двухэтапную схему:

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

Поскольку регистр хранит уже готовые, очищенные и рассчитанные данные (там уже гарантированно заполнена вся необходимая аналитика, рассчитана себестоимость списания по FIFO/LIFO/Средней), движку проводок не нужно вычислять сложные формулы. Он просто берет готовые измерения регистра (например, Номенклатура, Склад, Проект) и подставляет их в субконто дебета или кредита соответствующего счета.

Если вы проектируете крупную систему с высокой нагрузкой, мы настоятельно рекомендуем использовать именно этот паттерн трансляции данных из регистров. Это убережет базу данных от избыточных блокировок и существенно повысит общую производительность системы.

Резюме и практические рекомендации

Выбор конкретного метода реализации гибкого движка проводок зависит от масштаба вашей системы и уровня подготовки тех, кто будет ее администрировать:

← На главную