Полезные технологии
Все статьи раздела
Основы работы с V7 через OLE.
Андрей Егоров (где-то в 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 ПИШИТЕ.
ЕСЛИ ВЫКРОЮ ВРЕМЯ, ТО ПОИЩУ РЕШЕНИЕ.
|
Партнеры:
Также может быть интересно:
Канал Россия 1 на http://spbtvonline.ru/
|