245 lines
8.2 KiB
Plaintext
245 lines
8.2 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "36ae91c9",
|
||
"metadata": {},
|
||
"source": [
|
||
"# 02 — 3 KPI principales\n",
|
||
"\n",
|
||
"Por **centro real de facturación** (`tpv_terminals.center_id` del invoice) y ventana temporal:\n",
|
||
"\n",
|
||
"1. **A — Valor facturado de quotes call_center que CONVIRTIERON** \n",
|
||
" Quote creado por usuario call_center + existe `tpv_orders_invoice` con el mismo `order_id`. Sumamos `tpv_orders_order.total_cost` o líneas. Centro = centro del invoice.\n",
|
||
"\n",
|
||
"2. **B — Valor facturado total a esos mismos clientes en centros** \n",
|
||
" Misma identidad cliente (`customer_id` y/o `vehicle_id` y/o `tlf`+`matricula` normalizados). Todas las facturas del cliente en ese centro en la misma ventana. Debe ser ≥ A.\n",
|
||
"\n",
|
||
"3. **C — Facturación total del centro** \n",
|
||
" Suma de invoices del centro en la ventana, todos los clientes."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "7938921f",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"from google.cloud import bigquery\n",
|
||
"import pandas as pd\n",
|
||
"\n",
|
||
"PROJECT = \"autingo-159109\"\n",
|
||
"DATASET = \"psql_dcpublic\"\n",
|
||
"bq = bigquery.Client(project=PROJECT)\n",
|
||
"\n",
|
||
"WINDOW_DAYS = 90\n",
|
||
"\n",
|
||
"def q(sql):\n",
|
||
" return bq.query(sql).to_dataframe()"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "c7cfa785",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Setup: CTEs base reutilizables\n",
|
||
"\n",
|
||
"Construimos una query maestra con CTEs para A, B, C juntos por centro."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "3e06df33",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"SQL_KPI = f\"\"\"\n",
|
||
"DECLARE window_days INT64 DEFAULT {WINDOW_DAYS};\n",
|
||
"DECLARE t_start TIMESTAMP DEFAULT TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL window_days DAY);\n",
|
||
"\n",
|
||
"WITH\n",
|
||
"cc_users AS (\n",
|
||
" SELECT DISTINCT tpvuser_id AS user_id\n",
|
||
" FROM `{PROJECT}.{DATASET}.tpv_authorization_tpvuser_centers`\n",
|
||
" WHERE dccenter_id IN (159, 162)\n",
|
||
"),\n",
|
||
"-- Quotes creados por call_center que TIENEN invoice (convertidos)\n",
|
||
"cc_converted AS (\n",
|
||
" SELECT\n",
|
||
" q.id AS quote_id,\n",
|
||
" q.order_id,\n",
|
||
" q.created_at AS quote_ts,\n",
|
||
" o.customer_id,\n",
|
||
" o.vehicle_id,\n",
|
||
" o.terminal_id,\n",
|
||
" t.center_id,\n",
|
||
" o.total_cost,\n",
|
||
" i.id AS invoice_id,\n",
|
||
" i.created_at AS invoice_ts\n",
|
||
" FROM `{PROJECT}.{DATASET}.tpv_orders_quote` q\n",
|
||
" JOIN cc_users cc ON q.created_by_id = cc.user_id\n",
|
||
" JOIN `{PROJECT}.{DATASET}.tpv_orders_order` o ON q.order_id = o.id\n",
|
||
" JOIN `{PROJECT}.{DATASET}.tpv_orders_invoice` i ON i.order_id = o.id\n",
|
||
" LEFT JOIN `{PROJECT}.{DATASET}.tpv_terminals` t ON o.terminal_id = t.id\n",
|
||
" WHERE q.created_at >= t_start\n",
|
||
" AND q.deleted_at IS NULL\n",
|
||
"),\n",
|
||
"-- Clientes \"tocados\" por call_center (customer_id + vehicle_id)\n",
|
||
"cc_clients AS (\n",
|
||
" SELECT DISTINCT center_id, customer_id, vehicle_id\n",
|
||
" FROM cc_converted\n",
|
||
" WHERE customer_id IS NOT NULL\n",
|
||
"),\n",
|
||
"-- Todas las facturas en la ventana, con centro real\n",
|
||
"all_invoices AS (\n",
|
||
" SELECT\n",
|
||
" i.id AS invoice_id,\n",
|
||
" i.order_id,\n",
|
||
" i.created_at AS invoice_ts,\n",
|
||
" o.customer_id,\n",
|
||
" o.vehicle_id,\n",
|
||
" o.terminal_id,\n",
|
||
" t.center_id,\n",
|
||
" o.total_cost\n",
|
||
" FROM `{PROJECT}.{DATASET}.tpv_orders_invoice` i\n",
|
||
" JOIN `{PROJECT}.{DATASET}.tpv_orders_order` o ON i.order_id = o.id\n",
|
||
" LEFT JOIN `{PROJECT}.{DATASET}.tpv_terminals` t ON o.terminal_id = t.id\n",
|
||
" WHERE i.created_at >= t_start\n",
|
||
"),\n",
|
||
"-- B: facturas de los mismos clientes en cualquier centro NO call_center\n",
|
||
"client_invoices_in_centers AS (\n",
|
||
" SELECT ai.*\n",
|
||
" FROM all_invoices ai\n",
|
||
" JOIN cc_clients cc ON ai.customer_id = cc.customer_id\n",
|
||
" WHERE ai.center_id NOT IN (159, 162) -- excluye los propios centros call_center\n",
|
||
"),\n",
|
||
"kpi_a AS (\n",
|
||
" SELECT center_id,\n",
|
||
" COUNT(DISTINCT quote_id) AS quotes_cc_convertidos,\n",
|
||
" COUNT(DISTINCT invoice_id) AS invoices_a,\n",
|
||
" SUM(total_cost) AS valor_a\n",
|
||
" FROM cc_converted\n",
|
||
" WHERE center_id IS NOT NULL\n",
|
||
" GROUP BY center_id\n",
|
||
"),\n",
|
||
"kpi_b AS (\n",
|
||
" SELECT center_id,\n",
|
||
" COUNT(DISTINCT invoice_id) AS invoices_b,\n",
|
||
" SUM(total_cost) AS valor_b\n",
|
||
" FROM client_invoices_in_centers\n",
|
||
" GROUP BY center_id\n",
|
||
"),\n",
|
||
"kpi_c AS (\n",
|
||
" SELECT center_id,\n",
|
||
" COUNT(DISTINCT invoice_id) AS invoices_c,\n",
|
||
" SUM(total_cost) AS valor_c\n",
|
||
" FROM all_invoices\n",
|
||
" WHERE center_id IS NOT NULL\n",
|
||
" GROUP BY center_id\n",
|
||
")\n",
|
||
"SELECT\n",
|
||
" c.id AS center_id,\n",
|
||
" c.name AS center_name,\n",
|
||
" COALESCE(a.quotes_cc_convertidos, 0) AS quotes_cc_convertidos,\n",
|
||
" ROUND(COALESCE(a.valor_a, 0), 2) AS A_valor_quote_cc_convertido,\n",
|
||
" ROUND(COALESCE(b.valor_b, 0), 2) AS B_valor_mismo_cliente_centro,\n",
|
||
" ROUND(COALESCE(c2.valor_c, 0), 2) AS C_valor_total_centro,\n",
|
||
" ROUND(SAFE_DIVIDE(COALESCE(a.valor_a, 0), c2.valor_c), 4) AS A_sobre_C,\n",
|
||
" ROUND(SAFE_DIVIDE(COALESCE(b.valor_b, 0), c2.valor_c), 4) AS B_sobre_C\n",
|
||
"FROM `{PROJECT}.{DATASET}.centers` c\n",
|
||
"LEFT JOIN kpi_a a ON c.id = a.center_id\n",
|
||
"LEFT JOIN kpi_b b ON c.id = b.center_id\n",
|
||
"LEFT JOIN kpi_c c2 ON c.id = c2.center_id\n",
|
||
"WHERE c.id NOT IN (159, 162)\n",
|
||
" AND COALESCE(c2.valor_c, 0) > 0\n",
|
||
"ORDER BY C_valor_total_centro DESC\n",
|
||
"\"\"\"\n",
|
||
"\n",
|
||
"df = q(SQL_KPI)\n",
|
||
"print(f\"Centros con actividad ({WINDOW_DAYS}d): {len(df)}\")\n",
|
||
"df.head(30)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "724baf5c",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Totales globales"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "75c3297e",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"totals = df[[\"A_valor_quote_cc_convertido\", \"B_valor_mismo_cliente_centro\", \"C_valor_total_centro\"]].sum()\n",
|
||
"print(totals.to_string())\n",
|
||
"print()\n",
|
||
"print(f\"A/C global: {totals.A_valor_quote_cc_convertido / totals.C_valor_total_centro:.4f}\")\n",
|
||
"print(f\"B/C global: {totals.B_valor_mismo_cliente_centro / totals.C_valor_total_centro:.4f}\")\n",
|
||
"print(f\"Lift B vs A: {totals.B_valor_mismo_cliente_centro / totals.A_valor_quote_cc_convertido:.2f}x\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "0fba60db",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Top 15 centros por A (valor traído por call_center)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "f90abe7e",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"import matplotlib.pyplot as plt\n",
|
||
"\n",
|
||
"top = df.sort_values(\"A_valor_quote_cc_convertido\", ascending=False).head(15)\n",
|
||
"fig, ax = plt.subplots(figsize=(10, 6))\n",
|
||
"x = range(len(top))\n",
|
||
"ax.barh(x, top.A_valor_quote_cc_convertido, label=\"A (cc → factura)\")\n",
|
||
"ax.barh(x, top.B_valor_mismo_cliente_centro - top.A_valor_quote_cc_convertido,\n",
|
||
" left=top.A_valor_quote_cc_convertido, label=\"B−A (mismo cliente extra)\")\n",
|
||
"ax.set_yticks(x)\n",
|
||
"ax.set_yticklabels(top.center_name)\n",
|
||
"ax.invert_yaxis()\n",
|
||
"ax.set_xlabel(\"€ facturados\")\n",
|
||
"ax.legend()\n",
|
||
"ax.set_title(f\"Top 15 centros — quotes call_center ({WINDOW_DAYS}d)\")\n",
|
||
"plt.tight_layout()\n",
|
||
"plt.show()"
|
||
]
|
||
}
|
||
],
|
||
"metadata": {
|
||
"kernelspec": {
|
||
"display_name": "Python 3 (ipykernel)",
|
||
"language": "python",
|
||
"name": "python3"
|
||
},
|
||
"language_info": {
|
||
"codemirror_mode": {
|
||
"name": "ipython",
|
||
"version": 3
|
||
},
|
||
"file_extension": ".py",
|
||
"mimetype": "text/x-python",
|
||
"name": "python",
|
||
"nbconvert_exporter": "python",
|
||
"pygments_lexer": "ipython3",
|
||
"version": "3.13.5"
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 5
|
||
}
|