Организация контроля за кассирами на удаленных точках в управлении торговлей 8.1

1С Контроль за кассирамиПроблема стояла следующая:

Есть более двадцати удаленных магазинов (распределенная Управление торговлей), в каждом магазине есть ККМ, подключенная к базе. Иногда возникали сложности с тем, что не совпадали суммы по отчету о розничных продажах и Z-отчету. Иногда умудрялись так пробить, что и не сообразишь сразу, что, да как :???: В связи с этим пришлось организовать небольшой контроль по кассе ККМ и вот что из этого получилось…

Создаем регистр сведений (периодичность — секунда)
Измерения:

  • КассаККМ (Справочник «Кассы ККМ»);

Ресурсы:

  • ТипСобытия (Перечисление «Типы событий ККМ»);
  • Событие (Строка);

Вид регистра сведений

Не забываем включить данный регистр в план обмена РБД.

Перечисление «Типы событий ККМ» может принимать следующие значения:

  • Продажа;
  • Ошибка;
  • Z-отчет;
  • Z-отчет (ошибка);
  • X-отчет;
  • X-отчет (ошибка);
  • Возврат;

Перечисление "Типы событий ККМ"

Немного правим пробитие чека ККМ

РасшифровкаОшибки = "";

// Запишем событие "Начало пробития чека"
ТипСобытия = ?(ВидОперации = Перечисления.ВидыОперацийЧекККМ.Продажа, Перечисления.АП_ТипыСобытийККМ.Продажа, Перечисления.АП_ТипыСобытийККМ.Возврат);
АП_Наработки.ЗаписатьСобытиеККМ(КассаККМ, Товары.Выгрузить(), "Начало пробития чека...", , ТипСобытия);

// Организуем задержку в одну секунду, на всякий случай
ТекВремя = ТекущаяДата();

Пока ТекВремя = ТекущаяДата() Цикл
   ОбработкаПрерыванияПользователя();
КонецЦикла;

Если Не ПровестиИРаспечататьЧек(Ответ, Паника, ЭтаФорма, РасшифровкаОшибки) Тогда
   ОбщегоНазначения.СообщитьОбОшибке(Ответ);
   ТекстПаники = "Возможны расхождения ИБ и ленты ФР!";
   ТекстЗаголовка = "Внимание! Критическая ошибка!!!";

   Если Паника Тогда
      Предупреждение(ТекстПаники, , ТекстЗаголовка);
   КонецЕсли;

   Если ЗначениеЗаполнено(РасшифровкаОшибки) Тогда
      ТекстЗаголовка = ТекстЗаголовка + Символы.ПС + РасшифровкаОшибки;
   КонецЕсли;

   // Запишем событие ККМ
   ТекстСобытия = ОбщегоНазначения.СформироватьТекстСообщения(Ответ);
   АП_Наработки.ЗаписатьСобытиеККМ(КассаККМ, , ТекстСобытия, ТекстПаники + " " + ТекстЗаголовка, Перечисления.АП_ТипыСобытийККМ.Ошибка);
Иначе
   АП_Наработки.ЗаписатьСобытиеККМ(КассаККМ, , "Чек успешно пробит на ККМ" , , ТипСобытия);
КонецЕсли;

Процедура ЗаписатьСобытиеККМ выглядит так:

