Курсы валют / Модуль набора записей

файл
///////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2024, ООО 1С-Софт
// Все права защищены. Эта программа и сопроводительные материалы предоставляются 
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
// Текст лицензии доступен по ссылке:
// https://creativecommons.org/licenses/by/4.0/legalcode
///////////////////////////////////////////////////////////////////////////////////////////////////////

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

#Область ОписаниеПеременных

Перем ОшибкаРасчетаКурсаПоФормуле;
Перем КодыВалют;

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

#Область ОбработчикиСобытий

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

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

#Область СлужебныеПроцедурыИФункции

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

		Если ВыбраннаяВалюта <> Неопределено Тогда // Нужно обновить курс только указанной валюты.
			ОбновленныеПериоды = Неопределено;
			Если Не ДополнительныеСвойства.Свойство("ОбновленныеПериоды", ОбновленныеПериоды) Тогда
				ОбновленныеПериоды = Новый Соответствие;
				ДополнительныеСвойства.Вставить("ОбновленныеПериоды", ОбновленныеПериоды);
			КонецЕсли;
			// Повторно не обновляем курс за один и тот же период.
			Если ОбновленныеПериоды[ЗаписьОсновнойВалюты.Период] = Неопределено Тогда
				//@skip-check query-in-loop - порционная обработка больших объемов данных
				ОбновитьКурсЗависимойВалюты(ВыбраннаяВалюта, ЗаписьОсновнойВалюты); 
				ОбновленныеПериоды.Вставить(ЗаписьОсновнойВалюты.Период, Истина);
			КонецЕсли;
		Иначе	// Обновить курс всех зависимых валют.
			ЗависимыеВалюты = РаботаСКурсамиВалют.СписокЗависимыхВалют(ЗаписьОсновнойВалюты.Валюта, ДополнительныеСвойства);
			Для Каждого ЗависимаяВалюта Из ЗависимыеВалюты Цикл
				//@skip-check query-in-loop - порционная обработка больших объемов данных
				ОбновитьКурсЗависимойВалюты(ЗависимаяВалюта, ЗаписьОсновнойВалюты); 
			КонецЦикла;
		КонецЕсли;
		
	КонецЦикла;
	
КонецПроцедуры

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

// Очищает курсы зависимых валют.
//
Процедура УдалитьКурсыЗависимыхВалют(Знач РежимЗамещенияУдаление)
	
	Если ОбщегоНазначения.ЭтоУдалениеНабораЗаписей(РежимЗамещенияУдаление) Тогда
		УдалитьКурсыПоНаборуЗаписей(РежимЗамещенияУдаление);
	Иначе
		УдалитьКурсыПоОтбору();
	КонецЕсли;
	
КонецПроцедуры

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

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

Процедура УдалитьКурсыВалюты(ВалютаСсылка, Период)
	НаборЗаписей = РегистрыСведений.КурсыВалют.СоздатьНаборЗаписей();
	НаборЗаписей.Отбор.Валюта.Установить(ВалютаСсылка);
	НаборЗаписей.Отбор.Период.Установить(Период);
	НаборЗаписей.ДополнительныеСвойства.Вставить("ОтключитьКонтрольПодчиненныхВалют");
	НаборЗаписей.Записать();
КонецПроцедуры

Функция КурсВалютыПоФормуле(Валюта, Формула, Период)
	
	ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Расчет курса валюты ""%1"" по формуле ""%2"" за период ""%3""не выполнен:'",
		ОбщегоНазначения.КодОсновногоЯзыка()), Валюта, Формула, Период);
	
	Попытка
		Результат = Справочники.Валюты.КурсВалютыПоФормуле(Формула, Период, КодыВалют());
	Исключение
		Если ОшибкаРасчетаКурсаПоФормуле = Неопределено Тогда
			ОшибкаРасчетаКурсаПоФормуле = Новый Соответствие;
		КонецЕсли;
		Если ОшибкаРасчетаКурсаПоФормуле[Валюта] = Неопределено Тогда
			ОшибкаРасчетаКурсаПоФормуле.Вставить(Валюта, Истина);
			ИнформацияОбОшибке = ИнформацияОбОшибке();
			
			ОбщегоНазначения.СообщитьПользователю(ТекстОшибки + Символы.ПС + ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке), 
				Валюта, "Объект.ФормулаРасчетаКурса");
				
			Если ДополнительныеСвойства.Свойство("ОбновитьКурсЗависимойВалюты") Тогда
				ВызватьИсключение ТекстОшибки + Символы.ПС + ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке);
			КонецЕсли;
			
			ЗаписьЖурналаРегистрации(НСтр("ru = 'Валюты.Загрузка курсов валют'", ОбщегоНазначения.КодОсновногоЯзыка()),
				УровеньЖурналаРегистрации.Ошибка, Валюта.Метаданные(), Валюта, 
				ТекстОшибки + Символы.ПС + ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке));
		КонецЕсли;
		Результат = Неопределено;
	КонецПопытки;
	
	Возврат Результат;
КонецФункции

Функция КодыВалют()
	
	КодыВалют = Неопределено;
	ДополнительныеСвойства.Свойство("КодыВалют", КодыВалют);
	
	Если КодыВалют = Неопределено Тогда
		КодыВалют = Справочники.Валюты.КодыВалют();
	КонецЕсли;
	
	Возврат КодыВалют;
	
КонецФункции

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

#Иначе
ВызватьИсключение НСтр("ru = 'Недопустимый вызов объекта на клиенте.'");
#КонецЕсли

Связанные материалы