Есть обработка в составе конфигурации, предполагается вызывать ее из другой обработки и в зависимости от прав пользователя открывать форму или просто без открытия формы выполнять какие то процедуры и закрывать обработку с возвратом данных в вызываемую обработку. С открытием формы все понятно и все работает, без открытия формы тоже работает, только данные не возвращает, а хотелось бы. Кусок кода открываемой обработки:

```1c
    &НаКлиенте
    Процедура ПриОткрытии(Отказ)
    Если ВыполнитьСразу Тогда
    СменаИлиПериодВыборки = 0;
    МассивИмен = Новый Массив;
    .......
    СтрокаОшибок = ";
    Если НЕ ЗагрузитьТЗИзПП(МассивИмен,СтрокаОшибок) Тогда
    //Отказ = Истина;
    //ЗакрытьСПередачейРезультата(СтрокаОшибок)
    Сообщить(СтрокаОшибок);
    КонецЕсли;
    КонецЕсли;
    УправлениеВидимостьюДоступностью();
    КонецПроцедуры

    &НаКлиенте
    Процедура ЗакрытьСПередачейРезультата(СтрокаОшибок)

    СтруктураОтвета = Новый Структура;
    СтруктураОтвета.Вставить("СтрокаОшибок", СтрокаОшибок);
    СтруктураОтвета.Вставить("ЕстьОшибки", Истина);
    Закрыть(СтруктураОтвета);

    КонецПроцедуры
```

Закомментированные строки не работают как хотелось бы - при закрытии не заходит в обработку оповещения при закрытии и не передает параметры. Как быть??
---
Идея кривая с самого начала.
Я бы сделал так:
1) перенёс всю обработку данных из модуля формы в модуль менеджера и объявил бы её экспортной функцией. Из формы вызывал бы функцию в модуле менеджера.
2) Открытие формы - отдельно. Выполнение обработки - отдельно. Выполнение обработки вызывать явным образом - из менеджера. Форма вызывается только тогда, когда надо именно посмотреть форму (изменить настройки?).

Кстати, если всё правильно сделать, можно было бы и рег.задание повесить, и вообще не запускать обработку вручную - только ради настроек.
---
сделать функцию в модуле обработки, которая будет выполнять нужные действия и возвращать результат.
Модуль менеджера это реинкарнация глобального модуля 7.7, 1с разбила его на сотни общих модулей и выносит в них большую часть функционала.В принципе можно и так, все перенести в глобальный модуль и делать там. Тогда не очень понятно, зачем вообще обработка.
---
Поясню...Хотелось бы следующего - возможность запускать обработку как в автомате(под пользователем) так и в обычном режиме с выводом формы на экран и выбором различных параметров (под админом). Обработка делает следующее...подключается к сторонней базе на скл, загружает ее в ТЧ, затем в зависимости от загруженных данных создает документы...Поэтому решил использовать обработку
(5)
смотри внимательно (0)
Какая разница между моей процедурой ЗакрытьСПередачейРезультата и твой СделатьИЗакрыть??
---
Какая разница между моей процедурой ЗакрытьСПередачейРезультата и твой СделатьИЗакрыть??
Разница в том, что СделатьИЗакрыть будет вызываться после открытия и вернет корректные данные.
---
а где ее вызывать?
---
вотже:
```1c
    Процедура ПриОткрытии()
    Если НеОткрыватьФорму Тогда
    ПодключитьОбработчикОжидания("СделатьИЗакрыть", 0.1, Истина)
    КонецЕсли
    КонецПроцедуры
```
---
Идея кривая с самого начала.
Я бы сделал так:
1) перенёс всю обработку данных из модуля формы в модуль менеджера и объявил бы её экспортной функцией. Из формы вызывал бы функцию в модуле менеджера.
2) Открытие формы - отдельно. Выполнение обработки - отдельно. Выполнение обработки вызывать явным образом - из менеджера. Форма вызывается только тогда, когда надо именно посмотреть форму (изменить настройки?).

