Initial commit: ROA2WEB - FastAPI + Vue.js + Telegram Bot

Modern ERP Reports Application with microservices architecture

Tech Stack:
- Backend: FastAPI + python-oracledb (Oracle DB integration)
- Frontend: Vue.js 3 + PrimeVue + Vite
- Telegram Bot: python-telegram-bot + SQLite
- Infrastructure: Shared database pool, JWT authentication, SSH tunnel

Features:
- FastAPI backend with async Oracle connection pool
- Vue.js 3 responsive frontend with PrimeVue components
- Telegram bot alternative interface
- Microservices architecture with shared components
- Complete deployment support (Linux Docker + Windows IIS)
- Comprehensive testing (Playwright E2E + pytest)

Repository Structure:
- reports-app/ - Main application (backend, frontend, telegram-bot)
- shared/ - Shared components (database pool, auth, utils)
- deployment/ - Deployment scripts (Linux & Windows)
- docs/ - Project documentation
- security/ - Security scanning and git hooks
This commit is contained in:
2025-10-25 14:55:08 +03:00
commit 6b13ffa183
237 changed files with 70035 additions and 0 deletions

View File

@@ -0,0 +1,118 @@
from pydantic import BaseModel
from decimal import Decimal
from typing import List, Dict, Optional, Any
class TreasuryAccount(BaseModel):
"""Cont de trezorerie (bancă/casă)"""
cont: str # 5121, 5124, 5311, 5314
nume_cont: str # "Bancă LEI", "Casă VALUTA" etc
nume_banca: str # Numele băncii din vbalanta_parteneri.nume
sold: Decimal
valuta: str
class TrendData(BaseModel):
"""Model pentru datele de trend - MODEL VECHI"""
labels: List[str]
incasari: List[Decimal]
plati: List[Decimal]
trezorerie: List[Decimal]
incasari_total: Decimal
plati_total: Decimal
trezorerie_total: Decimal
incasari_change: Optional[float] = None
plati_change: Optional[float] = None
trezorerie_change: Optional[float] = None
class TrendsResponse(BaseModel):
"""Model pentru răspunsul endpoint-ului de trenduri - MODEL NOU"""
# Current period data
periods: List[str]
clienti_facturat: List[float]
clienti_incasat: List[float]
furnizori_facturat: List[float]
furnizori_achitat: List[float]
clienti_sold: List[float]
furnizori_sold: List[float]
trezorerie_sold: Optional[List[float]] = None
rata_incasare_clienti: List[float]
rata_achitare_furnizori: List[float]
# Previous period data (for year-over-year comparison in sparklines)
previous_periods: Optional[List[str]] = None
clienti_facturat_prev: Optional[List[float]] = None
clienti_incasat_prev: Optional[List[float]] = None
furnizori_facturat_prev: Optional[List[float]] = None
furnizori_achitat_prev: Optional[List[float]] = None
clienti_sold_prev: Optional[List[float]] = None
furnizori_sold_prev: Optional[List[float]] = None
trezorerie_sold_prev: Optional[List[float]] = None
# Metadata and analytics
metadata: Dict[str, Any]
growth_rates: Optional[Dict[str, float]] = None
class DashboardSummary(BaseModel):
"""Model pentru toate datele dashboard-ului"""
# CLIENȚI - statistici existente
clienti_total_facturat: Decimal # precdeb + debit (conturi 4111, 461)
clienti_total_incasat: Decimal # preccred + credit (conturi 4111, 461)
clienti_avansuri: Decimal # sold 419 (pasiv): credit - debit
clienti_sold_total: Decimal # (facturat - incasat) - avansuri
clienti_sold_restant: Decimal # sold cu datascad < azi
# CLIENȚI - NOI câmpuri pentru sold în termen
clienti_sold_in_termen: Decimal # sold cu datascad >= azi
# CLIENȚI - NOI detalieri restanțe (sold cu datascad < azi)
clienti_restant_7: Decimal # restant 1-7 zile
clienti_restant_14: Decimal # restant 8-14 zile
clienti_restant_30: Decimal # restant 15-30 zile
clienti_restant_60: Decimal # restant 31-60 zile
clienti_restant_90: Decimal # restant 61-90 zile
clienti_restant_90plus: Decimal # restant 90+ zile
# CLIENȚI - NOI detalieri scadențe (sold cu datascad >= azi)
clienti_scadent_7: Decimal # scadent în 1-7 zile
clienti_scadent_14: Decimal # scadent în 8-14 zile
clienti_scadent_30: Decimal # scadent în 15-30 zile
clienti_scadent_60: Decimal # scadent în 31-60 zile
clienti_scadent_90: Decimal # scadent în 61-90 zile
clienti_scadent_90plus: Decimal # scadent în 90+ zile
# FURNIZORI - statistici existente
furnizori_total_facturat: Decimal # preccred + credit (conturi 401, 404, 462)
furnizori_total_achitat: Decimal # precdeb + debit (conturi 401, 404, 462)
furnizori_avansuri: Decimal # sold 409x (activ): debit - credit
furnizori_sold_total: Decimal # (facturat - achitat) - avansuri
furnizori_sold_restant: Decimal # sold cu datascad < azi
# FURNIZORI - NOI câmpuri pentru sold în termen
furnizori_sold_in_termen: Decimal # sold cu datascad >= azi
# FURNIZORI - NOI detalieri restanțe (sold cu datascad < azi)
furnizori_restant_7: Decimal # restant 1-7 zile
furnizori_restant_14: Decimal # restant 8-14 zile
furnizori_restant_30: Decimal # restant 15-30 zile
furnizori_restant_60: Decimal # restant 31-60 zile
furnizori_restant_90: Decimal # restant 61-90 zile
furnizori_restant_90plus: Decimal # restant 90+ zile
# FURNIZORI - NOI detalieri scadențe (sold cu datascad >= azi)
furnizori_scadent_7: Decimal # scadent în 1-7 zile
furnizori_scadent_14: Decimal # scadent în 8-14 zile
furnizori_scadent_30: Decimal # scadent în 15-30 zile
furnizori_scadent_60: Decimal # scadent în 31-60 zile
furnizori_scadent_90: Decimal # scadent în 61-90 zile
furnizori_scadent_90plus: Decimal # scadent în 90+ zile
# TREZORERIE - existente
treasury_accounts: List[TreasuryAccount]
treasury_totals_by_currency: Dict[str, Decimal]
# DATE SUPLIMENTARE pentru trend analysis
clienti_facturat_luna_anterioara: Optional[Decimal] = Decimal('0')
furnizori_facturat_luna_anterioara: Optional[Decimal] = Decimal('0')
clienti_facturat_an_curent: Optional[Decimal] = Decimal('0')
clienti_facturat_an_anterior: Optional[Decimal] = Decimal('0')
furnizori_facturat_an_curent: Optional[Decimal] = Decimal('0')
furnizori_facturat_an_anterior: Optional[Decimal] = Decimal('0')

View File

@@ -0,0 +1,73 @@
"""
Modele Pydantic pentru facturi - Compatibile cu aplicația Flask existentă
"""
from pydantic import BaseModel, Field, validator
from datetime import date
from typing import Optional, List, Literal
from decimal import Decimal
class InvoiceBase(BaseModel):
"""Model de bază pentru factură - mapează exact pe rezultatul query-ului Flask"""
nume: str = Field(description="Numele partenerului")
nract: int = Field(description="Numărul actului")
dataact: Optional[date] = Field(description="Data actului")
datascad: Optional[date] = Field(description="Data scadentă")
contract: Optional[str] = Field(description="Numărul contractului")
cod_fiscal: Optional[str] = Field(description="Codul fiscal")
reg_comert: Optional[str] = Field(description="Registrul comerțului")
class Invoice(InvoiceBase):
"""Model complet pentru factură cu calcule financiare"""
totctva: Decimal = Field(description="Total cu TVA", decimal_places=2)
achitat: Decimal = Field(description="Suma achitată", decimal_places=2)
soldfinal: Decimal = Field(description="Soldul final", decimal_places=2)
css_class: Literal["", "invoice-paid", "invoice-overdue"] = Field(
default="", description="Clasa CSS pentru stilizare"
)
@validator('css_class', always=True)
def determine_css_class(cls, v, values):
"""Determină automat clasa CSS bazată pe status factură"""
if 'soldfinal' in values and 'datascad' in values:
sold = values['soldfinal']
data_scad = values['datascad']
if sold < 1:
return 'invoice-paid'
elif data_scad and data_scad < date.today() and sold != 0:
return 'invoice-overdue'
return ''
class InvoiceFilter(BaseModel):
"""Filtru pentru căutarea facturilor"""
company: str = Field(description="Codul firmei (schema Oracle)")
partner_type: Literal["CLIENTI", "FURNIZORI"] = Field(description="Tipul partenerului")
date_from: Optional[date] = Field(description="Data de început")
date_to: Optional[date] = Field(description="Data de sfârșit")
partner_name: Optional[str] = Field(description="Filtru după nume")
only_unpaid: bool = Field(default=True, description="Doar neachitate")
min_amount: Optional[Decimal] = Field(description="Suma minimă")
max_amount: Optional[Decimal] = Field(description="Suma maximă")
page: int = Field(default=1, ge=1, description="Pagina")
page_size: int = Field(default=50, ge=1, le=1000, description="Mărimea paginii")
class InvoiceListResponse(BaseModel):
"""Răspuns pentru lista de facturi"""
invoices: List[Invoice]
total_count: int
filtered_count: int
total_amount: Decimal
page: int
page_size: int
has_more: bool
class InvoiceSummary(BaseModel):
"""Rezumat pentru facturi - pentru dashboard"""
company: str
partner_type: str
total_invoices: int
total_amount: Decimal
paid_amount: Decimal
outstanding_amount: Decimal
overdue_amount: Decimal
overdue_count: int

View File

@@ -0,0 +1,37 @@
from pydantic import BaseModel
from decimal import Decimal
from datetime import datetime
from typing import Optional, List
class BankCashRegister(BaseModel):
"""Model pentru Registrul de Casă și Bancă"""
nume: str
nract: int
dataact: datetime
nume_cont_bancar: str # din vbalanta_parteneri.nume
incasari: Decimal
plati: Decimal
sold: Decimal
valuta: str
tip_registru: str # "BANCA LEI", "CASA VALUTA" etc
explicatia: str
class RegisterFilter(BaseModel):
"""Filtre pentru registrul de casă și bancă"""
company: str
date_from: Optional[datetime] = None
date_to: Optional[datetime] = None
partner_name: Optional[str] = None
page: int = 1
page_size: int = 50
class RegisterListResponse(BaseModel):
"""Răspuns pentru lista din registru"""
registers: List[BankCashRegister]
total_count: int
filtered_count: int
total_incasari: Decimal
total_plati: Decimal
page: int
page_size: int
has_more: bool