Андрей Егоров где-то в 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 – ПИШИТЕ. ЕСЛИ ВЫКРОЮ ВРЕМЯ, ТО ПОИЩУ РЕШЕНИЕ. |