Андрей
Егоров где-то в
2001
Примечание – большая часть
написанного здесь текста с примерами взята по памяти (пару лет
назад изучал вопрос достаточно подробно, поэтому что-то в алгоритмах может и не работать – я
ведь их не копировал откуда-то, а прямо тут же и
писал, так что за синтаксические ошибки не пинайте). На данный
момент я активно OLE не пользуюсь (не из-за каких-то проблем с самим OLE, а из-за отсутствия надобности в нём в текущий
момент).
Основные преимущества, благодаря которым OLE активно
используется в работе с V7:
- Для вызывающей базы по барабану – какой тип имеет
вызываемая база (DBF или SQL)
- Объектами вызываемой базы можно управлять практически
всеми известными методами работы с объектами V7 (т.е. со
справочниками работают методы справочников, с
документами – методы документов, и т.д.).
Соответственно, можно напрямую решить – стоит
отрабатывать конкретные объекты вызываемой базы, или же
пропустить их.
1. Присоединение к базе V7 через
OLE.
БазаОле=СоздатьОбъект("V77.Application");
//
Получаем доступ к OLE объекту V7 |
Очень важно знать, какая версия V7 установлена на
компьютере (локальная, сетевая или SQL), так как каждая из них
прописывает в реестре свое значение ключа для запуска через
OLE:
- Локальная версия (на одного пользователя):
V77L.Application
- Сетевая версия: V77.Application Версия SQL:
V77S.Application
Далее, вместо термина «вызываемая
база» будет написано просто «база OLE», а вместо термина
«вызывающая база» – «местная база«.
Теперь, мы должны знать несколько параметров для запуска
базы OLE: каталог базы, имя пользователя и пароль. Ну,
наверное, ещё понадобится возможность запустить базу OLE в
монопольном режиме ;-).
Запускаем базу следующим образом:
КаталогБазыОЛе =
"C:\program files\1cv77\МояБаза\";
ПользовательОле =
"Администратор";
ПарольОле =
"qwerty";
МонопольныйРежимOLE = " /m";
// для
немонопольного запуска указать пустую
строку!
ЗапускБезЗаставки = 1;
// для
появления заставки поставьте
"0"
РезультатПодключения =
БазаОле.Initialize
( БазаОле.RMTrade , "/d" +
Сокрлп(КаталогБазыОле) + "
/n" +
Сокрлп(ПользовательОле)+ " /p" +
Сокрлп(ПарольОле) + МонопольныйРежимOLE
,
?(ЗапускБезЗаставки =
1,"NO_SPLASH_SHOW",""));
Если
РезультатПодключения = 0 Тогда
Предупреждение("Ошибка
подключения.");
КонецЕсли;
|
Комментарий: функции СокрЛП() указаны в примере на тот
случай, если пользователь захочет указанные выше переменные
сделать реквизитами диалога (проблема при этом состоит в том,
что в алгоритм программа передаст полное значение реквизита,
т.е. допишет в конце значения то количество пробелов, которое
необходимо для получения полной длины строки, указанной в
свойствах реквизита диалога).
2. Доступ к объектам базы OLE
Запомните на будущее как непреложный факт:
- Из местной базы в базу OLE (и, соответственно, наоборот)
напрямую методом присвоения можно перенести только числовые значения, даты и строки ограниченной длины. Т.е.
местная база «поймет» прекрасно без дополнительных
алгоритмов преобразования полученного значения только
простые типы значений. Кроме того, под ограничением строк
подразумевается проблемы с пониманием в местной базе
реквизитов объектов базы OLE типа Строка неограниченной
длины. К этому же еще надо добавить и периодические
реквизиты. Естественно, под методом присвоения
подразумеваются и попытки сравнения объектов из
разных баз.
- Есть проблемы при попытке перенести пустую дату –
OLE может ее конвертировать, например, в 31.12.1899 и т.п.
Поэтому вам лучше заранее выяснить те значения, которые
могут появится в местной базе при переносе пустых дат, чтобы
предусмотреть условия преобразования их в местной
базе.
Доступ к константам базы OLE
ЗначениеКонстантыOLE =
БазаОле.Константа.ДатаЗапретаРедактирования; |
Доступ к справочникам и документам базы OLE (через функцию CreateObject)
СпрOLE =
БазаОле.CreateObject("Справочник.Фирмы");
ДокOLE =
БазаОле.CreateObject("Документ.РасходнаяНакладная");
// "СоздатьОбъект" в OLE не
работает! |
После создания объекта справочника или документа к нему
применимы все методы касающиеся соответствующего объекта V7:
СпрОле.ВыбратьЭлементы();
Пока
СпрОле.ПолучитьЭлемент()
Цикл
Сообщить(Спр.Наименование);
КонецЦикла; |
Заметьте, что если В операторе Сообщить вместо
Спр.наименование вы укажете Спр.ТекущийЭлемент(), то вместо строкового или числового представления этого
элемента программа выдаст просто "OLE". Именно это я и имел в
виду, когда говорил, что напрямую мало что можно вызвать.
Гарантированно не будут работать методы ТекущийЭлемент() и ТекущийДокумент() (ошибки V7 не будет, но и результат работы будет нулевой).
Рассмотрим следующий пример:
СпрOLE =
БазаОле.CreateObject("Справочник.Фирмы");
// это
справочник в базе OLE
Док =
СоздатьОбъект("Документ.РасходнаяНакладная");
// а
это документ в местной базе
Док.Новый(); //
создаем новый документ в местной
базе
СпрOLE.НайтиПоКоду(1,0);
// ищем в базе
OLE фирму с кодом "1"
Док.Фирма =
СпрOLE.ТекущийЭлемент();
// такой метод не
сработает, т.к. справа от "=" стоит
// объект не
местной базы, и местная база его не поймёт
//
однако сработает следующий метод:
Спр =
СоздатьОбъект("Справочник.Фирмы");
// создаем объект
справочника местной
базы
Спр.найтиПоКоду(СпрОле.Код,0);
// Или
Спр.НайтиПоНаименованию(СпрОле.Наименование,0,0);
//
т.е. СпрОле.Код и Спр.Наименование являются
обычными
// числовыми/строковыми значениями,
//
которые понимает местная база
Док.Фирма =
Спр.ТекущийЭлемент();
// Вот теперь все в порядке,
т.к. с обоих сторон метода
// стоят объекты только
местной базы |
Отсюда вывод: возможность доступа к объектам базы V7 через
OLE требуется, в основном, только для определенной
задачи – получить доступ к реквизитам определенного
элемента справочника или документа.
Однако не забываем, что объекты базы OLE поддерживают все методы работы с ними, в том числе и Новый(). Приведем пример, противоположный
предыдущему:
ДокОле =
CreateObject("Документ.РасходнаяНакладная");
//
Создаем документ в базе
OLE
ДокОле.Новый();
Спр =
СоздатьОбъект("Справочник.Фирмы");
// В местной базе
получаем доступ к
справочнику
Спр.НайтиПоКоду(1,0);
// Находим
в местной базе фирму с кодом 1
ДокОле.Фирма =
Спр.ТекущийЭлемент();
// такой метод не
сработает
// однако сработает следующий
метод:
СпрОле =
СоздатьОбъект("Справочник.Фирмы");
// создаем объект
справочника базы
OLE
СпрОле.найтиПоКоду(Спр.Код,0);
// Или
СпрОле.НайтиПоНаименованию(Спр.Наименование,0,0);
//
т.е. Спр.Код и Спр.наименование являются
// обычными
числовыми/строковыми значениями,
// которые понимает
база OLE
ДокОле.Фирма = СпрОле.ТекущийЭлемент();
// Вот теперь все в порядке, т.к. с обоих
сторон
// метода стоят объекты базы
OLE
ДокОле.Записать();
// запишем документ в
базе OLE
Если ДокОле.Провести()=0 Тогда
Сообщить("Ничего не вышло");
КонецЕсли; |
Доступ к регистрам базы OLE: не сложнее
справочников и документов
РегОле=БазаOLE.CreateObject("Регистр.ОстаткиТоваров");
РегОле.ВыбратьИтоги();
Пока
РегОле.ПолучитьИтог()=1 Цикл
// Не забываем, что
надо указывать наименование
Сообщить(
"Остаток
для " +
Рег.Товар.Наименование +
" на складе "
+
Рег.Склад.Наименование +
" равен " +
Рег.ОстатокТовара
);
КонецЦикла; |
Доступ к перечислениям базы OLE (аналогичен
константе)
ЗначениеПеречисленияOLE =
БазаОле.Перечисление.Булево.НеЗнаю; // ;-) |
Заметьте, что пользы для местной базы от переменной
ЗначениеПеречисленияOLE особо-то и нет, ведь,
подобно справочнику и документу, перечисление также напрямую
недоступно для местной базы.
Пожалуй, пример работы с ними может быть следующим (в
качестве параметра условия):
СмотретьТолькоВозвратыПоставщикам = 1;
// предположим, что это флажок в форме
диалога,
// который мы либо устанавливаем, либо
снимаем
ДокОле =
CreateObject("Документ.РасходнаяНакладная");
ДокОле.ВыбратьДокументы(НачДата,КонДата);
// НачДата и КонДата также реквизиты формы
//
диалога, но база OLE прекрасно их понимает -
// ведь
это же даты
Пока ДокОле.ПолучитьДокумент()=1 Цикл
Если СмотретьТолькоВозвратыПоставщикам = 1
Тогда
Если ДокОле.ПризнакНакладной <>
БазаОле.Перечисление.ПризнРасхНакл.ВозвратПоставщику
Тогда
Продолжить;
КонецЕсли;
Иначе
Если ДокОле.ПризнакНакладной
=
БазаОле.Перечисление.ПризнРасхНакл.ВозвратПоставщику
Тогда
Продолжить;
КонецЕсли;
КонецЕсли;
Сообщить(
ДокОле.Вид() + "
№ "+ДокОле.НомерДок +
" от " +
ДокОле.датаДок
);
КонецЦикла; |
Доступ к счетам базы OLE
СчтОле=БазаОле.CreateObject("Счет");
СчтОле.НайтиПоКоду("10.5");
// нашли в базе OLE счет 10.5 |
Доступ к ВидамСубконто базы OLE (аналогичен
перечислению)
ВидСубконтоКонтрагентыОле =
БазаОле.ВидыСубконто.Контрагенты; |
По аналогии со справочниками и документами работает объект
Периодический, план счетов работает по аналогии с
ВидомСубконто, ну и далее в том же духе.
Отдельную главу посвятим запросу, а сейчас – стоп. Самое-то главное забыли!
Доступ к функциям и процедурам глобального модуля
базы OLE
Как же я про это забыл-то, а? Поскольку при запуске базы
автоматически компилируется глобальный модуль, то нам
становятся доступны его функции и процедуры (поправлюсь –
только те, у которых выставлена опция Экспорт). Плюс к ним еще
и различные системные функции V7. А доступны они нам через
функцию V7 EvalExpr(). Приведем примеры работы с
базой OLE:
ДатаАктуальностиОле =
БазаОле.EvalExpr("ПолучитьДатуТА()");
// Возвращает
дату актуальности
ИмяПользователяОле =
БазаОле.EvalExpr("ИмяПользователя()");
// возвращает
строку
// попробуем теперь получить числовое
значение НДС у
// элемента номенклатуры через
функцию глобального
// модуля ПроцентНДС(СтавкаНДС)
ТовОле =
БазаОле.CreateObject("Справочник.Номенклатура");
ТовОле.ВыбратьЭлементы();
//
Найдем элемент справочника (не группа!)
Пока
ТовОле.ПолучитьЭлемент()=1 Цикл
Если
ТовОле.ЭтоГруппа()=0 Тогда
Прервать;
КонецЕсли;
КонецЦикла;
ЧисловоеЗначениеПроцентаНДСТовараОле
=
БазаОле.EvalExpr("ПроцентНДС(Перечисление.ЗначенияНДС."
+
ТовОле.СтавкаНДС.Идентификатор()+")"); |
На самом деле, в последней строке примера я исхитрился и
забежал немного вперед. Дело в том, что как и запрос (см.
отдельную главу), так и EvalExpr() выполняются внутри базы OLE, причем команды передаются им строкой,
и поэтому приходится ломать голову, как передать им
необходимые значения объектов базы OLE в строке,
сформированной в местной базе.
3. Алгоритмы преобразования объектов в удобоваримый вид
между базами
Ясно, что алгоритмы преобразования нужны не только для
переноса объектов в между и базами, но и для такой простой
задачи, как попытки сравнить их между собой. И еще раз обращу
внимание: ОБЪЕКТЫ ОДНОЙ БАЗЫ ПРЕКРАСНО ПОНИМАЮТ ДРУГ ДРУГА,
ПРОБЛЕМЫ ВОЗНИКАЮТ ТОЛЬКО КОГДА ВЫ НАЧИНАЕТЕ СВЯЗЫВАТЬ МЕЖДУ
СОБОЙ ОБЪЕКТЫ РАЗНЫХ БАЗ.
Команда
ДокОле.Фирма=СпрОле.ТекущийЭлемент();
// где ДокОле – документ базы OLE,
// a
СпрОле – справочник "Фирмы" базы OLE |
будет прекрасно работать без ошибок. Не забывайте это,
чтобы не перемудрить с алгоритмами!
Итак, повторяюсь, что и перенести напрямую, и просто
сравнить можно только даты (причем не пустые), числа и строки
ограниченной длины. Вопрос – как же нам сравнить
агрегатные объекты из разных баз (не числа, не даты и не
строки), т.е. как их преобразовать в эту самую
строку/число/дату?.
Преобразование справочников и документов базы OLE (если есть аналоги в местной базе).
В принципе, преобразование их было уже рассмотрено в
примерах выше и сводится к поиску их аналогов в местной базе.
Могу еще раз привести пример, заодно с использованием
регистров:
// ВыбФирма, НачДата,
КонДата, ВыбДокумент
// это реквизиты диалога в
местной базе
// причем они все указаны, т.е. не
пустые (чтобы не делать
//лишних команд в
примере)
ДокОле =
БазаОле.CreateObject("Документ.РасходнаяНакладная");
//
объект базы OLE
Док =
СоздатьОбъект("Документ.РасходнаяНакладная");
// а
это его аналог в местной базе
Спр =
СоздатьОбъект("Справочник.Фирмы");
// а это –
местный справочник
фирм
ДокОле.ВыбратьДокументы(НачДата,КонДата);
Пока
ДокОле.ПолучитьДокумент()=1 Цикл
// Ищем в местном
справочнике фирм аналог фирмы
// из базы OLE (по
коду):
Если Спр.найтиПоКоду(ДокОле.Фирма.Код,1)=0
Тогда
Сообщить(
"Найден документ с неизвестной
фирмой!"
"Ее код: " + ДокОле.Фирма.Код+""
);
//
На самом деле, она может быть просто не указана :))
Если ДокОле.Фирма.Выбран()=0 Тогда
Сообщить("Oна просто не указана!
:))");
КонецЕсли;
ИначеЕсли
Спр.ТекущийЭлемент()=ВыбФирма
Тогда
Сообщить(
"Найден документ указанной вами
фирмы!"
" № "+ ДокОле.НомерДок +" от " +
ДокОле.ДатаДок+""
);
КонецЕсли;
// Ищем
аналог документа в местной
базе
Док.НайтиПоНомеру(ДокОле.НомерДок,ДокОле.датаДок);
Если
Док.Выбран()=1 Тогда
Сообщить(
"Документ №
"+Док.НомерДок + " от " +
""+Док.ДатаДок + " уже
есть!"
);
Если Док.ТекущийДокумент() =
ВыбДокумент Тогда
Предупреждение("Документ
найден!");
КонецЕсли;
Иначе
Сообщить(
"Документ
№ "+ДокОле.НомерДок + " от " +
"" + ДокОле.ДатаДок +
" не найден в
базе!");
КонецЕсли;
КонецЦикла;
// А
заодно и получим остаток товара в базе OLE:
РегОле =
БазаОле.CreateObject("Регистр.ОстаткиТоваров");
ТовОле
=
БазаОле.CreateObject("Справочник.Номенклатура");
ТовОле.НайтиПоКоду(ВыбТовар.Код,0);
ФрмОле
=
БазаОле.CreateObject("Справочник.Фирмы");
ФрмОле.НайтиПоКоду(ВыбФирма.Код,0);
ОстатокТовараНаСкладеОле
=
РегОле.СводныйОстаток(
ТовОле.ТекущийЭлемент(),,
Фрм.ТекущийЭлемент(),
"ОстатокТовара"); |
Преобразование перечислений и видов субконто (подразумевается, что в обоих базах есть аналоги)
Вся задача сводится к получению строкового или числового
представления перечисления или вида субконто. Не поймите это
как прямую команду воспользоваться функцией Строка() или Число() ;-). Нет. Для этого у нас есть обращение к
уникальному представлению перечисления и вида субконто –
метод Идентификатор() или ЗначениеПоНомеру(). Второй вариант не очень
подходит, так как зачастую в разных базах даже перечисления
бывают расположены в другом порядке, а вот идентификаторы
стараются держать одинаковыми в разных базах. Отсюда вывод,
пользуйтесь идентификаторами. Кстати, не путайте вид субконто
с самим субконто! Привожу пример преобразования:
// ДокОле – документ
базы OLE, и
// уже спозиционирован на нужном нам
документе,
// Док – документ местной базы
//
(например, новый), который мы пытаемся заполнить
//
реквизитами из документа базы OLE
// Будем считать,
что реквизиты типа "справочник"
// мы уже перенесли
:)
// Преобразуем перечисление, и не говорите
потом
// "с ума сойти, как оказалось все
просто!"
Если
ПустоеЗначение(
ДокОле.ПризнакНакладной.Идентификатор())=0
Тогда
// если не проверим реквизит на пустое значение
-
//нарвемся на ошибку V7 :(
Док.ПризнакНакладной =
Перечисление.ПризнРасхНакл.ЗначениеПоИдентификатору(
ДокОле.ПризнакНакладной.Идентификатор());
// Что и требовалось
сделать
КонецЕсли;
// Преобразуем вид
субконто
Если
ПустоеЗначение(
ДокОле.ВидСубконтоСчетаЗатрат.Идентификатор())=0
Тогда
// если не проверим реквизит на пустое
значение -
// нарвемся на ошибку V7 :(
Док.
ВидСубконтоСчетаЗатрат =
ВидСубконто.ЗначениеПоИдентификатору(
ДокОле.ВидСубконтоСчетаЗатрат.Идентификатор());
// Что и требовалось сделать
КонецЕсли; |
То же самое относится и к плану счетов – принцип
работы с ним тот же, что и для вида субконто.
Преобразование счетов
Во многом объект Счет аналогичен объекту Справочник. Отсюда
и пляшем:
Док.СчетУчета=СчетПоКоду(ДокОле.СчетУчета.Код);
// присвоили документу реквизит счета
Если
СчетПоКоду(ДокОле.СчетУчета.Код)=СчетПоКоду("10.5")
Тогда
// Это именно счет 10.5
:)
КонецЕсли;
// Я специально оставил в "Если"
функцию СчетПоКоду(),
// т.к. в планах счетов разных
баз
// могут быть разные форматы счетов, и простое
сравнение
// кода может привести к противоположному
результату.
// Кстати, и сам СчетПоКоду() тоже может
иногда подвести,
// так что вам решать, каким методом
пользоваться:) |
Наверное, по преобразованию объектов уже хватит.
4. Работа с запросами и EvalExpr()
Наконец-то добрались и до запросов. Надо пояснить несколько
вещей, касаемых запросов (да и EvalExpr() тоже).
Самое главное – компиляция текста OLE-запроса (т.е. разбор всех переменных внутри
запроса), как и сами OLE-запросы, выполняются внутри базы OLE и поэтому ни одна переменная, ни один
реквизит местной базы там недоступны. Да и сам запрос даже не
подозревает, что его запускают по OLE из другой базы! Поэтому,
чтобы правильно составить текст, иногда требуется не только
обдумать, как передать параметры запроса в базу OLE, но и
обдумать, что нужно добавить в глобальный модуль той самой
базы OLE, чтобы как-то собрать для запроса переменные!
- Поскольку сам текст запроса и функции EvalExpr() является по сути текстом, а не
набором параметров, то напрямую передать ему ссылку на
элемент справочника, документ, счет и т.п. нельзя.
Исключение может быть составлено для конкретных значений
перечислений, видов субконто, констант, планов счетов и т.п.
- Хоть и многим и так понятно, что я скажу дальше, но я все-таки уточню: при описании переменных в
тексте запроса не забывайте, что объекты базы надо указывать
напрямую, без всяких префиксов типа БазаОле.
- Отрабатывать запрос сложно тем, что ошибки, например,
при компиляции напрямую не увидеть. Поэтому начинаем
пошагово готовится к отработке запроса в базе OLE.
Вначале допишем в глобальном модуле базы OLE
несколько строк, которые нам помогут в работе:
Перем
BR СписокЗначенийЗапроса[10] Экспорт;
// Мы в них
запихнем"значения для запроса
Функция
СкорректироватьСписок(
ИндексМассива,
Действие,
ТипОбъекта
= "",
ВидОбъекта = "",
Параметр1 =
"",
Параметр2 = "",
Параметр3 = "",
// Ну и
далее, сколько надо
// ...
Параметр99 = "")
Экспорт
ИндексМассива=Число(ИндексМассива);
Если ИндексМассива = 0 Тогда
Возврат -99;
// Не указали индекс массива
КонецЕсли;
Если Действие = 1 Тогда
// Очистить список
значений
СпЗапроса[ИндексМассива].УдалитьВсе();
Возврат
1;
ИначеЕсли Действие = 2 Тогда
// Добавить
объект в список
Если ТипОбъекта = "Документ"
Тогда
Если ВидОбъекта = "" Тогда
Возврат -99;
// Передавайте нормальный вид объекта!
КонецЕсли;
Попытка
Док =
СоздатьОбъект("Документ."+ВидОбъекта);
Исключение
Возврат
-99;
// Попытка обращения к неверному
объекту
КонецПопытки;
Если
Док.НайтиПоНомеру(Параметр1,Параметр2)=1
Тогда
СпЗапроса[ИндексМассива].ДобавитьЗначение(
Док.ТекущийДокумент());
Возврат
1; // Нашли документ
Иначе
Возврат 0; // Не нашли
документ :(
КонецЕсли;
ИначеЕсли ТипОбъекта =
"Справочник" Тогда
Если ВидОбъекта = ""
Тогда
Возврат -99;
// Передавайте нормальный вид
объекта!
КонецЕсли;
Попытка
Спр =
СоздатьОбъект("Справочник."+ВидОбъекта);
Исключение
Возврат
-99;
// Попытка обращения к неверному
объекту
КонецПопытки;
// Параметр1 –
Код
// Параметр2 – наименование
//
Параметр3 – флаг глобального поиска
Если
Спр.НайтиПоКоду(Параметр1,Число(Параметр3))=1
Тогда
СпЗапроса[ИндексМассива].ДобавитьЗначение(
Спр.ТекущийЭлемент());
Возврат
1; // Нашли элемент
ИначеЕсли
Спр.НайтиПоНаименованию(
Параметр2,
Число(Параметр3))=1
Тогда
СпЗапроса[ИндексМассива].ДобавитьЗначение(
Спр.ТекущийЭлемент());
Возврат
1; // Нашли элемент
Иначе
Возврат 0; // Не
нашли элемент :(
КонецЕсли;
ИначеЕсли
ТипОбъекта = "Счет" Тогда
// Вид объекта –
Идентификатор плана счетов
// Параметр1 – Код
счета
Если ВидОбъекта<>""
Тогда
ИскомыйСчет =
СчетПоКоду(Параметр1,
ПланСчетов.ЗначениеПоИдентификатору(
ВидОбъекта));
Иначе
ИскомыйСчет
= СчетПоКоду( Параметр1);
КонецЕсли;
Если
ПустоеЗначение(ИскомыйСчет) = 1 Тогда
Возврат 0; //
не нашли счет
:(
Иначе
СпЗапроса[ИндексМассива].ДобавитьЗначение(
ИскомыйСчет);
Возврат
1; // нашли счет
КонецЕсли;
Иначе
Возврат
-99;
// Неверный тип объекта
КонецЕсли;
ИначеЕсли Действие = 3 Тогда
// Вернуть
размер списка
Возврат
СпЗапроса[ИндексМассива].РазмерСписка();
Иначе
Возврат
-99;
// Неверное действие
КонецЕсли;
Возврат -999;
КонецФункции
Процедура
ПриНачалеРаботыСистемы()
// Данная процедура уже
есть в глобальном модуле,
// просто надо дописать в
неё несколько строк:
Для к=1 По 10
Цикл
СпЗапроса[к]=СоздатьОбъект("СписокЗначений");
КонецЦикла;
КонецПроцедуры |
Теперь начинаем потихоньку писать сам запрос. Что мы имеем?
В форме диалога местной базы есть несколько реквизитов диалога
(либо это будут местные переменные):
- Даты периода (НачДата и КонДата)
- Элементы справочников для фильтрации (ВыбТовар,
ВыбФирма, ВыбКлиент, и т.д.)
- Какие-либо флажки (ТолькоЗамерзающийЗимойТовар , ...)
Мы начинаем писать запрос и сразу попадаем в этакую
ловушку:
ТекстЗапроса = " Период с
Начдата по КонДата; "; |
Вроде все в порядке, но такой запрос не выполнится в базе
OLE, так как там понятия не имеют, что такое НачДата и
КонДата. Ведь эти переменные действительны только для местной
базы! Переписываем запрос заново:
ТекстЗапроса = " Период с
'"+НачДата+ "' по '"+КонДата+"';
// Будет типа
'01.01.02'
| Товар =
Регистр.ОстаткиТоваров.Товар;
| Фирма =
Регистр.ОстаткиТоваров.Фирма;
| Склад =
Регистр.ОстаткиТоваров.Склад;
| Остаток =
Регистр.ОстаткиТоваров.Остаток;
| Группировка Товар
без групп;
| Группировка Документ;
| Функция
НачОст=НачОст(Остаток);
| Функция
КонОст=КонОст(Остаток);
| Функция
ПрихОст=Приход(Остаток)
| Функция
РасхОст=Расход(Остаток);"; |
Так... Дошли до условий отбора в запросе. Рассмотрим два
варианта, когда выбран ВыбТовар:
// 1-й вариант, выбран
элемент справочника (не группа).
// Самый простой
случай - коды товаров совпадают абсолютно
//
Вариант 1а.
Если ВыбТовар.Выбран()=1 Тогда
ТекстЗапроса = ТекстЗапроса +
"Условие
(Товар.Код = "
+ВыбТовар.Код+");";
КонецЕсли;
// Вариант 1б.
Чтоб запрос работал быстрее
// Вначале добавим к
запросу переменную в общем списке:
// | КодТовара =
Регистр.ОстаткиТоваров.Товар.Код;
// А уж потом
добавим к запросу условие (такое
// условие будет
выполнятся проще, так как
// запрос при формировании
таблицы запроса сразу
// сформирует отдельную
колонку кодов и по ней уже
// будет отбирать, а не
будет каждый раз при обработке
// товаров извлекать
из них код)
Если ВыбТовар.Выбран()=1 Тогда
ТекстЗапроса = ТекстЗапроса +
"Условие
(КодТовара = " +ВыбТовар.Код+");";
КонецЕсли; |
Казалось бы все очень просто. По аналогии – если
уникальность для товаров ведется по наименованию, то простой
заменой слова «код» на слово «наименование» мы решаем вопрос и
здесь. Теперь рассмотрим, когда мы выбрали группу, т.е. текст
условия должен будет выглядеть так:
| Условие
(товар.ПринадлежитГруппе(КакаяТоГруппа)=1); |
Правда, и здесь можно проблему решить двумями путями ;-). Первый путь – когда мы имеем дело с
двухуровневым справочником. Тогда проблема группы решается так
же просто, как и в первом варианте:
// Добавляем в
списке переменных строку:
| КодРодителя =
Регистр.ОстаткиТоваров.Товар.Родитель.Код;
//
Далее пишем условие:
Если ВыбТовар.Выбран()=1 Тогда
ТекстЗапроса = ТекстЗапроса +
"Условие
(КодРодителя = "
+ВыбТовар.Код+");";
КонецЕсли; |
А если справочник очень даже многоуровневый? Вот для этого
мы и используем написанную ранее
функцию. Предположим, что
список значений запроса с индексом массива 1 мы будем
использовать
для хранения подобных значений (например,
хранить в нем группы товаров, клиентов) для
хитрых
условий. Итак, например, в ВыбТовар у нас указана группа
товаров, а в
ВыбКлиент – группа клиентов, которым мы
товары группы ВыбТовар продавали.
Кроме того, мы должны пропустить накладные возвратов
поставщикам, и не забыть, что товары
надо еще и отбирать
по флажку ТолькоЗамерзающийЗимойТовар:
// Очистим список
значений запроса
Результат =
БазаОле.EvalExpr("СкорректироватьСписок(1,1)");
//
Закинем в список значений запроса группу товаров
//
(он сам найдет ее в базе OLE)
// И запоминаем (в
уме), что этой группе соответствует
// 1-e значение
списка
Результат =
БазаОле.EvalExpr("СкорректироватьСписок(1,2,
""Справочник"",
""" +
Выбтовар.Вид())+ """," +
ВыбТовар.Код + ",
""" +
ВыбТовар.Наименование + """)");
//
Теперь закинем в список значений запроса группу клиентов
// И запоминаем, что этой группе соответствует
// 2-е значение списка
Результат =
БазаОле.EvalExpr("СкорректироватьСписок(1,2,
""Справочник"",
""" +
ВыбКлиент.Вид())+ """," +
ВыбКлиент.Код +
", """ +
ВыбКлиент.Наименование + """)");
// А
еще до кучи и фирму из ВыбФирма
// И запоминаем, что
этой фирме соответствует
// 3-е значение
списка
Результат =
БазаОле.EvalExpr("СкорректироватьСписок(1,2,
""Справочник"",
""" +
ВыбФирма.Вид())+ """," +
ВыбФирма.Код + ",
""" +
ВыбФирма.Наименование + """)");
//
Теперь формируем текст запроса
ТекстЗапроса = "
Период с '"+НачДата+ "' по '"+КонДата+"';
| Товар =
Документ.РасходнаяНакладная.Товар;
| Замерзает =
|
Документ.РасходнаяНакладная.Товар.ЗамерзаетЗимой;
|
Признак =
Документ.РасходнаяНакладная.ПризнакНакладной;
| Фирма
= Документ.РасходнаяНакладная.Фирма;
| Клиент =
Документ.РасходнаяНакладная.Клиент;
| Количество =
Документ.РасходнаяНакладная.Количество;
| СуммаДок =
Документ.РасходнаяНакладная.Сумма;
| Группировка
Товар без групп;
| Группировка Документ;
|
Функция СуммаОтгрузки=Сумма(СуммаДок);
| Условие
(Признак<>
|
Перечисление.ПризнРасхНакл.ВозвратПоставщику);
|
Условие (Замерзает = " + ТолькоЗамерзающийЗимойТовар +
");
// Внимание! Начинается:
| Условие
(Товар.ПринадлежитГруппе(
|
СпЗапроса[1].ПолучитьЗначение(1))=1);
| Условие
(Клиент.ПринадлежитГруппе(
|
СпЗапроса[1].ПолучитьЗначение(2))=1);
| Условие
(Фирма= СпЗапроса[1].ПолучитьЗначение(3));"; |
Уфф. Вроде все. Остается только запустить запрос:
Запрос =
БазаОле.CreateObject("Запрос");
Если
Запрос.Выполнить(ТекстЗапроса)=0 Тогда
Предупреждение("Запрос
безутешен!");
Возврат;
КонецЕсли; |
Ну, а с реквизитами запроса разбираемся так же, как указано
было выше в предыдущих разделах. И не забываем, что кроме
хранения конкретных значений можно использовать другие списки
значений запроса. Например, можно заполнить какой-либо список значений запроса списком
клиентов и использовать его в запросе:
// Всякими
правдами/неправдами заполнили список значений // (хотя
бы через другой запрос :) // конкретными клиентами //
Предположим, индекс массива равен "2". Тогда в тексте //
запроса появится следующее: | Условие (Клиенты в
СписокЗначенийЗапроса[2]); |
Что осталось за бортом
Перенос реквизитов неограниченной длины, работа с
периодическими реквизитами.
|