Files
vending_data_intelligence_r…/main.py
Marius Mutu 0b732f7a7a Initial commit: Data Intelligence Report Generator
- Oracle ERP ROA integration with sales analytics and margin analysis
- Excel multi-sheet reports with conditional formatting
- PDF executive summaries with charts via ReportLab
- Optimized SQL queries (no cartesian products)
- Docker support for cross-platform deployment
- Configurable alert thresholds for business intelligence

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 15:41:56 +02:00

671 lines
26 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
Data Intelligence Report Generator
===================================
Generates comprehensive Excel and PDF reports with financial analytics
for ERP data stored in Oracle Database.
Usage:
python main.py [--months 12] [--output-dir ./output]
Author: Claude AI for ROMFAST SRL
"""
import sys
import argparse
from datetime import datetime
from pathlib import Path
import warnings
warnings.filterwarnings('ignore')
# Check dependencies
def check_dependencies():
"""Check if all required packages are installed"""
missing = []
packages = {
'oracledb': 'oracledb',
'pandas': 'pandas',
'openpyxl': 'openpyxl',
'matplotlib': 'matplotlib',
'reportlab': 'reportlab',
'dotenv': 'python-dotenv'
}
for import_name, pip_name in packages.items():
try:
__import__(import_name)
except ImportError:
missing.append(pip_name)
if missing:
print("❌ Pachete lipsă. Rulează:")
print(f" pip install {' '.join(missing)} --break-system-packages")
sys.exit(1)
check_dependencies()
import oracledb
import pandas as pd
from dateutil.relativedelta import relativedelta
from config import (
ORACLE_CONFIG, get_dsn, OUTPUT_DIR, COMPANY_NAME,
ANALYSIS_MONTHS, MIN_SALES_FOR_ANALYSIS, LOW_MARGIN_THRESHOLD,
RECOMMENDATION_THRESHOLDS
)
from queries import QUERIES
from report_generator import (
ExcelReportGenerator, PDFReportGenerator,
create_monthly_chart, create_client_concentration_chart, create_production_chart,
create_cash_cycle_chart
)
from recommendations import RecommendationsEngine
class OracleConnection:
"""Context manager for Oracle database connection"""
def __init__(self):
self.connection = None
def __enter__(self):
try:
print(f"🔌 Conectare la Oracle: {get_dsn()}...")
self.connection = oracledb.connect(
user=ORACLE_CONFIG['user'],
password=ORACLE_CONFIG['password'],
dsn=get_dsn()
)
print("✓ Conectat cu succes!")
return self.connection
except oracledb.Error as e:
print(f"❌ Eroare conexiune Oracle: {e}")
raise
def __exit__(self, exc_type, exc_val, exc_tb):
if self.connection:
self.connection.close()
print("✓ Conexiune închisă.")
def execute_query(connection, query_name: str, query_info: dict) -> pd.DataFrame:
"""Execute a query and return results as DataFrame"""
try:
sql = query_info['sql']
params = query_info.get('params', {})
print(f" 📊 Executare: {query_name}...", end=" ")
with connection.cursor() as cursor:
cursor.execute(sql, params)
columns = [col[0] for col in cursor.description]
rows = cursor.fetchall()
df = pd.DataFrame(rows, columns=columns)
print(f"✓ ({len(df)} rânduri)")
return df
except oracledb.Error as e:
print(f"❌ Eroare: {e}")
return pd.DataFrame()
def generate_reports(args):
"""Main function to generate all reports"""
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
excel_path = OUTPUT_DIR / f"data_intelligence_report_{timestamp}.xlsx"
pdf_path = OUTPUT_DIR / f"data_intelligence_report_{timestamp}.pdf"
print("\n" + "="*60)
print(" DATA INTELLIGENCE REPORT GENERATOR")
print("="*60)
print(f" Perioada: Ultimele {args.months} luni")
print(f" Output: {OUTPUT_DIR}")
print("="*60 + "\n")
# Update parameters with command line arguments
for query_info in QUERIES.values():
if 'months' in query_info.get('params', {}):
query_info['params']['months'] = args.months
# Calculate reporting period string
end_date = datetime.now()
start_date = end_date - relativedelta(months=args.months)
period_str = f"Perioada: {start_date.strftime('%d.%m.%Y')} - {end_date.strftime('%d.%m.%Y')}"
# Add period to descriptions for queries with months parameter
for query_name, query_info in QUERIES.items():
if 'months' in query_info.get('params', {}):
original_desc = query_info.get('description', '')
query_info['description'] = f"{original_desc}\n{period_str}"
# Connect and execute queries
results = {}
with OracleConnection() as conn:
print("\n📥 Extragere date din Oracle:\n")
for query_name, query_info in QUERIES.items():
df = execute_query(conn, query_name, query_info)
results[query_name] = df
# Generate Excel Report
print("\n📝 Generare raport Excel...")
excel_gen = ExcelReportGenerator(excel_path)
# Generate recommendations based on all data
print("\n🔍 Generare recomandări automate...")
recommendations_engine = RecommendationsEngine(RECOMMENDATION_THRESHOLDS)
recommendations_df = recommendations_engine.analyze_all(results)
results['recomandari'] = recommendations_df
print(f"{len(recommendations_df)} recomandări generate")
# Add sheets in logical order (updated per PLAN_INDICATORI_LICHIDITATE_YOY.md)
sheet_order = [
# SUMAR EXECUTIV
'sumar_executiv',
'sumar_executiv_yoy',
'recomandari',
# INDICATORI AGREGATI (MUTATI SUS - imagine de ansamblu)
'indicatori_agregati_venituri',
'indicatori_agregati_venituri_yoy',
'portofoliu_clienti',
'concentrare_risc',
'concentrare_risc_yoy',
'sezonalitate_lunara',
# INDICATORI GENERALI & LICHIDITATE
'indicatori_generali',
'indicatori_lichiditate',
'clasificare_datorii',
'grad_acoperire_datorii',
'proiectie_lichiditate',
# ALERTE
'vanzari_sub_cost',
'clienti_marja_mica',
# CICLU CASH
'ciclu_conversie_cash',
# ANALIZA CLIENTI
'marja_per_client',
'clienti_ranking_profit',
'frecventa_clienti',
'concentrare_clienti',
'trending_clienti',
'marja_client_categorie',
# PRODUSE
'top_produse',
'marja_per_categorie',
'marja_per_gestiune',
'articole_negestionabile',
'productie_vs_revanzare',
# PRETURI
'dispersie_preturi',
'clienti_sub_medie',
'evolutie_discount',
# FINANCIAR
'dso_dpo',
'dso_dpo_yoy',
'solduri_clienti',
'aging_creante',
'facturi_restante',
'solduri_furnizori',
'aging_datorii',
'facturi_restante_furnizori',
'pozitia_cash',
# ISTORIC
'vanzari_lunare',
# STOC
'stoc_curent',
'stoc_lent',
'rotatie_stocuri',
# PRODUCTIE
'analiza_prajitorie',
]
# Legends for each sheet explaining column calculations
legends = {
'recomandari': {
'CATEGORIE': 'Domeniu: Marja, Clienti, Stoc, Financiar',
'STATUS': 'OK = bine, ATENTIE = necesita atentie, ALERTA = actiune urgenta',
'VEZI_DETALII': 'Sheet-ul cu date detaliate pentru acest indicator'
},
'marja_per_client': {
'VANZARI_FARA_TVA': 'SUM(cantitate × preț) din fact_vfacturi_detalii',
'COST_TOTAL': 'SUM(cantitate × pret_achizitie)',
'MARJA_BRUTA': 'Vânzări - Cost = SUM(cantitate × (preț - pret_achizitie))',
'PROCENT_MARJA': 'Marja Brută / Vânzări × 100'
},
'clienti_marja_mica': {
'VANZARI_FARA_TVA': 'SUM(cantitate × preț) pentru client',
'MARJA_BRUTA': 'Vânzări - Cost',
'PROCENT_MARJA': 'Marja / Vânzări × 100 (sub 15% = alertă)'
},
'vanzari_sub_cost': {
'PRET_VANZARE': 'Preț unitar din factură (fact_vfacturi_detalii.pret)',
'COST': 'Preț achiziție (pret_achizitie)',
'PIERDERE': '(Preț vânzare - Cost) × Cantitate (negativ = pierdere)'
},
'stoc_curent': {
'TIP_GESTIUNE': 'Preț vânzare (nr_pag=7) sau Preț achiziție',
'VALOARE_STOC_ACHIZITIE': '(cants+cant-cante) × pret din vstoc',
'VALOARE_STOC_VANZARE': 'Doar pentru gestiuni preț vânzare, altfel gol'
},
'stoc_lent': {
'CANTITATE': 'Stoc final = cants + cant - cante',
'VALOARE': 'Cantitate × preț achiziție',
'ZILE_FARA_MISCARE': 'Zile de la ultima ieșire (dataout) sau intrare'
},
'rotatie_stocuri': {
'VALOARE_STOC': 'Stoc curent (cants+cant-cante) × preț achiziție',
'VANZARI_12_LUNI': 'Doar vânzări (nu transferuri/consumuri) din ultimele 12 luni',
'ROTATIE': 'Vânzări / Stoc (de câte ori s-a rotit stocul)',
'ZILE_STOC': 'La ritmul actual, în câte zile se epuizează'
},
'dispersie_preturi': {
'NR_TRANZACTII': 'Număr total linii factură pentru acest produs',
'VARIATIE_PROCENT': '(Preț Max - Preț Min) / Preț Mediu × 100',
'NR_LA_PRET_MIN': 'Câte tranzacții au fost la prețul minim',
'CLIENT_PRET_MIN': 'Primul client care a cumpărat la preț minim'
},
'top_produse': {
'VALOARE_VANZARI': 'SUM(cantitate × preț)',
'MARJA_BRUTA': 'SUM(cantitate × (preț - pret_achizitie))',
'PROCENT_MARJA': 'Marja / Vânzări × 100'
},
'marja_per_categorie': {
'VANZARI_FARA_TVA': 'Total vânzări pe subgrupă',
'COST_TOTAL': 'Total cost achiziție pe subgrupă',
'PROCENT_MARJA': 'Marja / Vânzări × 100'
},
'marja_per_gestiune': {
'VANZARI_FARA_TVA': 'Total vânzări pe gestiune (doar articole gestionabile)',
'MARJA_BRUTA': 'Total marjă pe gestiune',
'PROCENT_MARJA': 'Marja / Vânzări × 100'
},
'articole_negestionabile': {
'DENUMIRE': 'Nume articol negestionabil (in_stoc=0)',
'VANZARI_FARA_TVA': 'Total vânzări pentru articole care nu se țin pe stoc',
'MARJA_BRUTA': 'Vânzări - Cost',
'PROCENT_MARJA': 'Marja / Vânzări × 100'
},
'vanzari_lunare': {
'VANZARI_FARA_TVA': 'Total vânzări în lună',
'MARJA_BRUTA': 'Total marjă în lună',
'NR_FACTURI': 'Număr facturi emise',
'NR_CLIENTI': 'Clienți unici activi'
},
# NEW legends for financial and aggregated sheets
'indicatori_agregati_venituri': {
'LINIE_BUSINESS': 'Producție proprie / Materii prime / Marfă revândută',
'PROCENT_VENITURI': 'Contribuția la totalul vânzărilor',
'CONTRIBUTIE_PROFIT': 'Contribuția la profitul total (%)'
},
'sezonalitate_lunara': {
'MEDIE_VANZARI': 'Media vânzărilor pe 24 luni pentru această lună',
'DEVIERE_PROCENT': 'Cât de mult deviază de la media globală',
'CLASIFICARE': 'LUNĂ PUTERNICĂ / LUNĂ SLABĂ / NORMAL'
},
'portofoliu_clienti': {
'VALOARE': 'Numărul de clienți în fiecare categorie',
'EXPLICATIE': 'Definiția categoriei de clienți'
},
'concentrare_risc': {
'PROCENT': 'Procentul din vânzări pentru Top N clienți',
'STATUS': 'OK / ATENTIE / RISC MARE'
},
'ciclu_conversie_cash': {
'INDICATOR': 'DIO (zile stoc) + DSO (zile încasare) - DPO (zile plată)',
'ZILE': 'Numărul de zile pentru fiecare component',
'EXPLICATIE': 'Ce reprezintă fiecare indicator'
},
'clienti_ranking_profit': {
'RANG_PROFIT': 'Poziția clientului după profit (nu vânzări)',
'RANG_VANZARI': 'Poziția clientului după vânzări',
'PROFIT_BRUT': 'Vânzări - Cost = profitul efectiv adus'
},
'frecventa_clienti': {
'COMENZI_PE_LUNA': 'Media comenzilor pe lună',
'VALOARE_MEDIE_COMANDA': 'Valoarea medie per comandă',
'EVOLUTIE_FRECVENTA_YOY': 'Schimbarea frecvenței față de anul trecut'
},
'marja_client_categorie': {
'STATUS_MARJA': 'OK / MARJĂ MICĂ (<15%) / PIERDERE (negativă)',
'CATEGORIA': 'Grupa de produse',
'PROCENT_MARJA': 'Marja pentru acest client la această categorie'
},
'evolutie_discount': {
'PRET_INITIAL': 'Prețul mediu în primele 6 luni',
'PRET_ACTUAL': 'Prețul mediu în ultimele 6 luni',
'VARIATIE_PRET_PROCENT': 'Scăderea/creșterea prețului (negativ = discount)'
},
'dso_dpo': {
'DSO': 'Days Sales Outstanding - zile medii încasare clienți',
'DPO': 'Days Payables Outstanding - zile medii plată furnizori',
'STATUS': 'OK / ATENTIE / ALERTA'
},
'solduri_clienti': {
'SOLD_CURENT': 'Suma de încasat de la client (din cont 4111)',
'TIP_SOLD': 'Creanță (ne datorează) sau Avans client (am încasat în avans)'
},
'aging_creante': {
'NEAJUNS_SCADENTA': 'Facturi nescadente încă',
'ZILE_1_30': 'Restanțe 1-30 zile',
'PESTE_90_ZILE': 'Restanțe critice >90 zile - risc de neîncasare'
},
'facturi_restante': {
'ZILE_INTARZIERE': 'Zile de la scadență',
'SUMA_RESTANTA': 'Valoarea rămasă de încasat'
},
'aging_datorii': {
'NEAJUNS_SCADENTA': 'Datorii neajunse la scadență',
'ZILE_1_30': 'Restanțe 1-30 zile',
'ZILE_31_60': 'Restanțe 31-60 zile',
'ZILE_61_90': 'Restanțe 61-90 zile',
'PESTE_90_ZILE': 'Restanțe critice >90 zile',
'TOTAL_SOLD': 'Total datorii către furnizor'
},
'facturi_restante_furnizori': {
'ZILE_INTARZIERE': 'Zile de la scadență',
'SUMA_RESTANTA': 'Valoarea rămasă de plătit'
},
'solduri_furnizori': {
'SOLD_CURENT': 'Suma de plătit furnizorului (din cont 401)',
'TIP_SOLD': 'Datorie (trebuie să plătim) sau Avans (am plătit în avans)'
},
'pozitia_cash': {
'SOLD_CURENT': 'Disponibilul curent în cont/casă',
'DESCRIERE': 'Tipul contului (bancă/casă, lei/valută)'
},
# =====================================================================
# NEW: Legends for Indicatori Generali, Lichiditate, YoY sheets
# =====================================================================
'indicatori_generali': {
'INDICATOR': 'Grad îndatorare, autonomie financiară, ROA, marjă netă',
'VALOARE': 'Valoarea calculată a indicatorului',
'STATUS': 'OK / ATENȚIE / ALERTĂ bazat pe praguri standard',
'RECOMANDARE': 'Acțiune sugerată pentru îmbunătățire'
},
'indicatori_lichiditate': {
'INDICATOR': 'Lichiditate curentă, rapidă, cash ratio, fond de rulment',
'VALOARE': 'Valoarea calculată (rată sau sumă RON)',
'STATUS': 'OK / ATENȚIE / ALERTĂ',
'INTERPRETARE': 'Ce înseamnă valoarea pentru business'
},
'clasificare_datorii': {
'CATEGORIE': 'Termen scurt (<30z) / mediu (31-90z) / lung (>90z)',
'VALOARE': 'Suma datoriilor în categoria respectivă',
'NR_FACTURI': 'Numărul facturilor în acea categorie'
},
'grad_acoperire_datorii': {
'VALOARE': 'Cash disponibil + încasări așteptate vs plăți scadente',
'ACOPERIRE': 'OK / ATENȚIE / DEFICIT - dacă puteți plăti datoriile',
'EXPLICATIE': 'Ce înseamnă pentru fluxul de numerar'
},
'proiectie_lichiditate': {
'PERIOADA': 'Azi / 30 zile / 60 zile / 90 zile',
'SOLD_PROIECTAT': 'Cash estimat la sfârșitul perioadei',
'FLUX_NET': 'Încasări - Plăți pentru perioada respectivă',
'STATUS': 'OK dacă sold pozitiv, ALERTĂ dacă negativ'
},
'sumar_executiv_yoy': {
'VALOARE_CURENTA': 'Valoarea din ultimele 12 luni',
'VALOARE_ANTERIOARA': 'Valoarea din anul anterior (12-24 luni)',
'VARIATIE_PROCENT': 'Creștere/scădere procentuală',
'TREND': 'CREȘTERE / SCĂDERE / STABIL'
},
'dso_dpo_yoy': {
'VALOARE_CURENTA': 'Zile încasare/plată actuale',
'VALOARE_ANTERIOARA': 'Zile în perioada anterioară',
'VARIATIE_ZILE': 'Diferența în zile (+ = mai rău pentru DSO, mai bine pentru DPO)',
'TREND': 'ÎMBUNĂTĂȚIRE / DETERIORARE / STABIL'
},
'concentrare_risc_yoy': {
'PROCENT_CURENT': '% vânzări la Top N clienți - an curent',
'PROCENT_ANTERIOR': '% vânzări la Top N clienți - an trecut',
'VARIATIE': 'Schimbarea în puncte procentuale',
'TREND': 'DIVERSIFICARE (bine) / CONCENTRARE (risc) / STABIL'
},
'indicatori_agregati_venituri_yoy': {
'LINIE_BUSINESS': 'Producție proprie / Materii prime / Marfă',
'VANZARI_CURENTE': 'Vânzări în ultimele 12 luni',
'VANZARI_ANTERIOARE': 'Vânzări în perioada anterioară',
'VARIATIE_PROCENT': 'Creștere/scădere procentuală',
'TREND': 'CREȘTERE / SCĂDERE / STABIL'
},
'analiza_prajitorie': {
'CANTITATE_INTRARI': 'Cantitate intrata (cant > 0, cante = 0)',
'VALOARE_INTRARI': 'Valoare intrari = cantitate x pret',
'CANTITATE_IESIRI': 'Cantitate iesita (cant = 0, cante > 0)',
'VALOARE_IESIRI': 'Valoare iesiri = cantitate x pret',
'CANTITATE_TRANSFORMARI_IN': 'Cantitate intrata in transformari',
'CANTITATE_TRANSFORMARI_OUT': 'Cantitate iesita din transformari',
'SOLD_NET_CANTITATE': 'Sold net = Total intrari - Total iesiri',
'SOLD_NET_VALOARE': 'Valoare neta a soldului'
}
}
for query_name in sheet_order:
if query_name in results:
# Tratare speciala pentru 'sumar_executiv' - adauga recomandari sub KPIs
if query_name == 'sumar_executiv':
query_info = QUERIES.get(query_name, {})
excel_gen.add_sheet_with_recommendations(
name='Sumar Executiv',
df=results['sumar_executiv'],
recommendations_df=results.get('recomandari'),
title=query_info.get('title', 'Sumar Executiv'),
description=query_info.get('description', ''),
legend=legends.get('sumar_executiv'),
top_n_recommendations=5
)
# Pastreaza sheet-ul complet de recomandari
elif query_name == 'recomandari':
excel_gen.add_sheet(
name='RECOMANDARI',
df=results['recomandari'],
title='Recomandari Automate (Lista Completa)',
description='Toate insight-urile si actiunile sugerate bazate pe analiza datelor',
legend=legends.get('recomandari')
)
elif query_name in QUERIES:
query_info = QUERIES[query_name]
# Create short sheet name from query name
sheet_name = query_name.replace('_', ' ').title()[:31]
excel_gen.add_sheet(
name=sheet_name,
df=results[query_name],
title=query_info.get('title', query_name),
description=query_info.get('description', ''),
legend=legends.get(query_name)
)
excel_gen.save()
# Generate PDF Report
print("\n📄 Generare raport PDF...")
pdf_gen = PDFReportGenerator(pdf_path, company_name=COMPANY_NAME)
# Title page
pdf_gen.add_title_page()
# KPIs
pdf_gen.add_kpi_section(results.get('sumar_executiv'))
# NEW: Indicatori Generali section
if 'indicatori_generali' in results and not results['indicatori_generali'].empty:
pdf_gen.add_table_section(
"Indicatori Generali de Business",
results.get('indicatori_generali'),
columns=['INDICATOR', 'VALOARE', 'STATUS', 'RECOMANDARE'],
max_rows=10
)
# NEW: Indicatori Lichiditate section
if 'indicatori_lichiditate' in results and not results['indicatori_lichiditate'].empty:
pdf_gen.add_table_section(
"Indicatori de Lichiditate",
results.get('indicatori_lichiditate'),
columns=['INDICATOR', 'VALOARE', 'STATUS', 'RECOMANDARE'],
max_rows=10
)
# NEW: Proiecție Lichiditate
if 'proiectie_lichiditate' in results and not results['proiectie_lichiditate'].empty:
pdf_gen.add_table_section(
"Proiecție Cash Flow 30/60/90 zile",
results.get('proiectie_lichiditate'),
columns=['PERIOADA', 'SOLD_PROIECTAT', 'INCASARI', 'PLATI', 'STATUS'],
max_rows=5
)
# NEW: Recommendations section (top priorities)
if 'recomandari' in results and not results['recomandari'].empty:
pdf_gen.add_recommendations_section(results['recomandari'])
# Alerts
pdf_gen.add_alerts_section({
'vanzari_sub_cost': results.get('vanzari_sub_cost', pd.DataFrame()),
'clienti_marja_mica': results.get('clienti_marja_mica', pd.DataFrame())
})
pdf_gen.add_page_break()
# Monthly chart
if 'vanzari_lunare' in results and not results['vanzari_lunare'].empty:
fig = create_monthly_chart(results['vanzari_lunare'])
pdf_gen.add_chart_image(fig, "Evoluția Vânzărilor și Marjei")
# Client concentration
if 'concentrare_clienti' in results and not results['concentrare_clienti'].empty:
fig = create_client_concentration_chart(results['concentrare_clienti'])
pdf_gen.add_chart_image(fig, "Concentrare Clienți")
pdf_gen.add_page_break()
# NEW: Cash Conversion Cycle chart
if 'ciclu_conversie_cash' in results and not results['ciclu_conversie_cash'].empty:
fig = create_cash_cycle_chart(results['ciclu_conversie_cash'])
pdf_gen.add_chart_image(fig, "Ciclu Conversie Cash (DIO + DSO - DPO)")
# Production vs Resale
if 'productie_vs_revanzare' in results and not results['productie_vs_revanzare'].empty:
fig = create_production_chart(results['productie_vs_revanzare'])
pdf_gen.add_chart_image(fig, "Producție Proprie vs Revânzare")
# Top clients table
pdf_gen.add_table_section(
"Top 15 Clienți după Vânzări",
results.get('marja_per_client'),
columns=['CLIENT', 'VANZARI_FARA_TVA', 'MARJA_BRUTA', 'PROCENT_MARJA'],
max_rows=15
)
pdf_gen.add_page_break()
# Top products
pdf_gen.add_table_section(
"Top 15 Produse după Vânzări",
results.get('top_produse'),
columns=['PRODUS', 'VALOARE_VANZARI', 'MARJA_BRUTA', 'PROCENT_MARJA'],
max_rows=15
)
# Trending clients
pdf_gen.add_table_section(
"Trending Clienți (YoY)",
results.get('trending_clienti'),
columns=['CLIENT', 'VANZARI_12_LUNI', 'VANZARI_AN_ANTERIOR', 'VARIATIE_PROCENT', 'TREND'],
max_rows=15
)
# NEW: Aging Creanțe table
if 'aging_creante' in results and not results['aging_creante'].empty:
pdf_gen.add_page_break()
pdf_gen.add_table_section(
"Aging Creanțe (Vechime Facturi Neîncasate)",
results.get('aging_creante'),
columns=['CLIENT', 'NEAJUNS_SCADENTA', 'ZILE_1_30', 'ZILE_31_60', 'PESTE_90_ZILE', 'TOTAL_SOLD'],
max_rows=15
)
# Stoc lent
if 'stoc_lent' in results and not results['stoc_lent'].empty:
pdf_gen.add_page_break()
pdf_gen.add_table_section(
"Stoc Lent (>90 zile fără mișcare)",
results.get('stoc_lent'),
columns=['PRODUS', 'NUME_GESTIUNE', 'CANTITATE', 'VALOARE', 'ZILE_FARA_MISCARE'],
max_rows=20
)
pdf_gen.save()
# Summary
print("\n" + "="*60)
print(" ✅ RAPOARTE GENERATE CU SUCCES!")
print("="*60)
print(f"\n 📊 Excel: {excel_path}")
print(f" 📄 PDF: {pdf_path}")
print("\n" + "="*60)
return excel_path, pdf_path
def main():
"""Entry point"""
parser = argparse.ArgumentParser(
description='Data Intelligence Report Generator',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Exemple:
python main.py # Raport pentru ultimele 12 luni
python main.py --months 6 # Raport pentru ultimele 6 luni
python main.py --output-dir /tmp # Salvare în alt director
"""
)
parser.add_argument(
'--months', '-m',
type=int,
default=ANALYSIS_MONTHS,
help=f'Numărul de luni pentru analiză (default: {ANALYSIS_MONTHS})'
)
parser.add_argument(
'--output-dir', '-o',
type=Path,
default=OUTPUT_DIR,
help=f'Directorul pentru output (default: {OUTPUT_DIR})'
)
args = parser.parse_args()
# Ensure output directory exists
args.output_dir.mkdir(parents=True, exist_ok=True)
try:
generate_reports(args)
except KeyboardInterrupt:
print("\n\n⚠️ Întrerupt de utilizator.")
sys.exit(1)
except Exception as e:
print(f"\n❌ Eroare: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
if __name__ == '__main__':
main()