Почему код с директивой &НаСервере может выполняться на клиенте в 1С:Розница 2.3 и как это исправить?

Программист 1С v8.3 (Управляемые формы) 1С:Розница Торговля и дистрибуция
← На главную

Разберем распространенную ситуацию, когда код, помеченный директивой &НаСервере, неожиданно вызывает ошибку "Метод недоступен на клиенте" в 1С:Розница 2.3. Эта проблема часто возникает при разработке внешних обработок, особенно при попытке получить цены товаров с учетом всех действующих акций. Мы выясним корневую причину такого поведения и предложим несколько эффективных решений.

Исходная проблема разработчика: Расчет цен с акциями и ошибка "Метод недоступен на клиенте"

Разработчик столкнулся с необходимостью программного получения цен товаров с учетом всех маркетинговых акций в 1С:Розница 2.3. Отсутствие готовых процедур в модулях для этой цели привело к попытке эмулировать работу рабочего места кассира (РМК).

План заключался в следующем:

  1. Программно создать объект формы РМК: Обр = Обработки.РМКУправляемыйРежим; ФР = Обр.ПолучитьФорму("Форма");
  2. Заполнить в объекте формы информацию о товарах (номенклатура, количество, цена и т.д.).
  3. Вызвать серверную процедуру для расчета скидок и наценок: РозничныеПродажиСервер.РассчитатьСкидкиНаценкиНаСервере(...).

Несмотря на то, что вся процедура была помечена директивой &НаСервере, при вызове внутренней функции СкидкиНаценкиСерверПереопределяемый.Рассчитать, произошла ошибка: "Метод недоступен на клиенте" на строке: ВходныеПараметры.Вставить("СписокОдноразовыхКодов", Объект.ПредъявленныеКодыОднократныхСкидок.Выгрузить.ВыгрузитьКолонку("КодСкидки"));. Отладка показала, что метод Выгрузить оказался недоступным. Возник вопрос, почему же серверный метод вызвал ошибку доступности на клиенте, если всё, казалось бы, должно выполняться на сервере?

Давайте посмотрим на часть кода, которая приводила к ошибке:


&НаСервере
Процедура ТестНаСервере

    Обр = Обработки.РМКУправляемыйРежим;
    ФР = Обр.ПолучитьФорму("Форма");
    
    // Затем заполняем в объекте формы строку товара:
    НС = ФР.Объект.Товары.Добавить;
    НС.Номенклатура = Справочники.Номенклатура.НайтиПоНаименованию("Номенклатура");
    НС.Склад = Справочники.Склады.НайтиПоНаименованию("Склад");
    НС.КоличествоУпаковок = 1;
    НС.Количество = 1;
    НС.Цена = 1390;
    НС.Сумма = 1390;
    НС.СтавкаНДС = Перечисления.СтавкиНДС.БезНДС;
    НС.Организация = Справочники.Организации.НайтиПоНаименованию("Организация");
    НС.ТипНоменклатуры = Перечисления.ТипыНоменклатуры.Товар;
    НС.СуммаВсего = 1390;
    НС.ВидНалога = Перечисления.ТипыСистемНалогообложенияККТ.Патент;

    // Заполняем структуру параметров по аналогии с данными отладчика
    УправляемыеСкидки = Новый СписокЗначений;
    СтруктураПараметры = Новый Структура;
    СтруктураПараметры.Вставить("ПрименятьКОбъекту", Истина);
    СтруктураПараметры.Вставить("ТолькоПредварительныйРасчет", Ложь);
    СтруктураПараметры.Вставить("ВосстанавливатьУправляемыеСкидки", Ложь);
    СтруктураПараметры.Вставить("УправляемыеСкидки", УправляемыеСкидки);
    СтруктураПараметры.Вставить("ТолькоСообщенияПослеОформления", Ложь);
    СтруктураПараметры.Вставить("ПорядковыйНомерПродажи", 1);
    СтруктураПараметры.Вставить("НеВыводитьСообщения", Ложь);
    РабочееМесто = ПараметрыСеанса.РабочееМестоКлиента;
    СтруктураПараметры.Вставить("РабочееМесто", РабочееМесто);
    СтруктураПараметры.Вставить("КонтролироватьОстаткиТоваров", Ложь);
    
    АдресПримененныхСкидокВоВременномХранилище = "";
    РозничныеПродажиСервер.РассчитатьСкидкиНаценкиНаСервере(СтруктураПараметры, ФР.Объект, АдресПримененныхСкидокВоВременномХранилище, ФР.УникальныйИдентификатор, Ложь);

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

&НаКлиенте
Процедура Тест(Команда)
    ТестНаСервере;
КонецПроцедуры

При отладке выяснилось, что Объект имел тип ДанныеФормаСтруктура, а Объект.ПредъявленныеКодыОднократныхСкидок — тип ДанныеФормыКоллекция. Метод Выгрузить у ДанныеФормыКоллекция является серверным. Возникает вопрос: почему же серверный метод вызвал ошибку доступности на клиенте?

Причина ошибки: Специфика работы толстого клиента в файловой базе

Ключевая причина ошибки кроется в специфике выполнения кода в 1С:Предприятии, особенно при использовании толстого клиента в файловом варианте работы базы данных.

Давайте проанализируем ситуацию:

  1. Толстый клиент в файловой базе: Когда мы работаем с файловой базой данных, используя толстый клиент, вся функциональность, включая исполнение кода на встроенном языке, выполняется на компьютере пользователя. В этом режиме толстый клиент сам реализует всю функциональность файловой СУБД, и нет четкого разделения между клиентским и серверным окружением в традиционном понимании клиент-серверной архитектуры.
  2. Игнорирование директивы &НаСервере: В таком режиме работы (толстый клиент, файловая база) директивы &НаСервере и &НаКлиенте могут игнорироваться или не приводить к ожидаемому поведению. Весь код, даже помеченный как серверный, фактически компилируется и исполняется на клиентской стороне. Это приводит к тому, что при попытке вызвать методы, которые доступны только на сервере (например, Выгрузить у ДанныеФормыКоллекция), система выдает ошибку "Метод недоступен на клиенте", потому что хотя мы и думаем, что находимся на сервере, фактически исполнение происходит в клиентском контексте толстого клиента.
  3. Типы данных форм: Объекты типа ДанныеФормаСтруктура и ДанныеФормыКоллекция представляют данные, связанные с формой. Хотя сами они могут быть переданы на сервер, методы, которые манипулируют этими данными способом, предназначенным только для сервера (как Выгрузить для получения таблицы значений), требуют истинного серверного контекста.

Таким образом, несмотря на явное указание директивы &НаСервере, среда выполнения толстого клиента в файловой базе не предоставляет истинно серверного контекста для выполнения этого кода, что и приводит к конфликту доступности методов.

Различия между тонким и толстым клиентом для управляемого приложения

Для полного понимания проблемы, важно рассмотреть различия между тонким и толстым клиентом в контексте управляемого приложения, как в 1С:Розница 2.3.

Толстый клиент (в управляемом режиме):

  1. Самодостаточность: Это полноценное приложение, которое может выполнять практически всю функциональность, предоставляемую встроенным языком.
  2. Ресурсоемкость: Требует значительных ресурсов на компьютере пользователя, так как обрабатывает большую часть данных и логики.
  3. Работа с файловой базой: В файловом варианте взаимодействует напрямую с файловой базой данных, требуя прямого файлового доступа и выполняя всю логику базы данных на клиентской стороне.
  4. Игнорирование директив: Как мы выяснили, в файловом режиме может игнорировать или некорректно обрабатывать директивы &НаСервере.
  5. Конфигуратор: Поддерживает работу в режиме конфигуратора.

Тонкий клиент:

  1. Легковесность: Это "тонкий" клиент, который выполняет минимальный объем работы на своей стороне. Основные вычисления и обработка данных проводятся на сервере 1С:Предприятия.
  2. Сетевое взаимодействие: Подключается к серверу 1С:Предприятия (или веб-серверу) через HTTP или HTTPS, что делает его менее требовательным к пропускной способности сети и более устойчивым к разрывам соединения.
  3. Строгое разделение: В тонком клиенте соблюдается строгое разделение клиентской и серверной логики. Код, помеченный &НаСервере, гарантированно выполняется на сервере, а код &НаКлиенте – на клиенте.
  4. Автоматическое обновление: Поддерживает автоматическое обновление через интернет, что упрощает администрирование.

Именно тонкий клиент является рекомендованным и наиболее корректным способом работы с управляемым приложением, обеспечивающим правильное функционирование клиент-серверной архитектуры и соблюдение директив компиляции.

Решение проблемы: Переход на тонкий клиент

Наиболее очевидным и правильным решением данной проблемы является использование тонкого клиента для работы с вашей внешней обработкой и конфигурацией 1С:Розница 2.3.

Если вы запускаете файловую базу через толстый клиент, переключитесь на тонкий клиент. При запуске информационной базы через ярлык 1С:Предприятия выберите "Тонкий клиент" вместо "Клиент (обычное приложение)" или "Клиент (управляемое приложение)". Это обеспечит правильное разделение контекстов выполнения и гарантирует, что ваш код с директивой &НаСервере действительно будет исполняться на сервере, где метод Выгрузить будет доступен.

Альтернативные способы расчета цен с акциями без эмуляции формы РМК

Хотя переход на тонкий клиент решит проблему выполнения кода, подход с программным получением формы Обработки.РМКУправляемыйРежим и заполнением ее объекта является косвенным и может быть не самым оптимальным. Создание формы и ее объекта является ресурсоемкой операцией и, как правило, должно быть избегнуто в чисто серверных процедурах, если нет прямой необходимости во взаимодействии с интерфейсом.

Вместо эмуляции работы формы РМК, мы рекомендуем использовать более прямые и предназначенные для этого серверные API конфигурации 1С:Розница 2.3.

Давайте рассмотрим подробнее, как можно подойти к задаче получения цен с акциями более эффективно:

  1. Использование специализированных серверных функций: В конфигурации "1С:Розница" существуют модули, которые содержат готовую логику для расчета скидок и наценок. Вы уже используете функцию РозничныеПродажиСервер.РассчитатьСкидкиНаценкиНаСервере, что является верным направлением. Однако, для ее корректной работы важно передавать ей объекты данных, которые адекватно обрабатываются в серверном контексте, а не объекты форм.
  2. Подготовка данных на сервере: Вместо заполнения объекта формы, создавайте структуры данных, таблицы значений или объекты прикладных типов (например, ДокументОбъект.ЧекККМ) непосредственно на сервере. Эти объекты будут содержать всю необходимую информацию о товарах, клиенте, складе, условиях продажи.
    
    &НаСервере
    Функция ПолучитьЦеныСАкциямиНаСервере(СписокТоваров, Клиент, Склад, Организация)
    
        // Создадим временный объект чека для расчета скидок.
        // Это более близко к тому, как система работает "изнутри",
        // когда происходит реальная продажа.
        ЧекОбъект = Документы.ЧекККМ.СоздатьОбъект();
        ЧекОбъект.Дата = ТекущаяДата();
        ЧекОбъект.Организация = Организация;
        ЧекОбъект.Склад = Склад;
        ЧекОбъект.Покупатель = Клиент; // Если есть клиент, для персональных скидок
        // ЧекОбъект.Подразделение = ... // Дополнительные параметры при необходимости
    
        // Заполняем табличную часть Товары из переданного списка
        Для Каждого СтрокаТовара Из СписокТоваров Цикл
            НоваяСтрока = ЧекОбъект.Товары.Добавить();
            НоваяСтрока.Номенклатура = СтрокаТовара.Номенклатура;
            НоваяСтрока.Количество = СтрокаТовара.Количество;
            // Исходная цена до применения скидок
            НоваяСтрока.Цена = СтрокаТовара.Цена; 
            НоваяСтрока.Сумма = СтрокаТовара.Количество * СтрокаТовара.Цена;
            НоваяСтрока.СтавкаНДС = СтрокаТовара.СтавкаНДС; // Передаем ставку НДС
            НоваяСтрока.ТипНоменклатуры = СтрокаТовара.ТипНоменклатуры;
            // Дополнительные поля по необходимости, например, СвойствоВарианта, Характеристика
        КонецЦикла;
    
        // Параметры для расчета скидок. 
        // Многие из них могут быть настроены в соответствии с вашей задачей.
        СтруктураПараметры = Новый Структура();
        СтруктураПараметры.Вставить("ПрименятьКОбъекту", Истина);
        СтруктураПараметры.Вставить("ТолькоПредварительныйРасчет", Ложь);
        СтруктураПараметры.Вставить("ВосстанавливатьУправляемыеСкидки", Ложь);
        СтруктураПараметры.Вставить("УправляемыеСкидки", Новый СписокЗначений());
        СтруктураПараметры.Вставить("ТолькоСообщенияПослеОформления", Ложь);
        СтруктураПараметры.Вставить("ПорядковыйНомерПродажи", 1);
        СтруктураПараметры.Вставить("НеВыводитьСообщения", Ложь);
        // РабочееМесто может быть важно для определения действующих акций.
        // Если вы не эмулируете РМК, можно получить его из ПараметрыСеанса 
        // или передать конкретный справочник.РабочиеМеста
        СтруктураПараметры.Вставить("РабочееМесто", ПараметрыСеанса.РабочееМестоКлиента); 
        СтруктураПараметры.Вставить("КонтролироватьОстаткиТоваров", Ложь);
        // Дополнительные параметры могут потребоваться в зависимости от настроек скидок.
    
        // Вызываем серверную процедуру расчета скидок
        АдресПримененныхСкидокВоВременномХранилище = "";
        // УникальныйИдентификатор обычно используется для связи данных формы с сервером.
        // Для чисто серверного расчета можно использовать Новый УникальныйИдентификатор()
        // или передать УИ документа, если он уже записан.
        УникальныйИдентификаторДляСкидок = Новый УникальныйИдентификатор(); 
                
        РозничныеПродажиСервер.РассчитатьСкидкиНаценкиНаСервере(
            СтруктураПараметры,
            ЧекОбъект, // Передаем объект документа, а не данные формы
            АдресПримененныхСкидокВоВременномХранилище,
            УникальныйИдентификаторДляСкидок,
            Ложь
        );
    
        // Возвращаем рассчитанные данные. 
        // В табличной части ЧекОбъект.Товары будут обновленные цены и суммы с учетом скидок.
        Возврат ЧекОбъект.Товары.Выгрузить();
    
    КонецФункции
    

    В этом примере СписокТоваров может быть таблицей значений или массивом структур, который вы формируете на клиенте и передаете на сервер. ЧекОбъект будет полноценным серверным объектом типа ДокументОбъект.ЧекККМ, и все его методы, включая внутренние вызовы Выгрузить, будут работать корректно.

  3. Изучение модулей конфигурации: Внимательно изучите модули, такие как РозничныеПродажиСервер, СкидкиНаценкиСерверПереопределяемый, а также их процедуры и функции. Часто в них можно найти подходящие методы для работы с ценами и скидками без необходимости эмулировать работу форм.
    • Конфигурация "1С:Розница" предоставляет обширный функционал для настройки маркетинговых акций, скидок, наценок и ограничений. Скидки могут быть автоматическими, управляемыми вручную, ручными и в виде бонусных программ. Автоматические скидки применяются в документах продажи при выполнении условий, заданных в настройках скидки. Для программного расчета можно использовать внутренние механизмы, которые оперируют данными этих настроек.
    • Документ "Маркетинговые акции" и справочник "Скидки (наценки, ограничения)" хранят все правила предоставления скидок. Для получения актуальных цен с учетом акций, необходимо программно получать данные из этих справочников и регистров сведений, где хранятся условия применения скидок (например, "За разовый объем продаж" по сумме покупки). Затем, на основе этих данных, можно реализовать алгоритм расчета, имитирующий логику платформы, но уже на сервере.

Заключение

Мы выяснили, что ошибка "Метод недоступен на клиенте" при выполнении кода с директивой &НаСервере в файловой базе с толстым клиентом является ожидаемым поведением. Основное решение — переход на тонкий клиент, который обеспечивает правильное разделение клиентского и серверного контекстов. Кроме того, рассмотрели более эффективные подходы к программному расчету цен и скидок, избегая создания объектов форм на сервере и используя прямые серверные API конфигурации. Следуя этим рекомендациям, вы сможете корректно реализовать необходимую функциональность.

← На главную