6-sub sprint: Dokumenti+HNS profil+Admin+ERP+CRM+PGŽ filter
SUB1 Dokumenti: pgz:dokumenti SECTIONS handler u app.html (klikabilan grid 19 godišnjaka, PDF stream) SUB2 HNS profil: sport2.html drill-down — bio-chips (visina/težina/noga/poz/dres) + HNS deep + Google + Wiki + 🏆 Karijera/📅 Utakmice tabovi (Josip Zec id=449: 257 nast/182 gol/15 sez) SUB3 Admin Users: sidebar.js href fix /admin/users → /sport/admin/users + razriješen audit ID konflikt SUB4 ERP Full: 5 novih endpointa (invoice-uploads, racuni/ulazni/{rid}/uploads, expense-reports, putni-nalog-racuni, payments) + 3 nova taba (📎 Uploads/OCR, ✈ Putni, 💰 Plaćanja) + inline stavke drill-down + sidebar entry SUB5 CRM Salesforce-Lite: dodan crm_v2 sidebar entry (router 956 linija već mounted) SUB6 PGŽ filter: 2 nova endpointa /api/v2/savezi/priority-sort + /api/v2/clanovi/priority-sort; togglePGZFilter wired u Klubovi/Savezi/Sportaši (sport2.html + app.html); ⭐💰📖 badge prefix; klubovi 1536/1641, savezi 35/246, sportaši 4979/5499 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+130
-2
@@ -1134,14 +1134,142 @@ def proracun_list():
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# 11) HEALTH/DEBUG
|
||||
# 11) INVOICE UPLOADS (PDF/scan attachments to ulazni računi)
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
@router.get("/invoice-uploads")
|
||||
def invoice_uploads_list(klub_id: Optional[int] = None,
|
||||
ocr_status: Optional[str] = None,
|
||||
q: Optional[str] = None,
|
||||
limit: int = 200):
|
||||
where = ["1=1"]
|
||||
params: list = []
|
||||
if klub_id:
|
||||
where.append("klub_id=%s"); params.append(klub_id)
|
||||
if ocr_status:
|
||||
where.append("ocr_status=%s"); params.append(ocr_status)
|
||||
if q:
|
||||
where.append("(file_name ILIKE %s OR ai_vendor_name ILIKE %s OR ai_invoice_no ILIKE %s)")
|
||||
params.extend([f"%{q}%", f"%{q}%", f"%{q}%"])
|
||||
params.append(limit)
|
||||
rows = db_query(
|
||||
"SELECT id, klub_id, file_name, file_path, file_size, mime, ocr_status, "
|
||||
"ocr_confidence, ai_invoice_no, ai_invoice_date, ai_vendor_name, ai_vendor_oib, "
|
||||
"ai_amount_gross, ai_currency, invoice_id, uploaded_by, "
|
||||
"uploaded_at FROM pgz_sport.invoice_uploads "
|
||||
f"WHERE {' AND '.join(where)} ORDER BY id DESC LIMIT %s",
|
||||
tuple(params))
|
||||
return {"count": len(rows), "rows": rows}
|
||||
|
||||
|
||||
@router.get("/racuni/ulazni/{rid}/uploads")
|
||||
def racuni_ulazni_uploads(rid: int):
|
||||
"""Uploads (file attachments) linked to an ulazni racun via invoice_id."""
|
||||
rows = db_query(
|
||||
"SELECT id, file_name, file_path, file_size, mime, ocr_status, "
|
||||
"ai_invoice_no, ai_vendor_name, ai_amount_gross, uploaded_at "
|
||||
"FROM pgz_sport.invoice_uploads WHERE invoice_id=%s ORDER BY id DESC",
|
||||
(rid,))
|
||||
return {"count": len(rows), "rows": rows}
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# 12) PUTNI NALOZI / EXPENSE REPORTS
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
@router.get("/expense-reports")
|
||||
def expense_reports_list(klub_id: Optional[int] = None,
|
||||
status: Optional[str] = None,
|
||||
report_type: Optional[str] = None,
|
||||
godina: Optional[int] = None,
|
||||
limit: int = 200):
|
||||
where = ["1=1"]
|
||||
params: list = []
|
||||
if klub_id:
|
||||
where.append("er.klub_id=%s"); params.append(klub_id)
|
||||
if status:
|
||||
where.append("er.status=%s"); params.append(status)
|
||||
if report_type:
|
||||
where.append("er.report_type=%s"); params.append(report_type)
|
||||
if godina:
|
||||
where.append("EXTRACT(YEAR FROM er.date_from)=%s"); params.append(godina)
|
||||
params.append(limit)
|
||||
rows = db_query(
|
||||
"SELECT er.id, er.klub_id, k.naziv AS klub_naziv, er.report_type, er.report_no, "
|
||||
"er.destination, er.purpose, er.date_from, er.date_to, er.km_driven, "
|
||||
"er.cost_total, er.dnevnice_count, er.dnevnice_amount, er.status, "
|
||||
"er.approved_at, er.paid_at, er.created_at "
|
||||
"FROM pgz_sport.expense_reports er "
|
||||
"LEFT JOIN pgz_sport.klubovi k ON k.id=er.klub_id "
|
||||
f"WHERE {' AND '.join(where)} ORDER BY er.id DESC LIMIT %s",
|
||||
tuple(params))
|
||||
return {"count": len(rows), "rows": rows}
|
||||
|
||||
|
||||
@router.get("/putni-nalog-racuni")
|
||||
def putni_nalog_racuni_list(putni_nalog_id: Optional[int] = None,
|
||||
invoice_id: Optional[int] = None,
|
||||
limit: int = 200):
|
||||
where = ["1=1"]
|
||||
params: list = []
|
||||
if putni_nalog_id:
|
||||
where.append("pnr.putni_nalog_id=%s"); params.append(putni_nalog_id)
|
||||
if invoice_id:
|
||||
where.append("pnr.invoice_id=%s"); params.append(invoice_id)
|
||||
params.append(limit)
|
||||
rows = db_query(
|
||||
"SELECT pnr.id, pnr.putni_nalog_id, pnr.invoice_id, pnr.kategorija, "
|
||||
"pnr.napomena, pnr.attached_at, "
|
||||
"er.report_no, er.destination, er.purpose, "
|
||||
"i.invoice_no, i.vendor_name, i.amount_gross, i.currency "
|
||||
"FROM pgz_sport.putni_nalog_racuni pnr "
|
||||
"LEFT JOIN pgz_sport.expense_reports er ON er.id=pnr.putni_nalog_id "
|
||||
"LEFT JOIN pgz_sport.invoices i ON i.id=pnr.invoice_id "
|
||||
f"WHERE {' AND '.join(where)} ORDER BY pnr.id DESC LIMIT %s",
|
||||
tuple(params))
|
||||
return {"count": len(rows), "rows": rows}
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# 13) PAYMENTS (uplate/isplate, bank reconciliation)
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
@router.get("/payments")
|
||||
def payments_list(klub_id: Optional[int] = None,
|
||||
matched_status: Optional[str] = None,
|
||||
payment_method: Optional[str] = None,
|
||||
godina: Optional[int] = None,
|
||||
limit: int = 200):
|
||||
where = ["1=1"]
|
||||
params: list = []
|
||||
if klub_id:
|
||||
where.append("p.klub_id=%s"); params.append(klub_id)
|
||||
if matched_status:
|
||||
where.append("p.matched_status=%s"); params.append(matched_status)
|
||||
if payment_method:
|
||||
where.append("p.payment_method=%s"); params.append(payment_method)
|
||||
if godina:
|
||||
where.append("EXTRACT(YEAR FROM p.payment_date)=%s"); params.append(godina)
|
||||
params.append(limit)
|
||||
rows = db_query(
|
||||
"SELECT p.id, p.klub_id, k.naziv AS klub_naziv, p.invoice_id, "
|
||||
"p.expense_report_id, p.clanarina_id, p.payment_date, p.amount, p.currency, "
|
||||
"p.payment_method, p.iban_from, p.iban_to, p.reference, p.description, "
|
||||
"p.bank_statement_no, p.bank_transaction_id, p.matched_status, p.created_at "
|
||||
"FROM pgz_sport.payments p "
|
||||
"LEFT JOIN pgz_sport.klubovi k ON k.id=p.klub_id "
|
||||
f"WHERE {' AND '.join(where)} ORDER BY p.payment_date DESC, p.id DESC LIMIT %s",
|
||||
tuple(params))
|
||||
return {"count": len(rows), "rows": rows}
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# 14) HEALTH/DEBUG
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
@router.get("/health")
|
||||
def erp_health():
|
||||
counts = {}
|
||||
for t in ["kontni_plan", "partneri", "dnevnik_zapisa", "knjizenja",
|
||||
"racuni_ulazni", "racuni_izlazni", "racun_stavke",
|
||||
"zaposlenici", "place_obracun"]:
|
||||
"zaposlenici", "place_obracun", "proracun",
|
||||
"invoice_uploads", "expense_reports", "putni_nalog_racuni", "payments"]:
|
||||
try:
|
||||
r = db_one(f"SELECT COUNT(*) AS c FROM pgz_sport.{t}")
|
||||
counts[t] = r["c"] if r else 0
|
||||
|
||||
Reference in New Issue
Block a user