Sacar pestaña Test del admin
- Borrado public/components/test-panel.js + su nav button + su rango de router + sus rutas /test/products-with-stock y /test/order. - Borrados handleGetProductsWithStock + handleCreateTestOrder y los api wrappers getProductsWithStock + createTestOrder. Ya no se usan en ningún flujo: pruebas de bot se hacen vía simulator. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,6 @@ import "./components/aliases-crud.js";
|
|||||||
import "./components/recommendations-crud.js";
|
import "./components/recommendations-crud.js";
|
||||||
import "./components/quantities-crud.js";
|
import "./components/quantities-crud.js";
|
||||||
import "./components/orders-crud.js";
|
import "./components/orders-crud.js";
|
||||||
import "./components/test-panel.js";
|
|
||||||
import "./components/takeovers-crud.js";
|
import "./components/takeovers-crud.js";
|
||||||
import "./components/zone-map-editor.js";
|
import "./components/zone-map-editor.js";
|
||||||
import "./components/settings-crud.js";
|
import "./components/settings-crud.js";
|
||||||
|
|||||||
@@ -98,7 +98,6 @@ class OpsShell extends HTMLElement {
|
|||||||
<a class="nav-btn" href="/cantidades" data-view="quantities">Cantidades</a>
|
<a class="nav-btn" href="/cantidades" data-view="quantities">Cantidades</a>
|
||||||
<a class="nav-btn" href="/pedidos" data-view="orders">Pedidos</a>
|
<a class="nav-btn" href="/pedidos" data-view="orders">Pedidos</a>
|
||||||
<a class="nav-btn" href="/configuracion" data-view="settings">Config</a>
|
<a class="nav-btn" href="/configuracion" data-view="settings">Config</a>
|
||||||
<a class="nav-btn" href="/test" data-view="test">Test</a>
|
|
||||||
</nav>
|
</nav>
|
||||||
<div class="spacer"></div>
|
<div class="spacer"></div>
|
||||||
<div class="notification-bell" id="notificationBell" title="Takeovers pendientes">
|
<div class="notification-bell" id="notificationBell" title="Takeovers pendientes">
|
||||||
@@ -164,12 +163,6 @@ class OpsShell extends HTMLElement {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="viewTest" class="view">
|
|
||||||
<div class="layout-crud">
|
|
||||||
<test-panel></test-panel>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="viewTakeovers" class="view">
|
<div id="viewTakeovers" class="view">
|
||||||
<div class="layout-crud">
|
<div class="layout-crud">
|
||||||
<takeovers-crud></takeovers-crud>
|
<takeovers-crud></takeovers-crud>
|
||||||
|
|||||||
@@ -1,391 +0,0 @@
|
|||||||
import { api } from "../lib/api.js";
|
|
||||||
import { modal } from "../lib/modal.js";
|
|
||||||
|
|
||||||
// Datos aleatorios para generar usuarios de prueba
|
|
||||||
const NOMBRES = ["Juan", "María", "Carlos", "Ana", "Pedro", "Laura", "Diego", "Sofía"];
|
|
||||||
const APELLIDOS = ["García", "Rodríguez", "Martínez", "López", "González", "Fernández", "Pérez"];
|
|
||||||
const CALLES = ["Av. Corrientes", "Av. Santa Fe", "Calle Florida", "Av. Rivadavia", "Av. Cabildo", "Av. Libertador"];
|
|
||||||
|
|
||||||
function randomItem(arr) {
|
|
||||||
return arr[Math.floor(Math.random() * arr.length)];
|
|
||||||
}
|
|
||||||
|
|
||||||
function randomInt(min, max) {
|
|
||||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateTestUser() {
|
|
||||||
const randomPhone = `549${randomInt(1000000000, 9999999999)}`;
|
|
||||||
const wa_chat_id = `${randomPhone}@s.whatsapp.net`;
|
|
||||||
const nombre = randomItem(NOMBRES);
|
|
||||||
const apellido = randomItem(APELLIDOS);
|
|
||||||
|
|
||||||
return {
|
|
||||||
wa_chat_id,
|
|
||||||
phone: randomPhone,
|
|
||||||
name: `${nombre} ${apellido}`,
|
|
||||||
address: {
|
|
||||||
first_name: nombre,
|
|
||||||
last_name: apellido,
|
|
||||||
address_1: `${randomItem(CALLES)} ${randomInt(100, 9000)}`,
|
|
||||||
city: "CABA",
|
|
||||||
state: "Buenos Aires",
|
|
||||||
postcode: `${randomInt(1000, 1999)}`,
|
|
||||||
country: "AR",
|
|
||||||
phone: randomPhone,
|
|
||||||
email: `${randomPhone}@no-email.local`,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
class TestPanel extends HTMLElement {
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this.attachShadow({ mode: "open" });
|
|
||||||
this.products = [];
|
|
||||||
this.selectedProducts = [];
|
|
||||||
this.testUser = null;
|
|
||||||
this.lastOrder = null;
|
|
||||||
this.loading = false;
|
|
||||||
|
|
||||||
this.shadowRoot.innerHTML = `
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
/* Aliases locales que apuntan a las globals del theme */
|
|
||||||
--line: var(--border);
|
|
||||||
--blue: var(--accent);
|
|
||||||
--green: var(--ok);
|
|
||||||
--red: var(--err);
|
|
||||||
}
|
|
||||||
* { box-sizing: border-box; font-family: var(--font-sans); }
|
|
||||||
.container {
|
|
||||||
height: 100%;
|
|
||||||
background: var(--bg);
|
|
||||||
color: var(--text);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 16px;
|
|
||||||
padding: 16px;
|
|
||||||
overflow: auto;
|
|
||||||
max-width: 600px;
|
|
||||||
}
|
|
||||||
.panel {
|
|
||||||
background: var(--panel);
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 16px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
.panel-title {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--muted);
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
border-bottom: 1px solid var(--line);
|
|
||||||
padding-bottom: 8px;
|
|
||||||
}
|
|
||||||
.section {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
.section-title {
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--blue);
|
|
||||||
}
|
|
||||||
button {
|
|
||||||
background: var(--blue);
|
|
||||||
border: none;
|
|
||||||
color: white;
|
|
||||||
padding: 8px 16px;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 12px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: opacity 0.15s;
|
|
||||||
}
|
|
||||||
button:hover { opacity: 0.9; }
|
|
||||||
button:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
||||||
button.secondary {
|
|
||||||
background: transparent;
|
|
||||||
border: 1px solid var(--line);
|
|
||||||
color: var(--muted);
|
|
||||||
}
|
|
||||||
button.secondary:hover { border-color: var(--blue); color: var(--text); }
|
|
||||||
button.success { background: var(--green); }
|
|
||||||
input, select {
|
|
||||||
background: var(--bg);
|
|
||||||
border: 1px solid var(--line);
|
|
||||||
color: var(--text);
|
|
||||||
padding: 8px 12px;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
input:focus, select:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: var(--blue);
|
|
||||||
}
|
|
||||||
.product-list {
|
|
||||||
max-height: 200px;
|
|
||||||
overflow-y: auto;
|
|
||||||
border: 1px solid var(--line);
|
|
||||||
border-radius: 6px;
|
|
||||||
}
|
|
||||||
.product-item {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 80px 60px 30px;
|
|
||||||
gap: 8px;
|
|
||||||
padding: 8px;
|
|
||||||
border-bottom: 1px solid var(--line);
|
|
||||||
align-items: center;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
.product-item:last-child { border-bottom: none; }
|
|
||||||
.product-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
||||||
.product-qty { text-align: right; }
|
|
||||||
.product-unit { color: var(--muted); }
|
|
||||||
.remove-btn {
|
|
||||||
background: var(--red);
|
|
||||||
padding: 4px 8px;
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
.user-info {
|
|
||||||
background: var(--bg);
|
|
||||||
border: 1px solid var(--line);
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 12px;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
.user-info div { margin-bottom: 4px; }
|
|
||||||
.user-info span { color: var(--muted); }
|
|
||||||
.result {
|
|
||||||
background: var(--bg);
|
|
||||||
border: 1px solid var(--line);
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 12px;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
.result.success { border-color: var(--green); }
|
|
||||||
.result.error { border-color: var(--red); }
|
|
||||||
.result-label { color: var(--muted); font-size: 10px; text-transform: uppercase; }
|
|
||||||
.result-value { font-weight: 600; margin-top: 4px; }
|
|
||||||
.result-value.big { font-size: 18px; }
|
|
||||||
.result-link {
|
|
||||||
color: var(--blue);
|
|
||||||
text-decoration: underline;
|
|
||||||
cursor: pointer;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
.row { display: flex; gap: 8px; align-items: center; }
|
|
||||||
.flex-1 { flex: 1; }
|
|
||||||
.loading { opacity: 0.5; pointer-events: none; }
|
|
||||||
.empty { color: var(--muted); font-size: 12px; text-align: center; padding: 20px; }
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<div class="panel">
|
|
||||||
<div class="panel-title">1. Generar Orden de Prueba</div>
|
|
||||||
|
|
||||||
<div class="section">
|
|
||||||
<div class="row">
|
|
||||||
<button id="btnGenerate">Generar Orden Aleatoria</button>
|
|
||||||
<button id="btnClear" class="secondary">Limpiar</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="section">
|
|
||||||
<div class="section-title">Productos seleccionados</div>
|
|
||||||
<div class="product-list" id="productList">
|
|
||||||
<div class="empty">Click "Generar Orden Aleatoria" para comenzar</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="section">
|
|
||||||
<div class="section-title">Datos del usuario</div>
|
|
||||||
<div class="user-info" id="userInfo">
|
|
||||||
<div class="empty">Se generarán automáticamente</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="section">
|
|
||||||
<button id="btnCreateOrder" class="success" disabled>Crear Orden en WooCommerce</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="section" id="orderResult" style="display:none;">
|
|
||||||
<div class="result success">
|
|
||||||
<div class="result-label">Orden creada</div>
|
|
||||||
<div class="result-value big" id="orderIdValue">—</div>
|
|
||||||
<div style="margin-top:8px;">
|
|
||||||
<span class="result-label">Total:</span>
|
|
||||||
<span id="orderTotalValue">—</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback() {
|
|
||||||
this.shadowRoot.getElementById("btnGenerate").onclick = () => this.generateRandomOrder();
|
|
||||||
this.shadowRoot.getElementById("btnClear").onclick = () => this.clearAll();
|
|
||||||
this.shadowRoot.getElementById("btnCreateOrder").onclick = () => this.createOrder();
|
|
||||||
|
|
||||||
this.loadProducts();
|
|
||||||
}
|
|
||||||
|
|
||||||
async loadProducts() {
|
|
||||||
try {
|
|
||||||
const result = await api.getProductsWithStock();
|
|
||||||
this.products = result.items || [];
|
|
||||||
console.log(`[test-panel] Loaded ${this.products.length} products with stock`);
|
|
||||||
} catch (e) {
|
|
||||||
console.error("[test-panel] Error loading products:", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async generateRandomOrder() {
|
|
||||||
if (this.products.length === 0) {
|
|
||||||
await this.loadProducts();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.products.length === 0) {
|
|
||||||
modal.warn("No hay productos con stock disponible");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generar usuario aleatorio
|
|
||||||
this.testUser = generateTestUser();
|
|
||||||
|
|
||||||
// Seleccionar 1-3 productos aleatorios
|
|
||||||
const numProducts = randomInt(1, Math.min(3, this.products.length));
|
|
||||||
const shuffled = [...this.products].sort(() => Math.random() - 0.5);
|
|
||||||
this.selectedProducts = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < numProducts; i++) {
|
|
||||||
const product = shuffled[i];
|
|
||||||
const isKg = product.sell_unit === "kg";
|
|
||||||
const quantity = isKg ? randomInt(200, 2000) : randomInt(1, 5);
|
|
||||||
|
|
||||||
this.selectedProducts.push({
|
|
||||||
product_id: product.woo_product_id,
|
|
||||||
name: product.name,
|
|
||||||
quantity,
|
|
||||||
unit: isKg ? "kg" : "unit",
|
|
||||||
price: product.price,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.renderProductList();
|
|
||||||
this.renderUserInfo();
|
|
||||||
this.updateButtonStates();
|
|
||||||
}
|
|
||||||
|
|
||||||
renderProductList() {
|
|
||||||
const container = this.shadowRoot.getElementById("productList");
|
|
||||||
|
|
||||||
if (this.selectedProducts.length === 0) {
|
|
||||||
container.innerHTML = `<div class="empty">Click "Generar Orden Aleatoria" para comenzar</div>`;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
container.innerHTML = this.selectedProducts.map((p, i) => `
|
|
||||||
<div class="product-item">
|
|
||||||
<div class="product-name" title="${p.name}">${p.name}</div>
|
|
||||||
<div class="product-qty">${p.unit === "kg" ? `${p.quantity}g` : `${p.quantity}u`}</div>
|
|
||||||
<div class="product-unit">$${Number(p.price || 0).toFixed(0)}</div>
|
|
||||||
<button class="remove-btn" data-index="${i}">X</button>
|
|
||||||
</div>
|
|
||||||
`).join("");
|
|
||||||
|
|
||||||
container.querySelectorAll(".remove-btn").forEach(btn => {
|
|
||||||
btn.onclick = (e) => {
|
|
||||||
const idx = parseInt(e.target.dataset.index);
|
|
||||||
this.selectedProducts.splice(idx, 1);
|
|
||||||
this.renderProductList();
|
|
||||||
this.updateButtonStates();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
renderUserInfo() {
|
|
||||||
const container = this.shadowRoot.getElementById("userInfo");
|
|
||||||
|
|
||||||
if (!this.testUser) {
|
|
||||||
container.innerHTML = `<div class="empty">Se generarán automáticamente</div>`;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const addr = this.testUser.address;
|
|
||||||
container.innerHTML = `
|
|
||||||
<div><span>Nombre:</span> ${addr.first_name} ${addr.last_name}</div>
|
|
||||||
<div><span>Dirección:</span> ${addr.address_1}</div>
|
|
||||||
<div><span>Ciudad:</span> ${addr.city}, ${addr.state}</div>
|
|
||||||
<div><span>Teléfono:</span> ${addr.phone}</div>
|
|
||||||
<div><span>Email:</span> ${addr.email}</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateButtonStates() {
|
|
||||||
const hasProducts = this.selectedProducts.length > 0;
|
|
||||||
this.shadowRoot.getElementById("btnCreateOrder").disabled = !hasProducts;
|
|
||||||
}
|
|
||||||
|
|
||||||
async createOrder() {
|
|
||||||
if (this.loading) return;
|
|
||||||
this.loading = true;
|
|
||||||
|
|
||||||
const btn = this.shadowRoot.getElementById("btnCreateOrder");
|
|
||||||
btn.disabled = true;
|
|
||||||
btn.textContent = "Creando...";
|
|
||||||
|
|
||||||
try {
|
|
||||||
const basket = {
|
|
||||||
items: this.selectedProducts.map(p => ({
|
|
||||||
product_id: p.product_id,
|
|
||||||
quantity: p.quantity,
|
|
||||||
unit: p.unit,
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = await api.createTestOrder({
|
|
||||||
basket,
|
|
||||||
address: this.testUser?.address || null,
|
|
||||||
wa_chat_id: this.testUser?.wa_chat_id || null,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result.ok) {
|
|
||||||
this.lastOrder = result;
|
|
||||||
this.shadowRoot.getElementById("orderIdValue").textContent = `#${result.woo_order_id}`;
|
|
||||||
this.shadowRoot.getElementById("orderTotalValue").textContent = `$${Number(result.total || 0).toFixed(2)}`;
|
|
||||||
this.shadowRoot.getElementById("orderResult").style.display = "block";
|
|
||||||
this.shadowRoot.getElementById("inputAmount").value = result.total || "";
|
|
||||||
} else {
|
|
||||||
modal.error("Error: " + (result.error || "Error desconocido"));
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error("[test-panel] createOrder error:", e);
|
|
||||||
modal.error("Error creando orden: " + e.message);
|
|
||||||
} finally {
|
|
||||||
this.loading = false;
|
|
||||||
btn.textContent = "Crear Orden en WooCommerce";
|
|
||||||
this.updateButtonStates();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clearAll() {
|
|
||||||
this.selectedProducts = [];
|
|
||||||
this.testUser = null;
|
|
||||||
this.lastOrder = null;
|
|
||||||
|
|
||||||
this.renderProductList();
|
|
||||||
this.renderUserInfo();
|
|
||||||
this.updateButtonStates();
|
|
||||||
|
|
||||||
this.shadowRoot.getElementById("orderResult").style.display = "none";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("test-panel", TestPanel);
|
|
||||||
@@ -234,18 +234,6 @@ export const api = {
|
|||||||
return this.listOrders({ page: 1, limit });
|
return this.listOrders({ page: 1, limit });
|
||||||
},
|
},
|
||||||
|
|
||||||
async getProductsWithStock() {
|
|
||||||
return fetch("/test/products-with-stock").then(r => r.json());
|
|
||||||
},
|
|
||||||
|
|
||||||
async createTestOrder({ basket, address, wa_chat_id }) {
|
|
||||||
return fetch("/test/order", {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({ basket, address, wa_chat_id }),
|
|
||||||
}).then(r => r.json());
|
|
||||||
},
|
|
||||||
|
|
||||||
// --- Prompts CRUD ---
|
// --- Prompts CRUD ---
|
||||||
async prompts() {
|
async prompts() {
|
||||||
return fetch("/prompts").then(r => r.json());
|
return fetch("/prompts").then(r => r.json());
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ const ROUTES = [
|
|||||||
{ pattern: /^\/cantidades$/, view: "quantities", params: [] },
|
{ pattern: /^\/cantidades$/, view: "quantities", params: [] },
|
||||||
{ pattern: /^\/pedidos$/, view: "orders", params: [] },
|
{ pattern: /^\/pedidos$/, view: "orders", params: [] },
|
||||||
{ pattern: /^\/pedidos\/([^/]+)$/, view: "orders", params: ["id"] },
|
{ pattern: /^\/pedidos\/([^/]+)$/, view: "orders", params: ["id"] },
|
||||||
{ pattern: /^\/test$/, view: "test", params: [] },
|
|
||||||
{ pattern: /^\/config-prompts$/, view: "prompts", params: [] },
|
{ pattern: /^\/config-prompts$/, view: "prompts", params: [] },
|
||||||
{ pattern: /^\/atencion-humana$/, view: "takeovers", params: [] },
|
{ pattern: /^\/atencion-humana$/, view: "takeovers", params: [] },
|
||||||
{ pattern: /^\/configuracion$/, view: "settings", params: [] },
|
{ pattern: /^\/configuracion$/, view: "settings", params: [] },
|
||||||
@@ -33,7 +32,6 @@ const VIEW_TO_PATH = {
|
|||||||
crosssell: "/crosssell",
|
crosssell: "/crosssell",
|
||||||
quantities: "/cantidades",
|
quantities: "/cantidades",
|
||||||
orders: "/pedidos",
|
orders: "/pedidos",
|
||||||
test: "/test",
|
|
||||||
prompts: "/config-prompts",
|
prompts: "/config-prompts",
|
||||||
takeovers: "/atencion-humana",
|
takeovers: "/atencion-humana",
|
||||||
settings: "/configuracion",
|
settings: "/configuracion",
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
import {
|
import { handleListOrders } from "../handlers/testing.js";
|
||||||
handleListOrders,
|
|
||||||
handleGetProductsWithStock,
|
|
||||||
handleCreateTestOrder,
|
|
||||||
} from "../handlers/testing.js";
|
|
||||||
import { handleGetOrderStats } from "../handlers/stats.js";
|
import { handleGetOrderStats } from "../handlers/stats.js";
|
||||||
|
|
||||||
export const makeListOrders = (tenantIdOrFn) => async (req, res) => {
|
export const makeListOrders = (tenantIdOrFn) => async (req, res) => {
|
||||||
@@ -29,31 +25,3 @@ export const makeGetOrderStats = (tenantIdOrFn) => async (req, res) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const makeGetProductsWithStock = (tenantIdOrFn) => async (req, res) => {
|
|
||||||
try {
|
|
||||||
const tenantId = typeof tenantIdOrFn === "function" ? tenantIdOrFn() : tenantIdOrFn;
|
|
||||||
const result = await handleGetProductsWithStock({ tenantId });
|
|
||||||
res.json(result);
|
|
||||||
} catch (err) {
|
|
||||||
console.error("[testing] getProductsWithStock error:", err);
|
|
||||||
res.status(500).json({ ok: false, error: err.message || "internal_error" });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const makeCreateTestOrder = (tenantIdOrFn) => async (req, res) => {
|
|
||||||
try {
|
|
||||||
const tenantId = typeof tenantIdOrFn === "function" ? tenantIdOrFn() : tenantIdOrFn;
|
|
||||||
const { basket, address, wa_chat_id } = req.body || {};
|
|
||||||
|
|
||||||
if (!basket?.items?.length) {
|
|
||||||
return res.status(400).json({ ok: false, error: "basket_required" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await handleCreateTestOrder({ tenantId, basket, address, wa_chat_id });
|
|
||||||
res.json(result);
|
|
||||||
} catch (err) {
|
|
||||||
console.error("[testing] createTestOrder error:", err);
|
|
||||||
res.status(500).json({ ok: false, error: err.message || "internal_error" });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { createOrder, syncOrdersIncremental } from "../../4-woo-orders/wooOrders.js";
|
import { syncOrdersIncremental } from "../../4-woo-orders/wooOrders.js";
|
||||||
import { listProducts } from "../db/repo.js";
|
|
||||||
import * as ordersRepo from "../../4-woo-orders/ordersRepo.js";
|
import * as ordersRepo from "../../4-woo-orders/ordersRepo.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,51 +23,3 @@ export async function handleListOrders({ tenantId, page = 1, limit = 50 }) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Obtiene productos con stock para testing
|
|
||||||
*/
|
|
||||||
export async function handleGetProductsWithStock({ tenantId }) {
|
|
||||||
const allProducts = await listProducts({ tenantId, limit: 500 });
|
|
||||||
const withStock = allProducts.filter(p =>
|
|
||||||
p.stock_status === "instock" &&
|
|
||||||
p.price &&
|
|
||||||
Number(p.price) > 0
|
|
||||||
);
|
|
||||||
return { items: withStock };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Crea una orden de prueba en WooCommerce
|
|
||||||
*/
|
|
||||||
export async function handleCreateTestOrder({ tenantId, basket, address, wa_chat_id }) {
|
|
||||||
if (!basket?.items?.length) {
|
|
||||||
throw new Error("basket_empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
const order = await createOrder({
|
|
||||||
tenantId,
|
|
||||||
wooCustomerId: null, // Sin customer de Woo para testing
|
|
||||||
basket,
|
|
||||||
address,
|
|
||||||
run_id: `test-${Date.now()}`,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Calcular total desde line_items
|
|
||||||
let total = 0;
|
|
||||||
if (order?.raw?.line_items) {
|
|
||||||
for (const item of order.raw.line_items) {
|
|
||||||
total += Number(item.total) || 0;
|
|
||||||
}
|
|
||||||
} else if (order?.raw?.total) {
|
|
||||||
total = Number(order.raw.total) || 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
ok: true,
|
|
||||||
woo_order_id: order?.id || null,
|
|
||||||
total,
|
|
||||||
line_items: order?.line_items || [],
|
|
||||||
raw: order?.raw || null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { makeListProductQtyRules, makeGetProductQtyRules, makeSaveProductQtyRule
|
|||||||
import { makeListPendingTakeovers, makeListAllTakeovers, makeGetTakeover, makeRespondToTakeover, makeCancelTakeover, makeCheckPendingTakeover } from "../../0-ui/controllers/takeovers.js";
|
import { makeListPendingTakeovers, makeListAllTakeovers, makeGetTakeover, makeRespondToTakeover, makeCancelTakeover, makeCheckPendingTakeover } from "../../0-ui/controllers/takeovers.js";
|
||||||
import { makeGetSettings, makeSaveSettings } from "../../0-ui/controllers/settings.js";
|
import { makeGetSettings, makeSaveSettings } from "../../0-ui/controllers/settings.js";
|
||||||
import { makeDeleteConversation, makeDeleteUser, makeListUsers, makeRetryLast } from "../../0-ui/controllers/admin.js";
|
import { makeDeleteConversation, makeDeleteUser, makeListUsers, makeRetryLast } from "../../0-ui/controllers/admin.js";
|
||||||
import { makeListOrders, makeGetOrderStats, makeGetProductsWithStock, makeCreateTestOrder } from "../../0-ui/controllers/testing.js";
|
import { makeListOrders, makeGetOrderStats } from "../../0-ui/controllers/testing.js";
|
||||||
import { getAgentMetrics } from "../../3-turn-engine/agent/runTurn.js";
|
import { getAgentMetrics } from "../../3-turn-engine/agent/runTurn.js";
|
||||||
|
|
||||||
function nowIso() {
|
function nowIso() {
|
||||||
@@ -108,10 +108,6 @@ export function createSimulatorRouter({ tenantId }) {
|
|||||||
router.get("/api/orders", makeListOrders(getTenantId));
|
router.get("/api/orders", makeListOrders(getTenantId));
|
||||||
router.get("/api/stats/orders", makeGetOrderStats(getTenantId));
|
router.get("/api/stats/orders", makeGetOrderStats(getTenantId));
|
||||||
|
|
||||||
// --- Testing routes ---
|
|
||||||
router.get("/test/products-with-stock", makeGetProductsWithStock(getTenantId));
|
|
||||||
router.post("/test/order", makeCreateTestOrder(getTenantId));
|
|
||||||
|
|
||||||
return router;
|
return router;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user