Кстати, если всё правильно сделать, можно было бы и рег.задание повесить, и вообще не запускать обработку вручную - только ради настроек.
---
По поводу кривизны идеи: менеджеры, объекты и регзадания можно рассматривать, только если обработка может обрабатываться на сервере. Но часто такое все нужно делать именно на клиенте. И если можно обойтись без серверных вызовов, то почему бы не обойтись?
---
Обработка данных на клиенте....
Дальше можете не продолжать.
---
ну в данном случае ТС в конце концов проговорился:
Обработка делает следующее...подключается к сторонней базе на скл
но также можно представить и загрузку данных из файла, который доступен только на клиенте, и получение данных из интернета (который на сервере зарезан), и прочая и прочая... так что не надо скоропалительных выводов.
---
обработка подключается к сторонней базе на SQL... ничего не напомнило? Тем более, что по коду видно, что обработка именно вставляет данные в базу, а возвращает только результат - были ошибки или нет. В этом случае явно надо переносить на сервер. Тем более, держу пари, что у ЗагрузитьТЗИзПП стоит директива &НаСервере!
Другими словами: оно ВСЁ РАВНО выполняется на сервере, но при этом реализовано через... ммм.... неверные вызовы.
---
обработка подключается к сторонней базе на SQL... ничего не напомнило? Нет. А что должно было напомнить?
В общем, коллега, у нас телепаторы немного по разному настроены. Ваш какой то уж больно... категоричный.
---
Клиент НЕ МОЖЕТ соединяться с SQL. Это только сервер.
Клиент НЕ МОЖЕТ записать данные в базу. Это только сервер.

В форме так или иначе ВЫЗЫВАЕТСЯ сервер. Только правильно при его вызове оставить только заглушку, вызывающую функцию в модуле менеджера. Тогда и данными можно будет управлять не только из формы, и рег.задание настроить, да и не нагружать клиента несвойственными ему функциями.

И почему я объясняю Вам такие азбучные истины?
---
Клиент НЕ МОЖЕТ соединяться с SQL.
Ой-ли?

```1c
    бд = новый comobject("adodb.connection");
    бд.Оpen(...) ;
    выборка = бд.Execute("select...");
```

Что доКлиент НЕ МОЖЕТ записать данные в базу.так у ТС из формы нужно просто вернуть некие данные в вызывающую форму, нет?
---
Ой-ли? SQL через ActiveX/COM... Всё. Finita la comedia.
так у ТС из формы нужно просто вернуть некие данные в вызывающую форму, нет?

Кто-то тут предлагал читать то, что писал автор вопроса, не Вы? :-)

Обработка возвращает ТОЛЬКО статус. Булево. Ну, и описание ошибки. Всё.
Форма ОБЯЗАНА вызывать сервер. Так что, хватит Вам флудить.
---
простите, а вы под SQL что понимаете?
---
хватит флудить.
---
(20) Чуток отвлекся на работу, а тут такая дискуссия. Всем спасибо, кажется решение найдено - в (10), только как всегда много вопросов - все же модуль менеджера? или может быть модуль объекта?
(19) SQL через ActiveX/COM... Всё. Finita la comedia.
не через внешние ли источники данных предлагаете?
---
все же модуль менеджера? или может быть модуль объекта?
В принципе, для 1С большой разницы нет, IMHO (сам не замерял). Но теоретически, правильнее именно в модуле менеджера.
Так делают в самой 1С, так же делается во многих языках программирования. В других языках это связано с тем, что создание объекта - это тоже выделение ресурсов... А для 1С это, кажется, просто для удобства - чтобы код был короче.
---
Думаю в моем случае придется все же создавать объект. Там заполняется ТЧ, При заполнении идет анализ на наличие ошибок, если ошибки есть - не продолжаем, надо под админом открывать форму и исправлять в ней ошибки, если ошибок нет - создаем нужные документы на основании заполненной ТЧ....
---
Ну, правильнее было бы анализировать Таблицу значений (получать в виде параметра) и возвращать ошибку, если что не так. А в коде - модифицировать (хотя бы и заполнять!) ту самую таблицу значений. А в форме потом делать ТабличнаяЧасть.Загрузить(ТаблицаЗначений);
---
почему правильнее? Потому что это даст возможность вставить всё в рег.задание и если есть ошибки, просто игнорировать и ничего не делать. Зато в форме тот же код будет заполнять ТЧ, а потом ещё и об ошибках сообщать.
---
ну коль пошла такая пьянка....

