Как ограничить видимость касс ККМ и чеков других магазинов для кассира при возврате товара в 1С Розница 2.3?

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

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

Суть проблемы: Свободный выбор кассы ККМ при возврате товара

В типовой конфигурации 1С Розница 2.3, когда кассир в рабочем месте кассира (РМК) выбирает операцию «Возврат», в поле «Касса ККМ» ему доступен полный список всех касс, зарегистрированных в базе. Это означает, что кассир может выбрать любую кассу и просмотреть чеки по продажам, независимо от того, к какой кассе или магазину он привязан. Более того, как показал опыт пользователей, такая возможность позволяет оформлять возвраты по чекам, пробитым в других магазинах, что, хотя и допустимо с юридической точки зрения в рамках одной сети (при совпадении ИНН и ОГРН), часто противоречит внутренним правилам и операционным требованиям торговых предприятий.

Такая вседозволенность влечет за собой ряд негативных последствий:

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

Пользователи пытались решить эту проблему различными способами, но стандартные настройки прав доступа, как выяснилось, не позволяют добиться желаемого результата. Даже установка запретов по кассам не дает нужного эффекта: при возврате кассир всё равно видит весь список.

Законодательный аспект и бизнес-требования

Прежде чем погрузиться в технические детали, важно понимать, что законодательство РФ в сфере защиты прав потребителей действительно допускает возврат товара в любой магазин сети, если речь идет об одной и той же юридической организации (т.е. совпадает ИНН и ОГРН торговых точек). Таким образом, с юридической точки зрения, возможность возврата товара, пробитого в другом магазине той же сети, является допустимой практикой.

Однако, с точки зрения внутреннего операционного управления бизнесом, многие компании предпочитают ограничивать такие операции. Это связано с удобством учета, контролем остатков, ответственностью материально-ответственных лиц и общим упорядочиванием бизнес-процессов. Именно эти бизнес-требования и вынуждают нас искать техническое решение для ограничения видимости.

Решение проблемы: Расширение конфигурации

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

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

Пошаговая реализация ограничения видимости через расширение

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

  1. Автоматическая подстановка кассы ККМ текущего кассира при открытии формы выбора чека для возврата.
  2. Полное скрытие остальных касс в списке выбора для данного кассира.
  3. Обеспечение видимости только чеков, пробитых через собственную кассу кассира, даже при попытках использования стандартных отборов или поиска.

Давайте рассмотрим подробнее, как мы можем это реализовать.

Шаг 1: Создание нового расширения

Первым делом нам необходимо создать новое расширение конфигурации. Это можно сделать в режиме «Конфигуратор» через меню «Конфигурация» → «Расширения конфигурации» → «Добавить». Присваиваем расширению осмысленное имя, например, «ОграничениеВидимостиКассККМРМК».

Шаг 2: Определение целевой формы и объектов

Нам нужно понять, какая форма отвечает за выбор чеков ККМ при возврате. В 1С Розница 2.3 для этих целей используется форма списка чеков ККМ. Обычно это форма объекта «Документ.ЧекККМ» или форма выбора, вызываемая из РМК при создании документа «Возврат товаров от покупателя».

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

Шаг 3: Определение текущего кассира и его кассы ККМ

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

  1. Текущий пользователь: Получим текущего пользователя системы с помощью Пользователи.ТекущийПользователь().
  2. Рабочее место: Кассир в РМК всегда работает с определенного рабочего места. Рабочее место связано с кассой ККМ. Мы можем получить текущее рабочее место пользователя, используя соответствующие методы типовых подсистем или данные справочника РабочиеМеста.
  3. Касса ККМ рабочего места: Из справочника КассыККМ или регистров сведений (например, НастройкиРабочихМест, КассыККМРабочиеМеста) мы можем получить ссылку на кассу ККМ, которая назначена текущему рабочему месту. Это критически важный шаг, так как именно по этой кассе мы будем фильтровать чеки.

Пример получения текущей кассы ККМ (может отличаться в зависимости от версии и настроек, но принцип общий):


// В серверном модуле расширения
Функция ПолучитьКассуККМТекущегоКассира() Экспорт
    ТекущийПользователь = Пользователи.ТекущийПользователь();
    Если ТекущийПользователь = Неопределено Тогда
        Возврат Неопределено;
    КонецЕсли;

    // В Рознице 2.3 рабочее место чаще всего хранится в константах или справочнике
    // и связывается с пользователем или сессией.
    // Примерный код для получения рабочего места и кассы ККМ.
    // Реальная логика может быть сложнее и зависеть от настройки.
    РабочееМестоКассира = Неопределено;
    Если ЗначениеЗаполнено(Константы.РабочееМестоКассира.Получить()) Тогда
        // Если рабочее место задано в константе для данной сессии
        РабочееМестоКассира = Константы.РабочееМестоКассира.Получить();
    Иначе
        // Или ищем по текущему пользователю в справочнике РабочиеМеста
        Запрос = Новый Запрос;
        Запрос.Текст = 
            "ВЫБРАТЬ ПЕРВЫЕ 1
            |    РабочиеМеста.Ссылка
            |ИЗ
            |    Справочник.РабочиеМеста КАК РабочиеМеста
            |ГДЕ
            |    РабочиеМеста.Пользователь = &Пользователь";
        Запрос.УстановитьПараметр("Пользователь", ТекущийПользователь);
        РезультатЗапроса = Запрос.Выполнить();
        Если Не РезультатЗапроса.Пустой() Тогда
            РабочееМестоКассира = РезультатЗапроса.Выбрать().Ссылка;
        КонецЕсли;
    КонецЕсли;

    Если РабочееМестоКассира = Неопределено Тогда
        Возврат Неопределено;
    КонецЕсли;

    // Теперь, когда есть рабочее место, найдем связанную кассу ККМ
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ ПЕРВЫЕ 1
        |    КассыККМ.Ссылка
        |ИЗ
        |    Справочник.КассыККМ КАК КассыККМ
        |ГДЕ
        |    КассыККМ.РабочееМесто = &РабочееМесто";
    Запрос.УстановитьПараметр("РабочееМесто", РабочееМестоКассира);
    РезультатЗапроса = Запрос.Выполнить();
    Если Не РезультатЗапроса.Пустой() Тогда
        Возврат РезультатЗапроса.Выбрать().Ссылка;
    Иначе
        Возврат Неопределено;
    КонецЕсли;
КонецФункции

Шаг 4: Модификация динамического списка чеков ККМ

Это центральная часть решения. Нам нужно изменить запрос, который формирует данные для динамического списка чеков ККМ. Для этого мы можем использовать механизм подписки на событие или расширение формы.

Вариант 1: Подписка на событие формы динамического списка

В расширении мы можем найти форму списка чеков ККМ (например, Документ.ЧекККМ.ФормаВыбора или Документ.ЧекККМ.ФормаСписка, в зависимости от того, какая форма вызывается в РМК для выбора чеков) и добавить в неё обработчик события ПриСозданииНаСервере или ПриЧтенииНаСервере для динамического списка.

В этом обработчике мы получим ссылку на динамический список, модифицируем его основной запрос, добавив условие фильтрации по текущей кассе ККМ.

Примерный алгоритм действий:

  1. В расширении находим объект Документ.ЧекККМ.
  2. Перехватываем его форму выбора (например, ФормаВыбора или ФормаСписка, которая используется в РМК).
  3. В модуле формы расширения добавляем обработчик события, например, для динамического списка Список:

// В модуле формы расширения для Документ.ЧекККМ.ФормаВыбора или Документ.ЧекККМ.ФормаСписка
&НаСервере
Процедура СписокПриСозданииНаСервере(Элемент)

    // Получаем текущую кассу ККМ, для которой нужно отфильтровать чеки
    ТекущаяКассаККМ = Расширение123_Модули.ПолучитьКассуККМТекущегоКассира(); // Вызываем нашу функцию

    Если ТекущаяКассаККМ <> Неопределено Тогда
        // Устанавливаем отбор для динамического списка
        // Сначала очистим существующие отборы, если они есть и могут мешать
        Элемент.Список.Отбор.Элементы.Очистить(); 

        // Добавляем элемент отбора по кассе ККМ
        НовыйЭлементОтбора = Элемент.Список.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
        НовыйЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("КассаККМ");
        НовыйЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
        НовыйЭлементОтбора.ПравоеЗначение = ТекущаяКассаККМ;
        НовыйЭлементОтбора.Использование = Истина;

        // Если нам нужно также, чтобы поле 'Касса ККМ' в форме было заполнено и недоступно для изменения:
        Если Элементы.КассаККМ <> Неопределено Тогда
            Элементы.КассаККМ.Значение = ТекущаяКассаККМ;
            Элементы.КассаККМ.Доступность = Ложь; // Делаем поле недоступным для изменения
            Элементы.КассаККМ.ТолькоПросмотр = Истина;
        КонецЕсли;

    КонецЕсли;

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

Вариант 2: Изменение основного запроса динамического списка

Этот подход более мощный и гарантирует, что пользователь не сможет обойти наш фильтр через стандартные отборы интерфейса. Мы перехватываем форму и изменяем свойство ОсновнойЗапрос динамического списка.

  1. В расширении находим форму списка чеков ККМ.
  2. Создаем модуль объекта расширения для этой формы.
  3. В обработчике события ПриСозданииНаСервере получаем динамический список и его основной запрос.
  4. Добавляем в запрос условие ГДЕ ЧекиККМ.КассаККМ = &ТекущаяКассаККМ.
  5. Устанавливаем параметр &ТекущаяКассаККМ.

// В модуле формы расширения для Документ.ЧекККМ.ФормаВыбора или Документ.ЧекККМ.ФормаСписка
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)

    // Получаем текущую кассу ККМ, для которой нужно отфильтровать чеки
    ТекущаяКассаККМ = Расширение123_Модули.ПолучитьКассуККМТекущегоКассира(); // Вызываем нашу функцию

    Если ТекущаяКассаККМ <> Неопределено Тогда
        // Получаем динамический список
        ДинамическийСписок = Элементы.Список; // Предполагается, что имя динамического списка - "Список"

        // Проверяем, что это динамический список и у него есть основной запрос
        Если ТипЗнч(ДинамическийСписок) = Тип("ДинамическийСписок") И Не ДинамическийСписок.ОсновнойЗапрос.Пустая() Тогда
            ТекстЗапроса = ДинамическийСписок.ОсновнойЗапрос;

            // Добавляем условие фильтрации в запрос. 
            // Важно правильно определить, куда вставить условие, 
            // чтобы не нарушить структуру запроса (например, после ГДЕ или перед УПОРЯДОЧИТЬ)
            Если Найти(Верх(ТекстЗапроса), "ГДЕ") = 0 Тогда
                ТекстЗапроса = ТекстЗапроса + " ГДЕ ЧекиККМ.КассаККМ = &ТекущаяКассаККМ";
            Иначе
                ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ГДЕ", "ГДЕ ЧекиККМ.КассаККМ = &ТекущаяКассаККМ И ");
            КонецЕсли;

            ДинамическийСписок.ОсновнойЗапрос = ТекстЗапроса;

            // Устанавливаем параметр запроса
            ДинамическийСписок.Параметры.УстановитьЗначение("ТекущаяКассаККМ", ТекущаяКассаККМ);
        КонецЕсли;

        // Если нам нужно также, чтобы поле 'Касса ККМ' в форме было заполнено и недоступно для изменения:
        // Проверяем наличие элемента формы с именем 'КассаККМ'
        Если Элементы.Найти("КассаККМ") <> Неопределено Тогда
            Элементы.КассаККМ.Значение = ТекущаяКассаККМ;
            Элементы.КассаККМ.Доступность = Ложь; // Делаем поле недоступным для изменения
            Элементы.КассаККМ.ТолькоПросмотр = Истина;
        КонецЕсли;

    КонецЕсли;

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

Важные моменты при изменении запроса:

  1. Точное имя источника данных: Убедитесь, что имя таблицы или псевдоним для ЧекККМ (в нашем примере ЧекиККМ) в запросе динамического списка совпадает с тем, что вы используете в условии ГДЕ. Проанализируйте исходный запрос динамического списка, чтобы это определить.
  2. Размещение условия: Если в запросе уже есть условие ГДЕ, новое условие нужно добавить через И. Если ГДЕ нет, добавляем его целиком.
  3. Параметры запроса: Важно не только добавить условие, но и установить соответствующий параметр для динамического списка.
  4. Проверка на обход: Этот метод изменения основного запроса является наиболее надежным, так как он изменяет корневую выборку данных, и пользовательские отборы или поиск будут применяться уже к отфильтрованному набору данных.

Шаг 5: Тестирование

После реализации расширения обязательно проведите тщательное тестирование:

  1. Войдите в РМК под разными кассирами, привязанными к разным кассам ККМ.
  2. Попытайтесь оформить возврат товара.
  3. Убедитесь, что в списке чеков видны только те чеки, которые были пробиты через кассу текущего кассира.
  4. Попробуйте использовать стандартные фильтры и поиск по номеру чека или дате, чтобы убедиться, что они не позволяют увидеть "чужие" чеки.
  5. Проверьте, что поле "Касса ККМ" в форме выбора чека корректно заполняется и заблокировано от изменений.

Дополнительные соображения и возможные проблемы

В процессе разработки расширения и его внедрения могут возникнуть следующие моменты:

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

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

← На главную