Своя операция в закрытии месяца? Легко!

Обработки - Закрытие периода

ERP закрытие месяца операции закрытия месяца реклассификация контроль операций своя операция закрытия месяца

59
Иногда приходится сталкиваться с задачей включения в пункты закрытия месяца своих операций. В данной статье будет показано – как с минимальными изменениями конфигурации, а также использованием расширений решить такую задачу.

Для решения задачи в качестве типовой конфигурации будем использовать «1С: ERP Управление предприятием 2». 

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

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

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

Обратимся к дереву метаданных конфигурации. Нам необходимо использовать следующие объекты:

  1. Отчет: «КонтрольОформленияДокументовТовародвижений»
  2. Общий модуль: «ЗакрытиеМесяцаСервер»
  3. Перечисление: «ОперацииЗакрытияМесяца»

Наиболее существенными в части доработок являются первые 2 объекта. Они не несут в себе структурных изменений метаданных – их можно легко разместить в расширении и производить модификацию именно там. Как создавать расширения конфигурации и как добавлять туда объекты – в интернете достаточно информации, можно без труда найти. В данной статье я опишу наиболее значимые моменты. Итак, поехали:

  1. Добавим в конфигурацию новое значение перечисления: «КонтрольОформленияДокументовПоОрдернымСкладам»
  2. Поместим процедуру ЗаполнитьОписаниеЭтаповЗакрытияМесяца(...) общего модуля в расширение. Наш код будет выполняться после основного кода процедуры. Его выделим в отдельную область.
  3. Добавим функцию, которая будет отвечать за открытия отчета с указанными параметрами (период, организация).
  4. Добавим процедуру, которая анализирует корректность оформления товарных документов. По сути, внутри ее производится вызов функции, в которой выполняется запрос из отчета. Именно из-за этого я не буду приводить листинг данной функции. По результатам выполнения такого запроса мы устанавливаем состояние у нашей операции.
  5. Поместим в расширение модуль объекта отчета «КонтрольОформленияДокументовТовародвижений». Мы сделаем этого для того, чтобы обработать входные параметры из операции закрытия месяца, и использовать их как значения отбора для компоновщика отчета.

 

 Общий модуль «ЗакрытиеМесяцаСервер»

#Область ОписаниеЭтаповЗакрытияМесяца

&После("ЗаполнитьОписаниеЭтаповЗакрытияМесяца")
Процедура Расш_ЗаполнитьОписаниеЭтаповЗакрытияМесяца(ТаблицаЭтапов)
	#Область КонтрольПоОрдернымСкладам	
	// наша операция будет располагаться внутри группы операций, которые находятся на 
	// "ручном контроле"
	ТекущийРодитель = ИдентификаторГруппыРучныеОперации();

	// добавляем непосредственно в таблицу, которая содержит перечень операций
	НоваяСтрока = ДобавитьЭтапВТаблицу(ТаблицаЭтапов, ТекущийРодитель,
		Перечисления.ОперацииЗакрытияМесяца.КонтрольОформленияДокументовПоОрдернымСкладам,
		Истина, Ложь, Ложь);
		
	// так как наша операция по факту показывает что есть какие то отклонения в товародвижении 
	// но не выполняет никаких действий по автоматическому исправлению ситуации, то
	// основным действием - является интерактивный вызов отчета с нужными параметрами
	НоваяСтрока.ВыполняетсяВручную = Истина;
	НоваяСтрока.ТекстВыполнить     = НСтр("ru='Показать'");
	НоваяСтрока.ДействиеИспользование = ОписаниеДействия_СервернаяПроцедура(
		"ЗакрытиеМесяцаСервер.Использование_КонтрольОформленияДокументовПоОрдернымСкладам");
	
	// укажем что действием по нажатию гиперссылки будет являться открытие отчета с автоматическим формированием 
	НоваяСтрока.ДействиеВыполнить  = ОписаниеДействия_ОткрытьФормуОтчета("Отчет.КонтрольОформленияДокументовТовародвижений.Форма", Новый Структура("СформироватьПриОткрытии", истина));
	
	#КонецОбласти //КонтрольПоОрдернымСкладам		
	
КонецПроцедуры

#КонецОбласти

