Pasé mi propia tienda por un validador: 9 filas rechazadas y 2 eran culpa mía
Lo que encontró CardIntake en el inventario real de PokeLances, y por qué no todas las reglas de validación deberían rechazar.
Tengo una tienda de cartas Pokémon, PokeLances, donde los productos se cargan uno por uno desde el panel. Es lento y fácil de hacer mal. Por eso construí CardIntake: recibe el inventario en un Excel, CSV o JSON, revisa cada fila contra el catálogo de TCGplayer y su precio de mercado, y genera un archivo listo para importar.
Para probarla con datos reales, convertí el inventario que ya estaba publicado en una hoja y lo pasé por las mismas reglas. Este fue el resultado:
batch 1 tienda_20261022.xlsx [xlsx] PARTIAL: 184 loaded (20 price flags), 9 rejected
De 193 filas, 9 quedaron rechazadas y 20 cargaron con una alerta de precio.
Las 9 filas rechazadas
| Filas | Motivo | ¿De quién era el problema? |
|---|---|---|
| 4 | Producto publicado dos veces | De la tienda |
| 1 | Carta japonesa publicada como inglesa | De la tienda |
| 2 | Producto ausente del catálogo sincronizado | Del catálogo, que iba atrasado |
| 2 | La impresión Normal no existe para esa carta |
Mío |
Lo más útil estaba en los duplicados: 3 de los 4 tenían dos precios distintos. El mismo Marill aparecía a S/ 55 y a S/ 40. Es difícil notarlo revisando de una en una, porque cada publicación se ve bien por separado.
La carta japonesa salió por un detalle pequeño. El mensaje decía:
name: file says 'Penny (JP)' but product 535108 is 'Penny'
El nombre traía "(JP)", pero el producto apuntaba a la versión inglesa del catálogo. La regla solo comparaba nombres para detectar errores en el ID, y aun así destapó un error de idioma.
Las 2 filas que eran culpa mía
La tienda no guarda la impresión de cada carta (Normal, Holofoil, Reverse Holofoil...). Como ese dato llegaba vacío, mi validador asumió Normal, y para estas dos cartas esa impresión no existe:
printing: 'Normal' does not exist for this card (available: Holofoil, Reverse Holofoil)
Las cartas estaban bien. Lo que estaba mal era mi suposición. Si hubiera contado cada rechazo como un error de la tienda, habría sacado una conclusión falsa de mi propia herramienta.
Cuando un validador rechaza algo, conviene hacerse primero una pregunta: ¿el dato está mal o lo está la regla?
Rechazar o avisar
No traté todos los problemas igual. Un ID de producto equivocado no puede ser correcto, así que esa fila se rechaza. Un precio raro sí puede ser correcto: quizá es una oferta deliberada o una carta difícil de conseguir en Perú. Por eso el precio solo levanta una alerta y la decisión queda en manos de quien vende:
def price_check(price_pen: Decimal, market_usd) -> tuple[Decimal | None, str | None]:
"""Compare the asking price with the TCGplayer market price converted to soles."""
if market_usd is None:
return None, "NO_MARKET_PRICE"
market_pen = (Decimal(str(market_usd)) * config.USD_PEN).quantize(Decimal("0.01"))
if market_pen <= 0:
return None, "NO_MARKET_PRICE"
ratio = price_pen / market_pen
if ratio < config.PRICE_LOW_RATIO:
return market_pen, "BELOW_MARKET"
if ratio > config.PRICE_HIGH_RATIO and price_pen > config.PRICE_FLOOR_PEN:
return market_pen, "ABOVE_MARKET"
return market_pen, None
La alerta se activa por debajo de 0.6 veces o por encima de 1.6 veces el mercado. En esa corrida marcó 19 cartas caras y una barata: una Maractus a S/ 50 cuando el mercado rondaba los S/ 96.90.
La condición price_pen > config.PRICE_FLOOR_PEN existe por los datos reales. Una carta común que vale 0.25 dólares se vende a S/ 5, más de cinco veces su precio de mercado convertido, y eso es normal. Sin ese piso, las alertas se llenarían de ruido hasta que nadie las leyera.
Lo que me llevo
- Prueba tus validadores con datos reales. Los datos sintéticos traen los errores que ya imaginaste al generarlos. Los reales traen también los otros.
- Separa los errores de las dudas. Si todo se rechaza, se bloquean casos válidos. Si todo solo avisa, nadie hace caso.
- Revisa también tus reglas. Cada rechazo puede decir algo de los datos o de tus suposiciones.
- Limita el daño posible. CardIntake solo lee la tienda y entrega un archivo que importo yo, así que una hoja mal hecha no puede romper producción.
Todavía tengo pendiente arreglar la causa de esos dos falsos rechazos: la tienda tiene que empezar a guardar la impresión de cada carta.