Skip to content

musecollaboration/MiniERP

Repository files navigation

MiniERP: Модуль бухгалтерского учета

Python FastAPI PostgreSQL Docker Tests

Production-ready бухгалтерский модуль для малого и среднего бизнеса с полным соблюдением принципов двойной записи, автоматизированной обработкой документов через OCR и асинхронной архитектурой.


Содержание


Возможности

Бухгалтерский учет

  • План счетов с типами (актив/пассив/доход/расход/капитал)
  • Двойная запись с жесткой валидацией балансировки дебета и кредита
  • Проводки с привязкой к первичным документам
  • Финансовые отчеты:
    • Оборотно-сальдовая ведомость (ОСВ)
    • Баланс по счетам за период
    • Обороты с группировкой по счетам

Автоматизация

  • OCR документов - автоматическое распознавание текста из PDF/изображений (Tesseract)
  • Асинхронная обработка - фоновая очередь задач через Celery + RabbitMQ
  • Отслеживание статусов - PENDING → PROCESSED → FAILED
  • Structured logging - JSON-логи для ELK/Grafana

DevOps & Quality

  • Docker Compose - полная инфраструктура в контейнерах
  • 100% покрытие тестами - интеграционные и unit-тесты
  • Alembic миграции - версионирование схемы БД
  • Type hints - полная типизация кода
  • OpenAPI/Swagger - автогенерируемая документация API

Технологический стек

Backend

Компонент Технология Версия Назначение
Web Framework FastAPI 0.120.4 Async REST API
ORM SQLAlchemy 2.0.44 Async database layer
DB Driver asyncpg 0.30.0 PostgreSQL async driver
Validation Pydantic 2.12.3 Data validation & serialization
JSON orjson 3.10.15 Fast JSON encoding
Logging structlog 25.5.0 Structured JSON logs

Infrastructure

Компонент Технология Версия Назначение
Database PostgreSQL 15 Relational data storage
Task Queue Celery 5.5.3 Async task processing
Message Broker RabbitMQ 3-management Task queue broker
Cache Redis 7 Celery backend & caching
Migrations Alembic 1.17.1 Database versioning

OCR & Processing

Компонент Технология Назначение
OCR Engine Tesseract Text recognition
PDF Processing pdf2image PDF to image conversion
Image Processing Pillow Image manipulation

Testing & Quality

Компонент Технология Назначение
Testing pytest 8.4.2
Async Tests pytest-asyncio 1.2.0
HTTP Client httpx 0.28.1
Coverage pytest-cov 7.0.0

Архитектура

Структура проекта

MiniERP/
├── accounting/                    # Основное приложение
│   ├── api/                       # REST API endpoints
│   │   ├── documents.py           # Загрузка документов
│   │   ├── entries.py             # Бухгалтерские проводки
│   │   └── reports.py             # Финансовые отчеты
│   ├── core/                      # Ядро приложения
│   │   ├── config.py              # Настройки (Pydantic Settings)
│   │   ├── database.py            # Async engine, sessions
│   │   └── dependencies.py        # FastAPI dependencies
│   ├── models/                    # SQLAlchemy ORM модели
│   │   ├── account.py             # План счетов
│   │   ├── document.py            # Первичные документы
│   │   └── entry.py               # Проводки и строки проводок
│   ├── schemas/                   # Pydantic схемы
│   │   ├── document.py            # DTO для документов
│   │   ├── entry.py               # DTO для проводок
│   │   └── report.py              # DTO для отчетов
│   ├── services/                  # Бизнес-логика
│   │   ├── entry_service.py       # Создание проводок
│   │   └── report_service.py      # Генерация отчетов
│   ├── tasks/                     # Celery задачи
│   │   └── ocr.py                 # OCR processing
│   ├── utils/                     # Утилиты
│   │   └── ocr.py                 # OCR helpers
│   └── tests/                     # Тесты
│       ├── conftest.py            # Pytest fixtures
│       ├── test_documents.py      # Тесты загрузки
│       ├── test_entries.py        # Тесты проводок
│       └── test_reports.py        # Тесты отчетов
├── migrations/                    # Alembic миграции
│   ├── env.py                     # Alembic environment
│   └── versions/                  # История миграций
│       ├── 20240101_0001_*.py     # Создание таблиц
│       ├── 20240101_0002_*.py     # Внешние ключи
│       └── 20240101_0003_*.py     # Расширение precision
├── worker/                        # Celery worker
│   └── celery_worker.py           # Worker configuration
├── data/                          # Runtime данные
│   ├── media/                     # Загруженные документы
│   └── ocr_tmp/                   # Временные файлы OCR
├── docker-compose.yml             # Инфраструктура
├── Dockerfile                     # API/Worker image
├── alembic.ini                    # Alembic config
├── pyproject.toml                 # Python dependencies
└── requirements.txt               # Pip freeze

