В предыдущей статье //infostart.ru/public/1721980/ был предложен т.н. функциональный подход при создании конфигураций. Суть его заключается в том, что результат проведения документа зависит только от самого документа, но не от окружения. Было показано, что простое расширение регистра накопления позволяет решать частную задачу резервирования в функциональном стиле. Еще одно расширение регистров накопления, которое я хочу предложить здесь, дает возможность решать в функциональном стиле уже очень широкий круг задач. Возможно, что вообще все или почти все.
Для начала вспомним, как устроен регистр накопления. Мы делаем записи в регистр. Записи могут быть двух видов: "приход" или "расход". На основании этих записей система предоставляет в наше распоряжение виртуальную таблицу остатков. Остатки вычисляются с помощью очевидных арифметических операций: "приход" это "+", "расход" это "-". При этом, нас совершенно не заботит порядок внесения записей в регистр. Если что-то вносится "задним числом", виртуальная таблица все равно выдаст нам верный остаток.
Самый важный момент заключается в том, что "внутри" регистра накопления может "лежать" что-то более сложное, чем элементарная арифметика. Набор операций "приход-расход" пришел к нам из докомпьютерной эпохи. И в настоящее время мы не обязаны ограничивать себя им.
Заменим набор операций "приход-расход" на набор "накопить-распределить". Операция "накопить" является полным аналогом операции "приход". Изменено только имя. Операция "распределить" в частном случае может работать, как аналог операции "расход", но в общем случае ее поведение более сложное. Рассмотрим его на конкретном примере.
Есть два документа поступления товаров
Обработка проведения для документов поступления выглядит так:
Движения.Себестоимость.Записывать=истина;
для каждого стр из Товары цикл
дв=Движения.Себестоимость.Добавить();
дв.ВидДвиженияРасширенный=Перечисления.ВидДвижения.Накопить;
дв.Период=Дата;
дв.Товар=стр.Товар;
дв.Партия=ссылка;
дв.Количество=стр.Количество;
дв.Сумма=стр.СуммаСНДС;
конеццикла;
Результат проведения этих двух документов
Есть один документ отгрузки
В обработке проведения
Движения.Себестоимость.Записывать=истина;
для каждого стр из Товары цикл
дв=Движения.Себестоимость.Добавить();
дв.ВидДвиженияРасширенный=Перечисления.ВидДвижения.Распределить;
дв.Период=Дата;
дв.Товар=стр.Товар;
дв.Количество=стр.Количество;
конеццикла;
Результат проведения
Представим, что наш расширенный регистр умеет обрабатывать операцию "распределить". Тогда, при обращении к виртуальной таблице оборотов, мы могли бы получать правильное распределение, в соответствии с тем или иным методом. Метод распределения можно было бы указывать в параметрах регистра.
Я воспользовался подпиской на событие и сэмулировал работу такого регистра (см. приложение). В результате я получил вот такую "виртуальную" таблицу оборотов
На основе этих данных можно построить так любимый многими отчет типа такого
Реализацию эмуляции можно найти в приложении. Но здесь она не так уж и важна. Гораздо интереснее то, что нам удается применить функциональный принцип. А это в свою очередь дает нам простоту в модулях объектов. Например, обработка проведения документа "Отгрузка" у меня сейчас выглядит так:
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
если не Основание.Пустая() тогда
Движения.Резервирование.Записывать=истина;
для каждого стр из Товары цикл
дв=Движения.Резервирование.Добавить();
дв.ВидДвижения=ВидДвиженияНакопления.Расход;
дв.Период=Дата;
дв.Заказ=Основание;
дв.Товар=стр.Товар;
дв.Количество=стр.Количество;
конеццикла;
конецесли;
Движения.Себестоимость.Записывать=истина;
для каждого стр из Товары цикл
дв=Движения.Себестоимость.Добавить();
дв.ВидДвиженияРасширенный=Перечисления.ВидДвижения.Распределить;
дв.Период=Дата;
дв.Товар=стр.Товар;
дв.Количество=стр.Количество;
конеццикла;
Движения.ОстаткиТоваров.Записывать=истина;
для каждого стр из Товары цикл
дв=Движения.ОстаткиТоваров.Добавить();
дв.ВидДвижения=ВидДвиженияНакопления.Расход;
дв.Период=Дата;
дв.Товар=стр.Товар;
дв.Количество=стр.Количество;
конеццикла;
Движения.Продажи.Записывать=истина;
для каждого стр из Товары цикл
дв=Движения.Продажи.Добавить();
дв.Период=Дата;
дв.Товар=стр.Товар;
дв.Количество=стр.Количество;
дв.Сумма=стр.СуммаСНДС;
конеццикла;
КонецПроцедуры
И это при том, что здесь у нас и резервирование и партионный учет. Функциональный подход дает нам такую простоту и прозрачность, которые мы уже давно не видели в типовых конфигурациях.
В приложении демонстрационная выгрузка базы. Пример тестировался на версии платформы 8.3.19.1467.