#Область КонтрольОформленияДокументовПоОрдернымСкладам

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

	ИначеЕсли Результат.Строки.Количество() = 1 Тогда
		
		УстановитьСостояниеНеТребуется(
			ПараметрыОбработчика,
			НСтр("ru='Нет неоформленных документов.'"));
		
	Иначе
				
		ИтоговаяСтрока = Результат.Строки[Результат.Строки.Количество() - 1];		
		ТекстПояснения = НСтр("ru='Требуется оформление документов по ордерным складам'");
		
		УстановитьСостояниеНеВыполнен(
			ПараметрыОбработчика,
			ТекстПояснения,
			,
			,
			Перечисления.ВариантыВажностиПроблемыСостоянияСистемы.Предупреждение);
		
	КонецЕсли;
	
КонецПроцедуры

// Описание действия "Открыть произвольную форму".
//
// Параметры:
//	ИмяФормы 				 - Строка - имя открываемой формы 
//	ПараметрыВСтруктуреОтбор - Булево - признак того, что параметры открываемой формы надо передать внутри параметра Отбор с типом Структура
//
// Возвращаемое значение:
//	Структура - см. СтруктураОписанияДействия()
//
Функция ОписаниеДействия_ОткрытьФормуОтчета(ИмяФормы, ПараметрыОткрытия) Экспорт
	Описание = СтруктураОписанияДействия();
	
	Описание.ВидДействия = Перечисления.ВидыДействийРасшифровкиОперацийЗакрытияМесяца.ОткрытьФорму;
	Описание.ИмяФормы    = ИмяФормы;
	Описание.НаКлиенте   = Истина;
	
	ПоляПараметровФормы = "Организация, МассивОрганизаций, ПериодРегистрации, Период, НачалоПериода, КонецПериода, ДатаОкончанияПериода";
	
	Описание.Вставить("ПараметрыФормы", Новый Структура(ПоляПараметровФормы));
	Описание.ПараметрыФормы.Вставить(ЗакрытиеМесяцаСервер.ИмяСлужебногоСвойстваОткрываемыхФорм(), Истина);
	
	Для каждого Элемент из ПараметрыОткрытия Цикл
		Описание.ПараметрыФормы.Вставить(Элемент.Ключ, Элемент.Значение);	
	КонецЦикла;
	
	Возврат Описание;
	
КонецФункции

#КонецОбласти

Модуль объекта «КонтрольОформленияДокументовТовародвижений»

#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда
	
#Область СлужебныйПрограммныйИнтерфейс

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

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

КонецПроцедуры

#КонецОбласти

#КонецЕсли

Вот как это выглядит в пользовательском режиме:

Рис. 1 Наша добавленная ручная операция «Контроль оформления документов по ордерным складам».

Если требуется расшифровка – по гиперссылке «Показать» открывается отчет.

Рис 2. Отчет, вызываемый из закрытия месяца.

В нашей конфигурации сделана небольшая доработка, в результате которой склады связаны с организациями. Поэтому отрабатывает отбор по складам организаций. В результате мы видим только нужные склады. Бухгалтеру нет необходимости открывать отчет вручную, указывать отборы – все делается из обработки закрытия месяца, если есть проблема - она увидит это, как и увидит проблемные документы с необходимыми рекомендациями.

Пример №2. Перед формированием финансового результата необходимо обязательно проводить реклассификацию договоров. То есть – нужна операция, которая является существенной для определения финансового результата. Как это реализовать? Легко. Алгоритм действий такой же. Но есть небольшое дополнение:

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

Дополним уже добавленную ранее в расширение процедуру. Приводить листинг процедуры реклассификации и ее актуальности я не буду. Здесь важен другой момент: у нас будет использоваться 3 "триггера": проверка ранее выполненной реклассификации и насколько она актуальна - "ДействиеИспользование", выполнение процедуры реклассификации - "ДействиеВыполнить", открытие перечня сформированных регламентных документов - "ДействиеПодробнее".

