С 1 июля 2017 года на территории Российской Федерации стало обязательным использование контрольно-кассовых машин (ККМ), отправляющих все свои транзакции прямо в Федеральную налоговую службу. Так называемых онлайн-касс. Введение этого новшества уже успели обсудить со всех сторон или по крайней мере со всех серьезных сторон. Могут ли у федерального закона быть не слишком серьезные стороны и какой простор для творчества это нам дает – об этом под катом.
Все ККМ теперь сообщают в ФНС о наших транзакциях, а ФНС, желая убедиться в честности каждой онлайн-кассы, выпустила специальное мобильное приложение (Google Play, App Store), которое позволяет пользователю проверить любой предоставленный ему чек и сообщить в компетентные органы, если проверка завершилась неудачей.
Когда я слышу о том, что кто-то начинает собирать большие объемы информации, я думаю не о потенциальной прибыли от этого и не о затратах на внедрение. И даже не о том, что Большой брат наблюдает. У меня в голове сразу рисуются таблички, которые можно фильтровать и джойнить, кубики, которые можно вертеть, диаграммки, которые можно строить и т.д. Можем ли мы как-то приклеиться к потоку наших же собственных данных, идущих от продавца к государству? Можем. (В виде небольшого бантика приложение ФНС также дает возможность получить электронную версию этого самого чека.)
Когда я узнал об этой возможности, я загорелся. Более внимательный взгляд на возможности приложения меня не разочаровал: чек можно получить не только в html или png, но и в json.
Дело в том, что я стараюсь вести учет всех своих расходов. Стараюсь делать это регулярно, но, конечно, постоянно оказывается, что несколько недель я это делать забывал. И начинается сеанс эриксонианского гипноза с погружением в прошлое и заполнением белых пятен.
Вот от этого мучительного процесса ФНС и поможет нам избавиться.
Получается, теоретически, вместо того, чтобы в конце месяца (или двух) анализировать выписки из нескольких банков и ворох бумажных чеков, пытаясь понять, куда же опять слились все деньги, можно анализировать гору писем в ящике с json во вложениях. Перспектива тоже так себе, но в отличие от первой она куда лучше поддается автоматизации.
Итак, совершая покупку, мы можем получать на почту наш чек в формате JSON. Причем, в теории, при покупке для этого нам даже не нужно приложение от ФНС: мы можем заранее сгенерировать в приложении визитку с qr-кодом нашей почты и предъявлять ее кассиру в момент покупки. Тогда мы можем запросить вместо бумажного чека электронный, который должен будет сразу падать в почту. Как при интернет покупке.
Но даже ФНС прямо пишет об этой функции – «Не обольщайтесь». Ее поддерживают не все ККМ. И даже там, где ее поддерживают ККМ, совсем не факт, что ее поддерживают сами кассиры. Так что пока это для сильных духом.
Получив чек на почту, нам остается только автоматизировать его разбор и выгрузку в удобное для нас хранилище.
Нужно выбрать, куда все это выгружать так, чтобы дальше было удобно с этим работать.
Не знаю, как вы, а лично я, пользуясь теми или иными бюджетницами, в итоге всегда экспортирую данные из них для анализа в старый добрый Excel. Какие бы графики и диаграммы разработчики не включили в свой продукт заранее, они никогда не смогут предугадать всего, что я захочу вытянуть из данных. Какие транзакции учитывать, какие — не учитывать, сопоставить с фазами луны, сгруппировать, поделить на сегодняшнее число… Excel все это может.
Причем, если мы положим наш Excel в OneDrive, то он будет прекрасно доступен через веб, в том числе и с мобильных ОС. И, в отличие от GoogleDrive, здесь будут прекрасно работать формулы, в том числе те, что позволят нам организовать каскадные выпадающие списки для категории и подкатегории каждой покупки.
Дело за малым – разобраться с парсером. Нам нужен механизм, получающий из почтового ящика письмо с вложением, разбирающий json вложения и записывающий результат в табличку Excel, лежащую в OneDrive.
В идеальном мире я вижу этот парсер чем-то вроде степа для IFTTT. Тогда наше решение жило бы полностью в сети и не требовало от пользователя вообще ничего (раз уж мы про идеальный мир, то в нем и «визитки» ФНС работают идеально).
Однако сходу понять, как человеку с улицы написать свой степ для IFTTT, – равно как и понять, возможно ли это вообще. Мне не удалось. Если кто-то сможет подсказать толковый гайд или альтернативную платформу, буду весьма признателен.
Раз уж с полностью сетевым решением не заладилось, реализуем одно звено парсера на десктопе. В компании НОРБИТ я занимаюсь внедрением Dynamics CRM, так что стек Microsoft для меня самый привычный. Да и мы уже начали им пользоваться, выбрав Excel и OneDrive. Реализуем плагин для Outlook, куда поместим всю требуемую нам логику.
Здесь я пошел по пути наименьшего сопротивления.
Для начала взял полученный от ФНС JSON и скормил его в json2csharp.com. Получил структуру классов для десериализации:
public class Item
{
public string name { get; set; }
public int nds18 { get; set; }
public int price { get; set; }
public double quantity { get; set; }
public int sum { get; set; }
public int? nds10 { get; set; }
}
public class Receipt
{
public List<Item> items { get; set; }
public string retailPlaceAddress { get; set; }
public string userInn { get; set; }
public int requestNumber { get; set; }
public int nds18 { get; set; }
public string fiscalDriveNumber { get; set; }
public string user { get; set; }
public string @operator { get; set; }
public int fiscalDocumentNumber { get; set; }
public int taxationType { get; set; }
public int ecashTotalSum { get; set; }
public string kktRegId { get; set; }
public DateTime dateTime { get; set; }
public int operationType { get; set; }
public int cashTotalSum { get; set; }
public int receiptCode { get; set; }
public int nds10 { get; set; }
public int totalSum { get; set; }
public int shiftNumber { get; set; }
public int fiscalSign { get; set; }
}
Теперь нужно получить письмо, десериализовать аттач в полученные выше классы и превратить их в XLSX.
Для десериализации я взял Newtonsoft.Json, а для записи в эксель — ClosedXML. Впоследствие оказалось, что можно было не заморачиваться с записью прямо в XLSX, а спокойно писать в CSV — почему-то ClosedXML крашится при работе с файлами, имеющими сводные таблицы. Так что статистику пока что пришлось вынести в отдельный эксель-файл, а выгрузку из плагина ему подсунуть как источник данных.
Сам плагин очень простой. Чтобы он не обрабатывал все входящие письма, я завел отдельный ящик именно для чеков и проверяю, что письмо пришло именно в него. Дальше нужно, конечно, вынести это в настраиваемые параметры, как и все адреса папок.
void Items_ItemAdd(object Item)
{
Outlook.MailItem mail = (Outlook.MailItem)Item;
if (Item != null)
{
if (mail.Attachments.Count == 1 & mail.To.Equals("my@mail.address"))
{
Outlook.Attachment attach = mail.Attachments[1];
string path = "C:\_Data\_tmp\" + attach.FileName;
attach.SaveAsFile(path);
string text = System.IO.File.ReadAllText(path);
System.IO.File.Delete(path);
List<TableRow> objectList = GetBillsData(text);
WriteBillsToXLSX(objectList);
}
}
}
private static void WriteBillsToXLSX(List<TableRow> objectList)
{
var workbook = new XLWorkbook(tablePath);
var ws = workbook.Worksheet("Products");
int startrow = ws.LastRowUsed().RowNumber();
if (ws.CellsUsed().Count() != 0)
startrow = ws.CellsUsed().Last().Address.RowNumber;
foreach (var item in objectList)
{
startrow++;
ws.Cell(startrow, 1).Value = item.dateTime;
ws.Cell(startrow, 2).Value = item.sum;
ws.Cell(startrow, 3).Value = item.quantity;
ws.Cell(startrow, "J").Value = item.user;
ws.Cell(startrow, "S").Value = item.name;
}
workbook.SaveAs(tablePath);
}
private static List<TableRow> GetBillsData(string bill)
{
Receipt doc = JsonConvert.DeserializeObject<Receipt>(bill);
List<TableRow> objectList = new List<TableRow>();
foreach (var item in doc.items)
{
objectList.Add(new TableRow(item, doc));
}
return objectList;
}
Вся трансформация данных от JSON к строке таблицы убрана в конструктор TableRow.
Теперь, совершив покупку, нам достаточно отсканировать чек, и все его строчки сами попадут в наш лежащий в облаке файл. Просмотреть этот файл мы сможем тут же, не отходя от кассы. Просмотр доступен нам на любой ОС, поддерживающей работу современных браузеров.
Останется только проставить категории и подкатегории для загруженных строк и можно строить любую угодную нашей душе аналитику. Еще пара строк кода, и плагин научится сам проставлять категории/подкатегории для строк, если в файле уже есть строка с таким же описанием и заполненной ранее категорией.
Вот такой круговорот данных в природе.
Автор: norbit