primer commit
This commit is contained in:
commit
22535b2682
|
|
@ -0,0 +1,72 @@
|
||||||
|
import re
|
||||||
|
import pandas as pd
|
||||||
|
import os
|
||||||
|
from datetime import datetime
|
||||||
|
from PyPDF2 import PdfReader
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
# Carpeta con tus tickets PDF
|
||||||
|
ticket_folder = "tickets"
|
||||||
|
|
||||||
|
def extract_data_from_pdf(file_path):
|
||||||
|
reader = PdfReader(file_path)
|
||||||
|
text = ""
|
||||||
|
for page in reader.pages:
|
||||||
|
text += page.extract_text() + "\n"
|
||||||
|
|
||||||
|
date_match = re.search(r"(\d{2}/\d{2}/\d{4})", text)
|
||||||
|
fecha = datetime.strptime(date_match.group(1), "%d/%m/%Y") if date_match else None
|
||||||
|
|
||||||
|
products = []
|
||||||
|
for line in text.splitlines():
|
||||||
|
match = re.match(r"\d*\s?(.*?)\s+(\d+,\d{2})\s+(\d+,\d{2})", line)
|
||||||
|
if match:
|
||||||
|
nombre = match.group(1).strip().upper()
|
||||||
|
unit_price = float(match.group(2).replace(",", "."))
|
||||||
|
total_price = float(match.group(3).replace(",", "."))
|
||||||
|
products.append((fecha, nombre, unit_price, total_price))
|
||||||
|
else:
|
||||||
|
match_simple = re.match(r"(.*?)\s+(\d+,\d{2})$", line)
|
||||||
|
if match_simple:
|
||||||
|
nombre = match_simple.group(1).strip().upper()
|
||||||
|
total_price = float(match_simple.group(2).replace(",", "."))
|
||||||
|
products.append((fecha, nombre, None, total_price))
|
||||||
|
|
||||||
|
return products
|
||||||
|
|
||||||
|
# Recolectar todos los productos de todos los tickets
|
||||||
|
datos = []
|
||||||
|
for file in os.listdir(ticket_folder):
|
||||||
|
if file.endswith(".pdf"):
|
||||||
|
path = os.path.join(ticket_folder, file)
|
||||||
|
datos.extend(extract_data_from_pdf(path))
|
||||||
|
|
||||||
|
# Crear DataFrame
|
||||||
|
columnas = ["fecha", "producto", "precio_unitario", "precio_total"]
|
||||||
|
df = pd.DataFrame(datos, columns=columnas)
|
||||||
|
df.dropna(subset=["fecha"], inplace=True)
|
||||||
|
|
||||||
|
# Normalizar nombres de producto (puedes hacer un diccionario de equivalencias si quieres afinar más)
|
||||||
|
df["producto"] = df["producto"].str.upper().str.strip()
|
||||||
|
|
||||||
|
# Guardar detalle completo
|
||||||
|
df.to_csv("detalle_productos.csv", index=False)
|
||||||
|
|
||||||
|
# Agrupar por producto
|
||||||
|
resumen = df.groupby("producto").agg(
|
||||||
|
veces_comprado=("fecha", "count"),
|
||||||
|
gasto_total=("precio_total", "sum"),
|
||||||
|
primera_vez=("fecha", "min"),
|
||||||
|
ultima_vez=("fecha", "max")
|
||||||
|
).sort_values("gasto_total", ascending=False)
|
||||||
|
resumen.to_csv("resumen_productos.csv")
|
||||||
|
|
||||||
|
# Gasto mensual
|
||||||
|
df["mes"] = df["fecha"].dt.to_period("M")
|
||||||
|
gasto_mensual = df.groupby("mes")["precio_total"].sum()
|
||||||
|
gasto_mensual.to_csv("gasto_mensual.csv")
|
||||||
|
|
||||||
|
print("\n✅ Análisis completado. Archivos generados:")
|
||||||
|
print("- detalle_productos.csv")
|
||||||
|
print("- resumen_productos.csv")
|
||||||
|
print("- gasto_mensual.csv")
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
import re
|
||||||
|
import pandas as pd
|
||||||
|
import os
|
||||||
|
from datetime import datetime
|
||||||
|
from PyPDF2 import PdfReader
|
||||||
|
from collections import defaultdict
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import seaborn as sns
|
||||||
|
|
||||||
|
# Carpeta con tus tickets PDF
|
||||||
|
ticket_folder = "tickets"
|
||||||
|
|
||||||
|
def extract_data_from_pdf(file_path):
|
||||||
|
reader = PdfReader(file_path)
|
||||||
|
text = ""
|
||||||
|
for page in reader.pages:
|
||||||
|
text += page.extract_text() + "\n"
|
||||||
|
|
||||||
|
date_match = re.search(r"(\d{2}/\d{2}/\d{4})", text)
|
||||||
|
fecha = datetime.strptime(date_match.group(1), "%d/%m/%Y") if date_match else None
|
||||||
|
|
||||||
|
products = []
|
||||||
|
for line in text.splitlines():
|
||||||
|
match = re.match(r"\d*\s?(.*?)\s+(\d+,\d{2})\s+(\d+,\d{2})", line)
|
||||||
|
if match:
|
||||||
|
nombre = match.group(1).strip().upper()
|
||||||
|
unit_price = float(match.group(2).replace(",", "."))
|
||||||
|
total_price = float(match.group(3).replace(",", "."))
|
||||||
|
products.append((fecha, nombre, unit_price, total_price))
|
||||||
|
else:
|
||||||
|
match_simple = re.match(r"(.*?)\s+(\d+,\d{2})$", line)
|
||||||
|
if match_simple:
|
||||||
|
nombre = match_simple.group(1).strip().upper()
|
||||||
|
total_price = float(match_simple.group(2).replace(",", "."))
|
||||||
|
products.append((fecha, nombre, None, total_price))
|
||||||
|
|
||||||
|
return products
|
||||||
|
|
||||||
|
# Recolectar todos los productos de todos los tickets
|
||||||
|
datos = []
|
||||||
|
for file in os.listdir(ticket_folder):
|
||||||
|
if file.endswith(".pdf"):
|
||||||
|
path = os.path.join(ticket_folder, file)
|
||||||
|
datos.extend(extract_data_from_pdf(path))
|
||||||
|
|
||||||
|
# Crear DataFrame
|
||||||
|
columnas = ["fecha", "producto", "precio_unitario", "precio_total"]
|
||||||
|
df = pd.DataFrame(datos, columns=columnas)
|
||||||
|
df.dropna(subset=["fecha"], inplace=True)
|
||||||
|
|
||||||
|
# Normalizar nombres de producto
|
||||||
|
df["producto"] = df["producto"].str.upper().str.strip()
|
||||||
|
|
||||||
|
# Guardar detalle completo
|
||||||
|
df.to_csv("detalle_productos.csv", index=False)
|
||||||
|
|
||||||
|
# Agrupar por producto
|
||||||
|
resumen = df.groupby("producto").agg(
|
||||||
|
veces_comprado=("fecha", "count"),
|
||||||
|
gasto_total=("precio_total", "sum"),
|
||||||
|
gasto_promedio=("precio_total", "mean"),
|
||||||
|
primera_vez=("fecha", "min"),
|
||||||
|
ultima_vez=("fecha", "max")
|
||||||
|
).sort_values("gasto_total", ascending=False)
|
||||||
|
resumen.to_csv("resumen_productos.csv")
|
||||||
|
|
||||||
|
# Gasto mensual
|
||||||
|
df["mes"] = df["fecha"].dt.to_period("M")
|
||||||
|
gasto_mensual = df.groupby("mes")["precio_total"].sum()
|
||||||
|
gasto_mensual.to_csv("gasto_mensual.csv")
|
||||||
|
|
||||||
|
# Graficar precios por producto a lo largo del tiempo
|
||||||
|
plt.figure(figsize=(10, 6))
|
||||||
|
for producto in df["producto"].unique():
|
||||||
|
producto_df = df[df["producto"] == producto]
|
||||||
|
plt.plot(producto_df["fecha"], producto_df["precio_total"], label=producto)
|
||||||
|
plt.title("Evolución de los precios de los productos a lo largo del tiempo")
|
||||||
|
plt.xlabel("Fecha")
|
||||||
|
plt.ylabel("Precio Total")
|
||||||
|
plt.legend()
|
||||||
|
plt.xticks(rotation=45)
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.savefig("grafico_precios_productos.png")
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
# Crear una lista de la compra: productos más comprados en el último mes
|
||||||
|
ultimo_mes = df["fecha"].max().replace(day=1) # Primer día del mes actual
|
||||||
|
productos_ultimo_mes = df[df["fecha"] >= ultimo_mes].groupby("producto").agg(
|
||||||
|
cantidad_comprada=("producto", "count"),
|
||||||
|
gasto_total=("precio_total", "sum")
|
||||||
|
).sort_values("gasto_total", ascending=False)
|
||||||
|
productos_ultimo_mes.to_csv("lista_compra_ultimo_mes.csv")
|
||||||
|
|
||||||
|
print("\n✅ Análisis completado. Archivos generados:")
|
||||||
|
print("- detalle_productos.csv")
|
||||||
|
print("- resumen_productos.csv")
|
||||||
|
print("- gasto_mensual.csv")
|
||||||
|
print("- grafico_precios_productos.png")
|
||||||
|
print("- lista_compra_ultimo_mes.csv")
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
import re
|
||||||
|
import pandas as pd
|
||||||
|
import os
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from PyPDF2 import PdfReader
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
# Carpeta con tus tickets PDF
|
||||||
|
ticket_folder = "tickets"
|
||||||
|
|
||||||
|
def extract_data_from_pdf(file_path):
|
||||||
|
reader = PdfReader(file_path)
|
||||||
|
text = ""
|
||||||
|
for page in reader.pages:
|
||||||
|
text += page.extract_text() + "\n"
|
||||||
|
|
||||||
|
date_match = re.search(r"(\d{2}/\d{2}/\d{4})", text)
|
||||||
|
fecha = datetime.strptime(date_match.group(1), "%d/%m/%Y") if date_match else None
|
||||||
|
|
||||||
|
products = []
|
||||||
|
for line in text.splitlines():
|
||||||
|
match = re.match(r"\d*\s?(.*?)\s+(\d+,\d{2})\s+(\d+,\d{2})", line)
|
||||||
|
if match:
|
||||||
|
nombre = match.group(1).strip().upper()
|
||||||
|
unit_price = float(match.group(2).replace(",", "."))
|
||||||
|
total_price = float(match.group(3).replace(",", "."))
|
||||||
|
products.append((fecha, nombre, unit_price, total_price))
|
||||||
|
else:
|
||||||
|
match_simple = re.match(r"(.*?)\s+(\d+,\d{2})$", line)
|
||||||
|
if match_simple:
|
||||||
|
nombre = match_simple.group(1).strip().upper()
|
||||||
|
total_price = float(match_simple.group(2).replace(",", "."))
|
||||||
|
products.append((fecha, nombre, None, total_price))
|
||||||
|
|
||||||
|
return products
|
||||||
|
|
||||||
|
# Recolectar todos los productos de todos los tickets
|
||||||
|
datos = []
|
||||||
|
for file in os.listdir(ticket_folder):
|
||||||
|
if file.endswith(".pdf"):
|
||||||
|
path = os.path.join(ticket_folder, file)
|
||||||
|
datos.extend(extract_data_from_pdf(path))
|
||||||
|
|
||||||
|
# Crear DataFrame
|
||||||
|
columnas = ["fecha", "producto", "precio_unitario", "precio_total"]
|
||||||
|
df = pd.DataFrame(datos, columns=columnas)
|
||||||
|
df.dropna(subset=["fecha"], inplace=True)
|
||||||
|
|
||||||
|
# Normalizar nombres de producto
|
||||||
|
df["producto"] = df["producto"].str.upper().str.strip()
|
||||||
|
|
||||||
|
# Calcular el tiempo entre compras para cada producto
|
||||||
|
df["diferencia_dias"] = df.groupby("producto")["fecha"].diff().dt.days
|
||||||
|
|
||||||
|
# Calcular la frecuencia de compra (promedio de días entre compras)
|
||||||
|
frecuencia_compra = df.groupby("producto")["diferencia_dias"].mean().reset_index()
|
||||||
|
|
||||||
|
# Estimación de la duración de los productos (cuánto duran en casa)
|
||||||
|
# Suponemos que compras aproximadamente la misma cantidad cada vez.
|
||||||
|
# Si se desea una estimación más precisa, se pueden agregar más datos sobre cantidad.
|
||||||
|
frecuencia_compra["proxima_compra_estimado"] = df["fecha"].max() + pd.to_timedelta(frecuencia_compra["diferencia_dias"], unit="D")
|
||||||
|
|
||||||
|
# Ahora seleccionamos los productos que más frecuentemente compras
|
||||||
|
# y predecimos cuándo volverás a comprar basándonos en la frecuencia.
|
||||||
|
productos_estimados = frecuencia_compra.sort_values("diferencia_dias", ascending=True)
|
||||||
|
|
||||||
|
# Listar la compra estimada
|
||||||
|
productos_estimados["producto"] = productos_estimados["producto"].str.title() # Capitalizar el nombre del producto
|
||||||
|
productos_estimados["fecha_estimada_proxima_compra"] = productos_estimados["proxima_compra_estimado"].dt.strftime("%d/%m/%Y")
|
||||||
|
|
||||||
|
# Guardar la lista estimada de la compra
|
||||||
|
productos_estimados.to_csv("lista_compra_estimado.csv", index=False)
|
||||||
|
|
||||||
|
# Mostrar los primeros resultados
|
||||||
|
print("\n✅ Lista de la compra estimada para la próxima compra:")
|
||||||
|
print(productos_estimados[["producto", "diferencia_dias", "fecha_estimada_proxima_compra"]])
|
||||||
|
|
||||||
|
print("\n✅ Archivos generados:")
|
||||||
|
print("- lista_compra_estimado.csv")
|
||||||
|
|
@ -0,0 +1,147 @@
|
||||||
|
import re
|
||||||
|
import pandas as pd
|
||||||
|
import os
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from PyPDF2 import PdfReader
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
# Carpeta con tus tickets PDF
|
||||||
|
ticket_folder = "tickets"
|
||||||
|
|
||||||
|
def extract_data_from_pdf(file_path):
|
||||||
|
reader = PdfReader(file_path)
|
||||||
|
text = ""
|
||||||
|
for page in reader.pages:
|
||||||
|
text += page.extract_text() + "\n"
|
||||||
|
|
||||||
|
date_match = re.search(r"(\d{2}/\d{2}/\d{4})", text)
|
||||||
|
fecha = datetime.strptime(date_match.group(1), "%d/%m/%Y") if date_match else None
|
||||||
|
|
||||||
|
products = []
|
||||||
|
for line in text.splitlines():
|
||||||
|
match = re.match(r"\d*\s?(.*?)\s+(\d+,\d{2})\s+(\d+,\d{2})", line)
|
||||||
|
if match:
|
||||||
|
nombre = match.group(1).strip().upper()
|
||||||
|
unit_price = float(match.group(2).replace(",", "."))
|
||||||
|
total_price = float(match.group(3).replace(",", "."))
|
||||||
|
products.append((fecha, nombre, unit_price, total_price))
|
||||||
|
else:
|
||||||
|
match_simple = re.match(r"(.*?)\s+(\d+,\d{2})$", line)
|
||||||
|
if match_simple:
|
||||||
|
nombre = match_simple.group(1).strip().upper()
|
||||||
|
total_price = float(match_simple.group(2).replace(",", "."))
|
||||||
|
products.append((fecha, nombre, None, total_price))
|
||||||
|
|
||||||
|
return products
|
||||||
|
|
||||||
|
# Recolectar todos los productos de todos los tickets
|
||||||
|
datos = []
|
||||||
|
for file in os.listdir(ticket_folder):
|
||||||
|
if file.endswith(".pdf"):
|
||||||
|
path = os.path.join(ticket_folder, file)
|
||||||
|
datos.extend(extract_data_from_pdf(path))
|
||||||
|
|
||||||
|
# Crear DataFrame
|
||||||
|
columnas = ["fecha", "producto", "precio_unitario", "precio_total"]
|
||||||
|
df = pd.DataFrame(datos, columns=columnas)
|
||||||
|
df.dropna(subset=["fecha"], inplace=True)
|
||||||
|
|
||||||
|
# Normalizar nombres de producto
|
||||||
|
df["producto"] = df["producto"].str.upper().str.strip()
|
||||||
|
|
||||||
|
# Calcular el tiempo entre compras para cada producto
|
||||||
|
df["diferencia_dias"] = df.groupby("producto")["fecha"].diff().dt.days
|
||||||
|
|
||||||
|
# Calcular la frecuencia de compra (promedio de días entre compras)
|
||||||
|
frecuencia_compra = df.groupby("producto")["diferencia_dias"].mean().reset_index()
|
||||||
|
|
||||||
|
# Estimación de la duración de los productos (cuánto duran en casa)
|
||||||
|
frecuencia_compra["proxima_compra_estimado"] = df["fecha"].max() + pd.to_timedelta(frecuencia_compra["diferencia_dias"], unit="D")
|
||||||
|
|
||||||
|
# Ahora seleccionamos los productos que más frecuentemente compras
|
||||||
|
productos_estimados = frecuencia_compra.sort_values("diferencia_dias", ascending=True)
|
||||||
|
|
||||||
|
# Listar la compra estimada
|
||||||
|
productos_estimados["producto"] = productos_estimados["producto"].str.title() # Capitalizar el nombre del producto
|
||||||
|
productos_estimados["fecha_estimada_proxima_compra"] = productos_estimados["proxima_compra_estimado"].dt.strftime("%d/%m/%Y")
|
||||||
|
|
||||||
|
# Generar la lista de la compra en formato HTML
|
||||||
|
html_content = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Lista de la Compra Estimada</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
background-color: #f4f4f9;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
color: #333;
|
||||||
|
padding-top: 20px;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: 80%;
|
||||||
|
margin: 30px auto;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
padding: 12px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
tr:hover {
|
||||||
|
background-color: #f1f1f1;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>Lista de la Compra Estimada</h1>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Producto</th>
|
||||||
|
<th>Días Promedio entre Compras</th>
|
||||||
|
<th>Fecha Estimada de la Próxima Compra</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Añadir filas con los productos
|
||||||
|
for _, row in productos_estimados.iterrows():
|
||||||
|
html_content += f"""
|
||||||
|
<tr>
|
||||||
|
<td>{row['producto']}</td>
|
||||||
|
<td>{row['diferencia_dias']:.0f}</td>
|
||||||
|
<td>{row['fecha_estimada_proxima_compra']}</td>
|
||||||
|
</tr>
|
||||||
|
"""
|
||||||
|
|
||||||
|
html_content += """
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Guardar el archivo HTML
|
||||||
|
with open("lista_compra_estimado.html", "w", encoding="utf-8") as file:
|
||||||
|
file.write(html_content)
|
||||||
|
|
||||||
|
print("\n✅ Página HTML generada: lista_compra_estimado.html")
|
||||||
|
|
@ -0,0 +1,176 @@
|
||||||
|
import re
|
||||||
|
import pandas as pd
|
||||||
|
import os
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from PyPDF2 import PdfReader
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
# Carpeta con tus tickets PDF
|
||||||
|
ticket_folder = "tickets"
|
||||||
|
|
||||||
|
def extract_data_from_pdf(file_path):
|
||||||
|
reader = PdfReader(file_path)
|
||||||
|
text = ""
|
||||||
|
for page in reader.pages:
|
||||||
|
text += page.extract_text() + "\n"
|
||||||
|
|
||||||
|
date_match = re.search(r"(\d{2}/\d{2}/\d{4})", text)
|
||||||
|
fecha = datetime.strptime(date_match.group(1), "%d/%m/%Y") if date_match else None
|
||||||
|
|
||||||
|
products = []
|
||||||
|
for line in text.splitlines():
|
||||||
|
match = re.match(r"\d*\s?(.*?)\s+(\d+,\d{2})\s+(\d+,\d{2})", line)
|
||||||
|
if match:
|
||||||
|
nombre = match.group(1).strip().upper()
|
||||||
|
unit_price = float(match.group(2).replace(",", "."))
|
||||||
|
total_price = float(match.group(3).replace(",", "."))
|
||||||
|
products.append((fecha, nombre, unit_price, total_price))
|
||||||
|
else:
|
||||||
|
match_simple = re.match(r"(.*?)\s+(\d+,\d{2})$", line)
|
||||||
|
if match_simple:
|
||||||
|
nombre = match_simple.group(1).strip().upper()
|
||||||
|
total_price = float(match_simple.group(2).replace(",", "."))
|
||||||
|
products.append((fecha, nombre, None, total_price))
|
||||||
|
|
||||||
|
return products
|
||||||
|
|
||||||
|
# Recolectar todos los productos de todos los tickets
|
||||||
|
def load_and_process_data():
|
||||||
|
datos = []
|
||||||
|
for file in os.listdir(ticket_folder):
|
||||||
|
if file.endswith(".pdf"):
|
||||||
|
path = os.path.join(ticket_folder, file)
|
||||||
|
datos.extend(extract_data_from_pdf(path))
|
||||||
|
|
||||||
|
# Crear DataFrame
|
||||||
|
columnas = ["fecha", "producto", "precio_unitario", "precio_total"]
|
||||||
|
df = pd.DataFrame(datos, columns=columnas)
|
||||||
|
df.dropna(subset=["fecha"], inplace=True)
|
||||||
|
|
||||||
|
# Normalizar nombres de producto
|
||||||
|
df["producto"] = df["producto"].str.upper().str.strip()
|
||||||
|
|
||||||
|
return df
|
||||||
|
|
||||||
|
# Calcular la frecuencia de compra y la fecha estimada de la próxima compra
|
||||||
|
def calculate_purchase_estimates(df):
|
||||||
|
# Calcular el tiempo entre compras para cada producto
|
||||||
|
df["diferencia_dias"] = df.groupby("producto")["fecha"].diff().dt.days
|
||||||
|
|
||||||
|
# Calcular la frecuencia de compra (promedio de días entre compras)
|
||||||
|
frecuencia_compra = df.groupby("producto")["diferencia_dias"].mean().reset_index()
|
||||||
|
|
||||||
|
# Estimación de la duración de los productos (cuánto duran en casa)
|
||||||
|
frecuencia_compra["proxima_compra_estimado"] = df["fecha"].max() + pd.to_timedelta(frecuencia_compra["diferencia_dias"], unit="D")
|
||||||
|
|
||||||
|
# Ahora seleccionamos los productos que más frecuentemente compras
|
||||||
|
frecuencia_compra["producto"] = frecuencia_compra["producto"].str.title() # Capitalizar el nombre del producto
|
||||||
|
frecuencia_compra["fecha_estimada_proxima_compra"] = frecuencia_compra["proxima_compra_estimado"].dt.strftime("%d/%m/%Y")
|
||||||
|
|
||||||
|
return frecuencia_compra
|
||||||
|
|
||||||
|
# Generar la lista de la compra semanal
|
||||||
|
def generate_weekly_shopping_list(frecuencia_compra):
|
||||||
|
today = datetime.today()
|
||||||
|
# Calcular la fecha del próximo jueves
|
||||||
|
days_until_next_thursday = (3 - today.weekday()) % 7
|
||||||
|
next_thursday = today + timedelta(days=days_until_next_thursday)
|
||||||
|
|
||||||
|
# Filtrar los productos cuya próxima compra se estima para la próxima semana
|
||||||
|
productos_estimados = frecuencia_compra[frecuencia_compra["proxima_compra_estimado"] <= next_thursday]
|
||||||
|
|
||||||
|
# Generar la lista de la compra en formato HTML
|
||||||
|
html_content = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Lista de la Compra Semanal</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
background-color: #f4f4f9;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
color: #333;
|
||||||
|
padding-top: 20px;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: 80%;
|
||||||
|
margin: 30px auto;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
padding: 12px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
tr:hover {
|
||||||
|
background-color: #f1f1f1;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>Lista de la Compra Semanal (Próximo Jueves)</h1>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Producto</th>
|
||||||
|
<th>Días Promedio entre Compras</th>
|
||||||
|
<th>Fecha Estimada de la Próxima Compra</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Añadir filas con los productos
|
||||||
|
for _, row in productos_estimados.iterrows():
|
||||||
|
html_content += f"""
|
||||||
|
<tr>
|
||||||
|
<td>{row['producto']}</td>
|
||||||
|
<td>{row['diferencia_dias']:.0f}</td>
|
||||||
|
<td>{row['fecha_estimada_proxima_compra']}</td>
|
||||||
|
</tr>
|
||||||
|
"""
|
||||||
|
|
||||||
|
html_content += """
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Guardar el archivo HTML
|
||||||
|
file_name = f"lista_compra_semanal_{next_thursday.strftime('%d_%m_%Y')}.html"
|
||||||
|
with open(file_name, "w", encoding="utf-8") as file:
|
||||||
|
file.write(html_content)
|
||||||
|
|
||||||
|
print(f"\n✅ Página HTML generada: {file_name}")
|
||||||
|
|
||||||
|
# Función para actualizar la lista de la compra después de agregar un nuevo ticket
|
||||||
|
def update_shopping_list():
|
||||||
|
# Cargar y procesar los datos
|
||||||
|
df = load_and_process_data()
|
||||||
|
|
||||||
|
# Calcular las estimaciones de la compra
|
||||||
|
frecuencia_compra = calculate_purchase_estimates(df)
|
||||||
|
|
||||||
|
# Generar la lista de la compra para la próxima semana (jueves)
|
||||||
|
generate_weekly_shopping_list(frecuencia_compra)
|
||||||
|
|
||||||
|
# Llamar a la función para actualizar la lista de la compra
|
||||||
|
update_shopping_list()
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
import re
|
||||||
|
import pandas as pd
|
||||||
|
import os
|
||||||
|
from datetime import datetime
|
||||||
|
from PyPDF2 import PdfReader
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
# Carpeta con tus tickets PDF
|
||||||
|
ticket_folder = "tickets"
|
||||||
|
|
||||||
|
# Palabras clave que indican líneas que no deben ser procesadas
|
||||||
|
exclude_keywords = [
|
||||||
|
"TARJETA BANCARIA", "IVA BASE IMPONIBLE", "CUOTA", "TOTAL",
|
||||||
|
"SE ADMITEN DEVOLUCIONES CON TICKET", "N.C", "AUT", "AID",
|
||||||
|
"Verificado por dispositivo", "Visa Credit", "IMPORTE", "TARJ. BANCARIA"
|
||||||
|
]
|
||||||
|
|
||||||
|
def extract_data_from_pdf(file_path):
|
||||||
|
reader = PdfReader(file_path)
|
||||||
|
text = ""
|
||||||
|
for page in reader.pages:
|
||||||
|
text += page.extract_text() + "\n"
|
||||||
|
|
||||||
|
# Buscar la fecha en el ticket (formato dd/mm/yyyy)
|
||||||
|
date_match = re.search(r"(\d{2}/\d{2}/\d{4})", text)
|
||||||
|
fecha = datetime.strptime(date_match.group(1), "%d/%m/%Y") if date_match else None
|
||||||
|
|
||||||
|
products = []
|
||||||
|
# Procesar línea por línea del texto extraído
|
||||||
|
for line in text.splitlines():
|
||||||
|
# Ignorar líneas que contienen palabras clave de exclusión
|
||||||
|
if any(keyword in line for keyword in exclude_keywords):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Buscar productos con precio unitario y precio total
|
||||||
|
match = re.match(r"\d*\s?(.*?)\s+(\d+,\d{2})\s+(\d+,\d{2})", line)
|
||||||
|
if match:
|
||||||
|
nombre = match.group(1).strip().upper() # El nombre del producto
|
||||||
|
unit_price = float(match.group(2).replace(",", ".")) # Precio unitario
|
||||||
|
total_price = float(match.group(3).replace(",", ".")) # Precio total
|
||||||
|
products.append((fecha, nombre, unit_price, total_price))
|
||||||
|
else:
|
||||||
|
# En caso de no encontrar el precio unitario, solo se extrae el precio total
|
||||||
|
match_simple = re.match(r"(.*?)\s+(\d+,\d{2})$", line)
|
||||||
|
if match_simple:
|
||||||
|
nombre = match_simple.group(1).strip().upper()
|
||||||
|
total_price = float(match_simple.group(2).replace(",", "."))
|
||||||
|
products.append((fecha, nombre, None, total_price))
|
||||||
|
|
||||||
|
return products
|
||||||
|
|
||||||
|
# Recolectar todos los productos de todos los tickets
|
||||||
|
datos = []
|
||||||
|
for file in os.listdir(ticket_folder):
|
||||||
|
if file.endswith(".pdf"):
|
||||||
|
path = os.path.join(ticket_folder, file)
|
||||||
|
datos.extend(extract_data_from_pdf(path))
|
||||||
|
|
||||||
|
# Crear DataFrame
|
||||||
|
columnas = ["fecha", "producto", "precio_unitario", "precio_total"]
|
||||||
|
df = pd.DataFrame(datos, columns=columnas)
|
||||||
|
df.dropna(subset=["fecha"], inplace=True)
|
||||||
|
|
||||||
|
# Normalizar nombres de producto (puedes hacer un diccionario de equivalencias si quieres afinar más)
|
||||||
|
df["producto"] = df["producto"].str.upper().str.strip()
|
||||||
|
|
||||||
|
# Guardar detalle completo
|
||||||
|
df.to_csv("detalle_productos.csv", index=False)
|
||||||
|
|
||||||
|
# Agrupar por producto
|
||||||
|
resumen = df.groupby("producto").agg(
|
||||||
|
veces_comprado=("fecha", "count"),
|
||||||
|
gasto_total=("precio_total", "sum"),
|
||||||
|
primera_vez=("fecha", "min"),
|
||||||
|
ultima_vez=("fecha", "max")
|
||||||
|
).sort_values("gasto_total", ascending=False)
|
||||||
|
resumen.to_csv("resumen_productos.csv")
|
||||||
|
|
||||||
|
# Gasto mensual
|
||||||
|
df["mes"] = df["fecha"].dt.to_period("M")
|
||||||
|
gasto_mensual = df.groupby("mes")["precio_total"].sum()
|
||||||
|
gasto_mensual.to_csv("gasto_mensual.csv")
|
||||||
|
|
||||||
|
# Lista de compra estimada
|
||||||
|
# Calcular la frecuencia de compra y la estimación del próximo producto necesario
|
||||||
|
df["dias_entre_compras"] = df.groupby("producto")["fecha"].diff().dt.days
|
||||||
|
df["promedio_dias_entre_compras"] = df.groupby("producto")["dias_entre_compras"].transform("mean")
|
||||||
|
df["proxima_compra"] = df["fecha"] + pd.to_timedelta(df["promedio_dias_entre_compras"], unit='D')
|
||||||
|
|
||||||
|
# Crear la lista de la compra para la próxima semana
|
||||||
|
proxima_semana = datetime.now() + pd.Timedelta(weeks=1)
|
||||||
|
compra_estimacion = df[df["proxima_compra"] <= proxima_semana]
|
||||||
|
|
||||||
|
# Guardar la lista de compra estimada
|
||||||
|
compra_estimacion.to_csv("compra_estimacion.csv", index=False)
|
||||||
|
|
||||||
|
# Crear un archivo HTML visual para la lista de la compra
|
||||||
|
html = compra_estimacion[["producto", "precio_unitario", "precio_total", "proxima_compra"]].sort_values("proxima_compra").to_html(index=False)
|
||||||
|
|
||||||
|
# Guardar el archivo HTML
|
||||||
|
with open("lista_compra_estimada.html", "w") as file:
|
||||||
|
file.write(html)
|
||||||
|
|
||||||
|
print("\n✅ Análisis completado. Archivos generados:")
|
||||||
|
print("- detalle_productos.csv")
|
||||||
|
print("- resumen_productos.csv")
|
||||||
|
print("- gasto_mensual.csv")
|
||||||
|
print("- compra_estimacion.csv")
|
||||||
|
print("- lista_compra_estimada.html")
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
import re
|
||||||
|
import pandas as pd
|
||||||
|
import os
|
||||||
|
from datetime import datetime
|
||||||
|
from PyPDF2 import PdfReader
|
||||||
|
|
||||||
|
# Carpeta con los tickets PDF
|
||||||
|
ticket_folder = "tickets"
|
||||||
|
|
||||||
|
# Palabras clave que indican líneas que no hay que procesar
|
||||||
|
exclude_keywords = [
|
||||||
|
"TARJETA BANCARIA", "IVA BASE IMPONIBLE", "CUOTA", "TOTAL",
|
||||||
|
"SE ADMITEN DEVOLUCIONES CON TICKET", "N.C", "AUT", "AID",
|
||||||
|
"Verificado por dispositivo", "Visa Credit", "IMPORTE", "TARJ. BANCARIA"
|
||||||
|
]
|
||||||
|
|
||||||
|
def extract_data_from_pdf(file_path):
|
||||||
|
reader = PdfReader(file_path)
|
||||||
|
text = ""
|
||||||
|
for page in reader.pages:
|
||||||
|
text += page.extract_text() + "\n"
|
||||||
|
|
||||||
|
# Buscar la fecha
|
||||||
|
date_match = re.search(r"(\d{2}/\d{2}/\d{4})", text)
|
||||||
|
fecha = datetime.strptime(date_match.group(1), "%d/%m/%Y") if date_match else None
|
||||||
|
|
||||||
|
products = []
|
||||||
|
|
||||||
|
for line in text.splitlines():
|
||||||
|
if any(keyword in line for keyword in exclude_keywords):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Coincide con líneas tipo: "2 ROLLO HOGAR DOBLE 2,35 4,70"
|
||||||
|
match = re.match(r"(\d+)\s+(.*?)\s+(\d+,\d{2})\s+(\d+,\d{2})$", line)
|
||||||
|
if match:
|
||||||
|
cantidad = int(match.group(1))
|
||||||
|
producto = match.group(2).strip().upper()
|
||||||
|
precio_unitario = float(match.group(3).replace(",", "."))
|
||||||
|
precio_total = float(match.group(4).replace(",", "."))
|
||||||
|
products.append((fecha, cantidad, producto, precio_unitario, precio_total))
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Coincide con líneas tipo: "1 CROISSANT RELL CACAO 1,90"
|
||||||
|
match_simple = re.match(r"(\d+)\s+(.*?)\s+(\d+,\d{2})$", line)
|
||||||
|
if match_simple:
|
||||||
|
cantidad = int(match_simple.group(1))
|
||||||
|
producto = match_simple.group(2).strip().upper()
|
||||||
|
precio_total = float(match_simple.group(3).replace(",", "."))
|
||||||
|
precio_unitario = precio_total / cantidad
|
||||||
|
products.append((fecha, cantidad, producto, round(precio_unitario, 2), precio_total))
|
||||||
|
continue
|
||||||
|
|
||||||
|
return products
|
||||||
|
|
||||||
|
# Recolectar todos los datos de los tickets
|
||||||
|
datos = []
|
||||||
|
for file in os.listdir(ticket_folder):
|
||||||
|
if file.endswith(".pdf"):
|
||||||
|
path = os.path.join(ticket_folder, file)
|
||||||
|
datos.extend(extract_data_from_pdf(path))
|
||||||
|
|
||||||
|
# Crear DataFrame
|
||||||
|
columnas = ["fecha", "cantidad", "producto", "precio_unitario", "precio_total"]
|
||||||
|
df = pd.DataFrame(datos, columns=columnas)
|
||||||
|
df.dropna(subset=["fecha"], inplace=True)
|
||||||
|
|
||||||
|
# Guardar detalle completo
|
||||||
|
df.to_csv("detalle_productos.csv", index=False)
|
||||||
|
|
||||||
|
# Agrupar por producto
|
||||||
|
resumen = df.groupby("producto").agg(
|
||||||
|
veces_comprado=("fecha", "count"),
|
||||||
|
total_unidades=("cantidad", "sum"),
|
||||||
|
gasto_total=("precio_total", "sum"),
|
||||||
|
primera_vez=("fecha", "min"),
|
||||||
|
ultima_vez=("fecha", "max")
|
||||||
|
).sort_values("gasto_total", ascending=False)
|
||||||
|
resumen.to_csv("resumen_productos.csv")
|
||||||
|
|
||||||
|
# Gasto mensual
|
||||||
|
df["mes"] = df["fecha"].dt.to_period("M")
|
||||||
|
gasto_mensual = df.groupby("mes")["precio_total"].sum()
|
||||||
|
gasto_mensual.to_csv("gasto_mensual.csv")
|
||||||
|
|
||||||
|
# Calcular próxima compra estimada por media de días entre compras
|
||||||
|
df["dias_entre_compras"] = df.groupby("producto")["fecha"].diff().dt.days
|
||||||
|
df["promedio_dias_entre_compras"] = df.groupby("producto")["dias_entre_compras"].transform("mean")
|
||||||
|
df["proxima_compra"] = df["fecha"] + pd.to_timedelta(df["promedio_dias_entre_compras"], unit='D')
|
||||||
|
|
||||||
|
# Crear la lista estimada para la próxima compra (esta semana)
|
||||||
|
proxima_semana = datetime.now() + pd.Timedelta(days=7)
|
||||||
|
compra_estimacion = df[df["proxima_compra"] <= proxima_semana]
|
||||||
|
|
||||||
|
# Guardar lista estimada
|
||||||
|
compra_estimacion.to_csv("compra_estimacion.csv", index=False)
|
||||||
|
|
||||||
|
# Generar HTML visual
|
||||||
|
html = compra_estimacion[["producto", "cantidad", "precio_unitario", "precio_total", "proxima_compra"]].sort_values("proxima_compra").to_html(index=False)
|
||||||
|
with open("lista_compra_estimada.html", "w") as file:
|
||||||
|
file.write(html)
|
||||||
|
|
||||||
|
print("\n✅ Todo listo. Archivos generados:")
|
||||||
|
print("- detalle_productos.csv")
|
||||||
|
print("- resumen_productos.csv")
|
||||||
|
print("- gasto_mensual.csv")
|
||||||
|
print("- compra_estimacion.csv")
|
||||||
|
print("- lista_compra_estimada.html")
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
fecha,cantidad,producto,precio_unitario,precio_total,mes,dias_entre_compras,promedio_dias_entre_compras,proxima_compra
|
||||||
|
2025-01-16,14,ESTACIONES,0.16,2.2,2025-01,,27.5,2025-02-12 12:00:00.000000000
|
||||||
|
2025-01-23,11,CORAZON ROMANA,0.1,1.1,2025-01,,62.0,2025-03-26 00:00:00.000000000
|
||||||
|
2025-01-23,14,ESTACIONES,0.16,2.2,2025-01,7.0,27.5,2025-02-19 12:00:00.000000000
|
||||||
|
2025-01-23,112,HUEVOS CAMPEROS,0.02,2.72,2025-01,,5.928571428571429,2025-01-28 22:17:08.571428568
|
||||||
|
2025-01-29,112,HUEVOS CAMPEROS,0.02,2.72,2025-01,6.0,5.928571428571429,2025-02-03 22:17:08.571428568
|
||||||
|
2025-02-07,112,HUEVOS CAMPEROS,0.02,2.72,2025-02,9.0,5.928571428571429,2025-02-12 22:17:08.571428568
|
||||||
|
2025-02-13,26,HUEVOS CAMPEROS,1.65,3.3,2025-02,6.0,5.928571428571429,2025-02-18 22:17:08.571428568
|
||||||
|
2025-02-20,112,HUEVOS CAMPEROS,0.02,2.72,2025-02,7.0,5.928571428571429,2025-02-25 22:17:08.571428568
|
||||||
|
2025-02-27,112,HUEVOS CAMPEROS,0.02,2.72,2025-02,7.0,5.928571428571429,2025-03-04 22:17:08.571428568
|
||||||
|
2025-03-05,112,HUEVOS CAMPEROS,0.03,3.22,2025-03,6.0,5.928571428571429,2025-03-10 22:17:08.571428568
|
||||||
|
2025-03-12,24,ESTACIONES,2.2,4.4,2025-03,48.0,27.5,2025-04-08 12:00:00.000000000
|
||||||
|
2025-03-12,112,HUEVOS CAMPEROS,0.03,3.22,2025-03,7.0,5.928571428571429,2025-03-17 22:17:08.571428568
|
||||||
|
2025-03-20,112,HUEVOS CAMPEROS,0.03,3.22,2025-03,8.0,5.928571428571429,2025-03-25 22:17:08.571428568
|
||||||
|
2025-03-26,112,HUEVOS CAMPEROS,0.03,3.22,2025-03,6.0,5.928571428571429,2025-03-31 22:17:08.571428568
|
||||||
|
2025-03-26,16,HUEVOS CAMPEROS,0.12,1.9,2025-03,0.0,5.928571428571429,2025-03-31 22:17:08.571428568
|
||||||
|
2025-04-03,112,HUEVOS CAMPEROS,0.03,3.22,2025-04,8.0,5.928571428571429,2025-04-08 22:17:08.571428568
|
||||||
|
2025-04-10,16,HUEVOS CAMPEROS,0.12,1.9,2025-04,7.0,5.928571428571429,2025-04-15 22:17:08.571428568
|
||||||
|
2025-04-10,112,HUEVOS CAMPEROS,0.03,3.22,2025-04,0.0,5.928571428571429,2025-04-15 22:17:08.571428568
|
||||||
|
2025-04-16,112,HUEVOS CAMPEROS,0.03,3.22,2025-04,6.0,5.928571428571429,2025-04-21 22:17:08.571428568
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
fecha,cantidad,producto,precio_unitario,precio_total
|
||||||
|
2025-01-02,1200,SERVIL. BLANCAS,0.0,1.25
|
||||||
|
2025-01-09,230,B.ENVASES C.FÁCIL,1.65,3.3
|
||||||
|
2025-01-09,340,B.MINI PAPELERA,1.35,4.05
|
||||||
|
2025-01-16,14,ESTACIONES,0.16,2.2
|
||||||
|
2025-01-23,11,CORAZON ROMANA,0.1,1.1
|
||||||
|
2025-01-23,14,ESTACIONES,0.16,2.2
|
||||||
|
2025-01-23,112,HUEVOS CAMPEROS,0.02,2.72
|
||||||
|
2025-01-29,112,HUEVOS CAMPEROS,0.02,2.72
|
||||||
|
2025-01-29,13,"RECIPIENTES 0,5L",0.16,2.05
|
||||||
|
2025-02-07,112,HUEVOS CAMPEROS,0.02,2.72
|
||||||
|
2025-02-13,26,HUEVOS CAMPEROS,1.65,3.3
|
||||||
|
2025-02-20,112,HUEVOS CAMPEROS,0.02,2.72
|
||||||
|
2025-02-27,112,HUEVOS CAMPEROS,0.02,2.72
|
||||||
|
2025-03-05,112,HUEVOS CAMPEROS,0.03,3.22
|
||||||
|
2025-03-12,24,ESTACIONES,2.2,4.4
|
||||||
|
2025-03-12,112,HUEVOS CAMPEROS,0.03,3.22
|
||||||
|
2025-03-20,112,HUEVOS CAMPEROS,0.03,3.22
|
||||||
|
2025-03-26,11,CORAZON ROMANA,0.1,1.1
|
||||||
|
2025-03-26,112,HUEVOS CAMPEROS,0.03,3.22
|
||||||
|
2025-03-26,16,HUEVOS CAMPEROS,0.12,1.9
|
||||||
|
2025-04-03,112,HUEVOS CAMPEROS,0.03,3.22
|
||||||
|
2025-04-10,16,HUEVOS CAMPEROS,0.12,1.9
|
||||||
|
2025-04-10,112,HUEVOS CAMPEROS,0.03,3.22
|
||||||
|
2025-04-16,112,HUEVOS CAMPEROS,0.03,3.22
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
mes,precio_total
|
||||||
|
2025-01,21.59
|
||||||
|
2025-02,11.46
|
||||||
|
2025-03,20.28
|
||||||
|
2025-04,11.56
|
||||||
|
Binary file not shown.
|
After Width: | Height: | Size: 122 KiB |
|
|
@ -0,0 +1,94 @@
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
from datetime import datetime
|
||||||
|
import mysql.connector
|
||||||
|
from PyPDF2 import PdfReader
|
||||||
|
|
||||||
|
# Configura tu conexión
|
||||||
|
DB_CONFIG = {
|
||||||
|
'host': 'tecnologia-facil.es',
|
||||||
|
'user': 'tatvil',
|
||||||
|
'password': 'Eavne,e1m',
|
||||||
|
'database': 'autocompra'
|
||||||
|
}
|
||||||
|
|
||||||
|
TICKETS_DIR = './tickets'
|
||||||
|
|
||||||
|
def extraer_datos_pdf(ruta_pdf):
|
||||||
|
reader = PdfReader(ruta_pdf)
|
||||||
|
texto = ""
|
||||||
|
for page in reader.pages:
|
||||||
|
texto += page.extract_text() + "\n"
|
||||||
|
|
||||||
|
# Fecha
|
||||||
|
match_fecha = re.search(r"(\d{2}/\d{2}/\d{4})", texto)
|
||||||
|
fecha = datetime.strptime(match_fecha.group(1), "%d/%m/%Y") if match_fecha else None
|
||||||
|
|
||||||
|
# Total
|
||||||
|
match_total = re.search(r"TOTAL\s*\(?€?\)?\s*(\d+,\d{2})", texto)
|
||||||
|
total = float(match_total.group(1).replace(",", ".")) if match_total else 0.0
|
||||||
|
|
||||||
|
# Método de pago
|
||||||
|
metodo = "Tarjeta" if "TARJETA" in texto.upper() else "Efectivo"
|
||||||
|
|
||||||
|
# Productos
|
||||||
|
productos = []
|
||||||
|
for linea in texto.splitlines():
|
||||||
|
linea = linea.strip()
|
||||||
|
if re.match(r".*TOTAL.*|TARJETA|IVA|CUOTA|BASE|AUT:|VERIFICADO|N\.C:|IMPORTE|DEVOLUCIONES", linea, re.IGNORECASE):
|
||||||
|
continue
|
||||||
|
|
||||||
|
match = re.match(r"(\d+)\s+(.*?)\s+(\d+,\d{2})\s+(\d+,\d{2})", linea)
|
||||||
|
if match:
|
||||||
|
cantidad = int(match.group(1))
|
||||||
|
nombre = match.group(2).strip().upper()
|
||||||
|
precio_unitario = float(match.group(3).replace(",", "."))
|
||||||
|
precio_total = float(match.group(4).replace(",", "."))
|
||||||
|
productos.append((nombre, cantidad, precio_unitario, precio_total))
|
||||||
|
else:
|
||||||
|
match_simple = re.match(r"1\s+(.*?)\s+(\d+,\d{2})", linea)
|
||||||
|
if match_simple:
|
||||||
|
nombre = match_simple.group(1).strip().upper()
|
||||||
|
precio = float(match_simple.group(2).replace(",", "."))
|
||||||
|
productos.append((nombre, 1, precio, precio))
|
||||||
|
|
||||||
|
return fecha, total, metodo, productos
|
||||||
|
|
||||||
|
def insertar_en_bd(fecha, total, metodo, productos, cursor):
|
||||||
|
cursor.execute(
|
||||||
|
"INSERT INTO tickets (fecha, total, metodo_pago) VALUES (%s, %s, %s)",
|
||||||
|
(fecha, total, metodo)
|
||||||
|
)
|
||||||
|
ticket_id = cursor.lastrowid
|
||||||
|
|
||||||
|
for nombre, cantidad, precio_unitario, precio_total in productos:
|
||||||
|
cursor.execute(
|
||||||
|
"INSERT INTO productos (ticket_id, nombre, cantidad, precio_unitario, precio_total) VALUES (%s, %s, %s, %s, %s)",
|
||||||
|
(ticket_id, nombre, cantidad, precio_unitario, precio_total)
|
||||||
|
)
|
||||||
|
|
||||||
|
def procesar_todos_los_pdfs():
|
||||||
|
conn = mysql.connector.connect(**DB_CONFIG)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
for archivo in os.listdir(TICKETS_DIR):
|
||||||
|
if archivo.lower().endswith(".pdf"):
|
||||||
|
ruta = os.path.join(TICKETS_DIR, archivo)
|
||||||
|
print(f"📄 Procesando: {archivo}")
|
||||||
|
try:
|
||||||
|
fecha, total, metodo, productos = extraer_datos_pdf(ruta)
|
||||||
|
if fecha and productos:
|
||||||
|
insertar_en_bd(fecha, total, metodo, productos, cursor)
|
||||||
|
print(f"✅ Insertado: {len(productos)} productos (fecha {fecha.strftime('%d/%m/%Y')})")
|
||||||
|
else:
|
||||||
|
print(f"⚠️ No se pudo extraer la información de {archivo}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Error procesando {archivo}: {e}")
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
|
print("🎉 Proceso finalizado.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
procesar_todos_los_pdfs()
|
||||||
|
|
@ -0,0 +1,128 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Mi Lista de la Compra</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: sans-serif; max-width: 800px; margin: auto; padding: 1em; }
|
||||||
|
h1, h2 { color: #2c3e50; }
|
||||||
|
textarea { width: 100%; height: 150px; margin-top: 10px; }
|
||||||
|
input[type="text"], input[type="number"], input[type="file"] { width: 100%; padding: 5px; margin: 5px 0; }
|
||||||
|
button { padding: 10px; margin-top: 10px; cursor: pointer; }
|
||||||
|
ul { list-style: none; padding: 0; }
|
||||||
|
li { margin-bottom: 5px; }
|
||||||
|
.precio { color: #888; font-size: 0.9em; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Mi Lista de la Compra</h1>
|
||||||
|
|
||||||
|
<h2>Subir ticket PDF</h2>
|
||||||
|
<input type="file" id="ticketPDF" accept="application/pdf">
|
||||||
|
<button onclick="leerPDF()">Procesar ticket</button>
|
||||||
|
<pre id="pdfTexto" style="white-space: pre-wrap; background: #f4f4f4; padding: 10px; display:none;"></pre>
|
||||||
|
|
||||||
|
<h2>Productos frecuentes</h2>
|
||||||
|
<ul id="listaProductos">
|
||||||
|
<li><label><input type="checkbox" value="Leche semi 6x2"> Leche semi 6x2 <span class="precio">(1.76€/L)</span></label></li>
|
||||||
|
<li><label><input type="checkbox" value="Huevos camperos 12"> Huevos camperos 12 <span class="precio">(3.22€)</span></label></li>
|
||||||
|
<li><label><input type="checkbox" value="Yogur griego limón"> Yogur griego limón <span class="precio">(1.70€)</span></label></li>
|
||||||
|
<li><label><input type="checkbox" value="Plátano"> Plátano <span class="precio">(3.20€/kg)</span></label></li>
|
||||||
|
<li><label><input type="checkbox" value="Tomate receta artesana"> Tomate receta artesana <span class="precio">(2.10€)</span></label></li>
|
||||||
|
<li><label><input type="checkbox" value="Tortilla patata cebolla"> Tortilla patata cebolla <span class="precio">(2.60€)</span></label></li>
|
||||||
|
<li><label><input type="checkbox" value="Calabacín verde"> Calabacín verde <span class="precio">(1.30€/kg)</span></label></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Añadir producto manualmente</h2>
|
||||||
|
<input type="text" id="nuevoProducto" placeholder="Nombre del producto">
|
||||||
|
<input type="number" id="precioProducto" placeholder="Precio (€)">
|
||||||
|
<button onclick="agregarProducto()">Añadir</button>
|
||||||
|
|
||||||
|
<h2>Lista generada</h2>
|
||||||
|
<textarea id="listaGenerada" readonly></textarea>
|
||||||
|
<button onclick="generarLista()">Generar Lista</button>
|
||||||
|
<button onclick="copiarLista()">Copiar al portapapeles</button>
|
||||||
|
|
||||||
|
<h2>Predicción de productos</h2>
|
||||||
|
<button onclick="predecirProductos()">Sugerir productos que podrías necesitar</button>
|
||||||
|
<ul id="predicciones"></ul>
|
||||||
|
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.10.377/pdf.min.js"></script>
|
||||||
|
<script>
|
||||||
|
const historial = {
|
||||||
|
"Leche semi 6x2": ["2025-04-03", "2025-04-10", "2025-04-16"],
|
||||||
|
"Huevos camperos 12": ["2025-04-03", "2025-04-10"],
|
||||||
|
"Yogur griego limón": ["2025-04-03", "2025-04-10"],
|
||||||
|
"Plátano": ["2025-04-03", "2025-04-10", "2025-04-16"],
|
||||||
|
"Tomate receta artesana": ["2025-04-03", "2025-04-10", "2025-04-16"],
|
||||||
|
"Tortilla patata cebolla": ["2025-04-03", "2025-04-10", "2025-04-16"],
|
||||||
|
"Calabacín verde": ["2025-04-03", "2025-04-10", "2025-04-16"]
|
||||||
|
};
|
||||||
|
|
||||||
|
function agregarProducto() {
|
||||||
|
const nombre = document.getElementById('nuevoProducto').value.trim();
|
||||||
|
const precio = document.getElementById('precioProducto').value.trim();
|
||||||
|
if (!nombre) return;
|
||||||
|
const li = document.createElement('li');
|
||||||
|
li.innerHTML = `<label><input type="checkbox" value="${nombre}"> ${nombre} <span class="precio">(${precio ? precio + '€' : ''})</span></label>`;
|
||||||
|
document.getElementById('listaProductos').appendChild(li);
|
||||||
|
document.getElementById('nuevoProducto').value = '';
|
||||||
|
document.getElementById('precioProducto').value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function generarLista() {
|
||||||
|
const checks = document.querySelectorAll('#listaProductos input[type="checkbox"]');
|
||||||
|
let seleccionados = [];
|
||||||
|
checks.forEach(chk => { if (chk.checked) seleccionados.push(chk.value); });
|
||||||
|
document.getElementById('listaGenerada').value = seleccionados.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
function copiarLista() {
|
||||||
|
const txt = document.getElementById('listaGenerada');
|
||||||
|
txt.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
alert('Lista copiada al portapapeles.');
|
||||||
|
}
|
||||||
|
|
||||||
|
function predecirProductos() {
|
||||||
|
const predicciones = document.getElementById('predicciones');
|
||||||
|
predicciones.innerHTML = '';
|
||||||
|
const hoy = new Date("2025-04-18");
|
||||||
|
Object.keys(historial).forEach(producto => {
|
||||||
|
const fechas = historial[producto].map(f => new Date(f));
|
||||||
|
fechas.sort((a, b) => b - a);
|
||||||
|
if (fechas.length >= 2) {
|
||||||
|
const ultima = fechas[0];
|
||||||
|
const anterior = fechas[1];
|
||||||
|
const diff = (ultima - anterior) / (1000 * 60 * 60 * 24);
|
||||||
|
const diasDesdeUltima = (hoy - ultima) / (1000 * 60 * 60 * 24);
|
||||||
|
if (diasDesdeUltima >= diff - 1) {
|
||||||
|
const li = document.createElement('li');
|
||||||
|
li.textContent = `Puede que necesites: ${producto}`;
|
||||||
|
predicciones.appendChild(li);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function leerPDF() {
|
||||||
|
const archivo = document.getElementById('ticketPDF').files[0];
|
||||||
|
if (!archivo) return;
|
||||||
|
|
||||||
|
const pdfData = await archivo.arrayBuffer();
|
||||||
|
const pdf = await pdfjsLib.getDocument({ data: pdfData }).promise;
|
||||||
|
let texto = '';
|
||||||
|
|
||||||
|
for (let i = 1; i <= pdf.numPages; i++) {
|
||||||
|
const page = await pdf.getPage(i);
|
||||||
|
const content = await page.getTextContent();
|
||||||
|
texto += content.items.map(item => item.str).join(' ') + '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
const salida = document.getElementById('pdfTexto');
|
||||||
|
salida.style.display = 'block';
|
||||||
|
salida.textContent = texto.trim();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,146 @@
|
||||||
|
<table border="1" class="dataframe">
|
||||||
|
<thead>
|
||||||
|
<tr style="text-align: right;">
|
||||||
|
<th>producto</th>
|
||||||
|
<th>cantidad</th>
|
||||||
|
<th>precio_unitario</th>
|
||||||
|
<th>precio_total</th>
|
||||||
|
<th>proxima_compra</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>HUEVOS CAMPEROS</td>
|
||||||
|
<td>112</td>
|
||||||
|
<td>0.02</td>
|
||||||
|
<td>2.72</td>
|
||||||
|
<td>2025-01-28 22:17:08.571428568</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>HUEVOS CAMPEROS</td>
|
||||||
|
<td>112</td>
|
||||||
|
<td>0.02</td>
|
||||||
|
<td>2.72</td>
|
||||||
|
<td>2025-02-03 22:17:08.571428568</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>ESTACIONES</td>
|
||||||
|
<td>14</td>
|
||||||
|
<td>0.16</td>
|
||||||
|
<td>2.20</td>
|
||||||
|
<td>2025-02-12 12:00:00.000000000</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>HUEVOS CAMPEROS</td>
|
||||||
|
<td>112</td>
|
||||||
|
<td>0.02</td>
|
||||||
|
<td>2.72</td>
|
||||||
|
<td>2025-02-12 22:17:08.571428568</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>HUEVOS CAMPEROS</td>
|
||||||
|
<td>26</td>
|
||||||
|
<td>1.65</td>
|
||||||
|
<td>3.30</td>
|
||||||
|
<td>2025-02-18 22:17:08.571428568</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>ESTACIONES</td>
|
||||||
|
<td>14</td>
|
||||||
|
<td>0.16</td>
|
||||||
|
<td>2.20</td>
|
||||||
|
<td>2025-02-19 12:00:00.000000000</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>HUEVOS CAMPEROS</td>
|
||||||
|
<td>112</td>
|
||||||
|
<td>0.02</td>
|
||||||
|
<td>2.72</td>
|
||||||
|
<td>2025-02-25 22:17:08.571428568</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>HUEVOS CAMPEROS</td>
|
||||||
|
<td>112</td>
|
||||||
|
<td>0.02</td>
|
||||||
|
<td>2.72</td>
|
||||||
|
<td>2025-03-04 22:17:08.571428568</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>HUEVOS CAMPEROS</td>
|
||||||
|
<td>112</td>
|
||||||
|
<td>0.03</td>
|
||||||
|
<td>3.22</td>
|
||||||
|
<td>2025-03-10 22:17:08.571428568</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>HUEVOS CAMPEROS</td>
|
||||||
|
<td>112</td>
|
||||||
|
<td>0.03</td>
|
||||||
|
<td>3.22</td>
|
||||||
|
<td>2025-03-17 22:17:08.571428568</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>HUEVOS CAMPEROS</td>
|
||||||
|
<td>112</td>
|
||||||
|
<td>0.03</td>
|
||||||
|
<td>3.22</td>
|
||||||
|
<td>2025-03-25 22:17:08.571428568</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>CORAZON ROMANA</td>
|
||||||
|
<td>11</td>
|
||||||
|
<td>0.10</td>
|
||||||
|
<td>1.10</td>
|
||||||
|
<td>2025-03-26 00:00:00.000000000</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>HUEVOS CAMPEROS</td>
|
||||||
|
<td>112</td>
|
||||||
|
<td>0.03</td>
|
||||||
|
<td>3.22</td>
|
||||||
|
<td>2025-03-31 22:17:08.571428568</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>HUEVOS CAMPEROS</td>
|
||||||
|
<td>16</td>
|
||||||
|
<td>0.12</td>
|
||||||
|
<td>1.90</td>
|
||||||
|
<td>2025-03-31 22:17:08.571428568</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>ESTACIONES</td>
|
||||||
|
<td>24</td>
|
||||||
|
<td>2.20</td>
|
||||||
|
<td>4.40</td>
|
||||||
|
<td>2025-04-08 12:00:00.000000000</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>HUEVOS CAMPEROS</td>
|
||||||
|
<td>112</td>
|
||||||
|
<td>0.03</td>
|
||||||
|
<td>3.22</td>
|
||||||
|
<td>2025-04-08 22:17:08.571428568</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>HUEVOS CAMPEROS</td>
|
||||||
|
<td>112</td>
|
||||||
|
<td>0.03</td>
|
||||||
|
<td>3.22</td>
|
||||||
|
<td>2025-04-15 22:17:08.571428568</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>HUEVOS CAMPEROS</td>
|
||||||
|
<td>16</td>
|
||||||
|
<td>0.12</td>
|
||||||
|
<td>1.90</td>
|
||||||
|
<td>2025-04-15 22:17:08.571428568</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>HUEVOS CAMPEROS</td>
|
||||||
|
<td>112</td>
|
||||||
|
<td>0.03</td>
|
||||||
|
<td>3.22</td>
|
||||||
|
<td>2025-04-21 22:17:08.571428568</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
@ -0,0 +1,492 @@
|
||||||
|
producto,diferencia_dias,proxima_compra_estimado,fecha_estimada_proxima_compra
|
||||||
|
1Champiñón Grande Ban,0.0,2025-04-16 00:00:00.000000000,16/04/2025
|
||||||
|
%,2.0392156862745097,2025-04-18 00:56:28.235294116,18/04/2025
|
||||||
|
1Champiñon Limpio Lam,4.9411764705882355,2025-04-20 22:35:17.647058820,20/04/2025
|
||||||
|
Tarjeta Bancaria,5.777777777777778,2025-04-21 18:40:00.000000001,21/04/2025
|
||||||
|
Total,5.777777777777778,2025-04-21 18:40:00.000000001,21/04/2025
|
||||||
|
Total (€),5.777777777777778,2025-04-21 18:40:00.000000001,21/04/2025
|
||||||
|
1Q. Lonchas Light,6.0,2025-04-22 00:00:00.000000000,22/04/2025
|
||||||
|
1+Prot Pud Caramelo,6.933333333333334,2025-04-22 22:23:59.999999997,22/04/2025
|
||||||
|
1+Prot Natilla Vaini,6.933333333333334,2025-04-22 22:23:59.999999997,22/04/2025
|
||||||
|
1Girasoles Quesos,7.0,2025-04-23 00:00:00.000000000,23/04/2025
|
||||||
|
1Q. Lonchas Cremoso,7.0,2025-04-23 00:00:00.000000000,23/04/2025
|
||||||
|
1Frankfurt Viena Pavo,7.0,2025-04-23 00:00:00.000000000,23/04/2025
|
||||||
|
1Yogur Limon,7.0,2025-04-23 00:00:00.000000000,23/04/2025
|
||||||
|
1Griego Natural,7.0,2025-04-23 00:00:00.000000000,23/04/2025
|
||||||
|
14 Estaciones,7.0,2025-04-23 00:00:00.000000000,23/04/2025
|
||||||
|
1+Prot Natilla Choco,7.0,2025-04-23 00:00:00.000000000,23/04/2025
|
||||||
|
Fritada Pisto,7.0,2025-04-23 00:00:00.000000000,23/04/2025
|
||||||
|
112 Huevos Camperos,7.545454545454546,2025-04-23 13:05:27.272727268,23/04/2025
|
||||||
|
1Cebolla Dulce,8.0,2025-04-24 00:00:00.000000000,24/04/2025
|
||||||
|
1Burger Espinacas,8.0,2025-04-24 00:00:00.000000000,24/04/2025
|
||||||
|
1Uva Roja S/S,8.0,2025-04-24 00:00:00.000000000,24/04/2025
|
||||||
|
1Pan M. 55% Centeno,8.666666666666666,2025-04-24 16:00:00.000000002,24/04/2025
|
||||||
|
Griego Limón,8.818181818181818,2025-04-24 19:38:10.909090907,24/04/2025
|
||||||
|
1Queso Cheddar Loncha,8.857142857142858,2025-04-24 20:34:17.142857146,24/04/2025
|
||||||
|
1Pan M 35% Avena,8.909090909090908,2025-04-24 21:49:05.454545453,24/04/2025
|
||||||
|
1Cebolla Tubo,9.0,2025-04-25 00:00:00.000000000,25/04/2025
|
||||||
|
1Zumo Fresco 1/2 L,9.7,2025-04-25 16:48:00.000000000,25/04/2025
|
||||||
|
1Q Rallado Fundir,10.0,2025-04-26 00:00:00.000000000,26/04/2025
|
||||||
|
1Hummus Clasico,10.25,2025-04-26 06:00:00.000000000,26/04/2025
|
||||||
|
Rebuenas,10.375,2025-04-26 09:00:00.000000000,26/04/2025
|
||||||
|
Griego Natural,10.4,2025-04-26 09:36:00.000000000,26/04/2025
|
||||||
|
1Mantequilla Past.Con,10.5,2025-04-26 12:00:00.000000000,26/04/2025
|
||||||
|
Café Cleche D.Gusto,10.88888888888889,2025-04-26 21:20:00.000000000,26/04/2025
|
||||||
|
1Yogur Sabor Coco,10.88888888888889,2025-04-26 21:20:00.000000000,26/04/2025
|
||||||
|
1Tortilla Pat C/Ceb,11.25,2025-04-27 06:00:00.000000000,27/04/2025
|
||||||
|
1Patata Roja,11.857142857142858,2025-04-27 20:34:17.142857146,27/04/2025
|
||||||
|
1Zanahoria Bolsa,12.0,2025-04-28 00:00:00.000000000,28/04/2025
|
||||||
|
1Queso Vaca Havarti,12.25,2025-04-28 06:00:00.000000000,28/04/2025
|
||||||
|
Yogur Limon,12.25,2025-04-28 06:00:00.000000000,28/04/2025
|
||||||
|
Croissant Rell Cacao,12.4,2025-04-28 09:36:00.000000000,28/04/2025
|
||||||
|
1Lenteja,13.0,2025-04-29 00:00:00.000000000,29/04/2025
|
||||||
|
1Choc 85% Cacao,13.5,2025-04-29 12:00:00.000000000,29/04/2025
|
||||||
|
1Limpiador Dentadura,13.5,2025-04-29 12:00:00.000000000,29/04/2025
|
||||||
|
1Seta Laminada,13.75,2025-04-29 18:00:00.000000000,29/04/2025
|
||||||
|
Tom. Receta Artesana,13.857142857142858,2025-04-29 20:34:17.142857146,29/04/2025
|
||||||
|
1Queso Cottage,14.0,2025-04-30 00:00:00.000000000,30/04/2025
|
||||||
|
1Judia Redonda 500 G,14.0,2025-04-30 00:00:00.000000000,30/04/2025
|
||||||
|
1Griego Limón,14.0,2025-04-30 00:00:00.000000000,30/04/2025
|
||||||
|
1Tiburon,14.0,2025-04-30 00:00:00.000000000,30/04/2025
|
||||||
|
Digestive Avena Choc,14.0,2025-04-30 00:00:00.000000000,30/04/2025
|
||||||
|
1Papel Higienico 4 Ca,14.5,2025-04-30 12:00:00.000000000,30/04/2025
|
||||||
|
Leche Semi P6,14.857142857142858,2025-04-30 20:34:17.142857146,30/04/2025
|
||||||
|
1Caldo Verduras 12 P.,15.0,2025-05-01 00:00:00.000000000,01/05/2025
|
||||||
|
1Choco Gotas Fundir,15.0,2025-05-01 00:00:00.000000000,01/05/2025
|
||||||
|
16 Huevos Camperos,15.0,2025-05-01 00:00:00.000000000,01/05/2025
|
||||||
|
1Gall Digestive,15.5,2025-05-01 12:00:00.000000000,01/05/2025
|
||||||
|
1Nuez Natural,15.666666666666666,2025-05-01 16:00:00.000000002,01/05/2025
|
||||||
|
1Judía Plana 750 Gr,16.0,2025-05-02 00:00:00.000000000,02/05/2025
|
||||||
|
1Calabaza Trozos,16.333333333333332,2025-05-02 07:59:59.999999997,02/05/2025
|
||||||
|
Patata Roja,16.333333333333332,2025-05-02 07:59:59.999999997,02/05/2025
|
||||||
|
Cafe Molido Natural,16.6,2025-05-02 14:24:00.000000000,02/05/2025
|
||||||
|
1Cuidacol Natural,17.0,2025-05-03 00:00:00.000000000,03/05/2025
|
||||||
|
1Solomillo Añojo,17.5,2025-05-03 12:00:00.000000000,03/05/2025
|
||||||
|
1Esp Verde Fino,18.0,2025-05-04 00:00:00.000000000,04/05/2025
|
||||||
|
1Zanahoria 500 G,18.2,2025-05-04 04:48:00.000000000,04/05/2025
|
||||||
|
1Tofu,18.333333333333332,2025-05-04 07:59:59.999999997,04/05/2025
|
||||||
|
1Digestive Avena Choc,18.333333333333332,2025-05-04 07:59:59.999999997,04/05/2025
|
||||||
|
1Mandarina 2 Kg,19.4,2025-05-05 09:36:00.000000000,05/05/2025
|
||||||
|
1Crema Tex-Mex,20.75,2025-05-06 18:00:00.000000000,06/05/2025
|
||||||
|
1Pan De Pueblo,21.0,2025-05-07 00:00:00.000000000,07/05/2025
|
||||||
|
Mozzarella Fresca,21.0,2025-05-07 00:00:00.000000000,07/05/2025
|
||||||
|
1Burger Berenjena,21.0,2025-05-07 00:00:00.000000000,07/05/2025
|
||||||
|
1Pan 12 Cereal/Semill,21.0,2025-05-07 00:00:00.000000000,07/05/2025
|
||||||
|
Plátano Manzana 120G,22.0,2025-05-08 00:00:00.000000000,08/05/2025
|
||||||
|
Fresa Arándanos Aven,22.0,2025-05-08 00:00:00.000000000,08/05/2025
|
||||||
|
Fresa Platano 120G,22.0,2025-05-08 00:00:00.000000000,08/05/2025
|
||||||
|
1Mezcla 4 Quesos,22.666666666666668,2025-05-08 16:00:00.000000002,08/05/2025
|
||||||
|
1Pistacho Tost 0% Sal,23.333333333333332,2025-05-09 07:59:59.999999997,09/05/2025
|
||||||
|
1Gall Digestive Avena,24.0,2025-05-10 00:00:00.000000000,10/05/2025
|
||||||
|
1Pan Campeon Mundo,24.25,2025-05-10 06:00:00.000000000,10/05/2025
|
||||||
|
1Q Mitad Semi,24.5,2025-05-10 12:00:00.000000000,10/05/2025
|
||||||
|
1Margarina Con Sal,24.5,2025-05-10 12:00:00.000000000,10/05/2025
|
||||||
|
1Pan Moño,25.0,2025-05-11 00:00:00.000000000,11/05/2025
|
||||||
|
1Ajo Seco 250 G,25.666666666666668,2025-05-11 16:00:00.000000002,11/05/2025
|
||||||
|
1Croissant Rell Cacao,26.0,2025-05-12 00:00:00.000000000,12/05/2025
|
||||||
|
Soja Con Chocolate,27.0,2025-05-13 00:00:00.000000000,13/05/2025
|
||||||
|
Burger Berenjena,27.0,2025-05-13 00:00:00.000000000,13/05/2025
|
||||||
|
"0,400 Kg 2,10 €/Kg",27.0,2025-05-13 00:00:00.000000000,13/05/2025
|
||||||
|
1Gall Digestive Choco,27.5,2025-05-13 12:00:00.000000000,13/05/2025
|
||||||
|
1Dátil Sin Hueso,27.5,2025-05-13 12:00:00.000000000,13/05/2025
|
||||||
|
1Leche Semi Calcio,28.0,2025-05-14 00:00:00.000000000,14/05/2025
|
||||||
|
1Uva Blanca S/Sem,28.0,2025-05-14 00:00:00.000000000,14/05/2025
|
||||||
|
1+ Proteínas Flan,28.0,2025-05-14 00:00:00.000000000,14/05/2025
|
||||||
|
1Cebolla Roja,28.0,2025-05-14 00:00:00.000000000,14/05/2025
|
||||||
|
1Atun Claro Natural,28.0,2025-05-14 00:00:00.000000000,14/05/2025
|
||||||
|
1Mermelada Fresa,28.0,2025-05-14 00:00:00.000000000,14/05/2025
|
||||||
|
1Queso Emmental Taco,28.666666666666668,2025-05-14 16:00:00.000000002,14/05/2025
|
||||||
|
1Levadura Panaderia,29.0,2025-05-15 00:00:00.000000000,15/05/2025
|
||||||
|
1Seta Bandeja,29.0,2025-05-15 00:00:00.000000000,15/05/2025
|
||||||
|
Arroz Sos,30.333333333333332,2025-05-16 07:59:59.999999997,16/05/2025
|
||||||
|
1Hogaza Centeno 50%,31.0,2025-05-17 00:00:00.000000000,17/05/2025
|
||||||
|
Nata Para Cocinar,33.0,2025-05-19 00:00:00.000000000,19/05/2025
|
||||||
|
1Crema 100% Cacahuete,33.0,2025-05-19 00:00:00.000000000,19/05/2025
|
||||||
|
1Salsa De Soja Salada,34.0,2025-05-20 00:00:00.000000000,20/05/2025
|
||||||
|
1Tortillas Mexicanas,34.0,2025-05-20 00:00:00.000000000,20/05/2025
|
||||||
|
1P. Pav Red. Sal Bipa,35.0,2025-05-21 00:00:00.000000000,21/05/2025
|
||||||
|
1Fresh 0%Alcohol Enj.,35.0,2025-05-21 00:00:00.000000000,21/05/2025
|
||||||
|
1Pañuelo Locion,35.0,2025-05-21 00:00:00.000000000,21/05/2025
|
||||||
|
1Cebolla 2 Kg,35.0,2025-05-21 00:00:00.000000000,21/05/2025
|
||||||
|
1Leche Semi P6,35.0,2025-05-21 00:00:00.000000000,21/05/2025
|
||||||
|
Ravioli Req.Espinaca,35.0,2025-05-21 00:00:00.000000000,21/05/2025
|
||||||
|
Macarron,38.5,2025-05-24 12:00:00.000000000,24/05/2025
|
||||||
|
Caramelo Eucaliptus,38.5,2025-05-24 12:00:00.000000000,24/05/2025
|
||||||
|
1Croissant,41.0,2025-05-27 00:00:00.000000000,27/05/2025
|
||||||
|
Fresh 0%Alcohol Enj.,41.5,2025-05-27 12:00:00.000000000,27/05/2025
|
||||||
|
1Miel De Flores Kg,42.0,2025-05-28 00:00:00.000000000,28/05/2025
|
||||||
|
1Azúcar Moreno 1Kg,42.0,2025-05-28 00:00:00.000000000,28/05/2025
|
||||||
|
1Mozzarella Fresca,42.0,2025-05-28 00:00:00.000000000,28/05/2025
|
||||||
|
Medialunas Calabaza,42.0,2025-05-28 00:00:00.000000000,28/05/2025
|
||||||
|
1Batido Cacao Energy,42.0,2025-05-28 00:00:00.000000000,28/05/2025
|
||||||
|
Azucar,42.0,2025-05-28 00:00:00.000000000,28/05/2025
|
||||||
|
Leche Semi Calcio,55.0,2025-06-10 00:00:00.000000000,10/06/2025
|
||||||
|
Tortilla Pat C/Ceb,55.0,2025-06-10 00:00:00.000000000,10/06/2025
|
||||||
|
1Cacao Puro 0%,55.0,2025-06-10 00:00:00.000000000,10/06/2025
|
||||||
|
1Espinaca Picada,56.0,2025-06-11 00:00:00.000000000,11/06/2025
|
||||||
|
1Pan Sem Y P Calabaza,56.0,2025-06-11 00:00:00.000000000,11/06/2025
|
||||||
|
11 Corazon Romana,62.0,2025-06-17 00:00:00.000000000,17/06/2025
|
||||||
|
1Caramelos Lima 0%,62.0,2025-06-17 00:00:00.000000000,17/06/2025
|
||||||
|
1Pan Tostado Clasico,62.0,2025-06-17 00:00:00.000000000,17/06/2025
|
||||||
|
1Galleta Canela,69.0,2025-06-24 00:00:00.000000000,24/06/2025
|
||||||
|
Spaghetti,70.0,2025-06-25 00:00:00.000000000,25/06/2025
|
||||||
|
1Salsa Fresca Quesos,70.0,2025-06-25 00:00:00.000000000,25/06/2025
|
||||||
|
1Vela Te Chai,76.0,2025-07-01 00:00:00.000000000,01/07/2025
|
||||||
|
1Champu Extra Suave,76.0,2025-07-01 00:00:00.000000000,01/07/2025
|
||||||
|
1Grana Padano Escamas,83.0,2025-07-08 00:00:00.000000000,08/07/2025
|
||||||
|
1Coca-Cola 12 Latas,84.0,2025-07-09 00:00:00.000000000,09/07/2025
|
||||||
|
1Vaselina Aroma Framb,84.0,2025-07-09 00:00:00.000000000,09/07/2025
|
||||||
|
1Nidos Al Huevo,97.0,2025-07-22 00:00:00.000000000,22/07/2025
|
||||||
|
1Spaghetti,104.0,2025-07-29 00:00:00.000000000,29/07/2025
|
||||||
|
"0,064 Kg 5,00 €/Kg",,,
|
||||||
|
"0,190 Kg 2,70 €/Kg",,,
|
||||||
|
"0,202 Kg 2,39 €/Kg",,,
|
||||||
|
"0,220 Kg 2,90 €/Kg",,,
|
||||||
|
"0,234 Kg 3,40 €/Kg",,,
|
||||||
|
"0,242 Kg 2,29 €/Kg",,,
|
||||||
|
"0,242 Kg 3,40 €/Kg",,,
|
||||||
|
"0,242 Kg 5,10 €/Kg",,,
|
||||||
|
"0,250 Kg 2,70 €/Kg",,,
|
||||||
|
"0,258 Kg 5,00 €/Kg",,,
|
||||||
|
"0,260 Kg 5,10 €/Kg",,,
|
||||||
|
"0,272 Kg 2,39 €/Kg",,,
|
||||||
|
"0,324 Kg 1,94 €/Kg",,,
|
||||||
|
"0,336 Kg 2,19 €/Kg",,,
|
||||||
|
"0,352 Kg 2,45 €/Kg",,,
|
||||||
|
"0,354 Kg 3,16 €/Kg",,,
|
||||||
|
"0,354 Kg 5,10 €/Kg",,,
|
||||||
|
"0,356 Kg 1,94 €/Kg",,,
|
||||||
|
"0,358 Kg 2,45 €/Kg",,,
|
||||||
|
"0,390 Kg 5,00 €/Kg",,,
|
||||||
|
"0,402 Kg 2,19 €/Kg",,,
|
||||||
|
"0,410 Kg 2,10 €/Kg",,,
|
||||||
|
"0,488 Kg 2,70 €/Kg",,,
|
||||||
|
"0,504 Kg 2,19 €/Kg",,,
|
||||||
|
"0,512 Kg 3,16 €/Kg",,,
|
||||||
|
"0,518 Kg 2,19 €/Kg",,,
|
||||||
|
"0,552 Kg 2,55 €/Kg",,,
|
||||||
|
"0,610 Kg 2,19 €/Kg",,,
|
||||||
|
"0,632 Kg 5,00 €/Kg",,,
|
||||||
|
"0,668 Kg 3,20 €/Kg",,,
|
||||||
|
"0,678 Kg 1,75 €/Kg",,,
|
||||||
|
"0,688 Kg 2,29 €/Kg",,,
|
||||||
|
"0,694 Kg 1,94 €/Kg",,,
|
||||||
|
"0,694 Kg 2,05 €/Kg",,,
|
||||||
|
"0,694 Kg 2,29 €/Kg",,,
|
||||||
|
"0,696 Kg 2,29 €/Kg",,,
|
||||||
|
"0,730 Kg 2,59 €/Kg",,,
|
||||||
|
"0,746 Kg 3,20 €/Kg",,,
|
||||||
|
"0,748 Kg 2,29 €/Kg",,,
|
||||||
|
"0,802 Kg 2,29 €/Kg",,,
|
||||||
|
"0,806 Kg 2,20 €/Kg",,,
|
||||||
|
"0,830 Kg 2,60 €/Kg",,,
|
||||||
|
"0,872 Kg 1,75 €/Kg",,,
|
||||||
|
"0,874 Kg 1,65 €/Kg",,,
|
||||||
|
"0,882 Kg 2,60 €/Kg",,,
|
||||||
|
"0,884 Kg 1,75 €/Kg",,,
|
||||||
|
"0,888 Kg 2,28 €/Kg",,,
|
||||||
|
"0,896 Kg 2,90 €/Kg",,,
|
||||||
|
"0,914 Kg 2,60 €/Kg",,,
|
||||||
|
"0,916 Kg 2,59 €/Kg",,,
|
||||||
|
"0,918 Kg 2,90 €/Kg",,,
|
||||||
|
"0,922 Kg 2,14 €/Kg",,,
|
||||||
|
"0,936 Kg 2,00 €/Kg",,,
|
||||||
|
"0,948 Kg 1,89 €/Kg",,,
|
||||||
|
"0,952 Kg 2,10 €/Kg",,,
|
||||||
|
"0,954 Kg 2,05 €/Kg",,,
|
||||||
|
"0,958 Kg 1,31 €/Kg",,,
|
||||||
|
"0,960 Kg 2,90 €/Kg",,,
|
||||||
|
"0,984 Kg 2,59 €/Kg",,,
|
||||||
|
"0,986 Kg 1,89 €/Kg",,,
|
||||||
|
"0,988 Kg 2,00 €/Kg",,,
|
||||||
|
"0,990 Kg 1,75 €/Kg",,,
|
||||||
|
"0,992 Kg 1,75 €/Kg",,,
|
||||||
|
1+ Proteínas Fresa,,,
|
||||||
|
1+Prot Pla-Açaí,,,
|
||||||
|
1+Proteinas Stracciat,,,
|
||||||
|
"1,012 Kg 2,59 €/Kg",,,
|
||||||
|
"1,032 Kg 2,59 €/Kg",,,
|
||||||
|
"1,048 Kg 2,60 €/Kg",,,
|
||||||
|
"1,054 Kg 2,90 €/Kg",,,
|
||||||
|
"1,056 Kg 2,14 €/Kg",,,
|
||||||
|
"1,084 Kg 1,59 €/Kg",,,
|
||||||
|
"1,094 Kg 1,75 €/Kg",,,
|
||||||
|
"1,128 Kg 1,99 €/Kg",,,
|
||||||
|
"1,130 Kg 1,39 €/Kg",,,
|
||||||
|
"1,150 Kg 1,75 €/Kg",,,
|
||||||
|
"1,154 Kg 2,29 €/Kg",,,
|
||||||
|
"1,158 Kg 2,05 €/Kg",,,
|
||||||
|
"1,168 Kg 2,59 €/Kg",,,
|
||||||
|
"1,198 Kg 2,00 €/Kg",,,
|
||||||
|
"1,212 Kg 2,00 €/Kg",,,
|
||||||
|
"1,226 Kg 2,29 €/Kg",,,
|
||||||
|
"1,244 Kg 2,00 €/Kg",,,
|
||||||
|
"1,254 Kg 1,75 €/Kg",,,
|
||||||
|
"1,306 Kg 2,14 €/Kg",,,
|
||||||
|
"1,314 Kg 1,30 €/Kg",,,
|
||||||
|
"1,328 Kg 2,00 €/Kg",,,
|
||||||
|
"1,334 Kg 2,29 €/Kg",,,
|
||||||
|
"1,336 Kg 1,75 €/Kg",,,
|
||||||
|
"1,356 Kg 2,14 €/Kg",,,
|
||||||
|
"1,382 Kg 2,59 €/Kg",,,
|
||||||
|
"1,418 Kg 1,75 €/Kg",,,
|
||||||
|
"1,442 Kg 2,15 €/Kg",,,
|
||||||
|
"1,452 Kg 2,70 €/Kg",,,
|
||||||
|
"1,490 Kg 3,20 €/Kg",,,
|
||||||
|
"1,592 Kg 1,99 €/Kg",,,
|
||||||
|
"1,626 Kg 2,14 €/Kg",,,
|
||||||
|
"1,664 Kg 2,59 €/Kg",,,
|
||||||
|
"1,714 Kg 1,99 €/Kg",,,
|
||||||
|
"1,722 Kg 2,14 €/Kg",,,
|
||||||
|
"1,740 Kg 2,95 €/Kg",,,
|
||||||
|
"1,788 Kg 3,20 €/Kg",,,
|
||||||
|
"1,978 Kg 2,45 €/Kg",,,
|
||||||
|
1200 Servil. Blancas,,,
|
||||||
|
"13 Recipientes 0,5L",,,
|
||||||
|
1A. Negras S/Hueso,,,
|
||||||
|
1Aceite De Girasol,,,
|
||||||
|
1Aceite Ricino,,,
|
||||||
|
1Aceite V.Extra,,,
|
||||||
|
1Aceituna Chupadedos,,,
|
||||||
|
1Agua Facial Avena,,,
|
||||||
|
1Agua Oxigenada,,,
|
||||||
|
1Ajo Granulado,,,
|
||||||
|
1Albahaca,,,
|
||||||
|
1Allioli Tarrina,,,
|
||||||
|
1Alpro Skyr Fresa,,,
|
||||||
|
1Ambientador Conc.Del,,,
|
||||||
|
1Anacardo Natural,,,
|
||||||
|
1Apio Verde,,,
|
||||||
|
1Arena Aglomerante,,,
|
||||||
|
1Arroz Albufera,,,
|
||||||
|
1Atún Claro Oliva Pk6,,,
|
||||||
|
1Avena Molida,,,
|
||||||
|
1Azucar,,,
|
||||||
|
1Bayeta Microfibra Cr,,,
|
||||||
|
1Beber Fresa,,,
|
||||||
|
1Bicarbonato Kilo,,,
|
||||||
|
1Burrata,,,
|
||||||
|
1C. Eucalipto 0%,,,
|
||||||
|
1C. Vitamina K,,,
|
||||||
|
1Caballa Ft Oliva Bpk,,,
|
||||||
|
1Cacahuete Frito Miel,,,
|
||||||
|
1Café Soluble Classic,,,
|
||||||
|
1Carbon Vegetal,,,
|
||||||
|
1Cebolla Caramelizada,,,
|
||||||
|
1Cepillo Infantil,,,
|
||||||
|
1Cera Depil Cazuela,,,
|
||||||
|
1Cera Mate,,,
|
||||||
|
1Champú Family,,,
|
||||||
|
1Choco Fundir Blanco,,,
|
||||||
|
1Choco Fundir Negro,,,
|
||||||
|
1Chocodays,,,
|
||||||
|
1Chía,,,
|
||||||
|
1Cocktail Natural,,,
|
||||||
|
1Cola Cao,,,
|
||||||
|
1Comprimidos Dormir,,,
|
||||||
|
1Conos,,,
|
||||||
|
1Contra Des Certifica,,,
|
||||||
|
1Cortauñas,,,
|
||||||
|
1Cous Cous,,,
|
||||||
|
1Crema Avellana Cacao,,,
|
||||||
|
1Crema De Verduras,,,
|
||||||
|
1Cristales Multiusos,,,
|
||||||
|
1Cuidacol Fresa,,,
|
||||||
|
1Cápsulas Valeriana,,,
|
||||||
|
1Det Blanca Y Color,,,
|
||||||
|
1Det Polvo Mano,,,
|
||||||
|
1Doritos Bits,,,
|
||||||
|
1Encendedor Mini,,,
|
||||||
|
1Esponja Vegetal,,,
|
||||||
|
1Extrait De Ámbar,,,
|
||||||
|
1Fanta Limón X9,,,
|
||||||
|
1Fideo Cabello,,,
|
||||||
|
1Filete Salmón,,,
|
||||||
|
1Fingers De Queso,,,
|
||||||
|
1Fluido Hidrat Oil Fr,,,
|
||||||
|
1Fosforos Madera Gran,,,
|
||||||
|
1Galleta Rell Crema,,,
|
||||||
|
1Galletas Mantequilla,,,
|
||||||
|
1Garbanzo Pedrosillan,,,
|
||||||
|
1Gel Hidroalcohólico,,,
|
||||||
|
1Gel Hidrotermal,,,
|
||||||
|
1Gel Limpiador Facial,,,
|
||||||
|
1Gel Wc C/ Lejía,,,
|
||||||
|
1Gnocchi Quesos,,,
|
||||||
|
1Golosinas Mix Azúcar,,,
|
||||||
|
1Grana Padano Rallado,,,
|
||||||
|
1Guisante Fino,,,
|
||||||
|
1Guisantes,,,
|
||||||
|
1Hamburguesa,,,
|
||||||
|
1Hamburguesa De Lomo,,,
|
||||||
|
1Harina De Trigo 5Kg.,,,
|
||||||
|
1Helices Lenteja Roja,,,
|
||||||
|
1Higienico Doble Roll,,,
|
||||||
|
1Humus Kalamata,,,
|
||||||
|
1Impulsor,,,
|
||||||
|
1Infusion Dormir,,,
|
||||||
|
1Infusion Respir,,,
|
||||||
|
1Kéfir Vegetal Coco,,,
|
||||||
|
1Kétchup,,,
|
||||||
|
1Lasaña Precocida,,,
|
||||||
|
1Leche Entera P6,,,
|
||||||
|
1Leche Sem S/Lact P6,,,
|
||||||
|
1Limpiador Conc. Aloe,,,
|
||||||
|
1Limpiador Vitro,,,
|
||||||
|
1Loción Hidrata,,,
|
||||||
|
1Lote 3 Bayetas Peq.,,,
|
||||||
|
1Lote Cepillo Oral B,,,
|
||||||
|
1Ls Albondigas Patata,,,
|
||||||
|
1Lágrimas Hidratantes,,,
|
||||||
|
1M.Croiss Mantequilla,,,
|
||||||
|
1Manzana Golden Bolsa,,,
|
||||||
|
1Marias Doradas,,,
|
||||||
|
1Mascarilla Natural,,,
|
||||||
|
1Melocotón-Uva P6,,,
|
||||||
|
1Merm. Melocotón,,,
|
||||||
|
1Mermelada Albaricoqu,,,
|
||||||
|
1Mermelada Higo,,,
|
||||||
|
1Miel De Flores Dosif,,,
|
||||||
|
1Mini Taquitos Jamon,,,
|
||||||
|
1Mixtura Can/Jilguero,,,
|
||||||
|
"1Molde 1,5",,,
|
||||||
|
1Mousse Protein Choco,,,
|
||||||
|
1Muesli C/Chocolate,,,
|
||||||
|
1Multiusos,,,
|
||||||
|
1Nachos,,,
|
||||||
|
1Naranja 5 Kg.,,,
|
||||||
|
1Naranja Malla 3 Kg,,,
|
||||||
|
1Nata Para Cocinar,,,
|
||||||
|
1Negro 99% Cacao,,,
|
||||||
|
1Orégano,,,
|
||||||
|
1Pajaritas Vegetales,,,
|
||||||
|
1Palillos Redondos,,,
|
||||||
|
1Pan Blanco,,,
|
||||||
|
1Pan De Leche,,,
|
||||||
|
1Pan M. Brioche,,,
|
||||||
|
1Pan Pita,,,
|
||||||
|
1Pan Torrijas,,,
|
||||||
|
1Papel Vegetal 30H,,,
|
||||||
|
1Pastilla Cisterna,,,
|
||||||
|
1Patata Guarn. 1 Kg.,,,
|
||||||
|
1Patatas Corte Grueso,,,
|
||||||
|
1Pañuelo Caja,,,
|
||||||
|
1Pechuga De Pollo 92%,,,
|
||||||
|
1Perejil,,,
|
||||||
|
1Piadina 4 Unidades,,,
|
||||||
|
1Pimiento Semipicante,,,
|
||||||
|
1Pimiento Trico,,,
|
||||||
|
1Piña S/Az Añadido P6,,,
|
||||||
|
1Plumero Atra.3 Reca,,,
|
||||||
|
1Polvo Talco,,,
|
||||||
|
1Posavajillas,,,
|
||||||
|
1Pringles Onion,,,
|
||||||
|
1Pringles Original,,,
|
||||||
|
1Proteina 0% Natural,,,
|
||||||
|
1Q Rallado Emmental,,,
|
||||||
|
1Q. Lonchas Edam Tier,,,
|
||||||
|
1Q. Maasdam Tierno,,,
|
||||||
|
1Q. Untar Suave,,,
|
||||||
|
1Queso Blanco Natural,,,
|
||||||
|
1Queso Feta,,,
|
||||||
|
1Queso Fresco Burgos,,,
|
||||||
|
1Queso Gouda Lonchas,,,
|
||||||
|
1Queso Rallado Pizza,,,
|
||||||
|
1Queso Rallado Polvo,,,
|
||||||
|
1Recambio Antihumedad,,,
|
||||||
|
1Regaña,,,
|
||||||
|
1Remolacha En Tiras,,,
|
||||||
|
1Repollo Liso Partido,,,
|
||||||
|
1Repollo Rizado P,,,
|
||||||
|
1Roja Acidulce Bolsa,,,
|
||||||
|
1Rollo Hogar Doble,,,
|
||||||
|
1Ron Añejo,,,
|
||||||
|
1Rooibos Citrico,,,
|
||||||
|
1Roscón Relleno,,,
|
||||||
|
1Sal Yodada,,,
|
||||||
|
1Salsa Barbacoa,,,
|
||||||
|
1Salsa De Queso,,,
|
||||||
|
1Salsa De Trufa,,,
|
||||||
|
1Salsa Pesto,,,
|
||||||
|
1Salsa Pesto Fresco,,,
|
||||||
|
1Salsa Piri Piri,,,
|
||||||
|
1Salsa Ranchera,,,
|
||||||
|
1Salsa Soja S/Gluten,,,
|
||||||
|
1Sardinillas A.Oliva,,,
|
||||||
|
1Sazonador Pasta,,,
|
||||||
|
1Sirope C. Avellana,,,
|
||||||
|
1Soja Con Chocolate,,,
|
||||||
|
1Soja Mango,,,
|
||||||
|
1Spray Própolis,,,
|
||||||
|
1T. 100%Integrales,,,
|
||||||
|
1Te Matcha,,,
|
||||||
|
1Te Verde Hierbabuena,,,
|
||||||
|
1Tijera Uñas,,,
|
||||||
|
1Tiras Fritas,,,
|
||||||
|
1Toallitas Limpiagafa,,,
|
||||||
|
1Tomate Concentrado,,,
|
||||||
|
1Tortita Maiz C/Choco,,,
|
||||||
|
1Tortitas De Maiz,,,
|
||||||
|
1Tónico Árbol De Té,,,
|
||||||
|
1Vela Cifra 2 V2,,,
|
||||||
|
1Vela Cifra 4 V2,,,
|
||||||
|
1Vela Vainilla,,,
|
||||||
|
1Velas Toffee,,,
|
||||||
|
1Virgen Extra 500,,,
|
||||||
|
1Xuxes Top Ten,,,
|
||||||
|
1Yakisoba Clásico,,,
|
||||||
|
1Yogur Cabra,,,
|
||||||
|
1Yogur Fresa,,,
|
||||||
|
1Zumo Manzana Pack,,,
|
||||||
|
1Árbol Del Té,,,
|
||||||
|
"2,050 Kg 2,45 €/Kg",,,
|
||||||
|
"2,116 Kg 1,75 €/Kg",,,
|
||||||
|
"2,130 Kg 2,95 €/Kg",,,
|
||||||
|
"2,158 Kg 1,99 €/Kg",,,
|
||||||
|
"2,468 Kg 1,99 €/Kg",,,
|
||||||
|
Abrillantador Lavav.,,,
|
||||||
|
Amb C Fru Silvestres,,,
|
||||||
|
Arena Aglomerante,,,
|
||||||
|
Atún Claro Oliva Pk6,,,
|
||||||
|
Azúcar Moreno 1Kg,,,
|
||||||
|
B.Envases C.Fácil,,,
|
||||||
|
B.Mini Papelera,,,
|
||||||
|
Bastoncillo Famil,,,
|
||||||
|
Cacahuete Sin Sal,,,
|
||||||
|
Caldo Verduras 12 P.,,,
|
||||||
|
Cebolla Tierna,,,
|
||||||
|
Cepillo Pack-3 Medio,,,
|
||||||
|
Conejo Choco Leche,,,
|
||||||
|
Crema Calabaza,,,
|
||||||
|
Escobilla Wc,,,
|
||||||
|
Espinaca Cortada,,,
|
||||||
|
Estaciones,,,
|
||||||
|
Fosforos Madera Gran,,,
|
||||||
|
Frieg.Insecticida,,,
|
||||||
|
Fruta Variada 120G,,,
|
||||||
|
Garbanzo Pedrosillan,,,
|
||||||
|
Gnocchi Quesos,,,
|
||||||
|
Golác.Fresa-Plátano,,,
|
||||||
|
Higienico Doble Roll,,,
|
||||||
|
Huevos Camperos,,,
|
||||||
|
Judía Plana 350 Gr,,,
|
||||||
|
Lenteja Pardina,,,
|
||||||
|
Limpiador Wc Polvo,,,
|
||||||
|
Marias Doradas,,,
|
||||||
|
Mascarilla Quirúr.,,,
|
||||||
|
Mezcla 4 Quesos,,,
|
||||||
|
Minis Leche Avellana,,,
|
||||||
|
Noodle Sabor Picante,,,
|
||||||
|
Noodle Sabor Pollo,,,
|
||||||
|
Pastillas Desinfect,,,
|
||||||
|
Pizza Margarita,,,
|
||||||
|
Queso Blanco Natural,,,
|
||||||
|
Refill Dermo,,,
|
||||||
|
Rollo Hogar Doble,,,
|
||||||
|
Salsa Ligera,,,
|
||||||
|
Sardinilla Re. Sal O,,,
|
||||||
|
Tofu,,,
|
||||||
|
Tom.Frito Est.Casero,,,
|
||||||
|
Tortillas Mexicanas,,,
|
||||||
|
Yak Soja Cup,,,
|
||||||
|
Ñoquis De Patata,,,
|
||||||
|
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,187 @@
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Lista de la Compra Semanal</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
background-color: #f4f4f9;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
color: #333;
|
||||||
|
padding-top: 20px;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: 80%;
|
||||||
|
margin: 30px auto;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
padding: 12px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
tr:hover {
|
||||||
|
background-color: #f1f1f1;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>Lista de la Compra Semanal (Próximo Jueves)</h1>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Producto</th>
|
||||||
|
<th>Días Promedio entre Compras</th>
|
||||||
|
<th>Fecha Estimada de la Próxima Compra</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>%</td>
|
||||||
|
<td>2</td>
|
||||||
|
<td>18/04/2025</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>1+Prot Natilla Choco</td>
|
||||||
|
<td>7</td>
|
||||||
|
<td>23/04/2025</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>1+Prot Natilla Vaini</td>
|
||||||
|
<td>7</td>
|
||||||
|
<td>22/04/2025</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>1+Prot Pud Caramelo</td>
|
||||||
|
<td>7</td>
|
||||||
|
<td>22/04/2025</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>112 Huevos Camperos</td>
|
||||||
|
<td>8</td>
|
||||||
|
<td>23/04/2025</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>14 Estaciones</td>
|
||||||
|
<td>7</td>
|
||||||
|
<td>23/04/2025</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>1Burger Espinacas</td>
|
||||||
|
<td>8</td>
|
||||||
|
<td>24/04/2025</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>1Cebolla Dulce</td>
|
||||||
|
<td>8</td>
|
||||||
|
<td>24/04/2025</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>1Champiñon Limpio Lam</td>
|
||||||
|
<td>5</td>
|
||||||
|
<td>20/04/2025</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>1Champiñón Grande Ban</td>
|
||||||
|
<td>0</td>
|
||||||
|
<td>16/04/2025</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>1Frankfurt Viena Pavo</td>
|
||||||
|
<td>7</td>
|
||||||
|
<td>23/04/2025</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>1Girasoles Quesos</td>
|
||||||
|
<td>7</td>
|
||||||
|
<td>23/04/2025</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>1Griego Natural</td>
|
||||||
|
<td>7</td>
|
||||||
|
<td>23/04/2025</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>1Q. Lonchas Cremoso</td>
|
||||||
|
<td>7</td>
|
||||||
|
<td>23/04/2025</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>1Q. Lonchas Light</td>
|
||||||
|
<td>6</td>
|
||||||
|
<td>22/04/2025</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>1Uva Roja S/S</td>
|
||||||
|
<td>8</td>
|
||||||
|
<td>24/04/2025</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>1Yogur Limon</td>
|
||||||
|
<td>7</td>
|
||||||
|
<td>23/04/2025</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>Fritada Pisto</td>
|
||||||
|
<td>7</td>
|
||||||
|
<td>23/04/2025</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>Tarjeta Bancaria</td>
|
||||||
|
<td>6</td>
|
||||||
|
<td>21/04/2025</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>Total</td>
|
||||||
|
<td>6</td>
|
||||||
|
<td>21/04/2025</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>Total (€)</td>
|
||||||
|
<td>6</td>
|
||||||
|
<td>21/04/2025</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
|
@ -0,0 +1,136 @@
|
||||||
|
producto,cantidad_comprada,gasto_total
|
||||||
|
TARJETA BANCARIA,3,480.01
|
||||||
|
TOTAL (€),3,480.01
|
||||||
|
%,9,35.31
|
||||||
|
TOTAL,3,35.31
|
||||||
|
LECHE SEMI P6,2,21.12
|
||||||
|
1ACEITE V.EXTRA,1,15.85
|
||||||
|
1RON AÑEJO,1,15.15
|
||||||
|
1Q MITAD SEMI,1,13.69
|
||||||
|
TOM. RECETA ARTESANA,2,12.600000000000001
|
||||||
|
1PATATA ROJA,3,11.399999999999999
|
||||||
|
MINIS LECHE AVELLANA,1,11.0
|
||||||
|
1COCA-COLA 12 LATAS,1,10.8
|
||||||
|
CAFÉ CLECHE D.GUSTO,1,10.4
|
||||||
|
GRIEGO LIMÓN,3,10.2
|
||||||
|
112 HUEVOS CAMPEROS,3,9.66
|
||||||
|
1EXTRAIT DE ÁMBAR,1,9.0
|
||||||
|
1ZUMO FRESCO 1/2 L,3,8.95
|
||||||
|
LENTEJA PARDINA,1,8.4
|
||||||
|
CONEJO CHOCO LECHE,1,8.0
|
||||||
|
1TORTILLA PAT C/CEB,3,7.800000000000001
|
||||||
|
1UVA ROJA S/S,3,7.62
|
||||||
|
1MANDARINA 2 KG,2,6.79
|
||||||
|
1Q. LONCHAS LIGHT,2,5.8
|
||||||
|
"1,788 KG 3,20 €/KG",1,5.72
|
||||||
|
1+PROT PUD CARAMELO,3,5.699999999999999
|
||||||
|
1CHAMPIÑÓN GRANDE BAN,2,5.52
|
||||||
|
1+PROT NATILLA VAINI,3,5.25
|
||||||
|
1LECHE SEMI P6,1,5.1
|
||||||
|
1BURGER BERENJENA,2,4.8
|
||||||
|
BURGER BERENJENA,1,4.8
|
||||||
|
"1,490 KG 3,20 €/KG",1,4.77
|
||||||
|
1CALABAZA TROZOS,2,4.720000000000001
|
||||||
|
1CARBON VEGETAL,1,4.7
|
||||||
|
1ESP VERDE FINO,2,4.6
|
||||||
|
GRIEGO NATURAL,1,4.54
|
||||||
|
1GRIEGO NATURAL,2,4.54
|
||||||
|
1PAN M. 55% CENTENO,3,4.41
|
||||||
|
MEDIALUNAS CALABAZA,1,4.4
|
||||||
|
1CACAO PURO 0%,1,4.15
|
||||||
|
1QUESO CHEDDAR LONCHA,2,4.1
|
||||||
|
1CHAMPIÑON LIMPIO LAM,2,4.039999999999999
|
||||||
|
1HAMBURGUESA DE LOMO,1,4.0
|
||||||
|
1PAN CAMPEON MUNDO,2,3.98
|
||||||
|
CARAMELO EUCALIPTUS,1,3.9
|
||||||
|
1CEBOLLA 2 KG,1,3.9
|
||||||
|
ARROZ SOS,1,3.76
|
||||||
|
1ARENA AGLOMERANTE,1,3.75
|
||||||
|
1CHOC 85% CACAO,2,3.7
|
||||||
|
1AJO SECO 250 G,2,3.7
|
||||||
|
1Q. MAASDAM TIERNO,1,3.62
|
||||||
|
1SPRAY PRÓPOLIS,1,3.6
|
||||||
|
1PISTACHO TOST 0% SAL,1,3.55
|
||||||
|
"1MOLDE 1,5",1,3.15
|
||||||
|
1QUESO EMMENTAL TACO,1,3.15
|
||||||
|
SOJA CON CHOCOLATE,1,3.1
|
||||||
|
MASCARILLA QUIRÚR.,1,3.0
|
||||||
|
YOGUR LIMON,2,3.0
|
||||||
|
1GALLETAS MANTEQUILLA,1,2.85
|
||||||
|
1JUDIA REDONDA 500 G,1,2.8
|
||||||
|
1GRANA PADANO ESCAMAS,1,2.75
|
||||||
|
1PAN M 35% AVENA,2,2.74
|
||||||
|
"1,048 KG 2,60 €/KG",1,2.72
|
||||||
|
"0,918 KG 2,90 €/KG",1,2.66
|
||||||
|
"0,896 KG 2,90 €/KG",1,2.6
|
||||||
|
1PATATA GUARN. 1 KG.,1,2.55
|
||||||
|
1MINI TAQUITOS JAMON,1,2.55
|
||||||
|
1SIROPE C. AVELLANA,1,2.5
|
||||||
|
1MASCARILLA NATURAL,1,2.5
|
||||||
|
"1,212 KG 2,00 €/KG",1,2.42
|
||||||
|
"1,198 KG 2,00 €/KG",1,2.4
|
||||||
|
"0,746 KG 3,20 €/KG",1,2.39
|
||||||
|
1PAN M. BRIOCHE,1,2.35
|
||||||
|
"0,882 KG 2,60 €/KG",1,2.29
|
||||||
|
1DÁTIL SIN HUESO,1,2.25
|
||||||
|
1M.CROISS MANTEQUILLA,1,2.15
|
||||||
|
"0,668 KG 3,20 €/KG",1,2.14
|
||||||
|
1GNOCCHI QUESOS,1,2.1
|
||||||
|
"0,952 KG 2,10 €/KG",1,2.0
|
||||||
|
1PIADINA 4 UNIDADES,1,1.99
|
||||||
|
1MERMELADA HIGO,1,1.95
|
||||||
|
1YAKISOBA CLÁSICO,1,1.95
|
||||||
|
1CROISSANT RELL CACAO,1,1.95
|
||||||
|
16 HUEVOS CAMPEROS,1,1.9
|
||||||
|
"0,986 KG 1,89 €/KG",1,1.86
|
||||||
|
1AZÚCAR MORENO 1KG,1,1.85
|
||||||
|
"0,802 KG 2,29 €/KG",1,1.84
|
||||||
|
1MEZCLA 4 QUESOS,1,1.83
|
||||||
|
1GRANA PADANO RALLADO,1,1.8
|
||||||
|
1ZANAHORIA 500 G,2,1.8
|
||||||
|
"1,314 KG 1,30 €/KG",1,1.71
|
||||||
|
1CEBOLLA CARAMELIZADA,1,1.7
|
||||||
|
1PAN TOSTADO CLASICO,1,1.7
|
||||||
|
1HELICES LENTEJA ROJA,1,1.66
|
||||||
|
1PAN SEM Y P CALABAZA,1,1.65
|
||||||
|
1SALSA DE QUESO,1,1.65
|
||||||
|
1PAN 12 CEREAL/SEMILL,1,1.61
|
||||||
|
1VASELINA AROMA FRAMB,1,1.6
|
||||||
|
1GALL DIGESTIVE,1,1.6
|
||||||
|
1CREMA TEX-MEX,1,1.6
|
||||||
|
1TOALLITAS LIMPIAGAFA,1,1.6
|
||||||
|
1PAN DE PUEBLO,1,1.58
|
||||||
|
"1,130 KG 1,39 €/KG",1,1.57
|
||||||
|
1MERMELADA FRESA,1,1.55
|
||||||
|
1LEVADURA PANADERIA,1,1.5
|
||||||
|
1CARAMELOS LIMA 0%,1,1.5
|
||||||
|
1MARGARINA CON SAL,1,1.5
|
||||||
|
1YOGUR SABOR COCO,2,1.5
|
||||||
|
1+PROTEINAS STRACCIAT,1,1.5
|
||||||
|
1SALSA FRESCA QUESOS,1,1.45
|
||||||
|
1PASTILLA CISTERNA,1,1.45
|
||||||
|
"0,874 KG 1,65 €/KG",1,1.44
|
||||||
|
1NIDOS AL HUEVO,1,1.41
|
||||||
|
1ESPONJA VEGETAL,1,1.4
|
||||||
|
1POSAVAJILLAS,1,1.35
|
||||||
|
"0,488 KG 2,70 €/KG",1,1.32
|
||||||
|
1MOUSSE PROTEIN CHOCO,1,1.3
|
||||||
|
1SPAGHETTI,1,1.25
|
||||||
|
1PALILLOS REDONDOS,1,1.25
|
||||||
|
"0,958 KG 1,31 €/KG",1,1.25
|
||||||
|
1SALSA DE SOJA SALADA,1,1.25
|
||||||
|
1FOSFOROS MADERA GRAN,1,1.2
|
||||||
|
1SALSA BARBACOA,1,1.15
|
||||||
|
1TORTILLAS MEXICANAS,1,1.13
|
||||||
|
1PAN TORRIJAS,1,1.13
|
||||||
|
1ALLIOLI TARRINA,1,1.1
|
||||||
|
1AZUCAR,1,1.05
|
||||||
|
1DORITOS BITS,1,0.95
|
||||||
|
1NACHOS,1,0.9
|
||||||
|
1GUISANTES,1,0.9
|
||||||
|
1CEPILLO INFANTIL,1,0.9
|
||||||
|
"0,242 KG 3,40 €/KG",1,0.82
|
||||||
|
"0,234 KG 3,40 €/KG",1,0.8
|
||||||
|
1CONOS,1,0.8
|
||||||
|
1TIRAS FRITAS,1,0.75
|
||||||
|
1AGUA OXIGENADA,1,0.65
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
producto,veces_comprado,total_unidades,gasto_total,primera_vez,ultima_vez
|
||||||
|
HUEVOS CAMPEROS,15,1402,43.24,2025-01-23,2025-04-16
|
||||||
|
ESTACIONES,3,52,8.8,2025-01-16,2025-03-12
|
||||||
|
B.MINI PAPELERA,1,340,4.05,2025-01-09,2025-01-09
|
||||||
|
B.ENVASES C.FÁCIL,1,230,3.3,2025-01-09,2025-01-09
|
||||||
|
CORAZON ROMANA,2,22,2.2,2025-01-23,2025-03-26
|
||||||
|
"RECIPIENTES 0,5L",1,13,2.05,2025-01-29,2025-01-29
|
||||||
|
SERVIL. BLANCAS,1,1200,1.25,2025-01-02,2025-01-02
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
$host = "tecnologia-facil.es";
|
||||||
|
$dbname = "autocompra";
|
||||||
|
$username = "mytecda6d2e";
|
||||||
|
$password = "3s5jJzz8";
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8", $username, $password);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
die("Error de conexión: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
<?php
|
||||||
|
require 'db.php';
|
||||||
|
|
||||||
|
$data = json_decode(file_get_contents("php://input"), true);
|
||||||
|
|
||||||
|
$fecha = $data["fecha"];
|
||||||
|
$total = $data["total"];
|
||||||
|
$metodo = $data["metodo_pago"];
|
||||||
|
$productos = $data["productos"];
|
||||||
|
|
||||||
|
$pdo->beginTransaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO tickets (fecha, total, metodo_pago) VALUES (?, ?, ?)");
|
||||||
|
$stmt->execute([$fecha, $total, $metodo]);
|
||||||
|
$ticketId = $pdo->lastInsertId();
|
||||||
|
|
||||||
|
foreach ($productos as $producto) {
|
||||||
|
// Buscar o crear producto
|
||||||
|
$nombre = strtoupper(trim($producto["nombre"]));
|
||||||
|
$stmt = $pdo->prepare("SELECT id FROM productos WHERE nombre = ?");
|
||||||
|
$stmt->execute([$nombre]);
|
||||||
|
$prod = $stmt->fetch();
|
||||||
|
|
||||||
|
if ($prod) {
|
||||||
|
$productoId = $prod["id"];
|
||||||
|
} else {
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO productos (nombre, ultima_compra) VALUES (?, ?)");
|
||||||
|
$stmt->execute([$nombre, $fecha]);
|
||||||
|
$productoId = $pdo->lastInsertId();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insertar línea de ticket
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO lineas_ticket (ticket_id, producto_id, cantidad, precio_unitario, precio_total)
|
||||||
|
VALUES (?, ?, ?, ?, ?)");
|
||||||
|
$stmt->execute([
|
||||||
|
$ticketId,
|
||||||
|
$productoId,
|
||||||
|
$producto["cantidad"],
|
||||||
|
$producto["precio_unitario"],
|
||||||
|
$producto["precio_total"]
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Actualizar última compra
|
||||||
|
$stmt = $pdo->prepare("UPDATE productos SET ultima_compra = ? WHERE id = ?");
|
||||||
|
$stmt->execute([$fecha, $productoId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo->commit();
|
||||||
|
echo json_encode(["status" => "ok"]);
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$pdo->rollBack();
|
||||||
|
echo json_encode(["status" => "error", "message" => $e->getMessage()]);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
SELECT supermercados.nombre, direcciones.direccion, poblaciones.nombre
|
||||||
|
FROM tiendas
|
||||||
|
JOIN supermercados ON tiendas.supermercado_id = supermercados.id
|
||||||
|
JOIN direcciones ON tiendas.direccion_id = direcciones.id
|
||||||
|
JOIN poblaciones ON direcciones.codigo_postal = poblaciones.id;
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
CREATE TABLE productos (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
nombre VARCHAR(255) NOT NULL,
|
||||||
|
marca_id INT FOREIGN KEY REFERENCES marcas(id),
|
||||||
|
codigo_barras VARCHAR(13) NOT NULL UNIQUE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE marcas (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
nombre VARCHAR(255) NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE tickets (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
tienda_id INT,
|
||||||
|
fecha DATE NOT NULL,
|
||||||
|
total DECIMAL(10, 2),
|
||||||
|
metodo_pago VARCHAR(50)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE supermercados ( -- Solo la cadena de supermercados, no la tienda
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
nombre VARCHAR (50) NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE tiendas ( -- La tienda concreta, con su dirección
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
nombre VARCHAR(255) NOT NULL,
|
||||||
|
supermercado_id INT,
|
||||||
|
direccion_id INT, -- Relaciona con la tabla direcciones
|
||||||
|
FOREIGN KEY (supermercado_id) REFERENCES supermercados(id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (direccion_id) REFERENCES direcciones(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE direcciones (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
direccion VARCHAR(255) NOT NULL,
|
||||||
|
codigo_postal VARCHAR(5)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE poblaciones (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY, -- Codigo postal de la poblacion
|
||||||
|
nombre VARCHAR(255) NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE provincias (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY, -- Los dos primeros digitos del codigo postal de la provincia
|
||||||
|
nombre VARCHAR(255) NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO provincias (id, nombre) VALUES
|
||||||
|
(1, 'Álava'),
|
||||||
|
(2, 'Albacete'),
|
||||||
|
(3, 'Alicante'),
|
||||||
|
(4, 'Almería'),
|
||||||
|
(5, 'Ávila'),
|
||||||
|
(6, 'Badajoz'),
|
||||||
|
(7, 'Islas Baleares'),
|
||||||
|
(8, 'Barcelona'),
|
||||||
|
(9, 'Burgos'),
|
||||||
|
(10, 'Cáceres'),
|
||||||
|
(11, 'Cádiz'),
|
||||||
|
(12, 'Castellón'),
|
||||||
|
(13, 'Ciudad Real'),
|
||||||
|
(14, 'Córdoba'),
|
||||||
|
(15, 'La Coruña'),
|
||||||
|
(16, 'Cuenca'),
|
||||||
|
(17, 'Gerona'),
|
||||||
|
(18, 'Granada'),
|
||||||
|
(19, 'Guadalajara'),
|
||||||
|
(20, 'Guipúzcoa'),
|
||||||
|
(21, 'Huelva'),
|
||||||
|
(22, 'Huesca'),
|
||||||
|
(23, 'Jaén'),
|
||||||
|
(24, 'León'),
|
||||||
|
(25, 'Lérida'),
|
||||||
|
(26, 'La Rioja'),
|
||||||
|
(27, 'Lugo'),
|
||||||
|
(28, 'Madrid'),
|
||||||
|
(29, 'Málaga'),
|
||||||
|
(30, 'Murcia'),
|
||||||
|
(31, 'Navarra'),
|
||||||
|
(32, 'Ourense'),
|
||||||
|
(33, 'Asturias'),
|
||||||
|
(34, 'Palencia'),
|
||||||
|
(35, 'Las Palmas'),
|
||||||
|
(36, 'Pontevedra'),
|
||||||
|
(37, 'Salamanca'),
|
||||||
|
(38, 'Santa Cruz de Tenerife'),
|
||||||
|
(39, 'Cantabria'),
|
||||||
|
(40, 'Segovia'),
|
||||||
|
(41, 'Sevilla'),
|
||||||
|
(42, 'Soria'),
|
||||||
|
(43, 'Tarragona'),
|
||||||
|
(44, 'Teruel'),
|
||||||
|
(45, 'Toledo'),
|
||||||
|
(46, 'Valencia'),
|
||||||
|
(47, 'Valladolid'),
|
||||||
|
(48, 'Vizcaya'),
|
||||||
|
(49, 'Zamora'),
|
||||||
|
(50, 'Zaragoza'),
|
||||||
|
(51, 'Ceuta'),
|
||||||
|
(52, 'Melilla');
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE lineas_ticket (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
ticket_id INT,
|
||||||
|
producto_id INT,
|
||||||
|
cantidad INT,
|
||||||
|
precio_unitario DECIMAL(10, 2),
|
||||||
|
FOREIGN KEY (ticket_id) REFERENCES tickets(id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (producto_id) REFERENCES productos(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- OPCIONAL: Si en el futuro quiero tener varios perfiles o compartir la app
|
||||||
|
CREATE TABLE usuarios (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
nombre VARCHAR(100),
|
||||||
|
email VARCHAR(100),
|
||||||
|
password_hash VARCHAR(255)
|
||||||
|
);
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Subir Ticket</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 2rem auto;
|
||||||
|
padding: 1rem;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
label, input, select, button {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
.producto {
|
||||||
|
border-top: 1px solid #ccc;
|
||||||
|
padding-top: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Subir Ticket</h1>
|
||||||
|
<form id="ticketForm">
|
||||||
|
<label>Fecha:
|
||||||
|
<input type="date" name="fecha" required>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>Total (€):
|
||||||
|
<input type="number" step="0.01" name="total" required>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>Método de pago:
|
||||||
|
<select name="metodo_pago" required>
|
||||||
|
<option value="Tarjeta">Tarjeta</option>
|
||||||
|
<option value="Efectivo">Efectivo</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<h2>Productos</h2>
|
||||||
|
<div id="productosContainer"></div>
|
||||||
|
|
||||||
|
<button type="button" onclick="agregarProducto()">+ Añadir producto</button>
|
||||||
|
<button type="submit">Enviar ticket</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function agregarProducto() {
|
||||||
|
const container = document.getElementById("productosContainer");
|
||||||
|
const index = container.children.length;
|
||||||
|
|
||||||
|
const div = document.createElement("div");
|
||||||
|
div.className = "producto";
|
||||||
|
div.innerHTML = `
|
||||||
|
<label>Nombre:
|
||||||
|
<input type="text" name="producto_nombre_${index}" required>
|
||||||
|
</label>
|
||||||
|
<label>Cantidad:
|
||||||
|
<input type="number" step="1" name="producto_cantidad_${index}" required>
|
||||||
|
</label>
|
||||||
|
<label>Precio unitario (€):
|
||||||
|
<input type="number" step="0.01" name="producto_unitario_${index}" required>
|
||||||
|
</label>
|
||||||
|
<label>Precio total (€):
|
||||||
|
<input type="number" step="0.01" name="producto_total_${index}" required>
|
||||||
|
</label>
|
||||||
|
`;
|
||||||
|
container.appendChild(div);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("ticketForm").addEventListener("submit", function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const form = new FormData(e.target);
|
||||||
|
const productos = [];
|
||||||
|
|
||||||
|
let index = 0;
|
||||||
|
while (form.has(`producto_nombre_${index}`)) {
|
||||||
|
productos.push({
|
||||||
|
nombre: form.get(`producto_nombre_${index}`),
|
||||||
|
cantidad: parseInt(form.get(`producto_cantidad_${index}`)),
|
||||||
|
precio_unitario: parseFloat(form.get(`producto_unitario_${index}`)),
|
||||||
|
precio_total: parseFloat(form.get(`producto_total_${index}`))
|
||||||
|
});
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
fecha: form.get("fecha"),
|
||||||
|
total: parseFloat(form.get("total")),
|
||||||
|
metodo_pago: form.get("metodo_pago"),
|
||||||
|
productos: productos
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch("subir_ticket.php", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify(data)
|
||||||
|
})
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(res => {
|
||||||
|
alert("Ticket subido con éxito");
|
||||||
|
location.reload();
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
alert("Error al subir el ticket");
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Añadir un primer producto al cargar la página
|
||||||
|
agregarProducto();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue