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