Процедура ЗаписатьСобытиеККМ(КассаККМ, ТабличнаяЧасть = Неопределено, ТекстСобытия = Неопределено, ДопТекстСобытия = Неопределено, ТипСобытия) Экспорт

   Если ЗначениеЗаполнено(ТекстСобытия) Тогда
      
      Событие = ТекстСобытия;
      Если ЗначениеЗаполнено(ДопТекстСобытия) Тогда
         Событие = Событие + Символы.ПС + ДопТекстСобытия;
      КонецЕсли;
      
      Если ТабличнаяЧасть <> Неопределено Тогда
         // Добавим к тексту ошибки то, что было в чеке, когда она произошла
         Событие = Событие + Символы.ПС + СформироватьТекстСобытияПоТЧ(ТабличнаяЧасть);
      КонецЕсли;

   Иначе
      // Регистрируем продажу
      Событие = СформироватьТекстСобытияПоТЧ(ТабличнаяЧасть);
   КонецЕсли;
   
   // Записываем в регистр
   ЗаписьСобытия = РегистрыСведений.АП_СобытияККМ.СоздатьМенеджерЗаписи();
   ЗаписьСобытия.КассаККМ = КассаККМ;
   ЗаписьСобытия.Период = ТекущаяДата();
   ЗаписьСобытия.ТипСобытия = ТипСобытия;
   ЗаписьСобытия.Событие = Событие;
   ЗаписьСобытия.Записать(Истина);

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

Функция СформироватьТекстСобытияПоТЧ(ТабличнаяЧасть)

   Событие = "Сумма по чеку: " + Строка(ТабличнаяЧасть.Итог("Сумма"));
   Событие = Событие + Символы.ПС + "Список товаров:";
   
   Для Каждого Обход Из ТабличнаяЧасть Цикл
      // Номер строки
      Событие = Событие + Символы.ПС + "Строка " + Обход.НомерСтроки + ": ";
      // Наименование
      Событие = Событие + Обход.Номенклатура.Артикул + " " + Обход.Номенклатура.Наименование + " ";
      // Количество
      Событие = Событие + "Количество:" + Обход.Количество + " ";
      // Единица измерения
      Событие = Событие + Обход.ЕдиницаИзмерения.Наименование + " ";
      // Цена
      Событие = Событие + "Цена:" + Обход.Цена + " ";
      // Сумма
      Событие = Событие + "Сумма:" + Обход.Сумма + " ";
      // Скидки
      Событие = Событие + Обход.ПроцентАвтоматическихСкидок + " ";
      Событие = Событие + Обход.ПроцентСкидкиНаценки;
   КонецЦикла;

   Возврат Событие;

КонецФункции

Теперь в обработку закрытия смены добавляем вызов регистрации

Результат = ПолучитьСерверТО().ОтчетСГашением(ФР, Пароль);
Если ЗначениеЗаполнено(Результат) Тогда
   ТекстОшибки = ПолучитьСерверТО().ПолучитьТекстОшибкиФРТО(Результат);
   Сообщить(ТекстОшибки, СтатусСообщения.Важное);
   // Вставка -(
   АП_Наработки.ЗаписатьСобытиеККМ(КассаККМ, , ТекстОшибки, , , Перечисления.АП_ТипыСобытийККМ.ZОтчетОшибка);

В обработку обслуживания ККМ (у нас во всех магазинах стоят Меркурии) добавляем следующее

Перем СписокСчетчиков;
Перем ЗначенияСчетчиков;

Процедура ЗаполнитьЗначениеСчетчика(Значение, НомерСчетчика)

   Если НомерСчетчика = 0 Тогда
      ЗначенияСчетчиков.СуммаПродаж = Значение;
   ИначеЕсли НомерСчетчика = 4 Тогда
      ЗначенияСчетчиков.СуммаСкидок = Значение;
   ИначеЕсли НомерСчетчика = 9 Тогда
      ЗначенияСчетчиков.СуммаВнесений = Значение;
   ИначеЕсли НомерСчетчика = 10 Тогда
      ЗначенияСчетчиков.СуммаИзъятий = Значение;
   ИначеЕсли НомерСчетчика = 11 Тогда
      ЗначенияСчетчиков.СуммаНаличных = Значение;
   ИначеЕсли НомерСчетчика = 12 Тогда
      ЗначенияСчетчиков.НарастающийИтог = Значение;
   ИначеЕсли НомерСчетчика = 36 Тогда
      ЗначенияСчетчиков.СуммаВозвратов = Значение;
   КонецЕсли;

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

ЗначенияСчетчиков = Новый Структура();
ЗначенияСчетчиков.Вставить("СуммаПродаж", 0);
ЗначенияСчетчиков.Вставить("СуммаСкидок", 0);
ЗначенияСчетчиков.Вставить("СуммаВнесений", 0);
ЗначенияСчетчиков.Вставить("СуммаИзъятий", 0);
ЗначенияСчетчиков.Вставить("СуммаНаличных", 0);
ЗначенияСчетчиков.Вставить("НарастающийИтог", 0);
ЗначенияСчетчиков.Вставить("СуммаВозвратов", 0);

Правим функцию Z-отчет

Функция ZОтчет(Объект, Пароль, НомерЧека, НомерСмены) Экспорт

   Результат = мНетОшибки;

   // Печать Z-отчета
   Попытка
      // Без открытия смены (регистрации кассира) ФР не печатает отчеты
      ОткрытьСмену(Объект, "");
      // Печатаем краткий Z-отчет. Нулевые значения счетчиков не печатаются.
      // Объект.Драйвер.ZОтчет(1);
      // Получим значения со счетчиков ККМ
      Для Каждого Обход Из СписокСчетчиков Цикл
         НомерСчетчика = Обход.Значение;
         ЗаполнитьЗначениеСчетчика(Объект.Драйвер.ЗапроситьСчетчик(НомерСчетчика,0), НомерСчетчика);
      КонецЦикла;
      НомерОтчета = Объект.Драйвер.ZОтчет(1);
      АП_Наработки.ЗаписатьСобытиеККМОтчет(ЗначенияСчетчиков, НомерОтчета, Перечисления.АП_ТипыСобытийККМ.ZОтчет);
   Исключение
      // Получение текстового описания ошибки
      ПолучитьТекстОшибки(Объект);
      Результат = мОшибкаНеизвестно;
   КонецПопытки;

   Возврат Результат;

КонецФункции

 


Тоже самое делаем и с X-отчет.
Модели кассовых машин, конечно, могут быть разные, поэтому тут нужен индивидуальный подход.

Осталось написать только еще одну процедуру, которая сохранить данные об отчетах ККМ. Выглядеть она может примерно так

Процедура ЗаписатьСобытиеККМОтчет(ЗначениеСчетчиков, НомерОтчета, ТипОтчета) Экспорт

   Форм = "ЧЦ=15; ЧДЦ=2; ЧН=-";

   СуммаПродаж = Формат(ЗначениеСчетчиков.СуммаПродаж , Форм);
   СуммаВнесений = Формат(ЗначениеСчетчиков.СуммаВнесений , Форм);
   СуммаИзъятий = Формат(ЗначениеСчетчиков.СуммаИзъятий , Форм);
   СуммаСкидок = Формат(ЗначениеСчетчиков.СуммаСкидок , Форм);
   СуммаВозвратов = Формат(ЗначениеСчетчиков.СуммаВозвратов , Форм);
   СуммаНаличных = Формат(ЗначениеСчетчиков.СуммаНаличных , Форм);
   НарастающийИтог = Формат(ЗначениеСчетчиков.НарастающийИтог , Форм);

   // Получим представление отчета
   ТекстОтчета = "     " + ?(ТипОтчета = Перечисления.АП_ТипыСобытийККМ.XОтчет, "X-ОТЧЕТ СВОДНЫЙ ", "Z-ОТЧЕТ ") + "№ " + НомерОтчета;
   ТекстОтчета = ТекстОтчета + Символы.ПС + "ПРОДАЖИ " + СуммаПродаж;
   ТекстОтчета = ТекстОтчета + ?(ЗначениеСчетчиков.СуммаВозвратов <> 0, Символы.ПС + "ВОЗВРАТЫ " + СуммаВозвратов, "");
   ТекстОтчета = ТекстОтчета + ?(ЗначениеСчетчиков.СуммаСкидок <> 0, Символы.ПС + "СКИДКИ " + СуммаСкидок, "");
   ТекстОтчета = ТекстОтчета + ?(ЗначениеСчетчиков.СуммаВнесений <> 0, Символы.ПС + "ВНЕСЕНИЕ НАЛИЧНЫХ " + СуммаВнесений, "");
   ТекстОтчета = ТекстОтчета + ?(ЗначениеСчетчиков.СуммаИзъятий <> 0, Символы.ПС + "ИЗЪЯТИЕ НАЛИЧНЫХ " + СуммаИзъятий, "");
   ТекстОтчета = ТекстОтчета + Символы.ПС + "CУММА НАЛИЧНЫХ " + СуммаНаличных;
   ТекстОтчета = ТекстОтчета + Символы.ПС + Символы.ПС + "НАРАСТАЮЩИЙ ИТОГ " + НарастающийИтог;

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

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

Ну и для разнообразия подкрасим строки при выводе в списке нашего регистра сведений

Если ДанныеСтроки.ТипСобытия = Перечисления.АП_ТипыСобытийККМ.Ошибка Или ДанныеСтроки.ТипСобытия = Перечисления.АП_ТипыСобытийККМ.ZОтчетОшибка Тогда
   ОформлениеСтроки.ЦветФона = Новый Цвет(225, 165, 165);
ИначеЕсли ДанныеСтроки.ТипСобытия = Перечисления.АП_ТипыСобытийККМ.Возврат Тогда
   ОформлениеСтроки.ЦветФона = Новый Цвет(175, 255, 175);
ИначеЕсли ДанныеСтроки.ТипСобытия = Перечисления.АП_ТипыСобытийККМ.XОтчетОшибка Тогда
   ОформлениеСтроки.ЦветФона = Новый Цвет(225, 195, 195);
ИначеЕсли ДанныеСтроки.ТипСобытия = Перечисления.АП_ТипыСобытийККМ.ZОтчет Тогда
   ОформлениеСтроки.ЦветФона = Новый Цвет(205, 255, 255);
ИначеЕсли ДанныеСтроки.ТипСобытия = Перечисления.АП_ТипыСобытийККМ.XОтчет Тогда
   ОформлениеСтроки.ЦветФона = Новый Цвет(240, 255, 255);
КонецЕсли;

В итоге мы получаем полную, ну или практически полную, картину того, что творят наши кассиры.

Осталось только сидеть и наслаждаться жизнью ;-)

События ККМ

Возвраты в событиях ККМ

Информация о Z-отчете

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

4 комментария к записи «Организация контроля за кассирами на удаленных точках в управлении торговлей 8.1»

  1. хорошее решение!
    только еще в 2000г. делали контроль за кассирами в супермаркете так, что в реальном времени отражалась нажатая кассиром клавиша или просканированный товар и плюс к этому видео камеры за спиной кассира. База на Oracle и хороший доступ в интернет позволял контролировать из Москвы действия кассира в Н.Новгороде. 😉 Это позволяло отследить одну из уловок кассира и его сообщника когда дорогостоящий товар проносится над штрихкодсканером создавая иллюзию пробития товара, но при этом не попадал в чек (например с отвернутым в сторону от сканера штрихкодом)

    • Ну у нас не гипермаркеты, поэтому такой тотальный контроль не требовался 🙄 Хотя…может быть лишним он и не был бы

  2. Эхх, где были мои глаза, месяц назад методом проб и ошибок сам ваял. Единственное, добавил (и вам рекомендую) — при закрытии смены формируется и сохраняется на диск отчет в mxl-е что-бы ст.кассиры закрывающие смену видели z-отчет целиком, с чеками и покупкам.

Добавить комментарий для alxd Отменить ответ

Ваш адрес email не будет опубликован. Обязательные поля помечены *