Общий модуль 1С — модуль без собственных данных с экспортными процедурами и функциями для форм, объектов, обработок и регламентных заданий. Где выполняется его код и кто может его вызвать, решают флажки в свойствах. Ошибка во флажках оборачивается сообщением «Переменная не определена» на клиенте или лишними серверными вызовами, от которых тормозит форма.
Ниже — шесть модулей для проверки ИНН контрагента: формат проверяется на клиенте, дубли ищутся одним вызовом сервера, настройка читается через кэш. Тот же код работает в форме, модуле объекта и расширении. Константу КонтролироватьДублиИНН (Булево) добавьте сами, справочник Контрагенты с реквизитом ИНН есть в большинстве типовых. Платформа — 8.3.6 и новее.
Модуль создают в конфигураторе: «Общие» → «Общие модули» → «Добавить». Дальше в палитре свойств выбирают контексты:
| Задача | Флажки | Суффикс имени |
|---|---|---|
| Запросы, запись, расчёты | Сервер, Внешнее соединение, Клиент (обычное приложение) | нет или Сервер |
| Вызов с клиента без контекста формы | Сервер, Вызов сервера | ВызовСервера |
| Сообщения, диалоги, открытие форм | Клиент (управляемое приложение) | Клиент |
| Вычисления без обращения к базе | оба «Клиента», Сервер, Внешнее соединение | КлиентСервер |
| Кэш на сервере | как у серверного + повторное использование | ПовтИсп |
| Кэш на клиенте, меньше серверных вызовов | Клиент (управляемое приложение) + «На время сеанса» | КлиентПовтИсп |
| Код, которому нужны полные права | Сервер, Привилегированный | ПолныеПрава |
| Обработчики ожидания, методы без имени модуля | Клиент (управляемое приложение), Глобальный | Глобальный |
Суффиксы — соглашение стандартов 1С, платформа их не проверяет, зато по вызову сразу видно, где он выполнится. Серверные модули БСП называются без суффикса (ОбщегоНазначения), а в КА 2.5 больше сотни модулей с суффиксом Сервер, например АссортиментСервер. Привилегированных модулей в КА 2.5 нет совсем: привилегированный режим включают точечно в коде.
Сюда кладут код без обращения к базе: проверки формата, расчёты, работу со строками. На клиенте он выполняется без вызова сервера, на сервере — напрямую.
// Общий модуль ПроверкаКонтрагентовКлиентСервер.
// Флажки: Клиент (управляемое приложение), Клиент (обычное приложение),
// Сервер, Внешнее соединение.
// Проверяет длину, состав и контрольные цифры ИНН.
//
// Параметры:
// ИНН - Строка - ИНН организации (10 цифр) или физического лица (12 цифр).
//
// Возвращаемое значение:
// Строка - текст ошибки; пустая строка, если ИНН корректен.
//
Функция ОшибкаИНН(ИНН) Экспорт
СтрокаИНН = СокрЛП(ИНН);
Длина = СтрДлина(СтрокаИНН);
Если Длина <> 10 И Длина <> 12 Тогда
Возврат НСтр("ru = 'ИНН должен состоять из 10 или 12 цифр'");
КонецЕсли;
Цифры = Новый Массив;
Для Позиция = 1 По Длина Цикл
ТекущийСимвол = Сред(СтрокаИНН, Позиция, 1);
Если СтрНайти("0123456789", ТекущийСимвол) = 0 Тогда
Возврат НСтр("ru = 'ИНН может содержать только цифры'");
КонецЕсли;
Цифры.Добавить(Число(ТекущийСимвол));
КонецЦикла;
Если Длина = 10 Тогда
Верно = (КонтрольнаяЦифра(Цифры, "2,4,10,3,5,9,4,6,8") = Цифры[9]);
Иначе
Верно = (КонтрольнаяЦифра(Цифры, "7,2,4,10,3,5,9,4,6,8") = Цифры[10])
И (КонтрольнаяЦифра(Цифры, "3,7,2,4,10,3,5,9,4,6,8") = Цифры[11]);
КонецЕсли;
Возврат ?(Верно, "", НСтр("ru = 'Неверные контрольные цифры ИНН'"));
КонецФункции
// Без Экспорт: функция видна только внутри этого модуля.
Функция КонтрольнаяЦифра(Цифры, СтрокаВесов)
Веса = СтрРазделить(СтрокаВесов, ",");
Сумма = 0;
Для Индекс = 0 По Веса.ВГраница() Цикл
Сумма = Сумма + Цифры[Индекс] * Число(Веса[Индекс]);
КонецЦикла;
Возврат (Сумма % 11) % 10;
КонецФункции
ОшибкаИНН с Экспорт вызывают форма, модуль объекта и другие модули.КонтрольнаяЦифра без Экспорт видна только внутри модуля, вызов снаружи даст «Метод объекта не обнаружен».Новый Запрос сломает компиляцию на клиенте. Серверную ветку оборачивают в #Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда … #КонецЕсли.Серверному модулю флажок «Вызов сервера» не нужен: его и так видят модули объектов, серверные процедуры форм и регламентные задания.
// Общий модуль ПроверкаКонтрагентов.
// Флажки: Сервер, Внешнее соединение, Клиент (обычное приложение).
// Ищет других контрагентов с тем же ИНН.
//
// Параметры:
// ИНН - Строка - проверяемый ИНН.
// ТекущийКонтрагент - СправочникСсылка.Контрагенты - исключается из поиска.
//
// Возвращаемое значение:
// Массив из Структура - свойства Ссылка и Наименование.
//
Функция ДублиПоИНН(ИНН, ТекущийКонтрагент) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Контрагенты.Ссылка КАК Ссылка,
| Контрагенты.Наименование КАК Наименование
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| Контрагенты.ИНН = &ИНН
| И Контрагенты.Ссылка <> &ТекущийКонтрагент
| И НЕ Контрагенты.ПометкаУдаления";
Запрос.УстановитьПараметр("ИНН", СокрЛП(ИНН));
Запрос.УстановитьПараметр("ТекущийКонтрагент", ТекущийКонтрагент);
Дубли = Новый Массив;
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Дубли.Добавить(Новый Структура("Ссылка, Наименование",
Выборка.Ссылка, Выборка.Наименование));
КонецЦикла;
Возврат Дубли;
КонецФункции
СправочникОбъект или ТаблицаЗначений на тонкий клиент не передаются.Ссылка.Наименование не прочитать.Для клиента заводят тонкий модуль-шлюз с флажками «Сервер» и «Вызов сервера»:
// Общий модуль ПроверкаКонтрагентовВызовСервера.
// Флажки: Сервер, Вызов сервера.
// Шлюз к ПроверкаКонтрагентов.ДублиПоИНН для клиентского кода.
Функция ДублиПоИНН(Знач ИНН, Знач ТекущийКонтрагент) Экспорт
// Параметры пришли с клиента: проверяем типы, прежде чем передать дальше.
Если ТипЗнч(ИНН) <> Тип("Строка") Тогда
ВызватьИсключение НСтр("ru = 'Параметр ИНН должен быть строкой'");
КонецЕсли;
Если ТипЗнч(ТекущийКонтрагент) <> Тип("СправочникСсылка.Контрагенты") Тогда
ТекущийКонтрагент = Справочники.Контрагенты.ПустаяСсылка();
КонецЕсли;
Возврат ПроверкаКонтрагентов.ДублиПоИНН(ИНН, ТекущийКонтрагент);
КонецФункции
// Шлюз для клиентского кэша настроек.
Функция НастройкиПроверки() Экспорт
Возврат ПроверкаКонтрагентовПовтИсп.НастройкиПроверки();
КонецФункции
Знач в параметрах: без него параметры после вызова возвращаются на клиент, а это лишний трафик.Режим задаётся для всего модуля. Платформа запоминает результат функции для каждого набора параметров и при повторном вызове не выполняет её тело. «На время вызова» — кэш живёт до конца текущего вызова сервера. «На время сеанса» — дольше, но в общем случае значение удаляется через 20 минут после вычисления или через 6 минут после последнего использования, а также при нехватке памяти и перезапуске рабочего процесса.
// Общий модуль ПроверкаКонтрагентовПовтИсп.
// Флажки: Сервер, Внешнее соединение, Клиент (обычное приложение).
// Повторное использование возвращаемых значений: На время вызова.
// Возвращаемое значение:
// ФиксированнаяСтруктура - свойство КонтролироватьДубли (Булево).
//
Функция НастройкиПроверки() Экспорт
// Настройка нужна всем, а права на константу есть не у всех.
УстановитьПривилегированныйРежим(Истина);
КонтролироватьДубли = Константы.КонтролироватьДублиИНН.Получить();
УстановитьПривилегированныйРежим(Ложь);
Возврат Новый ФиксированнаяСтруктура("КонтролироватьДубли", КонтролироватьДубли);
КонецФункции
ФиксированнаяСтруктура не даёт вызывающему коду изменить закэшированное значение.// Общий модуль ПроверкаКонтрагентовКлиентПовтИсп.
// Флажки: Клиент (управляемое приложение).
// Повторное использование возвращаемых значений: На время сеанса.
Функция НастройкиПроверки() Экспорт
// Первый вызов идёт на сервер, следующие возвращают значение из кэша клиента.
Возврат ПроверкаКонтрагентовВызовСервера.НастройкиПроверки();
КонецФункции
Первый вызов идёт на сервер, следующие берут ответ из кэша клиента, и изменение поля ИНН не стоит лишнего обращения к серверу.
Ловушки повторного использования:
ОбновитьПовторноИспользуемыеЗначения() сбрасывает кэш только своего сеанса, поэтому на сеанс кэшируйте то, что не меняется днём.Массив — следующий вызов получит изменённый массив. Возвращайте фиксированные коллекции.БухгалтерскийУчетВызовСервераПовтИсп экономят вычисление, но клиент всё равно обращается к серверу.Клиентский модуль собирает проверку в сценарий: формат — локально, дубли — одним вызовом сервера, ошибка — у поля.
// Общий модуль ПроверкаКонтрагентовКлиент.
// Флажки: Клиент (управляемое приложение).
// Проверяет ИНН в форме контрагента и выводит ошибку у поля.
//
// Возвращаемое значение:
// Булево - Истина, если ошибок нет.
//
Функция ПроверитьИНН(ИНН, ТекущийКонтрагент) Экспорт
// Формат проверяем на клиенте, без обращения к серверу.
ТекстОшибки = ПроверкаКонтрагентовКлиентСервер.ОшибкаИНН(ИНН);
Если ЗначениеЗаполнено(ТекстОшибки) Тогда
СообщитьОбОшибке(ТекстОшибки);
Возврат Ложь;
КонецЕсли;
Если Не ПроверкаКонтрагентовКлиентПовтИсп.НастройкиПроверки().КонтролироватьДубли Тогда
Возврат Истина;
КонецЕсли;
// Один внеконтекстный вызов сервера.
Дубли = ПроверкаКонтрагентовВызовСервера.ДублиПоИНН(ИНН, ТекущийКонтрагент);
Если Дубли.Количество() > 0 Тогда
СообщитьОбОшибке(СтрШаблон(НСтр("ru = 'ИНН уже указан у контрагента «%1»'"),
Дубли[0].Наименование));
Возврат Ложь;
КонецЕсли;
Возврат Истина;
КонецФункции
Процедура СообщитьОбОшибке(ТекстОшибки)
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ТекстОшибки;
Сообщение.Поле = "Объект.ИНН";
Сообщение.Сообщить();
КонецПроцедуры
Поле привязывает сообщение к полю основного реквизита формы.// Модуль формы элемента справочника Контрагенты.
// В форме добавлена команда ПоказатьДубли.
&НаКлиенте
Процедура ИННПриИзменении(Элемент)
Если ЗначениеЗаполнено(Объект.ИНН) Тогда
ПроверкаКонтрагентовКлиент.ПроверитьИНН(Объект.ИНН, Объект.Ссылка);
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПоказатьДубли(Команда)
Если Не ЗначениеЗаполнено(Объект.ИНН) Тогда
Возврат;
КонецЕсли;
// Так нельзя: модуль без флажка «Вызов сервера» на клиенте не виден.
// ЧислоДублей = ПроверкаКонтрагентов.ДублиПоИНН(Объект.ИНН, Объект.Ссылка).Количество();
ЧислоДублей = ЧислоДублейНаСервере(Объект.ИНН, Объект.Ссылка);
ПоказатьПредупреждение(, СтрШаблон(НСтр("ru = 'Контрагентов с таким ИНН: %1'"), ЧислоДублей));
КонецПроцедуры
&НаСервереБезКонтекста
Функция ЧислоДублейНаСервере(Знач ИНН, Знач ТекущийКонтрагент)
// На сервере серверный общий модуль доступен напрямую.
Возврат ПроверкаКонтрагентов.ДублиПоИНН(ИНН, ТекущийКонтрагент).Количество();
КонецФункции
ИННПриИзменении вызывает только клиентский модуль и не зависит от устройства проверки.ПоказатьДубли идёт к серверному модулю через &НаСервереБезКонтекста: на сервер уходят два параметра. &НаСервере гонял бы туда и обратно все данные формы — он нужен, когда сервер меняет реквизиты формы.Итог: при ошибке формата форма не обращается к серверу, при корректном ИНН делает один вызов и ещё один за сеанс — за настройкой.
// Модуль объекта справочника Контрагенты.
#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда
Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)
Если Не ЗначениеЗаполнено(ИНН) Тогда
Возврат;
КонецЕсли;
// Та же функция, что проверяла поле в форме.
ТекстОшибки = ПроверкаКонтрагентовКлиентСервер.ОшибкаИНН(ИНН);
Если Не ЗначениеЗаполнено(ТекстОшибки)
И ПроверкаКонтрагентовПовтИсп.НастройкиПроверки().КонтролироватьДубли
И ПроверкаКонтрагентов.ДублиПоИНН(ИНН, Ссылка).Количество() > 0 Тогда
ТекстОшибки = НСтр("ru = 'Контрагент с таким ИНН уже есть'");
КонецЕсли;
Если ЗначениеЗаполнено(ТекстОшибки) Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ТекстОшибки;
Сообщение.Поле = "ИНН";
Сообщение.УстановитьДанные(ЭтотОбъект);
Сообщение.Сообщить();
Отказ = Истина;
КонецЕсли;
КонецПроцедуры
#КонецЕсли
ОшибкаИНН — та же функция, что в форме: правило описано один раз.УстановитьДанные(ЭтотОбъект) связывает сообщение с объектом, и форма подсветит поле ИНН.Записать() обработчик не срабатывает — вызывайте ПроверитьЗаполнение() явно.Свой модуль в расширении создают так же, с теми же флажками, а имя начинают с префикса расширения: Расш1_ПроверкаКонтрагентов. Код основной конфигурации модули расширения не видит, их вызывают объекты самого расширения.
Чтобы изменить существующий модуль без снятия с поддержки, его заимствуют и перехватывают метод. Пример: у ИНН «0000000000» контрольные цифры сходятся, а такой ИНН нужно отсекать.
// Расширение с префиксом Расш1_.
// Общий модуль ПроверкаКонтрагентовКлиентСервер заимствован из конфигурации.
&Вместо("ОшибкаИНН")
Функция Расш1_ОшибкаИНН(ИНН)
// Выполняем исходную функцию и получаем её результат.
ТекстОшибки = ПродолжитьВызов(ИНН);
// У «0000000000» контрольные цифры сходятся, но такой ИНН не нужен.
Если ПустаяСтрока(ТекстОшибки) И ПустаяСтрока(СтрЗаменить(СокрЛП(ИНН), "0", "")) Тогда
ТекстОшибки = НСтр("ru = 'ИНН не может состоять из одних нулей'");
КонецЕсли;
Возврат ТекстОшибки;
КонецФункции
ПродолжитьВызов выполняет исходную функцию и возвращает её результат.Экспорт или с опечаткой.ПриИзменении стоит вызов ВызовСервера. Проверки переносите в КлиентСервер, неизменные данные — в КлиентПовтИсп.В конфигураторе откройте ветку «Общие» → «Общие модули», нажмите «Добавить», задайте имя с суффиксом контекста и отметьте флажки в палитре свойств. Методы для вызова из других модулей помечайте Экспорт, затем обновите конфигурацию базы данных (F7).
Нет, раздела описания переменных у общего модуля нет. Значение на сеанс храните в параметрах сеанса на сервере, в ПараметрыПриложения на клиенте или возвращайте из функции модуля с повторным использованием.
Да, по тем же правилам: из модуля объекта обработки доступны серверные модули, из клиентского кода её формы — клиентские и модули с флажком «Вызов сервера». Свой общий модуль внутри внешней обработки создать нельзя.
Если код скопирован по десятку форм, а формы делают лишние серверные вызовы, структуру модулей быстрее перепроектировать с опытным программистом — посмотрите услуги по доработке 1С. Опишите задачу в форме размещения задания, и исполнители предложат решение через расширение, без снятия конфигурации с поддержки.
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.