Взаимодействие через OLE

Примечание - большая часть написанного здесь текста с примерами взята по памяти (пару лет назад изучал достаточно подробно, поэтому может что-то в алгоритмах не работать - я ведь их не копировал откуда-то, а прямо тут же и писал, так что за синтаксические ошибки не пинайте) - на данный момент я активно OLE не пользуюсь (не из-за каких-то проблем с самим OLE, а из-за отсутствия надобности в его использовании в текущий момент).

Примечание - большая часть написанного здесь текста с примерами взята по памяти (пару лет назад изучал достаточно подробно, поэтому может что-то в алгоритмах не работать - я ведь их не копировал откуда-то, а прямо тут же и писал, так что за синтаксические ошибки не пинайте) - на данный момент я активно 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.

Запомните на будущее как непреложный факт:

  1. Из местной базы в базу OLE (и, соответственно, наоборот) напрямую методом присвоения можно перенести только числовые значения, даты и строки ограниченной длины!!! Т.е. местная база поймет прекрасно без дополнительных алгоритмов преобразования полученного значения только указанные типы значений. Кроме того, под ограничением строк подразумевается проблемы с пониманием в местной базе реквизитов объектов базы OLE типа "Строка неограниченной длины". К этому же еще надо добавить и периодические реквизиты. Естественно, под методом присвоения подразумеваются и попытки сравнить объекты разных баз в одном условии (например, в алгоритмах "Если" или "Пока" и т.п.).
  2. Есть проблемы при попытке перенести "пустую" дату - 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-базы, чтобы как-то собрать для запросы переменные!

  1. Поскольку сам текст запроса и функции EvalExpr() является по сути текстом, а не набором параметров, то напрямую передать ему ссылку на элемент справочника, документ, счет и т.п. нельзя. Исключение может быть составлено для конкретных значений перечислений, видов субконто, констант, планов счетов и т.п.
  2. Хоть и многим и так понятно, что я скажу дальше, но я все-таки уточню: при описании переменных в тексте запроса не забывайте, что объекты базы надо указывать напрямую, без всяких префиксов типа "БазаОле".
  3. Отрабатывать запрос сложно тем, что ошибки, например, при компиляции напрямую не увидеть. Поэтому начинаем пошагово готовится к отработке запроса в базе 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
  • noveng

    Статейка полезная, но я не понял малясь про обращение к массиву как к списку значений...По крайней мере у меня не прокатывает конструкция
    СписокЗначенийЗапроса[ИндексМассива].ДобавитьЗначение(Спр.ТекущийЭлемент()); Ругается, что нет такого ДобавитьЗначение у массива....

Уголовное дело Елены Блиновской передали в суд

Блогер спрятала от налоговой 906 млн рублей, из них 716 млн вывела через две подконтрольные фирмы. Следователи допросили больше 200 свидетелей и провели свыше 20 обысков.

Бесплатно с Госзакупки

Ведение казначейского счета: начало работы, требования к предоставляемым подтверждающим документам

Казначейский счет — это специальный 71 счет, который открывается для учета бюджетных средств в рамках казначейского сопровождения.

Ведение казначейского счета: начало работы, требования к предоставляемым подтверждающим документам

Брокер ВТБ запустил голосовые рекомендации для инвесторов

Голосовые советы позволят инвесторам быстрее принимать решения о проведении наиболее выгодных сделок.

Курсы повышения
квалификации

18
Официальное удостоверение с занесением в госреестр Рособрнадзора
Расчетный счет

Расчетно-кассовое обслуживание для ИП: как найти свой идеальный тариф

Открытие расчетного счета — один из первых шагов для любого индивидуального предпринимателя. С помощью РКО ИП ведет безналичные расчеты, принимает оплату от клиентов на счет, переводит средства контрагентам и бюджету. Банки предлагают разные тарифы на расчетно-кассовое обслуживание, которые отличаются по стоимости и условиям. 

Расчетно-кассовое обслуживание для ИП: как найти свой идеальный тариф

15 курсов бренд-менеджера для начинающих 

Бренд-менеджер — это специалист в области маркетинга, который занимается продвижением бренда. Его задача: сформировать мнение у покупателей по продукту, услуге или самой компании, склонить их к приобретению. На всех этапах он должен следить за репутацией компании и торговой марки.

15 курсов бренд-менеджера для начинающих 
ПСН

Как совместить патент с УСН и что делать, если ИП превысил лимиты

Индивидуальный предприниматель теряет право на применение патентной системы налогообложения, когда его совокупный доход превышает 60 млн рублей. В таком случае нужно применять только УСН и пересчитать весь налог.

Опытом делятся эксперты-практики, без воды
ЭДО

Хранение документов. На бумаге или в электронном виде? Затраты и риски

Документооборот — это настоящее и прошлое любой организации. В данном материале рассмотрим варианты форм хранения документа, сравним, какие затраты на это потребуются, обозначим преимущества того или иного метода хранения и возможные риски.

Хранение документов. На бумаге или в электронном виде? Затраты и риски
Право

Как подписать соглашение NDA с самозанятым

Для нормальной работы самозанятому нужен доступ к конфиденциальной информации. Но плательщик НПД — не штатный работник, он не подписывает ЛНА компании и на него нельзя повлиять дисциплинарными мерами ответственности. Проблему может решить соглашение о конфиденциальности (NDA). Рассказываем, как его правильно оформить.

Как подписать соглашение NDA с самозанятым
Охрана труда

Новый порядок оказания первой помощи с 1 сентября 2024 года. Что нужно знать работодателю

С 1 сентября 2024 года вступает в силу новый порядок оказания первой помощи.

Новый порядок оказания первой помощи с 1 сентября 2024 года. Что нужно знать работодателю

Жителям ЕС запретили участвовать в обмене заблокированными акциями

Власти Евросоюза призвали инвесторов и компании не участвовать в обмене заблокированных ценных бумаг, поскольку в этом механизме участвует подсанкционный Национальный расчетный депозитарий.

НДС на УСН, выбор СНО, топ претензий от ФНС, и господдержка бизнеса — какие вебинары пройдут в августе 2024 года

Подготовили для вас анонс предстоящих вебинаров в августе.

НДС на УСН, выбор СНО, топ претензий от ФНС, и господдержка бизнеса — какие вебинары пройдут в августе 2024 года
УСН

В законе о налоговой реформе-2025 в части УСН есть спорные моменты

Федеральный закон от 12.07.2024 № 176-ФЗ о совершенствовании налоговой системы России с 2025 года содержит много изменений и новшеств по УСН. Уже сейчас возникают вопросы, как их применять и трактовать на практике.

Куда не движется движимость

Рассуждение на одну из вечных тем в налоговом праве: переквалификация налоговыми органами движимого имущества в недвижимое.

Куда не движется движимость

Что такое амортизация облигаций и зачем инвестору про нее знать

Облигация — это долговая ценная бумага, по которой эмитент (тот, кто выпускает такую ценную бумагу) получает денежные средства в долг от инвестора. Взамен эмитент гарантирует, что выплатит инвестору в определенный срок стоимость облигации (номинал) и процент за пользование средствами (купонный доход) в будущем. Величина прибыли, которую инвестор получает в результате вложений в облигации, называется доходностью облигаций.

Что такое амортизация облигаций и зачем инвестору про нее знать
ПСН

Отказ от патента не отменить, как считать лимит для ПСН, особенности учета при переходе с УСН: что нужно знать бухгалтеру и ИП

ПСН на первый взгляд простой режим, но есть много нюансов. Разбираем три ситуации, в которых путаются бухгалтеры и ИП.

Иллюстрация: Вера Ревина/Клерк.ру

Прямые и косвенные расходы: пересмотрите их перечень, чтобы законно экономить на налоге на прибыль

Проверьте, верно ли вы классифицировали расходы на прямые и косвенные. Основная сложность в том, что Налоговый кодекс позволяет самостоятельно определять их перечень. Причем неверное распределение, а также некорректные положения учетной политики, могут привести к многомиллионным доначислениям. В статье рассказали, на что обратить внимание при распределении затрат с учетом актуальной судебной практики.

Прямые и косвенные расходы: пересмотрите их перечень, чтобы законно экономить на налоге на прибыль

На Новый год-2025 каникулы продлятся 11 дней

Вышел проект постановления Минтруда о праздничных днях в 2025 году. На майских россияне будут отдыхать восемь дней, а в Новый год — 11 дней.

ФСБУ

Поступление аренды в 1С отражают как услугу или услугу аренды: от чего зависит

Заполнение в 1С поступления предметов аренды как просто услуг или услуг аренды зависит от статуса компании и сдачи их в субаренду.

ОСАГО

На маркетплейсе Сбера водители купили 534 тысячи полисов ОСАГО

Водители могут застраховать машину онлайн за несколько минут. Достаточно ввести данные документов и выбрать страховщика на маркетплейсе.

Интересные материалы

Общество

Для автопутешественников подготовили 50 маршрутов

К 2030 году число туристических поездок на автомобилях вырастет с 26 млн до 50 млн.