gifts2017

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

Опубликовал Александр Шкураев (salexdv) в раздел Программирование - Практика программирования

Вот решил поделиться своим опытом - может кому пригодится :)

Проблема стояла следующая:

Есть более десяти удаленных магазинов (распределенная Управление торговлей),

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

Картина: Кассир FULL CONTROL

Создаем регистр сведений (периодичность - секунда)
    Измерения:
            - КассаККМ (Справочник "Кассы ККМ");
    Ресурсы:
            - ТипСобытия (Перечисление "Типы событий ККМ");
            - Событие (Строка)

Состав регистра сведений

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

Перечисление "Типы событий ККМ" может принимать следующие значения:
    - Продажа;
    - Ошибка;
    - 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);   
        КонецЕсли;



В итоге мы получаем полную, ну или практически полную, картину того, что творят наши кассиры.
Осталось только сидеть и наслаждаться жизнью Smile

Запись о начале пробития чека ККМ

Расшифровка Z-отчета

Список событий ККМ

Очень надеюсь, что хоть кому-нибудь моя статья поможет Smile

http://shkuraev.ru

См. также

Подписаться Добавить вознаграждение

Комментарии

1. Александр Герасимов (AleksGlbux) 24.07.09 07:17
2. Alxd (salexdv) 24.07.09 07:22
3. Александр Окулов (PowerBoy) 24.07.09 08:13
8. Alxd (salexdv) 28.07.09 20:53
Спасибо! Жизнь заставляет :)
9. Сергей Огородников (Serg O.) 03.12.09 14:05
Классная статья...
предлагаю добавить еще одно измерение в регистр - Кассир (тип Справочники.Пользователи),

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

так как на одной кассе может работать (в разные дни или в один день!) несколько кассиров (продавцов).
10. Alxd (salexdv) 03.12.09 17:49
(9) Все в твоих руках :) У меня просто много-много точек, а кассир на каждой только один
11. Евгений Левченко (MYRZILKA123) 14.11.11 11:22
прикольно буду пробывать )))
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа