Как настроить ограничение на сумму возврата товара в 1С:Розница и 1С:УТ?

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

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

В этой статье мы подробно разберем, как реализовать подобное ограничение в типовых конфигурациях 1С (таких как 1С:Розница, 1С:Управление нашей фирмой и 1С:Управление торговлей), проанализируем возможные уязвимости этого процесса и разработаем надежный алгоритм проверки.

Почему в 1С нет готовой настройки «из коробки»?

Проанализируем стандартные возможности конфигураций 1С. В типовых решениях права доступа кассиров разграничиваются бинарно. В дополнительных правах пользователя мы можем либо полностью разрешить операцию «Чек возврата», либо полностью её запретить. Настройка конкретных денежных лимитов на одну операцию или на смену в стандартном функционале отсутствует.

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

Логическая уязвимость: проблема «дробления» возвратов

Перед тем как перейти к написанию кода, обсудим важнейший методологический нюанс, который часто упускают при постановке технического задания. Если мы просто установим жесткий лимит на сумму одного чека возврата (например, 10 000 рублей), недобросовестный сотрудник сможет легко обойти это ограничение.

Допустим, клиенту (или сообщнику кассира) нужно вернуть товар на сумму 30 000 рублей. Что помешает менеджеру оформить три последовательных возврата по 9 900 рублей? Ровным счетом ничего. Система пропустит эти чеки, так как каждый из них по отдельности не превышает установленный лимит.

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

  1. Проверка по исходному документу продажи. Программа должна анализировать не сумму текущего документа возврата, а общую сумму исходного чека продажи, на основании которого выполняется операция. Если общая сумма покупки превышала 10 000 рублей, то даже частичный возврат на 1 000 рублей потребует авторизации администратора.
  2. Контроль суммарного лимита за смену (день). Программа подсчитывает общую сумму всех возвратов, сделанных текущим кассиром за день. Если лимит превышен, последующие возвраты блокируются до подтверждения руководителем.
  3. Идентификация клиента. Контроль суммы возвратов по конкретной дисконтной карте или контрагенту за текущие сутки.

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

Шаг 1. Создание расширения и выбор точки встраивания

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

Создадим расширение конфигурации и добавим в него документ ЧекККМ. Переопределим обработчик события ПередЗаписью модуля объекта документа с помощью аннотации &Перед.

Шаг 2. Программный код проверки лимита возврата

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


&Перед("ПередЗаписью")
Процедура Расш1_ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
	
	// Проверяем, что документ проводится и это чек на возврат
	Если ЭтотОбъект.ОбменДанными.Загрузка ИЛИ НЕ ЭтотОбъект.Проведение Тогда
		Возврат;
	КонецЕсли;
	
	// Проверяем вид операции чека
	Если ЭтотОбъект.ВидОперации = Перечисления.ВидыОперацийЧекККМ.Возврат Тогда
		
		// Если у пользователя есть полные права или роль Администратора, проверку не выполняем
		Если РольДоступна("ПолныеПрава") ИЛИ РольДоступна("ДобавлениеИзменениеПраваДоступа") Тогда
			Возврат;
		КонецЕсли;
		
		ЛимитОдногоВозврата = 10000; // Порог в рублях
		ЛимитСменыВсего     = 30000; // Максимальная сумма возвратов кассира за смену
		
		// Проверка 1. Превышение лимита по одному чеку
		Если ЭтотОбъект.СуммаДокумента > ЛимитОдногоВозврата Тогда
			Отказ = Истина;
			ВызватьИсключение "Превышен лимит суммы возврата (10 000 руб.). Требуется авторизация администратора!";
		КонецЕсли;
		
		// Проверка 2. Защита от дробления (контроль общей суммы за смену)
		ТекущийПользователь = Пользователи.ТекущийПользователь();
		СуммаВозвратовЗаСмену = ПолучитьСуммуВозвратовЗаСмену(ТекущийПользователь, ЭтотОбъект.Ссылка);
		
		Если (СуммаВозвратовЗаСмену + ЭтотОбъект.СуммаДокумента) > ЛимитСменыВсего Тогда
			Отказ = Истина;
			ВызватьИсключение "Превышен дневной лимит возвратов для кассира (30 000 руб.). Операция заблокирована!";
		КонецЕсли;
		
	КонецЕсли;
	
КонецПроцедуры

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


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

Шаг 3. Интеграция с интерфейсом РМК и реавторизация

Просто вызывать исключение через ВызватьИсключение — технически надежный, но не самый удобный для работы интерфейсный вариант. На практике кассиру придется закрывать чек, звать администратора, тот будет заходить под своей учетной записью, оформлять возврат и снова перезаходить под кассиром. Это существенно задерживает очередь покупателей.

Рассмотрим более эргономичный подход. В современном Рабочем месте кассира (РМК) платформы 1С реализован механизм «замены прав» или временного подтверждения старшего кассира:

Для настройки такой схемы в коде формы РМК перехватывается нажатие кнопки «Оформить возврат» и вызывается стандартная форма авторизации платформы: РМКУправлениеКлиент.ЗапроситьАвторизацию(...).

Резюме и рекомендации

Внедрение лимита на сумму возврата — это действенный инструмент защиты бизнеса от финансовых потерь. Разрабатывая такое решение, важно соблюдать баланс между безопасностью и удобством работы касс:

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