PDF link target=_blank + nginx timeouts + priority filteri (samo s podacima)
nginx (sport.rinet.one): - proxy_read_timeout 60s → 300s - proxy_send_timeout 300s - proxy_buffering off (PDF stream) - client_max_body_size 50M → 100M Endpoints: - /api/v2/klubovi/financirani: +with_data filter (samo s potporama/godišnjakom/HNS) - /api/v2/sportasi/filtered: +samo_priority +samo_s_hns Frontend: - PDF link target=_blank rel=noopener - window._klub_only_priority = true (default) - window._sportas_only_priority = true (default) DB View: - pgz_sport.v_nogomet_priority (prima_potpore, u_godisnjaku, ima_hns_roster)
This commit is contained in:
@@ -1,27 +1,38 @@
|
||||
#!/usr/bin/env python3
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# Fajl: routers/erp_full_router.py | v1.0.0 | 05.05.2026
|
||||
# Fajl: routers/erp_full_router.py | v1.1.0 | 05.05.2026
|
||||
# Autor: Damir Radulić <dradulic@outlook.com> / damir@rinet.one
|
||||
# Lokacija: /opt/pgz-sport/routers/erp_full_router.py
|
||||
# Svrha: FULL ERP (SAP-Lite) — kontni plan, dnevnik, glavna knjiga,
|
||||
# partneri, ulazni/izlazni računi (+ FINA e-Račun XML import),
|
||||
# PDV, plaće, izvještaji (Bilanca/PnL/Cashflow), PDF/XLSX export.
|
||||
# PDV, plaće, izvještaji (Bilanca/PnL/Cashflow), PDF/XLSX export,
|
||||
# invoice_uploads (OCR), expense_reports (Putni nalozi), payments.
|
||||
# v1.1.0 (2026-05-05): + POST /invoice-uploads multipart upload (Agent E).
|
||||
# Mount: /api/v2/erp/*
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
import re
|
||||
from datetime import date, datetime
|
||||
from decimal import Decimal
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
from typing import Optional, List
|
||||
from xml.etree import ElementTree as ET
|
||||
|
||||
import psycopg2
|
||||
import psycopg2.extras
|
||||
from fastapi import APIRouter, HTTPException, Query, Body, UploadFile, File, Depends, Header
|
||||
from fastapi import APIRouter, HTTPException, Query, Body, UploadFile, File, Depends, Header, Form
|
||||
from fastapi.responses import StreamingResponse
|
||||
from pydantic import BaseModel
|
||||
|
||||
# ── Upload destination (relative to web root /uploads/...) ──────────
|
||||
UPLOAD_BASE = Path("/opt/pgz-sport/uploads")
|
||||
INVOICE_UPLOAD_DIR = UPLOAD_BASE / "invoices"
|
||||
INVOICE_UPLOAD_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
router = APIRouter(prefix="/api/v2/erp", tags=["erp_full"])
|
||||
|
||||
DB = dict(host='10.10.0.2', port=6432, dbname='rinet_v3', user='rinet', password='R1net2026!SecureDB#v7')
|
||||
@@ -1172,6 +1183,43 @@ def racuni_ulazni_uploads(rid: int):
|
||||
return {"count": len(rows), "rows": rows}
|
||||
|
||||
|
||||
# ── Upload new invoice file (multipart) ─────────────────────────────
|
||||
@router.post("/invoice-uploads")
|
||||
async def invoice_uploads_create(
|
||||
file: UploadFile = File(...),
|
||||
klub_id: Optional[int] = Form(None),
|
||||
invoice_id: Optional[int] = Form(None),
|
||||
):
|
||||
"""Accepts PDF/JPG/PNG of an invoice. Stores file under /uploads/invoices/
|
||||
and inserts a row into invoice_uploads with ocr_status='pending'.
|
||||
Returns the new id; OCR/AI extraction runs separately."""
|
||||
raw = await file.read()
|
||||
if not raw:
|
||||
raise HTTPException(400, "Empty file")
|
||||
if len(raw) > 25 * 1024 * 1024:
|
||||
raise HTTPException(413, "File > 25 MB")
|
||||
safe = re.sub(r"[^A-Za-z0-9._-]+", "_", file.filename or "upload.bin")[:120]
|
||||
sha = hashlib.sha256(raw).hexdigest()
|
||||
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
rel = f"invoices/{ts}_{sha[:10]}_{safe}"
|
||||
abs_path = UPLOAD_BASE / rel
|
||||
abs_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(abs_path, "wb") as f:
|
||||
f.write(raw)
|
||||
new_id = db_exec(
|
||||
"INSERT INTO pgz_sport.invoice_uploads "
|
||||
"(klub_id, file_name, file_path, file_size, mime, sha256, "
|
||||
" ocr_status, invoice_id, uploaded_at) "
|
||||
"VALUES (%s,%s,%s,%s,%s,%s,'pending',%s, now()) RETURNING id",
|
||||
(klub_id, file.filename, rel, len(raw),
|
||||
file.content_type or "application/octet-stream",
|
||||
sha, invoice_id),
|
||||
returning=True,
|
||||
)
|
||||
return {"ok": True, "id": new_id, "file_path": rel,
|
||||
"file_size": len(raw), "sha256": sha}
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# 12) PUTNI NALOZI / EXPENSE REPORTS
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
Reference in New Issue
Block a user