Универсальные функции с примерами использования

Опубликовал Роман Уничкин (unichkin) в раздел Программирование - Универсальные функции

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

Вместо предисловия

Я знаю про CloudConf, и сервисы хранения кода, но на инфостарте сижу каждый день, поэтому мне здесь удобнее. Да и возможность поделиться какими-то своими идеями, получив отклик тоже интересна.
Код я оформляю в соответствии с соглашениями о написании кода.

1. Замер времени выполнения

Для более точных замеров - с версии платформы 8.2.17 можно использовать метод ГК ТекущаяУниверсальнаяДатаВМиллисекундах()

Старт = ТекущаяДата();
// Произвольный код....
Финиш = ТекущаяДата();

КолСекундЛог = Финиш - Старт;
чч = ЦЕЛ(КолСекундЛог/3600);
мм = ЦЕЛ((КолСекундЛог - чч*3600)/60);
сс =  КолСекундЛог - чч*3600 - мм*60;
СтрокаВремя =	Формат(чч, "ЧЦ=2; ЧН=; ЧВН=") + ":" + 
				Формат(мм, "ЧЦ=2; ЧН=; ЧВН=") + ":" + 
				Формат(сс, "ЧЦ=2; ЧН=; ЧВН=") + " ("+ КолСекундЛог +" сек.)";
Сообщить("" + ТекущаяДата() + ": обработано за " + СтрокаВремя + ".", СтатусСообщения.Информация);
		
2. Проверка значения на вхождение в некоторый интервал
// Проверка значения на вхождение в некоторый интервал  
//
//Параметры (название, тип, дифференцированное значение):
// Значение - Число, Дата - Проверяемое значение
// НачалоИнтервала - Число, Дата
// КонецИнтервала - Число, Дата
// СтрогаяПроверка - Булево
//
//Возвращаемое значение:
// Булево
//
Функция ЗначениеВИнтервале(Значение, НачалоИнтервала, КонецИнтервала, СтрогаяПроверка = Ложь)

	Если СтрогаяПроверка Тогда
		Возврат НачалоИнтервала < Значение И Значение < КонецИнтервала;
	Иначе 
		Возврат НачалоИнтервала <= Значение И Значение <= КонецИнтервала; 
	КонецЕсли; 

КонецФункции
		

Пример использования:

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

Пример использования:

//Парсинг строки вида "дд.мм.гггг":
Результат = Дата(1,1,1);
СтруктураЧастиСтроки = РазбитьСтроку(ЗначениеЯчейки, ".");

Если СтруктураЧастиСтроки.Разбито Тогда
	ст_дд = Прав(СтруктураЧастиСтроки.Левая, 2);
	чс_дд = Число(ст_дд);
	
	СтруктураЧастиСтроки = РазбитьСтроку(СтруктураЧастиСтроки.Правая, ".");
	
	Если СтруктураЧастиСтроки.Разбито Тогда
		ст_мм = Прав(СтруктураЧастиСтроки.Левая, 2);
		чс_мм = Число(ст_мм);
		
		ст_гггг = Лев(СтруктураЧастиСтроки.Правая, 4);
		чс_гггг = Число(ст_гггг);			
		
		Результат = Дата(чс_гггг, чс_мм,  чс_дд);
		
	КонецЕсли; 
КонецЕсли; 	
Возврат Результат;
		
4. Преобразование табличного документа в таблицу значений (перебор)

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

// Преобразование табличного документа в таблицу значений
//
//Параметры:
// ТабДок - ТабличныйДокумент
//
//Возвращаемое значение:
//	ТаблицаЗначений 
//
Функция ТабличныйДокументВТаблицуЗначений(ТабДок)
    
    ТаблицаДанныеДокумента = Новый ТаблицаЗначений();
    
    Для СчетчикКолонок = 1 По ТабДок.ШиринаТаблицы Цикл
        
        ИмяКолонки = "_" + СчетчикКолонок;
        ТаблицаДанныеДокумента.Колонки.Добавить(ИмяКолонки);
        
        Для СчетчикСтрок = 1 По ТабДок.ВысотаТаблицы Цикл
            
            ИндексСтроки = СчетчикСтрок - 1;
            
            Если СчетчикКолонок = 1 Тогда
                СтрокаТаблицы = ТаблицаДанныеДокумента.Добавить();
            Иначе
                СтрокаТаблицы = ТаблицаДанныеДокумента[ИндексСтроки];
            КонецЕсли;
            
            ТекущаяЯчейка = ТабДок.Область(СчетчикСтрок, СчетчикКолонок);
            ЗначениеЯчейки = ТекущаяЯчейка.Текст;
            
            СтрокаТаблицы[ИмяКолонки] = ЗначениеЯчейки; 
            
        КонецЦикла
        
    КонецЦикла;    
    
    Возврат ТаблицаДанныеДокумента;
    
КонецФункции
		
5. Загрузка файлов: выбор на клиенте, обработка на сервере

Приведенный ниже код иллюстрирует работу с файлом типа "xlsx". Работа ведется из управляемой формы:

  • пользователь выбирает файл;

  • файл помещается в временное хранилище, и становится доступен на сервере в виде двоичных данных;

  • производится запись двоичных данных в временный файл на сервере, с проверкой: если запись временного файла занимает более 30 секунд, пользователь получит уведомление о прерывании загрузки.

// Выбор загружаемого файла, заполнение служебного реквизита формы "АдресФайлаВВременномХранилище"
//
//Возвращаемое значение:
// Булево - Истина при успешном выборе файла
//
&НаКлиенте
Функция ПроизведенУспешныйВыборФайла()
	
	ЭтаФорма.АдресФайлаВВременномХранилище = "";
	
	#Если ВебКлиент Тогда
		
		Если НЕ ПодключитьРасширениеРаботыСФайлами() Тогда
			УстановитьРасширениеРаботыСФайлами();
			ПодключитьРасширениеРаботыСФайлами();
		КонецЕсли;
		
		ПоместитьФайл(ЭтаФорма.АдресФайлаВВременномХранилище);
		
	#Иначе
		
		ДиалогФыбораФайла = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
		
		ДиалогФыбораФайла.Фильтр = "Таблица(*.xlsx)|*.xlsx";
		ДиалогФыбораФайла.Заголовок = "Выберите файл";
		ДиалогФыбораФайла.ПредварительныйПросмотр = Ложь;
		ДиалогФыбораФайла.Расширение = "xlsx";
		ДиалогФыбораФайла.МножественныйВыбор = Ложь;
		
		Если ДиалогФыбораФайла.Выбрать() Тогда
			ФайлЗагрузки = ДиалогФыбораФайла.ПолноеИмяФайла;
		Иначе
			Возврат Ложь;
		КонецЕсли;
		
		ЭтаФорма.АдресФайлаВВременномХранилище = ПоместитьВоВременноеХранилище(Новый ДвоичныеДанные(ДиалогФыбораФайла.ПолноеИмяФайла));
		
	#КонецЕсли
	
	Возврат НЕ ПустаяСтрока(ЭтаФорма.АдресФайлаВВременномХранилище); 
	
КонецФункции
		

Пример использования:

//...		
&НаКлиенте
Процедура ЗагрузитьФайл(Команда)
	
	Если ПроизведенУспешныйВыборФайла() Тогда
		ВыполнитьЗагрузкуФайлаНаСервере(); 
	КонецЕсли; 
	
КонецПроцедуры

//...
&НаСервере
Процедура ВыполнитьЗагрузкуФайлаНаСервере()
	
	АдресВременногоФайла = ПолучитьИмяВременногоФайла("xlsx");
	
    // Запись двоичных данных из вр. хранилища:
    ДвДанные = ПолучитьИзВременногоХранилища(ЭтаФорма.АдресФайлаВВременномХранилище);

	Если ЗаписьФайлаПроизошлаУспешно(ДвДанные, АдресВременногоФайла) Тогда // см. ниже "Проверка записи файла
		// обработка данных файла...	
	Иначе
		ЛОГ("загрузка прервана на сервере - файл """+ АдресВременногоФайла +""" не обнаружен...");
	КонецЕсли; 	
	
КонецПроцедуры
//...
		
6. Попытка записи данных в файл с таймаутом на время записи

// Попытка записи данных в файл с таймаутом на время записи
//
//Параметры:
// Источник - Произвольный объект 1С, поддерживающий запись в файл через метод "Записать" - например ДвоичныеДанные или ТекстовыйДокумент
// АдресФайла - Строка
// ОграничениеВремениЗаписи - Число, количество секунд в течение которого будет выполнятся проверка существования файла
// КомандаЗаписи - Строка, для различных случаев сохранения файла, в алгоритме будет параметром метода "Выполнить"
//
//Возвращаемое значение: 
// Булево
//
Функция ЗаписьФайлаПроизошлаУспешно(Источник, АдресФайла, ОграничениеВремениЗаписи = 30, КомандаЗаписи = "")
    
	Попытка 
		
		Если ПустаяСтрока(КомандаЗаписи) Тогда
			Источник.Записать(АдресФайла);				
		Иначе
			Выполнить(КомандаЗаписи);
		КонецЕсли; 
		
        ТекДата = ТекущаяДата();
        ПредСекунда = 0;
        Пока Истина Цикл
            
            КолСекунд = ТекущаяДата() - ТекДата;
            ТекСекунда = КолСекунд;
            
            Если ПредСекунда < ТекСекунда Тогда
                // Раз в секунду - проверка существования записываемого файла
                ЗаписываемыйФайл = Новый Файл(АдресФайла);
                Если ЗаписываемыйФайл.Существует() Тогда
                    Возврат Истина;
                КонецЕсли; 
                
                ПредСекунда = ТекСекунда;
            КонецЕсли; 
            
            Если КолСекунд > ОграничениеВремениЗаписи Тогда
                ВызватьИсключение "время записи файла превысило "+ ОграничениеВремениЗаписи +" секунд";
            КонецЕсли; 
        КонецЦикла; 
        
    Исключение
        Лог("неудачная попытка записи файла на сервере - " + ОписаниеОшибки()); 
        Возврат Ложь;
    КонецПопытки;    
    
КонецФункции
		

Пример использования:

 //...
ТекстXSD = ЭтотОбъект.ПолучитьМакет("МакетXSD");
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xsd"); 

// Запись xsd файла с проверкой по тайм-ауту
Если НЕ ЗаписьФайлаПроизошлаУспешно(ТекстXSD, ИмяВременногоФайла, 5) Тогда
	Возврат;	
КонецЕсли;  

// Или, если нужна запись с параметром:
Если НЕ ЗаписьФайлаПроизошлаУспешно(ТекстXSD, ИмяВременногоФайла, 5, "Источник.Записать(АдресФайла, КодировкаТекста.UTF8);") Тогда
	Возврат;	
КонецЕсли;  
//...
		
7. Проверка интернет-соединения (ping)
// Проверка доступности переданного узла с помощью команды "ping"
//
//Параметры:
// АдресURL - пингуемый адрес
//
//Возвращаемое значение: 
//	Булево
//
Функция Пинг(АдресURL = "")
	
	Если ПустаяСтрока(АдресURL) Тогда
		АдресURL = "www.ya.ru";			
	КонецЕсли; 
	
	objShell = Новый COMОбъект("WScript.Shell") ;
	objScriptExec = objShell.Exec("ping.exe -n 1 " + АдресURL);
	strPingResults = НРег(objScriptExec.StdOut.ReadAll());
	ЕстьСоединение = Найти(strPingResults, "ttl=") > 0;	
	
	Возврат ЕстьСоединение;
	
КонецФункции
		

Пример использования:

//...
ЕстьСоединениеСИнтернетом = Пинг(); // По-умолчанию пингуется www.ya.ru
//...
		
8. Быстрое описание типа
// Упрощенный конструктор описания типа
//
//Параметры:
// ИмяТипа - Строка, Массив элементов типа "Тип" - первый параметр конструктора ОписаниеТипов()
// п1 - Произвольный, первый параметр квалификатора 
// п2 - Произвольный, второй параметр квалификатора 
// п3 - Произвольный, третий параметр квалификатора 
//
//Возвращаемое значение: 
//	ОписаниеТипов
//
Функция ОписаниеТипа(ИмяТипа, п1 = Неопределено, п2 = Неопределено, п3 = Неопределено)
    
    Перем Результат;
    
    Если ИмяТипа = "Строка" Тогда
        
        п1 = ?(п1 = Неопределено, 0, п1);
		Если п2 = Неопределено Тогда
     	   п2 = ДопустимаяДлина.Переменная;			
		КонецЕсли; 
      
        КвалСтроки = Новый КвалификаторыСтроки(п1, п2);
        Результат = Новый ОписаниеТипов(ИмяТипа,,,,КвалСтроки);
        
    ИначеЕсли ИмяТипа = "Число" Тогда    
        
        п1 = ?(п1 = Неопределено, 0, п1);
        п2 = ?(п2 = Неопределено, 0, п2);
		Если п3 = Неопределено Тогда
    	    п3 = ДопустимыйЗнак.Любой;			
		КонецЕсли; 
        
        КвалЧисла = Новый КвалификаторыЧисла(п1, п2, п3);
        Результат = Новый ОписаниеТипов(ИмяТипа,,,КвалЧисла);
        
    ИначеЕсли ИмяТипа = "Дата" Тогда
		
		Если п1 = Неопределено Тогда
        	п1 = ЧастиДаты.ДатаВремя;			
		КонецЕсли; 
        
        КвалДаты = Новый КвалификаторыДаты(п1);
        Результат = Новый ОписаниеТипов(ИмяТипа,,,,,КвалДаты);
        
    Иначе
        Результат = Новый ОписаниеТипов(ИмяТипа);
    КонецЕсли; 
    
    Возврат Результат;
    
КонецФункции
		

Пример использования:

//...
Функция НоваяТаблицаОписанияОпераций()
	
	тз = Новый ТаблицаЗначений;
	тз.Колонки.Добавить("Сумма"         , ОписаниеТипа("Число", 15, 2)); 
	тз.Колонки.Вставить("Описание"      , ОписаниеТипа("Строка", 100));
	тз.Колонки.Вставить("ДатаОперации"  , ОписаниеТипа("Дата"));

	МассивТипов = Новый Массив();
	МассивТипов.Добавить(Тип("ДокументСсылка.Встреча"));
	МассивТипов.Добавить(Тип("ДокументСсылка.ЗапланированноеВзаимодействие"));
	тз.Колонки.Вставить("Событие"       , ОписаниеТипа(МассивТипов));

	тз.Колонки.Вставить("Ответственный" , ОписаниеТипа("СправочникСсылка.Пользователи"));
	Возврат тз;
		
КонецФункции
//...
		
9. Получение ссылки документа по его представлению

Отладочная универсальная функция "СПС" (ссылка по строке) - предназначена для получения ссылки документа по его представлению. Удобно использовать как в режиме отладки, так и в режиме 1С через табло, чтобы быстро (без открытия формы) посмотреть реквизит документа, представление которого было скопировано - например из отчета.

// Получение ссылки документа по его представлению, учтена возможность наличия произвольных символов слева/справа от представления документа.
//
//Параметры (название, тип, дифференцированное значение):
// ПредставлениеДокумента - Строка - Представление документа, слева/справа он представления могу находится произвольные символы 
//
//Возвращаемое значение:
// ДокументСсылка - в случае успешного парсинга строки и поиска документа. 
// Неопределено - в противном случае.
//
Функция СПС(ПредставлениеДокумента) ЭКСПОРТ
    
    // Бывает так, что в синонимах документа присутствует " от ": "Возврат товаров от покупателя".
    // Поэтому служебный разделитель " от " необходимо искать с конца представления документа
    
    ДлинаСтроки = СтрДлина(стр);
    
    НайденаПозицияОТ = Ложь;
    
    Для _сч = -ДлинаСтроки По 1 Цикл        
        сч = - _сч;
        поз_от = сч-3; 
        стр_от = Сред(стр, поз_от, 4);
        
        Если стр_от = " от " Тогда
            НайденаПозицияОТ = Истина;
            Прервать;            
        КонецЕсли; 
    КонецЦикла; 
    
    Если НЕ НайденаПозицияОТ Тогда
        Возврат Неопределено;        
    КонецЕсли; 
    
#Область Получение_даты_документа
    Если Истина Тогда
    
        поз_НачалоДаты = поз_от + 4;
        стр_Дата = СокрЛП(Сред(стр, поз_НачалоДаты));
        поз_Точки = Найти(стр_Дата, "."); 
        стр_дд = Сред(стр_Дата, 1, поз_Точки - 1);
        чс_дд = Число(стр_дд);
        
        стр_Дата = Сред(стр_Дата, поз_Точки + 1);
        поз_Точки = Найти(стр_Дата, ".");     
        стр_мес = Сред(стр_Дата, 1, поз_Точки - 1); 
        чс_мес = Число(стр_мес);
        
        стр_Дата = Сред(стр_Дата, поз_Точки + 1);
        поз_Пробела = Найти(стр_Дата, " ");     
        стр_гг = Сред(стр_Дата, 1, поз_Пробела - 1);    
        чс_гг = Число(стр_гг);
        
        стр_Время = СокрЛП(Сред(стр_Дата, поз_Пробела + 1)); 
        поз_Двоеточие = Найти(стр_Время, ":");
        стр_чч = Сред(стр_Время, 1, поз_Двоеточие - 1);
        чс_чч = Число(стр_чч);
        
        стр_Время = Сред(стр_Время, поз_Двоеточие + 1); 
        поз_Двоеточие = Найти(стр_Время, ":");
        стр_мин = Сред(стр_Время, 1, поз_Двоеточие - 1);
        чс_мин = Число(стр_мин);
        
        стр_Время = Сред(стр_Время, поз_Двоеточие + 1); 
        стр_сек = Лев(стр_Время, 2);
        чс_сек = Число(стр_сек);
        
        ДатаДокумента = Дата(чс_гг, чс_мес, чс_дд, чс_чч, чс_мин, чс_сек);        
    КонецЕсли; 
#КонецОбласти 
    
    // Номер нахожу как первую строку между пробелами слева от строки " от ". Знак "№" есть не везде    
    стр_ПредставлениеИНомер = СокрЛП(Лев(стр, поз_от));
    ДлинаПредставленияИНомера = СтрДлина(стр_ПредставлениеИНомер);
    ДлинаНомера = 0;
    Для _сч =-ДлинаПредставленияИНомера  По 1 Цикл
        
        сим = Сред(стр_ПредставлениеИНомер, -_сч, 1);

        Если сим = " " Тогда
            Прервать;            
        КонецЕсли; 
        
        ДлинаНомера= ДлинаНомера + 1;
    КонецЦикла; 
    
    ДлинаПредставленияДокумента = ДлинаПредставленияИНомера - ДлинаНомера;
    
    НомерДокумента = СокрЛП(Сред(стр_ПредставлениеИНомер, ДлинаПредставленияДокумента));
    Если Лев(НомерДокумента, 1) = "№" Тогда
        НомерДокумента = Сред(НомерДокумента, 2);        
    КонецЕсли; 
    
    ПредставлениеДокумента = СокрЛП(Лев(стр_ПредставлениеИНомер, ДлинаПредставленияДокумента));
    
    ПодходящееИмя = "";
    Для каждого МетаДок Из Метаданные.Документы Цикл
        
        Если ПредставлениеДокумента = МетаДок.Синоним Тогда
            Возврат Документы[МетаДок.Имя].НайтиПоНомеру(НомерДокумента, ДатаДокумента);    
        КонецЕсли;        
        
        Если ПустаяСтрока(ПодходящееИмя) Тогда
            Если Найти(ПредставлениеДокумента, МетаДок.Синоним) > 0 Тогда
                ПодходящееИмя = МетаДок.Имя;         
            КонецЕсли;             
        КонецЕсли; 
    КонецЦикла; 
    
    Если НЕ ПустаяСтрока(ПодходящееИмя) Тогда
        Возврат Документы[ПодходящееИмя].НайтиПоНомеру(НомерДокумента, ДатаДокумента);            
    КонецЕсли;     
    
КонецФункции
		
10. Объединение ячеек шапки табличного документа с повторяющимся текстом

Этот метод служит для решения частной задачи отображения группировок колонок в табличных документах, полученных с помощью СКД. Исходная идея вот здесь: СКД. Как объединить заголовки родительских группировок колонок в таблице. Я причесал код и добавил расчет ширины для объединяемой колонки - СКД при группировке выводит дубли колонок с исходной шириной, логично при свертке к ней и вернуться. Этот вариант не будет работать с уже объединенными ячейками. Может позже допилю, пока нет нужды. Как использовать: в СКД делаем одну группировку колонок, в которую добавлем необходимые поля. После вывода табличного документа на форму вызываем метод.

// Объединяет ячейки шапки табличного документа с повторяющимся текстом
// Служит для решения задачи отображения группировок колонок в табличных документах, полученных с помощью СКД
//
//Параметры:
// ТабДок - ТабличныйДокумент
// ВысотаШапки - Число, если не передана, высотой шапки считается высота фиксации таблицы
//
Процедура СвернутьЗаголовкиШапкиТабличногоДокумента(ТабДок, ВысотаШапки = 0)

	ВысотаШапки = ?(ВысотаШапки = 0, ТабДок.ФиксацияСверху, ВысотаШапки);		
	НачалоШапки = ?(ТабДок.ФиксацияСлева = 0, 1, ТабДок.ФиксацияСлева);
	
	Для СчетчикСтрок=1 По ВысотаШапки Цикл
		
		НомерПервойКолонкиОбъединения = 0;
		Для СчетчикКолонок=НачалоШапки По ТабДок.ШиринаТаблицы Цикл			

			ОбъединятьЯчейки = Ложь;
			Ячейка = ТабДок.Область(СчетчикСтрок, СчетчикКолонок);
			Если ПустаяСтрока(Ячейка.Текст) Тогда
				Продолжить;				
			КонецЕсли; 
			ЯчейкаСлед = ТабДок.Область(СчетчикСтрок, СчетчикКолонок+1);
			ОбъединятьЯчейки = Ячейка.Текст = ЯчейкаСлед.Текст;
			
			Если ОбъединятьЯчейки Тогда				
				НомерПервойКолонкиОбъединения = ?(НомерПервойКолонкиОбъединения = 0, СчетчикКолонок, НомерПервойКолонкиОбъединения);
				
			ИначеЕсли НомерПервойКолонкиОбъединения > 0 Тогда
				ТекстЗаголовка = ТабДок.Область(СчетчикСтрок, СчетчикКолонок).Текст;
				ОбъединяемаяОбласть = ТабДок.Область(СчетчикСтрок, НомерПервойКолонкиОбъединения, СчетчикСтрок, СчетчикКолонок);
				ОбъединяемаяОбласть.Объединить();
				
				КоличествоКолонокВОбъединении = СчетчикКолонок - НомерПервойКолонкиОбъединения;
				ОбъединяемаяОбласть.ШиринаКолонки = ОбъединяемаяОбласть.ШиринаКолонки / КоличествоКолонокВОбъединении;
				
				ОбъединяемаяОбласть.ГоризонтальноеПоложение = ГоризонтальноеПоложение.Центр;
				ОбъединяемаяОбласть.Текст = ТекстЗаголовка;
				
				НомерПервойКолонкиОбъединения = 0;
			КонецЕсли;
			
		КонецЦикла;
		
	КонецЦикла;
	
КонецПроцедуры

См. также

Комментарии
1. Артем Бардюг (Йожкин Кот) 1025 26.02.16 17:21 Сейчас в теме
Замер времени можно сделать точнее:
ТекущаяУниверсальнаяДатаВМиллисекундах(). Функция появилась в каком-то релизе 8.3
Dmitriy_T; JesteR; dark_wolf; vladir; +4 Ответить 1
2. tireal 61 02.03.16 08:36 Сейчас в теме
вот держи в копилку, вместо своего замера
докВремяНачала = ТекущаяУниверсальнаяДатаВМиллисекундах();
//процедура замера
докВремяКонцаВыполнения = ТекущаяУниверсальнаяДатаВМиллисекундах();
докДельта = (докВремяКонцаВыполнения - докВремяНачала) / 1000;


докДельтаПредставление = ПолучитьПредставлениеВремени(докДельта);

Функция ПолучитьПредставлениеВремени(Время)
	Секунды = Время % 60;
	Минуты = (Время-Секунды)/60;
	Минуты = Минуты % 60;
	Часы = (Время-Секунды-Минуты)/3600;
	
	Возврат "" + Окр(Часы) + " ч. " + Окр(Минуты) + " мин. " + Окр(Секунды, 2) + " сек.";
	
КонецФункции
...Показать Скрыть
3. Роман Уничкин (unichkin) 305 02.03.16 12:01 Сейчас в теме
(1) Йожкин Кот, это поддерживается только с 8.2.17
(2) tireal, спасибо за комментарий. Но:
- свою функцию я могу использовать в любом релизе платформы. Да, я порой работаю с 8.1 - как ни странно оно еще есть.
- если мне когда-нибудь понадобится настолько низкоуровневое отслеживание выполнения алгоритма - я скорее воспользуюсь замером времени отладчика
- могу предположить что назначение использования вашей функции - тестирование скорости исполнения алгоритма (т. е. она для этой цели писалась); моей - просто замер времени работы для сообщения/ помещения в лог - секунды вполне устраивают
Останусь при своем, но буду знать и о таком варианте, еще раз спасибо.
4. Виктор Назаров (androgin) 02.03.16 14:58 Сейчас в теме
вместо Функция РазбитьСтроку(стр, сим)
можно вполне использовать СтрРазделить()
5. Роман Уничкин (unichkin) 305 02.03.16 15:12 Сейчас в теме
(4) androgin, Во-первых назначение СтрРазделить() и моей РазбитьСтроку() - разное. От слова совсем. Во-вторых СтрРазделить() - это начиная с 8.3.6.1977.
6. Андрей Акулов (DrAku1a) 1188 03.03.16 02:46 Сейчас в теме
(3) unichkin, посмотри как сделано в ИР и универсальном отчете (от alexk-is), там есть до миллиекунд.
Попытка
		Scr = Новый COMОбъект("MSScriptControl.ScriptControl");
	Исключение
		Сообщить(ОписаниеОшибки(), СтатусСообщения.Внимание);
		Возврат 0;
	КонецПопытки;
	Scr.Language = "javascript";
	Время = Scr.Eval("new Date().getTime()");
	Возврат Время;
...Показать Скрыть

По поводу разделения строки - держи в копилку:
Функция ИзвлечьСлово(Строка, Разделитель)
	П = найти(Строка+Разделитель, Разделитель);
	Ответ = Лев(Строка, П-1);
	Строка = Сред(Строка, П+СтрДлина(Разделитель));
	Возврат Ответ;
КонецФункции

Пример:
стр = "1,2,3,4";
Сообщить(ИзвлечьСлово(стр, ",")); //1
Сообщить(стр); // 2,3,4
Сообщить(ИзвлечьСлово(стр, ",")); //2
Сообщить(стр); // 3,4
Сообщить(ИзвлечьСлово(стр, ",")); //3
Сообщить(стр); // 4
Сообщить(ИзвлечьСлово(стр, ",")); //4
Сообщить(стр); // "" (пустая строка)
Сообщить(ИзвлечьСлово(стр, ",")); // "" (пустая строка)
...Показать Скрыть

Если заранее известно, что строка не содержит символов перевода строки, то можно пользоваться стандартным:
СтрПолучитьСтроку(СтрЗаменить(<ИсходнаяСтрока>, <Разделитель>, Символы.ПС), <НомерСлова>)
7. Андрей (h00k) 43 03.03.16 05:28 Сейчас в теме
(3) unichkin,
- если мне когда-нибудь понадобится настолько низкоуровневое отслеживание выполнения алгоритма - я скорее воспользуюсь замером времени отладчика

Смешно. Вы думаете всякие извращения с джаваскриптами и т.п. появились потому что ни кто не додумался время выполнения в отладчике смотреть?!
Спасибо любимой 1С, они хоть от необходимости использовать внешние скрипты избавили, добавив в платформу функцию
ТекущаяУниверсальнаяДатаВМиллисекундах()

А для старых версий платформы остается только замерять время выполнения при помощи внешних средств, например джава-скрипта.
8. Роман Уничкин (unichkin) 305 03.03.16 11:43 Сейчас в теме
(6) Знаю я об этих фишках, спасибо. Функция "ИзвлечьСлово" - не учитывает что символа может и не быть. И режет исходную строку. Как-раз чтобы этого избежать я наваял сп_РазбитьСтроку() - ее конечно не стоит применять на большом массиве данных, но для чтения небольших листов Excell - самое оно.
(7) h00k, почитайте внимательнее мой комментарий в (3) . И да, мне удобнее воспользоваться замером времени, чем вставлять в конфу отладочную печать. Не могу вспомнить случая, когда я не мог без этого обойтись. Может опыта еще маловато)

Имхо, все извращения с получением настолько точного времени - связаны с необходимостью отслеживать СКОРОСТЬ, а не ВРЕМЯ выполнения алгоритма. Разница трудноуловима, но она есть. Мне не надо с точностью до миллисекунды знать сколько времени проводился массив документов - 20 мин меня вполне устроит. Но если я тестирую напр. как быстрее создавать таблицу значений - тут нужны миллисекунды. Просто для универсальности функция замера обычно реализуется наиболее детализированно - т.е. через тот-же ява-скрипт. Ну а мне как-то не нужно было. Если понадобится - возьму из (6).
9. Валерий Дегрик (mars207) 20 09.03.16 09:37 Сейчас в теме
Загрузка файлов: выбор на клиенте, обработка на сервере

На веб клиенте давно не работает. Проблема всплывающих окон в браузерах. На последних релизах платформы не тестировал, но подозреваю что не исправили.
10. Роман Уничкин (unichkin) 305 09.03.16 09:39 Сейчас в теме
(9) mars207, тестировал на 8.3.5 - у меня работает. чяднт?
11. Виктор Назаров (androgin) 13.03.16 02:25 Сейчас в теме
(9) mars207, все прекрасно работает
12. Виктор Назаров (androgin) 13.03.16 02:26 Сейчас в теме
(5) unichkin, уже давно все типовые версии требуют 8.3.6.2094 как минимальную
13. Михаил (MishaD) 12 13.03.16 10:03 Сейчас в теме
Странно, у меня половина типовых на 8.2.14.540, а еще половина на 7.7.027
14. Роман Уничкин (unichkin) 305 13.03.16 13:18 Сейчас в теме
(12) androgin, далеко не все используют исключительно типовые, и далеко не все еще перешли на новые конфы с УФ и БСП. Я уже писал в (3) что и с 8.1 иногда работаю.
15. Виктор Назаров (androgin) 21.03.16 14:33 Сейчас в теме
(14) unichkin, то, что вы тормозите собственное развитие и сидите в старых платформах и на старых методах - вынуждает вас писать подобные коды и изобретать очередной велосипед.
В 1С далеко не дураки сидят
16. Андрей (h00k) 43 21.03.16 22:31 Сейчас в теме
(15) androgin,
то, что вы тормозите собственное развитие и сидите в старых платформах и на старых методах - вынуждает вас писать подобные коды

Вы можете ошибаться... собственное развитие и вынужденная работа со старыми версиями конфигураций в учетных системах заказчика/ работодателя - в большинстве случаев ни как не связаны.
Переход предприятия на новую систему учета/ платформу - это достаточно дорогое удовольствие, и только из-за желания программиста работать с новыми версиями платформ/ конфигураций ни кто, в здравом уме, переходить не будет.
17. Роман Уничкин (unichkin) 305 22.03.16 01:56 Сейчас в теме
(15) androgin, не понимаю сути претензий. И с чего вы решили что я где-то там "торможусь"?.. Я подписан на рассылку, просматриваю ИТС, мониторю зазеркалье (о птичках) работаю в основном на 8.3.6. И абсолютно солидарен с вами что в 1С сидят не дураки. Я только не понимаю, к чему это было сказано) Лучше перечитайте (5), попробуйте вникнуть в назначение сп_РазбитьСтроку() и понять таки что СтрРазделитьСтроку() в ее контексте в принципе не нужна. Я наверное сам дал еды троллю - лишним было писать "Во-вторых СтрРазделить() - это начиная с 8.3.6.1977".
18. Андрей Крутских (K_A_O) 481 28.03.16 11:33 Сейчас в теме
> Преобразование табличного документа в таблицу значений

Вот это вроде с давних времен работает

Построитель = Новый ПостроительОтчета;
Построитель.ИсточникДанных=Новый ОписаниеИсточникаДанных(ТабДок.Область());       
Построитель.Выполнить();
ТЗ = Построитель.Результат.Выгрузить();
pavlov_dv; TreeDogNight; Rokstedi; unichkin; +4 Ответить
19. Александр Шипков (sansys) 28 07.01.17 12:17 Сейчас в теме
Спасибо за статью. Иногда стоит вылезти из своего болота и посмотреть как люди делают :))))) +1
20. Сергей Долинин (ImHunter) 6 09.01.17 07:52 Сейчас в теме
Про замеры времени. Написал пару процедур - засечь время и показать время замера (+ 2 вспомогательные). Недостаток - в многопотоке некорректно работает. А так, удобненько.
// клиентский модуль

Процедура ЗасечьТаймер() Экспорт 
	УправляемыйИнтерфейсСервер.СохранитьЗначениеВХранилище( "ТекВремяТаймера", ТекущаяДата() );
	//ПоместитьВоВременноеХранилище( ТекущаяДата(), );
КонецПроцедуры

Процедура ПоказатьВремяТаймера( пЗаголовок = "", пЧерезСообщение = Ложь ) Экспорт 
	д = УправляемыйИнтерфейсСервер.ПолучитьЗначениеИзХранилища( "ТекВремяТаймера" );
	общ = ТекущаяДата() - д;
	мин = цел( общ / 60 );
	сек = общ - мин * 60;
	час = цел( мин / 60);
	мин = мин - час * 60;
	лВрем = ?( пЧерезСообщение, "Время выполнения:", "Время:" );
	Если час <> 0 Тогда 
		лВрем = лВрем + " " + час + " час"
	КонецЕсли;
	Если Мин <> 0 Тогда 
		лВрем = лВрем + " " + мин + " мин"
	КонецЕсли;
	лВрем = лВрем + " " + сек + " сек";
	Если пЧерезСообщение=Ложь Тогда 
		ПоказатьОповещениеПользователя( пЗаголовок, , лВрем );
	Иначе 
		Сообщить( ?( ПустаяСтрока( пЗаголовок ), "Действие выполнено", пЗаголовок ) + ". " + лВрем );
	КонецЕсли;
КонецПроцедуры

// серверный модуль УправляемыйИнтерфейсСервер

Процедура СохранитьЗначениеВХранилище( пКлюч, пЗначение ) Экспорт
	ХранилищеСистемныхНастроек.Сохранить( , пКлюч, пЗначение, , ПараметрыСеанса.ТекущийПользователь );	
КонецПроцедуры

Функция ПолучитьЗначениеИзХранилища( пКлюч ) Экспорт
	Возврат ХранилищеСистемныхНастроек.Загрузить( , пКлюч, , ПараметрыСеанса.ТекущийПользователь );
КонецФункции


// пример использования
&НаКлиенте
Процедура СделатьЧтоТо()
  УправляемыйИнтерфейс.ЗасечьТаймер();
  // что-то делаем
  // ......................
  УправляемыйИнтерфейс.ПоказатьВремяТаймера("Сделали что-то!");
КонецПроцедуры

...Показать Скрыть