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>
This commit is contained in:
2025-12-02 15:41:56 +02:00
commit 0b732f7a7a
15 changed files with 5420 additions and 0 deletions

670
main.py Normal file
View File

@@ -0,0 +1,670 @@
#!/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()