////#define DEBUG
Функция Конт(Конт) Возврат Конт; КонецФункции

////#if DEBUG
//Процедура ПриОткрытии()
//	Форма.Параметр._ПриОткрытии();
//КонецПроцедуры
////#endif

//_____________________________________________________________________________
Функция Сравнить(П1, П2) Экспорт
	Если ТипЗначения(П1) <> ТипЗначения(П2) Тогда
		Возврат 0;
	КонецЕсли;

	П1_ = П1; П2_ = П2;
	Если ТипЗначения(П1) = 2 Тогда
		П1_ = СокрП(П1);
		П2_ = СокрП(П2);
	КонецЕсли;
	Если П1_ = П2_ Тогда
		Возврат 1;
	КонецЕсли;
	Возврат 0;
КонецФункции

//_____________________________________________________________________________
// Возвращает последнее непустое значение из переданного списка параметров
Функция ПоследнееНепустое() Экспорт
	Перем СпПарам, н, Значение;

	СпПарам = Конт(Контекст).ПолучитьСписокПараметров("ПоследнееНепустое");
	н = СпПарам.РазмерСписка();
	Пока н > 0 Цикл
		Значение = СпПарам.ПолучитьЗначение(н);
		Если ПустоеЗначение(Значение) = 0 Тогда
			Возврат Значение;
		КонецЕсли;
		н = н - 1;
	КонецЦикла;
КонецФункции

//_____________________________________________________________________________
Функция ЧислоПрописью(ЗначЧисло) Экспорт
	Перем Возвр;
	Перем ЦелЧасть, ДробЧасть;
	Перем Дробная;
	Перем Окончание;
	Перем ПоследнееСлово;

	ЦелЧасть = Цел(ЗначЧисло);
	ДробЧасть = Окр(ЗначЧисло - ЦелЧасть, 3);

	Если ДробЧасть=Окр(ДробЧасть,0) Тогда
		ДробЧасть = 0;
		Дробная = "";

	ИначеЕсли ДробЧасть = Окр(ДробЧасть, 1) Тогда
		ДробЧасть = Окр(ДробЧасть, 1) * 10;
		Дробная = "десят";

	ИначеЕсли ДробЧасть = Окр(ДробЧасть, 2) Тогда
		ДробЧасть = Окр(ДробЧасть, 2) * 100;
		Дробная = "сот";
	Иначе
		ДробЧасть = ДробЧасть * 1000;
		Дробная = "тысячн";
	КонецЕсли;

	Возвр = "";
	Возвр = Возвр + ?(ПустоеЗначение(ЦелЧасть) = 1, "Ноль", СокрЛП(Формат(ЦелЧасть, "ЧП")));

	Если Дробная <> "" Тогда // есть дробная часть
		// теперь займемся окончанием последнего слова целой части
		ПоследнееСлово = "";
		Окончание = "";

		Пока (Возвр <> "") И (Возвр = СокрП(Возвр)) Цикл // последний символ в строке не пробел
			ПоследнееСлово = Прав(Возвр, 1) + ПоследнееСлово; // добавляем последнюю букву
			Возвр = Лев(Возвр, СтрДлина(Возвр) - 1); // отрезаем последнюю букву
		КонецЦикла;

		Если Врег(ПоследнееСлово) = "ОДИН" Тогда
			// первую букву слова оставляем
			ПоследнееСлово = Лев(ПоследнееСлово, 1) + "дна";
			Окончание = "ая";
		Иначе
			Если (Врег(ПоследнееСлово) = "ДВА") Тогда
				ПоследнееСлово = Лев(ПоследнееСлово, 1) + "ве";
			КонецЕсли;
			Окончание = "ых";
		КонецЕсли;

		Возвр = Возвр + ПоследнееСлово + " цел" + Окончание + " " + НРег(СокрЛП(Формат(ДробЧасть, "ЧП")));

		// теперь займемся окончанием последнего слова дробной части
		ПоследнееСлово = "";
		Окончание = "";

		Пока Возвр = СокрП(Возвр) Цикл // последний символ в строке не пробел
			ПоследнееСлово = Прав(Возвр, 1) + ПоследнееСлово; // добавляем последнюю букву
			Возвр = Лев(Возвр, СтрДлина(Возвр) - 1); // отрезаем последнюю букву
		КонецЦикла;

		Если Врег(ПоследнееСлово) = "ОДИН" Тогда
			// первую букву слова оставляем
			ПоследнееСлово = Лев(ПоследнееСлово, 1) + "дна";
			Окончание = "ая";
		Иначе
			Если (Врег(ПоследнееСлово) = "ДВА") Тогда
				ПоследнееСлово = Лев(ПоследнееСлово, 1) + "ве";
			КонецЕсли;

			Окончание = "ых";
		КонецЕсли;

		Возвр = Возвр + ПоследнееСлово + " " + Дробная + Окончание;
	КонецЕсли;

	Возврат Возвр;
