Свой кусочек информационного пирога по 54-ФЗ – обработка для получения данных о своих покупках по чекам ОФД (на примере сети магазинов «Лента»)




Новшества по 54-ФЗ в области новой ККТ и все новые аббревиатуры из двух и трех букв (ОФД, ФН, ФД и т.д.) – одни из главных событий в торговой отрасли 2024-2024-2024 гг. Несмотря на всю сложность выполнения новых обязательных требований для магазинов, для рядовых пользователей появляется возможность автоматизировать получение данных о своих покупках от Операторов фискальных данных (ОФД). Вашему вниманию предлагается несколько вариантов использования данных со своих онлайн-чеков (на примере получения данных о покупках в магазинах «Лента»).

Новшества по 54-ФЗ в области новой ККТ и все новые аббревиатуры из двух и трех букв (ОФД, ФН, ФД и т.д.) – одни из главных событий в торговой отрасли 2024-2024-2024 гг. Несмотря на всю сложность выполнения новых обязательных требований для магазинов, для рядовых пользователей появляется возможность автоматизировать получение данных о своих покупках от Операторов фискальных данных (ОФД).

Государство ввело QR-код в чеке как инструмент гражданского контроля. Можно скачать бесплатное мобильное приложение «Проверка кассового чека в ФНС России», отсканировать QR-код и проверить чек на корректность. Однако, практической пользы для накопления данных о своих покупках с последующим их анализом, мобильное приложение ФНС пока предоставляет в очень ограниченном объеме. Кроме того, остается вопрос сохранения относительной приватности покупателя, так как для получения возможностей сохранения чеков в своем личном кабинете необходимо зарегистрироваться с предоставлением своих персональных данных.

Вашему вниманию предлагается несколько вариантов использования данных со своих онлайн-чеков (на примере получения данных о покупках в магазинах «Лента»):

— внешняя обработка для получения данных о покупках по ФПД (фискальному признаку документа) и сумме чека. Теоретические обработка должна работать в любой конфигурации на управляемых формах, у которой режим совместимости позволяет работать с методами типа «СтрНайти». Открывается через меню «Файл»

— мобильное приложение на платформе 1С с возможностью сканирования QR-кодов и загрузки данных в мобильное приложение с последующим анализом на количество и сумму покупок по товарам. Мобильное приложение скомпилировано для ОС Android (хотя приложение проверялась на разных версиях системы Android 3-4-5-6, однако, возможна ситуация, когда приложение не будет работать). Разработанное мобильное приложение никуда не передает данные и не обменивается с другими базами данных, приложениями или программами. Для загрузки онлайн-чеков не используются какие-либо персональные данные пользователя. Все накопленные данные являются исключительно собственностью пользователя мобильного приложения. Поэтому вся ответственность за сохранностью и целостностью данных лежит исключительно на пользователе мобильного приложения. Если у вас не получается загрузить мобильное приложением, то попробуйте его скачать с облака ТПУ по ссылке: https://filecloud.tpu.ru/index.php/s/ejfo64JndsC78HV

— инструкция по работе с мобильным приложением. Инструкцию по работе с мобильным приложением можно также скачать по ссылке: https://filecloud.tpu.ru/index.php/s/sBQ9JmHxLYMKp3p

