Главная> Статьи

Андрей Бертыш

Некоторые трюки в 1C.


Вcтупление


1С-ники делятся на поклонников платформы 7.7 и на поклонников восьмерки
Очень многие из тех, кто уверенно перешел на восьмерку с семерки любит делиться своим опытом перехода и удовольствием от новой платформы и новых возможностей. Мне что- то нравится в одной платформе/методологии/модели бизнеса, что-то в другой, но в спор я стараюсь не вмешиваться. Семеркой я стал заниматься где-то с четвертого релиза платформы 7.7 это год эдак 1999, а восьмеркой я стал заниматься с 2005 го года. Последние несколько месяцев мне по стечению обстоятельств, пришлось вернуться на решение задач на семерке из-за старых заказчиков, которые не хотят переходить с платформы 7.7, имея разработанные специально под их специфику конфигурации, которые работают не один уже год, в их бизнесе облегчая им жизнь. Что надо сказать про обратный переход, после восьмерки обнаруживаешь, что отвык от некоторых конструкций языка и путаются в голове НоваяСтрока и ДобавитьСтроку. Приходится для получения неких сложных отчетов по требованиям заказчика писать больше кода, чем получилось бы при использовании конструкторов запросов в восьмерке или тем паче СКД (Системы компоновки данных). Но в итоге возникло понимание, что сейчас в 1С идет много молодежи, которая при этом сразу идёт на восьмерку. Но что делать, если у клиента 7.7 и он не желает переходить? Учить 7.7, что морально для восьмерошника будет некомфортно, точно так же для меня было некомфортно в свое время садиться за платформу 1С 6.0. Надо признать, кстати, что многие кому на 6.0 было комфортно, с выходом платформы 7.7 ушли с рынка 1С. Некоторые сведения можно почерпнуть из Интернета, например с сайта первые шаги http://www.firststeps.ru/1c/1cbuh1.html , но задачи могут быть таковы, что этого опыта будет маловато. Отсюда и родилась данная идея для данной статьи

1C 8:Периодические регистры сведений и запросы


В основном разговор будет вестись вокруг регистров сведений и запросов платформы восемь и аналогов и эмуляции регистров сведений и запросов платформы 7.7.
Начнем мы что бы сравнивать с создания каркасной конфигурации на платформе 8.1, 8.2 на мой взгляд хороша но на 13 декабря 2010 года пока ещё сыровата.
Итак, добавим в 8.1 новую конфигурацию и создадим справочник "валюты" с одним только кодом и наименованием, добавим периодический регистр сведений "курсы валют" с периодичностью день, добавим консоль запросов в состав конфигурации, создадим интерфейс и роль с правами на использование во всех отношениях наших немногочисленных объектов,
Лучше сначала создавать интерфейс, что бы потом разрешить роли его использовать.
Войдем в режим пользователя и введем два значения курса на 01.01.2009 го 29 и на 03.01.2009 го 30. Пусть только два значения. Только два раза менялся курс. Сформируем первый запрос. Его задача вывести значения курса по тем дням, по которым он менялся
ВЫБРАТЬ
КурсыВалют.Валюта,
КурсыВалют.Курс,
КурсыВалют.Период
ИЗ
РегистрСведений.КурсыВалют КАК КурсыВалют
ГДЕ
КурсыВалют.Валюта = &Валюта
В результате запроса на выходе таблица
ВалютаКурсПериод
USD29,00001.01.2009
USD30,00003.01.2009
Что вполне соответствует введенным нами данным
Следующий запрос будет выводить курсы за несколько дней по дням, но прежде всего нам для этого запроса понадобится таблица дат

Таблица дат


Данный запрос мы будем формировать в разных вариантах

Вариант первый


Вариант первый данный запрос имеет необходимое количество параметров, по которым помимо отбора валюты в запросе формируется таблица дат
Подобный запрос в части формирования таблицы дат можно формировать на лету программным кодом вида
ТаблицаДат="";
Запрос
=Новый Запрос;
Счетчик=
0;
Пока НачПериода+Счетчик*
60*60*24<=КонПериода Цикл
Если Счетчик=
0 Тогда
ТаблицаДат=
"ВЫБРАТЬ
|&Дата"+Счетчик+" КАК Дата";
Иначе
ТаблицаДат=ТаблицаДат+
"
|ОБЪЕДИНИТЬ ВСЕ
|ВЫБРАТЬ &Дата"+Счетчик;
КонецЕсли;
Запрос. УстановитьПараметр (
"Дата"+Счетчик, НачПериода+Счетчик*60*60*24);
Счетчик=Счетчик+
1;
КонецЦикла;

Данная конструкция будет работать в 8.0,8.1,8.2

Вариант второй


Другой способ сформировать таблицу дат в 8.1 и в 8.2 это использовать менеджер временных таблиц, а именно
МВТ=Новый МенеджерВременныхТаблиц;
Запрос=Новый Запрос;
Запрос.МенеджерВременныхТаблиц=МВТ;
ТЗ=Новый ТаблицаЗначений;
КД=Новый КвалификаторыДаты (ЧастиДаты. ДатаВремя);
Массив=Новый Массив;
Массив.Добавить (Тип(
"Дата"));
ОТД=Новый ОписаниеТипов (Массив,,КД);
ТЗ.Колонки.Добавить (
"Дата",ОТД);
Счетчик=
0;
Пока НачалоДня(НачПериода)+ Счетчик*
60*60*24<=КонПериода Цикл
Стр=ТЗ.Добавить();
Стр.Дата=НачалоДня (НачПериода)+ Счетчик*
60*60*24;
Счетчик=Счетчик+
1;
КонецЦикла;
Запрос.УстановитьПараметр (
"ТЗ",ТЗ);
Запрос.Текст =
"ВЫБРАТЬ
| Дата
|
|ПОМЕСТИТЬ
| ВременнаяТаблица
|
|ИЗ &ТЗ КАК ТЗ
|
|ИНДЕКСИРОВАТЬ ПО Дата";
Запрос.Выполнить ();
Запрос.Текст=
"ВЫБРАТЬ * ИЗ ВременнаяТаблица КАК ВременнаяТаблица";
Результат=Запрос.Выполнить();

Вариант третий


Третий вариант формирования таблицы дат отличается наличием ограничения - таблица дат формируется только в пределах дат одного года
Запрос=Новый Запрос ("ВЫБРАТЬ РАЗЛИЧНЫЕ
|Даты.Дата
|ИЗ
|(ВЫБРАТЬ
| Месяцы.Номер КАК Номер,
| ТаблицаДней.Дни КАК Дни,
| ДОБАВИТЬКДАТЕ(ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(&НачДата, ГОД), МЕСЯЦ, Месяцы.Номер - 1), ДЕНЬ, ТаблицаДней.Дни - 1) КАК Дата
|ИЗ
| (ВЫБРАТЬ
| 1 КАК Дни
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 2
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 3
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 4
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 5
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 6
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 7
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 8
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 9
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 10
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 11
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 12
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 13
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 14
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 15
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 16
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 17
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 18
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 19
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 20
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 21
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 22
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 23
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 24
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 25
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 26
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 27
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 28
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 29
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 30
|
| ОБЪЕДИНИТЬ
|
| ВЫБРАТЬ
| 31) КАК ТаблицаДней,
| (ВЫБРАТЬ
| 1 КАК Номер
|
| ОБЪЕДИНИТЬ ВСЕ
|
| ВЫБРАТЬ
| 2
|
| ОБЪЕДИНИТЬ ВСЕ
|
| ВЫБРАТЬ
| 3
|
| ОБЪЕДИНИТЬ ВСЕ
|
| ВЫБРАТЬ
| 4
|
| ОБЪЕДИНИТЬ ВСЕ
|
| ВЫБРАТЬ
| 5
|
| ОБЪЕДИНИТЬ ВСЕ
|
| ВЫБРАТЬ
| 6
|
| ОБЪЕДИНИТЬ ВСЕ
|
| ВЫБРАТЬ
| 7
|
| ОБЪЕДИНИТЬ ВСЕ
|
| ВЫБРАТЬ
| 8
|
| ОБЪЕДИНИТЬ ВСЕ
|
| ВЫБРАТЬ
| 9
|
| ОБЪЕДИНИТЬ ВСЕ
|
| ВЫБРАТЬ
| 10
|
| ОБЪЕДИНИТЬ ВСЕ
|
| ВЫБРАТЬ
| 11
|
| ОБЪЕДИНИТЬ ВСЕ
|
| ВЫБРАТЬ
| 12) КАК Месяцы) КАК Даты
|ГДЕ
|Даты.Дата МЕЖДУ &НачДата И &КонДата");

Запрос.УстановитьПараметр (
"НачДата",НачалоДня(НачПериода));
Запрос.УстановитьПараметр (
"КонДата",НачалоДня(КонПериода));
Результат=Запрос.Выполнить();

Вариант четвертый


Четвертый вариант формирования таблицы дат также отличается наличием того же ограничения, но он отличается по методологие формирования перечня дней и месяцев. В принципе данный приём можно использовать для формирования любых перечней чисел.
Запрос=Новый Запрос ("ВЫБРАТЬ
| 0 КАК зн
|ПОМЕСТИТЬ т01
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| 1
|;
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| т1.зн * 2 + т0.зн КАК зн
|ПОМЕСТИТЬ т02
|ИЗ
| т01 КАК т0,
| т01 КАК т1
|;
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| т1.зн * 4 + т0.зн КАК зн
|ПОМЕСТИТЬ Месяцы
|ИЗ
| т02 КАК т0,
| т02 КАК т1
|;
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| т03.зн * 2 + т01.зн КАК Дни
|ПОМЕСТИТЬ ТаблицаДней
|ИЗ
| Месяцы КАК т03,
| т01 КАК т01
|ГДЕ
| т03.зн * 2 + т01.зн <> 0
|;
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ РАЗЛИЧНЫЕ
| Даты.Дата
|ИЗ
| (ВЫБРАТЬ
| ДОБАВИТЬКДАТЕ(ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(&НачДата, ГОД), МЕСЯЦ, Месяцы.зн - 1), ДЕНЬ, ТаблицаДней.Дни - 1) КАК Дата,
| ТаблицаДней.Дни КАК Дни
| ИЗ
| Месяцы КАК Месяцы,
| ТаблицаДней КАК ТаблицаДней) КАК Даты
|ГДЕ
|Даты.Дата МЕЖДУ &НачДата И &КонДата");

Запрос.УстановитьПараметр (
"НачДата",НачалоДня(НачПериода));
Запрос.УстановитьПараметр (
"КонДата",НачалоДня(КонПериода));
Результат=Запрос.Выполнить();

Вариант пятый


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

Второй запрос


Первый запрос во всех вариантах формирует таблицу дат
Второй запрос использует первый
Итак:
ВЫБРАТЬ Даты.Дата КАК ДатаДок, Валюты. Валюта КАК Валюта, МАКСИМУМ(Валюты.Период) КАК ПериодДок ИЗ (//Первый запрос) КАК Даты ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК Валюты ПО Валюты.Период <= Даты.Дата ГДЕ
Валюты.Валюта=&Валюта
СГРУППИРОВАТЬ ПО Даты.Дата,
Валюты.Валюта
Мы обращаемся к истории изменений курса дата изменений должна удовлетворять двум условиям
1)Валюты.Период <= Даты.Дата
2)МАКСИМУМ(Валюты.Период) КАК ПериодДок
на этом этапе все лишние даты отбрасываются Если в этот момент потребовать вернуть курс, то запрос потребует группировки по курсам, а группировка по курсам приведет к попаданию в результирующую таблицу всех курсов какие были до момента включая дату изменения. Что нам не надо
Результат второго запроса выглядит так
01.01.2009 0:00:00 USD 01.01.2009
02.01.2009 0:00:00 USD 01.01.2009
03.01.2009 0:00:00 USD 03.01.2009
04.01.2009 0:00:00 USD 03.01.2009
05.01.2009 0:00:00 USD 03.01.2009

Третий запрос


ВЫБРАТЬ
Таб1.ДатаДок,
Таб2.Курс,
Таб1.Валюта
ИЗ
(//Второй запрос) КАК Таб1
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК Таб2
ПО Таб1.ПериодДок = Таб2.Период
И Таб1.Валюта=Таб2.Валюта
ГДЕ
Таб2.Валюта=&Валюта
Мы имеем таблицу с предыдущего запроса где все даты + даты последних изменений состояния регистра и нам остается только взять значение ресурса на дату последних изменений для чего нам и нужно условие
Таб1.ПериодДок = Таб2.Период
В данной редакции запросов я слишком много раз ставлю условие отбора по валюте, так много мест с условиями не обязательно
Результат третьего запроса выглядит так
01.01.2009 0:00:00 29,000 USD
02.01.2009 0:00:00 29,000 USD
03.01.2009 0:00:00 30,000 USD
04.01.2009 0:00:00 30,000 USD
05.01.2009 0:00:00 30,000 USD

1С 7.7


Что нам и требовалось получить, а сейчас попробуем получить все то же самое на платформе 7.7. В платформе 7.7 нет периодических регистров сведений, да и непериодических тоже, для хранения истории используются периодические реквизиты справочников. Итак, добавим в 7.7 новую конфигурацию и загрузим в неё объединением с типовой конфигурацией "Торговлей и склад" справочник "валюты". Так же перенесем из типовой конфигурации необходимые обработки для просмотра и редактирования истории периодических реквизитов, процедуры и функции глобального модуля для работы с периодическими реквизитами и добавим те же значения, но уже в 2010 году.

Получение значения на дату


Итак, наш код в 7.7 в первом приближении. Задача получить значение на указанную дату. Тут есть два способа, первый мы работаем со справочником и обращаемся к значению периодического реквизита через метод получить
Спр=СоздатьОбъект("Справочник.Валюты");
Спр.НайтиЭлемент(ВыбТекущийЭлемент);
Сообщить(
"Курс="+Спр.Курс.Получить (ВыбНачПериода));
Второй способ мы сразу задействуем метод использовать дату. Данный вариант удобен, когда был сложный код и реквизит периодическим не был, а тут он вдруг периодическим стал. Тогда мы просто объявляем использовать дату, и работаем со справочником, как и работали с минимальными изменениями в коде
Спр=СоздатьОбъект("Справочник.Валюты");
Спр.ИспользоватьДату (ВыбНачПериода);
Спр.НайтиЭлемент (ВыбТекущийЭлемент);
Сообщить(
"Курс="+Спр.Курс);
Лог сообщений после исполнения данной функции
сообщение Курс=29
сообщение Курс=29

Выборка дат изменения


Для выборки дат изменения мы должны использовать объект периодический
Пер=СоздатьОбъект("Периодический");
Пер.ИспользоватьОбъект (
"Курс",ВыбТекущийЭлемент);
Пер.ВыбратьЗначения(ВыбНачПериода,ВыбКонПериода);
Пока Пер.ПолучитьЗначение()=
1 Цикл
Сообщить(
"Изменение на дату "+
Пер.ДатаЗнач+

" значение курса равно "+
Пер.Значение);
КонецЦикла;
Лог сообщения после данной функции выгляди так
сообщение Изменение на дату 01.01.10 значение курса равно 29
сообщение Изменение на дату 03.01.10 значение курса равно 30

Работа через запросы


Сначала мы попробуем обратиться через один запрос
Запрос= СоздатьОбъект("Запрос");
ТекстЗапроса =

"//{{ЗАПРОС(Сформировать)
|Период с ВыбНачПериода по ВыбКонПериода;
|ОбрабатыватьДокументы все;
|Обрабатывать НеПомеченныеНаУдаление;
|ТекущийЭлемент = Справочник.Валюты.ТекущийЭлемент;
|Курс = Справочник.Валюты.Курс;
|Функция КурсСумма = Сумма(Курс);
|Группировка День все;
|Условие(ТекущийЭлемент = ВыбТекущийЭлемент);
|"//}}ЗАПРОС
;
Если Запрос.Выполнить(ТекстЗапроса)=
0 Тогда
Возврат
"";
КонецЕсли;
Пока Запрос.Группировка (
1) = 1 Цикл
Сообщить(
"Значение на дату "+
Запрос.День+

" курса равно "+
Запрос.КурсСумма);
КонецЦикла;

Лог этого запроса выглядит так
сообщение Значение на дату . . курса равно 30
сообщение Значение на дату 01.01.10 курса равно 0
сообщение Значение на дату 02.01.10 курса равно 0
сообщение Значение на дату 03.01.10 курса равно 0
сообщение Значение на дату 04.01.10 курса равно 0
сообщение Значение на дату 05.01.10 курса равно 0
Для второго запроса нам понадобиться таблица дат (документов). Мы в конечном запросе будем использовать внешнюю функцию, возвращающую значение курса и группировку по документам. Так же мы используем вторую внешнюю функцию, возвращающую половинку курса. Это пример нам нужен для демонстрации использования внешних переменных функциями, вызываемыми из запросов. Так, например если первая функция должна выполнить сложный анализ и вернуть некое число если истина, а вторая функция возвращает число если результат ложь, то имеет смысл ограничиться анализом в первой функции вместо анализа в обоих и в случае истины возвращать число в первой функции и 0 в случае если значение первой функции ложь. Перед возвратом значения имеет смысл положить значение во внешнюю переменную для второй внешней функции, при этом вторая внешняя функция будет возвращать тупо значение переменной вместо повтора анализа, что ускорит работу запроса. Итак, открываем структуру метаданных и создаем документ точка. Это будет наш служебный документ, который будет являться таблицей дат.
Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса =

"//{{ЗАПРОС(Сформировать)
|Период с ВыбНачПериода по ВыбКонПериода;
|ОбрабатыватьДокументы все;
|ТекущийДокумент = Документ.Точка.ТекущийДокумент;
|Функция Счётчик = Счётчик();
|Группировка День все;
|"//}}ЗАПРОС
;
// Здесь в запросе выборка документов вида Точка по дням
// один документ должен соответствовать одной дате

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

ТекстЗапроса =

"//{{ЗАПРОС(Сформировать)
|Период с ВыбНачПериода по ВыбКонПериода;
|ОбрабатыватьДокументы все;
|ТекущийДокумент = Документ.Точка.ТекущийДокумент;
|ДатаДок = Документ.Точка.ДатаДок;
|Функция Курс = Сумма(ВернутьКурс(ДатаДок));
|Функция ПолКурса = Сумма (ВернутьПолКурса());
|Группировка ТекущийДокумент;
|"//}}ЗАПРОС
;
Если Запрос.Выполнить (ТекстЗапроса) =
0 Тогда
Возврат
"" ;
КонецЕсли;
Пока Запрос.Группировка(
1)= 1 Цикл
Сообщить(
"Значение на дату "+
Запрос.ДатаДок+
" курса равно "+
Запрос.Курс+
" пол курса равно "+
Запрос.ПолКурса);
КонецЦикла;
Здесь во втором запросе мы используем внешние функции, анализирующую ВернутьКурс(ДатаДок) и тупо возвращающую значение переменной ВернутьПолКурса
Рассмотрим сам код модуля
Перем ПолКурса;

Функция ВернутьКурс(ДатаДок)
Спр=СоздатьОбъект(
"Справочник.Валюты");
Спр.НайтиЭлемент (ВыбТекущийЭлемент);
Курс=Спр.Курс.Получить (ДатаДок);
ПолКурса=Курс/
2;
//Присвоение значения переменной
Возврат(Курс);
КонецФункции

Функция ВернутьПолКурса ()
Возврат(ПолКурса);
КонецФункции
Выполним данный код и посмотрим на лог сообщений
сообщение Значение на дату 01.01.10 курса равно 29 пол курса равно 14
сообщение Значение на дату 02.01.10 курса равно 29 пол курса равно 14
сообщение Значение на дату 03.01.10 курса равно 30 пол курса равно 15
сообщение Значение на дату 04.01.10 курса равно 30 пол курса равно 15
сообщение Значение на дату 05.01.10 курса равно 30 пол курса равно 15
Лог сообщений выглядит весьма странно, так мы замечаем, что для курса 30 все хорошо половинка курса равна 15ти, а вот для 29ти половинка курса равна 14ти, что не есть правильно. Что это нам показывает? Что в некоторых случаях при применении внешних функций дробная часть может отбрасываться и её необходимо передавать как целое число еще одной внешней функции. Модифицируем код обработки
Перем ДробьКурса,ДробьПолКурса;

Функция ВернутьКурс2 (ДатаДок)
Спр=СоздатьОбъект(
"Справочник.Валюты");
Спр.НайтиЭлемент (ВыбТекущийЭлемент);
Курс=Спр.Курс.Получить (ДатаДок);
ПолКурса=Курс/
2;
ДробьКурса=(Курс-Цел(Курс))*
10000;
ДробьПолКурса=(ПолКурса-Цел(ПолКурса))*
10000;
Возврат(Курс);
КонецФункции

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

Функция ВернутьДробьПолКурса ()
Возврат(ДробьПолКурса);
КонецФункции

Функция ПравильноеЧисло (Само,Дробь)
//А вдруг да на каком-то релизе данной ошибки не будет
Если Само-Цел(Само)*
10000<>Дробь Тогда
Возврат(Число(Строка(Само)+
"." +Строка(Дробь)));
Иначе
Возврат(Само);
КонецЕсли;
КонецФункции
И немного подправим код запроса и код обработки после запроса
ТекстЗапроса =
"//{{ЗАПРОС(Сформировать)
|Период с ВыбНачПериода по ВыбКонПериода;
|ОбрабатыватьДокументы все;
|ТекущийДокумент = Документ.Точка.ТекущийДокумент;
|ДатаДок = Документ.Точка.ДатаДок;
|Функция Курс = Сумма(ВернутьКурс2(ДатаДок));
|Функция ДробьКурса = Сумма(ВернутьДробьКурса());
|Функция ПолКурса = Сумма(ВернутьПолКурса());
|Функция ДробьПолКурса = Сумма(ВернутьДробьПолКурса());
|Группировка ТекущийДокумент;
|"//}}ЗАПРОС
;
Если Запрос.Выполнить(ТекстЗапроса) =
0 Тогда
Возврат
"";
КонецЕсли;
Пока Запрос.Группировка (
1) = 1 Цикл
Сообщить(
"Значение на дату "+ Запрос.ДатаДок+
" курса равно"+
ПравильноеЧисло(Запрос.Курс,Запрос.ДробьКурса)+

" пол курса равно "+
ПравильноеЧисло (Запрос.ПолКурса,Запрос.ДробьПолКурса));
КонецЦикла;
Посмотрим лог сообщений
сообщение Значение на дату 01.01.10 курса равно 29.0 пол курса равно 14.5
сообщение Значение на дату 02.01.10 курса равно 29.0 пол курса равно 14.5
сообщение Значение на дату 03.01.10 курса равно 30.0 пол курса равно 15

Регистр сведений на 7.7

Все работает как надо, но несколько неудобно. Тот, кто уже поработал в восьмерке и вынужден работать на 7.7. скажет тут, как бы хорошо бы было использовать периодические регистры сведений на платформе 7.7. Что же с определенными ограничениями такая возможность существует. Что это за ограничения и что это за возможность. Во-первых, регистр сведений можно сделать отдельным справочником и обращаться к нему прямыми запросами через компоненту 1С++, но это слишком сложно. Гораздо проще задействовать компоненту оперативный учет, если она установлена и в качестве регистра сведений, задействовать регистр накопления. Ограничения тут будут следующие наш "Регистр сведений" не может быть независимым и должен иметь обязательно один или несколько документов регистраторов. Дата движения по регистру сведений вынуждена совпадать с датой и позицией документа. Мы не можем записать движения в неоткрытый период оперативного учета и обратиться запросом позже точки актуальности. На этом все.
Добавляем в нашу конфигурацию РегистрНакопления КурсыВалют с измерением Валюта и ресурсом Курс. Добавляем документ Регистратор с реквизитами Шапки Валюта и Курс
Пишем код проведения документа
Процедура ОбработкаПроведения ()
Если ПустоеЗначение(Валюта)=
1 Тогда
Сообщить(
"Не выбрана валюта!" );
СтатусВозврата(
0);
Возврат;
КонецЕсли;

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

Если ИтогиАктуальны()=
0 Тогда
ВремКурсыВалют.ВременныйРасчет();
ВремРегистры.Актуальность(
1);
ВремРегистры.РассчитатьРегистрыНа (ТекущийДокумент());
КонецЕсли;

ТЗ=СоздатьОбъект(
"ТаблицаЗначений");
ВремКурсыВалют.ВыгрузитьИтоги(ТЗ,
1,1);
Если ТЗ.КоличествоСтрок ()>
1 Тогда
Сообщить(
"Системная ошибка!" );
СтатусВозврата(
0);
Возврат;
КонецЕсли;

Если ТЗ.КоличествоСтрок()=
1 Тогда
//Списываем старый курс
Регистр.КурсыВалют.Валюта=Валюта;
Регистр.КурсыВалют.Курс=ТЗ.ПолучитьЗначение (
1,"Курс");
Регистр.КурсыВалют.ДвижениеРасходВыполнить ();
КонецЕсли;

Регистр.КурсыВалют.Валюта=Валюта;
Регистр.КурсыВалют.Курс=Курс;
Регистр.КурсыВалют.ДвижениеПриходВыполнить();
КонецПроцедуры
Модифицируем наш отчет, в конфигурации он называется Демо добавлением новой ветки.
Функция ПроверкаПериода (Фл=0) далее
Функция РегистрСведений()
Перем Запрос,ТекстЗапроса;
Если ПроверкаПериода(
2)=1 Тогда Возврат ""; КонецЕсли;

Запрос = СоздатьОбъект(
"Запрос");
ТекстЗапроса =

"//{{ЗАПРОС(Сформировать)
|Период с ВыбНачПериода по ВыбКонПериода;
|Обрабатывать НеПомеченныеНаУдаление;
|Валюта = Регистр.КурсыВалют.Валюта;
|Курс = Регистр.КурсыВалют.Курс;
|Функция КурсКонОст = КонОст(Курс);
|Группировка День все;
|Условие(Валюта = ВыбТекущийЭлемент);
|"//}}ЗАПРОС
;
Если Запрос.Выполнить(ТекстЗапроса) =
0 Тогда
Возврат
"";
КонецЕсли;

Пока Запрос.Группировка(
1) = 1 Цикл
Сообщить(
"На дату "+
Запрос.День+

" курс равен "+
Окр(Запрос.КурсКонОст,
4));
КонецЦикла;
КонецФункции
Вводим в пользовательском режиме два документа регистратора (предварительно открыв период) на соответствующие даты, но уже что бы избежать путаницы на 2010 год и сформировав отчет, смотрим лог
сообщение На дату 05.01.10 курс равен 37.4568
сообщение На дату 06.01.10 курс равен 37.4568
сообщение На дату 07.01.10 курс равен 24.1234
Все получилось как надо
Продолжение ожидается.