```1c
    Функция ЗагрузитьТЗИзПП(СменаИлиПериодВыборки,МассивИмен,СтрокаОшибки) Экспорт

    Connection = Новый COMОбъект("ADODB.Connection");
    Connection.Open(СтрокаПодключения());

    RS = Новый COMОбъект("ADODB.Recordset");
    RS.ActiveConnection = Connection;

    Если СменаИлиПериодВыборки = 0 Тогда
    QueryText = "SEL ECT
    | Shift.DateTimeOfBegin AS Начало_смены
    | ,Shift.DateTimeOfEnd AS Конец_смены
    | ,ClientName.Name AS Клиент
    | ,ClientName.kClient AS КодКлиент
    | ,Product.name AS Продукт
    | ,Product.kprCard AS КодПродукт
    | ,TRS.price AS Цена
    | ,SUM(TRS.kolCard)AS Карта
    | ,SUM(TRS.kolTRK)AS ТРК
    |FROM gas.dbo.CrdTransActRegister as TRS
    | LEFT JOIN gas.dbo.CrdClients ClientName ON TRS.kClient = ClientName.kClient
    | LEFT JOIN gas.dbo.products Product ON TRS.kPr = Product.kprCard
    | LEFT JOIN (SELECT TOP 1 DateTimeOfBegin
    | ,DateTimeOfEnd
    | ,ShiftNumber
    | FR OM gas.dbo.Shifts
    | ORDER BY DateTimeOfBegin DESC) Shift ON 1=1
    |WHERE [Tcard]=11
    | AND [kodTransAct]=1
    | AND TRS.DateAct >= Shift.DateTimeOfBegin
    |GROUP BY Shift.DateTimeOfBegin, Shift.DateTimeOfEnd, ClientName.Name, ClientName.kClient, Product.name, Product.kprCard, TRS.price";
    Иначе
    QueryText = "SELECT
    | Shift.DateTimeOfBegin AS Начало_смены
    | ,Shift.DateTimeOfEnd AS Конец_смены
    | ,ClientName.Name AS Клиент
    | ,ClientName.kClient AS КодКлиент
    | ,Product.name AS Продукт
    | ,Product.kprCard AS КодПродукт
    | ,TRS.price AS Цена
    | ,SUM(TRS.kolCard) AS Карта
    | ,SUM(TRS.kolTRK) AS ТРК
    |FROM gas.dbo.CrdTransActRegister AS TRS
    |LEFT JOIN gas.dbo.Shifts Shift ON (TRS.DateAct >= Shift.DateTimeOfBegin AND TRS.DateAct <= ISNULL(Shift.DateTimeOfEnd,'26660306 23:59:59'))
    |LEFT JOIN gas.dbo.CrdClients ClientName ON TRS.kClient = ClientName.kClient
    |LEFT JOIN gas.dbo.products Product ON TRS.kPr = Product.kprCard
    |WHERE TRS.Tcard=11
    | AND TRS.kodTransAct=1
    | AND (Shift.DateTimeOfBegin>='"+Формат(НачалоДня(НачДата),"ДФ='yyyyMMdd HH:mm:ss'")+"')
    | AND (ISNULL(Shift.DateTimeOfEnd,Shift.DateTimeOfBegin)<='"+Формат(КонецДня(КонДата),"ДФ='yyyyMMdd HH:mm:ss'")+"')
    |GROUP BY Shift.DateTimeOfBegin, Shift.DateTimeOfEnd, ClientName.Name, ClientName.kClient, Product.name, Product.kprCard, TRS.price
    |ORDER BY Shift.DateTimeOfBegin";
    КонецЕсли;
    RS.CursorType = 3;
    RS.Open(QueryText);
    ТЗ = Новый ТаблицаЗначений;
    Для Каждого ИмяКолонки из МассивИмен Цикл
    ТЗ.Колонки.Добавить(ИмяКолонки);
    КонецЦикла;
    Пока RS.EOF() = 0 Цикл

    // Можно обращаться и обрабатывать значения полей выборки.
    НоваяСтрока = ТЗ.Добавить();
    Для Сч = 0 по МассивИмен.ВГраница() Цикл
    НоваяСтрока[Сч] = RS.Fields(Сч).Value;
    КонецЦикла;
    //Добавим сумму операции

    RS.MoveNext();
    КонецЦикла;
    тзТрансакции.Загрузить(ТЗ);

    RS.Close();
    Connection.Close();
    // Дозаполним и проверим
    ВсеОК = Истина;
    СтрокаОшибки = ";
    Для Каждого СтрокаТЗ из тзТрансакции Цикл
    СтрокаТЗ.КлиентСсылка = РегистрыСведений.ах_СоответствияКлиенты.Получить(Новый Структура("КодПП", СтрокаТЗ.КодКлиент)).Контрагент;
    Если Не ЗначениеЗаполнено(СтрокаТЗ.КлиентСсылка) Тогда
    ВсеОК = Ложь;
    СтрокаОшибки = СтрокаОшибки + "| Не найдено в соответствии - " + СтрокаТЗ.Клиент;
    КонецЕсли;
    СтрокаТЗ.ВидОперации = РегистрыСведений.ах_СоответствияКлиенты.Получить(Новый Структура("КодПП", СтрокаТЗ.КодКлиент)).ВидОперации;
    СтрокаТЗ.ПродуктСсылка = РегистрыСведений.ах_СоответствияТовары.Получить(Новый Структура("КодПП", СтрокаТЗ.КодПродукт)).Товар;
    Если Не ЗначениеЗаполнено(СтрокаТЗ.ПродуктСсылка) Тогда
    ВсеОК = Ложь;
    СтрокаОшибки = СтрокаОшибки + "| Не найдено в соответствии - " + СтрокаТЗ.Продукт;
    КонецЕсли;
    КонецЦикла;
    Если Не ВсеОК Тогда
    СтрокаОшибки = СтрокаОшибки + "| Обратитесь за помощью к администратору.'";
    КонецЕсли;
    Возврат ВсеОк;
    КонецФункции
```
---
Вот именно! У Вас достаточно, фактически, заменить пару строк кода - и анализироваться будет таблица значений. Только надо будет получать эту таблицу значений в параметре функции.
---
Большое спасибо за замечания и предложения....буду думать и работать в этом направлении. Про внешние источники данных я как то не очень к этому отношусь - изменение конфигурации, тем более что сторонняя база тоже постоянно дорабатывается разработчиками, а у нас РИБ, порядка 20 удаленных узлов, где приходится подключаться, и под админом делать обновления....СОМ соединение в этом плане гибче, поэтому его и выбрал.
---
Я бы изменил заголовок в

```1c
    ЗагрузитьТЗИзПП(СменаИлиПериодВыборки,тзТрансакции,СтрокаОшибки)
```

и потом заменил

```1c
    RS.Open(QueryText);
    ТЗ = Новый ТаблицаЗначений;
    Для Каждого ИмяКолонки из МассивИмен Цикл
    ТЗ.Колонки.Добавить(ИмяКолонки);
    КонецЦикла;
    Пока RS.EOF() = 0 Цикл

    // Можно обращаться и обрабатывать значения полей выборки.
    НоваяСтрока = ТЗ.Добавить();
    Для Сч = 0 по МассивИмен.ВГраница() Цикл
    НоваяСтрока[Сч] = RS.Fields(Сч).Value;
    КонецЦикла;
    //Добавим сумму операции

    RS.MoveNext();
    КонецЦикла;
    тзТрансакции.Загрузить(ТЗ);
```

на примерно так:

```1c
    RS.Open(QueryText);
    Пока RS.EOF() = 0 Цикл

    // Можно обращаться и обрабатывать значения полей выборки.
    НоваяСтрока = тзТрансакции.Добавить();
    Для Каждого КолонкаТаблицы Из тзТрансакции.Колонки Цикл
    НоваяСтрока[КолонкаТаблицы.Имя] = RS.Fields(КолонкаТаблицы).Value;
    КонецЦикла;
    //Добавим сумму операции

    RS.MoveNext();
    КонецЦикла;
```

Ну, это, разумеется, если у Вас имена колонок таблицы совпадает с внешней БД.

Тогда на сервере в модуле формы, при вызове этой функции, когда заполняете МассивИмен, вместо этого создаёте пустую таблицу значений с нужной структурой.

Потом, получив результат, в форме можете его обработать и заполнять табличную часть простым ТабличнаяЧасть.Загрузить(тзТрансакции), если всё ОК, либо выводить сообщение об ошибке...
В рег.задании можно обрабатывать при успехе и отбрасывать при неудаче. Конечно, хорошо бы при этом в журнал регистрации писать...

А ещё я бы на Вашем месте озаботился переписать всё это дело в асинхронный вариант. Но это - уже долго и печально (хотя результат того стОит).

Ну, и да, я не люблю COM. Во-первых, отпадают все варианты, кроме Windows. Во-вторых, это большая "дыра" в безопасности... И да, Вы правы, я использую для таких целей внешние источники данных, там хоть как-то можно прошерстить под Linux.
---
Ещё есть мнение, что Вы вызываете эту обработку неверно. То есть, она, возможно, и возвращает структуру, но это значение потом никто не получает и не обрабатывает. Обработчик закрытия при вызове подключали? Показывайте код вызова обработки.
---
Если вы вольны модифицировать код обработки - перенесите нужный функционал из формы в модуль менеджера, и дело с концом
---
Если я правильно понял, Вы предлагаете вместо "ОткрытьФорму" через объект обработки вызывать просто ее методы из модуля менеджера?
---
Ну или
```1c
    Процедура СделатьИЗакрыть()
    структ = Сделать();
    Закрыть(структ)
    КонецПроцедуры

    ПриОткрытии()
    Если НеОткрыватьФорму Тогда
    ПодключитьОбработчикОжидания("СделатьИЗакрыть", 0.1, Истина)
    КонецЕсли
    КонецПроцедуры
```
---
Тем более, что автор вопроса всё понял и всё переделал, судя по всему. У него всё получилось, вероятно.
А Ваши посты уже мимо темы. Не будьте троллем.