Ниже приведены исходные модули внешней обработки. Настоящая статья написана в поддержку доклада «Народный big data или 54-ФЗ на службе анализа и планирования для рядовых покупателей» на конференции INFOSTART EVENT 2024 COMMUNITY (прямая ссылка на доклад: http://event.infostart.ru/2017/agenda/#item644121) Если доклад пройдет в итоговый список выступлений, то будет также выложены исходные модули мобильного приложения.

Модули формы внешней обработки:


&НаСервере
Процедура ЗагрузитьЧекНаСервере()

ТекОбъект = РеквизитФормыВЗначение("Объект");

URL = СокрЛП(Объект.URLЧека);
ОтправляемаяКомандаНаФокус = URL;

URLРазделенный = ТекОбъект.РазделитьURL(URL);
Таймаут = 5;

ИмяСервера           = URLРазделенный.ИмяСервера;
ПутьКФайлуНаСервере  = URLРазделенный.ПутьКФайлуНаСервере;
Протокол             = URLРазделенный.Протокол;
Порт = Неопределено;
ЗащищенноеСоединение = Неопределено;
Если Протокол = "https" Тогда
ЗащищенноеСоединение = Новый ЗащищенноеСоединениеOpenSSL;
КонецЕсли;
ПолнаяСтруктураURL = ТекОбъект.СтруктураURI(URL);
Если Не ПустаяСтрока(ПолнаяСтруктураURL.Порт) Тогда
ИмяСервера = ПолнаяСтруктураURL.Хост;
Порт = ПолнаяСтруктураURL.Порт;
КонецЕсли;

Прокси = ТекОбъект.ПолучитьПрокси(Протокол);

Попытка
Соединение = Новый HTTPСоединение(ИмяСервера, Порт, , , Прокси, Таймаут, ЗащищенноеСоединение);
Исключение
ИнформацияОбОшибке = ИнформацияОбОшибке();
СообщениеОбОшибке = НСтр("ru = 'Ошибка при создании HTTP-соединения с сервером %1:'") + Символы.ПС + "%2";
ВызватьИсключение СообщениеОбОшибке;
КонецПопытки;

Заголовки = Новый Соответствие;
HTTPЗапрос = Новый HTTPЗапрос(ПутьКФайлуНаСервере, Заголовки);

HTTPОтвет = Соединение.Получить(HTTPЗапрос);
ФайлОтвета = HTTPОтвет.ПолучитьТелоКакСтроку();
HTTPОтвет = ТекОбъект.СтроковыеФункцииКлиентСерверИзвлечьТекстИзHTML(ФайлОтвета);
КоличествоТоваровВЧеке = СтрЧислоВхождений(HTTPОтвет,"vmb_i67bc30956372029315_vblock")/13 - 1;

Для НомерТовара = 0 По КоличествоТоваровВЧеке Цикл

ПоправкаНаДлинуНомераСтроки = 0;
Если НомерТовара >= 10 Тогда
ПоправкаНаДлинуНомераСтроки = 1;
КонецЕсли;

ТекНачалоСтрокиТовара = СтрНайти(HTTPОтвет,"vmb_i67bc30956372029315_vblock"+СокрЛП(НомерТовара),,,2);
Если ТекНачалоСтрокиТовара = 0 Тогда
Продолжить;
КонецЕсли;

НачалоСтрокиТовара = ТекНачалоСтрокиТовара + 54 + ПоправкаНаДлинуНомераСтроки;
ОкончаниеСтрокиТовара = СтрНайти(HTTPОтвет,"</span>",,НачалоСтрокиТовара);
ДлинаНазванияТовара = ОкончаниеСтрокиТовара - НачалоСтрокиТовара;
НазваниеТовара = Сред(HTTPОтвет,НачалоСтрокиТовара,ДлинаНазванияТовара);

//количество
НачалоСтрокиКоличества = СтрНайти(HTTPОтвет,"vmb_i67bc30956372029315_vblock"+СокрЛП(НомерТовара),,,3) + 54 + ПоправкаНаДлинуНомераСтроки;
ОкончаниеСтрокиКоличества = СтрНайти(HTTPОтвет,"</span>",,НачалоСтрокиКоличества);
ДлинаКоличества = ОкончаниеСтрокиКоличества - НачалоСтрокиКоличества;
Количество = Сред(HTTPОтвет,НачалоСтрокиКоличества,ДлинаКоличества);

//цена
НачалоСтрокиЦены = СтрНайти(HTTPОтвет,"vmb_i67bc30956372029315_vblock"+СокрЛП(НомерТовара),,,4) + 54 + ПоправкаНаДлинуНомераСтроки;
ОкончаниеСтрокиЦены = СтрНайти(HTTPОтвет,"</span>",,НачалоСтрокиЦены);
ДлинаЦены = ОкончаниеСтрокиЦены - НачалоСтрокиЦены;
Цена = Сред(HTTPОтвет,НачалоСтрокиЦены,ДлинаЦены);

//сумма
НачалоСтрокиСуммы = СтрНайти(HTTPОтвет,"vmb_i67bc30956372029315_vblock"+СокрЛП(НомерТовара),,,9) + 55 + ПоправкаНаДлинуНомераСтроки;
ОкончаниеСтрокиСуммы = СтрНайти(HTTPОтвет,"</span>",,НачалоСтрокиСуммы);
ДлинаСуммы = ОкончаниеСтрокиСуммы - НачалоСтрокиСуммы;
Сумма = Сред(HTTPОтвет,НачалоСтрокиСуммы,ДлинаСуммы);

НоваяСтрока = Объект.Покупки.Добавить();
НоваяСтрока.НазваниеТовара = НазваниеТовара;
НоваяСтрока.Количество = Количество;
НоваяСтрока.Цена = Цена;
НоваяСтрока.Сумма = Сумма;

КонецЦикла;

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

&НаКлиенте
Процедура ЗагрузитьЧек(Команда)

Объект.Покупки.Очистить();
СразуСсылкаНаЧек = "http://receipt.taxcom.ru/v01/show?fp=" + СокрЛП(Объект.ФПД) + "&s=" + СокрЛП(Формат(Объект.СуммаПокупки,"ЧГ=0")) + "&sf=False&sfn=False";
Объект.URLЧека = СразуСсылкаНаЧек;
ЗагрузитьЧекНаСервере();

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

&НаКлиенте
Процедура ПерейтиПоСсылке(Команда)
ЗапуститьПриложение(Объект.URLЧека);
КонецПроцедуры

Модули объекта внешней обработки:

Функция РазделитьURL(URL) Экспорт

Результат = ПолучениеФайловИзИнтернетаКлиентСервер.РазделитьURL(URL);

Если Результат.Свойство("ПустьКФайлуНаСервере") Тогда
Результат.Вставить("ПутьКФайлуНаСервере", Результат.ПустьКФайлуНаСервере);
КонецЕсли;
Возврат Результат;

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

Функция СтруктураURI(СтрокаURI) Экспорт

Возврат ОбщегоНазначенияКлиентСервер.СтруктураURI(СтрокаURI);

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

Функция ПолучитьПрокси(Протокол) Экспорт

Попытка
Возврат ПолучениеФайловИзИнтернетаКлиентСервер.ПолучитьПрокси(Протокол);
Исключение
Возврат Неопределено;
КонецПопытки;

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

Процедура УстановитьHTTPЗаголовки(Заголовки) Экспорт

Куки = Новый Соответствие;
Куки.Вставить("new-int-rel", "1");
Куки.Вставить("expires", Формат(ДобавитьМесяц(ТекущаяДата(), 1), "Л=en_US; ДФ='ddd, dd-MMM-yyyy H:mm:ss G""MT'"));
Куки.Вставить("path", "/");
СтрКуки = "";
Для Каждого Кука Из Куки Цикл
СтрКуки = СтрКуки + "; " + Кука.Ключ + "=" + КодироватьСтроку(Кука.Значение, СпособКодированияСтроки.КодировкаURL);
КонецЦикла;

СтрКуки = Сред(СтрКуки, 3);

Заголовки.Вставить("Cookie", СтрКуки);

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

Функция СтроковыеФункцииКлиентСерверИзвлечьТекстИзHTML(Знач ИсходныйТекст) Экспорт
Результат = "";

Текст = НРег(ИсходныйТекст);

Позиция = Найти(Текст, "<body");
Если Позиция > 0 Тогда
Текст = Сред(Текст, Позиция + 5);
ИсходныйТекст = Сред(ИсходныйТекст, Позиция + 5);
Позиция = Найти(Текст, ">");
Если Позиция > 0 Тогда
Текст = Сред(Текст, Позиция + 1);
ИсходныйТекст = Сред(ИсходныйТекст, Позиция + 1);
КонецЕсли;
КонецЕсли;

Позиция = Найти(Текст, "</body>");
Если Позиция > 0 Тогда
Текст = Лев(Текст, Позиция - 1);
ИсходныйТекст = Лев(ИсходныйТекст, Позиция - 1);
КонецЕсли;

ПозицияНачалаУдаления = стрНайти(Текст, "ttc_multiple_block_panel",,1);
ДлинаТекста = СтрДлина(Текст);

ВтораяЧастьТекста = Прав(Текст,ДлинаТекста - ПозицияНачалаУдаления - 29);
ИсходныйТекст = ВтораяЧастьТекста;

Результат = Результат + ИсходныйТекст;

Возврат СокрЛП(Результат);

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

Данная статья не претендует на универсальность в загрузке данных чеков с ОФД. Автор хотел лишь поделиться своим мнением и некоторыми наработками в вопросе работы с доступными данными с сайтов ОФД.

9 Comments

  1. maxx

    Интересно

    Сейчас почти наладили взаимодействие Битрикс + Касса Атол на ферме касс с последующи сброса чека в базу 1с

    А откуда информацию берете о сервисах по получению информации о чеках?

    Reply
  2. delta

    (1) В данном случае данные о сервисе берутся непосредственно с сервиса проверки чеков оператора фискальных данных Такском (http://receipt.taxcom.ru/) с передачей всех необходимых для проверки чеков для этого ОФД параметров: фискального признака документа и суммы чека.

    В обработке это строка кода:

    СразуСсылкаНаЧек = «http://receipt.taxcom.ru/v01/show?fp=» + СокрЛП(Объект.ФПД) + «&s=» + СокрЛП(Формат(Объект.СуммаПокупки,»ЧГ=0″))

    Reply
  3. maxx

    (2)т.е. у всех ОФД буду открытые сервисы для программного подключения и вытаскивания информации о чеках (а не просто формочка на их сайте) ?

    Reply
  4. delta

    (3) Этот вопрос к каждому ОФД в отдельности. На сайтах ОФД про такого рода возможности ничего не написано. Лично мы в своих исследованиях из 10 действующих на текущий момент ОФД возможность получения данных по чекам нашли только у троих. Частично этот вопрос раскрывается в моем докладе на конференции Инфостарт-2017 (http://event.infostart.ru/2017/agenda/#item644121).

    Reply
  5. olegmedvedev

    (4)а каких ОФД не подскажите? первый ОФД(https://www.1-ofd.ru/) там есть?

    Reply
  6. delta

    (5) Нет, с Первого ОФД автоматически данные получать пока не получается. Данные удается пока получать только с ЭВОТОРа, ПЕТЕР-СЕРВИС Спецтехнологии и Такскома. Но даже во всех этих случаях есть свои ньюансы и сложности.

    Reply
  7. Mobsted

    А в каком формате чек возвращается?

    Reply
  8. delta

    (7) Для приведенного примера работы с ОФД Такском формат чека XML. Некоторые ОФД возвращают в формате JSON.

    Reply
  9. delta

    Нашел на gihube интересный код для получения данных онлайн-чека с Первого ОФД, нужно только помочь перевести его часть кода с Python на язык 1С (вставил сюда только самую значимую часть кода:

    url_first_get = «https://consumer.1-ofd.ru/#/landing»
    url_receipt_get = «https://consumer.1-ofd.ru/api/tickets/ticket/{}»
    url_receipt_find = «https://consumer.1-ofd.ru/api/tickets/find-ticket»
    
    def is_suitable(self, data): //функция по возврату булевых данных
    return data[‘fiscal_drive_id’] and data[‘fiscal_id’] and data[‘fiscal_document_number’] and not data[‘kkt’]
    
    def search(self): //self — сама на себя
    print(«Search in ofd1…»)
    
    ofd1_payload = {
    «fiscalDocumentNumber»: self.fiscal_document_number,
    «fiscalDriveId»:  self.fiscal_drive_id,
    «fiscalId»:    self.fiscal_id
    }
    # fix for single quotes server error
    ofd1_payload = json.dumps(ofd1_payload, sort_keys=True)
    
    session = requests.Session()
    session.get(self.url_first_get)
    
    session.headers.update({
    ‘Content-Type’: ‘application/json’,  # fix 415 error
    ‘X-XSRF-TOKEN’: session.cookies.get_dict()[‘XSRF-TOKEN’],
    ‘User-Agent’: ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36’
    })
    
    # print(session.headers)
    # print(session.cookies.get_dict())
    # cookies = session.cookies.get_dict().copy()
    # cookies.update({
    #  ‘PLAY_LANG’: ‘ru’
    # })
    # print(cookies)
    
    ofd1 = session.post(self.url_receipt_find, data=ofd1_payload)
    
    if (ofd1.status_code == 200):
    answer = ofd1.json()
    status = answer[«status»]
    self.receipt_id = answer[«uid»]
    
    print(«Getting the receipt…»)
    ofd1 = requests.get(self.url_receipt_get.format(self.receipt_id))
    
    if (ofd1.status_code == 200):
    self.raw = json.dumps(
    ofd1.json(), ensure_ascii=False).encode(‘utf8’)
    self.receipt_data = json.loads(self.raw)
    
    filename = self.get_receipt_file_name()
    
    if not os.path.exists(filename):
    with open(filename, ‘w’) as outfile:
    outfile.write(self.raw)
    else:
    print(«Receipt already saved!»)
    if not self.resend:
    print(«Skipping…»)
    return False
    return True
    else:
    print(«Error {} while getting receipt from ofd1!».format(
    ofd1.status_code))
    if config.debug:
    print(ofd1.text)
    
    elif (ofd1.status_code == 404):
    print(«Not found!»)
    
    else:
    print(«Error {} while searching in ofd1!».format(ofd1.status_code))
    if config.debug:
    print(ofd1.text)
    
    return False
    

    Показать

    Reply

Leave a Comment

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