Компоненты системы

┌─────────────────┐
│   Client/UI     │
└────────┬────────┘
         │ HTTP/REST
         ▼
┌─────────────────┐      ┌──────────────┐
│   FastAPI API   │◄────►│  PostgreSQL  │
│   (uvicorn)     │      │   Database   │
└────────┬────────┘      └──────────────┘
         │
         │ Celery Tasks
         ▼
┌─────────────────┐      ┌──────────────┐
│  Celery Worker  │◄────►│   RabbitMQ   │
│  (OCR Process)  │      │    Broker    │
└─────────────────┘      └──────────────┘
                                │
                                ▼
                         ┌──────────────┐
                         │    Redis     │
                         │   Backend    │
                         └──────────────┘

Быстрый старт

Предварительные требования

  • Docker 24.0+
  • Docker Compose 2.0+
  • 4GB RAM минимум

1. Клонирование и настройка

# Клонировать репозиторий
git clone <repository-url>
cd MiniERP

# Создать .env файл (опционально, есть значения по умолчанию)
cat > .env << EOF
ENVIRONMENT=development
DATABASE_URL=postgresql+asyncpg://postgres:postgres@db:5432/accounting
CELERY_BROKER_URL=amqp://guest:guest@rabbitmq:5672//
CELERY_RESULT_BACKEND=redis://redis:6379/0
EOF
docker compose up -d

# Дождаться готовности всех сервисов (30-60 секунд)
docker compose ps

2. Применить миграции

# Автоматически применятся при старте API, или вручную:
docker compose exec api alembic upgrade head

3. Проверить работу

# Health check
curl http://localhost:8001/health/
# Ожидаемый ответ: {"status":"ok"}

# Swagger документация
open http://localhost:8001/docs

# RabbitMQ Management UI
open http://localhost:15673/
# Login: guest / guest

4. Запустить тесты

# Все тесты
docker compose exec api pytest -v

# С coverage отчетом
docker compose exec api pytest --cov=accounting --cov-report=html

# Только определенный модуль
docker compose exec api pytest accounting/tests/test_entries.py -v

API Endpoints

Документация

Основные эндпоинты

Health Check

GET /health/

Ответ:

{
  "status": "ok"
}

Создать бухгалтерскую проводку

POST /entries/
Content-Type: application/json

Тело запроса:

{
  "date": "2025-11-02",
  "description": "Оплата от клиента",
  "lines": [
    {
      "account_id": 1,
      "debit": "1000.00",
      "credit": "0"
    },
    {
      "account_id": 2,
      "debit": "0",
      "credit": "1000.00"
    }
  ],
  "document_id": null
}

Ответ (201 Created):

{
  "id": 1,
  "date": "2025-11-02",
  "description": "Оплата от клиента",
  "document_id": null,
  "created_at": "2025-11-02T10:30:00Z",
  "lines": [
    {
      "id": 1,
      "account_id": 1,
      "debit": "1000.00",
      "credit": "0.00"
    },
    {
      "id": 2,
      "account_id": 2,
      "debit": "0.00",
      "credit": "1000.00"
    }
  ]
}

Валидация:

  • Дебет должен равняться кредиту (Dt = Kt)
  • Минимум 2 строки в проводке
  • Все account_id должны существовать

Ошибки:

// 400 Bad Request
{
  "detail": "Debits must equal credits: debit=1000.00, credit=500.00"
}

Загрузить документ с OCR обработкой

POST /documents/upload/
Content-Type: multipart/form-data

Параметры:

  • file: файл (PDF/PNG/JPG, max 10MB)

Ответ (201 Created):

{
  "id": 1,
  "file_path": "documents/2025/11/02/invoice_123.pdf",
  "file_type": "application/pdf",
  "uploaded_at": "2025-11-02T10:35:00Z",
  "status": "PENDING"
}

Статусы обработки:

  • PENDING - в очереди на обработку
  • PROCESSED - успешно обработан
  • FAILED - ошибка при обработке

Пример с curl:

curl -X POST http://localhost:8001/documents/upload/ \
  -F "file=@invoice.pdf"

Получить оборотно-сальдовую ведомость

GET /reports/balance/?date_from=2025-11-01&date_to=2025-11-30

Параметры:

  • date_from: начало периода (YYYY-MM-DD)
  • date_to: конец периода (YYYY-MM-DD)

Ответ (200 OK):

{
  "date_from": "2025-11-01",
  "date_to": "2025-11-30",
  "items": [
    {
      "account_id": 1,
      "account_code": "101",
      "account_name": "Касса",
      "opening_balance": "0",
      "debit": "5000.00",
      "credit": "3000.00",
      "closing_balance": "2000.00"
    },
    {
      "account_id": 2,
      "account_code": "600",
      "account_name": "Расходы",
      "opening_balance": "1000.00",
      "debit": "3000.00",
      "credit": "2000.00",
      "closing_balance": "2000.00"
    }
  ],
  "total_debit": "8000.00",
  "total_credit": "5000.00"
}

Получить отчет по оборотам

GET /reports/turnover/?date_from=2025-11-01&date_to=2025-11-30

Параметры:

  • date_from: начало периода
  • date_to: конец периода

Ответ (200 OK):

{
  "date_from": "2025-11-01",
  "date_to": "2025-11-30",
  "items": [
    {
      "account_id": 1,
      "account_code": "101",
      "account_name": "Касса",
      "opening_debit": "0",
      "opening_credit": "0",
      "turnover_debit": "5000.00",
      "turnover_credit": "3000.00",
      "closing_debit": "5000.00",
      "closing_credit": "3000.00"
    }
  ],
  "total_turnover_debit": "5000.00",
  "total_turnover_credit": "3000.00"
}

База данных

Схема данных

-- План счетов
CREATE TABLE accounts (
    id SERIAL PRIMARY KEY,
    code VARCHAR(32) UNIQUE NOT NULL,
    name VARCHAR(255) NOT NULL,
    type account_type NOT NULL,  -- ASSET, LIABILITY, EQUITY, REVENUE, EXPENSE
    is_active BOOLEAN NOT NULL DEFAULT true
);

-- Первичные документы
CREATE TABLE documents (
    id SERIAL PRIMARY KEY,
    file_path VARCHAR(512) NOT NULL,
    file_type VARCHAR(128) NOT NULL,
    uploaded_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    status document_status NOT NULL  -- PENDING, PROCESSED, FAILED
);

-- Бухгалтерские проводки
CREATE TABLE entries (
    id SERIAL PRIMARY KEY,
    date DATE NOT NULL,
    description VARCHAR(512) NOT NULL,
    document_id INTEGER REFERENCES documents(id),
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- Строки проводок (двойная запись)
CREATE TABLE entry_lines (
    id SERIAL PRIMARY KEY,
    entry_id INTEGER NOT NULL REFERENCES entries(id) ON DELETE CASCADE,
    account_id INTEGER NOT NULL REFERENCES accounts(id) ON DELETE RESTRICT,
    debit NUMERIC(12,2) NOT NULL CHECK (debit >= 0),
    credit NUMERIC(12,2) NOT NULL CHECK (credit >= 0)
);

Индексы

CREATE INDEX ix_entries_date ON entries(date);
CREATE INDEX ix_entries_document_id ON entries(document_id);
CREATE INDEX ix_entry_lines_account_id ON entry_lines(account_id);
CREATE INDEX ix_documents_status ON documents(status);

Миграции

# Создать новую миграцию
docker compose exec api alembic revision --autogenerate -m "Description"

# Применить миграции
docker compose exec api alembic upgrade head

# Откатить последнюю миграцию
docker compose exec api alembic downgrade -1

# Показать текущую версию
docker compose exec api alembic current

# История миграций
docker compose exec api alembic history

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

Запуск тестов

# Все тесты
docker compose exec api pytest

# С подробным выводом
docker compose exec api pytest -v

# Только определенный файл
docker compose exec api pytest accounting/tests/test_entries.py

# Только один тест
docker compose exec api pytest accounting/tests/test_entries.py::test_create_entry_success

# С покрытием кода
docker compose exec api pytest --cov=accounting --cov-report=term-missing

# HTML отчет о покрытии
docker compose exec api pytest --cov=accounting --cov-report=html
# Открыть: htmlcov/index.html

Структура тестов

# accounting/tests/conftest.py - общие fixtures
@pytest.fixture
async def engine_for_tests() -> AsyncEngine:
    """Отдельный engine для каждого теста"""

@pytest.fixture
async def db_session(engine_for_tests) -> AsyncSession:
    """Тестовая сессия БД"""

@pytest.fixture
async def client(app, engine_for_tests) -> AsyncClient:
    """HTTP клиент с dependency overrides"""

# accounting/tests/test_entries.py
async def test_create_entry_success(client, db_session):
    """Проверка создания валидной проводки"""

async def test_create_entry_validation_error(client, db_session):
    """Проверка валидации Dt = Kt"""

# accounting/tests/test_reports.py
async def test_balance_report(client, db_session):
    """Проверка оборотно-сальдовой ведомости"""

async def test_turnover_report(client, db_session):
    """Проверка отчета по оборотам"""

# accounting/tests/test_documents.py
async def test_document_upload_triggers_task(client):
    """Проверка загрузки документа и запуска Celery task"""

Покрытие кода

Текущее покрытие: 100% всех критичных модулей:

  • API endpoints: 100%
  • Services: 100%
  • Models: 100%
  • Schemas: 100%

Разработка

Локальная разработка

# Установить зависимости для разработки
pip install -r requirements.txt
pip install -e .

# Установить pre-commit hooks
pre-commit install

# Запустить pre-commit на всех файлах
pre-commit run --all-files

# Форматирование кода
black accounting/
ruff check accounting/ --fix

# Проверка типов
mypy accounting/

Переменные окружения

# .env файл
ENVIRONMENT=development          # development/production
DATABASE_URL=postgresql+asyncpg://user:pass@host:port/db
CELERY_BROKER_URL=amqp://guest:guest@rabbitmq:5672//
CELERY_RESULT_BACKEND=redis://redis:6379/0

# Для production
SECRET_KEY=your-secret-key-here
ALLOWED_HOSTS=api.example.com,www.example.com

Логирование

# Structured JSON logs
import structlog

logger = structlog.get_logger(__name__)

logger.info("entry_created", entry_id=entry.id, amount=total)
logger.error("validation_failed", error=str(e), payload=data)

# Вывод:
# {"event": "entry_created", "entry_id": 123, "amount": "1000.00", "timestamp": "..."}

Добавление нового endpoint

# 1. Создать схему в accounting/schemas/
class NewFeatureCreate(BaseModel):
    name: str
    value: Decimal

class NewFeatureRead(NewFeatureCreate):
    id: int
    created_at: datetime

# 2. Создать модель в accounting/models/
class NewFeature(Base):
    __tablename__ = "new_features"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(255))
    value: Mapped[Decimal] = mapped_column(Numeric(12, 2))

# 3. Создать миграцию
docker compose exec api alembic revision --autogenerate -m "add new_features table"
docker compose exec api alembic upgrade head

# 4. Создать сервис в accounting/services/
class NewFeatureService:
    def __init__(self, session: AsyncSession):
        self.session = session

    async def create(self, data: NewFeatureCreate) -> NewFeature:
        feature = NewFeature(**data.model_dump())
        self.session.add(feature)
        await self.session.flush()
        await self.session.commit()
        return feature

# 5. Создать endpoint в accounting/api/
@router.post("/", response_model=NewFeatureRead)
async def create_feature(
    payload: NewFeatureCreate,
    session: AsyncSession = Depends(get_db)
) -> NewFeatureRead:
    service = NewFeatureService(session)
    feature = await service.create(payload)
    return NewFeatureRead.model_validate(feature)

# 6. Зарегистрировать router в accounting/main.py
from accounting.api import new_features
app.include_router(new_features.router, prefix="/new-features", tags=["NewFeatures"])

# 7. Написать тесты в accounting/tests/test_new_features.py
async def test_create_feature(client):
    response = await client.post("/new-features/", json={
        "name": "test",
        "value": "100.00"
    })
    assert response.status_code == 201

Production Deployment

Docker Production Build

# Собрать production image
docker build -t minierp:latest .

# Запустить с production настройками
docker run -d \
  --name minierp-api \
  -p 8000:8000 \
  -e ENVIRONMENT=production \
  -e DATABASE_URL=postgresql+asyncpg://... \
  minierp:latest

Kubernetes Deployment

# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: minierp-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: minierp-api
  template:
    metadata:
      labels:
        app: minierp-api
    spec:
      containers:
        - name: api
          image: minierp:latest
          ports:
            - containerPort: 8000
          env:
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: minierp-secrets
                  key: database-url
            - name: ENVIRONMENT
              value: "production"
          livenessProbe:
            httpGet:
              path: /health/
              port: 8000
            initialDelaySeconds: 30
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /health/
              port: 8000
            initialDelaySeconds: 10
            periodSeconds: 5
          resources:
            requests:
              memory: "256Mi"
              cpu: "250m"
            limits:
              memory: "512Mi"
              cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:
  name: minierp-api
spec:
  selector:
    app: minierp-api
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8000
  type: LoadBalancer

Nginx Reverse Proxy

# /etc/nginx/sites-available/minierp
upstream minierp {
    server 127.0.0.1:8001;
    # Для multiple workers:
    # server 127.0.0.1:8001;
    # server 127.0.0.1:8002;
    # server 127.0.0.1:8003;
}

server {
    listen 80;
    server_name api.minierp.example.com;

    # Redirect HTTP to HTTPS
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name api.minierp.example.com;

    ssl_certificate /etc/letsencrypt/live/api.minierp.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.minierp.example.com/privkey.pem;

    client_max_body_size 10M;

    location / {
        proxy_pass http://minierp;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /static/ {
        alias /var/www/minierp/static/;
        expires 30d;
    }
}

Systemd Service

# /etc/systemd/system/minierp-api.service
[Unit]
Description=MiniERP FastAPI Application
After=network.target postgresql.service redis.service

[Service]
Type=notify
User=minierp
Group=minierp
WorkingDirectory=/opt/minierp
Environment="PATH=/opt/minierp/venv/bin"
Environment="DATABASE_URL=postgresql+asyncpg://..."
ExecStart=/opt/minierp/venv/bin/uvicorn accounting.main:app \
    --host 0.0.0.0 \
    --port 8000 \
    --workers 4 \
    --log-config logging.json
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
# Запустить сервис
sudo systemctl daemon-reload
sudo systemctl enable minierp-api
sudo systemctl start minierp-api
sudo systemctl status minierp-api

# Логи
journalctl -u minierp-api -f

Celery Worker Service

# /etc/systemd/system/minierp-worker.service
[Unit]
Description=MiniERP Celery Worker
After=network.target rabbitmq-server.service redis.service

[Service]
Type=forking
User=minierp
Group=minierp
WorkingDirectory=/opt/minierp
Environment="PATH=/opt/minierp/venv/bin"
ExecStart=/opt/minierp/venv/bin/celery -A worker.celery_worker worker \
    --loglevel=info \
    --concurrency=4 \
    --pidfile=/var/run/minierp-worker.pid
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

Мониторинг и алерты

# Интеграция с Prometheus
from prometheus_fastapi_instrumentator import Instrumentator

app = FastAPI()
Instrumentator().instrument(app).expose(app)

# Метрики доступны на /metrics
# prometheus.yml
scrape_configs:
  - job_name: "minierp-api"
    static_configs:
      - targets: ["localhost:8000"]
    metrics_path: "/metrics"
    scrape_interval: 15s

Бэкапы PostgreSQL

# Ежедневный бэкап через cron
# /etc/cron.daily/minierp-backup
#!/bin/bash
BACKUP_DIR=/backups/minierp
DATE=$(date +%Y%m%d_%H%M%S)
pg_dump -U postgres accounting | gzip > $BACKUP_DIR/backup_$DATE.sql.gz

# Удалить бэкапы старше 30 дней
find $BACKUP_DIR -name "backup_*.sql.gz" -mtime +30 -delete

Безопасность

# accounting/core/security.py
from fastapi import Security, HTTPException
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials

security = HTTPBearer()

async def verify_token(credentials: HTTPAuthorizationCredentials = Security(security)):
    """Проверка JWT токена"""
    if credentials.credentials != "valid-token":
        raise HTTPException(status_code=401, detail="Invalid token")
    return credentials.credentials

# В endpoint:
@router.get("/")
async def protected_route(token: str = Depends(verify_token)):
    return {"message": "Authorized"}

Дополнительные ресурсы

Документация

Полезные команды

# Docker
docker compose logs -f api              # Логи API
docker compose logs -f worker           # Логи Celery worker
docker compose exec api bash            # Зайти в контейнер
docker compose restart api              # Перезапустить API
docker compose down -v                  # Остановить и удалить volumes

# PostgreSQL
docker compose exec db psql -U postgres -d accounting
# SELECT * FROM entries LIMIT 10;
# \dt                    -- Список таблиц
# \d entries             -- Описание таблицы

# Redis
docker compose exec redis redis-cli
# KEYS *
# GET key_name

# RabbitMQ
docker compose exec rabbitmq rabbitmqctl list_queues
docker compose exec rabbitmq rabbitmqctl list_exchanges

# Python
docker compose exec api python -m accounting.main
docker compose exec api python -c "from accounting.core.database import engine; print(engine)"

Участие в разработке

Стиль кода

  • Python 3.11+
  • Black formatter (line length 120)
  • Ruff linter
  • Type hints обязательны
  • Docstrings в формате Google

Процесс разработки

  1. Fork репозитория
  2. Создать feature branch (git checkout -b feature/amazing-feature)
  3. Сделать изменения и commit (git commit -m 'Add amazing feature')
  4. Push в branch (git push origin feature/amazing-feature)
  5. Открыть Pull Request

Требования к PR

  • Все тесты проходят
  • Coverage не снижается
  • Pre-commit hooks пройдены
  • Документация обновлена
  • Changelog обновлен

Лицензия

MIT License - see LICENSE file for details


Известные проблемы

  • OCR работает только с русским и английским языками (можно добавить другие через Tesseract language packs)
  • Максимальный размер файла для загрузки - 10MB (настраивается в config.py)
  • При больших объемах данных (>100k проводок) рекомендуется партиционирование таблиц по дате

Планы развития

  • Добавить аутентификацию (JWT)
  • Реализовать RBAC (Role-Based Access Control)
  • Добавить экспорт отчетов в Excel/PDF
  • Интеграция с внешними банками (API банков)
  • GraphQL API в дополнение к REST
  • Websocket для real-time обновлений
  • Grafana dashboards для мониторинга
  • Автоматическое создание проводок из распознанных документов

Сделано с используя FastAPI и современный Python stack

Применить миграции

docker compose exec api alembic upgrade head

Прогнать тесты

docker compose exec api pytest -v

Документация API

open http://localhost:8001/docs


## Качество и CI

- `docker compose exec api ruff check .`
- `docker compose exec api mypy .`
- `docker compose exec api pytest --cov=accounting`
- GitHub Actions: линтеры + тесты на pull request

## Переменные окружения

Задаются через `.env` (см. `.env.example`): URL базы данных, брокеры Celery/Redis, каталоги медиа и временных файлов OCR.

## Celery & OCR

- worker `minierp-worker` обрабатывает очередь `ocr`
- OCR: pytesseract + pdf2image + Pillow
- После успешного распознавания создаётся бухгалтерская проводка, документ помечается `processed`

## Полезные команды

```bash
# Запустить воркер вручную
docker compose exec worker celery -A worker.celery_worker.celery_app worker --loglevel=info

# Создать новую миграцию
docker compose exec api alembic revision --autogenerate -m "describe change"

# Запустить линтеры локально
docker compose exec api pre-commit run --all-files

About

Production-ready модуль бухгалтерского учета с полной реализацией принципов двойной записи, асинхронной обработкой документов через OCR и современной микросервисной архитектурой

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors