Андрей
Егоров где-то
в 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 несколько строк,
которые нам помогут в работе:
Перем СписокЗначенийЗапроса[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]);
|
Что осталось за бортом
Перенос реквизитов неограниченной длины, работа с периодическими
реквизитами.
ГРОМАДНЕЙШИЕ ИЗВИНЕНИЯ ЗА
ВОЗМОЖНЫЕ СИНТАКСИЧЕСКИЕ И ОРФОГРАФИЧЕСКИЕ ОШИБКИ В ДОКУМЕНТЕ
ПИСАЛ БЕЗ ПРОВЕРКИ В V7, БОЛЬШУЮ ЧАСТЬ ПО ПАМЯТИ.
ЕСЛИ ОБНАРУЖИТЕ ОШИБКИ,
ДА И ВООБЩЕ, ЕСЛИ ЕСТЬ ВОПРОСЫ ПО OLE ПИШИТЕ.
ЕСЛИ ВЫКРОЮ ВРЕМЯ, ТО ПОИЩУ РЕШЕНИЕ.
|