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

Обмен - Обмен с другими системами

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

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

Государство ввело 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 2017 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);
	ИсходныйТекст = ВтораяЧастьТекста;
	
	Результат = Результат + ИсходныйТекст;

	Возврат СокрЛП(Результат);
	
КонецФункции

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

Скачать файлы

Наименование Файл Версия Размер
Внешняя обработка для получения данных о покупках по ФПД и сумме чека
.epf 11,18Kb
11.07.17
2
.epf 11,18Kb 2 Скачать
Мобильное приложение на платформе 1С с возможностью сканирования QR-кодов и загрузки данных в мобильное приложение
.apk 27,93Mb
11.07.17
0
.apk 27,93Mb Скачать
Инструкция по работе с мобильным приложением
.pdf 940,18Kb
11.07.17
0
.pdf 940,18Kb Скачать

См. также

Вознаграждение за ответ
Показать полностью
Комментарии
1. Максим Гончаров (maxx) 608 11.07.17 22:47 Сейчас в теме
Интересно
Сейчас почти наладили взаимодействие Битрикс + Касса Атол на ферме касс с последующи сброса чека в базу 1с

А откуда информацию берете о сервисах по получению информации о чеках?
2. Андрей Важдаев (delta) 274 12.07.17 03:41 Сейчас в теме
(1) В данном случае данные о сервисе берутся непосредственно с сервиса проверки чеков оператора фискальных данных Такском (http://receipt.taxcom.ru/) с передачей всех необходимых для проверки чеков для этого ОФД параметров: фискального признака документа и суммы чека.
В обработке это строка кода:
СразуСсылкаНаЧек = "http://receipt.taxcom.ru/v01/show?fp=" + СокрЛП(Объект.ФПД) + "&s=" + СокрЛП(Формат(Объект.СуммаПокупки,"ЧГ=0"))
3. Максим Гончаров (maxx) 608 12.07.17 09:41 Сейчас в теме
(2)т.е. у всех ОФД буду открытые сервисы для программного подключения и вытаскивания информации о чеках (а не просто формочка на их сайте) ?
4. Андрей Важдаев (delta) 274 12.07.17 10:04 Сейчас в теме
(3) Этот вопрос к каждому ОФД в отдельности. На сайтах ОФД про такого рода возможности ничего не написано. Лично мы в своих исследованиях из 10 действующих на текущий момент ОФД возможность получения данных по чекам нашли только у троих. Частично этот вопрос раскрывается в моем докладе на конференции Инфостарт-2017 (http://event.infostart.ru/2017/agenda/#item644121).
5. Олег Медведев (olgerd666) 38 12.07.17 10:12 Сейчас в теме
(4)а каких ОФД не подскажите? первый ОФД(https://www.1-ofd.ru/) там есть?
6. Андрей Важдаев (delta) 274 12.07.17 13:22 Сейчас в теме
(5) Нет, с Первого ОФД автоматически данные получать пока не получается. Данные удается пока получать только с ЭВОТОРа, ПЕТЕР-СЕРВИС Спецтехнологии и Такскома. Но даже во всех этих случаях есть свои ньюансы и сложности.
7. mobsted@mobsted.com (Mobsted) 12.07.17 14:18 Сейчас в теме
А в каком формате чек возвращается?
8. Андрей Важдаев (delta) 274 12.07.17 17:35 Сейчас в теме
(7) Для приведенного примера работы с ОФД Такском формат чека XML. Некоторые ОФД возвращают в формате JSON.
9. Андрей Важдаев (delta) 274 19.07.17 07:43 Сейчас в теме +10 $m
Нашел на 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
...Показать Скрыть
Оставьте свое сообщение