Смекни!
smekni.com

Взаимодействие через OLE (стр. 2 из 3)

Как же я про это забыл-то, а? Поскольку при запуске базы автоматически компилируется глобальный модуль, то нам становятся доступны функции и процедуры глобального модуля (поправлюсь - только те, у которых стоит признак "Экспорт"). Плюс к ним еще и различные системные функции 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 Цикл СписокЗначенийЗапроса[Сч]=СоздатьОбъект("СписокЗначений"); КонецЦикла; КонецПроцедуры //**************************************************************

Теперь начинаем потихоньку писать сам запрос. Что мы имеем: