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
73 lines
3.0 KiB
Python
73 lines
3.0 KiB
Python
"""
|
|
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 |