Данная статья является продолжение публикации «Наука о чеках. Глава 1. Есть только чек между прошлым и будущим», где приводилось описание (и пример) того, каким образом можно использовать накопленную информацию о чеках для анализа деятельности сторонних магазинов.
В данной работе речь пойдёт о том, как можно посмотреть структуру и значения параметров чека (или чеков) в json-файле, который можно получить, например, из бесплатного мобильного приложения ФНС «Проверка чеков ФНС России» (далее по тексту МП ФНС).
Что за задача?
Посмотреть структуру и значение параметров чека (или чеков) в json-файле с помощью типовых возможностей платформы 1С 8. Для этого необходимо создать внешнюю обработку.
Что дано?
Дан json-файл, который получается из МП ФНС. Параметры чека (чеков) в имеющемся json-файл могут быть самыми разными – это зависит как от способа получения json-файла из МП ФНС, так и параметров чеков, которые сначала контрольно-кассовые аппараты (ККТ) передали операторам фискальных данных (ОФД), а ОФД передало в ФНС.
Какие условия и ограничения?
- Так как структура чеков может отличаться (в зависимости от ККТ и ОФД), то необходимо динамически формировать структуру выводимой на форму таблицы.
- В целях достижения «кросс-конфигурационности» обработка должна самым минимальным образом использовать обращение к общим процедурам и модулям конфигурации 1С.
Какой порядок создания обработки?
1. Для начала создадим новую внешнюю обработку и добавим в ней самую обычную форму.
2. Откроем модуль формы и для начала добавим пару технических процедур (не забудьте процедуру ПриСозданииНаСервере связать с соответствующим событием на форме):
Область СлужебныеПроцедуры
#Область СлужебныеПроцедуры
&НаСервере
Функция ЧтоОбработатьИВернуть(ЧтоОбработать, ПараметрыЧтоОбработатьИВернуть = Неопределено)
ЭтаОбработка = РеквизитФормыВЗначение("Объект");
Если ЧтоОбработать = "ПрочитатьОтветВJSON" Тогда
ЭтоСоответствие = ПараметрыЧтоОбработатьИВернуть.ЭтоСоответствие;
Адрес = ПараметрыЧтоОбработатьИВернуть.Адрес;
Если Не ЭтоСоответствие Тогда
Возврат ЭтаОбработка.ПрочитатьОтветВJSON(Адрес,,,Истина,Истина);
Иначе
Возврат ЭтаОбработка.ПрочитатьОтветВJSON(Адрес,Истина,,Истина,Истина);
КонецЕсли;
ИначеЕсли ЧтоОбработать = "ЗагрузитьСтруктуруФайлаСЧеками" Тогда
СписокДокументов = ПараметрыЧтоОбработатьИВернуть.СписокДокументов;
ЭтоСоответствие = ПараметрыЧтоОбработатьИВернуть.ЭтоСоответствие;
ТЗПараметровЧеков = ЭтаОбработка.ЗагрузитьСтруктуруИзФайлаНаСервере(СписокДокументов,ЭтоСоответствие);
Возврат ТЗПараметровЧеков;
КонецЕсли;
КонецФункции
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
ДобавитьВозможностьПросмотраСтруктурыФайлаСЧеками();
КонецПроцедуры
#КонецОбласти
3. После этого продолжим работу с модулем формы и добавим две процедуры по работе со структурой файла с чеком (чеками):
Область СтруктураФайлаСЧеками
#Область СтруктураФайлаСЧеками
&НаКлиенте
Процедура ЗагрузитьСтруктуруФайлаСЧеками(Команда)
ДиалогВыбора = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
ДиалогВыбора.Фильтр = НСтр("ru = 'Файл с чеками'") + " (*.json)|*.json";
ДиалогВыбора.Заголовок = НСтр("ru = 'Выберите файл для загрузки чеков'");
ДиалогВыбора.ПредварительныйПросмотр = Истина;
ДиалогВыбора.Расширение = "json";
ДиалогВыбора.ИндексФильтра = 0;
ДиалогВыбора.ПроверятьСуществованиеФайла = Истина;
ДиалогВыбора.МножественныйВыбор = Ложь;
ОписаниеОповещения = Новый ОписаниеОповещения("ПоместитьФайлВоВременноеХранилищеСтруктуруФайлаСЧеками", ЭтаФорма);
НачатьПомещениеФайла(ОписаниеОповещения, ,ДиалогВыбора,Истина, УникальныйИдентификатор);
КонецПроцедуры
&НаКлиенте
Процедура ПоместитьФайлВоВременноеХранилищеСтруктуруФайлаСЧеками(ФайлВыбран, Адрес, ПолноеИмяВыбранногоФайла, ПараметрыОповещения) Экспорт
Если НЕ ФайлВыбран Тогда
Возврат;
КонецЕСли;
ИмяФайлаДанных = ОбщегоНазначенияКлиентСервер.РазложитьПолноеИмяФайла(ПолноеИмяВыбранногоФайла).Имя;
ЭтоСоответствие = Ложь;
Попытка
ПараметрыЧтоОбработатьИВернуть = Новый Структура("Адрес,ЭтоСоответствие",Адрес,Ложь);
СписокДокументов = ЧтоОбработатьИВернуть("ПрочитатьОтветВJSON", ПараметрыЧтоОбработатьИВернуть);
Исключение
ПараметрыЧтоОбработатьИВернуть = Новый Структура("Адрес,ЭтоСоответствие",Адрес,Истина);
СписокДокументов = ЧтоОбработатьИВернуть("ПрочитатьОтветВJSON", ПараметрыЧтоОбработатьИВернуть);
ЭтоСоответствие = Истина;
КонецПопытки;
ПараметрыЧтоОбработатьИВернуть = Новый Структура("СписокДокументов,ЭтоСоответствие",СписокДокументов,ЭтоСоответствие);
СоздатьТаблицуЗначенийПараметровЧеков(ПараметрыЧтоОбработатьИВернуть);
КонецПроцедуры
#КонецОбласти
4. Не будем останавливаться на достигнутом и добавим несколько процедур для работы с динамическим интерфейсом (согласно ранее заявленным условиям):
Область РаботаСДинамическимИнтерфейсом
#Область РаботаСДинамическимИнтерфейсом
&НаСервере
Процедура ДобавитьВозможностьПросмотраСтруктурыФайлаСЧеками()
НовКоманда = ЭтаФорма.Команды.Добавить("ЗагрузитьСтруктуруФайлаСЧеками");
НовКоманда.Действие = "ЗагрузитьСтруктуруФайлаСЧеками";
нЭлемент = ЭтаФорма.Элементы.Добавить("ЗагрузитьСтруктуруФайлаСЧеками", Тип("КнопкаФормы"), );
нЭлемент.Заголовок = "Загрузить файл с чеками (для просмотра его структуры)";
нЭлемент.ИмяКоманды = "ЗагрузитьСтруктуруФайлаСЧеками";
КонецПроцедуры
&НаСервере
Процедура СоздатьТаблицуЗначенийПараметровЧеков(ПараметрыЧтоОбработатьИВернуть)
МассивУдаляемыхРеквизитов = Новый Массив;
Попытка
ИмяУжеЗанято = Элементы.ТаблицаЗначенийПараметровЧеков;
МассивУдаляемыхРеквизитов.Добавить("ТаблицаЗначенийПараметровЧеков");
Исключение
Попытка
ИмяУжеЗанято = Элементы.ТаблицаЗначенийПараметровЧеков;
МассивУдаляемыхРеквизитов.Добавить("ТаблицаЗначенийПараметровЧеков");
Исключение
//надо создавать таблицу
КонецПопытки;
КонецПопытки;
Если МассивУдаляемыхРеквизитов.Количество() > 0 Тогда
ЭтаФорма.Элементы.Удалить(Элементы["ТаблицаЗначенийПараметровЧеков"]);
КонецЕсли;
ТЗПараметровЧеков = ЧтоОбработатьИВернуть("ЗагрузитьСтруктуруФайлаСЧеками",ПараметрыЧтоОбработатьИВернуть);
МассивДобавляемыхРеквизитов = Новый Массив;
//добавить реквизит ТЗ
МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы("ТаблицаЗначенийПараметровЧеков", Новый ОписаниеТипов("ТаблицаЗначений")));
Для Каждого Колонка Из ТЗПараметровЧеков.Колонки Цикл
МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы(Колонка.Имя, Новый ОписаниеТипов("Строка"),"ТаблицаЗначенийПараметровЧеков" ,Колонка.Имя));
КонецЦикла;
ИзменитьРеквизиты(МассивДобавляемыхРеквизитов,МассивУдаляемыхРеквизитов);
ВывестиТаблицуЗначенийПараметровЧеков(ТЗПараметровЧеков);
КонецПроцедуры
&НаСервере
Процедура ВывестиТаблицуЗначенийПараметровЧеков(ТЗПараметровЧеков)
ВывестиТаблицуЗначенийПараметровЧековНаФорму(ЭтаФорма, "ТаблицаЗначенийПараметровЧеков", , "Таблица значений параметров чеков",ТЗПараметровЧеков);
КонецПроцедуры
&НаСервере
Процедура ВывестиТаблицуЗначенийПараметровЧековНаФорму(Форма, ИмяТаблицы, Родитель, ЗаголовокТаблицы, ТЗПараметровЧеков) Экспорт
ТаблицаФормы = Форма.Элементы.Добавить(ИмяТаблицы, Тип("ТаблицаФормы"), Родитель);
ТаблицаФормы.ПутьКДанным = ИмяТаблицы;
ТаблицаФормы.ПоложениеЗаголовка = ПоложениеЗаголовкаЭлементаФормы.Нет;
ТаблицаФормы.ИзменятьПорядокСтрок = Ложь;
ТаблицаФормы.ИзменятьСоставСтрок = Ложь;
ТаблицаФормы.РазрешитьНачалоПеретаскивания = Ложь;
Для Каждого Колонка Из ТЗПараметровЧеков.Колонки Цикл
ДобавитьНовуюКолонкуТаблицыЗначенийПараметровЧеков(Форма, ТаблицаФормы, ИмяТаблицы, Колонка.Имя);
КонецЦикла;
Форма.ТаблицаЗначенийПараметровЧеков.Загрузить(ТЗПараметровЧеков);
КонецПроцедуры
&НаСервере
Процедура ДобавитьНовуюКолонкуТаблицыЗначенийПараметровЧеков(Форма, ТаблицаФормы, ИмяТаблицы, ИмяКолонки, СтруктураСвойств = Неопределено)
НоваяКолонка = Форма.Элементы.Добавить(ИмяТаблицы+ИмяКолонки, Тип("ПолеФормы"), ТаблицаФормы);
НоваяКолонка.Заголовок = ИмяКолонки;
НоваяКолонка.ПутьКДанным = ИмяТаблицы + "." + ИмяКолонки;
НоваяКолонка.Вид = ВидПоляФормы.ПолеВвода;
Если НЕ СтруктураСвойств = Неопределено Тогда
Для Каждого КлючЗначение Из СтруктураСвойств Цикл
Попытка
НоваяКолонка[КлючЗначение.Ключ] = СтруктураСвойств[КлючЗначение.Ключ];
Исключение
КонецПопытки;
КонецЦикла;
КонецЕсли;
КонецПроцедуры
#КонецОбласти
5. После этого откроем общий модуль обработки и добавим пару областей с необходимыми процедурами. Сначала создадим две процедуры по работе с json-файлом:
#Область РаботаСJSON
Функция ПрочитатьОтветВJSON(ТекОтветJSON, ПрочитатьВСоответствие = Ложь, ТекОписаниеОшибки = "", ПрочитатьИзФайла = Ложь, ЭтоСсылкаНаВременныйФайл = Ложь) Экспорт
Если ЭтоСсылкаНаВременныйФайл Тогда
//это адрес во временном хранилище
ВременныйФайлСЧеками = ПолучитьИмяВременногоФайла("json");
ДвоичныеДанные = ПолучитьИзВременногоХранилища(ТекОтветJSON);
ДвоичныеДанные.Записать(ВременныйФайлСЧеками);
ТекОтветJSON = ВременныйФайлСЧеками;
КонецЕсли;
Попытка
ЧтениеJSON = Новый ЧтениеJSON;
Если ПрочитатьИзФайла Тогда
ЧтениеJSON.ОткрытьФайл(ТекОтветJSON);
Иначе
ЧтениеJSON.УстановитьСтроку(ТекОтветJSON);
КонецЕсли;
Структура = ПрочитатьJSON(ЧтениеJSON, ПрочитатьВСоответствие);
ЧтениеJSON.Закрыть();
Если ЭтоСсылкаНаВременныйФайл Тогда
УдалитьФайлы(ТекОтветJSON);
КонецЕсли;
Возврат Структура;
Исключение
Сообщение = Новый СообщениеПользователю();
Сообщение.Текст = ТекОписаниеОшибки + Символы.ПС + ОписаниеОшибки();
Сообщение.Сообщить();
Если ЭтоСсылкаНаВременныйФайл Тогда
УдалитьФайлы(ТекОтветJSON);
КонецЕсли;
Возврат Неопределено;
КонецПопытки;
КонецФункции
Функция УстановкаJSON(ТекСтруктура, ПараметрЗаписи = "") Экспорт
ЗаписьJSON = Новый ЗаписьJSON;
Если ПараметрЗаписи = "" Тогда
ЗаписьJSON.УстановитьСтроку();
Иначе
ЗаписьJSON.УстановитьСтроку(ПараметрЗаписи);
КонецЕсли;
ЗаписатьJSON(ЗаписьJSON,ТекСтруктура,Новый НастройкиСериализацииJSON,"ПреобразованиеJSON");
Возврат ЗаписьJSON.Закрыть();
КонецФункции
#КонецОбласти
6. После этого добавим четыре процедуры по работе с самой структурой чека (чеков):
#Область РаботаСоСтруктуройЧеков
Функция ПолучитьЧтоЭтоНаСервере(СписокДокументов)
ЧтоЭто = "";
Если ТипЗнч(СписокДокументов) = ТипЗнч(Новый Массив) Тогда
//это массив чеков
Если СписокДокументов.Количество() > 0 Тогда
Если СписокДокументов[0].Свойство("ticket") Тогда
//1)
ЧтоЭто = "Выписка";
Иначе
//2)
ЧтоЭто = "СписокЧеков";
КонецЕсли;
Иначе
//массив пустой
КонецЕсли;
Иначе
Если ТипЗнч(СписокДокументов) = Тип("Соответствие") Тогда
//3)
ЧтоЭто = "АйосОтдельныйЧек";
Иначе
//4)
ЧтоЭто = "ОтдельныйЧек";
КонецЕсли;
КонецЕсли;
Возврат ЧтоЭто;
КонецФункции
Процедура ВывестиОтветСервера (ОбъектВывода)
Если ТипЗнч(ОбъектВывода) = Тип("Строка") Тогда
ОтветСервера = ОбъектВывода;
ИначеЕсли ТипЗнч(ОбъектВывода) = Тип("Массив") или ТипЗнч(ОбъектВывода) = Тип("Структура") Тогда
ПараметрЗаписи = Новый ПараметрыЗаписиJSON(," ");
ОтветСервера = УстановкаJSON(ОбъектВывода, ПараметрЗаписи);
КонецЕсли;
КонецПроцедуры
Процедура ЗагрузитьИзФайлаНаСервере(СписокДокументов, ЭтоСоответствие = Ложь, ОчищатьСписокЧеков = Истина,ТЗПараметровЧеков = Неопределено) Экспорт
НадоПолучитьСтруктуруФайла = Ложь;
Если Тип(ТЗПараметровЧеков) = Тип("ТаблицаЗначений") Тогда
НадоПолучитьСтруктуруФайла = Истина;
КонецЕсли;
Если СписокДокументов = Неопределено Тогда
Возврат;
КонецЕсли;
ВывестиОтветСервера(СписокДокументов);
ЧтоЭто = ПолучитьЧтоЭтоНаСервере(СписокДокументов);
Если ЗначениеЗаполнено(ЧтоЭто) Тогда
Для Каждого Чек из СписокДокументов Цикл
Если ЧтоЭто = "Выписка" Тогда
ПолныйЧек = Чек.ticket.document.receipt;
ИначеЕсли ЧтоЭто = "СписокЧеков" Тогда
ПолныйЧек = Чек;
ИначеЕсли ЧтоЭто = "АйосОтдельныйЧек" Тогда
ПолныйЧек = Чек;
ИначеЕсли ЧтоЭто = "ОтдельныйЧек" Тогда
ПолныйЧек = СписокДокументов;
КонецЕсли;
Если ЭтоСоответствие Тогда
Иначе
Если НадоПолучитьСтруктуруФайла Тогда
НовСтрока = ТЗПараметровЧеков.Добавить();
Для Каждого ТекСвойство Из ПолныйЧек Цикл
Если ТЗПараметровЧеков.Колонки.Найти(ТекСвойство.Ключ) = Неопределено Тогда
ТЗПараметровЧеков.Колонки.Добавить(ТекСвойство.Ключ);
НовСтрока[ТекСвойство.Ключ] = ТекСвойство.Значение;
Иначе
НовСтрока[ТекСвойство.Ключ] = ТекСвойство.Значение;
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Иначе
Сообщение = Новый СообщениеПользователю();
Сообщение.Текст = "Загружаемый файл не содержит данных в формате чеков ФНС!";
Сообщение.Сообщить();
КонецЕсли;
КонецПроцедуры
Функция ЗагрузитьСтруктуруИзФайлаНаСервере(СписокДокументов,ЭтоСоответствие) Экспорт
ТЗПараметровЧеков = Новый ТаблицаЗначений;
ЗагрузитьИзФайлаНаСервере(СписокДокументов,ЭтоСоответствие,Ложь,ТЗПараметровЧеков);
Возврат ТЗПараметровЧеков;
КонецФункции
#КонецОбласти
Что получилось?
В результате у нас получится обработка, которая позволяет открывать и просматривать структуру чеков из json-файла, например такого:
Или такого:
Примечание:
1. Что касается обещанной «кросс-конфигурационности», то полученная обработка использует только одно обращение к общему модулю (которое при необходимости, можно убрать):
ИмяФайлаДанных = ОбщегоНазначенияКлиентСервер.РазложитьПолноеИмяФайла(ПолноеИмяВыбранногоФайла).Имя;
2. Вы можете самостоятельно создать обработку для просмотра структуры и значений чеков. Для этого вам помогут приведенные в данной публикации модули. Или можете скачать уже готовую обработку, приложенную к данной публикации.
Разработка и тестирование обработки осуществлялись на типовой конфигурации «1С:Бухгалтерия предприятия, редакция 3.0 (3.0.111.25)» и платформе 1С:Предприятие 8.3 (8.3.18.1483). Работать данная обработка должна практически на любой управляемой конфигурации 1С на любой современной платформе 1С 8.