Примечание - большая часть написанного здесь текста с примерами взята по памяти (пару лет назад изучал достаточно подробно, поэтому может что-то в алгоритмах не работать - я ведь их не копировал откуда-то, а прямо тут же и писал, так что за синтаксические ошибки не пинайте) - на данный момент я активно OLE не пользуюсь (не из-за каких-то проблем с самим OLE, а из-за отсутствия надобности в его использовании в текущий момент).
Основные преимущества, благодаря которым OLE активно используется:
- Для вызывающей базы "по барабану" - какой тип вызываемой базы (DBF или SQL)
- Объектами вызываемой базы можно управлять всеми известными методами работы с объектами в 1С (т.е. со справочниками работают методы ВыбратьЭлементы(), ИспользоватьДату() и т.п., с документами - ВыбратьДокументы() и т.п.), соответственно, можно напрямую решить - стоит отрабатывать конкретные объекты базы OLE или пропустить их.
Пример 1. Присоединение к базе 1С через OLE.
БазаОле=СоздатьОбъект("V77.Application"); // Получаем доступ к OLE объекту 1С |
Локальная версия (на одного пользователя): | V77L.Application |
Сетевая версия: | V77.Application |
Версия SQL: | V77S.Application |
Далее вместо термина "вызываемая база" будет написано просто "база OLE", а вместо термина "вызывающая база" - "местная база"
Теперь, мы должны знать несколько параметров для запуска базы OLE: Каталог базы, имя пользователя и пароль. Ну, наверное, еще и желание запустить 1С в монопольном режиме :)
КаталогБазыОЛе = "C:program files1cv77МояБаза"; ПользовательОле = "Администратор"; ПарольОле = "qwerty"; МонопольныйРежимOLE = " /m"; // для немонопольного запуска указать пустую строку! ЗапускБезЗаставки = 1; // для появления заставки (например, чтобы наблюдать // процесс запуска базы OLE визуально) поставьте здесь "0" РезультатПодключения = БазаОле.Initialize ( БазаОле.RMTrade , "/d" + СокрЛП(КаталогБазыОле) + " /n" + СокрЛП(ПользовательОле)+ " /p" + СокрЛП(ПарольОле) + МонопольныйРежимOLE, ?(ЗапускБезЗаставки = 1,"NO_SPLASH_SHOW","")); Если РезультатПодключения = 0 Тогда Предупреждение("Не удалось подключится к указанной базе - проверьте вводные!"); КонецЕсли; |
Комментарий: функции СокрЛП() стоят в примере на случай, если пользователь захочет указанные выше переменные сделать в форме диалога, а проблема при этом состоит в том, что в алгоритм программа передаст полное значение реквизита (т.е. допишет в конце значения то количество пробелов, которое необходимо для получения полной длины строки (указана в свойствах реквизита диалога)).
Пример 2. Доступ к объектам базы OLE.
Запомните на будущее как непреложный факт:
- Из местной базы в базу OLE (и, соответственно, наоборот) напрямую методом присвоения можно перенести только числовые значения, даты и строки ограниченной длины!!! Т.е. местная база поймет прекрасно без дополнительных алгоритмов преобразования полученного значения только указанные типы значений. Кроме того, под ограничением строк подразумевается проблемы с пониманием в местной базе реквизитов объектов базы OLE типа "Строка неограниченной длины". К этому же еще надо добавить и периодические реквизиты. Естественно, под методом присвоения подразумеваются и попытки сравнить объекты разных баз в одном условии (например, в алгоритмах "Если" или "Пока" и т.п.).
- Есть проблемы при попытке перенести "пустую" дату - OLE может ее конвертировать, например, в 31.12.1899 года и т.п. Поэтому вам лучше заранее выяснить те значения, которые могут появится в местной базе при переносе "пустых" дат, чтобы предусмотреть условия преобразования их в местной базе.
A) Доступ к константам базы OLE:
ЗначениеКонстантыOLE = БазаОле.Константа.ДатаЗапретаРедактирования; |
Б) Доступ к справочникам и документам базы OLE (через функцию "CreateObject"):
СпрOLE = БазаОле.CreateObject("Справочник.Фирмы"); // "СоздатьОбъект" в OLE не работает! ДокOLE = БазаОле.CreateObject("Документ.РасходнаяНакладная"); |
После создания объекта справочника или документа к ним применимы все методы, касающиеся таких объектов в 1С:
СпрОле.ВыбратьЭлементы(); Пока СпрОле.ПолучитьЭлемент()=1 Цикл Сообщить(Спр.Наименование); КонецЦикла; |
Заметьте, что если вместо "Сообщить(Спр.Наименование)" вы укажете "Сообщить(Спр.ТекущийЭлемент())", то вместо строкового/числового представления этого элемента программа выдаст вам в окошке сообщение "OLE". Именно это я и имел в виду, когда говорил, что напрямую мало что можно перенести. Т.е. не будут работать следующие методы (ошибки 1С не будет, но и результат работы будет нулевой). Рассмотрим следующий пример:
СпрOLE = БазаОле.CreateObject("Справочник.Фирмы"); // это справочник в базе OLE Док = СоздатьОбъект("Документ.РасходнаяНакладная"); // а это документ в местной базе Док.Новый(); // создаем новый документ в местной базе СпрOLE.НайтиПоКоду(1,0); // спозиционируем в базе OLE // на фирме с кодом "1". Док.Фирма = СпрOLE.ТекущийЭлемент(); // такой метод не сработает, т.к. справа от "=" стоит // объект не местной базы, и местная база 1С его не понимает! |
Однако, сработает следующий метод:
Спр = СоздатьОбъект("Справочник.Фирмы"); // создаем объект справочника местной базы Спр.НайтиПоНаименованию(СпрОле.Наименование,0,0); // Или Спр.найтиПоКоду(СпрОле.Код,0); // т.е. СпрОле.Код и Спр.наименование // являются обычными числовыми/строковыми // значениями, которые понимает местная база! Док.Фирма = Спр.ТекущийЭлемент(); // Вот теперь все в порядке, т.к. с обоих сторон метода // стоят объекты только местной базы! |
Отсюда вывод: возможность доступа к объектам базы 1С через OLE требуется, в основном, только для определенной задачи - получить доступ к реквизитам определенного элемента справочника или документа. Однако, не забываем, что объекты базы OLE поддерживают все методы работы с ними, в т.ч. и "Новый()", т.е. приведем пример противоположный предыдущему:
ДокОле = CreateObject("Документ.РасходнаяНакладная"); // Создаем документ в базе OLE ДокОле.Новый(); Спр = СоздатьОбъект("Справочник.Фирмы"); // В местной базе получаем доступ к справочнику Спр.НайтиПоКоду(1,0); // Находим в местной базе фирму с кодом 1 (если есть) ДокОле.Фирма = Спр.ТекущийЭлемент(); // такой метод не сработает |
Однако, сработает следующий метод:
СпрОле = БазаОле.CreateObject("Справочник.Фирмы"); // создаем объект справочника базы 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!
Как же я про это забыл-то, а? Поскольку при запуске базы автоматически компилируется глобальный модуль, то нам становятся доступны функции и процедуры глобального модуля (поправлюсь - только те, у которых стоит признак "Экспорт"). Плюс к ним еще и различные системные функции 1С. А доступны они нам через функцию 1С OLE - EvalExpr(). Приведем примеры работы с базой OLE:
ДатаАктуальностиОле = БазаОле.EvalExpr("ПолучитьДатуТА()"); // Возвращает дату актуальности ИмяПользователяОле = БазаОле.EvalExpr("ИмяПользователя()"); // возвращает строку // // попробуем теперь получить числовое значение НДС у элемента номенклатуры // через функцию глобального модуля ПроцентНДС(СтавкаНДС) Экспорт! ТовОле = БазаОле.CreateObject("Справочник.Номенклатура"); ТовОле.ВыбратьЭлементы(); // Найдем элемент справочника (не группа!) Пока ТовОле.ПолучитьЭлемент()=1 Цикл Если ТовОле.ЭтоГруппа()=0 Тогда Прервать; КонецЕсли; КонецЦикла; ЧисловоеЗначениеПроцентаНДСТовараОле = БазаОле.EvalExpr("ПроцентНДС(Перечисление.ЗначенияНДС." + ТовОле.СтавкаНДС.Идентификатор()+")"); |
На самом деле, в последней строке примера я исхитрился и забежал немного вперед. Дело в том, что как и запрос (см. отдельную главу), так и EvalExpr() выполняются внутри базы OLE, причем команды передавается им обычной строкой, и поэтому надо долго думать, как передать необходимые ссылки на объекты базы OLE в строке текста местной базы. Так что, всегда есть возможность поломать голову над этим…
Алгоритмы преобразования объектов в "удобоваримый вид" между базами.
Ясно, что алгоритмы преобразования нужны не только для переноса объектов между и базами, но и для такой простой задачи, как попытки сравнить их между собой.
И еще раз обращу внимание: ОБЪЕКТЫ ОДНОЙ БАЗЫ ПРЕКРАСНО ПОНИМАЮТ ДРУГ ДРУГА, ПРОБЛЕМЫ ВОЗНИКАЮТ ТОЛЬКО ТОГДА, КОГДА ВЫ НАЧИНАЕТЕ СВЯЗЫВАТЬ МЕЖДУ СОБОЙ ОБЪЕКТЫ РАЗНЫХ БАЗ, т.е. команда
ДокОле.Фирма=СпрОле.ТекущийЭлемент();
// где ДокОле - документ базы OLE, а СпрОле - справочник "Фирмы" базы OLE
будет прекрасно работать без ошибок. Не забывайте это, чтобы не перемудрить с алгоритмами!
Итак, повторяюсь, что напрямую перенести, да и просто сравнить можно только даты (причем не "пустые"), числа и строки ограниченной длины. Итак, как же нам сравнить объекты разных баз (не числа, не даты, не строки), т.е. как их преобразовать в эту самую строку/число/дату.
А) Преобразование справочников/документов базы OLE (если есть аналогичные справочники/документы в местной базе). В принципе, преобразование их было уже рассмотрено в примерах выше и сводится к поиску их аналогов в местной базе. Могу еще раз привести пример, заодно с использованием регистров:
// ВыбФирма, НачДата, КонДата, ВыбДокумент - реквизиты диалога в местной базе // причем они все указаны, т.е. не пустые (чтобы не делать лишних команд в примере) ДокОле = БазаОле.CreateObject("Документ.РасходнаяНакладная"); // объект базы OLE Док = СоздатьОбъект("Документ.РасходнаяНакладная"); // а это - его аналог в местной базе Спр = СоздатьОбъект("Справочник.Фирмы"); // а это - местный справочник фирм ДокОле.ВыбратьДокументы(НачДата,КонДата); Пока ДокОле.ПолучитьДокумент()=1 Цикл // Ищем в местном справочнике фирм аналог фирмы из базы OLE (по коду)… Если Спр.НайтиПоКоду(ДокОле.Фирма.Код,1)=0 Тогда Сообщить("Найден документ с неизвестной фирмой! Ее код: " + ДокОле.Фирма.Код); // На самом деле, она может быть просто не указана :)) Если ДокОле.Фирма.Выбран()=0 Тогда Сообщить("Извините - она просто не указана! :))"); КонецЕсли; ИначеЕсли Спр.ТекущийЭлемент()=ВыбФирма Тогда Сообщить("Найден документ указанной вами фирмы! № "+ДокОле.НомерДок+" от "+ДокОле.ДатаДок); КонецЕсли; // Ищем аналог документа в местной базе Док.НайтиПоНомеру(ДокОле.НомерДок,ДокОле.ДатаДок); Если Док.Выбран()=1 Тогда Сообщить("Документ № "+Док.НомерДок + " от " + Док.ДатаДок + " уже есть!"); Если Док.ТекущийДокумент() = ВыбДокумент Тогда Предупреждение("Приз в студию! Найден указанный вами документ!!!"); КонецЕсли; Иначе Сообщить("Документ № "+ДокОле.НомерДок+" от "+ДокОле.ДатаДок+" не найден в базе!"); КонецЕсли; КонецЦикла; // А заодно и получим остаток товара в базе OLE: РегОле = БазаОле.CreateObject("Регистр.ОстаткиТоваров"); ТовОле = БазаОле.CreateObject("Справочник.Номенклатура"); ТовОле.НайтиПоКоду(ВыбТовар.Код,0); ФрмОле = БазаОле.CreateObject("Справочник.Фирмы"); ФрмОле.НайтиПоКоду(ВыбФирма.Код,0); ОстатокТовараНаСкладеОле = РегОле.СводныйОстаток(ТовОле.ТекущийЭлемент(),, ФрмОле.ТекущийЭлемент(),"ОстатокТовара"); |
Б) Преобразование перечислений и видов субконто (подразумевается, что в обоих базах есть аналогичные перечисления и виды субконто). Вся задача сводится к получению строкового или числового представления перечисления или вида субконто.
Не поймите это как прямую команду воспользоваться функцией Строка() или Число() :)) Нет. Для этого у нас есть обращение к уникальному представлению перечисления и вида субконто - метод Идентификатор() или ЗначениеПоНомеру(). Второй вариант не очень подходит, так как зачастую в разных базах даже перечисления бывают расположены в другом порядке, а вот идентификаторы стараются держать одинаковыми в разных базах. Отсюда вывод, пользуйтесь методом Идентификатор(). Кстати, не путайте вид субконто с самим субконто! Привожу пример преобразования:
// ДокОле - документ базы OLE, и уже спозиционирован на нужном нам документе, // Док - документ местной базы (например, новый), который мы пытаемся заполнить // реквизитами из документа базы OLE… // Будем считать, что реквизиты типа "справочник" мы уже перенесли :) // Преобразуем перечисление - и не говорите потом - с ума сойти, как оказалось все просто! Если ПустоеЗначение(ДокОле.ПризнакНакладной.Идентификатор())=0 Тогда // если не проверим реквизит на пустое значение - нарвемся на ошибку 1С Док.ПризнакНакладной = Перечисление.ПризнРасхНакл.ЗначениеПоИдентификатору( ДокОле.ПризнакНакладной.Идентификатор()); // Что и требовалось сделать КонецЕсли; // Преобразуем вид субконто Если ПустоеЗначение(ДокОле.ВидСубконтоСчетаЗатрат.Идентификатор())=0 Тогда // если не проверим реквизит на пустое значение - нарвемся на ошибку 1С Док.ВидСубконтоСчетаЗатрат = ВидСубконто.ЗначениеПоИдентификатору( ДокОле.ВидСубконтоСчетаЗатрат.Идентификатор()); // Что и требовалось сделать КонецЕсли; |
То же самое относится и к плану счетов - принцип у него тот же, что и у вида субконто…
В) Преобразование счетов:
Во многом объект "Счет" аналогичен объекту "Справочник". Отсюда и пляшем:
Док.СчетУчета=СчетПоКоду(ДокОле.СчетУчета.Код); // присвоили документу реквизит счета Если СчетПоКоду(ДокОле.СчетУчета.Код)=СчетПоКоду("10.5") Тогда // Это именно счет 10.5 :) КонецЕсли; // Я специально оставил в "Если" функцию СчетПоКоду(), т.к. в планах счетов разных баз // могут быть разные форматы счетов, и просто сравнение кода может привести к // противоположному результату. (Кстати, и сам СчетПоКоду() тоже может иногда подвести, // так что, вам решать - каким методом пользоваться…) |
Работа с запросами и 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 Тогда ТекстЗапроса = ТекстЗапроса + " | Условие (КодРодителя = " +ВыбТовар.Код+");"; КонецЕсли; // В качестве домашнего задания - переписать условие по наименоваиню :))) |
А если справочник очень даже многоуровневый? Вот для этого мы и используем написанную ранее функцию. Предположим, что список значений запроса с индексом массива " 1 " мы будем использовать для хранения подобных значений (например, хранить в нем группы товаров, клиентов) для хитрых условий. Итак, например, в ВыбТовар у нас указана группа товаров, а в ВыбКлиент - группа клиентов, которым мы товары группы ВыбТовар продавали. Кроме того, мы должны пропустить накладные возвратов поставщикам, и не забыть, что товары надо еще отбирать по флажку ТолькоЗамерзающийЗимойТовар:
// Очистим список значений запроса Результат = БазаОле.EvalExpr("СкорректироватьСписок(1,1)"); // Закинем в список значений запроса группу товаров (он сам найдет ее в базе OLE) // И запоминаем (в уме), что этой группе соответствует 1-е значение списка Результат = БазаОле.EvalExpr("СкорректироватьСписок(1, 2 , ""Справочник"", """ + Выбтовар.Вид())+ """," + ВыбТовар.Код + ", """ + ВыбТовар.Наименование + """)"); // Теперь закинем в список значений запроса группу клиентов // И запоминаем, что этой группе соответствует 2-е значение списка Результат = БазаОле.EvalExpr("СкорректироватьСписок(1, 2 , ""Справочник"", """ + ВыбКлиент.Вид())+ """," + ВыбКлиент.Код + ", """ + ВыбКлиент.Наименование + """)"); // А еще до кучи и фирму из ВыбФирма // И запоминаем, что этой фирме соответствует 3-е значение списка Результат = БазаОле.EvalExpr("СкорректироватьСписок(1, 2 , ""Справочник"", """ + ВыбФирма.Вид())+ """," + ВыбФирма.Код + ", """ + ВыбФирма.Наименование + """)"); // Теперь формируем текст запроса ТекстЗапроса = " Период с '"+НачДата+ "' по '"+КонДата+"'; | Товар = Документ.РасходнаяНакладная.Товар; | Замерзает = Документ.РасходнаяНакладная.Товар.ЗамерзаетЗимой; | Признак = Документ.РасходнаяНакладная.ПризнакНакладной; | Фирма = Документ.РасходнаяНакладная.Фирма; | Клиент = Документ.РасходнаяНакладная.Клиент; | Количество = Документ.РасходнаяНакладная.Количество; | СуммаДок = Документ.РасходнаяНакладная.Сумма; | Группировка Товар без групп; | Группировка Документ; | Функция СуммаОтгрузки=Сумма(СуммаДок); | Условие (Признак<>Перечисление.ПризнРасхНакл.ВозвратПоставщику); | Условие (Замерзает = " + ТолькоЗамерзающийЗимойТовар + "); // Внимание! Начинается: | Условие (Товар.ПринадлежитГруппе(СписокЗначенийЗапроса[1].ПолучитьЗначение(1))=1); | Условие (Клиент.ПринадлежитГруппе(СписокЗначенийЗапроса[1].ПолучитьЗначение(2))=1); | Условие (Фирма= СписокЗначенийЗапроса[1].ПолучитьЗначение(3));"; |
Уфф. Вроде все… Остается только запустить запрос:
Запрос = БазаОле.CreateObject("Запрос"); Если Запрос.Выполнить(ТекстЗапроса)=0 Тогда Предупреждение("Запрос безутешен!"); Возврат; КонецЕсли; |
Ну, а с реквизитами запроса разбираемся так же, как указано было выше в предыдущих разделах… И не забываем, что кроме хранения конкретных значений, можно использовать другие списки значений запроса. Например, можно заполнить какой-либо список значений запроса списком клиентов и использовать его в запросе:
// Всякими правдами/неправдами заполнили список значений (хотя бы через другой запрос :)) // конкретными клиентами… // Предположим, индекс массива равен "2". Тогда в тексте запроса появится следующее: | Условие (Клиенты в СписокЗначенийЗапроса[2]); |
P.S. Чего еще нет: перенос реквизитов неограниченной длины, более подробно остановиться на "периодических реквихитах"…
Автор - Руководитель информационно-технического отдела ООО МФФ "Аконит" Егоров Андрей Викторович
Комментарии
1Статейка полезная, но я не понял малясь про обращение к массиву как к списку значений...По крайней мере у меня не прокатывает конструкция
СписокЗначенийЗапроса[ИндексМассива].ДобавитьЗначение(Спр.ТекущийЭлемент()); Ругается, что нет такого ДобавитьЗначение у массива....