Files
vending_data_intelligence_r…/queries.py
Marius Mutu 8e076af166 Optimize slow SQL queries + Fix duplicate invoices bug
Performance optimizations (views → base tables):
- SUMAR_EXECUTIV: 6 UNION ALL → 1 CTE (130s → 0.4s)
- SUMAR_EXECUTIV_YOY: 2 CTEs → 1 scan with CASE (129s → 0.4s)
- CLIENTI_SUB_MEDIE: 2 duplicate CTEs → 1 base CTE (131s → 0.2s)
- VANZARI_LUNARE: view → base table swap (130s → 0.4s)
- INDICATORI_AGREGATI_VENITURI_YOY: 2 CTEs → 1 scan (129s → 0.2s)

Bug fix (duplicate invoices in vireg_parteneri):
- AGING_CREANTE, FACTURI_RESTANTE, AGING_DATORII,
  FACTURI_RESTANTE_FURNIZORI now filter by last closed
  calendar period to avoid showing same invoice multiple times

Total query time reduced from ~31 min to ~22 min.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-11 16:29:07 +02:00

2491 lines
107 KiB
Python

"""
SQL Queries for Data Intelligence Report
All queries use the existing views: fact_vfacturi2, fact_vfacturi_detalii, vstoc, vrul
IMPORTANT: Price calculation considers pret_cu_tva flag:
- If pret_cu_tva = 1: price includes VAT, must divide by (1 + proc_tvav/100)
- If pret_cu_tva = 0: price is already without VAT
Formula: CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END
"""
# =============================================================================
# 1. MARJA PER CLIENT
# =============================================================================
MARJA_PER_CLIENT = """
SELECT
f.id_part,
f.client,
f.cod_fiscal,
COUNT(DISTINCT f.id_vanzare) AS nr_facturi,
ROUND(SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END), 2) AS vanzari_fara_tva,
ROUND(SUM(d.cantitate * d.pret_achizitie), 2) AS cost_total,
ROUND(SUM(d.cantitate * (CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END - d.pret_achizitie)), 2) AS marja_bruta,
ROUND(
CASE WHEN SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END) > 0
THEN SUM(d.cantitate * (CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END - d.pret_achizitie)) * 100.0 / SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END)
ELSE 0 END
, 2) AS procent_marja
FROM fact_vfacturi2 f
JOIN fact_vfacturi_detalii d ON d.id_vanzare = f.id_vanzare
WHERE f.sters = 0
AND d.sters = 0
AND f.tip > 0
AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -:months)
GROUP BY f.id_part, f.client, f.cod_fiscal
ORDER BY marja_bruta DESC
"""
# =============================================================================
# 2. CLIENȚI CU MARJĂ MICĂ (sub prag)
# =============================================================================
CLIENTI_MARJA_MICA = """
SELECT * FROM (
SELECT
f.client,
f.cod_fiscal,
ROUND(SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END), 2) AS vanzari_fara_tva,
ROUND(SUM(d.cantitate * (CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END - d.pret_achizitie)), 2) AS marja_bruta,
ROUND(
CASE WHEN SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END) > 0
THEN SUM(d.cantitate * (CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END - d.pret_achizitie)) * 100.0 / SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END)
ELSE 0 END
, 2) AS procent_marja
FROM fact_vfacturi2 f
JOIN fact_vfacturi_detalii d ON d.id_vanzare = f.id_vanzare
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -:months)
GROUP BY f.id_part, f.client, f.cod_fiscal
HAVING SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END) > :min_sales
)
WHERE procent_marja < :margin_threshold
ORDER BY vanzari_fara_tva DESC
"""
# =============================================================================
# 3. MARJA PER CATEGORIE (Grupă + Subgrupă)
# =============================================================================
MARJA_PER_CATEGORIE = """
SELECT
NVL(sg.grupa, 'NECLASIFICAT') AS grupa,
NVL(d.subgrupa, 'NECLASIFICAT') AS subgrupa,
COUNT(DISTINCT f.id_vanzare) AS nr_facturi,
ROUND(SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END), 2) AS vanzari_fara_tva,
ROUND(SUM(d.cantitate * d.pret_achizitie), 2) AS cost_total,
ROUND(SUM(d.cantitate * (CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END - d.pret_achizitie)), 2) AS marja_bruta,
ROUND(
CASE WHEN SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END) > 0
THEN SUM(d.cantitate * (CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END - d.pret_achizitie)) * 100.0 / SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END)
ELSE 0 END
, 2) AS procent_marja
FROM fact_vfacturi2 f
JOIN fact_vfacturi_detalii d ON d.id_vanzare = f.id_vanzare
LEFT JOIN vgest_art_sbgr sg ON d.id_subgrupa = sg.id_subgrupa
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -:months)
GROUP BY sg.id_grupa, sg.grupa, d.id_subgrupa, d.subgrupa
ORDER BY vanzari_fara_tva DESC
"""
# =============================================================================
# 4. PRODUCȚIE PROPRIE vs MARFĂ REVÂNDUTĂ
# =============================================================================
PRODUCTIE_VS_REVANZARE = """
SELECT
CASE
WHEN d.cont IN ('341', '345') THEN 'Producție proprie'
WHEN d.cont = '301' THEN 'Materii prime'
ELSE 'Marfă revândută'
END AS tip_produs,
ROUND(SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END), 2) AS vanzari_fara_tva,
ROUND(SUM(d.cantitate * d.pret_achizitie), 2) AS cost_total,
ROUND(SUM(d.cantitate * (CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END - d.pret_achizitie)), 2) AS marja_bruta,
ROUND(
CASE WHEN SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END) > 0
THEN SUM(d.cantitate * (CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END - d.pret_achizitie)) * 100.0 / SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END)
ELSE 0 END
, 2) AS procent_marja
FROM fact_vfacturi2 f
JOIN fact_vfacturi_detalii d ON d.id_vanzare = f.id_vanzare
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -:months)
GROUP BY CASE
WHEN d.cont IN ('341', '345') THEN 'Producție proprie'
WHEN d.cont = '301' THEN 'Materii prime'
ELSE 'Marfă revândută'
END
ORDER BY vanzari_fara_tva DESC
"""
# =============================================================================
# 5. DISPERSIA PREȚURILOR PER PRODUS
# =============================================================================
DISPERSIE_PRETURI = """
WITH preturi_detalii AS (
SELECT
d.id_articol,
d.denumire,
d.subgrupa,
f.id_part,
f.client,
CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END AS pret_fara_tva,
d.cantitate,
MIN(CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END) OVER (PARTITION BY d.id_articol) AS pret_min_global
FROM fact_vfacturi2 f
JOIN fact_vfacturi_detalii d ON d.id_vanzare = f.id_vanzare
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -:months)
AND d.pret > 0
)
SELECT
denumire AS produs,
NVL(subgrupa, 'NECLASIFICAT') AS subgrupa,
COUNT(DISTINCT id_part) AS nr_clienti,
COUNT(*) AS nr_tranzactii,
ROUND(MIN(pret_fara_tva), 2) AS pret_minim,
ROUND(MAX(pret_fara_tva), 2) AS pret_maxim,
ROUND(AVG(pret_fara_tva), 2) AS pret_mediu,
ROUND((MAX(pret_fara_tva) - MIN(pret_fara_tva)) * 100.0 / NULLIF(AVG(pret_fara_tva), 0), 2) AS variatie_procent,
SUM(CASE WHEN pret_fara_tva = pret_min_global THEN 1 ELSE 0 END) AS nr_la_pret_min,
MAX(CASE WHEN pret_fara_tva = pret_min_global THEN client END) AS client_pret_min
FROM preturi_detalii
GROUP BY id_articol, denumire, subgrupa
HAVING COUNT(DISTINCT id_part) >= 3
AND MAX(pret_fara_tva) > MIN(pret_fara_tva) * 1.2
ORDER BY variatie_procent DESC
FETCH FIRST 50 ROWS ONLY
"""
# =============================================================================
# 6. CLIENȚI CARE CUMPĂRĂ SUB MEDIE
# =============================================================================
# OPTIMIZED: Single base CTE scans once, derives both aggregations from it
CLIENTI_SUB_MEDIE = """
WITH base_prices AS (
SELECT
d.id_articol,
f.id_part,
p.denumire AS client,
CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END AS pret_fara_tva,
d.cantitate
FROM VANZARI f
JOIN VANZARI_DETALII d ON d.id_vanzare = f.id_vanzare
JOIN NOM_PARTENERI p ON f.id_part = p.id_part
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -:months)
AND d.pret > 0
),
preturi_medii AS (
SELECT id_articol, AVG(pret_fara_tva) AS pret_mediu
FROM base_prices
GROUP BY id_articol
),
preturi_client AS (
SELECT id_articol, id_part, client, AVG(pret_fara_tva) AS pret_client, SUM(cantitate) AS cantitate_totala
FROM base_prices
GROUP BY id_articol, id_part, client
)
SELECT
a.denumire AS produs,
pc.client,
ROUND(pc.pret_client, 2) AS pret_platit,
ROUND(pm.pret_mediu, 2) AS pret_mediu,
ROUND((pm.pret_mediu - pc.pret_client) * 100.0 / pm.pret_mediu, 2) AS discount_vs_medie,
pc.cantitate_totala
FROM preturi_client pc
JOIN preturi_medii pm ON pm.id_articol = pc.id_articol
JOIN NOM_ARTICOLE a ON a.id_articol = pc.id_articol
WHERE pc.pret_client < pm.pret_mediu * 0.85
ORDER BY discount_vs_medie DESC
FETCH FIRST 100 ROWS ONLY
"""
# =============================================================================
# 7. VÂNZĂRI SUB COST (ALERTĂ CRITICĂ)
# =============================================================================
VANZARI_SUB_COST = """
SELECT
f.data_act,
f.serie_act || ' ' || f.numar_act AS factura,
f.client,
d.denumire AS produs,
d.cantitate,
ROUND(CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END, 2) AS pret_vanzare,
ROUND(d.pret_achizitie, 2) AS cost,
ROUND((CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END - d.pret_achizitie) * d.cantitate, 2) AS pierdere
FROM fact_vfacturi2 f
JOIN fact_vfacturi_detalii d ON d.id_vanzare = f.id_vanzare
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -:months)
AND d.pret_achizitie > 0
AND d.pret > 0
AND CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END < d.pret_achizitie
ORDER BY pierdere ASC
FETCH FIRST 100 ROWS ONLY
"""
# =============================================================================
# 8. TRENDING CLIENȚI YoY
# =============================================================================
TRENDING_CLIENTI = """
WITH vanzari_perioade AS (
SELECT
f.id_part,
f.client,
SUM(CASE WHEN f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -12)
THEN d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END ELSE 0 END) AS vanzari_an_curent,
SUM(CASE WHEN f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -24)
AND f.data_act < ADD_MONTHS(TRUNC(SYSDATE), -12)
THEN d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END ELSE 0 END) AS vanzari_an_trecut
FROM fact_vfacturi2 f
JOIN fact_vfacturi_detalii d ON d.id_vanzare = f.id_vanzare
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -24)
GROUP BY f.id_part, f.client
)
SELECT
client,
ROUND(vanzari_an_curent, 2) AS vanzari_12_luni,
ROUND(vanzari_an_trecut, 2) AS vanzari_an_anterior,
ROUND(
CASE WHEN vanzari_an_trecut > 0
THEN (vanzari_an_curent - vanzari_an_trecut) * 100.0 / vanzari_an_trecut
ELSE NULL END
, 2) AS variatie_procent,
CASE
WHEN vanzari_an_trecut = 0 AND vanzari_an_curent > 0 THEN 'CLIENT NOU'
WHEN vanzari_an_curent = 0 AND vanzari_an_trecut > 0 THEN 'PIERDUT'
WHEN vanzari_an_curent > vanzari_an_trecut * 1.2 THEN 'CREȘTERE'
WHEN vanzari_an_curent < vanzari_an_trecut * 0.8 THEN 'SCĂDERE'
ELSE 'STABIL'
END AS trend
FROM vanzari_perioade
WHERE vanzari_an_curent > 0 OR vanzari_an_trecut > 0
ORDER BY variatie_procent DESC NULLS LAST
"""
# =============================================================================
# 9. CONCENTRARE CLIENȚI (TOP 30 + REST)
# =============================================================================
CONCENTRARE_CLIENTI = """
WITH total_vanzari AS (
SELECT SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END) AS total
FROM fact_vfacturi2 f
JOIN fact_vfacturi_detalii d ON d.id_vanzare = f.id_vanzare
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -:months)
),
vanzari_client AS (
SELECT
f.client,
SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END) AS vanzari,
ROW_NUMBER() OVER (ORDER BY SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END) DESC) AS rn
FROM fact_vfacturi2 f
JOIN fact_vfacturi_detalii d ON d.id_vanzare = f.id_vanzare
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -:months)
GROUP BY f.id_part, f.client
),
top_clienti AS (
SELECT
vc.client,
ROUND(vc.vanzari, 2) AS vanzari,
ROUND(vc.vanzari * 100.0 / tv.total, 2) AS procent_din_total,
vc.rn
FROM vanzari_client vc
CROSS JOIN total_vanzari tv
WHERE vc.rn <= 30
),
rest_clienti AS (
SELECT
'ALȚI CLIENȚI' AS client,
ROUND(SUM(vc.vanzari), 2) AS vanzari,
ROUND(SUM(vc.vanzari) * 100.0 / tv.total, 2) AS procent_din_total,
31 AS rn
FROM vanzari_client vc
CROSS JOIN total_vanzari tv
WHERE vc.rn > 30
GROUP BY tv.total
HAVING SUM(vc.vanzari) > 0
)
SELECT
client,
vanzari,
procent_din_total,
SUM(procent_din_total) OVER (ORDER BY rn) AS procent_cumulat
FROM (
SELECT * FROM top_clienti
UNION ALL
SELECT * FROM rest_clienti
)
ORDER BY rn
"""
# =============================================================================
# 10. VÂNZĂRI LUNARE (SEZONALITATE)
# =============================================================================
# OPTIMIZED: Use base tables instead of views (removes 29 unnecessary JOINs)
VANZARI_LUNARE = """
SELECT
TO_CHAR(f.data_act, 'YYYY-MM') AS luna,
ROUND(SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END), 2) AS vanzari_fara_tva,
ROUND(SUM(d.cantitate * (CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END - d.pret_achizitie)), 2) AS marja_bruta,
COUNT(DISTINCT f.id_vanzare) AS nr_facturi,
COUNT(DISTINCT f.id_part) AS nr_clienti
FROM VANZARI f
JOIN VANZARI_DETALII d ON d.id_vanzare = f.id_vanzare
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -24)
GROUP BY TO_CHAR(f.data_act, 'YYYY-MM')
ORDER BY luna
"""
# =============================================================================
# 11. STOC CURENT PER GESTIUNE
# =============================================================================
STOC_CURENT = """
SELECT
s.nume_gestiune,
CASE WHEN g.nr_pag = 7 THEN 'Marfă preț vânzare' ELSE 'Marfă preț achiziție' END AS tip_gestiune,
s.grupa,
s.subgrupa,
COUNT(DISTINCT s.id_articol) AS nr_articole,
ROUND(SUM((s.cants + s.cant - s.cante) * s.pret), 2) AS valoare_stoc_achizitie,
CASE
WHEN g.nr_pag = 7 THEN ROUND(SUM((s.cants + s.cant - s.cante) * s.pretv), 2)
ELSE NULL
END AS valoare_stoc_vanzare
FROM vstoc s
JOIN nom_gestiuni g ON s.id_gestiune = g.id_gestiune
WHERE s.an = EXTRACT(YEAR FROM SYSDATE)
AND s.luna = EXTRACT(MONTH FROM SYSDATE)
AND (s.cants + s.cant - s.cante) > 0
GROUP BY s.id_gestiune, s.nume_gestiune, g.nr_pag, s.id_grupa, s.grupa, s.id_subgrupa, s.subgrupa
ORDER BY valoare_stoc_achizitie DESC
"""
# =============================================================================
# 12. STOC LENT (fără mișcare > 90 zile)
# =============================================================================
STOC_LENT = """
SELECT
s.denumire AS produs,
s.nume_gestiune,
(s.cants + s.cant - s.cante) AS cantitate,
ROUND((s.cants + s.cant - s.cante) * s.pret, 2) AS valoare,
s.dataout AS ultima_iesire,
ROUND(SYSDATE - NVL(s.dataout, s.datain)) AS zile_fara_miscare
FROM vstoc s
WHERE s.an = EXTRACT(YEAR FROM SYSDATE)
AND s.luna = EXTRACT(MONTH FROM SYSDATE)
AND (s.cants + s.cant - s.cante) > 0
AND (s.dataout IS NULL OR s.dataout < SYSDATE - 90)
AND (s.cants + s.cant - s.cante) * s.pret > 100
ORDER BY zile_fara_miscare DESC NULLS FIRST
FETCH FIRST 100 ROWS ONLY
"""
# =============================================================================
# 13. ROTAȚIE STOCURI
# =============================================================================
ROTATIE_STOCURI = """
WITH vanzari_articole AS (
SELECT
r.id_articol,
SUM(ABS(r.cante) * r.pret) AS valoare_vanzari
FROM vrul r
JOIN vanzari v ON r.id_fact = v.id_fact
WHERE r.id_tip_rulaj = 0
AND r.cante <> 0
AND r.dataact >= ADD_MONTHS(TRUNC(SYSDATE), -12)
GROUP BY r.id_articol
),
stoc_curent AS (
SELECT
s.id_articol,
s.denumire,
s.nume_gestiune,
SUM((s.cants + s.cant - s.cante) * s.pret) AS valoare_stoc
FROM vstoc s
WHERE s.an = EXTRACT(YEAR FROM SYSDATE)
AND s.luna = EXTRACT(MONTH FROM SYSDATE)
AND (s.cants + s.cant - s.cante) > 0
GROUP BY s.id_articol, s.denumire, s.nume_gestiune
)
SELECT
sc.denumire AS produs,
sc.nume_gestiune,
ROUND(sc.valoare_stoc, 2) AS valoare_stoc,
ROUND(NVL(va.valoare_vanzari, 0), 2) AS vanzari_12_luni,
ROUND(
CASE WHEN sc.valoare_stoc > 0
THEN NVL(va.valoare_vanzari, 0) / sc.valoare_stoc
ELSE 0 END
, 2) AS rotatie,
ROUND(
CASE WHEN NVL(va.valoare_vanzari, 0) > 0
THEN sc.valoare_stoc * 365 / va.valoare_vanzari
ELSE 9999 END
, 0) AS zile_stoc
FROM stoc_curent sc
LEFT JOIN vanzari_articole va ON va.id_articol = sc.id_articol
WHERE sc.valoare_stoc > 500
ORDER BY rotatie ASC NULLS FIRST
FETCH FIRST 100 ROWS ONLY
"""
# =============================================================================
# 14. ANALIZĂ PRĂJITORIE - MATERII PRIME vs OUTPUT
# =============================================================================
ANALIZA_PRAJITORIE = """
SELECT
TO_CHAR(r.dataact, 'YYYY-MM') AS luna,
CASE
WHEN r.cont = '301' THEN 'Materii prime'
WHEN r.cont = '341' THEN 'Semifabricate'
WHEN r.cont = '345' THEN 'Produse finite'
ELSE 'Altele'
END AS tip,
-- Intrari: cantitate > 0 AND cante = 0
ROUND(SUM(CASE WHEN r.cant > 0 AND NVL(r.cante, 0) = 0 THEN r.cant ELSE 0 END), 2) AS cantitate_intrari,
ROUND(SUM(CASE WHEN r.cant > 0 AND NVL(r.cante, 0) = 0 THEN r.cant * NVL(r.pret, 0) ELSE 0 END), 2) AS valoare_intrari,
-- Iesiri: cant = 0 AND cante > 0
ROUND(SUM(CASE WHEN NVL(r.cant, 0) = 0 AND r.cante > 0 THEN r.cante ELSE 0 END), 2) AS cantitate_iesiri,
ROUND(SUM(CASE WHEN NVL(r.cant, 0) = 0 AND r.cante > 0 THEN r.cante * NVL(r.pret, 0) ELSE 0 END), 2) AS valoare_iesiri,
-- Transformari: cant > 0 AND cante > 0 (intrare si iesire simultan)
ROUND(SUM(CASE WHEN r.cant > 0 AND r.cante > 0 THEN r.cant ELSE 0 END), 2) AS cantitate_transformari_in,
ROUND(SUM(CASE WHEN r.cant > 0 AND r.cante > 0 THEN r.cante ELSE 0 END), 2) AS cantitate_transformari_out,
-- Sold net
ROUND(SUM(NVL(r.cant, 0) - NVL(r.cante, 0)), 2) AS sold_net_cantitate,
ROUND(SUM((NVL(r.cant, 0) - NVL(r.cante, 0)) * NVL(r.pret, 0)), 2) AS sold_net_valoare
FROM vrul r
WHERE r.cont IN ('301', '341', '345')
AND r.dataact >= ADD_MONTHS(TRUNC(SYSDATE), -:months)
GROUP BY TO_CHAR(r.dataact, 'YYYY-MM'),
CASE WHEN r.cont = '301' THEN 'Materii prime'
WHEN r.cont = '341' THEN 'Semifabricate'
WHEN r.cont = '345' THEN 'Produse finite'
ELSE 'Altele' END
ORDER BY luna, tip
"""
# =============================================================================
# 15. SUMAR EXECUTIV - KPIs
# =============================================================================
# OPTIMIZED: Single CTE scans base tables once instead of 6 separate view scans (29 JOINs each)
SUMAR_EXECUTIV = """
WITH base_data AS (
SELECT
f.id_vanzare,
f.id_part,
d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END AS vanzare_fara_tva,
d.cantitate * (CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END - d.pret_achizitie) AS marja
FROM VANZARI f
JOIN VANZARI_DETALII d ON d.id_vanzare = f.id_vanzare
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -12)
),
aggregated AS (
SELECT
SUM(vanzare_fara_tva) AS total_vanzari,
SUM(marja) AS total_marja,
COUNT(DISTINCT id_vanzare) AS nr_facturi,
COUNT(DISTINCT id_part) AS nr_clienti
FROM base_data
)
SELECT
'Vânzări totale (fără TVA)' AS indicator,
TO_CHAR(ROUND(total_vanzari, 0), '999,999,999') AS valoare,
'RON' AS um
FROM aggregated
UNION ALL
SELECT
'Marja brută totală' AS indicator,
TO_CHAR(ROUND(total_marja, 0), '999,999,999') AS valoare,
'RON' AS um
FROM aggregated
UNION ALL
SELECT
'Procent marjă medie' AS indicator,
TO_CHAR(ROUND(total_marja * 100.0 / NULLIF(total_vanzari, 0), 2), '990.99') AS valoare,
'%' AS um
FROM aggregated
UNION ALL
SELECT
'Număr facturi' AS indicator,
TO_CHAR(nr_facturi, '999,999') AS valoare,
'buc' AS um
FROM aggregated
UNION ALL
SELECT
'Număr clienți activi' AS indicator,
TO_CHAR(nr_clienti, '999,999') AS valoare,
'buc' AS um
FROM aggregated
UNION ALL
SELECT
'Valoare stoc total' AS indicator,
TO_CHAR(ROUND(SUM(s.cant * s.pret), 0), '999,999,999') AS valoare,
'RON' AS um
FROM vstoc s
WHERE s.cant > 0
"""
# =============================================================================
# 16. TOP PRODUSE DUPĂ VÂNZĂRI
# =============================================================================
TOP_PRODUSE = """
SELECT
d.denumire AS produs,
NVL(d.subgrupa, 'NECLASIFICAT') AS subgrupa,
d.um,
ROUND(SUM(d.cantitate), 2) AS cantitate_vanduta,
ROUND(SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END), 2) AS valoare_vanzari,
ROUND(SUM(d.cantitate * (CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END - d.pret_achizitie)), 2) AS marja_bruta,
ROUND(
CASE WHEN SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END) > 0
THEN SUM(d.cantitate * (CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END - d.pret_achizitie)) * 100.0 / SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END)
ELSE 0 END
, 2) AS procent_marja
FROM fact_vfacturi2 f
JOIN fact_vfacturi_detalii d ON d.id_vanzare = f.id_vanzare
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -:months)
GROUP BY d.id_articol, d.denumire, d.subgrupa, d.um
ORDER BY valoare_vanzari DESC
FETCH FIRST 50 ROWS ONLY
"""
# =============================================================================
# 17. MARJA PER GESTIUNE (doar articole gestionabile)
# =============================================================================
MARJA_PER_GESTIUNE = """
SELECT
d.nume_gestiune,
COUNT(DISTINCT f.id_vanzare) AS nr_facturi,
ROUND(SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END), 2) AS vanzari_fara_tva,
ROUND(SUM(d.cantitate * (CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END - d.pret_achizitie)), 2) AS marja_bruta,
ROUND(
CASE WHEN SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END) > 0
THEN SUM(d.cantitate * (CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END - d.pret_achizitie)) * 100.0 / SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END)
ELSE 0 END
, 2) AS procent_marja
FROM fact_vfacturi2 f
JOIN fact_vfacturi_detalii d ON d.id_vanzare = f.id_vanzare
JOIN nom_articole a ON d.id_articol = a.id_articol
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -:months)
AND NVL(a.in_stoc, 1) = 1
GROUP BY d.id_gestiune, d.nume_gestiune
ORDER BY vanzari_fara_tva DESC
"""
# =============================================================================
# 18. ARTICOLE NEGESTIONABILE (servicii, etc.)
# =============================================================================
ARTICOLE_NEGESTIONABILE = """
SELECT
NVL(d.denumire, 'NECUNOSCUT') AS denumire,
NVL(d.subgrupa, 'NECLASIFICAT') AS subgrupa,
d.um,
COUNT(DISTINCT f.id_vanzare) AS nr_facturi,
ROUND(SUM(d.cantitate), 2) AS cantitate_vanduta,
ROUND(SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END), 2) AS vanzari_fara_tva,
ROUND(SUM(d.cantitate * d.pret_achizitie), 2) AS cost_total,
ROUND(SUM(d.cantitate * (CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END - d.pret_achizitie)), 2) AS marja_bruta,
ROUND(
CASE WHEN SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END) > 0
THEN SUM(d.cantitate * (CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END - d.pret_achizitie)) * 100.0 / SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END)
ELSE 0 END
, 2) AS procent_marja
FROM fact_vfacturi2 f
JOIN fact_vfacturi_detalii d ON d.id_vanzare = f.id_vanzare
JOIN nom_articole a ON d.id_articol = a.id_articol
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -:months)
AND NVL(a.in_stoc, 0) = 0
GROUP BY d.id_articol, d.denumire, d.subgrupa, d.um
ORDER BY vanzari_fara_tva DESC
"""
# =============================================================================
# 19. SOLDURI CLIENTI (Creanțe din cont 4111)
# =============================================================================
SOLDURI_CLIENTI = """
SELECT
p.denumire AS client,
p.cod_fiscal,
-- Sold direct: pozitiv = creanta, negativ = avans/storno/retur
ROUND(SUM(b.solddeb - b.soldcred), 2) AS sold_curent,
CASE
WHEN SUM(b.solddeb - b.soldcred) > 0 THEN 'Creanta'
WHEN SUM(b.solddeb - b.soldcred) < 0 THEN 'Avans/Storno'
ELSE 'Sold zero'
END AS tip_sold
FROM vbalanta_parteneri b
JOIN vnom_parteneri p ON b.id_part = p.id_part
WHERE b.cont LIKE '4111%'
AND b.an = EXTRACT(YEAR FROM SYSDATE)
AND b.luna = EXTRACT(MONTH FROM SYSDATE)
GROUP BY p.id_part, p.denumire, p.cod_fiscal
HAVING SUM(b.solddeb - b.soldcred) <> 0
ORDER BY sold_curent DESC
"""
# =============================================================================
# 20. SOLDURI FURNIZORI (Datorii din cont 401)
# =============================================================================
SOLDURI_FURNIZORI = """
SELECT
p.denumire AS furnizor,
p.cod_fiscal,
-- Sold direct: pozitiv = datorie, negativ = avans/storno
ROUND(SUM(b.soldcred - b.solddeb), 2) AS sold_curent,
CASE
WHEN SUM(b.soldcred - b.solddeb) > 0 THEN 'Datorie'
WHEN SUM(b.soldcred - b.solddeb) < 0 THEN 'Avans/Storno'
ELSE 'Sold zero'
END AS tip_sold
FROM vbalanta_parteneri b
JOIN vnom_parteneri p ON b.id_part = p.id_part
WHERE b.cont LIKE '401%'
AND b.an = EXTRACT(YEAR FROM SYSDATE)
AND b.luna = EXTRACT(MONTH FROM SYSDATE)
GROUP BY p.id_part, p.denumire, p.cod_fiscal
HAVING SUM(b.soldcred - b.solddeb) <> 0
ORDER BY sold_curent DESC
"""
# =============================================================================
# 21. AGING CREANTE (Buckets pentru creanțe - din vireg_parteneri)
# =============================================================================
# FIX: Filter by last closed calendar period to avoid duplicate invoices
AGING_CREANTE = """
WITH ultima_luna AS (
SELECT anul, luna FROM calendar ORDER BY anul DESC, luna DESC FETCH FIRST 1 ROW ONLY
),
solduri_clienti AS (
SELECT
r.id_part,
r.nume AS client,
r.serie_act || ' ' || r.nract AS nr_factura,
r.dataact AS data_factura,
r.datascad,
(r.precdeb + r.debit) AS valoare_factura,
(r.preccred + r.credit) AS valoare_achitata,
(r.precdeb + r.debit) - (r.preccred + r.credit) AS sold_ramas,
CASE
WHEN r.datascad IS NULL THEN 0
WHEN r.datascad >= TRUNC(SYSDATE) THEN 0
ELSE ROUND(TRUNC(SYSDATE) - r.datascad)
END AS zile_restante
FROM vireg_parteneri r, ultima_luna ul
WHERE (r.cont LIKE '4111%' OR r.cont LIKE '461%')
AND (r.precdeb + r.debit) - (r.preccred + r.credit) <> 0
AND r.an = ul.anul AND r.luna = ul.luna
)
SELECT
client,
ROUND(SUM(CASE WHEN zile_restante <= 0 THEN sold_ramas ELSE 0 END), 2) AS neajuns_scadenta,
ROUND(SUM(CASE WHEN zile_restante > 0 AND zile_restante <= 30 THEN sold_ramas ELSE 0 END), 2) AS zile_1_30,
ROUND(SUM(CASE WHEN zile_restante > 30 AND zile_restante <= 60 THEN sold_ramas ELSE 0 END), 2) AS zile_31_60,
ROUND(SUM(CASE WHEN zile_restante > 60 AND zile_restante <= 90 THEN sold_ramas ELSE 0 END), 2) AS zile_61_90,
ROUND(SUM(CASE WHEN zile_restante > 90 THEN sold_ramas ELSE 0 END), 2) AS peste_90_zile,
ROUND(SUM(sold_ramas), 2) AS total_sold
FROM solduri_clienti
GROUP BY id_part, client
HAVING SUM(sold_ramas) <> 0
ORDER BY total_sold DESC
"""
# =============================================================================
# 22. FACTURI RESTANTE CLIENTI (Depășite scadența - din vireg_parteneri)
# =============================================================================
# FIX: Filter by last closed calendar period to avoid duplicate invoices
FACTURI_RESTANTE = """
WITH ultima_luna AS (
SELECT anul, luna FROM calendar ORDER BY anul DESC, luna DESC FETCH FIRST 1 ROW ONLY
)
SELECT
r.nume AS client,
r.serie_act || ' ' || r.nract AS nr_factura,
r.dataact AS data_factura,
r.datascad AS data_scadenta,
ROUND(TRUNC(SYSDATE) - r.datascad) AS zile_intarziere,
ROUND((r.precdeb + r.debit) - (r.preccred + r.credit), 2) AS suma_restanta
FROM vireg_parteneri r, ultima_luna ul
WHERE (r.cont LIKE '4111%' OR r.cont LIKE '461%')
AND (r.precdeb + r.debit) - (r.preccred + r.credit) <> 0
AND r.datascad IS NOT NULL
AND r.datascad < TRUNC(SYSDATE)
AND r.an = ul.anul AND r.luna = ul.luna
ORDER BY zile_intarziere DESC
FETCH FIRST 100 ROWS ONLY
"""
# =============================================================================
# 22b. AGING DATORII FURNIZORI (din vireg_parteneri)
# =============================================================================
# FIX: Filter by last closed calendar period to avoid duplicate invoices
AGING_DATORII = """
WITH ultima_luna AS (
SELECT anul, luna FROM calendar ORDER BY anul DESC, luna DESC FETCH FIRST 1 ROW ONLY
),
solduri_furnizori AS (
SELECT
r.id_part,
r.nume AS furnizor,
r.serie_act || ' ' || r.nract AS nr_factura,
r.dataact AS data_factura,
r.datascad,
(r.preccred + r.credit) AS valoare_factura,
(r.precdeb + r.debit) AS valoare_achitata,
(r.preccred + r.credit) - (r.precdeb + r.debit) AS sold_ramas,
CASE
WHEN r.datascad IS NULL THEN 0
WHEN r.datascad >= TRUNC(SYSDATE) THEN 0
ELSE ROUND(TRUNC(SYSDATE) - r.datascad)
END AS zile_restante
FROM vireg_parteneri r, ultima_luna ul
WHERE (r.cont LIKE '401%' OR r.cont LIKE '404%' OR r.cont LIKE '462%')
AND (r.preccred + r.credit) - (r.precdeb + r.debit) <> 0
AND r.an = ul.anul AND r.luna = ul.luna
)
SELECT
furnizor,
ROUND(SUM(CASE WHEN zile_restante <= 0 THEN sold_ramas ELSE 0 END), 2) AS neajuns_scadenta,
ROUND(SUM(CASE WHEN zile_restante > 0 AND zile_restante <= 30 THEN sold_ramas ELSE 0 END), 2) AS zile_1_30,
ROUND(SUM(CASE WHEN zile_restante > 30 AND zile_restante <= 60 THEN sold_ramas ELSE 0 END), 2) AS zile_31_60,
ROUND(SUM(CASE WHEN zile_restante > 60 AND zile_restante <= 90 THEN sold_ramas ELSE 0 END), 2) AS zile_61_90,
ROUND(SUM(CASE WHEN zile_restante > 90 THEN sold_ramas ELSE 0 END), 2) AS peste_90_zile,
ROUND(SUM(sold_ramas), 2) AS total_sold
FROM solduri_furnizori
GROUP BY id_part, furnizor
HAVING SUM(sold_ramas) <> 0
ORDER BY total_sold DESC
"""
# =============================================================================
# 22c. FACTURI RESTANTE FURNIZORI (Depășite scadența)
# =============================================================================
# FIX: Filter by last closed calendar period to avoid duplicate invoices
FACTURI_RESTANTE_FURNIZORI = """
WITH ultima_luna AS (
SELECT anul, luna FROM calendar ORDER BY anul DESC, luna DESC FETCH FIRST 1 ROW ONLY
)
SELECT
r.nume AS furnizor,
r.serie_act || ' ' || r.nract AS nr_factura,
r.dataact AS data_factura,
r.datascad AS data_scadenta,
ROUND(TRUNC(SYSDATE) - r.datascad) AS zile_intarziere,
ROUND((r.preccred + r.credit) - (r.precdeb + r.debit), 2) AS suma_restanta
FROM vireg_parteneri r, ultima_luna ul
WHERE (r.cont LIKE '401%' OR r.cont LIKE '404%' OR r.cont LIKE '462%')
AND (r.preccred + r.credit) - (r.precdeb + r.debit) <> 0
AND r.datascad IS NOT NULL
AND r.datascad < TRUNC(SYSDATE)
AND r.an = ul.anul AND r.luna = ul.luna
ORDER BY zile_intarziere DESC
FETCH FIRST 100 ROWS ONLY
"""
# =============================================================================
# 23. DSO / DPO (Zile medii încasare/plată)
# Folosește jurnalele de TVA pentru vânzări/achiziții fără TVA
# =============================================================================
DSO_DPO = """
WITH vanzari_12_luni AS (
-- Vânzări fără TVA din ultimele 12 luni (jurnal vânzări vjv2025)
-- Filtrare: an/luna în interval + dataireg în anul/luna respectivă
SELECT SUM(
-- Baze impozabile cu TVA (ro*b)
NVL(ro24b, 0) + NVL(ro21b, 0) + NVL(ro20b, 0) + NVL(ro19b, 0) +
NVL(ro11b, 0) + NVL(ro9b, 0) + NVL(ro5b, 0) +
-- Baze neimpozabile (ro*nb)
NVL(ro24nb, 0) + NVL(ro21nb, 0) + NVL(ro20nb, 0) + NVL(ro19nb, 0) +
NVL(ro11nb, 0) + NVL(ro9nb, 0) + NVL(ro5nb, 0) +
-- Operațiuni fără TVA
NVL(roti, 0) + NVL(cescdd1, 0) + NVL(cescdd2, 0) + NVL(ceoptr, 0) +
NVL(cesvdd, 0) + NVL(cesvfdd, 0) + NVL(cesvfs, 0) +
NVL(wrscdd, 0) + NVL(fodd, 0) + NVL(fofdd, 0) + NVL(wrscfdd, 0) + NVL(wrn, 0)
) AS total_vanzari
FROM vjv2025
WHERE (an * 12 + luna) BETWEEN
(EXTRACT(YEAR FROM ADD_MONTHS(SYSDATE, -12)) * 12 + EXTRACT(MONTH FROM ADD_MONTHS(SYSDATE, -12)))
AND (EXTRACT(YEAR FROM SYSDATE) * 12 + EXTRACT(MONTH FROM SYSDATE))
AND EXTRACT(YEAR FROM dataireg) = an
AND EXTRACT(MONTH FROM dataireg) = luna
),
sold_clienti AS (
-- Sold creanțe clienți (cont 4111)
SELECT SUM(CASE WHEN b.solddeb > b.soldcred THEN b.solddeb - b.soldcred ELSE 0 END) AS total_creante
FROM vbalanta_parteneri b
WHERE b.cont LIKE '4111%'
AND b.an = EXTRACT(YEAR FROM SYSDATE)
AND b.luna = EXTRACT(MONTH FROM SYSDATE)
),
achizitii_12_luni AS (
-- Achiziții fără TVA din ultimele 12 luni (jurnal cumpărări vjc2025)
SELECT SUM(
-- Baze impozabile domestice (ro*b)
NVL(ro24b, 0) + NVL(ro21b, 0) + NVL(ro20b, 0) + NVL(ro19b, 0) +
NVL(ro11b, 0) + NVL(ro09b, 0) + NVL(ro05b, 0) +
-- Baze neimpozabile (ro*nb)
NVL(ro24nb, 0) + NVL(ro21nb, 0) + NVL(ro20nb, 0) + NVL(ro19nb, 0) +
NVL(ro11nb, 0) + NVL(ro9nb, 0) + NVL(ro5nb, 0) +
-- Furnizori (fo*b)
NVL(fo24b, 0) + NVL(fo21b, 0) + NVL(fo20b, 0) + NVL(fo19b, 0) +
NVL(fo11b, 0) + NVL(fo09b, 0) +
-- Taxare inversă (ti*b)
NVL(ti24b, 0) + NVL(ti21b, 0) + NVL(ti20b, 0) + NVL(ti19b, 0) +
NVL(ti11b, 0) + NVL(ti09b, 0) +
-- Achiziții intracomunitare (ce*b)
NVL(ceb, 0) + NVL(cebb, 0) + NVL(cesb, 0) +
-- Cross-border (xx*tib)
NVL(xx19tib, 0) + NVL(xx21tib, 0) + NVL(xx11tib, 0) + NVL(xx9tib, 0) +
-- Fără TVA
NVL(cen, 0)
) AS total_achizitii
FROM vjc2025
WHERE (an * 12 + luna) BETWEEN
(EXTRACT(YEAR FROM ADD_MONTHS(SYSDATE, -12)) * 12 + EXTRACT(MONTH FROM ADD_MONTHS(SYSDATE, -12)))
AND (EXTRACT(YEAR FROM SYSDATE) * 12 + EXTRACT(MONTH FROM SYSDATE))
AND EXTRACT(YEAR FROM dataireg) = an
AND EXTRACT(MONTH FROM dataireg) = luna
),
sold_furnizori AS (
-- Sold datorii furnizori (cont 401)
SELECT SUM(CASE WHEN b.soldcred > b.solddeb THEN b.soldcred - b.solddeb ELSE 0 END) AS total_datorii
FROM vbalanta_parteneri b
WHERE b.cont LIKE '401%'
AND b.an = EXTRACT(YEAR FROM SYSDATE)
AND b.luna = EXTRACT(MONTH FROM SYSDATE)
)
SELECT
'DSO (Zile incasare clienti)' AS indicator,
ROUND(NVL(sc.total_creante, 0) * 365 / NULLIF(v.total_vanzari, 0), 0) AS zile,
CASE
WHEN NVL(sc.total_creante, 0) * 365 / NULLIF(v.total_vanzari, 0) > 60 THEN 'ALERTA'
WHEN NVL(sc.total_creante, 0) * 365 / NULLIF(v.total_vanzari, 0) > 45 THEN 'ATENTIE'
ELSE 'OK'
END AS status
FROM vanzari_12_luni v, sold_clienti sc
UNION ALL
SELECT
'DPO (Zile plata furnizori)' AS indicator,
ROUND(NVL(sf.total_datorii, 0) * 365 / NULLIF(a.total_achizitii, 0), 0) AS zile,
CASE
WHEN NVL(sf.total_datorii, 0) * 365 / NULLIF(a.total_achizitii, 0) < 15 THEN 'ATENTIE'
ELSE 'OK'
END AS status
FROM achizitii_12_luni a, sold_furnizori sf
"""
# =============================================================================
# 24. POZITIA CASH (Conturi 5121, 5311)
# =============================================================================
POZITIA_CASH = """
SELECT
b.cont,
CASE
WHEN b.cont LIKE '5121%' THEN 'Conturi la banci in lei'
WHEN b.cont LIKE '5124%' THEN 'Conturi la banci in valuta'
WHEN b.cont LIKE '5311%' THEN 'Casa in lei'
WHEN b.cont LIKE '5314%' THEN 'Casa in valuta'
ELSE 'Alte disponibilitati'
END AS descriere,
ROUND(SUM(b.solddeb - b.soldcred), 2) AS sold_curent,
CASE
WHEN b.cont LIKE '512%' OR b.cont LIKE '531%' THEN 'RON'
WHEN b.cont LIKE '5124%' OR b.cont LIKE '5314%' THEN 'VALUTA'
ELSE 'RON'
END AS valuta
FROM vbal b
WHERE (b.cont LIKE '512%' OR b.cont LIKE '531%')
AND b.an = EXTRACT(YEAR FROM SYSDATE)
AND b.luna = EXTRACT(MONTH FROM SYSDATE)
GROUP BY b.cont
HAVING SUM(b.solddeb - b.soldcred) <> 0
ORDER BY b.cont
"""
# =============================================================================
# 25. CICLU CONVERSIE CASH (DIO + DSO - DPO)
# Folosește vbal clasa 3xx pentru stoc, jurnale TVA pentru vânzări/achiziții
# =============================================================================
CICLU_CONVERSIE_CASH = """
WITH metrici AS (
SELECT
-- Stoc curent din balanța de verificare, clasa 3xx
(SELECT SUM(b.solddeb - b.soldcred)
FROM vbal b
WHERE b.cont LIKE '3%'
AND b.an = EXTRACT(YEAR FROM SYSDATE)
AND b.luna = EXTRACT(MONTH FROM SYSDATE)) AS stoc_curent,
-- COGS din facturi (preț achiziție articole vândute)
(SELECT SUM(d.cantitate * d.pret_achizitie)
FROM fact_vfacturi2 f
JOIN fact_vfacturi_detalii d ON d.id_vanzare = f.id_vanzare
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -12)) AS cogs_12_luni,
-- Creanțe clienți
(SELECT SUM(CASE WHEN b.solddeb > b.soldcred THEN b.solddeb - b.soldcred ELSE 0 END)
FROM vbalanta_parteneri b
WHERE b.cont LIKE '4111%'
AND b.an = EXTRACT(YEAR FROM SYSDATE)
AND b.luna = EXTRACT(MONTH FROM SYSDATE)) AS creante,
-- Vânzări 12 luni din jurnal TVA vânzări (vjv2025)
(SELECT SUM(
-- Baze impozabile (ro*b)
NVL(ro24b, 0) + NVL(ro21b, 0) + NVL(ro20b, 0) + NVL(ro19b, 0) +
NVL(ro11b, 0) + NVL(ro9b, 0) + NVL(ro5b, 0) +
-- Baze neimpozabile (ro*nb)
NVL(ro24nb, 0) + NVL(ro21nb, 0) + NVL(ro20nb, 0) + NVL(ro19nb, 0) +
NVL(ro11nb, 0) + NVL(ro9nb, 0) + NVL(ro5nb, 0) +
-- Operațiuni fără TVA
NVL(roti, 0) + NVL(cescdd1, 0) + NVL(cescdd2, 0) + NVL(ceoptr, 0) +
NVL(cesvdd, 0) + NVL(cesvfdd, 0) + NVL(cesvfs, 0) +
NVL(wrscdd, 0) + NVL(fodd, 0) + NVL(fofdd, 0) + NVL(wrscfdd, 0) + NVL(wrn, 0))
FROM vjv2025
WHERE (an * 12 + luna) BETWEEN
(EXTRACT(YEAR FROM ADD_MONTHS(SYSDATE, -12)) * 12 + EXTRACT(MONTH FROM ADD_MONTHS(SYSDATE, -12)))
AND (EXTRACT(YEAR FROM SYSDATE) * 12 + EXTRACT(MONTH FROM SYSDATE))
AND EXTRACT(YEAR FROM dataireg) = an
AND EXTRACT(MONTH FROM dataireg) = luna) AS vanzari_12_luni,
-- Datorii furnizori
(SELECT SUM(CASE WHEN b.soldcred > b.solddeb THEN b.soldcred - b.solddeb ELSE 0 END)
FROM vbalanta_parteneri b
WHERE b.cont LIKE '401%'
AND b.an = EXTRACT(YEAR FROM SYSDATE)
AND b.luna = EXTRACT(MONTH FROM SYSDATE)) AS datorii_furnizori,
-- Achiziții 12 luni din jurnal TVA cumpărări (vjc2025)
(SELECT SUM(
-- Baze impozabile domestice (ro*b)
NVL(ro24b, 0) + NVL(ro21b, 0) + NVL(ro20b, 0) + NVL(ro19b, 0) +
NVL(ro11b, 0) + NVL(ro09b, 0) + NVL(ro05b, 0) +
-- Baze neimpozabile (ro*nb)
NVL(ro24nb, 0) + NVL(ro21nb, 0) + NVL(ro20nb, 0) + NVL(ro19nb, 0) +
NVL(ro11nb, 0) + NVL(ro9nb, 0) + NVL(ro5nb, 0) +
-- Furnizori (fo*b)
NVL(fo24b, 0) + NVL(fo21b, 0) + NVL(fo20b, 0) + NVL(fo19b, 0) +
NVL(fo11b, 0) + NVL(fo09b, 0) +
-- Taxare inversă (ti*b)
NVL(ti24b, 0) + NVL(ti21b, 0) + NVL(ti20b, 0) + NVL(ti19b, 0) +
NVL(ti11b, 0) + NVL(ti09b, 0) +
-- Achiziții intracomunitare (ce*b)
NVL(ceb, 0) + NVL(cebb, 0) + NVL(cesb, 0) +
-- Cross-border (xx*tib)
NVL(xx19tib, 0) + NVL(xx21tib, 0) + NVL(xx11tib, 0) + NVL(xx9tib, 0) +
-- Fără TVA
NVL(cen, 0))
FROM vjc2025
WHERE (an * 12 + luna) BETWEEN
(EXTRACT(YEAR FROM ADD_MONTHS(SYSDATE, -12)) * 12 + EXTRACT(MONTH FROM ADD_MONTHS(SYSDATE, -12)))
AND (EXTRACT(YEAR FROM SYSDATE) * 12 + EXTRACT(MONTH FROM SYSDATE))
AND EXTRACT(YEAR FROM dataireg) = an
AND EXTRACT(MONTH FROM dataireg) = luna) AS achizitii_12_luni
FROM dual
)
SELECT
'DIO (Zile stoc)' AS indicator,
ROUND(NVL(m.stoc_curent, 0) * 365 / NULLIF(m.cogs_12_luni, 0), 0) AS zile,
'Zile medii pentru transformarea stocului in vanzare' AS explicatie
FROM metrici m
UNION ALL
SELECT
'DSO (Zile incasare)' AS indicator,
ROUND(NVL(m.creante, 0) * 365 / NULLIF(m.vanzari_12_luni, 0), 0) AS zile,
'Zile medii pentru incasarea creantelor' AS explicatie
FROM metrici m
UNION ALL
SELECT
'DPO (Zile plata)' AS indicator,
ROUND(NVL(m.datorii_furnizori, 0) * 365 / NULLIF(m.achizitii_12_luni, 0), 0) AS zile,
'Zile medii pentru plata furnizorilor' AS explicatie
FROM metrici m
UNION ALL
SELECT
'CCC (Ciclu conversie cash)' AS indicator,
ROUND(
NVL(m.stoc_curent, 0) * 365 / NULLIF(m.cogs_12_luni, 0) +
NVL(m.creante, 0) * 365 / NULLIF(m.vanzari_12_luni, 0) -
NVL(m.datorii_furnizori, 0) * 365 / NULLIF(m.achizitii_12_luni, 0)
, 0) AS zile,
'DIO + DSO - DPO = zile de la plata furnizor pana la incasare client' AS explicatie
FROM metrici m
"""
# =============================================================================
# 26. INDICATORI AGREGATI VENITURI (Revenue mix per linie de business)
# =============================================================================
INDICATORI_AGREGATI_VENITURI = """
WITH vanzari_detaliate AS (
SELECT
CASE
WHEN d.cont IN ('341', '345') THEN 'Productie proprie'
WHEN d.cont = '301' THEN 'Materii prime'
ELSE 'Marfa revanduta'
END AS linie_business,
d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END AS vanzare,
d.cantitate * (CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END - d.pret_achizitie) AS marja
FROM fact_vfacturi2 f
JOIN fact_vfacturi_detalii d ON d.id_vanzare = f.id_vanzare
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -:months)
),
total AS (
SELECT SUM(vanzare) AS total_vanzari, SUM(marja) AS total_marja
FROM vanzari_detaliate
)
SELECT
v.linie_business,
ROUND(SUM(v.vanzare), 2) AS vanzari_ron,
ROUND(SUM(v.vanzare) * 100 / t.total_vanzari, 2) AS procent_venituri,
ROUND(SUM(v.marja), 2) AS marja_ron,
ROUND(SUM(v.marja) * 100 / NULLIF(SUM(v.vanzare), 0), 2) AS procent_marja,
ROUND(SUM(v.marja) * 100 / NULLIF(t.total_marja, 0), 2) AS contributie_profit
FROM vanzari_detaliate v
CROSS JOIN total t
GROUP BY v.linie_business, t.total_vanzari, t.total_marja
ORDER BY vanzari_ron DESC
"""
# =============================================================================
# 27. SEZONALITATE LUNARA (Analiza 24 luni)
# =============================================================================
SEZONALITATE_LUNARA = """
WITH vanzari_lunare AS (
SELECT
EXTRACT(MONTH FROM f.data_act) AS nr_luna,
TO_CHAR(f.data_act, 'Month', 'NLS_DATE_LANGUAGE=ROMANIAN') AS luna,
EXTRACT(YEAR FROM f.data_act) AS an,
SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END) AS vanzari
FROM fact_vfacturi2 f
JOIN fact_vfacturi_detalii d ON d.id_vanzare = f.id_vanzare
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -24)
GROUP BY EXTRACT(MONTH FROM f.data_act), TO_CHAR(f.data_act, 'Month', 'NLS_DATE_LANGUAGE=ROMANIAN'), EXTRACT(YEAR FROM f.data_act)
),
statistici_luna AS (
SELECT
nr_luna,
TRIM(luna) AS luna,
ROUND(AVG(vanzari), 2) AS medie_vanzari,
ROUND(MIN(vanzari), 2) AS min_vanzari,
ROUND(MAX(vanzari), 2) AS max_vanzari,
ROUND(STDDEV(vanzari), 2) AS deviere_std
FROM vanzari_lunare
GROUP BY nr_luna, TRIM(luna)
),
media_globala AS (
SELECT AVG(vanzari) AS medie_globala FROM vanzari_lunare
)
SELECT
s.luna,
s.medie_vanzari,
s.min_vanzari,
s.max_vanzari,
ROUND((s.medie_vanzari - m.medie_globala) * 100 / m.medie_globala, 2) AS deviere_procent,
CASE
WHEN s.medie_vanzari > m.medie_globala * 1.15 THEN 'LUNA PUTERNICA'
WHEN s.medie_vanzari < m.medie_globala * 0.85 THEN 'LUNA SLABA'
ELSE 'NORMAL'
END AS clasificare
FROM statistici_luna s
CROSS JOIN media_globala m
ORDER BY s.nr_luna
"""
# =============================================================================
# 28. PORTOFOLIU CLIENTI (Sănătatea portofoliului)
# =============================================================================
PORTOFOLIU_CLIENTI = """
WITH clienti_activi_3_luni AS (
SELECT COUNT(DISTINCT f.id_part) AS cnt
FROM fact_vfacturi2 f
WHERE f.sters = 0 AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -3)
),
clienti_activi_12_luni AS (
SELECT COUNT(DISTINCT f.id_part) AS cnt
FROM fact_vfacturi2 f
WHERE f.sters = 0 AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -12)
),
clienti_noi AS (
SELECT COUNT(DISTINCT f.id_part) AS cnt
FROM fact_vfacturi2 f
WHERE f.sters = 0 AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -12)
AND f.id_part NOT IN (
SELECT DISTINCT f2.id_part
FROM fact_vfacturi2 f2
WHERE f2.sters = 0 AND f2.tip > 0 AND f2.tip NOT IN (7, 8, 9, 24)
AND f2.data_act < ADD_MONTHS(TRUNC(SYSDATE), -12)
)
),
clienti_pierduti AS (
SELECT COUNT(DISTINCT f.id_part) AS cnt
FROM fact_vfacturi2 f
WHERE f.sters = 0 AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -24)
AND f.data_act < ADD_MONTHS(TRUNC(SYSDATE), -6)
AND f.id_part NOT IN (
SELECT DISTINCT f2.id_part
FROM fact_vfacturi2 f2
WHERE f2.sters = 0 AND f2.tip > 0 AND f2.tip NOT IN (7, 8, 9, 24)
AND f2.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -6)
)
),
clienti_inactivi AS (
SELECT COUNT(DISTINCT f.id_part) AS cnt
FROM fact_vfacturi2 f
WHERE f.sters = 0 AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -6)
AND f.data_act < ADD_MONTHS(TRUNC(SYSDATE), -3)
AND f.id_part NOT IN (
SELECT DISTINCT f2.id_part
FROM fact_vfacturi2 f2
WHERE f2.sters = 0 AND f2.tip > 0 AND f2.tip NOT IN (7, 8, 9, 24)
AND f2.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -3)
)
)
SELECT 'Clienti activi (ultimele 3 luni)' AS indicator, cnt AS valoare, 'Au cumparat recent' AS explicatie FROM clienti_activi_3_luni
UNION ALL
SELECT 'Clienti activi (ultimele 12 luni)' AS indicator, cnt AS valoare, 'Au cumparat in ultimul an' AS explicatie FROM clienti_activi_12_luni
UNION ALL
SELECT 'Clienti noi (ultimele 12 luni)' AS indicator, cnt AS valoare, 'Prima achizitie in ultimul an' AS explicatie FROM clienti_noi
UNION ALL
SELECT 'Clienti pierduti (>6 luni inactivi)' AS indicator, cnt AS valoare, 'Nu au mai cumparat de 6+ luni' AS explicatie FROM clienti_pierduti
UNION ALL
SELECT 'Clienti inactivi (3-6 luni)' AS indicator, cnt AS valoare, 'Risc de pierdere' AS explicatie FROM clienti_inactivi
"""
# =============================================================================
# 29. FRECVENTA CLIENTI (Frecvența comenzilor și evoluție YoY)
# =============================================================================
FRECVENTA_CLIENTI = """
WITH frecventa_curenta AS (
SELECT
f.id_part,
f.client,
COUNT(DISTINCT f.id_vanzare) AS comenzi_12_luni,
ROUND(COUNT(DISTINCT f.id_vanzare) / 12.0, 2) AS comenzi_pe_luna,
ROUND(SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END), 2) AS valoare_12_luni
FROM fact_vfacturi2 f
JOIN fact_vfacturi_detalii d ON d.id_vanzare = f.id_vanzare
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -12)
GROUP BY f.id_part, f.client
),
frecventa_anterioara AS (
SELECT
f.id_part,
COUNT(DISTINCT f.id_vanzare) AS comenzi_an_anterior
FROM fact_vfacturi2 f
WHERE f.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -24)
AND f.data_act < ADD_MONTHS(TRUNC(SYSDATE), -12)
GROUP BY f.id_part
)
SELECT
fc.client,
fc.comenzi_12_luni,
fc.comenzi_pe_luna,
fc.valoare_12_luni,
ROUND(fc.valoare_12_luni / NULLIF(fc.comenzi_12_luni, 0), 2) AS valoare_medie_comanda,
ROUND(
CASE WHEN fa.comenzi_an_anterior > 0
THEN (fc.comenzi_12_luni - fa.comenzi_an_anterior) * 100.0 / fa.comenzi_an_anterior
ELSE NULL END
, 2) AS evolutie_frecventa_yoy
FROM frecventa_curenta fc
LEFT JOIN frecventa_anterioara fa ON fa.id_part = fc.id_part
WHERE fc.valoare_12_luni > 5000
ORDER BY fc.valoare_12_luni DESC
"""
# =============================================================================
# 30. CONCENTRARE RISC (Risc de concentrare)
# =============================================================================
CONCENTRARE_RISC = """
WITH total_vanzari AS (
SELECT SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END) AS total
FROM fact_vfacturi2 f
JOIN fact_vfacturi_detalii d ON d.id_vanzare = f.id_vanzare
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -:months)
),
vanzari_client AS (
SELECT
f.id_part,
SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END) AS vanzari,
ROW_NUMBER() OVER (ORDER BY SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END) DESC) AS rn
FROM fact_vfacturi2 f
JOIN fact_vfacturi_detalii d ON d.id_vanzare = f.id_vanzare
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -:months)
GROUP BY f.id_part
)
SELECT
'Top 1 client' AS indicator,
ROUND(SUM(CASE WHEN vc.rn <= 1 THEN vc.vanzari ELSE 0 END) * 100 / tv.total, 2) AS procent,
CASE
WHEN SUM(CASE WHEN vc.rn <= 1 THEN vc.vanzari ELSE 0 END) * 100 / tv.total > 25 THEN 'RISC MARE'
WHEN SUM(CASE WHEN vc.rn <= 1 THEN vc.vanzari ELSE 0 END) * 100 / tv.total > 15 THEN 'ATENTIE'
ELSE 'OK'
END AS status
FROM vanzari_client vc
CROSS JOIN total_vanzari tv
GROUP BY tv.total
UNION ALL
SELECT
'Top 5 clienti' AS indicator,
ROUND(SUM(CASE WHEN vc.rn <= 5 THEN vc.vanzari ELSE 0 END) * 100 / tv.total, 2) AS procent,
CASE
WHEN SUM(CASE WHEN vc.rn <= 5 THEN vc.vanzari ELSE 0 END) * 100 / tv.total > 60 THEN 'RISC MARE'
WHEN SUM(CASE WHEN vc.rn <= 5 THEN vc.vanzari ELSE 0 END) * 100 / tv.total > 40 THEN 'ATENTIE'
ELSE 'OK'
END AS status
FROM vanzari_client vc
CROSS JOIN total_vanzari tv
GROUP BY tv.total
UNION ALL
SELECT
'Top 10 clienti' AS indicator,
ROUND(SUM(CASE WHEN vc.rn <= 10 THEN vc.vanzari ELSE 0 END) * 100 / tv.total, 2) AS procent,
CASE
WHEN SUM(CASE WHEN vc.rn <= 10 THEN vc.vanzari ELSE 0 END) * 100 / tv.total > 80 THEN 'RISC MARE'
WHEN SUM(CASE WHEN vc.rn <= 10 THEN vc.vanzari ELSE 0 END) * 100 / tv.total > 60 THEN 'ATENTIE'
ELSE 'OK'
END AS status
FROM vanzari_client vc
CROSS JOIN total_vanzari tv
GROUP BY tv.total
"""
# =============================================================================
# 31. CLIENTI RANKING PROFIT (Ranking după profit, nu venituri)
# =============================================================================
CLIENTI_RANKING_PROFIT = """
WITH vanzari_client AS (
SELECT
f.id_part,
f.client,
SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END) AS vanzari_fara_tva,
SUM(d.cantitate * d.pret_achizitie) AS cost_total,
SUM(d.cantitate * (CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END - d.pret_achizitie)) AS profit_brut
FROM fact_vfacturi2 f
JOIN fact_vfacturi_detalii d ON d.id_vanzare = f.id_vanzare
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -:months)
GROUP BY f.id_part, f.client
)
SELECT
client,
ROUND(vanzari_fara_tva, 2) AS vanzari_fara_tva,
ROUND(cost_total, 2) AS cost_total,
ROUND(profit_brut, 2) AS profit_brut,
ROUND(profit_brut * 100 / NULLIF(vanzari_fara_tva, 0), 2) AS procent_marja,
RANK() OVER (ORDER BY profit_brut DESC) AS rang_profit,
RANK() OVER (ORDER BY vanzari_fara_tva DESC) AS rang_vanzari
FROM vanzari_client
WHERE vanzari_fara_tva > 1000
ORDER BY profit_brut DESC
"""
# =============================================================================
# 32. MARJA CLIENT CATEGORIE (Marjă per categorie per client)
# =============================================================================
MARJA_CLIENT_CATEGORIE = """
SELECT
f.client,
NVL(sg.grupa, 'NECLASIFICAT') AS categoria,
ROUND(SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END), 2) AS vanzari,
ROUND(SUM(d.cantitate * (CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END - d.pret_achizitie)), 2) AS marja,
ROUND(
SUM(d.cantitate * (CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END - d.pret_achizitie)) * 100.0 /
NULLIF(SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END), 0)
, 2) AS procent_marja,
CASE
WHEN SUM(d.cantitate * (CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END - d.pret_achizitie)) < 0 THEN 'PIERDERE'
WHEN SUM(d.cantitate * (CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END - d.pret_achizitie)) * 100.0 /
NULLIF(SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END), 0) < 15 THEN 'MARJA MICA'
ELSE 'OK'
END AS status_marja
FROM fact_vfacturi2 f
JOIN fact_vfacturi_detalii d ON d.id_vanzare = f.id_vanzare
LEFT JOIN vgest_art_sbgr sg ON d.id_subgrupa = sg.id_subgrupa
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -:months)
GROUP BY f.id_part, f.client, sg.id_grupa, sg.grupa
HAVING SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END) > 1000
ORDER BY f.client, vanzari DESC
"""
# =============================================================================
# 33. EVOLUTIE DISCOUNT (Discount creep - evoluția prețului în timp)
# =============================================================================
EVOLUTIE_DISCOUNT = """
WITH preturi_vechi AS (
SELECT
d.id_articol,
d.denumire,
AVG(CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END) AS pret_mediu_vechi
FROM fact_vfacturi2 f
JOIN fact_vfacturi_detalii d ON d.id_vanzare = f.id_vanzare
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -12)
AND f.data_act < ADD_MONTHS(TRUNC(SYSDATE), -6)
AND d.pret > 0
GROUP BY d.id_articol, d.denumire
HAVING COUNT(*) >= 5
),
preturi_noi AS (
SELECT
d.id_articol,
AVG(CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END) AS pret_mediu_nou
FROM fact_vfacturi2 f
JOIN fact_vfacturi_detalii d ON d.id_vanzare = f.id_vanzare
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -6)
AND d.pret > 0
GROUP BY d.id_articol
HAVING COUNT(*) >= 5
)
SELECT
pv.denumire AS produs,
ROUND(pv.pret_mediu_vechi, 2) AS pret_initial,
ROUND(pn.pret_mediu_nou, 2) AS pret_actual,
ROUND((pn.pret_mediu_nou - pv.pret_mediu_vechi) * 100 / pv.pret_mediu_vechi, 2) AS variatie_pret_procent
FROM preturi_vechi pv
JOIN preturi_noi pn ON pn.id_articol = pv.id_articol
WHERE ABS(pn.pret_mediu_nou - pv.pret_mediu_vechi) / pv.pret_mediu_vechi > 0.05
ORDER BY variatie_pret_procent ASC
FETCH FIRST 50 ROWS ONLY
"""
# =============================================================================
# 34. INDICATORI GENERALI DE BUSINESS
# =============================================================================
INDICATORI_GENERALI = """
WITH
-- Capital propriu (conturi 10x, 11x, 12x, 117)
capitaluri AS (
SELECT SUM(b.soldcred - b.solddeb) AS capital_propriu
FROM vbal b
WHERE (b.cont LIKE '101%' OR b.cont LIKE '104%' OR b.cont LIKE '105%'
OR b.cont LIKE '106%' OR b.cont LIKE '117%' OR b.cont LIKE '121%')
AND b.an = EXTRACT(YEAR FROM SYSDATE)
AND b.luna = EXTRACT(MONTH FROM SYSDATE)
),
-- Datorii totale (16x, 40x, 42x, 44x, 46x)
datorii AS (
SELECT SUM(b.soldcred - b.solddeb) AS datorii_totale
FROM vbal b
WHERE (b.cont LIKE '16%' OR b.cont LIKE '40%' OR b.cont LIKE '42%'
OR b.cont LIKE '44%' OR b.cont LIKE '46%')
AND b.an = EXTRACT(YEAR FROM SYSDATE)
AND b.luna = EXTRACT(MONTH FROM SYSDATE)
),
-- Total activ (sold debitor toate conturile de activ)
activ AS (
SELECT SUM(b.solddeb - b.soldcred) AS total_activ
FROM vbal b
WHERE (b.cont LIKE '1%' OR b.cont LIKE '2%' OR b.cont LIKE '3%'
OR b.cont LIKE '4%' OR b.cont LIKE '5%')
AND b.cont NOT LIKE '16%' AND b.cont NOT LIKE '40%' AND b.cont NOT LIKE '42%'
AND b.cont NOT LIKE '44%' AND b.cont NOT LIKE '46%' AND b.cont NOT LIKE '47%'
AND b.an = EXTRACT(YEAR FROM SYSDATE)
AND b.luna = EXTRACT(MONTH FROM SYSDATE)
),
-- Vanzari si profit din ultimele 12 luni
vanzari AS (
SELECT
SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END) AS total_vanzari,
SUM(d.cantitate * (CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END - d.pret_achizitie)) AS profit_brut
FROM fact_vfacturi2 f
JOIN fact_vfacturi_detalii d ON d.id_vanzare = f.id_vanzare
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -12)
)
SELECT
'Grad indatorare' AS indicator,
ROUND(NVL(dt.datorii_totale, 0) / NULLIF(cp.capital_propriu, 0), 2) AS valoare,
CASE
WHEN NVL(dt.datorii_totale, 0) / NULLIF(cp.capital_propriu, 0) > 2 THEN 'ALERTA'
WHEN NVL(dt.datorii_totale, 0) / NULLIF(cp.capital_propriu, 0) > 1 THEN 'ATENTIE'
ELSE 'OK'
END AS status,
'< 1 = bine, > 2 = risc' AS interpretare,
CASE
WHEN NVL(dt.datorii_totale, 0) / NULLIF(cp.capital_propriu, 0) > 2 THEN 'Reduceti datoriile sau cresteti capitalul propriu'
WHEN NVL(dt.datorii_totale, 0) / NULLIF(cp.capital_propriu, 0) > 1 THEN 'Monitorizati nivelul datoriilor'
ELSE 'Situatie financiara solida'
END AS recomandare
FROM capitaluri cp, datorii dt
UNION ALL
SELECT
'Grad autonomie financiara' AS indicator,
ROUND(NVL(cp.capital_propriu, 0) / NULLIF(ac.total_activ, 0), 2) AS valoare,
CASE
WHEN NVL(cp.capital_propriu, 0) / NULLIF(ac.total_activ, 0) < 0.3 THEN 'ALERTA'
WHEN NVL(cp.capital_propriu, 0) / NULLIF(ac.total_activ, 0) < 0.5 THEN 'ATENTIE'
ELSE 'OK'
END AS status,
'> 0.5 = bine, < 0.3 = risc' AS interpretare,
CASE
WHEN NVL(cp.capital_propriu, 0) / NULLIF(ac.total_activ, 0) < 0.3 THEN 'Dependenta prea mare de creditori'
WHEN NVL(cp.capital_propriu, 0) / NULLIF(ac.total_activ, 0) < 0.5 THEN 'Cresteti capitalul propriu'
ELSE 'Autonomie financiara buna'
END AS recomandare
FROM capitaluri cp, activ ac
UNION ALL
SELECT
'Rata datoriilor' AS indicator,
ROUND(NVL(dt.datorii_totale, 0) / NULLIF(ac.total_activ, 0), 2) AS valoare,
CASE
WHEN NVL(dt.datorii_totale, 0) / NULLIF(ac.total_activ, 0) > 0.7 THEN 'ALERTA'
WHEN NVL(dt.datorii_totale, 0) / NULLIF(ac.total_activ, 0) > 0.5 THEN 'ATENTIE'
ELSE 'OK'
END AS status,
'< 0.5 = bine, > 0.7 = risc' AS interpretare,
CASE
WHEN NVL(dt.datorii_totale, 0) / NULLIF(ac.total_activ, 0) > 0.7 THEN 'Risc de insolventa - reduceti datoriile'
WHEN NVL(dt.datorii_totale, 0) / NULLIF(ac.total_activ, 0) > 0.5 THEN 'Atentie la capacitatea de plata'
ELSE 'Nivel acceptabil al datoriilor'
END AS recomandare
FROM datorii dt, activ ac
UNION ALL
SELECT
'Marja neta (%)' AS indicator,
ROUND(NVL(v.profit_brut, 0) * 100 / NULLIF(v.total_vanzari, 0), 2) AS valoare,
CASE
WHEN NVL(v.profit_brut, 0) * 100 / NULLIF(v.total_vanzari, 0) < 3 THEN 'ALERTA'
WHEN NVL(v.profit_brut, 0) * 100 / NULLIF(v.total_vanzari, 0) < 5 THEN 'ATENTIE'
ELSE 'OK'
END AS status,
'> 5% = bine, < 3% = risc' AS interpretare,
CASE
WHEN NVL(v.profit_brut, 0) * 100 / NULLIF(v.total_vanzari, 0) < 3 THEN 'Marja periculoasa - revizuiti preturile si costurile'
WHEN NVL(v.profit_brut, 0) * 100 / NULLIF(v.total_vanzari, 0) < 5 THEN 'Optimizati costurile sau cresteti preturile'
ELSE 'Marja acceptabila'
END AS recomandare
FROM vanzari v
UNION ALL
SELECT
'ROA - Rentabilitatea activelor (%)' AS indicator,
ROUND(NVL(v.profit_brut, 0) * 100 / NULLIF(ac.total_activ, 0), 2) AS valoare,
CASE
WHEN NVL(v.profit_brut, 0) * 100 / NULLIF(ac.total_activ, 0) < 2 THEN 'ALERTA'
WHEN NVL(v.profit_brut, 0) * 100 / NULLIF(ac.total_activ, 0) < 5 THEN 'ATENTIE'
ELSE 'OK'
END AS status,
'> 5% = bine, < 2% = slab' AS interpretare,
CASE
WHEN NVL(v.profit_brut, 0) * 100 / NULLIF(ac.total_activ, 0) < 2 THEN 'Activele nu genereaza profit suficient - optimizati utilizarea'
WHEN NVL(v.profit_brut, 0) * 100 / NULLIF(ac.total_activ, 0) < 5 THEN 'Cresteti eficienta utilizarii activelor'
ELSE 'Activele sunt utilizate eficient'
END AS recomandare
FROM vanzari v, activ ac
UNION ALL
SELECT
'Rotatia activelor' AS indicator,
ROUND(NVL(v.total_vanzari, 0) / NULLIF(ac.total_activ, 0), 2) AS valoare,
CASE
WHEN NVL(v.total_vanzari, 0) / NULLIF(ac.total_activ, 0) < 0.5 THEN 'ATENTIE'
ELSE 'OK'
END AS status,
'> 1 = eficient' AS interpretare,
CASE
WHEN NVL(v.total_vanzari, 0) / NULLIF(ac.total_activ, 0) < 0.5 THEN 'Active subutilizate - cresteti vanzarile'
ELSE 'Activele genereaza vanzari eficient'
END AS recomandare
FROM vanzari v, activ ac
"""
# =============================================================================
# 35. INDICATORI LICHIDITATE
# =============================================================================
INDICATORI_LICHIDITATE = """
WITH
-- Cash (conturi 5121, 5124, 5311, 5314)
cash AS (
SELECT SUM(b.solddeb - b.soldcred) AS cash_total
FROM vbal b
WHERE (b.cont LIKE '512%' OR b.cont LIKE '531%')
AND b.an = EXTRACT(YEAR FROM SYSDATE)
AND b.luna = EXTRACT(MONTH FROM SYSDATE)
),
-- Creante (cont 4111)
creante AS (
SELECT SUM(CASE WHEN b.solddeb > b.soldcred THEN b.solddeb - b.soldcred ELSE 0 END) AS creante_total
FROM vbalanta_parteneri b
WHERE b.cont LIKE '4111%'
AND b.an = EXTRACT(YEAR FROM SYSDATE)
AND b.luna = EXTRACT(MONTH FROM SYSDATE)
),
-- Stocuri
stocuri AS (
SELECT SUM((s.cants + s.cant - s.cante) * s.pret) AS stoc_total
FROM vstoc s
WHERE s.an = EXTRACT(YEAR FROM SYSDATE)
AND s.luna = EXTRACT(MONTH FROM SYSDATE)
AND (s.cants + s.cant - s.cante) > 0
),
-- Datorii curente (401, 404, 462)
datorii_curente AS (
SELECT SUM(CASE WHEN b.soldcred > b.solddeb THEN b.soldcred - b.solddeb ELSE 0 END) AS datorii_total
FROM vbalanta_parteneri b
WHERE (b.cont LIKE '401%' OR b.cont LIKE '404%' OR b.cont LIKE '462%')
AND b.an = EXTRACT(YEAR FROM SYSDATE)
AND b.luna = EXTRACT(MONTH FROM SYSDATE)
)
SELECT
'Lichiditate curenta' AS indicator,
ROUND((NVL(c.cash_total, 0) + NVL(cr.creante_total, 0) + NVL(st.stoc_total, 0)) / NULLIF(dc.datorii_total, 0), 2) AS valoare,
CASE
WHEN (NVL(c.cash_total, 0) + NVL(cr.creante_total, 0) + NVL(st.stoc_total, 0)) / NULLIF(dc.datorii_total, 0) < 1.0 THEN 'ALERTA'
WHEN (NVL(c.cash_total, 0) + NVL(cr.creante_total, 0) + NVL(st.stoc_total, 0)) / NULLIF(dc.datorii_total, 0) < 1.5 THEN 'ATENTIE'
ELSE 'OK'
END AS status,
'>= 1.5 ideal, >= 1.0 acceptabil' AS interpretare,
CASE
WHEN (NVL(c.cash_total, 0) + NVL(cr.creante_total, 0) + NVL(st.stoc_total, 0)) / NULLIF(dc.datorii_total, 0) < 1.0 THEN 'Risc de lichiditate - nu puteti acoperi datoriile curente'
WHEN (NVL(c.cash_total, 0) + NVL(cr.creante_total, 0) + NVL(st.stoc_total, 0)) / NULLIF(dc.datorii_total, 0) < 1.5 THEN 'Mentineti rezerve suplimentare'
ELSE 'Lichiditate buna'
END AS recomandare
FROM cash c, creante cr, stocuri st, datorii_curente dc
UNION ALL
SELECT
'Lichiditate rapida (Quick Ratio)' AS indicator,
ROUND((NVL(c.cash_total, 0) + NVL(cr.creante_total, 0)) / NULLIF(dc.datorii_total, 0), 2) AS valoare,
CASE
WHEN (NVL(c.cash_total, 0) + NVL(cr.creante_total, 0)) / NULLIF(dc.datorii_total, 0) < 0.7 THEN 'ALERTA'
WHEN (NVL(c.cash_total, 0) + NVL(cr.creante_total, 0)) / NULLIF(dc.datorii_total, 0) < 1.0 THEN 'ATENTIE'
ELSE 'OK'
END AS status,
'>= 1.0 ideal, >= 0.7 acceptabil' AS interpretare,
CASE
WHEN (NVL(c.cash_total, 0) + NVL(cr.creante_total, 0)) / NULLIF(dc.datorii_total, 0) < 0.7 THEN 'Dependenta de stocuri pentru plata datoriilor'
WHEN (NVL(c.cash_total, 0) + NVL(cr.creante_total, 0)) / NULLIF(dc.datorii_total, 0) < 1.0 THEN 'Accelerati incasarile'
ELSE 'Lichiditate rapida buna'
END AS recomandare
FROM cash c, creante cr, datorii_curente dc
UNION ALL
SELECT
'Rata de cash (Cash Ratio)' AS indicator,
ROUND(NVL(c.cash_total, 0) / NULLIF(dc.datorii_total, 0), 2) AS valoare,
CASE
WHEN NVL(c.cash_total, 0) / NULLIF(dc.datorii_total, 0) < 0.1 THEN 'ALERTA'
WHEN NVL(c.cash_total, 0) / NULLIF(dc.datorii_total, 0) < 0.2 THEN 'ATENTIE'
ELSE 'OK'
END AS status,
'>= 0.2 ideal, >= 0.1 minim' AS interpretare,
CASE
WHEN NVL(c.cash_total, 0) / NULLIF(dc.datorii_total, 0) < 0.1 THEN 'Cash insuficient - risc la plati urgente'
WHEN NVL(c.cash_total, 0) / NULLIF(dc.datorii_total, 0) < 0.2 THEN 'Cresteti rezervele de cash'
ELSE 'Rezerve de cash suficiente'
END AS recomandare
FROM cash c, datorii_curente dc
UNION ALL
SELECT
'Fond de rulment (RON)' AS indicator,
ROUND((NVL(c.cash_total, 0) + NVL(cr.creante_total, 0) + NVL(st.stoc_total, 0)) - NVL(dc.datorii_total, 0), 0) AS valoare,
CASE
WHEN (NVL(c.cash_total, 0) + NVL(cr.creante_total, 0) + NVL(st.stoc_total, 0)) - NVL(dc.datorii_total, 0) < 0 THEN 'ALERTA'
ELSE 'OK'
END AS status,
'Pozitiv = OK, Negativ = probleme' AS interpretare,
CASE
WHEN (NVL(c.cash_total, 0) + NVL(cr.creante_total, 0) + NVL(st.stoc_total, 0)) - NVL(dc.datorii_total, 0) < 0 THEN 'Fond de rulment negativ - necesita finantare'
ELSE 'Capital de lucru pozitiv'
END AS recomandare
FROM cash c, creante cr, stocuri st, datorii_curente dc
"""
# =============================================================================
# 36. CLASIFICARE DATORII PE TERMENE
# =============================================================================
CLASIFICARE_DATORII = """
WITH datorii_scadenta AS (
SELECT
r.id_part,
r.nume AS furnizor,
r.datascad,
(r.preccred + r.credit) - (r.precdeb + r.debit) AS sold_ramas,
CASE
WHEN r.datascad IS NULL THEN 'TERMEN_NEDEFINIT'
WHEN r.datascad <= TRUNC(SYSDATE) + 30 THEN 'TERMEN_SCURT'
WHEN r.datascad <= TRUNC(SYSDATE) + 90 THEN 'TERMEN_MEDIU'
ELSE 'TERMEN_LUNG'
END AS clasificare
FROM vireg_parteneri r
WHERE (r.cont LIKE '401%' OR r.cont LIKE '404%' OR r.cont LIKE '462%')
AND (r.preccred + r.credit) - (r.precdeb + r.debit) <> 0
)
SELECT
'Datorii termen scurt (<= 30 zile)' AS categorie,
ROUND(SUM(CASE WHEN clasificare = 'TERMEN_SCURT' THEN sold_ramas ELSE 0 END), 0) AS valoare,
COUNT(CASE WHEN clasificare = 'TERMEN_SCURT' THEN 1 END) AS nr_facturi,
'Prioritate INALTA - de platit urgent' AS observatie
FROM datorii_scadenta
UNION ALL
SELECT
'Datorii termen mediu (31-90 zile)' AS categorie,
ROUND(SUM(CASE WHEN clasificare = 'TERMEN_MEDIU' THEN sold_ramas ELSE 0 END), 0) AS valoare,
COUNT(CASE WHEN clasificare = 'TERMEN_MEDIU' THEN 1 END) AS nr_facturi,
'Prioritate MEDIE - planificati plata' AS observatie
FROM datorii_scadenta
UNION ALL
SELECT
'Datorii termen lung (> 90 zile)' AS categorie,
ROUND(SUM(CASE WHEN clasificare = 'TERMEN_LUNG' THEN sold_ramas ELSE 0 END), 0) AS valoare,
COUNT(CASE WHEN clasificare = 'TERMEN_LUNG' THEN 1 END) AS nr_facturi,
'Prioritate SCAZUTA' AS observatie
FROM datorii_scadenta
UNION ALL
SELECT
'Datorii fara scadenta definita' AS categorie,
ROUND(SUM(CASE WHEN clasificare = 'TERMEN_NEDEFINIT' THEN sold_ramas ELSE 0 END), 0) AS valoare,
COUNT(CASE WHEN clasificare = 'TERMEN_NEDEFINIT' THEN 1 END) AS nr_facturi,
'Verificati datele de scadenta' AS observatie
FROM datorii_scadenta
UNION ALL
SELECT
'TOTAL DATORII' AS categorie,
ROUND(SUM(sold_ramas), 0) AS valoare,
COUNT(*) AS nr_facturi,
'Suma totala de platit' AS observatie
FROM datorii_scadenta
"""
# =============================================================================
# 37. GRAD ACOPERIRE DATORII (Cash + Incasari vs Datorii scadente)
# =============================================================================
GRAD_ACOPERIRE_DATORII = """
WITH
cash_curent AS (
SELECT SUM(b.solddeb - b.soldcred) AS cash_total
FROM vbal b
WHERE (b.cont LIKE '512%' OR b.cont LIKE '531%')
AND b.an = EXTRACT(YEAR FROM SYSDATE)
AND b.luna = EXTRACT(MONTH FROM SYSDATE)
),
incasari_asteptate AS (
SELECT
ROUND(SUM(CASE WHEN r.datascad <= TRUNC(SYSDATE) + 30 THEN (r.precdeb + r.debit) - (r.preccred + r.credit) ELSE 0 END), 0) AS incasari_30,
ROUND(SUM(CASE WHEN r.datascad <= TRUNC(SYSDATE) + 60 THEN (r.precdeb + r.debit) - (r.preccred + r.credit) ELSE 0 END), 0) AS incasari_60,
ROUND(SUM(CASE WHEN r.datascad <= TRUNC(SYSDATE) + 90 THEN (r.precdeb + r.debit) - (r.preccred + r.credit) ELSE 0 END), 0) AS incasari_90
FROM vireg_parteneri r
WHERE (r.cont LIKE '4111%' OR r.cont LIKE '461%')
AND (r.precdeb + r.debit) - (r.preccred + r.credit) <> 0
),
plati_scadente AS (
SELECT
ROUND(SUM(CASE WHEN r.datascad <= TRUNC(SYSDATE) + 30 THEN (r.preccred + r.credit) - (r.precdeb + r.debit) ELSE 0 END), 0) AS plati_30,
ROUND(SUM(CASE WHEN r.datascad <= TRUNC(SYSDATE) + 60 THEN (r.preccred + r.credit) - (r.precdeb + r.debit) ELSE 0 END), 0) AS plati_60,
ROUND(SUM(CASE WHEN r.datascad <= TRUNC(SYSDATE) + 90 THEN (r.preccred + r.credit) - (r.precdeb + r.debit) ELSE 0 END), 0) AS plati_90
FROM vireg_parteneri r
WHERE (r.cont LIKE '401%' OR r.cont LIKE '404%' OR r.cont LIKE '462%')
AND (r.preccred + r.credit) - (r.precdeb + r.debit) <> 0
)
SELECT
'Cash disponibil' AS indicator,
NVL(c.cash_total, 0) AS valoare,
'-' AS acoperire,
'Disponibil imediat' AS explicatie
FROM cash_curent c
UNION ALL
SELECT
'Incasari asteptate 30 zile' AS indicator,
NVL(i.incasari_30, 0) AS valoare,
'-' AS acoperire,
'Facturi clienti scadente in 30 zile' AS explicatie
FROM incasari_asteptate i
UNION ALL
SELECT
'Plati scadente 30 zile' AS indicator,
NVL(p.plati_30, 0) AS valoare,
'-' AS acoperire,
'Facturi furnizori de platit in 30 zile' AS explicatie
FROM plati_scadente p
UNION ALL
SELECT
'GRAD ACOPERIRE 30 ZILE' AS indicator,
ROUND((NVL(c.cash_total, 0) + NVL(i.incasari_30, 0)) / NULLIF(p.plati_30, 0), 2) AS valoare,
CASE
WHEN (NVL(c.cash_total, 0) + NVL(i.incasari_30, 0)) / NULLIF(p.plati_30, 0) < 1 THEN 'DEFICIT'
WHEN (NVL(c.cash_total, 0) + NVL(i.incasari_30, 0)) / NULLIF(p.plati_30, 0) < 1.2 THEN 'ATENTIE'
ELSE 'OK'
END AS acoperire,
CASE
WHEN (NVL(c.cash_total, 0) + NVL(i.incasari_30, 0)) / NULLIF(p.plati_30, 0) < 1 THEN 'NECESITA FINANTARE'
ELSE 'Acoperiti platile'
END AS explicatie
FROM cash_curent c, incasari_asteptate i, plati_scadente p
UNION ALL
SELECT
'NECESAR FINANTARE 30 ZILE' AS indicator,
CASE
WHEN NVL(p.plati_30, 0) - (NVL(c.cash_total, 0) + NVL(i.incasari_30, 0)) > 0
THEN NVL(p.plati_30, 0) - (NVL(c.cash_total, 0) + NVL(i.incasari_30, 0))
ELSE 0
END AS valoare,
CASE
WHEN NVL(p.plati_30, 0) - (NVL(c.cash_total, 0) + NVL(i.incasari_30, 0)) > 0 THEN 'NECESAR'
ELSE 'NU E NECESAR'
END AS acoperire,
'Suma de obtinut pentru acoperirea deficitului' AS explicatie
FROM cash_curent c, incasari_asteptate i, plati_scadente p
"""
# =============================================================================
# 38. PROIECTIE LICHIDITATE 30/60/90 ZILE
# =============================================================================
PROIECTIE_LICHIDITATE = """
WITH
cash_curent AS (
SELECT SUM(b.solddeb - b.soldcred) AS cash_total
FROM vbal b
WHERE (b.cont LIKE '512%' OR b.cont LIKE '531%')
AND b.an = EXTRACT(YEAR FROM SYSDATE)
AND b.luna = EXTRACT(MONTH FROM SYSDATE)
),
incasari_programate AS (
SELECT
SUM(CASE WHEN r.datascad <= TRUNC(SYSDATE) + 30 THEN (r.precdeb + r.debit) - (r.preccred + r.credit) ELSE 0 END) AS incasari_30,
SUM(CASE WHEN r.datascad > TRUNC(SYSDATE) + 30 AND r.datascad <= TRUNC(SYSDATE) + 60 THEN (r.precdeb + r.debit) - (r.preccred + r.credit) ELSE 0 END) AS incasari_31_60,
SUM(CASE WHEN r.datascad > TRUNC(SYSDATE) + 60 AND r.datascad <= TRUNC(SYSDATE) + 90 THEN (r.precdeb + r.debit) - (r.preccred + r.credit) ELSE 0 END) AS incasari_61_90
FROM vireg_parteneri r
WHERE (r.cont LIKE '4111%' OR r.cont LIKE '461%')
AND (r.precdeb + r.debit) - (r.preccred + r.credit) <> 0
),
plati_programate AS (
SELECT
SUM(CASE WHEN r.datascad <= TRUNC(SYSDATE) + 30 THEN (r.preccred + r.credit) - (r.precdeb + r.debit) ELSE 0 END) AS plati_30,
SUM(CASE WHEN r.datascad > TRUNC(SYSDATE) + 30 AND r.datascad <= TRUNC(SYSDATE) + 60 THEN (r.preccred + r.credit) - (r.precdeb + r.debit) ELSE 0 END) AS plati_31_60,
SUM(CASE WHEN r.datascad > TRUNC(SYSDATE) + 60 AND r.datascad <= TRUNC(SYSDATE) + 90 THEN (r.preccred + r.credit) - (r.precdeb + r.debit) ELSE 0 END) AS plati_61_90
FROM vireg_parteneri r
WHERE (r.cont LIKE '401%' OR r.cont LIKE '404%' OR r.cont LIKE '462%')
AND (r.preccred + r.credit) - (r.precdeb + r.debit) <> 0
)
SELECT
'Sold initial (azi)' AS perioada,
ROUND(NVL(c.cash_total, 0), 0) AS sold_proiectat,
0 AS incasari,
0 AS plati,
0 AS flux_net,
'OK' AS status
FROM cash_curent c
UNION ALL
SELECT
'Proiectie 30 zile' AS perioada,
ROUND(NVL(c.cash_total, 0) + NVL(i.incasari_30, 0) - NVL(p.plati_30, 0), 0) AS sold_proiectat,
ROUND(NVL(i.incasari_30, 0), 0) AS incasari,
ROUND(NVL(p.plati_30, 0), 0) AS plati,
ROUND(NVL(i.incasari_30, 0) - NVL(p.plati_30, 0), 0) AS flux_net,
CASE
WHEN NVL(c.cash_total, 0) + NVL(i.incasari_30, 0) - NVL(p.plati_30, 0) < 0 THEN 'ALERTA'
WHEN NVL(c.cash_total, 0) + NVL(i.incasari_30, 0) - NVL(p.plati_30, 0) < NVL(c.cash_total, 0) * 0.2 THEN 'ATENTIE'
ELSE 'OK'
END AS status
FROM cash_curent c, incasari_programate i, plati_programate p
UNION ALL
SELECT
'Proiectie 60 zile' AS perioada,
ROUND(NVL(c.cash_total, 0) + NVL(i.incasari_30, 0) + NVL(i.incasari_31_60, 0) - NVL(p.plati_30, 0) - NVL(p.plati_31_60, 0), 0) AS sold_proiectat,
ROUND(NVL(i.incasari_31_60, 0), 0) AS incasari,
ROUND(NVL(p.plati_31_60, 0), 0) AS plati,
ROUND(NVL(i.incasari_31_60, 0) - NVL(p.plati_31_60, 0), 0) AS flux_net,
CASE
WHEN NVL(c.cash_total, 0) + NVL(i.incasari_30, 0) + NVL(i.incasari_31_60, 0) - NVL(p.plati_30, 0) - NVL(p.plati_31_60, 0) < 0 THEN 'ALERTA'
ELSE 'OK'
END AS status
FROM cash_curent c, incasari_programate i, plati_programate p
UNION ALL
SELECT
'Proiectie 90 zile' AS perioada,
ROUND(NVL(c.cash_total, 0) + NVL(i.incasari_30, 0) + NVL(i.incasari_31_60, 0) + NVL(i.incasari_61_90, 0) - NVL(p.plati_30, 0) - NVL(p.plati_31_60, 0) - NVL(p.plati_61_90, 0), 0) AS sold_proiectat,
ROUND(NVL(i.incasari_61_90, 0), 0) AS incasari,
ROUND(NVL(p.plati_61_90, 0), 0) AS plati,
ROUND(NVL(i.incasari_61_90, 0) - NVL(p.plati_61_90, 0), 0) AS flux_net,
CASE
WHEN NVL(c.cash_total, 0) + NVL(i.incasari_30, 0) + NVL(i.incasari_31_60, 0) + NVL(i.incasari_61_90, 0) - NVL(p.plati_30, 0) - NVL(p.plati_31_60, 0) - NVL(p.plati_61_90, 0) < 0 THEN 'ALERTA'
ELSE 'OK'
END AS status
FROM cash_curent c, incasari_programate i, plati_programate p
"""
# =============================================================================
# 39. SUMAR EXECUTIV YoY (cu comparatie an anterior)
# =============================================================================
# OPTIMIZED: Single 24-month scan with CASE-based period partitioning instead of 2 separate view scans
SUMAR_EXECUTIV_YOY = """
WITH base_data AS (
SELECT
f.id_vanzare,
f.id_part,
CASE WHEN f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -12) THEN 'CURENT' ELSE 'ANTERIOR' END AS perioada,
d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END AS vanzare,
d.cantitate * (CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END - d.pret_achizitie) AS marja
FROM VANZARI f
JOIN VANZARI_DETALII d ON d.id_vanzare = f.id_vanzare
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -24)
),
aggregated AS (
SELECT
SUM(CASE WHEN perioada = 'CURENT' THEN vanzare ELSE 0 END) AS vanzari_curente,
SUM(CASE WHEN perioada = 'ANTERIOR' THEN vanzare ELSE 0 END) AS vanzari_anterioare,
SUM(CASE WHEN perioada = 'CURENT' THEN marja ELSE 0 END) AS marja_curenta,
SUM(CASE WHEN perioada = 'ANTERIOR' THEN marja ELSE 0 END) AS marja_anterioara,
COUNT(DISTINCT CASE WHEN perioada = 'CURENT' THEN id_vanzare END) AS facturi_curente,
COUNT(DISTINCT CASE WHEN perioada = 'ANTERIOR' THEN id_vanzare END) AS facturi_anterioare,
COUNT(DISTINCT CASE WHEN perioada = 'CURENT' THEN id_part END) AS clienti_curenti,
COUNT(DISTINCT CASE WHEN perioada = 'ANTERIOR' THEN id_part END) AS clienti_anteriori
FROM base_data
)
SELECT
'Vanzari totale (RON)' AS indicator,
ROUND(vanzari_curente, 0) AS valoare_curenta,
ROUND(vanzari_anterioare, 0) AS valoare_anterioara,
ROUND(vanzari_curente - vanzari_anterioare, 0) AS variatie_absoluta,
ROUND((vanzari_curente - vanzari_anterioare) * 100 / NULLIF(vanzari_anterioare, 0), 2) AS variatie_procent,
CASE
WHEN vanzari_curente > vanzari_anterioare * 1.05 THEN 'CRESTERE'
WHEN vanzari_curente < vanzari_anterioare * 0.95 THEN 'SCADERE'
ELSE 'STABIL'
END AS trend
FROM aggregated
UNION ALL
SELECT
'Marja bruta (RON)' AS indicator,
ROUND(marja_curenta, 0) AS valoare_curenta,
ROUND(marja_anterioara, 0) AS valoare_anterioara,
ROUND(marja_curenta - marja_anterioara, 0) AS variatie_absoluta,
ROUND((marja_curenta - marja_anterioara) * 100 / NULLIF(marja_anterioara, 0), 2) AS variatie_procent,
CASE
WHEN marja_curenta > marja_anterioara * 1.05 THEN 'CRESTERE'
WHEN marja_curenta < marja_anterioara * 0.95 THEN 'SCADERE'
ELSE 'STABIL'
END AS trend
FROM aggregated
UNION ALL
SELECT
'Procent marja (%)' AS indicator,
ROUND(marja_curenta * 100 / NULLIF(vanzari_curente, 0), 2) AS valoare_curenta,
ROUND(marja_anterioara * 100 / NULLIF(vanzari_anterioare, 0), 2) AS valoare_anterioara,
ROUND(marja_curenta * 100 / NULLIF(vanzari_curente, 0) - marja_anterioara * 100 / NULLIF(vanzari_anterioare, 0), 2) AS variatie_absoluta,
NULL AS variatie_procent,
CASE
WHEN marja_curenta * 100 / NULLIF(vanzari_curente, 0) > marja_anterioara * 100 / NULLIF(vanzari_anterioare, 0) + 1 THEN 'CRESTERE'
WHEN marja_curenta * 100 / NULLIF(vanzari_curente, 0) < marja_anterioara * 100 / NULLIF(vanzari_anterioare, 0) - 1 THEN 'SCADERE'
ELSE 'STABIL'
END AS trend
FROM aggregated
UNION ALL
SELECT
'Numar facturi' AS indicator,
facturi_curente AS valoare_curenta,
facturi_anterioare AS valoare_anterioara,
facturi_curente - facturi_anterioare AS variatie_absoluta,
ROUND((facturi_curente - facturi_anterioare) * 100 / NULLIF(facturi_anterioare, 0), 2) AS variatie_procent,
CASE
WHEN facturi_curente > facturi_anterioare * 1.05 THEN 'CRESTERE'
WHEN facturi_curente < facturi_anterioare * 0.95 THEN 'SCADERE'
ELSE 'STABIL'
END AS trend
FROM aggregated
UNION ALL
SELECT
'Numar clienti activi' AS indicator,
clienti_curenti AS valoare_curenta,
clienti_anteriori AS valoare_anterioara,
clienti_curenti - clienti_anteriori AS variatie_absoluta,
ROUND((clienti_curenti - clienti_anteriori) * 100 / NULLIF(clienti_anteriori, 0), 2) AS variatie_procent,
CASE
WHEN clienti_curenti > clienti_anteriori THEN 'CRESTERE'
WHEN clienti_curenti < clienti_anteriori THEN 'SCADERE'
ELSE 'STABIL'
END AS trend
FROM aggregated
"""
# =============================================================================
# 40. DSO DPO YoY (comparatie an anterior)
# =============================================================================
DSO_DPO_YOY = """
WITH
-- Metrici curente
vanzari_curente AS (
SELECT SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END) AS total_vanzari
FROM fact_vfacturi2 f
JOIN fact_vfacturi_detalii d ON d.id_vanzare = f.id_vanzare
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -12)
),
sold_clienti_curent AS (
SELECT SUM(CASE WHEN b.solddeb > b.soldcred THEN b.solddeb - b.soldcred ELSE 0 END) AS total_creante
FROM vbalanta_parteneri b
WHERE b.cont LIKE '4111%'
AND b.an = EXTRACT(YEAR FROM SYSDATE)
AND b.luna = EXTRACT(MONTH FROM SYSDATE)
),
achizitii_curente AS (
SELECT SUM(ABS(r.cant * r.pret)) AS total_achizitii
FROM vrul r
WHERE r.id_tip_rulaj = 1 AND r.cant > 0
AND r.dataact >= ADD_MONTHS(TRUNC(SYSDATE), -12)
),
sold_furnizori_curent AS (
SELECT SUM(CASE WHEN b.soldcred > b.solddeb THEN b.soldcred - b.solddeb ELSE 0 END) AS total_datorii
FROM vbalanta_parteneri b
WHERE b.cont LIKE '401%'
AND b.an = EXTRACT(YEAR FROM SYSDATE)
AND b.luna = EXTRACT(MONTH FROM SYSDATE)
),
-- Metrici anterioare (aproximare - vanzari an anterior)
vanzari_anterioare AS (
SELECT SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END) AS total_vanzari
FROM fact_vfacturi2 f
JOIN fact_vfacturi_detalii d ON d.id_vanzare = f.id_vanzare
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -24)
AND f.data_act < ADD_MONTHS(TRUNC(SYSDATE), -12)
),
achizitii_anterioare AS (
SELECT SUM(ABS(r.cant * r.pret)) AS total_achizitii
FROM vrul r
WHERE r.id_tip_rulaj = 1 AND r.cant > 0
AND r.dataact >= ADD_MONTHS(TRUNC(SYSDATE), -24)
AND r.dataact < ADD_MONTHS(TRUNC(SYSDATE), -12)
),
-- Solduri anterioare (aproximare - luna curenta anul trecut)
sold_clienti_anterior AS (
SELECT SUM(CASE WHEN b.solddeb > b.soldcred THEN b.solddeb - b.soldcred ELSE 0 END) AS total_creante
FROM vbalanta_parteneri b
WHERE b.cont LIKE '4111%'
AND b.an = EXTRACT(YEAR FROM SYSDATE) - 1
AND b.luna = EXTRACT(MONTH FROM SYSDATE)
),
sold_furnizori_anterior AS (
SELECT SUM(CASE WHEN b.soldcred > b.solddeb THEN b.soldcred - b.solddeb ELSE 0 END) AS total_datorii
FROM vbalanta_parteneri b
WHERE b.cont LIKE '401%'
AND b.an = EXTRACT(YEAR FROM SYSDATE) - 1
AND b.luna = EXTRACT(MONTH FROM SYSDATE)
)
SELECT
'DSO (Zile incasare)' AS indicator,
ROUND(NVL(scc.total_creante, 0) * 365 / NULLIF(vc.total_vanzari, 0), 0) AS valoare_curenta,
ROUND(NVL(sca.total_creante, 0) * 365 / NULLIF(va.total_vanzari, 0), 0) AS valoare_anterioara,
ROUND(NVL(scc.total_creante, 0) * 365 / NULLIF(vc.total_vanzari, 0) - NVL(sca.total_creante, 0) * 365 / NULLIF(va.total_vanzari, 0), 0) AS variatie_zile,
CASE
WHEN NVL(scc.total_creante, 0) * 365 / NULLIF(vc.total_vanzari, 0) < NVL(sca.total_creante, 0) * 365 / NULLIF(va.total_vanzari, 0) THEN 'IMBUNATATIRE'
WHEN NVL(scc.total_creante, 0) * 365 / NULLIF(vc.total_vanzari, 0) > NVL(sca.total_creante, 0) * 365 / NULLIF(va.total_vanzari, 0) + 5 THEN 'DETERIORARE'
ELSE 'STABIL'
END AS trend,
CASE
WHEN NVL(scc.total_creante, 0) * 365 / NULLIF(vc.total_vanzari, 0) > 60 THEN 'ALERTA'
WHEN NVL(scc.total_creante, 0) * 365 / NULLIF(vc.total_vanzari, 0) > 45 THEN 'ATENTIE'
ELSE 'OK'
END AS status
FROM vanzari_curente vc, sold_clienti_curent scc, vanzari_anterioare va, sold_clienti_anterior sca
UNION ALL
SELECT
'DPO (Zile plata)' AS indicator,
ROUND(NVL(sfc.total_datorii, 0) * 365 / NULLIF(ac.total_achizitii, 0), 0) AS valoare_curenta,
ROUND(NVL(sfa.total_datorii, 0) * 365 / NULLIF(aa.total_achizitii, 0), 0) AS valoare_anterioara,
ROUND(NVL(sfc.total_datorii, 0) * 365 / NULLIF(ac.total_achizitii, 0) - NVL(sfa.total_datorii, 0) * 365 / NULLIF(aa.total_achizitii, 0), 0) AS variatie_zile,
CASE
WHEN NVL(sfc.total_datorii, 0) * 365 / NULLIF(ac.total_achizitii, 0) > NVL(sfa.total_datorii, 0) * 365 / NULLIF(aa.total_achizitii, 0) THEN 'IMBUNATATIRE'
WHEN NVL(sfc.total_datorii, 0) * 365 / NULLIF(ac.total_achizitii, 0) < NVL(sfa.total_datorii, 0) * 365 / NULLIF(aa.total_achizitii, 0) - 5 THEN 'DETERIORARE'
ELSE 'STABIL'
END AS trend,
CASE
WHEN NVL(sfc.total_datorii, 0) * 365 / NULLIF(ac.total_achizitii, 0) < 15 THEN 'ATENTIE'
ELSE 'OK'
END AS status
FROM achizitii_curente ac, sold_furnizori_curent sfc, achizitii_anterioare aa, sold_furnizori_anterior sfa
"""
# =============================================================================
# 41. CONCENTRARE RISC YoY (comparatie an anterior)
# =============================================================================
CONCENTRARE_RISC_YOY = """
WITH
-- Single scan for current year: compute total + per-client with ranking
vanzari_curent AS (
SELECT
f.id_part,
SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END) AS vanzari
FROM fact_vfacturi2 f
JOIN fact_vfacturi_detalii d ON d.id_vanzare = f.id_vanzare
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -12)
GROUP BY f.id_part
),
ranked_curent AS (
SELECT vanzari, ROW_NUMBER() OVER (ORDER BY vanzari DESC) AS rn
FROM vanzari_curent
),
metrics_curent AS (
SELECT
SUM(vanzari) AS total,
SUM(CASE WHEN rn <= 1 THEN vanzari ELSE 0 END) AS top1,
SUM(CASE WHEN rn <= 5 THEN vanzari ELSE 0 END) AS top5,
SUM(CASE WHEN rn <= 10 THEN vanzari ELSE 0 END) AS top10
FROM ranked_curent
),
-- Single scan for previous year
vanzari_anterior AS (
SELECT
f.id_part,
SUM(d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END) AS vanzari
FROM fact_vfacturi2 f
JOIN fact_vfacturi_detalii d ON d.id_vanzare = f.id_vanzare
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -24)
AND f.data_act < ADD_MONTHS(TRUNC(SYSDATE), -12)
GROUP BY f.id_part
),
ranked_anterior AS (
SELECT vanzari, ROW_NUMBER() OVER (ORDER BY vanzari DESC) AS rn
FROM vanzari_anterior
),
-- Raw metrics for anterior (may have NULL if no data)
metrics_anterior_raw AS (
SELECT
SUM(vanzari) AS total,
SUM(CASE WHEN rn <= 1 THEN vanzari ELSE 0 END) AS top1,
SUM(CASE WHEN rn <= 5 THEN vanzari ELSE 0 END) AS top5,
SUM(CASE WHEN rn <= 10 THEN vanzari ELSE 0 END) AS top10
FROM ranked_anterior
),
-- Fallback to 0 for NULL values (when no anterior data exists)
metrics_anterior AS (
SELECT
NVL(total, 0) AS total,
NVL(top1, 0) AS top1,
NVL(top5, 0) AS top5,
NVL(top10, 0) AS top10
FROM metrics_anterior_raw
),
-- Final metrics: just 1 row each, no cartesian product
combined AS (
SELECT
ROUND(mc.top1 * 100.0 / NULLIF(mc.total, 0), 2) AS pct_curent_1,
CASE WHEN ma.total = 0 THEN NULL ELSE ROUND(ma.top1 * 100.0 / ma.total, 2) END AS pct_anterior_1,
ROUND(mc.top5 * 100.0 / NULLIF(mc.total, 0), 2) AS pct_curent_5,
CASE WHEN ma.total = 0 THEN NULL ELSE ROUND(ma.top5 * 100.0 / ma.total, 2) END AS pct_anterior_5,
ROUND(mc.top10 * 100.0 / NULLIF(mc.total, 0), 2) AS pct_curent_10,
CASE WHEN ma.total = 0 THEN NULL ELSE ROUND(ma.top10 * 100.0 / ma.total, 2) END AS pct_anterior_10,
CASE WHEN ma.total > 0 THEN 1 ELSE 0 END AS has_anterior
FROM metrics_curent mc
CROSS JOIN metrics_anterior ma
)
SELECT
'Top 1 client' AS indicator,
pct_curent_1 AS procent_curent,
pct_anterior_1 AS procent_anterior,
CASE WHEN has_anterior = 1 THEN ROUND(pct_curent_1 - pct_anterior_1, 2) ELSE NULL END AS variatie,
CASE
WHEN has_anterior = 0 THEN 'FARA DATE YOY'
WHEN pct_curent_1 < pct_anterior_1 THEN 'DIVERSIFICARE'
WHEN pct_curent_1 > pct_anterior_1 + 5 THEN 'CONCENTRARE'
ELSE 'STABIL'
END AS trend
FROM combined
UNION ALL
SELECT
'Top 5 clienti' AS indicator,
pct_curent_5 AS procent_curent,
pct_anterior_5 AS procent_anterior,
CASE WHEN has_anterior = 1 THEN ROUND(pct_curent_5 - pct_anterior_5, 2) ELSE NULL END AS variatie,
CASE
WHEN has_anterior = 0 THEN 'FARA DATE YOY'
WHEN pct_curent_5 < pct_anterior_5 THEN 'DIVERSIFICARE'
WHEN pct_curent_5 > pct_anterior_5 + 5 THEN 'CONCENTRARE'
ELSE 'STABIL'
END AS trend
FROM combined
UNION ALL
SELECT
'Top 10 clienti' AS indicator,
pct_curent_10 AS procent_curent,
pct_anterior_10 AS procent_anterior,
CASE WHEN has_anterior = 1 THEN ROUND(pct_curent_10 - pct_anterior_10, 2) ELSE NULL END AS variatie,
CASE
WHEN has_anterior = 0 THEN 'FARA DATE YOY'
WHEN pct_curent_10 < pct_anterior_10 THEN 'DIVERSIFICARE'
WHEN pct_curent_10 > pct_anterior_10 + 5 THEN 'CONCENTRARE'
ELSE 'STABIL'
END AS trend
FROM combined
"""
# =============================================================================
# 42. INDICATORI AGREGATI VENITURI YoY
# =============================================================================
# OPTIMIZED: Single 24-month scan with CASE-based period partitioning, no FULL OUTER JOIN needed
INDICATORI_AGREGATI_VENITURI_YOY = """
WITH base_data AS (
SELECT
CASE
WHEN d.cont IN ('341', '345') THEN 'Productie proprie'
WHEN d.cont = '301' THEN 'Materii prime'
ELSE 'Marfa revanduta'
END AS linie_business,
CASE WHEN f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -12) THEN 'CURENT' ELSE 'ANTERIOR' END AS perioada,
d.cantitate * CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END AS vanzare,
d.cantitate * (CASE WHEN d.pret_cu_tva = 1 THEN d.pret / (1 + d.proc_tvav/100) ELSE d.pret END - d.pret_achizitie) AS marja
FROM VANZARI f
JOIN VANZARI_DETALII d ON d.id_vanzare = f.id_vanzare
WHERE f.sters = 0 AND d.sters = 0
AND f.tip > 0 AND f.tip NOT IN (7, 8, 9, 24)
AND f.data_act >= ADD_MONTHS(TRUNC(SYSDATE), -24)
)
SELECT
linie_business,
ROUND(SUM(CASE WHEN perioada = 'CURENT' THEN vanzare ELSE 0 END), 0) AS vanzari_curente,
ROUND(SUM(CASE WHEN perioada = 'ANTERIOR' THEN vanzare ELSE 0 END), 0) AS vanzari_anterioare,
ROUND(SUM(CASE WHEN perioada = 'CURENT' THEN vanzare ELSE 0 END) - SUM(CASE WHEN perioada = 'ANTERIOR' THEN vanzare ELSE 0 END), 0) AS variatie_vanzari,
ROUND((SUM(CASE WHEN perioada = 'CURENT' THEN vanzare ELSE 0 END) - SUM(CASE WHEN perioada = 'ANTERIOR' THEN vanzare ELSE 0 END)) * 100 / NULLIF(SUM(CASE WHEN perioada = 'ANTERIOR' THEN vanzare ELSE 0 END), 0), 2) AS variatie_procent,
ROUND(SUM(CASE WHEN perioada = 'CURENT' THEN marja ELSE 0 END) * 100 / NULLIF(SUM(CASE WHEN perioada = 'CURENT' THEN vanzare ELSE 0 END), 0), 2) AS marja_curenta_pct,
ROUND(SUM(CASE WHEN perioada = 'ANTERIOR' THEN marja ELSE 0 END) * 100 / NULLIF(SUM(CASE WHEN perioada = 'ANTERIOR' THEN vanzare ELSE 0 END), 0), 2) AS marja_anterioara_pct,
CASE
WHEN SUM(CASE WHEN perioada = 'CURENT' THEN vanzare ELSE 0 END) > SUM(CASE WHEN perioada = 'ANTERIOR' THEN vanzare ELSE 0 END) * 1.05 THEN 'CRESTERE'
WHEN SUM(CASE WHEN perioada = 'CURENT' THEN vanzare ELSE 0 END) < SUM(CASE WHEN perioada = 'ANTERIOR' THEN vanzare ELSE 0 END) * 0.95 THEN 'SCADERE'
ELSE 'STABIL'
END AS trend
FROM base_data
GROUP BY linie_business
ORDER BY SUM(CASE WHEN perioada = 'CURENT' THEN vanzare ELSE 0 END) DESC
"""
# Dictionary mapping query names to queries and their parameters
QUERIES = {
'sumar_executiv': {
'sql': SUMAR_EXECUTIV,
'params': {},
'title': 'Sumar Executiv - KPIs',
'description': 'Indicatori cheie de performanță pentru ultimele 12 luni'
},
'marja_per_client': {
'sql': MARJA_PER_CLIENT,
'params': {'months': 12},
'title': 'Marja per Client',
'description': 'Analiza profitabilității per client'
},
'clienti_marja_mica': {
'sql': CLIENTI_MARJA_MICA,
'params': {'months': 12, 'min_sales': 5000, 'margin_threshold': 15},
'title': '⚠️ ALERTĂ: Clienți cu Marjă Mică',
'description': 'Clienți cu marjă sub 15% și vânzări peste 5000 RON'
},
'vanzari_sub_cost': {
'sql': VANZARI_SUB_COST,
'params': {'months': 12},
'title': '🚨 ALERTĂ CRITICĂ: Vânzări sub Cost',
'description': 'Produse vândute sub prețul de achiziție'
},
'marja_per_categorie': {
'sql': MARJA_PER_CATEGORIE,
'params': {'months': 12},
'title': 'Marja per Categorie',
'description': 'Profitabilitate per grupă și subgrupă de produse'
},
'productie_vs_revanzare': {
'sql': PRODUCTIE_VS_REVANZARE,
'params': {'months': 12},
'title': 'Producție Proprie vs Revânzare',
'description': 'Comparație marjă: producție proprie vs marfă cumpărată'
},
'top_produse': {
'sql': TOP_PRODUSE,
'params': {'months': 12},
'title': 'Top 50 Produse',
'description': 'Cele mai vândute produse după valoare'
},
'marja_per_gestiune': {
'sql': MARJA_PER_GESTIUNE,
'params': {'months': 12},
'title': 'Marja per Gestiune',
'description': 'Profitabilitate per gestiune/depozit (doar articole gestionabile)'
},
'articole_negestionabile': {
'sql': ARTICOLE_NEGESTIONABILE,
'params': {'months': 12},
'title': 'Articole Negestionabile',
'description': 'Vânzări articole care nu se țin pe stoc (servicii, etc.)'
},
'dispersie_preturi': {
'sql': DISPERSIE_PRETURI,
'params': {'months': 12},
'title': 'Dispersia Prețurilor',
'description': 'Produse vândute la prețuri foarte diferite între clienți'
},
'clienti_sub_medie': {
'sql': CLIENTI_SUB_MEDIE,
'params': {'months': 12},
'title': 'Clienți cu Prețuri sub Medie',
'description': 'Clienți care cumpără cu >15% sub prețul mediu'
},
'trending_clienti': {
'sql': TRENDING_CLIENTI,
'params': {},
'title': 'Trending Clienți YoY',
'description': 'Evoluția vânzărilor per client: an curent vs an trecut'
},
'concentrare_clienti': {
'sql': CONCENTRARE_CLIENTI,
'params': {'months': 12},
'title': 'Concentrare Clienți (Top 30)',
'description': 'Dependența de clienții mari (top 30 + restul)'
},
'vanzari_lunare': {
'sql': VANZARI_LUNARE,
'params': {},
'title': 'Vânzări Lunare (24 luni)',
'description': 'Evoluția vânzărilor și marjei pe luni'
},
'stoc_curent': {
'sql': STOC_CURENT,
'params': {},
'title': 'Stoc Curent per Gestiune',
'description': 'Valoarea stocului actual'
},
'stoc_lent': {
'sql': STOC_LENT,
'params': {},
'title': '⚠️ Stoc Lent (>90 zile)',
'description': 'Produse fără mișcare de peste 90 de zile'
},
'rotatie_stocuri': {
'sql': ROTATIE_STOCURI,
'params': {},
'title': 'Rotație Stocuri',
'description': 'Viteza de rotație a stocurilor'
},
'analiza_prajitorie': {
'sql': ANALIZA_PRAJITORIE,
'params': {'months': 12},
'title': 'Analiză Prăjitorie',
'description': 'Fluxul materiilor prime și producției'
},
# =========================================================================
# NEW: Financial Queries
# =========================================================================
'solduri_clienti': {
'sql': SOLDURI_CLIENTI,
'params': {},
'title': 'Solduri Clienți (Creanțe)',
'description': 'Creanțe din cont 4111 - bani de încasat de la clienți'
},
'solduri_furnizori': {
'sql': SOLDURI_FURNIZORI,
'params': {},
'title': 'Solduri Furnizori (Datorii)',
'description': 'Datorii din cont 401 - bani de plătit către furnizori'
},
'aging_creante': {
'sql': AGING_CREANTE,
'params': {},
'title': 'Aging Creanțe',
'description': 'Vechimea creanțelor pe intervale: <30, 31-60, 61-90, >90 zile'
},
'facturi_restante': {
'sql': FACTURI_RESTANTE,
'params': {},
'title': '⚠️ Facturi Restante Clienți',
'description': 'Facturi clienți depășite ca scadență - necesită urmărire'
},
'aging_datorii': {
'sql': AGING_DATORII,
'params': {},
'title': 'Aging Datorii Furnizori',
'description': 'Vechimea datoriilor către furnizori pe intervale: <30, 31-60, 61-90, >90 zile'
},
'facturi_restante_furnizori': {
'sql': FACTURI_RESTANTE_FURNIZORI,
'params': {},
'title': '⚠️ Facturi Restante Furnizori',
'description': 'Facturi furnizori depășite ca scadență - de plătit urgent'
},
'dso_dpo': {
'sql': DSO_DPO,
'params': {},
'title': 'DSO / DPO',
'description': 'Zile medii încasare clienți (DSO) și plată furnizori (DPO)'
},
'pozitia_cash': {
'sql': POZITIA_CASH,
'params': {},
'title': 'Poziția Cash',
'description': 'Disponibilități în bancă și casă'
},
'ciclu_conversie_cash': {
'sql': CICLU_CONVERSIE_CASH,
'params': {},
'title': 'Ciclu Conversie Cash',
'description': 'CCC = DIO + DSO - DPO (zile de la plată furnizor la încasare client)'
},
# =========================================================================
# NEW: Aggregated Indicators
# =========================================================================
'indicatori_agregati_venituri': {
'sql': INDICATORI_AGREGATI_VENITURI,
'params': {'months': 12},
'title': 'Indicatori Agregați Venituri',
'description': 'Revenue mix per linie de business cu contribuție la profit'
},
'sezonalitate_lunara': {
'sql': SEZONALITATE_LUNARA,
'params': {},
'title': 'Sezonalitate Lunară',
'description': 'Analiza sezonalității pe 24 luni istoric'
},
'portofoliu_clienti': {
'sql': PORTOFOLIU_CLIENTI,
'params': {},
'title': 'Portofoliu Clienți',
'description': 'Sănătatea portofoliului: activi, noi, pierduți, inactivi'
},
'frecventa_clienti': {
'sql': FRECVENTA_CLIENTI,
'params': {},
'title': 'Frecvența Clienți',
'description': 'Frecvența comenzilor și evoluție YoY'
},
'concentrare_risc': {
'sql': CONCENTRARE_RISC,
'params': {'months': 12},
'title': 'Concentrare Risc',
'description': 'Risc concentrare: Top 1/5/10 clienți ca % din total'
},
# =========================================================================
# NEW: Improved Margin Analysis
# =========================================================================
'clienti_ranking_profit': {
'sql': CLIENTI_RANKING_PROFIT,
'params': {'months': 12},
'title': 'Clienți Ranking Profit',
'description': 'Ranking clienți după PROFIT (nu venituri)'
},
'marja_client_categorie': {
'sql': MARJA_CLIENT_CATEGORIE,
'params': {'months': 12},
'title': 'Marjă Client/Categorie',
'description': 'Marjă per categorie PER CLIENT (același client poate fi profitabil/neprofitabil pe categorii diferite)'
},
'evolutie_discount': {
'sql': EVOLUTIE_DISCOUNT,
'params': {},
'title': 'Evoluție Discount',
'description': 'Discount creep - evoluția prețului în timp (alerte scăderi)'
},
# =========================================================================
# NEW: Indicatori Generali și Lichiditate (PLAN_INDICATORI_LICHIDITATE_YOY)
# =========================================================================
'indicatori_generali': {
'sql': INDICATORI_GENERALI,
'params': {},
'title': 'Indicatori Generali de Business',
'description': 'Grad îndatorare, autonomie financiară, ROA, marjă netă'
},
'indicatori_lichiditate': {
'sql': INDICATORI_LICHIDITATE,
'params': {},
'title': 'Indicatori de Lichiditate',
'description': 'Lichiditate curentă, rapidă, cash ratio, fond de rulment'
},
'clasificare_datorii': {
'sql': CLASIFICARE_DATORII,
'params': {},
'title': 'Clasificare Datorii pe Termene',
'description': 'Datorii termen scurt (<30z), mediu (31-90z), lung (>90z)'
},
'grad_acoperire_datorii': {
'sql': GRAD_ACOPERIRE_DATORII,
'params': {},
'title': 'Grad Acoperire Datorii',
'description': 'Cash + încasări așteptate vs datorii scadente, necesar finanțare'
},
'proiectie_lichiditate': {
'sql': PROIECTIE_LICHIDITATE,
'params': {},
'title': 'Proiecție Lichiditate 30/60/90 zile',
'description': 'Sold cash proiectat pe orizonturi de 30, 60 și 90 zile'
},
# =========================================================================
# NEW: Comparații YoY
# =========================================================================
'sumar_executiv_yoy': {
'sql': SUMAR_EXECUTIV_YOY,
'params': {},
'title': 'Sumar Executiv YoY',
'description': 'KPI-uri cu comparație an curent vs an anterior'
},
'dso_dpo_yoy': {
'sql': DSO_DPO_YOY,
'params': {},
'title': 'DSO/DPO YoY',
'description': 'Evoluție zile încasare/plată vs an anterior'
},
'concentrare_risc_yoy': {
'sql': CONCENTRARE_RISC_YOY,
'params': {},
'title': 'Concentrare Risc YoY',
'description': 'Evoluție concentrare Top 1/5/10 clienți vs an anterior'
},
'indicatori_agregati_venituri_yoy': {
'sql': INDICATORI_AGREGATI_VENITURI_YOY,
'params': {},
'title': 'Indicatori Agregați Venituri YoY',
'description': 'Linii de business comparate cu anul anterior'
},
}