1757 lines
108 KiB
Plaintext
1757 lines
108 KiB
Plaintext
{
|
||
"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><NA></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><NA></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><NA></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><NA></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><NA></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><NA></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><NA></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><NA></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><NA></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><NA></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><NA></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><NA></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><NA></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><NA></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><NA></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 (471–478). 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
|
||
}
|