&После("ЗаполнитьОписаниеЭтаповЗакрытияМесяца")
Процедура Расш_ЗаполнитьОписаниеЭтаповЗакрытияМесяца(ТаблицаЭтапов)

	#Область РеклассификацияЗадолженности
	Индекс = Неопределено;
	// наша операция будет распологаться внутри группы операций, которые могут
	// выполняться как автоматически, так и вручную
	ТекущийРодитель = ИдентификаторГруппыРегламентированныйУчет();
	НоваяСтрока = ДобавитьЭтапВТаблицу(ТаблицаЭтапов, ТекущийРодитель, Перечисления.ОперацииЗакрытияМесяца.РеклассификацияДоговоровКредитаИДепозита);		
	
	// вклиниваем операцию после отражения документов в регл. учете
	Строка = ТаблицаЭтапов.Найти(Перечисления.ОперацииЗакрытияМесяца.ОтражениеДокументовВРегламентированномУчете, "Код");
	Если Не Строка = Неопределено Тогда
		Индекс = ТаблицаЭтапов.Индекс(Строка);
	КонецЕсли;
	
	Если Индекс <> Неопределено Тогда
		ИндексСтроки = ТаблицаЭтапов.Индекс(НоваяСтрока); 		
		ИндексСдвига = Индекс - ИндексСтроки;
		ИндексСдвига = ?(ИндексСдвига > 0, ИндексСдвига - 1, ИндексСдвига + 1);
		
		Если ИндексСдвига <> 0 Тогда
			ТаблицаЭтапов.Сдвинуть(ИндексСтроки, ИндексСдвига);
		КонецЕсли;		
	КонецЕсли;
	
	// ключевая строка!
	// наша операция не выполнится без предыдущих операций, в частности - 
	// отражения документов в регл. учете
	НоваяСтрока.ПредшествующиеЭтапы.Добавить(Перечисления.ОперацииЗакрытияМесяца.ОтражениеДокументовВРегламентированномУчете);
	НоваяСтрока.ВыполняетсяВручную = ложь;
	НоваяСтрока.ТекстВыполнить = НСтр("ru='Выполнить'");
	НоваяСтрока.ДействиеИспользование = ОписаниеДействия_СервернаяПроцедура(
		"ЗакрытиеМесяцаСервер.Использование_ВыполнитьРеклассификациюДоговоровКредитаИДепозита");
	НоваяСтрока.ДействиеВыполнить  = ОписаниеДействия_ВыполнитьРасчет(
		"ЗакрытиеМесяцаСервер.Выполнить_ВыполнитьРеклассификациюДоговоровКредитаИДепозита");
	
	// по действию "Подробнее" - открываем журнал документов "Реклассификация",
	// с отбором по периоду и организации.
	НоваяСтрока.ДействиеПодробнее = ОписаниеДействия_ОткрытьСписокДокументовРеклассификация();
	
	#КонецОбласти //РеклассификацияЗадолженности
		
КонецПроцедуры

 

Как это выглядит:

Рис 3. Добавленная операция «Реклассификации договоров кредита и депозита»

 

Если операция успешно выполнена, то по гиперссылке будет открыт список документов:

Рис 4. Список документов с отбором по организациям и периоду

 

Резюмируя все что было описано в статье:

  1. Добавить свои операции в закрытие месяца – достаточно легко и осуществляется с минимальными изменениями конфигурации.
  1. Мы описываем только необходимые «триггеры»: выполнения, расшифровки. А также алгоритмы выполнения операции. Все остальное берет на себя механизм закрытия месяца, который все это исполняет в фоновом режиме.
  1. Можно добавлять как информационные операции (например, как описанный выше вызов отчета), так и какие-либо сложные механизмы. Так же можно добавить, например, оповещение в виде sms или письма ответственным по результатам выполнения операции, да даже той же задачи будет достаточно. Тут возможности безграничны.

 

59

См. также

Комментарии
Избранное Подписка Сортировка: Древо
1. murzilka88 06.09.18 11:15 Сейчас в теме
А еще у нас в ERP РБП автоматически распределяются!
2. PerlAmutor 30 06.09.18 16:32 Сейчас в теме
А у нас франч добавил так свою операцию при внедрении, в результате операция закрытия месяца увеличилась на +8 часов, пока я не выявил причину спустя несколько месяцев и не исправил.
3. Shmell 76 06.09.18 19:47 Сейчас в теме
(2) Поэтому нужно добавлять операции, которые достаточно быстро выполняются по отношению ко всему закрытию месяца. Иначе есть смысл их выносить за контур закрытия месяца. Если нужно, чтобы это было в закрытии месяца, то код такой операции должен быть оптимальным, не перегруженым лишними действиями. Я так подозреваю - в Вашем случае сыграл злую шутку не оптимальный код или "перегруженная" логика самой операции.
VladimirL; zeegin; +2 Ответить
Оставьте свое сообщение