КонецФункции

//_____________________________________________________________________________
Функция СтрНайти(Где, Что, НачПоз = 1) Экспорт
	Перем Стр, н, стрЧто, Найдено, Поз, МинПоз;

	Если НачПоз <= 0 Тогда НачПоз = 1; КонецЕсли;

	Стр = Сред(Где, НачПоз);

	Если ТипЗначенияСтр(Что) = "СписокЗначений" Тогда
		МинПоз = СтрДлина(Стр) + 1;
		Найдено = 0;
		Для н = 1 По Что.РазмерСписка() Цикл
			стрЧто = Что.ПолучитьЗначение(н);
			Поз = Найти(Стр, стрЧто);
			Если Поз > 0 Тогда
				Если Поз < МинПоз Тогда
					Найдено = 1;
					МинПоз = Поз;
					Что.ТекущаяСтрока(н); //Надо показать вызывающему коду, какой именно разделитель примен¬н
				КонецЕсли;
			КонецЕсли;
		КонецЦикла;
		Поз = ?(Найдено = 1, МинПоз + НачПоз - 1, 0);
	Иначе
		Поз = Найти(Стр, Что);
	КонецЕсли;

	Возврат ?(Поз = 0, 0, Поз + НачПоз - 1);
КонецФункции

//_____________________________________________________________________________
Функция СтрПовторить(Фрагмент, Знач Количество) Экспорт
	Стр = "";
	Пока Количество > 0 Цикл
		Стр = Стр + Фрагмент;
		Количество = Количество - 1;
	КонецЦикла;
	Возврат Стр;
КонецФункции

//_____________________________________________________________________________
Функция СтрокуВСписок(Знач Стр, Разделитель, ОбрезатьПробелы) Экспорт
	Перем ПоСпискуРазделителей;
	Перем Поз, н;
	Перем Сп, Зн;

	Если ТипЗначенияСтр(Разделитель) = "СписокЗначений" Тогда
		ПоСпискуРазделителей = 1;
	Иначе
		ПоСпискуРазделителей = 0;
	КонецЕсли;

	Сп = СоздатьОбъект("СписокЗначений");
	Поз = СтрНайти(Стр, Разделитель);
	Пока Поз > 0 Цикл
		Зн = Лев(Стр, Поз - 1);
		Если ОбрезатьПробелы = 1 Тогда Зн = СокрЛП(Зн); КонецЕсли;
		Сп.ДобавитьЗначение(Зн, Зн);

		Если ПоСпискуРазделителей = 1 Тогда
			н = Разделитель.ТекущаяСтрока();
			Стр = Сред(Стр, Поз + СтрДлина(Разделитель.ПолучитьЗначение(н)));
		Иначе
			Стр = Сред(Стр, Поз + СтрДлина(Разделитель));
		КонецЕсли;
		Поз = СтрНайти(Стр, Разделитель);
	КонецЦикла;

	Если ПустаяСтрока(Стр) = 0 Тогда
		Если ОбрезатьПробелы = 1 Тогда Стр = СокрЛП(Стр); КонецЕсли;
		Сп.ДобавитьЗначение(Стр, Стр);
	КонецЕсли;
	Возврат Сп;
КонецФункции

//_____________________________________________________________________________
Функция СписокВСтроку(Сп, Разделитель = ",", ВключатьПустыеЗначения = 1) Экспорт
	Перем Стр, н, Значение, Зпт;

	Стр = ""; Зпт = "";
	Для н = 1 По Сп.РазмерСписка() Цикл
		Значение = Строка( Сп.ПолучитьЗначение(н) );
		Если ВключатьПустыеЗначения = 0 Тогда
			Если Значение = "" Тогда Продолжить; КонецЕсли;
		КонецЕсли;
		Стр = Стр + Зпт + Значение;
		Зпт = Разделитель;
	КонецЦикла;

	Возврат Стр;
КонецФункции

//_____________________________________________________________________________
Функция РазницаДат(Дата1, Дата2, Лет, Мес, Дней) Экспорт
	Перем Д1, Д2, Д;

	Лет = 0; Мес = 0; Дней = 0;
	Если ПустоеЗначение(Дата1) = 1 Тогда Возврат 0; КонецЕсли;
	Если ПустоеЗначение(Дата2) = 1 Тогда Возврат 0; КонецЕсли;
	Если Дата1 <= Дата2 Тогда
		Д1 = Дата1; Д2 = Дата2;
	Иначе
		Д1 = Дата2; Д2 = Дата1;
	КонецЕсли;

	Д = Д1;
	Пока 1 = 1 Цикл
		Если ДобавитьМесяц(Д, 12) <= Д2 Тогда
			Лет = Лет + 1;
			Д = ДобавитьМесяц(Д, 12);
		Иначе
			Прервать;
		КонецЕсли;
	КонецЦикла;

	Пока 1 = 1 Цикл
		Если ДобавитьМесяц(Д, 1) <= Д2 Тогда
			Мес = Мес + 1;
			Д = ДобавитьМесяц(Д, 1);
		Иначе
			Прервать;
		КонецЕсли;
	КонецЦикла;

	Пока 1 = 1 Цикл
		Если (Д + 1) <= Д2 Тогда
			Дней = Дней + 1;
			Д = Д + 1;
		Иначе
			Прервать;
		КонецЕсли;
	КонецЦикла;

	Возврат (Лет * 12) + Мес;
КонецФункции

//_____________________________________________________________________________
//Вычисляет границы пересечения двух периодов. Периоды могут быть открытыми (т.е. одна из дат пустая. Или обе.)
//Возвращает 1 если периоды пересекаются, и 0 - в противном случае.
Функция ПересечениеПериодов(НП1, КП1, НП2, КП2, ПересечениеНач = "", ПересечениеКон = "") Экспорт
	Перем Д1, Д2;

	Если (ПустоеЗначение(НП1) = 1) И (ПустоеЗначение(НП2) = 1) Тогда
		Д1 = '';
	ИначеЕсли ПустоеЗначение(НП1) = 1 Тогда
		Д1 = НП2;
	ИначеЕсли ПустоеЗначение(НП2) = 1 Тогда
		Д1 = НП1;
	Иначе
		Д1 = Макс(НП1, НП2);
	КонецЕсли;

	Если (ПустоеЗначение(КП1) = 1) И (ПустоеЗначение(КП2) = 1) Тогда
		Д2 = '';
	ИначеЕсли ПустоеЗначение(КП1) = 1 Тогда
		Д2 = КП2;
	ИначеЕсли ПустоеЗначение(КП2) = 1 Тогда
		Д2 = КП1;
	Иначе
		Д2 = Мин(КП1, КП2);
	КонецЕсли;

	Если (ПустоеЗначение(Д1) = 0) И (ПустоеЗначение(Д2) = 0) Тогда
		Если Д1 > Д2 Тогда
			Возврат 0;
		КонецЕсли;
	КонецЕсли;
	ПересечениеНач = Д1;
	ПересечениеКон = Д2;
	Возврат 1;
КонецФункции

//_____________________________________________________________________________
Функция ДатаИзСтроки(Знач стрДата) Экспорт
	Перем сзДата, День, Месяц, Год;

	стрДата = СокрЛП(стрДата);
	День = 0;
	Месяц = 0;
	Год = 0;

	Если Найти(стрДата, ".") = 0 Тогда
		Если (СтрДлина(стрДата) = 6) ИЛИ (СтрДлина(стрДата) = 8) Тогда
			День  = Число(Лев(стрДата, 2));
			Месяц = Число(Сред(стрДата, 3, 2));
			Год   = Число(Сред(стрДата, 5));
		КонецЕсли;
	Иначе
		сзДата = СтрокуВСписок(стрДата, ".", 1);
		Если сзДата.РазмерСписка() = 3 Тогда
			День  = Число(сзДата.ПолучитьЗначение(1));
			Месяц = Число(сзДата.ПолучитьЗначение(2));
			Год   = Число(сзДата.ПолучитьЗначение(3));
		КонецЕсли;
	КонецЕсли;

	Если (День = 0) ИЛИ (Месяц = 0) Тогда
		Возврат Дата(стрДата);
	КонецЕсли;

	Если Год < 40 Тогда
		Год = Год + 2000;
	ИначеЕсли Год < 100 Тогда
		Год = Год + 1900;
	КонецЕсли;

	Возврат Дата(Год, Месяц, День);
КонецФункции

//_____________________________________________________________________________
//_____________________________________________________________________________
//Проверяет, входит ли <Значение> в диапазон <Знач1> - <Знач2>
//Для агрегатных типов данных проверяется условие <Значение> = <Знач1>
Функция ФильтрЗначения(Знач Значение, Знач Знач1, Знач Знач2) Экспорт
	Тип1 = ТипЗначенияСтр(Знач1);
	Тип2 = ТипЗначенияСтр(Знач2);

	Если Тип1 = "СписокЗначений" Тогда
		Возврат Знач1.НайтиЗначение(Значение);
	ИначеЕсли Тип1 = "ТаблицаЗначений" Тогда
		Возврат Знач1.НайтиЗначение(Значение);
	КонецЕсли;

	Тип = ТипЗначенияСтр(Значение);
	Если (Тип <> Тип1) ИЛИ (Тип <> Тип2) ИЛИ (Тип1 <> Тип2) Тогда
		Значение = Строка(Значение);
		Знач1 = Строка(Знач1);
		Знач2 = Строка(Знач2);
	КонецЕсли;

	Если Тип = "Справочник" Тогда
		Если ПустоеЗначение(Знач1) = 0 Тогда
			Если Знач1.ЭтоГруппа() = 1 Тогда
				Если Значение.ПринадлежитГруппе(Знач1) = 0 Тогда Возврат 0; КонецЕсли;
			Иначе
				Если Значение <> Знач1 Тогда Возврат 0; КонецЕсли;
			КонецЕсли;
		КонецЕсли;
	ИначеЕсли Тип = "Счет" Тогда
		Если ПустоеЗначение(Знач1) = 0 Тогда
			Если Знач1.ЭтоГруппа() = 1 Тогда
				Если Значение.ПринадлежитГруппе(Знач1) = 0 Тогда Возврат 0; КонецЕсли;
			Иначе
				Если Значение <> Знач1 Тогда Возврат 0; КонецЕсли;
			КонецЕсли;
		КонецЕсли;
	ИначеЕсли Тип = "Перечисление" Тогда
		Если ПустоеЗначение(Знач1) = 0 Тогда
			Если Значение <> Знач1 Тогда Возврат 0; КонецЕсли;
		КонецЕсли;
	ИначеЕсли Тип = "Число" Тогда
		Если Значение < Знач1 Тогда Возврат 0; КонецЕсли;
		Если Значение > Знач2 Тогда Возврат 0; КонецЕсли;
	Иначе
		Если ПустоеЗначение(Знач1) = 0 Тогда
			Если Значение < Знач1 Тогда Возврат 0; КонецЕсли;
		КонецЕсли;
		Если ПустоеЗначение(Знач2) = 0 Тогда
			Если Значение > Знач2 Тогда Возврат 0; КонецЕсли;
		КонецЕсли;
	КонецЕсли;

	Возврат 1;
КонецФункции

//_____________________________________________________________________________
Функция НайтиФайлВнешнегоОтчета(ИмяОтчета, сзПути)
	Перем н, Путь;

	Для н = 1 По сзПути.РазмерСписка() Цикл
		Путь = сзПути.ПолучитьЗначение(н);
		Если ПустаяСтрока(Путь) = 1 Тогда Продолжить; КонецЕсли;

		Если ФС.СуществуетФайл(Путь + ИмяОтчета + ".ert") = 1 Тогда
			Возврат Путь + ИмяОтчета + ".ert";
		КонецЕсли;
	КонецЦикла;

	Если ФС.СуществуетФайл(КаталогИБ() + ИмяОтчета + ".ert") = 1 Тогда
		Возврат КаталогИБ() + ИмяОтчета + ".ert";
	ИначеЕсли ФС.СуществуетФайл(КаталогИБ() + "ExtForms\" + ИмяОтчета + ".ert") = 1 Тогда
		Возврат КаталогИБ() + "ExtForms\" + ИмяОтчета + ".ert";
	Иначе
		Возврат "" ;
	КонецЕсли;
КонецФункции

//_____________________________________________________________________________
//Пытается найти обработку, отчет или внешний отчет с указанным именем.
//Последовательность поиска:
// Если функция вызвана из внешнего отчета
//   1) Внешний отчет в том же каталоге, что и вызывающий отчет (если он внешний)
//   2) Внешний отчет в каталоге базы данных
//   3) Внешний отчет в каталоге ExtForms
//   4) Обработка
//   5) Отчет
// Если функция вызвана из объекта конфигурации
//   1) Обработка
//   2) Отчет
//   3) Внешний отчет в том же каталоге, что и вызывающий отчет (если он внешний)
//   4) Внешний отчет в каталоге базы данных
//   5) Внешний отчет в каталоге ExtForms
Функция ОткрытьОбработку(Знач ИмяОбработки, Параметры, Модально, Знач Внешняя, QuietMode = 0) Экспорт
	Перем Конт, Путь, Тип;
	Перем сзПути;
	Перем Диез;

	сзПути = СоздатьОбъект("СписокЗначений");

	Если ТипЗначения(Внешняя) = 2 Тогда
		сзПути.ДобавитьЗначение(Внешняя);
		Внешняя = 1;
	ИначеЕсли ТипЗначенияСтр(Внешняя) = "СписокЗначений" Тогда
		Для н = 1 По Внешняя.РазмерСписка() Цикл
			сзПути.ДобавитьЗначение( Внешняя.ПолучитьЗначение(н) );
		КонецЦикла;
		Внешняя = 1;
	КонецЕсли;

	//Докопаемся до вызывающей формы, и получим путь к ней. Если это была обработка, конечно.
	Конт = Конт(Контекст);
	Попытка
		Пока ТипЗначенияСтр(Конт) <> "ГрупповойКонтекст" Цикл
			Конт = Конт.ПолучитьКонтекстОкружения();
		КонецЦикла;
		Конт.РасположениеФайла(Путь, );
		сзПути.ДобавитьЗначение(Путь);
	Исключение
	КонецПопытки;
	ИмяОбработки = СокрЛП(ИмяОбработки);
	Если Прав(ИмяОбработки, 1) = "#" Тогда
		Диез = "#";
		ИмяОбработки = Лев(ИмяОбработки, СтрДлина(ИмяОбработки) - 1);
	Иначе
		Диез = "";
	КонецЕсли;

	Если (ПустаяСтрока(Путь) = 1) И (Внешняя = 0) Тогда
		Если Метаданные.Обработка(ИмяОбработки).Выбран() = 1 Тогда
			Тип = "Обработка";
		ИначеЕсли Метаданные.Отчет(ИмяОбработки).Выбран() = 1 Тогда
			Тип = "Отчет";
		Иначе
			Тип = "ВнешнийОтчет";
			Путь = НайтиФайлВнешнегоОтчета(ИмяОбработки, сзПути);
			Если Путь = "" Тогда
				Если QuietMode = 0 Тогда
					Сообщить("Не найдена обработка '" + ИмяОбработки + "'", "!");
				КонецЕсли;
				Возврат 0;
			КонецЕсли;
		КонецЕсли;
	Иначе
		Тип = "ВнешнийОтчет";
		Путь = НайтиФайлВнешнегоОтчета(ИмяОбработки, сзПути);
		Если Путь = "" Тогда
			Если Метаданные.Обработка(ИмяОбработки).Выбран() = 1 Тогда
				Тип = "Обработка";
			ИначеЕсли Метаданные.Отчет(ИмяОбработки).Выбран() = 1 Тогда
				Тип = "Отчет";
			Иначе
				Если QuietMode = 0 Тогда
					Сообщить("Не найдена обработка '" + ИмяОбработки + "'", "!");
				КонецЕсли;
				Возврат 0;
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;

	Если Тип = "ВнешнийОтчет" Тогда
		Тип = "Отчет";
		Если Модально = 0 Тогда
			ОткрытьФорму(Тип + Диез, Параметры, Путь);
		Иначе
			ОткрытьФормуМодально(Тип + Диез, Параметры, Путь);
		КонецЕсли;
	Иначе
		Если Модально = 0 Тогда
			ОткрытьФорму(Тип + "." + ИмяОбработки + Диез, Параметры);
		Иначе
			ОткрытьФормуМодально(Тип + "." + ИмяОбработки + Диез, Параметры);
		КонецЕсли;
	КонецЕсли;

	Возврат 1;
КонецФункции

//_____________________________________________________________________________
Функция ЯвляетсяПроизводнымОт(ЭкземплярКласса, ИмяБазовогоКласса) Экспорт
	Перем База;

	Если ТипЗначенияСтр(ЭкземплярКласса) = ИмяБазовогоКласса Тогда
		Возврат 1;
	КонецЕсли;

	Попытка
		База = ЭкземплярКласса.ПолучитьБазовыйКласс(ИмяБазовогоКласса);
		Если ТипЗначения(База) = 0 Тогда
			Возврат 0;
		Иначе
			Возврат 1;
		КонецЕсли;
	Исключение
		Возврат 0;
	КонецПопытки;
КонецФункции

//_____________________________________________________________________________
Функция ПолучитьВызывающийКонтекст(Конт) Экспорт
	Перем лКонт;
	лКонт = Конт;
	Пока ТипЗначенияСтр(лКонт) <> "ГрупповойКонтекст" Цикл
		Попытка
			лКонт = лКонт.ПолучитьКонтекстОкружения();
		Исключение
			Возврат лКонт;
		КонецПопытки;
	КонецЦикла;

	Возврат лКонт;
КонецФункции
