Большинство процессов в нашем современном мире стремится к автоматизации. Хотелось бы разместить здесь свою наработку. Надеюсь данный материал найдёт своего читателя. В данной статье рассмотрим автоматизацию введения ежедневных отчетов компании.
Какие проблемы решает скрипт?
- Избавление сотрудника от рутинной задачи. 
- Минимизация ошибок, которые мог бы случайно допустить человек. 
- Оптимизация затрат компании, сократить расходы на содержание штата сотрудников: менеджеров и бухгалтеров. 
В данной статье я хочу поделиться, как можно автоматизировать процессы внутри компании с помощью моего скрипта.
Архитектура скрипта

Архитектуру, которую вы видите на рисунке 1, очень примитивная. Данный скрипт находится в режиме разработки, сейчас он хорошо обрабатывает изображения с транзакциями Сбербанк.
Для работы с изображением я использовал следующие библиотеки:
- Tesseract – это библиотека JavaScript, которая извлекает из изображений слова практически на любом языке. 
- Sharp – это библиотека JavaScript, которая обрабатывает фото, на примере увидим, что она конкретно выполняет. 
Дополнительно: Для вывода логов я использовал библиотеку Pino.
Описание кода
- Обработка изображения. 
- Распознавание текста на изображении с помощью OCR. 
- Извлечение и подсчёт финансовых данных. 
Давайте посмотрим файлы в директории 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.
Тестирование
Давайте на примере данного изображения рассмотрим, как работает скрипт.
Изображение, которое мы будем обрабатывать:

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

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

Заключение
Данный пример хорошо показывает, как с помощью такого скрипта автоматизировать процесс ведения ежедневного отчёта. Он решает проблемы, описанные в статье, и упрощает работу с финансовыми данными. Данный скрипт находится в этапе разработки, было бы хорошо его дорабатывать вместе с другими разработчиками. В ближайших планах — добавить поддержку распознавания транзакций с других банков, которые используют несколько отличающийся формат данных. В будущем можно интегрировать этот скрипт с другими системами, такими как МойСклад, что позволит автоматизировать ещё больший объём работы, включая синхронизацию данных с бухгалтерскими системами, управление складскими остатками и улучшение общего управления бизнесом.
Комментарии (4)
 - john_samilin26.08.2024 12:00- Хороший пример, спасибо. А что насчет распознавания чеков? И почему в результате распознана только сумма каждой транзакции, но не ее описание (в данном случае имя плательщика)?  - glebanya12 Автор26.08.2024 12:00- Спасибо за вопрос. Распознавание чеков можно реализовать, спасибо за мысль. Описание пока с трудом распознается, работаю над этим.  - john_samilin26.08.2024 12:00- У меня есть пет-проект, которому как раз нужно распознавание чеков. Если интересно, можем заколлабиться 
 
 
 
           
 
DvoiNic
а сразу от банка нельзя информацию получать?