Большинство процессов в нашем современном мире стремится к автоматизации. Хотелось бы разместить здесь свою наработку. Надеюсь данный материал найдёт своего читателя. В данной статье рассмотрим автоматизацию введения ежедневных отчетов компании.

Какие проблемы решает скрипт?

  1. Избавление сотрудника от рутинной задачи.

  2. Минимизация ошибок, которые мог бы случайно допустить человек.

  3. Оптимизация затрат компании, сократить расходы на содержание штата сотрудников: менеджеров и бухгалтеров.

В данной статье я хочу поделиться, как можно автоматизировать процессы внутри компании с помощью моего скрипта.

Архитектура скрипта

Рисунок 1 – Архитектура скрипта
Рисунок 1 – Архитектура скрипта

Архитектуру, которую вы видите на рисунке 1, очень примитивная. Данный скрипт находится в режиме разработки, сейчас он хорошо обрабатывает изображения с транзакциями Сбербанк.

Для работы с изображением я использовал следующие библиотеки:

  1. Tesseract – это библиотека JavaScript, которая извлекает из изображений слова практически на любом языке.

  2. Sharp – это библиотека JavaScript, которая обрабатывает фото, на примере увидим, что она конкретно выполняет.

Дополнительно: Для вывода логов я использовал библиотеку Pino.

Описание кода

  1. Обработка изображения.

  2. Распознавание текста на изображении с помощью OCR.

  3. Извлечение и подсчёт финансовых данных.

Давайте посмотрим файлы в директории utils .

logger.js
import pino from 'pino';
import pretty from 'pino-pretty';

export const logger = pino(pretty({
    colorize: true,
    levelFirst: true,
}));

Pino предоставляет дополнительную функциональность и гибкость + в некоторых проектах линтеры могут запрещать использование console.log .

preload.js
import { fileURLToPath } from 'url';
import path, { dirname as pathDirname, resolve } from 'path';
import sharp from 'sharp';

const filename = fileURLToPath(import.meta.url);
const dirname = pathDirname(filename);

export const getPath = (relativePath) => {
    const ROOT_PATH = path.join(dirname, '../');
    return resolve(ROOT_PATH, relativePath);
};

export const preload = async (input, output) => {
    return await sharp(input)
    .resize(1200)
    .grayscale()
    .normalize()
    .sharpen()
    .toBuffer();
};

В этом коде мы используем библиотеку sharp, которая предназначена для обработки изображений в Node.js.

Что мы здесь делаем?
Изменяем размер изображения, делаем картинку черно-белой, улучаем контрастность, повышаем резкость => сохраняем результат в output.

Для чего мы это делаем?
Улучшаем визуальное качество нашего изображения. Тестирование показало, что не все транзакции распознает Tesseract.

Рассмотрим главный файл app.js.

app.js
import Tesseract from 'tesseract.js';
import { preload, getPath } from './utils/preload.js';
import { logger } from './utils/logger.js';
import { keywords } from './keywords.js';

const input = getPath('./assets/input.jpg');

const run = async (input) => {
    const output = await preload(input);

    try {
        const { data: { text } } = await Tesseract.recognize(output, 'rus', {
            logger: info => logger.info(info.status)
        });

        const amounts = [];
        const priceWithRRegex = /[+-]?\d+(?:[\s,]\d{3})*(?:[.,]\d+)?\s*Р/g;

        let ignoreNext = false;
        text.split('\n').forEach(line => {
            if (keywords.some(keyword => line.includes(keyword))) {
                ignoreNext = true;
            }

            if (!ignoreNext) {
                const priceMatches = line.match(priceWithRRegex);
                if (priceMatches) {
                    priceMatches.forEach(priceMatch => {
                        let price = priceMatch
                            .replace(/\s/g, '')
                            .replace(',', '.')
                            .replace('Р', '')
                            .trim();

                        if (!price.startsWith('+') && !price.startsWith('-')) {
                            price = '-' + price;
                        }

                        const numericPrice = parseFloat(price);
                        if (!isNaN(numericPrice)) {
                            amounts.push(numericPrice);
                        }
                    });
                }
            }

            if (ignoreNext) {
                ignoreNext = false;
            }
        });

        const total = amounts.reduce((acc, curr) => acc + curr, 0);

        return {
            amounts,
            total
        };

    } catch (error) {
        logger.info(error);
        throw error;
    }
};

run(input).then(result => {
    logger.info(result, 'Result: ');
});

Сначала вызывается функция preload, которая выполняет обработку изображения. Как я писал раннее, это нам нужно для того, чтобы улучшить качество распознавания текста.

Обрабатываем изображение с помощью Tesseract. Текст, который мы получили, разбивается на строки. Используя регулярное выражение priceWithRRegex, мы ищем в каждой строке суммы, обозначенные символом "Р". Также я задал keywords , после этих ключевых слов сумма не входит в массив. Например, общая сумма за день.

В итоге, после обработки данных мы получаем массив транзакций и их общую сумму.

Код всего проекта на GitHub.

Тестирование

Давайте на примере данного изображения рассмотрим, как работает скрипт.

Изображение, которое мы будем обрабатывать:

Рисунок 2 – Входящее изображение
Рисунок 2 – Входящее изображение

Обработанное изображение:

Рисунок 3 – Обработанное изображение
Рисунок 3 – Обработанное изображение

После обработки изображения идет извлечение текста и получение всех транзакций. В итоге мы получаем массив транзакций и их общую сумму.

Рисунок 4 – Результат работы
Рисунок 4 – Результат работы

Заключение

Данный пример хорошо показывает, как с помощью такого скрипта автоматизировать процесс ведения ежедневного отчёта. Он решает проблемы, описанные в статье, и упрощает работу с финансовыми данными. Данный скрипт находится в этапе разработки, было бы хорошо его дорабатывать вместе с другими разработчиками. В ближайших планах — добавить поддержку распознавания транзакций с других банков, которые используют несколько отличающийся формат данных. В будущем можно интегрировать этот скрипт с другими системами, такими как МойСклад, что позволит автоматизировать ещё больший объём работы, включая синхронизацию данных с бухгалтерскими системами, управление складскими остатками и улучшение общего управления бизнесом.

Мой Github

Комментарии (4)


  1. DvoiNic
    26.08.2024 12:00

    а сразу от банка нельзя информацию получать?


  1. john_samilin
    26.08.2024 12:00

    Хороший пример, спасибо. А что насчет распознавания чеков? И почему в результате распознана только сумма каждой транзакции, но не ее описание (в данном случае имя плательщика)?


    1. glebanya12 Автор
      26.08.2024 12:00

      Спасибо за вопрос. Распознавание чеков можно реализовать, спасибо за мысль. Описание пока с трудом распознается, работаю над этим.


      1. john_samilin
        26.08.2024 12:00

        У меня есть пет-проект, которому как раз нужно распознавание чеков. Если интересно, можем заколлабиться