Руководитель информационно-технического
отдела ООО МФФ "Аконит"
Егоров Андрей Викторович
Примечание - большая часть написанного здесь текста
с примерами взята по памяти (пару лет назад изучал достаточно подробно,
поэтому может что-то в алгоритмах не работать - я ведь их не копировал
откуда-то, а прямо тут же и писал, так что за синтаксические ошибки
не пинайте) - на данный момент я активно 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 files\1cv77\МояБаза\";
ПользовательОле = "Администратор";
ПарольОле = "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С, БОЛЬШУЮ ЧАСТЬ - ПО ПАМЯТИ.
ЕСЛИ ОБНАРУЖИТЕ ОШИБКИ, ДА И ВООБЩЕ, ЕСЛИ ЕСТЬ ВОПРОСЫ ПО OLE -
ПИШИТЕ. ЕСЛИ ВЫКРОЮ ВРЕМЯ, ТО ПОИЩУ РЕШЕНИЕ…
С наилучшими пожеланиями,
Руководитель информационно-технического отдела ООО МФФ "Аконит"
Егоров Андрей Викторович
=========
При подготовке HTML-кода текстов программ использован MD Text Editor
ї 2001 Белов Сергей Васильевич
(Soaron)
|