Files
2026-04-30 16:03:17 +02:00

1757 lines
108 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "1b3613c7-7f4f-4b10-b35b-a8ca3de3187a",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "cf59d6f5",
"metadata": {},
"source": [
"# Productos faltantes en aurgi.com (NAV 471 vs TPV ch 1)\n",
"\n",
"Del análisis anterior sabemos que en el mapeo `sales_code=471 → channel_id=1 (aurgi.com)`:\n",
"- **18,559 productos** tienen precio en NAV 471 pero **no** en TPV aurgi.com activo.\n",
"- **43,581 productos** tienen precio activo en TPV aurgi.com pero **no** en NAV 471.\n",
"\n",
"Objetivo: caracterizar estos dos grupos y ver **de qué fechas son** los faltantes."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "f2500a4d",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'autingo-159109'"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import google.auth\n",
"from google.cloud import bigquery\n",
"import pandas as pd\n",
"\n",
"PROJECT = \"autingo-159109\"\n",
"NAV = f\"`{PROJECT}.mssql2022_dbo.sales_price`\"\n",
"TPV = f\"`{PROJECT}.psql_dcpublic.sale_prices`\"\n",
"PRODUCTS = f\"`{PROJECT}.psql_dcpublic.products`\"\n",
"\n",
"creds, _ = google.auth.default()\n",
"if hasattr(creds, \"with_quota_project\"):\n",
" creds = creds.with_quota_project(None)\n",
"bq = bigquery.Client(project=PROJECT, credentials=creds,\n",
" client_options={\"quota_project_id\": None})\n",
"\n",
"def q(sql: str) -> pd.DataFrame:\n",
" return bq.query(sql).to_dataframe()\n",
"\n",
"pd.set_option('display.max_rows', 60)\n",
"pd.set_option('display.width', 120)\n",
"bq.project"
]
},
{
"cell_type": "markdown",
"id": "db616f8e",
"metadata": {},
"source": [
"## 1 · Contamos e identificamos ambos grupos\n",
"\n",
"Una sola query nos saca los 3 conteos de referencia + flags (¿el producto está `active`?, ¿tiene `nav_id`?, ¿flag `is_nav`?)."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "e144ef8b",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/egutierrez/fn_registry/projects/aurgi/analysis/sale_prices_comprobation/.venv/lib/python3.13/site-packages/google/cloud/bigquery/table.py:2086: UserWarning: BigQuery Storage module not found, fetch data with the REST endpoint instead.\n",
" warnings.warn(\n"
]
},
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>only_nav</th>\n",
" <th>only_tpv</th>\n",
" <th>both</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>18559</td>\n",
" <td>43581</td>\n",
" <td>103075</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" only_nav only_tpv both\n",
"0 18559 43581 103075"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"q(f\"\"\"\n",
"WITH nav AS (\n",
" SELECT p.id AS product_id, sp.item_no_,\n",
" CAST(sp.unit_price AS FLOAT64) AS nav_price,\n",
" sp.starting_date AS nav_start,\n",
" sp.fecha_ultima_modificacion AS nav_last_mod\n",
" FROM {NAV} sp\n",
" JOIN {PRODUCTS} p ON p.nav_id = sp.item_no_\n",
" WHERE sp._fivetran_deleted IS NOT TRUE\n",
" AND sp.sales_type = 1 AND sp.sales_code = \"471\"\n",
" AND sp.starting_date <= CURRENT_DATETIME()\n",
" AND (sp.ending_date IS NULL\n",
" OR sp.ending_date = DATETIME \"0001-01-01 00:00:00\"\n",
" OR sp.ending_date >= CURRENT_DATETIME())\n",
" AND sp.variant_code = \"\"\n",
"),\n",
"tpv AS (\n",
" SELECT product_id, unit_price AS tpv_price,\n",
" starting_date AS tpv_start,\n",
" created_at AS tpv_created,\n",
" updated_at AS tpv_updated\n",
" FROM {TPV}\n",
" WHERE channel_id = 1 AND active = TRUE\n",
" AND starting_date <= CURRENT_DATE()\n",
" AND (ending_date IS NULL OR ending_date >= CURRENT_DATE())\n",
")\n",
"SELECT\n",
" COUNTIF(nav.product_id IS NOT NULL AND tpv.product_id IS NULL) AS only_nav,\n",
" COUNTIF(nav.product_id IS NULL AND tpv.product_id IS NOT NULL) AS only_tpv,\n",
" COUNTIF(nav.product_id IS NOT NULL AND tpv.product_id IS NOT NULL) AS both\n",
"FROM nav FULL OUTER JOIN tpv USING(product_id)\n",
"\"\"\")"
]
},
{
"cell_type": "markdown",
"id": "2c486309",
"metadata": {},
"source": [
"## 2 · Sólo NAV (18,559 productos)\n",
"\n",
"Productos con precio en NAV `sales_code=471` **sin** precio activo en TPV aurgi.com (channel_id=1).\n",
"\n",
"Fuentes de fecha que miramos:\n",
"- `NAV.sales_price.starting_date` — cuándo empezó a ser válido ese precio en NAV\n",
"- `NAV.sales_price.fecha_ultima_modificacion` — última modificación del precio en NAV\n",
"- `products.created_at` — alta del producto en TPV\n",
"- `products.updated_at` — última actualización del producto en TPV"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "d121c377",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/egutierrez/fn_registry/projects/aurgi/analysis/sale_prices_comprobation/.venv/lib/python3.13/site-packages/google/cloud/bigquery/table.py:2086: UserWarning: BigQuery Storage module not found, fetch data with the REST endpoint instead.\n",
" warnings.warn(\n"
]
},
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>year_nav_start</th>\n",
" <th>n</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>2026</td>\n",
" <td>1403</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2025</td>\n",
" <td>6109</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>2024</td>\n",
" <td>4768</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>2023</td>\n",
" <td>2622</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>2022</td>\n",
" <td>2018</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5</th>\n",
" <td>2021</td>\n",
" <td>417</td>\n",
" </tr>\n",
" <tr>\n",
" <th>6</th>\n",
" <td>2020</td>\n",
" <td>741</td>\n",
" </tr>\n",
" <tr>\n",
" <th>7</th>\n",
" <td>2019</td>\n",
" <td>43</td>\n",
" </tr>\n",
" <tr>\n",
" <th>8</th>\n",
" <td>2018</td>\n",
" <td>13</td>\n",
" </tr>\n",
" <tr>\n",
" <th>9</th>\n",
" <td>2017</td>\n",
" <td>22</td>\n",
" </tr>\n",
" <tr>\n",
" <th>10</th>\n",
" <td>2016</td>\n",
" <td>331</td>\n",
" </tr>\n",
" <tr>\n",
" <th>11</th>\n",
" <td>2015</td>\n",
" <td>4</td>\n",
" </tr>\n",
" <tr>\n",
" <th>12</th>\n",
" <td>2014</td>\n",
" <td>6</td>\n",
" </tr>\n",
" <tr>\n",
" <th>13</th>\n",
" <td>2013</td>\n",
" <td>18</td>\n",
" </tr>\n",
" <tr>\n",
" <th>14</th>\n",
" <td>2012</td>\n",
" <td>38</td>\n",
" </tr>\n",
" <tr>\n",
" <th>15</th>\n",
" <td>2010</td>\n",
" <td>3</td>\n",
" </tr>\n",
" <tr>\n",
" <th>16</th>\n",
" <td>1753</td>\n",
" <td>3</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" year_nav_start n\n",
"0 2026 1403\n",
"1 2025 6109\n",
"2 2024 4768\n",
"3 2023 2622\n",
"4 2022 2018\n",
"5 2021 417\n",
"6 2020 741\n",
"7 2019 43\n",
"8 2018 13\n",
"9 2017 22\n",
"10 2016 331\n",
"11 2015 4\n",
"12 2014 6\n",
"13 2013 18\n",
"14 2012 38\n",
"15 2010 3\n",
"16 1753 3"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"q(f\"\"\"\n",
"WITH nav AS (\n",
" SELECT p.id AS product_id, sp.starting_date AS nav_start\n",
" FROM {NAV} sp\n",
" JOIN {PRODUCTS} p ON p.nav_id = sp.item_no_\n",
" WHERE sp._fivetran_deleted IS NOT TRUE\n",
" AND sp.sales_type = 1 AND sp.sales_code = \"471\"\n",
" AND sp.starting_date <= CURRENT_DATETIME()\n",
" AND (sp.ending_date IS NULL OR sp.ending_date = DATETIME \"0001-01-01 00:00:00\"\n",
" OR sp.ending_date >= CURRENT_DATETIME())\n",
" AND sp.variant_code = \"\"\n",
"),\n",
"tpv AS (\n",
" SELECT product_id FROM {TPV}\n",
" WHERE channel_id = 1 AND active = TRUE\n",
" AND starting_date <= CURRENT_DATE()\n",
" AND (ending_date IS NULL OR ending_date >= CURRENT_DATE())\n",
")\n",
"SELECT EXTRACT(YEAR FROM nav.nav_start) AS year_nav_start, COUNT(*) AS n\n",
"FROM nav LEFT JOIN tpv USING(product_id)\n",
"WHERE tpv.product_id IS NULL\n",
"GROUP BY year_nav_start\n",
"ORDER BY year_nav_start DESC\n",
"\"\"\")"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "3f0902bb",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/egutierrez/fn_registry/projects/aurgi/analysis/sale_prices_comprobation/.venv/lib/python3.13/site-packages/google/cloud/bigquery/table.py:2086: UserWarning: BigQuery Storage module not found, fetch data with the REST endpoint instead.\n",
" warnings.warn(\n"
]
},
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>total</th>\n",
" <th>aurgi_published</th>\n",
" <th>not_aurgi_published</th>\n",
" <th>show_on_ecommerce</th>\n",
" <th>is_blocked</th>\n",
" <th>is_nav_flag</th>\n",
" <th>status_1</th>\n",
" <th>status_0</th>\n",
" <th>status_null</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>18559</td>\n",
" <td>3</td>\n",
" <td>2</td>\n",
" <td>16547</td>\n",
" <td>316</td>\n",
" <td>18559</td>\n",
" <td>29</td>\n",
" <td>9151</td>\n",
" <td>0</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" total aurgi_published not_aurgi_published show_on_ecommerce is_blocked is_nav_flag status_1 status_0 \\\n",
"0 18559 3 2 16547 316 18559 29 9151 \n",
"\n",
" status_null \n",
"0 0 "
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"q(f\"\"\"\n",
"WITH nav AS (\n",
" SELECT p.id AS product_id, sp.fecha_ultima_modificacion AS nav_mod,\n",
" p.aurgi_published, p.show_on_ecommerce, p.is_blocked, p.status, p.is_nav\n",
" FROM {NAV} sp\n",
" JOIN {PRODUCTS} p ON p.nav_id = sp.item_no_\n",
" WHERE sp._fivetran_deleted IS NOT TRUE\n",
" AND sp.sales_type = 1 AND sp.sales_code = \"471\"\n",
" AND sp.starting_date <= CURRENT_DATETIME()\n",
" AND (sp.ending_date IS NULL OR sp.ending_date = DATETIME \"0001-01-01 00:00:00\"\n",
" OR sp.ending_date >= CURRENT_DATETIME())\n",
" AND sp.variant_code = \"\"\n",
"),\n",
"tpv AS (\n",
" SELECT product_id FROM {TPV}\n",
" WHERE channel_id = 1 AND active = TRUE\n",
" AND starting_date <= CURRENT_DATE()\n",
" AND (ending_date IS NULL OR ending_date >= CURRENT_DATE())\n",
"),\n",
"only_nav AS (\n",
" SELECT nav.* FROM nav LEFT JOIN tpv USING(product_id) WHERE tpv.product_id IS NULL\n",
")\n",
"SELECT\n",
" COUNT(*) AS total,\n",
" COUNTIF(aurgi_published) AS aurgi_published,\n",
" COUNTIF(NOT aurgi_published) AS not_aurgi_published,\n",
" COUNTIF(show_on_ecommerce) AS show_on_ecommerce,\n",
" COUNTIF(is_blocked) AS is_blocked,\n",
" COUNTIF(is_nav) AS is_nav_flag,\n",
" COUNTIF(status = 1) AS status_1,\n",
" COUNTIF(status = 0) AS status_0,\n",
" COUNTIF(status IS NULL) AS status_null\n",
"FROM only_nav\n",
"\"\"\")"
]
},
{
"cell_type": "markdown",
"id": "ceed6a8f",
"metadata": {},
"source": [
"### Flags de negocio en los sólo-NAV\n",
"\n",
"- **100%** son `is_nav=TRUE` (obvio — tienen precio NAV).\n",
"- **89%** (16,547) tienen `show_on_ecommerce=TRUE` → **están marcados para mostrarse online pero no tienen precio activo en aurgi.com**.\n",
"- Sólo **5** tienen `aurgi_published` NO-NULL (el flag está mayormente vacío).\n",
"- 316 están `is_blocked=TRUE`.\n",
"\n",
"¿Tienen estos productos precio en **otro canal TPV**?"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "2009a5b9",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/egutierrez/fn_registry/projects/aurgi/analysis/sale_prices_comprobation/.venv/lib/python3.13/site-packages/google/cloud/bigquery/table.py:2086: UserWarning: BigQuery Storage module not found, fetch data with the REST endpoint instead.\n",
" warnings.warn(\n"
]
},
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>channel_id</th>\n",
" <th>channel_name</th>\n",
" <th>n_products</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>3</td>\n",
" <td>autingo.es</td>\n",
" <td>446</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>10</td>\n",
" <td>Talleres Digitales</td>\n",
" <td>262</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>11</td>\n",
" <td>Amazon</td>\n",
" <td>78</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>13</td>\n",
" <td>Miravia</td>\n",
" <td>76</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>2</td>\n",
" <td>motortown.es</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5</th>\n",
" <td>12</td>\n",
" <td>canarias</td>\n",
" <td>1</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" channel_id channel_name n_products\n",
"0 3 autingo.es 446\n",
"1 10 Talleres Digitales 262\n",
"2 11 Amazon 78\n",
"3 13 Miravia 76\n",
"4 2 motortown.es 1\n",
"5 12 canarias 1"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"q(f\"\"\"\n",
"WITH nav_only AS (\n",
" SELECT p.id AS product_id\n",
" FROM {NAV} sp JOIN {PRODUCTS} p ON p.nav_id = sp.item_no_\n",
" WHERE sp._fivetran_deleted IS NOT TRUE AND sp.sales_type = 1 AND sp.sales_code = \"471\"\n",
" AND sp.starting_date <= CURRENT_DATETIME()\n",
" AND (sp.ending_date IS NULL OR sp.ending_date = DATETIME \"0001-01-01 00:00:00\"\n",
" OR sp.ending_date >= CURRENT_DATETIME())\n",
" AND sp.variant_code = \"\"\n",
" AND p.id NOT IN (\n",
" SELECT product_id FROM {TPV}\n",
" WHERE channel_id = 1 AND active = TRUE\n",
" AND starting_date <= CURRENT_DATE()\n",
" AND (ending_date IS NULL OR ending_date >= CURRENT_DATE())\n",
" )\n",
"),\n",
"other_ch AS (\n",
" SELECT DISTINCT sp.product_id, sp.channel_id FROM {TPV} sp\n",
" WHERE sp.active = TRUE AND sp.channel_id != 1\n",
" AND sp.starting_date <= CURRENT_DATE()\n",
" AND (sp.ending_date IS NULL OR sp.ending_date >= CURRENT_DATE())\n",
")\n",
"SELECT c.id AS channel_id, c.name AS channel_name, COUNT(*) AS n_products\n",
"FROM nav_only\n",
"JOIN other_ch ON other_ch.product_id = nav_only.product_id\n",
"JOIN `autingo-159109.psql_dcpublic.channels` c ON c.id = other_ch.channel_id\n",
"GROUP BY c.id, c.name ORDER BY n_products DESC\n",
"\"\"\")"
]
},
{
"cell_type": "markdown",
"id": "d4a4cc08",
"metadata": {},
"source": [
"**Sólo 864 de 18,559 (~5%)** tienen precio activo en otro canal. Los **17,695 restantes** solo viven en NAV 471 y no tienen precio TPV en ningún sitio.\n",
"\n",
"### Alta en TPV (products.created_at) de los sólo-NAV"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "9ff1af21",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/egutierrez/fn_registry/projects/aurgi/analysis/sale_prices_comprobation/.venv/lib/python3.13/site-packages/google/cloud/bigquery/table.py:2086: UserWarning: BigQuery Storage module not found, fetch data with the REST endpoint instead.\n",
" warnings.warn(\n"
]
},
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>year_prod_created</th>\n",
" <th>n</th>\n",
" <th>show_on_ecommerce</th>\n",
" <th>hidden_from_ecommerce</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>2026</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2025</td>\n",
" <td>198</td>\n",
" <td>198</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>2024</td>\n",
" <td>1224</td>\n",
" <td>982</td>\n",
" <td>242</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>2023</td>\n",
" <td>7957</td>\n",
" <td>7395</td>\n",
" <td>562</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>2022</td>\n",
" <td>5699</td>\n",
" <td>5285</td>\n",
" <td>414</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5</th>\n",
" <td>2021</td>\n",
" <td>1301</td>\n",
" <td>1283</td>\n",
" <td>18</td>\n",
" </tr>\n",
" <tr>\n",
" <th>6</th>\n",
" <td>2020</td>\n",
" <td>818</td>\n",
" <td>638</td>\n",
" <td>180</td>\n",
" </tr>\n",
" <tr>\n",
" <th>7</th>\n",
" <td>2019</td>\n",
" <td>35</td>\n",
" <td>11</td>\n",
" <td>24</td>\n",
" </tr>\n",
" <tr>\n",
" <th>8</th>\n",
" <td>2018</td>\n",
" <td>379</td>\n",
" <td>176</td>\n",
" <td>203</td>\n",
" </tr>\n",
" <tr>\n",
" <th>9</th>\n",
" <td>2017</td>\n",
" <td>82</td>\n",
" <td>40</td>\n",
" <td>42</td>\n",
" </tr>\n",
" <tr>\n",
" <th>10</th>\n",
" <td>2016</td>\n",
" <td>323</td>\n",
" <td>157</td>\n",
" <td>166</td>\n",
" </tr>\n",
" <tr>\n",
" <th>11</th>\n",
" <td>2015</td>\n",
" <td>173</td>\n",
" <td>129</td>\n",
" <td>44</td>\n",
" </tr>\n",
" <tr>\n",
" <th>12</th>\n",
" <td>2014</td>\n",
" <td>126</td>\n",
" <td>125</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>13</th>\n",
" <td>2013</td>\n",
" <td>4</td>\n",
" <td>2</td>\n",
" <td>2</td>\n",
" </tr>\n",
" <tr>\n",
" <th>14</th>\n",
" <td>2012</td>\n",
" <td>3</td>\n",
" <td>2</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>15</th>\n",
" <td>2011</td>\n",
" <td>105</td>\n",
" <td>7</td>\n",
" <td>98</td>\n",
" </tr>\n",
" <tr>\n",
" <th>16</th>\n",
" <td>2010</td>\n",
" <td>4</td>\n",
" <td>4</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>17</th>\n",
" <td>2009</td>\n",
" <td>19</td>\n",
" <td>18</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>18</th>\n",
" <td>2008</td>\n",
" <td>2</td>\n",
" <td>2</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>19</th>\n",
" <td>2007</td>\n",
" <td>50</td>\n",
" <td>45</td>\n",
" <td>5</td>\n",
" </tr>\n",
" <tr>\n",
" <th>20</th>\n",
" <td>2006</td>\n",
" <td>18</td>\n",
" <td>17</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>21</th>\n",
" <td>2005</td>\n",
" <td>8</td>\n",
" <td>6</td>\n",
" <td>2</td>\n",
" </tr>\n",
" <tr>\n",
" <th>22</th>\n",
" <td>2004</td>\n",
" <td>5</td>\n",
" <td>5</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>23</th>\n",
" <td>2003</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>24</th>\n",
" <td>2002</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>25</th>\n",
" <td>2001</td>\n",
" <td>2</td>\n",
" <td>0</td>\n",
" <td>2</td>\n",
" </tr>\n",
" <tr>\n",
" <th>26</th>\n",
" <td>2000</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>27</th>\n",
" <td>1999</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>28</th>\n",
" <td>1998</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>29</th>\n",
" <td>1997</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>30</th>\n",
" <td>1996</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>31</th>\n",
" <td>1995</td>\n",
" <td>2</td>\n",
" <td>2</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>32</th>\n",
" <td>1994</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>33</th>\n",
" <td>1993</td>\n",
" <td>3</td>\n",
" <td>2</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>34</th>\n",
" <td>1992</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>35</th>\n",
" <td>1991</td>\n",
" <td>9</td>\n",
" <td>7</td>\n",
" <td>2</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" year_prod_created n show_on_ecommerce hidden_from_ecommerce\n",
"0 2026 1 1 0\n",
"1 2025 198 198 0\n",
"2 2024 1224 982 242\n",
"3 2023 7957 7395 562\n",
"4 2022 5699 5285 414\n",
"5 2021 1301 1283 18\n",
"6 2020 818 638 180\n",
"7 2019 35 11 24\n",
"8 2018 379 176 203\n",
"9 2017 82 40 42\n",
"10 2016 323 157 166\n",
"11 2015 173 129 44\n",
"12 2014 126 125 1\n",
"13 2013 4 2 2\n",
"14 2012 3 2 1\n",
"15 2011 105 7 98\n",
"16 2010 4 4 0\n",
"17 2009 19 18 1\n",
"18 2008 2 2 0\n",
"19 2007 50 45 5\n",
"20 2006 18 17 1\n",
"21 2005 8 6 2\n",
"22 2004 5 5 0\n",
"23 2003 1 1 0\n",
"24 2002 1 1 0\n",
"25 2001 2 0 2\n",
"26 2000 1 1 0\n",
"27 1999 1 1 0\n",
"28 1998 1 1 0\n",
"29 1997 1 0 1\n",
"30 1996 1 1 0\n",
"31 1995 2 2 0\n",
"32 1994 1 1 0\n",
"33 1993 3 2 1\n",
"34 1992 1 1 0\n",
"35 1991 9 7 2"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"q(f\"\"\"\n",
"WITH nav_only AS (\n",
" SELECT p.id AS product_id, p.created_at AS prod_created, p.show_on_ecommerce\n",
" FROM {NAV} sp JOIN {PRODUCTS} p ON p.nav_id = sp.item_no_\n",
" WHERE sp._fivetran_deleted IS NOT TRUE AND sp.sales_type = 1 AND sp.sales_code = \"471\"\n",
" AND sp.starting_date <= CURRENT_DATETIME()\n",
" AND (sp.ending_date IS NULL OR sp.ending_date = DATETIME \"0001-01-01 00:00:00\"\n",
" OR sp.ending_date >= CURRENT_DATETIME())\n",
" AND sp.variant_code = \"\"\n",
" AND p.id NOT IN (\n",
" SELECT product_id FROM {TPV} WHERE channel_id = 1 AND active = TRUE\n",
" AND starting_date <= CURRENT_DATE()\n",
" AND (ending_date IS NULL OR ending_date >= CURRENT_DATE())\n",
" )\n",
")\n",
"SELECT\n",
" EXTRACT(YEAR FROM prod_created) AS year_prod_created,\n",
" COUNT(*) AS n,\n",
" COUNTIF(show_on_ecommerce) AS show_on_ecommerce,\n",
" COUNTIF(NOT show_on_ecommerce) AS hidden_from_ecommerce\n",
"FROM nav_only\n",
"GROUP BY year_prod_created\n",
"ORDER BY year_prod_created DESC\n",
"\"\"\")"
]
},
{
"cell_type": "markdown",
"id": "735eaa32",
"metadata": {},
"source": [
"### Muestra — productos sólo-NAV más recientes"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "853bea0a",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/egutierrez/fn_registry/projects/aurgi/analysis/sale_prices_comprobation/.venv/lib/python3.13/site-packages/google/cloud/bigquery/table.py:2086: UserWarning: BigQuery Storage module not found, fetch data with the REST endpoint instead.\n",
" warnings.warn(\n"
]
},
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>id</th>\n",
" <th>nav_id</th>\n",
" <th>normalized_code</th>\n",
" <th>nav_price</th>\n",
" <th>nav_start</th>\n",
" <th>nav_mod</th>\n",
" <th>prod_created</th>\n",
" <th>show_on_ecommerce</th>\n",
" <th>is_blocked</th>\n",
" <th>status</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>667307346</td>\n",
" <td>TD667307346</td>\n",
" <td>NaN</td>\n",
" <td>9.43</td>\n",
" <td>2025-05-04</td>\n",
" <td>2026-04-12</td>\n",
" <td>2023-05-22</td>\n",
" <td>True</td>\n",
" <td>&lt;NA&gt;</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>847358019</td>\n",
" <td>TD847358019</td>\n",
" <td>NaN</td>\n",
" <td>8.24</td>\n",
" <td>2024-07-05</td>\n",
" <td>2026-04-12</td>\n",
" <td>2023-09-08</td>\n",
" <td>True</td>\n",
" <td>&lt;NA&gt;</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>667308473</td>\n",
" <td>TD667308473</td>\n",
" <td>NaN</td>\n",
" <td>7.83</td>\n",
" <td>2024-09-26</td>\n",
" <td>2026-04-12</td>\n",
" <td>2023-05-22</td>\n",
" <td>True</td>\n",
" <td>&lt;NA&gt;</td>\n",
" <td>3</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>667381699</td>\n",
" <td>TD667381699</td>\n",
" <td>NaN</td>\n",
" <td>6.33</td>\n",
" <td>2025-04-08</td>\n",
" <td>2026-04-12</td>\n",
" <td>2023-05-22</td>\n",
" <td>True</td>\n",
" <td>&lt;NA&gt;</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>667311808</td>\n",
" <td>TD667311808</td>\n",
" <td>NaN</td>\n",
" <td>6.92</td>\n",
" <td>2025-07-02</td>\n",
" <td>2026-04-12</td>\n",
" <td>2023-05-22</td>\n",
" <td>True</td>\n",
" <td>&lt;NA&gt;</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5</th>\n",
" <td>667378510</td>\n",
" <td>TD667378510</td>\n",
" <td>NaN</td>\n",
" <td>10.42</td>\n",
" <td>2024-07-12</td>\n",
" <td>2026-04-12</td>\n",
" <td>2023-05-22</td>\n",
" <td>True</td>\n",
" <td>&lt;NA&gt;</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>6</th>\n",
" <td>667312553</td>\n",
" <td>TD667312553</td>\n",
" <td>NaN</td>\n",
" <td>4.00</td>\n",
" <td>2025-01-29</td>\n",
" <td>2026-04-12</td>\n",
" <td>2023-05-22</td>\n",
" <td>True</td>\n",
" <td>&lt;NA&gt;</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>7</th>\n",
" <td>667304440</td>\n",
" <td>TD667304440</td>\n",
" <td>NaN</td>\n",
" <td>8.69</td>\n",
" <td>2024-08-14</td>\n",
" <td>2026-04-12</td>\n",
" <td>2023-05-22</td>\n",
" <td>True</td>\n",
" <td>&lt;NA&gt;</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>8</th>\n",
" <td>667285678</td>\n",
" <td>TD667285678</td>\n",
" <td>NaN</td>\n",
" <td>32.51</td>\n",
" <td>2026-04-13</td>\n",
" <td>2026-04-12</td>\n",
" <td>2023-05-22</td>\n",
" <td>True</td>\n",
" <td>&lt;NA&gt;</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>9</th>\n",
" <td>667303072</td>\n",
" <td>TD667303072</td>\n",
" <td>NaN</td>\n",
" <td>8.57</td>\n",
" <td>2026-04-13</td>\n",
" <td>2026-04-12</td>\n",
" <td>2023-05-22</td>\n",
" <td>True</td>\n",
" <td>&lt;NA&gt;</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>10</th>\n",
" <td>1394846795</td>\n",
" <td>MKT1394846795</td>\n",
" <td>NaN</td>\n",
" <td>12.62</td>\n",
" <td>2026-04-13</td>\n",
" <td>2026-04-12</td>\n",
" <td>2024-12-05</td>\n",
" <td>True</td>\n",
" <td>&lt;NA&gt;</td>\n",
" <td>3</td>\n",
" </tr>\n",
" <tr>\n",
" <th>11</th>\n",
" <td>667307347</td>\n",
" <td>TD667307347</td>\n",
" <td>NaN</td>\n",
" <td>8.34</td>\n",
" <td>2024-08-14</td>\n",
" <td>2026-04-12</td>\n",
" <td>2023-05-22</td>\n",
" <td>True</td>\n",
" <td>&lt;NA&gt;</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>12</th>\n",
" <td>667304514</td>\n",
" <td>TD667304514</td>\n",
" <td>NaN</td>\n",
" <td>12.06</td>\n",
" <td>2024-09-24</td>\n",
" <td>2026-04-12</td>\n",
" <td>2023-05-22</td>\n",
" <td>True</td>\n",
" <td>&lt;NA&gt;</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>13</th>\n",
" <td>667312564</td>\n",
" <td>TD667312564</td>\n",
" <td>NaN</td>\n",
" <td>9.72</td>\n",
" <td>2025-03-15</td>\n",
" <td>2026-04-12</td>\n",
" <td>2023-05-22</td>\n",
" <td>True</td>\n",
" <td>&lt;NA&gt;</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>14</th>\n",
" <td>667311844</td>\n",
" <td>TD667311844</td>\n",
" <td>NaN</td>\n",
" <td>5.87</td>\n",
" <td>2025-12-17</td>\n",
" <td>2026-04-12</td>\n",
" <td>2023-05-22</td>\n",
" <td>True</td>\n",
" <td>&lt;NA&gt;</td>\n",
" <td>0</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" id nav_id normalized_code nav_price nav_start nav_mod prod_created show_on_ecommerce \\\n",
"0 667307346 TD667307346 NaN 9.43 2025-05-04 2026-04-12 2023-05-22 True \n",
"1 847358019 TD847358019 NaN 8.24 2024-07-05 2026-04-12 2023-09-08 True \n",
"2 667308473 TD667308473 NaN 7.83 2024-09-26 2026-04-12 2023-05-22 True \n",
"3 667381699 TD667381699 NaN 6.33 2025-04-08 2026-04-12 2023-05-22 True \n",
"4 667311808 TD667311808 NaN 6.92 2025-07-02 2026-04-12 2023-05-22 True \n",
"5 667378510 TD667378510 NaN 10.42 2024-07-12 2026-04-12 2023-05-22 True \n",
"6 667312553 TD667312553 NaN 4.00 2025-01-29 2026-04-12 2023-05-22 True \n",
"7 667304440 TD667304440 NaN 8.69 2024-08-14 2026-04-12 2023-05-22 True \n",
"8 667285678 TD667285678 NaN 32.51 2026-04-13 2026-04-12 2023-05-22 True \n",
"9 667303072 TD667303072 NaN 8.57 2026-04-13 2026-04-12 2023-05-22 True \n",
"10 1394846795 MKT1394846795 NaN 12.62 2026-04-13 2026-04-12 2024-12-05 True \n",
"11 667307347 TD667307347 NaN 8.34 2024-08-14 2026-04-12 2023-05-22 True \n",
"12 667304514 TD667304514 NaN 12.06 2024-09-24 2026-04-12 2023-05-22 True \n",
"13 667312564 TD667312564 NaN 9.72 2025-03-15 2026-04-12 2023-05-22 True \n",
"14 667311844 TD667311844 NaN 5.87 2025-12-17 2026-04-12 2023-05-22 True \n",
"\n",
" is_blocked status \n",
"0 <NA> 0 \n",
"1 <NA> 0 \n",
"2 <NA> 3 \n",
"3 <NA> 0 \n",
"4 <NA> 0 \n",
"5 <NA> 0 \n",
"6 <NA> 0 \n",
"7 <NA> 0 \n",
"8 <NA> 0 \n",
"9 <NA> 0 \n",
"10 <NA> 3 \n",
"11 <NA> 0 \n",
"12 <NA> 0 \n",
"13 <NA> 0 \n",
"14 <NA> 0 "
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"q(f\"\"\"\n",
"WITH nav_only AS (\n",
" SELECT p.id, p.nav_id, p.normalized_ref, p.normalized_code, p.status,\n",
" p.show_on_ecommerce, p.is_blocked, p.created_at,\n",
" CAST(sp.unit_price AS FLOAT64) AS nav_price,\n",
" sp.starting_date AS nav_start, sp.fecha_ultima_modificacion AS nav_mod\n",
" FROM {NAV} sp JOIN {PRODUCTS} p ON p.nav_id = sp.item_no_\n",
" WHERE sp._fivetran_deleted IS NOT TRUE AND sp.sales_type = 1 AND sp.sales_code = \"471\"\n",
" AND sp.starting_date <= CURRENT_DATETIME()\n",
" AND (sp.ending_date IS NULL OR sp.ending_date = DATETIME \"0001-01-01 00:00:00\"\n",
" OR sp.ending_date >= CURRENT_DATETIME())\n",
" AND sp.variant_code = \"\"\n",
" AND p.id NOT IN (\n",
" SELECT product_id FROM {TPV} WHERE channel_id = 1 AND active = TRUE\n",
" AND starting_date <= CURRENT_DATE()\n",
" AND (ending_date IS NULL OR ending_date >= CURRENT_DATE())\n",
" )\n",
")\n",
"SELECT id, nav_id, normalized_code, nav_price,\n",
" DATE(nav_start) AS nav_start, DATE(nav_mod) AS nav_mod,\n",
" DATE(created_at) AS prod_created,\n",
" show_on_ecommerce, is_blocked, status\n",
"FROM nav_only\n",
"ORDER BY nav_mod DESC LIMIT 15\n",
"\"\"\")"
]
},
{
"cell_type": "markdown",
"id": "130407d4",
"metadata": {},
"source": [
"---\n",
"\n",
"## 3 · Sólo TPV aurgi.com (43,581 productos)\n",
"\n",
"Productos con precio activo en TPV `channel_id=1` **sin** precio NAV 471.\n",
"\n",
"Dos sub-grupos posibles:\n",
"- **Sin `nav_id`**: productos nacidos en TPV (e-commerce propio)\n",
"- **Con `nav_id`** pero sin fila en NAV 471: productos NAV que simplemente no están en la lista 471"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "aef0ac6f",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/egutierrez/fn_registry/projects/aurgi/analysis/sale_prices_comprobation/.venv/lib/python3.13/site-packages/google/cloud/bigquery/table.py:2086: UserWarning: BigQuery Storage module not found, fetch data with the REST endpoint instead.\n",
" warnings.warn(\n"
]
},
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>total</th>\n",
" <th>no_nav_id</th>\n",
" <th>with_nav_id</th>\n",
" <th>is_nav_flag</th>\n",
" <th>aurgi_published</th>\n",
" <th>show_on_ecommerce</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>43581</td>\n",
" <td>0</td>\n",
" <td>43581</td>\n",
" <td>56</td>\n",
" <td>0</td>\n",
" <td>41975</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" total no_nav_id with_nav_id is_nav_flag aurgi_published show_on_ecommerce\n",
"0 43581 0 43581 56 0 41975"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"q(f\"\"\"\n",
"WITH nav AS (\n",
" SELECT p.id AS product_id FROM {NAV} sp\n",
" JOIN {PRODUCTS} p ON p.nav_id = sp.item_no_\n",
" WHERE sp._fivetran_deleted IS NOT TRUE AND sp.sales_type = 1 AND sp.sales_code = \"471\"\n",
" AND sp.starting_date <= CURRENT_DATETIME()\n",
" AND (sp.ending_date IS NULL OR sp.ending_date = DATETIME \"0001-01-01 00:00:00\"\n",
" OR sp.ending_date >= CURRENT_DATETIME())\n",
" AND sp.variant_code = \"\"\n",
"),\n",
"tpv_only AS (\n",
" SELECT sp.product_id, sp.starting_date AS tpv_start, sp.created_at AS tpv_created,\n",
" p.nav_id, p.is_nav, p.aurgi_published, p.show_on_ecommerce,\n",
" p.created_at AS prod_created\n",
" FROM {TPV} sp JOIN {PRODUCTS} p ON p.id = sp.product_id\n",
" WHERE sp.channel_id = 1 AND sp.active = TRUE\n",
" AND sp.starting_date <= CURRENT_DATE()\n",
" AND (sp.ending_date IS NULL OR sp.ending_date >= CURRENT_DATE())\n",
" AND sp.product_id NOT IN (SELECT product_id FROM nav)\n",
")\n",
"SELECT\n",
" COUNT(*) AS total,\n",
" COUNTIF(nav_id IS NULL OR nav_id = \"\") AS no_nav_id,\n",
" COUNTIF(nav_id IS NOT NULL AND nav_id != \"\") AS with_nav_id,\n",
" COUNTIF(is_nav) AS is_nav_flag,\n",
" COUNTIF(aurgi_published) AS aurgi_published,\n",
" COUNTIF(show_on_ecommerce) AS show_on_ecommerce\n",
"FROM tpv_only\n",
"\"\"\")"
]
},
{
"cell_type": "markdown",
"id": "98e6e743",
"metadata": {},
"source": [
"Los 43,581 sólo-TPV aurgi.com son todos productos **con `nav_id`** (0 sin él): el catálogo dice que vienen de NAV pero NAV no les ha puesto precio en la lista 471. 41,975 están `show_on_ecommerce=TRUE`.\n",
"\n",
"---\n",
"\n",
"## 4 · Gráfico: ¿dónde faltan precios por canal?\n",
"\n",
"Para cada mapeo conocido (471→1 aurgi, 472→2 motortown, 478→12 canarias) calculamos `only_nav`, `only_tpv` y `both` y los pintamos."
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "f7af6429",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/egutierrez/fn_registry/projects/aurgi/analysis/sale_prices_comprobation/.venv/lib/python3.13/site-packages/google/cloud/bigquery/table.py:2086: UserWarning: BigQuery Storage module not found, fetch data with the REST endpoint instead.\n",
" warnings.warn(\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/egutierrez/fn_registry/projects/aurgi/analysis/sale_prices_comprobation/.venv/lib/python3.13/site-packages/google/cloud/bigquery/table.py:2086: UserWarning: BigQuery Storage module not found, fetch data with the REST endpoint instead.\n",
" warnings.warn(\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/egutierrez/fn_registry/projects/aurgi/analysis/sale_prices_comprobation/.venv/lib/python3.13/site-packages/google/cloud/bigquery/table.py:2086: UserWarning: BigQuery Storage module not found, fetch data with the REST endpoint instead.\n",
" warnings.warn(\n"
]
},
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>mapeo</th>\n",
" <th>only_nav</th>\n",
" <th>only_tpv</th>\n",
" <th>both</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>NAV 471\\n→ TPV aurgi.com</td>\n",
" <td>18559</td>\n",
" <td>43581</td>\n",
" <td>103075</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>NAV 472\\n→ TPV motortown.es</td>\n",
" <td>5585</td>\n",
" <td>11</td>\n",
" <td>57059</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>NAV 478\\n→ TPV canarias</td>\n",
" <td>4722</td>\n",
" <td>9</td>\n",
" <td>41717</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" mapeo only_nav only_tpv both\n",
"0 NAV 471\\n→ TPV aurgi.com 18559 43581 103075\n",
"1 NAV 472\\n→ TPV motortown.es 5585 11 57059\n",
"2 NAV 478\\n→ TPV canarias 4722 9 41717"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def counts(sales_code: str, channel_id: int) -> dict:\n",
" sql = f\"\"\"\n",
" WITH nav AS (\n",
" SELECT p.id AS product_id FROM {NAV} sp JOIN {PRODUCTS} p ON p.nav_id = sp.item_no_\n",
" WHERE sp._fivetran_deleted IS NOT TRUE AND sp.sales_type = 1 AND sp.sales_code = \"{sales_code}\"\n",
" AND sp.starting_date <= CURRENT_DATETIME()\n",
" AND (sp.ending_date IS NULL OR sp.ending_date = DATETIME \"0001-01-01 00:00:00\"\n",
" OR sp.ending_date >= CURRENT_DATETIME())\n",
" AND sp.variant_code = \"\"\n",
" ),\n",
" tpv AS (\n",
" SELECT product_id FROM {TPV} WHERE channel_id = {channel_id} AND active = TRUE\n",
" AND starting_date <= CURRENT_DATE()\n",
" AND (ending_date IS NULL OR ending_date >= CURRENT_DATE())\n",
" )\n",
" SELECT\n",
" COUNTIF(nav.product_id IS NOT NULL AND tpv.product_id IS NULL) AS only_nav,\n",
" COUNTIF(nav.product_id IS NULL AND tpv.product_id IS NOT NULL) AS only_tpv,\n",
" COUNTIF(nav.product_id IS NOT NULL AND tpv.product_id IS NOT NULL) AS both\n",
" FROM nav FULL OUTER JOIN tpv USING(product_id)\n",
" \"\"\"\n",
" r = q(sql).iloc[0]\n",
" return {\"only_nav\": int(r.only_nav), \"only_tpv\": int(r.only_tpv), \"both\": int(r.both)}\n",
"\n",
"mappings = [(\"471\", 1, \"aurgi.com\"), (\"472\", 2, \"motortown.es\"), (\"478\", 12, \"canarias\")]\n",
"gap = pd.DataFrame([\n",
" {\"mapeo\": f\"NAV {sc}\\n→ TPV {name}\", **counts(sc, ch)} for sc, ch, name in mappings\n",
"])\n",
"gap"
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "1f00072d",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAA90AAAHqCAYAAAAZLi26AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAiThJREFUeJzs3Xd4FFX7//HPpjdSgJAQEhOa9M4DAtI0EIpABB56j4BKERCVLiKKoDQFwRIIKl2q6INSpAiIiESqURDpoQhJCJC+vz/4Zb6sSSDBLCHh/bquvWDPuefMPRsYuHdmzjGZzWazAAAAAABArrPJ6wQAAAAAACioKLoBAAAAALASim4AAAAAAKyEohsAAAAAACuh6AYAAAAAwEoougEAAAAAsBKKbgAAAAAArISiGwAAAAAAK6HoBgAAAADASii6AQAFjslk0sSJE/M6DQAAAIpuAEDuioiIkMlkMl5OTk56/PHHNXjwYF28eDGv08sVH374oSIiIvI6DTxgQUFBMplMGjJkSIa+bdu2yWQy6csvv8x02w8//FAmk0l169a1aF+9erVMJpM+/fTTLPe7adMmmUwmvf/++/eV9z//Tmb1CgoKkiRNnDjRot3FxUUVK1bUuHHjFBcXJ0lq27atXFxcdP369Sz32717dzk4OOjvv/++r7wBoKCwy+sEAAAF06RJk1SyZEklJCTohx9+0Lx58/TNN9/o8OHDcnFxyev0/pUPP/xQRYsWVZ8+ffI6FeSBTz75RKNHj5afn1+2t1m8eLGCgoL0008/6fjx4ypTpowkqXXr1vLw8NCSJUv03HPPZbrtkiVLZGtrqy5dutxXvo0aNdLnn39u0fbcc8+pTp06GjBggNHm5uZmETNv3jy5ubkpPj5e3333nd566y1t3bpVu3btUvfu3fXVV19pzZo16tWrV4Z93rx5U+vWrVOLFi1UpEiR+8obAAoKim4AgFW0bNlStWvXlnT7P/hFihTRjBkztG7dOnXt2jXTbW7cuCFXV9cHmSYywc8ha5UqVVJUVJTeeeedbF95PnnypHbv3q3Vq1dr4MCBWrx4sV5//XVJkqOjozp27KiFCxfq/PnzGQr5hIQErVmzRs2aNVOxYsXuK+dSpUqpVKlSFm3PP/+8SpUqpR49emS5XceOHVW0aFEjvkOHDlq9erV+/PFHtW3bVoUKFdKSJUsyLbrXrVunGzduqHv37veVMwAUJNxeDgB4IJ566ilJtwsQSerTp4/c3Nx04sQJtWrVSoUKFTL+g37jxg29/PLLCggIkKOjo8qVK6f33ntPZrPZYszExEQNHz5c3t7eKlSokNq2bauzZ89m2HefPn2MW2fvlH4b7T998cUXqlOnjlxcXOTl5aVGjRrpu+++k3T7FuMjR45o+/btxu23TZo0Mbb9888/9d///leFCxeWi4uLnnjiCX399dcZ9vHBBx+oUqVKxj5q166tJUuW3PUzTL+Fefny5RozZox8fX3l6uqqtm3b6syZMxniV65cqVq1asnZ2VlFixZVjx49dO7cuQyfTVY/h8ykf2a///67evToIQ8PD3l7e2v8+PEym806c+aM2rVrJ3d3d/n6+mr69OkW2yclJWnChAmqVauWPDw85OrqqoYNG+r777+3iPvrr79kMpn03nvvaebMmQoMDJSzs7MaN26sw4cPZ8jrt99+U8eOHVW4cGE5OTmpdu3aWr9+fYa47P58shIUFKRevXrpk08+0fnz57O1zeLFi+Xl5aXWrVurY8eOWrx4sUV/jx49lJaWpmXLlmXY9uuvv1ZsbOxDUbze+XfY2dlZ7du315YtW3Tp0qUMsUuWLDH+TgLAo46iGwDwQJw4cUKSLG41TUlJUUhIiIoVK6b33ntPHTp0kNlsVtu2bTVz5ky1aNFCM2bMULly5fTKK69oxIgRFmM+99xzmjVrlpo3b6533nlH9vb2at269b/K84033lDPnj1lb2+vSZMm6Y033lBAQIC2bt0qSZo1a5b8/f1Vvnx5ff755/r88881duxYSdLFixdVv359ffvtt3rxxRf11ltvKSEhQW3bttWaNWuMfXzyyScaOnSoKlasqFmzZumNN95Q9erVtXfv3mzl+NZbb+nrr7/Wa6+9pqFDh2rTpk0KDg7WrVu3jJiIiAh16tRJtra2mjJlivr376/Vq1frySefVExMjMV4mf0c7qVz585KS0vTO++8o7p162ry5MmaNWuWmjVrphIlSmjq1KkqU6aMRo4cqR07dhjbxcXF6dNPP1WTJk00depUTZw4UZcvX1ZISIgiIyMz7Oezzz7T+++/r0GDBmn06NE6fPiwnnrqKYv5AY4cOaInnnhCx44d06hRozR9+nS5uroqNDTU4nPP7s/nXsaOHauUlBS988472YpfvHix2rdvLwcHB3Xt2lV//PGH9u3bZ/Q3atRI/v7+mX7psmTJErm4uCg0NNSiPSUlRT169MjwZUVCQoI6duyon3/+OdvHk13//DvcvXt3paSkaMWKFRZxV69e1bfffqtnn31Wzs7OuZ4HAOQ7ZgAActHChQvNksybN282X7582XzmzBnzsmXLzEWKFDE7Ozubz549azabzebevXubJZlHjRplsf3atWvNksyTJ0+2aO/YsaPZZDKZjx8/bjabzebIyEizJPOLL75oEdetWzezJPPrr79utPXu3dscGBiYIdfXX3/dfOc/hX/88YfZxsbG/Oyzz5pTU1MtYtPS0ozfV6pUydy4ceMM4w0bNswsybxz506j7fr16+aSJUuag4KCjDHbtWtnrlSpUobt7+X77783SzKXKFHCHBcXZ7SvWLHCLMk8e/Zss9lsNiclJZmLFStmrly5svnWrVtG3IYNG8ySzBMmTDDasvo5ZCX9MxswYIDRlpKSYvb39zebTCbzO++8Y7Rfu3bN7OzsbO7du7dFbGJiosWY165dM/v4+Jj79etntJ08edIsyeLPjNlsNu/du9csyTx8+HCj7emnnzZXqVLFnJCQYLSlpaWZ69evby5btqzRlt2fT1YCAwPNrVu3NpvNZnPfvn3NTk5O5vPnz5vN5v/72axcudJim59//tksybxp0yYjL39/f/NLL71kEffKK6+YJZmjoqKMttjYWLOTk5O5a9euFrEpKSnmTp06me3t7c1fffWVRd/169fN9erVM3t5eZkPHDiQ5bG4urpa/FzulP4zjoqKMl++fNl88uRJ80cffWR2dHQ0+/j4mG/cuGHkUbx4cXO9evUstp8/f75Zkvnbb7/Ncv8A8CjhSjcAwCqCg4Pl7e2tgIAAdenSRW5ublqzZo1KlChhEffCCy9YvP/mm29ka2uroUOHWrS//PLLMpvN+t///mfEScoQN2zYsPvOee3atUpLS9OECRNkY2P5T2Rmt6H/0zfffKM6deroySefNNrc3Nw0YMAA/fXXXzp69KgkydPTU2fPnrW42pkTvXr1UqFChYz3HTt2VPHixY3P5Oeff9alS5f04osvysnJyYhr3bq1ypcvn+nt1P/8OdzLnZN+2draqnbt2jKbzQoLCzPaPT09Va5cOf35558WsQ4ODpKktLQ0Xb16VSkpKapdu7Z+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VJUqVbLcJjk5OctnuqtWrarz588bkxpkNV5cXJxWrlyZrWLaxobvoAAAAHKq0oJZD3R/R/oNy1F8nz59tGjRIuN94cKF9Z///EfTpk1T1apVczROTEyMxeo8/8arr76qcePGWUzOdevWLb3zzjtaunSpTp06pUKFCqlp06aaOHGiKlWqZMRNnDhRa9eutSj2d+7cqTZt2qhPnz6aOXOmRo4cqVKlSmn48OEqVapUruSM+0OVgVyVkpKihIQEpaSkKC0tTQkJCUpKSpKLi4s6d+6s8ePHKzY2Vn/88Yc++OADPffcc5KkU6dOadWqVYqPj1daWpp2796t999/XyEhIcbYTZo00cSJEyVJpUuXVoMGDTRmzBjdvHlTP/30kxYvXpzhue2lS5eqSJEiat68uUX76dOntWPHDiUmJio5OVkrVqzQunXrmMkcAACgAGrRooUuXLigCxcuaMuWLbKzs9MzzzyTZ/n88MMPOnHihDp06GC0JSYmKjg4WAsWLNDkyZP1+++/65tvvlFKSorq1q2rH3/8Mcvxvv76a4WEhGjEiBGaNWuWTCaTihYtqpCQEM2bN+9BHBLugqIbuWry5MlydnbWW2+9pa+++krOzs5GwTtnzhx5eHjI399fDRo0UFhYmMVz1rNmzZK/v788PT3Vr18/DRkyRKNGjTL6T58+rQYNGhjvly5dqnPnzsnb21sdOnTQtGnT1LhxY4t8wsPD1bdv3wxXsePj4zV06FAVKVJE3t7eeu+997RixQo98cQT1vhYAAAAkIccHR3l6+srX19fVa9eXaNGjdKZM2d0+fJlI+bQoUN66qmn5OzsrCJFimjAgAHGErQTJ07UokWLtG7dOplMJplMJm3bts3Y9s8//1TTpk3l4uKiatWqac+ePXfNZ9myZWrWrJmcnJyMtlmzZmnPnj3asGGDOnXqpMDAQNWpU0erVq1ShQoVFBYWJrPZnGGsJUuWqH379po2bZomTJhg0demTRstW7bsfj4y5CJuL0eumjhxonE1+p/c3d21dOnSTPsCAwO1c+fOLMc9deqUfHx81KxZM6OtRIkS+t///nfXfH766adM2ytWrHhfz94AAAAgf4uPj9cXX3yhMmXKGOuN37hxQyEhIapXr5727dunS5cu6bnnntPgwYMVERGhkSNH6tixY4qLi9PChQsl3b5N/fz585KksWPH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qn79+nJzc5OXl5d+/fVXLV++XD4+PpKkZs2ayd/f3ygmt2zZotOnT6tv3753HfPWrVvGreV3Ex8fr5EjR6pChQry9PSUm5ubjh07lq0r1Lk5zmuvvabLly9neou4dPs28ZCQEOMZ9latWik2NlZbt26VJNna2qp3796KiIiQ2WxWWlqaFi1apL59+2b4csLZ2Vk3b97M0fHlFEU3AAAAgALNyclJzZo10/jx47V792716dNHr7/+el6nlanly5fr119/1bVr13TixAm1atXK6LOxsVGfPn20aNEipaWlaeHChWratKlKlSp11zGLFi2qa9eu3XPfI0eO1Jo1a/T2229r586dioyMVJUqVZSUlJSjY/i343h6emr06NF64403MhTEqampWrRokb7++mvZ2dnJzs5OLi4uunr1qkWR3q9fP50+fVpbt27Vli1bdObMmUy/nLh69apxhd5amL0cAAAAwCOlYsWKxjJXFSpU0JkzZ3TmzBnjavfRo0cVExOjihUrZtjW3d1dfn5+2rVrlxo3bmy079q1y7gN/N8ICAhQ6dKls+zv27evJk+erNWrV2vNmjX69NNP7zlmjRo1dPTo0XvG7dq1S3369NGzzz4r6fYV67/++uuu29jb21us/X2/4/zTkCFD9P7772v27NkW7d98842uX7+uAwcOGMufSdLhw4fVt29fxcTEyNPTU6VLl1bjxo21YMECmc1mBQcHKzAwMMN+Dh8+rBo1auQot5ziSjcAAACAAunvv//WU089pS+++EIHDx7UyZMntXLlSk2bNk3t2rWTJAUHB6tKlSrq3r27fvnlF/3000/q1auXGjdubDH52J1eeeUVTZ06VcuXL1dUVJRGjRqlyMhIvfTSS1Y/ppIlS+qpp57SgAED5OjoqPbt299zm5CQEP3www/3jCtbtqxWr16tyMhI/frrr+rWrZvS0tLuuk1QUJC2bNmi6Oho42r6/YzzT05OTnrjjTf0/vvvW7SHh4erdevWqlatmipXrmy8OnXqJE9PTy1evNiIDQsLM76cyGp29507d951ErrcQNENAAAAoEByc3NT3bp1NXPmTDVq1EiVK1fW+PHj1b9/f82ZM0eSZDKZtG7dOnl5ealRo0YKDg5WqVKltHz58izHHTp0qEaMGKGXX35ZVapU0caNG7V+/XqVLVv2gRxXWFiYrl27pm7d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1Ut+fn7GM+gVKlRQixYt1L9/f/3000/atWuXBg8erC5duhhXObp16yYHBweFhYXpyJEjWr58uWbPnm1xa/hLL72kjRs3avr06frtt980ceJE/fzzzxo8eHCuHjMAIOdq1qwpb29v2dra6oknntCoUaO0fPnyDHErV66Ui4uLWrduneVY6XOW3HlVO/33hQoVuuf+7O3ttW7dOh04cEAlSpRQ9+7d1bdvXxUpUiTXjhcAAOQfOS66HR0dM9xmLUkXLlyQnV22HxHPlv/85z9as2aNli5dqsqVK+vNN9/UrFmz1L17dyPm1Vdf1ZAhQzRgwAD95z//UXx8vDZu3CgnJycjZvHixSpfvryefvpptWrVSk8++aTFGtweHh767rvvdPLkSdWqVUsvv/yyJkyYYLGWd/369Y2iv1q1avryyy+1du1aVa5cOVePGQDw79nYZP7P26effqrevXvf9d8rLy8v+fv7WzxKFRkZqYCAAHl4eGRrf5UqVdJ3332nK1euKDIyUomJiWrcuHHODwQAAOR7OV6nu2vXrrpw4YLWrVtn/OcjJiZGoaGhKlasmFasWGGVRAsC1uku+FinG8gbK1asUIsWLVSoUCHt379fHTt21KBBg/TKK68YMVFRUapQoYKioqJUtmxZi+0nTpyobdu2adu2bZKkCRMmaMOGDfrmm28kSa1atVJoaKgxkdq99nfw4EGVLl1a9vb22rBhgwYOHKgtW7bcdZkyAACQv2S3vsvxpen33ntPjRo1UmBgoGrUqCHp9hUAHx8fff755/efMQAA92nOnDkaMGCAUlJSVKJECb344ot6+eWXLWLCw8PVsGHDDAW3JJ0+fVoNGjQw3o8fP15///23KlSoIEnq0aOHxowZk+39rVixQvPmzVNCQoKqVaumtWvXUnADAPCIyvGVbun2RDSLFy/Wr7/+KmdnZ1WtWlVdu3aVvb29NXIsMLjSXfBxpRvIn6pUqaJt27bx3DUAAMg2q13pliRXV1eL550BAMjPDh06lNcpAACAAirHRfdnn3121/5evXrddzIAAAAAABQkOS66X3rpJYv3ycnJunnzphwcHOTi4kLRDQDINh5puX88zgIAQP6Q4yXDrl27ZvGKj49XVFSUnnzySS1dutQaOQIAAAAAkC/luOjOTNmyZfXOO+9kuAoOAAAAAMCjLFeKbkmys7PT+fPn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vXr1JN1+VvWtt97SpUuXVKxYMUnSpk2b5O7urooVKxox33zzjcXYmzZtMsZwcHBQrVq1tGXLFoWGhkq6fbv7li1bNHjw4Czzd3R0lKOj47//IPBImjhxoiZOnJhpX4MGDbJcJzsxMVHnzp0zrnR7e3tr7969We7nXv2StHDhQi1cuDBbeQMAAAD4Pw910T1o0CAtWbJE69atU6FChYxnsD08POTs7CwPDw+FhYVpxIgRKly4sNzd3TVkyBDVq1dPTzzxhCSpefPmqlixonr27Klp06YpOjpa48aN06BBg4yC+Pnnn9ecOXP06quvql+/ftq6datWrFihr7/+2shlxIgR6t27t2rXrq06depo1qxZunHjhvr27fvgPxjgLhwdHTOsZw8AAAAgbzzURfe8efMkSU2aNLFoX7hwoXEVb+bMmbKxsVGHDh2UmJiokJAQffjhh0asra2tNmzYoBdeeEH16tWTq6urevfurUmTJhkxJUuW1Ndff63hw4dr9uzZ8vf316effqqQkBAjpnPnzrp8+bImTJig6OhoVa9eXRs3bswwuRoAAAAAAOlMZrPZnNdJPCri4uLk4eGh2NhYubu753U6maq0YFZep5CvbTm4I69TyLcK1fLM6xTyNdeeC/I6hfvCOef+Hek3LK9TAPIVzjf3j/MNkLns1nf5YiI1AAAAAADyI4puAAAAAACshKIbAAAAAAAroegGAAAAAMBKKLoBAAAAALASim4AAAAAAKyEohsAAAAAACuh6AYAAAAAwEoougEAAAAAsBKKbgAAAAAArISiGwAAAAAAK6HoBgAAAADASii6AQAAAACwEopuAAAAAACshKIbAAAAAAAroegGAAAAAMBKKLoBAAAAALASim4AAAAAAKyEohsAAAAAACuh6AYAAAAAwEoougEAAAAAsBKKbgAAAAAArISiGwAAAAAAK6HoBgAAAADASii6AQAAAACwEopuAAAAAACshKIbAAAAAAAroegGAAAAAMBKKLoBAAAAALASim4AAAAAAKyEohsAAAAAACuh6AYAAAAAwEoougEAAAAAsBKKbgAAAAAArISiGwAAAAAAK6HoBgAAAADASii6AQCAhT59+sjBwUFubm7Ga8+ePfcdf+7cOYWGhqpIkSIqWrSoOnXqpMuXL9/3/gAAyE8ougEAQAYvvvii4uPjjVe9evXuO37Q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",
"text/plain": [
"<Figure size 1000x500 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data",
"transient": {}
}
],
"source": [
"import matplotlib.pyplot as plt\n",
"import numpy as np\n",
"\n",
"labels = gap[\"mapeo\"].tolist()\n",
"both = gap[\"both\"].values\n",
"only_nav = gap[\"only_nav\"].values\n",
"only_tpv = gap[\"only_tpv\"].values\n",
"\n",
"x = np.arange(len(labels))\n",
"w = 0.28\n",
"fig, ax = plt.subplots(figsize=(10, 5))\n",
"ax.bar(x - w, both, w, label=\"Both (OK)\", color=\"#2a9d8f\")\n",
"ax.bar(x, only_nav, w, label=\"Sólo NAV (falta TPV)\", color=\"#e76f51\")\n",
"ax.bar(x + w, only_tpv, w, label=\"Sólo TPV (falta NAV)\", color=\"#f4a261\")\n",
"ax.set_xticks(x); ax.set_xticklabels(labels)\n",
"ax.set_ylabel(\"# productos\")\n",
"ax.set_title(\"Productos por mapeo NAV↔TPV\")\n",
"ax.legend()\n",
"for i, vals in enumerate(zip(both, only_nav, only_tpv)):\n",
" for j, v in enumerate(vals):\n",
" ax.text(i + (j-1)*w, v, f\"{v:,}\", ha=\"center\", va=\"bottom\", fontsize=9)\n",
"plt.tight_layout()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"id": "bdbf837c",
"metadata": {},
"source": [
"## 5 · Mapeo de columnas TPV ↔ NAV\n",
"\n",
"Cheatsheet para reemplazar columnas al cruzar las dos tablas.\n",
"\n",
"### Claves y unión\n",
"\n",
"| Concepto | NAV (`mssql2022_dbo.sales_price`) | TPV (`psql_dcpublic.sale_prices`) | Cómo cruzar |\n",
"|---|---|---|---|\n",
"| Identificador producto | `item_no_` (STRING) | `product_id` (INTEGER) | `products.nav_id = NAV.item_no_` → `products.id = TPV.product_id` |\n",
"| Lista de precio / canal | `sales_type` + `sales_code` | `channel_id` | 471↔1 aurgi.com · 472↔2 motortown.es · 478↔12 canarias |\n",
"| Grupo descuento | — (no existe concepto equivalente) | `price_group_id` | TPV aplica grupos; NAV no |\n",
"| Variante | `variant_code` | — (no tiene variantes) | Filtrar NAV a `variant_code=''` |\n",
"| Unidad de medida | `unit_of_measure_code` | — | Asumir unidad por defecto (normalmente 'UD') |\n",
"| Cantidad mínima | `minimum_quantity` | — | Ignorar NAV con `minimum_quantity > 0` |\n",
"\n",
"### Datos del precio\n",
"\n",
"| Dato | NAV | TPV | Notas |\n",
"|---|---|---|---|\n",
"| Precio unitario | `unit_price` (BIGNUMERIC) | `unit_price` (FLOAT) | `CAST(NAV.unit_price AS FLOAT64)` para comparar |\n",
"| Moneda | `currency_code` | — | TPV asume EUR |\n",
"| Inicio validez | `starting_date` (DATETIME) | `starting_date` (DATE) | NAV usa `'0001-01-01'` como null |\n",
"| Fin validez | `ending_date` (DATETIME) | `ending_date` (DATE) | NAV usa `'0001-01-01'` como null |\n",
"| Activo | — (no hay flag; vigencia por fechas) | `active` (BOOLEAN) | Filtrar TPV `active=TRUE` |\n",
"| Borrado lógico | `_fivetran_deleted` | — | Filtrar NAV `_fivetran_deleted IS NOT TRUE` |\n",
"| Precio incluye IVA | `price_includes_vat` (INT 0/1) | — | En TPV se asume sin IVA |\n",
"\n",
"### Flags de descuento\n",
"\n",
"| Concepto | NAV | TPV |\n",
"|---|---|---|\n",
"| Permite descuento especial | `permite_dto__especial` (INT 0/1) | `allows_special_discount` (BOOL) |\n",
"| Permite descuento promoción | `permite_dto__promocion` (INT 0/1) | `allows_promo_discount` (BOOL) |\n",
"| Permite descuento factura | `allow_invoice_disc_` (INT 0/1) | `allows_invoice_discount` (BOOL) |\n",
"| Permite descuento línea | `allow_line_disc_` (INT 0/1) | — |\n",
"\n",
"### Oferta / promoción\n",
"\n",
"| Concepto | NAV | TPV |\n",
"|---|---|---|\n",
"| Es oferta | `oferta__s_n_` (INT 0/1) | — |\n",
"| Precio promoción | `tarifa_promocion_ae` (BIGNUMERIC) | — (TPV separa la promoción en otra tabla) |\n",
"\n",
"### Auditoría\n",
"\n",
"| Dato | NAV | TPV |\n",
"|---|---|---|\n",
"| Fecha última modificación | `fecha_ultima_modificacion` · `fechahora_modificacion` · `fecha_modificacion` | `updated_at` |\n",
"| Fecha alta | `starting_date` (proxy) | `created_at` |\n",
"| Sincronización | `_fivetran_synced` | `datastream_metadata` |\n",
"\n",
"---\n",
"\n",
"## 6 · Explicación sencilla de la lógica\n",
"\n",
"**NAV y TPV guardan los precios de forma distinta, pero representan lo mismo.** Para compararlos hace falta traducir tres cosas:\n",
"\n",
"1. **Producto** — NAV usa un código alfanumérico (`item_no_`, p.ej. `MKT539258`). TPV usa un id numérico interno (`product_id`). La tabla `products` hace de puente: su campo `nav_id` guarda el código NAV y su `id` es el id TPV.\n",
"\n",
"2. **Canal** — NAV separa las listas con el par `sales_type=1` (Customer Price Group) + un `sales_code` numérico (471478). TPV usa `channel_id`. No hay tabla de equivalencia: se deduce por coincidencia empírica de precios. Lo seguro hoy:\n",
" - **471 ↔ aurgi.com (canal 1)**\n",
" - **472 ↔ motortown.es (canal 2)**\n",
" - **478 ↔ canarias (canal 12)**\n",
" - 473, 474, 475, 476, 477 no tienen canal TPV — son price-groups internos.\n",
"\n",
"3. **Vigencia** — NAV no tiene flag `active`; lo considera vigente si `starting_date ≤ hoy` y (`ending_date` vacío o futuro). TPV tiene `active=TRUE` + el mismo rango de fechas. **Siempre hay que filtrar a lo vigente y activo** antes de comparar, o se mezcla histórico con vigente.\n",
"\n",
"### Pseudocódigo\n",
"\n",
"```\n",
"NAV.current = NAV where\n",
" sales_type = 1\n",
" AND sales_code in (471, 472, 478)\n",
" AND _fivetran_deleted != TRUE\n",
" AND starting_date <= today\n",
" AND (ending_date is null OR ending_date = '0001-01-01' OR ending_date >= today)\n",
" AND variant_code = ''\n",
"\n",
"TPV.current = TPV where\n",
" channel_id in (1, 2, 12)\n",
" AND active = TRUE\n",
" AND starting_date <= today\n",
" AND (ending_date is null OR ending_date >= today)\n",
"\n",
"compare = NAV.current FULL JOIN TPV.current\n",
" ON products.nav_id = NAV.item_no_\n",
" AND products.id = TPV.product_id\n",
" AND map(NAV.sales_code) = TPV.channel_id\n",
"\n",
"only_nav = filas con NAV pero sin TPV → falta poner precio en TPV\n",
"only_tpv = filas con TPV pero sin NAV → falta poner precio en NAV\n",
"both = filas con ambas → comparar unit_price\n",
"```\n",
"\n",
"### Lectura del gráfico\n",
"\n",
"- **motortown.es y canarias son sanos**: apenas faltan en TPV, casi nada falta en NAV.\n",
"- **aurgi.com tiene dos deudas gordas**: 18.5k productos con precio NAV pero no publicados (89% están marcados `show_on_ecommerce=TRUE` — son productos que el catálogo dice visibles pero sin precio web), y 43.6k precios TPV sin contrapartida NAV (todos con `nav_id` — son productos NAV que simplemente nadie les ha puesto precio en la lista 471)."
]
}
],
"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
}