Quitar el prefijo home_ de las 41 tablas del portal

El prefijo venía del nombre de la app Django (`home`), jubilada y borrada hoy,
así que ya no significaba nada: home_stripelog -> stripelog. La BD se sigue
llamando django_wow por historia (renombrarla es otra operación).

Comprobado antes de tocar nada: 0 colisiones con nombres existentes, 0 palabras
reservadas (contrastado contra las 260 de MySQL), sin vistas ni triggers que
dependieran de ellas. El RENAME va en una sola sentencia porque así es atómico,
y MySQL reapunta solo las 4 claves ajenas entre estas tablas.

170 referencias actualizadas en 37 ficheros. Se dejó a propósito
sql/drop_home_securitytoken_authuser_fk.sql sin tocar: es una migración ya
aplicada y reescribirla sería falsear el historial.

De paso salió un fallo previo: prices.ts consultaba `home_restoreitemprice`,
una tabla que NO existe. El try/catch de priceFrom se comía el error y devolvía
siempre el precio por defecto, así que el precio de restaurar objetos nunca fue
configurable. Se deja documentado en el código; crear la tabla es otra decisión.

Verificado tras aplicar: 0 tablas home_, las 51 siguen ahí, y el conteo exacto
de filas cuadra con el volcado previo (store_item 3068, item_data 44873,
stripelog 35, store_order 26, noticia 50). La web responde 200 en todas las
rutas y la portada carga las noticias sin errores.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-15 14:49:19 +00:00
parent 5fd006aca9
commit cccc70338d
39 changed files with 226 additions and 171 deletions
+19 -19
View File
@@ -7,7 +7,7 @@ import { MAX_COPIES, PD_PER_EUR, PV_PER_EUR, storeEuroTotal as pureEuroTotal } f
/**
* Tienda de ítems (transfiguración/equipo), portada del sistema antiguo (BENNU).
* Catálogo en `home_store_category` (árbol por `code` "1-1-1") + `home_store_item`
* Catálogo en `store_category` (árbol por `code` "1-1-1") + `store_item`
* (precio en PD o PV, cantidad, icono). Se paga con el saldo PD/PV de la cuenta y
* los ítems se envían por correo al personaje elegido (SOAP `.send items`).
*/
@@ -22,7 +22,7 @@ export function isValidCharName(name: string): boolean {
const MAIL_ITEM_LIMIT = 12 // máximo de ítems por correo en AzerothCore
export interface StoreItem {
id: number // fila de home_store_item (clave del carrito)
id: number // fila de store_item (clave del carrito)
itemId: number
name: string
icon: string | null
@@ -114,10 +114,10 @@ export function parseCategoryName(html: string): NamePart[] {
/**
* Árbol completo de la tienda (categorías anidadas con sus ítems en las hojas).
*
* El nombre del ítem sale de `home_item_data` (extraído de los DB2 del cliente
* El nombre del ítem sale de `item_data` (extraído de los DB2 del cliente
* 3.4.3, ver sql/db2/), que es el ÚNICO sitio con el nombre en inglés: el
* `home_store_item.name` del catálogo original es solo español. El de la
* categoría sale de `home_store_category.name_en` (ver sql/store_category_en.sql).
* `store_item.name` del catálogo original es solo español. El de la
* categoría sale de `store_category.name_en` (ver sql/store_category_en.sql).
* En ambos casos, si falta el nombre en ese idioma se cae al del catálogo, que
* siempre está.
*/
@@ -132,13 +132,13 @@ export async function getStoreCatalog(locale: string): Promise<StoreCategory[]>
try {
;[cats] = await db(DB.default).query<CatRow[]>(
`SELECT code, parent_code, COALESCE(NULLIF(${catNameCol}, ''), name) AS name, css, depth, sort
FROM home_store_category ORDER BY sort`,
FROM store_category ORDER BY sort`,
)
;[items] = await db(DB.default).query<ItemRow[]>(
`SELECT i.id, i.category_code, i.item_id, COALESCE(NULLIF(d.${nameCol}, ''), i.name) AS name,
i.icon, i.quantity, i.currency, i.price, i.preview, i.sort
FROM home_store_item i
LEFT JOIN home_item_data d ON d.entry = i.item_id
FROM store_item i
LEFT JOIN item_data d ON d.entry = i.item_id
ORDER BY i.sort`,
)
} catch {
@@ -184,7 +184,7 @@ export async function getStoreBalances(accountId: number): Promise<{ dp: number;
if (!accountId) return { dp: 0, vp: 0 }
try {
const [rows] = await db(DB.default).query<RowDataPacket[]>(
'SELECT dp, vp FROM home_api_points WHERE accountID = ?',
'SELECT dp, vp FROM api_points WHERE accountID = ?',
[accountId],
)
return rows[0] ? { dp: Number(rows[0].dp), vp: Number(rows[0].vp) } : { dp: 0, vp: 0 }
@@ -200,7 +200,7 @@ export interface StoreCartLine {
}
export interface PricedCart {
// `qty` = lote que entrega CADA copia (home_store_item.quantity);
// `qty` = lote que entrega CADA copia (store_item.quantity);
// `copies` = cuántas veces se compra la línea. Total de unidades = qty*copies.
// `name` = nombre del catálogo; lo usa el concepto del cobro con tarjeta.
lines: { id: number; itemId: number; name: string; qty: number; currency: Currency; price: number; copies: number }[]
@@ -217,13 +217,13 @@ export interface PricedCart {
*
* OJO: la IP va aquí a propósito (identificar al pagador si hay fraude o una
* devolución a mano), pero esto se la manda a la pasarela; hasta ahora solo se
* guardaba en `home_stripelog.acore_ip`. Se omite si no se conoce, y también el
* guardaba en `stripelog.acore_ip`. Se omite si no se conoce, y también el
* 0.0.0.0 de relleno, que no dice nada.
*
* Se recorta a `MAX_CONCEPT`: Stripe y SumUp limitan el campo (~250-255), y un
* carrito de 100 líneas se pasaría de largo. Se recortan solo los ÍTEMS, nunca
* la cabecera: quién ha pagado importa más que el listado completo, que además
* queda entero en `home_store_order`.
* queda entero en `store_order`.
*/
const MAX_CONCEPT = 200
@@ -255,7 +255,7 @@ export function storeConcept(
/**
* Valida el carrito contra la BD (precios/cantidades/moneda REALES, nunca del
* cliente) a partir de las filas `home_store_item.id` y las copias pedidas.
* cliente) a partir de las filas `store_item.id` y las copias pedidas.
* Devuelve null si está vacío, si alguna copia no es válida o si algún id no
* existe en el catálogo (mismo criterio que `priceCart` de send-gift).
*/
@@ -274,7 +274,7 @@ export async function priceStoreCart(cart: StoreCartLine[]): Promise<PricedCart
let rows: ItemRow[] = []
try {
;[rows] = await db(DB.default).query<ItemRow[]>(
`SELECT id, item_id, name, quantity, currency, price FROM home_store_item WHERE id IN (${ids.map(() => '?').join(',')})`,
`SELECT id, item_id, name, quantity, currency, price FROM store_item WHERE id IN (${ids.map(() => '?').join(',')})`,
ids,
)
} catch {
@@ -353,14 +353,14 @@ export async function purchaseStoreCart(
try {
await conn.beginTransaction()
const [pts] = await conn.query<RowDataPacket[]>(
'SELECT dp, vp FROM home_api_points WHERE accountID = ? FOR UPDATE',
'SELECT dp, vp FROM api_points WHERE accountID = ? FOR UPDATE',
[accountId],
)
const dp = pts[0] ? Number(pts[0].dp) : 0
const vp = pts[0] ? Number(pts[0].vp) : 0
if (dp < cart.pdTotal) { await conn.rollback(); return { success: false, error: 'insufficientPd' } }
if (vp < cart.vpTotal) { await conn.rollback(); return { success: false, error: 'insufficientVp' } }
await conn.query('UPDATE home_api_points SET dp = dp - ?, vp = vp - ? WHERE accountID = ?', [cart.pdTotal, cart.vpTotal, accountId])
await conn.query('UPDATE api_points SET dp = dp - ?, vp = vp - ? WHERE accountID = ?', [cart.pdTotal, cart.vpTotal, accountId])
await conn.commit()
charged = true
} catch {
@@ -382,7 +382,7 @@ export async function purchaseStoreCart(
const vpBack = back('pv')
if (charged && (pdBack > 0 || vpBack > 0)) {
await db(DB.default)
.query('UPDATE home_api_points SET dp = dp + ?, vp = vp + ? WHERE accountID = ?', [pdBack, vpBack, accountId])
.query('UPDATE api_points SET dp = dp + ?, vp = vp + ? WHERE accountID = ?', [pdBack, vpBack, accountId])
.catch(() => {})
}
// Si no salió ni un correo es el fallo de siempre (SOAP caído). Si salió
@@ -430,7 +430,7 @@ export async function createStoreOrder(
const amount = storeEuroTotal(cart.pdTotal, cart.vpTotal)
try {
await db(DB.default).query(
'INSERT INTO home_store_order (ref, account_id, character_name, items, amount) VALUES (?, ?, ?, ?, ?)',
'INSERT INTO store_order (ref, account_id, character_name, items, amount) VALUES (?, ?, ?, ?, ?)',
[ref, accountId, character, items, amount],
)
return true
@@ -458,7 +458,7 @@ export async function fulfillStoreOrder(
let rows: RowDataPacket[] = []
try {
;[rows] = await db(DB.default).query<RowDataPacket[]>(
'SELECT character_name, items, amount FROM home_store_order WHERE ref = ?',
'SELECT character_name, items, amount FROM store_order WHERE ref = ?',
[ref],
)
} catch {