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