Files
Autoparts-DB/pos/templates/yonke.html
consultoria-as a386ca5b4a
Some checks failed
CI / lint-and-test (3.11) (push) Has been cancelled
CI / lint-and-test (3.13) (push) Has been cancelled
Mejoras de sesion: estabilizacion, IA, Nexus Yonke y marketplace B2B
- F0 blindaje: Redis/Celery, pg_hba sin trust LAN, secretos a env 600, backups, webhook auth, jobs muertos
- F1 caja: bugs POS, reembolsos al corte, apartados, CFDI, topes de descuento en cotizacion/apartado
- F2/F7 IA de catalogo: autocategorizacion, dedup de SKUs con embeddings, OCR de facturas, descripciones, claves SAT, limpieza de fitment
- F3 IA en el flujo: vision Yonke, preguntas MeLi, venta cruzada, anomalias, sinonimos, busqueda semantica (rerank/embeddings)
- F4/F8 escala y cartera: /metrics+Prometheus+Grafana, pool, score de credito, churn, metricas multi-tenant
- Nexus Yonke (M1-M6): modelo de datos 1-de-1, alta por donador, vitrina publica, MeLi usado, consignacion, garantia por dias, reportes, etiquetas
- Marketplace B2B entre yonkes (catalogo + pedidos cross-tenant)
- Seguridad: auth en /api/admin/* (35 endpoints), alertas por tenant
- Migraciones v4.27/v4.28; timers expire-demos y rollup-metrics
2026-10-12 00:57:31 +00:00

1463 lines
73 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="es">
<head>
<script>/*pos_theme_early*/(function(){var t=localStorage.getItem("pos_theme")||"industrial";document.documentElement.setAttribute("data-theme",t);})()</script>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Yonke — Nexus Autoparts POS</title>
<link rel="stylesheet" href="/pos/static/css/chat.css" />
<link rel="stylesheet" href="/pos/static/css/tokens.css" />
<link rel="stylesheet" href="/pos/static/css/common.css" />
<link rel="stylesheet" href="/pos/static/css/pos-ui.css?v=33" />
<link rel="stylesheet" href="/pos/static/css/sidebar.css" />
<link rel="stylesheet" href="/pos/static/css/pos-glass.css" />
<link rel="stylesheet" href="/pos/static/css/inventory.css?v=34" />
<link rel="manifest" href="/pos/static/pwa/manifest.json" />
<meta name="theme-color" content="#F5A623" />
<link rel="shortcut icon" type="image/png" href="/pos/static/pwa/icon-192.png" />
<style>
/* ─── Yonke: page-specific bits ─────────────────────────────────────── */
.ynk-thumb {
width: 44px; height: 44px; object-fit: cover; border-radius: var(--radius-sm);
background: var(--color-surface-2); border: 1px solid var(--color-border); cursor: pointer;
display: block;
}
.ynk-thumb-slot { display: inline-block; cursor: pointer; line-height: 0; }
.ynk-thumb--empty {
display: flex; align-items: center; justify-content: center;
color: var(--color-text-muted); font-size: 9px; text-transform: uppercase;
letter-spacing: var(--tracking-wide); line-height: 1.15; text-align: center;
}
.ynk-grade {
display: inline-flex; align-items: center; justify-content: center;
width: 22px; height: 22px; border-radius: 50%; font-weight: 700; font-size: 11px;
border: 1px solid transparent;
}
.ynk-grade--A { background: rgba(34,197,94,.15); color: var(--color-success); border-color: rgba(34,197,94,.35); }
.ynk-grade--B { background: rgba(59,130,246,.15); color: var(--color-info, #3b82f6); border-color: rgba(59,130,246,.35); }
.ynk-grade--C { background: rgba(234,179,8,.15); color: #eab308; border-color: rgba(234,179,8,.35); }
.ynk-grade--D { background: rgba(239,68,68,.15); color: var(--color-error); border-color: rgba(239,68,68,.35); }
.ynk-grade--none { color: var(--color-text-muted); }
.ynk-uniq {
display: inline-block; margin-left: 4px; padding: 1px 5px; border-radius: var(--radius-full);
background: var(--color-primary-muted); color: var(--color-primary);
font-size: 0.625rem; font-weight: 700; letter-spacing: var(--tracking-wide);
text-transform: uppercase; vertical-align: middle;
}
.ynk-seg {
display: inline-flex; border: 1px solid var(--color-border);
border-radius: var(--radius-md); overflow: hidden; background: var(--color-bg-elevated);
}
.ynk-seg button {
border: none; background: transparent; color: var(--color-text-muted); cursor: pointer;
padding: 0 var(--space-4); height: 36px; font-family: var(--font-body);
font-size: var(--text-body-sm); font-weight: var(--font-weight-semibold);
letter-spacing: var(--tracking-wide); white-space: nowrap;
}
.ynk-seg button + button { border-left: 1px solid var(--color-border); }
.ynk-seg button:hover { color: var(--color-text-primary); background: var(--color-surface-2); }
.ynk-seg button.is-active { background: var(--color-primary); color: var(--color-text-inverse); }
.ynk-sku { font-family: var(--font-mono); font-weight: 700; color: var(--color-primary); }
.ynk-hint { font-size: var(--text-caption); color: var(--color-text-muted); }
.ynk-drop {
border: 1px dashed var(--color-border); border-radius: var(--radius-md);
padding: var(--space-4); text-align: center; background: var(--color-surface-1);
}
.ynk-photo-grid {
display: grid; grid-template-columns: repeat(auto-fill, minmax(88px, 1fr));
gap: var(--space-2); margin-top: var(--space-2);
}
.ynk-photo {
position: relative; aspect-ratio: 1 / 1; border-radius: var(--radius-md);
overflow: hidden; border: 1px solid var(--color-border); background: var(--color-surface-2);
}
.ynk-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ynk-photo__remove {
position: absolute; top: 3px; right: 3px; width: 20px; height: 20px; padding: 0;
border: none; border-radius: 50%; cursor: pointer; line-height: 1;
background: rgba(0,0,0,.65); color: #fff; font-size: 13px;
}
.ynk-empty {
padding: var(--space-6); text-align: center; color: var(--color-text-muted);
font-size: var(--text-body-sm);
}
.ynk-summary-card {
background: var(--color-surface-1); border: 1px solid var(--color-border);
border-radius: var(--radius-lg); padding: var(--space-4);
}
.ynk-summary-card h4 {
margin: 0 0 var(--space-3); font-size: var(--text-caption);
letter-spacing: var(--tracking-widest); text-transform: uppercase;
color: var(--color-text-muted);
}
.ynk-kv {
display: flex; justify-content: space-between; gap: var(--space-3);
padding: 5px 0; border-bottom: 1px dashed var(--color-border);
font-size: var(--text-body-sm);
}
.ynk-kv:last-child { border-bottom: none; }
.ynk-kv__k { color: var(--color-text-muted); }
.ynk-kv__v { color: var(--color-text-primary); font-weight: var(--font-weight-semibold); text-align: right; }
.ynk-save-result { margin-top: var(--space-3); min-height: 1.5em; font-size: var(--text-body-sm); }
.ynk-ok { color: var(--color-success); }
.ynk-err { color: var(--color-error); }
.ynk-warn{ color: #eab308; }
.ynk-rep-grid {
display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: var(--space-3); align-items: start;
}
.ynk-rep-grid > .ynk-hint { grid-column: 1 / -1; }
.ynk-link { color: var(--color-primary); text-decoration: underline; word-break: break-all; }
/* ─── Mayoreo B2B ─────────────────────────────────────────────────── */
.ynk-b2b-help {
background: var(--color-primary-muted); border: 1px solid rgba(245,166,35,.28);
border-radius: var(--radius-md); padding: var(--space-2) var(--space-3);
font-size: var(--text-caption); color: var(--color-text-primary);
margin-bottom: var(--space-3); line-height: 1.5;
}
.ynk-b2b-grid {
display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
gap: var(--space-3);
}
.ynk-b2b-card {
display: flex; flex-direction: column; overflow: hidden;
background: var(--color-surface-1); border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
}
.ynk-b2b-card__img {
width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block;
background: var(--color-surface-2); border-bottom: 1px solid var(--color-border);
}
.ynk-b2b-card__img--empty {
display: flex; align-items: center; justify-content: center;
color: var(--color-text-muted); font-size: 9px; text-transform: uppercase;
letter-spacing: var(--tracking-wide);
}
.ynk-b2b-card__body {
display: flex; flex-direction: column; gap: 5px; flex: 1;
padding: var(--space-3);
}
.ynk-b2b-card__name {
font-weight: var(--font-weight-semibold); font-size: var(--text-body-sm);
color: var(--color-text-primary);
}
.ynk-b2b-card__meta {
font-size: var(--text-caption); color: var(--color-text-muted);
display: flex; align-items: center; gap: 5px;
}
.ynk-b2b-card__price {
font-family: var(--font-mono); font-weight: 700;
color: var(--color-primary); font-size: 1.05rem;
}
.ynk-b2b-card__foot { margin-top: auto; padding-top: var(--space-2); }
.ynk-b2b-price-input { width: 110px; text-align: right; }
.ynk-b2b-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); }
</style>
</head>
<body>
<!-- =========================================================================
THEME SWITCHER BAR
========================================================================= -->
<header class="theme-bar" role="banner">
<div class="theme-bar__left">
<div class="theme-bar__store">
<span class="theme-bar__dot"></span>
Nexus Autoparts
</div>
<div class="theme-bar__sep"></div>
<span class="theme-bar__label">Yonke &mdash; Partes usadas</span>
</div>
<div class="theme-bar__right">
<span class="theme-bar__label">Tema:</span>
<button class="theme-btn theme-btn--industrial is-active" data-theme-target="industrial" onclick="setTheme('industrial')">
<span class="theme-btn__swatch"></span>
Industrial
</button>
<button class="theme-btn theme-btn--modern" data-theme-target="modern" onclick="setTheme('modern')">
<span class="theme-btn__swatch"></span>
Moderno
</button>
</div>
</header>
<!-- =========================================================================
APP SHELL
========================================================================= -->
<div class="app-shell">
<!-- Sidebar is replaced/injected by sidebar.js -->
<aside class="sidebar" id="sidebar" role="navigation" aria-label="Navegación principal"></aside>
<main class="main" role="main">
<!-- Page Header -->
<div class="page-header">
<div class="page-header__title-group">
<span class="page-header__eyebrow">Deshuesadero &middot; Partes usadas</span>
<h1 class="page-header__title">Nexus Yonke</h1>
</div>
<div class="page-header__actions">
<button class="btn btn--ghost" onclick="switchTab('donador')">
<svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
Buscar donador
</button>
<button class="btn btn--ghost" onclick="ynkRefreshPieces()">
<svg viewBox="0 0 24 24"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-.38-4.93"/></svg>
Sincronizar
</button>
<button class="btn btn--primary" onclick="switchTab('nueva')">
<svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
Nueva pieza
</button>
</div>
</div>
<!-- Summary Cards -->
<div class="summary-strip">
<div class="summary-card">
<div class="summary-card__icon">
<svg viewBox="0 0 24 24"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></svg>
</div>
<div class="summary-card__body">
<div class="summary-card__label">Piezas usadas</div>
<div class="summary-card__value" id="ynkStatPieces">--</div>
<div class="summary-card__sub">En catálogo</div>
</div>
</div>
<div class="summary-card">
<div class="summary-card__icon summary-card__icon--time">
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="M12 8v4l3 2"/></svg>
</div>
<div class="summary-card__body">
<div class="summary-card__label">Donadores</div>
<div class="summary-card__value" id="ynkStatDonors">--</div>
<div class="summary-card__sub">Vehículos desarmados</div>
</div>
</div>
<div class="summary-card">
<div class="summary-card__icon summary-card__icon--value">
<svg viewBox="0 0 24 24"><line x1="12" y1="1" x2="12" y2="23"/><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/></svg>
</div>
<div class="summary-card__body">
<div class="summary-card__label">Piezas en consigna</div>
<div class="summary-card__value" id="ynkStatConsignPieces">--</div>
<div class="summary-card__sub">Propiedad de terceros</div>
</div>
</div>
<div class="summary-card">
<div class="summary-card__icon summary-card__icon--alert">
<svg viewBox="0 0 24 24"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
</div>
<div class="summary-card__body">
<div class="summary-card__label">Consignaciones</div>
<div class="summary-card__value" id="ynkStatConsignments">--</div>
<div class="summary-card__sub"><span id="ynkStatConsignmentsSub">Abiertas</span></div>
</div>
</div>
</div>
<!-- Tabs Row -->
<div class="tabs-row" role="tablist" aria-label="Módulos de Yonke">
<button class="tab-btn is-active" role="tab" aria-selected="true" aria-controls="panel-piezas" onclick="switchTab('piezas')">
Piezas usadas <span class="tab-btn__badge" id="badge-piezas">0</span>
</button>
<button class="tab-btn" role="tab" aria-selected="false" aria-controls="panel-nueva" onclick="switchTab('nueva')">
Nueva pieza
</button>
<button class="tab-btn" role="tab" aria-selected="false" aria-controls="panel-donador" onclick="switchTab('donador')">
Por donador <span class="tab-btn__badge" id="badge-donadores">0</span>
</button>
<button class="tab-btn" role="tab" aria-selected="false" aria-controls="panel-consignaciones" onclick="switchTab('consignaciones')">
Consignaciones <span class="tab-btn__badge" id="badge-consignaciones">0</span>
</button>
<button class="tab-btn" role="tab" aria-selected="false" aria-controls="panel-b2b" onclick="switchTab('b2b')" title="Mayoreo entre negocios: compra y vende piezas usadas al mayoreo">
Mayoreo B2B <span class="tab-btn__badge" id="badge-b2b">0</span>
</button>
<button class="tab-btn" role="tab" aria-selected="false" aria-controls="panel-reportes" onclick="switchTab('reportes')">
Reportes
</button>
</div>
<!-- Tab Panels -->
<div class="tab-panels" id="tab-panels">
<!-- ===================================================================
TAB 1 — PIEZAS USADAS
=================================================================== -->
<div class="tab-panel is-active" id="panel-piezas" role="tabpanel">
<div class="toolbar">
<div class="search-box">
<svg viewBox="0 0 24 24" stroke-linecap="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="ynkQ" placeholder="Buscar por nombre, No. parte, SKU o VIN…" autocomplete="off" />
</div>
<select class="select-filter" id="ynkFiltCondition">
<option value="">Usadas y reconstruidas</option>
<option value="usado">Usado</option>
<option value="reconstruido">Reconstruido</option>
</select>
<select class="select-filter" id="ynkFiltGrade">
<option value="">Todo grado</option>
<option value="A">Grado A</option>
<option value="B">Grado B</option>
<option value="C">Grado C</option>
<option value="D">Grado D</option>
</select>
<select class="select-filter" id="ynkFiltOwnership">
<option value="">Propiedad: toda</option>
<option value="propio">Propio</option>
<option value="consigna">Consigna</option>
</select>
<select class="select-filter" id="ynkFiltPerPage">
<option value="25">25 / pág.</option>
<option value="50" selected>50 / pág.</option>
<option value="100">100 / pág.</option>
</select>
<div class="toolbar__spacer"></div>
<button class="btn btn--ghost btn--sm" onclick="ynkResetPieceFilters()">Limpiar</button>
<button class="btn btn--ghost btn--sm" onclick="ynkOpenImportModal()">
<svg viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
Importar CSV
</button>
<button class="btn btn--primary btn--sm" onclick="switchTab('nueva')">
<svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
Nueva pieza
</button>
</div>
<div class="table-wrapper">
<table class="data-table" id="ynkPiecesTable">
<thead>
<tr>
<th style="width:60px;">Foto</th>
<th>No. Parte / SKU</th>
<th>Nombre</th>
<th>Marca</th>
<th>Condición</th>
<th>Grado</th>
<th>Donador</th>
<th style="text-align:right">Precio</th>
<th style="text-align:right">Costo</th>
<th style="text-align:right">Stock</th>
<th>Ubicación</th>
<th>Acciones</th>
</tr>
</thead>
<tbody id="ynkPiecesBody">
<tr><td colspan="12" class="ynk-empty">Cargando piezas…</td></tr>
</tbody>
</table>
<div class="table-footer">
<span id="ynkPiecesFooter">—</span>
<div class="pagination" id="ynkPiecesPagination"></div>
</div>
</div>
</div>
<!-- ===================================================================
TAB 2 — NUEVA PIEZA (alta rápida)
=================================================================== -->
<div class="tab-panel" id="panel-nueva" role="tabpanel">
<div class="table-wrapper" style="padding:var(--space-5);">
<div style="display:flex;justify-content:space-between;align-items:center;gap:var(--space-3);margin-bottom:var(--space-4);flex-wrap:wrap;">
<div>
<h3 style="font-family:var(--font-heading);font-size:var(--text-h5,1rem);text-transform:uppercase;letter-spacing:var(--tracking-wide);">Alta rápida de pieza usada</h3>
<p class="ynk-hint" style="margin-top:4px;">Solo <strong>nombre</strong> y <strong>precio</strong> son obligatorios. El No. de parte se genera automático si lo dejas vacío.</p>
</div>
<span class="ynk-hint" id="ynkTimer">—</span>
</div>
<div class="toolbar" style="margin-bottom:var(--space-4);">
<div class="ynk-seg" role="tablist" aria-label="Modo de alta">
<button type="button" id="ynkSegSingle" class="is-active" onclick="ynkSetIntakeMode('single')">Pieza individual</button>
<button type="button" id="ynkSegFull" onclick="ynkSetIntakeMode('full')">Donador completo (varias piezas)</button>
</div>
</div>
<div id="ynkNuevaSimple">
<div class="inv-form-grid">
<div class="inv-field inv-field--full">
<label for="ynkName">Nombre de la pieza *</label>
<input type="text" id="ynkName" placeholder="Ej: Faro delantero izquierdo" autocomplete="off" />
</div>
<div class="inv-field">
<label for="ynkPartNumber">No. de parte / SKU</label>
<input type="text" id="ynkPartNumber" placeholder="se genera automático" autocomplete="off" />
</div>
<div class="inv-field">
<label for="ynkBrand">Marca de la pieza</label>
<input type="text" id="ynkBrand" placeholder="Ej: Toyota / Genérico" autocomplete="off" />
</div>
<div class="inv-field">
<label for="ynkPrice">Precio de venta *</label>
<input type="number" id="ynkPrice" step="0.01" min="0" placeholder="0.00" />
</div>
<div class="inv-field">
<label for="ynkCost">Costo</label>
<input type="number" id="ynkCost" step="0.01" min="0" placeholder="0.00" />
</div>
<div class="inv-field">
<label for="ynkCondition">Condición</label>
<select id="ynkCondition">
<option value="usado" selected>Usado</option>
<option value="reconstruido">Reconstruido</option>
</select>
</div>
<div class="inv-field">
<label for="ynkGrade">Grado</label>
<select id="ynkGrade">
<option value="">Sin grado</option>
<option value="A">A — Excelente</option>
<option value="B">B — Bueno</option>
<option value="C">C — Regular</option>
<option value="D">D — Para partes</option>
</select>
</div>
<div class="inv-field inv-field--full" style="flex-direction:row;align-items:center;gap:var(--space-2);">
<input type="checkbox" id="ynkUnique" checked style="width:auto;" />
<label for="ynkUnique" style="text-transform:none;letter-spacing:0;font-size:var(--text-body-sm);">Pieza única (1 de 1) — proviene de un vehículo específico</label>
</div>
<!-- Donor vehicle -->
<div class="inv-field inv-field--full" style="border-top:1px solid var(--color-border);padding-top:var(--space-3);">
<label>Vehículo donador</label>
</div>
<div class="inv-field inv-field--full">
<label for="ynkVin">VIN del donador</label>
<div style="display:flex;gap:var(--space-2);">
<input type="text" id="ynkVin" placeholder="17 caracteres (ej: JTDKB20U123456789)" maxlength="20" autocomplete="off" style="flex:1;text-transform:uppercase;" />
<button class="btn btn--secondary" type="button" onclick="ynkDecodeVin()" id="ynkDecodeBtn">
<svg viewBox="0 0 24 24"><path d="M4 6h16M4 12h16M4 18h10"/></svg>
Decodificar
</button>
</div>
<span class="ynk-hint" id="ynkVinStatus">Decodifica el VIN para autocompletar marca, modelo y año.</span>
</div>
<div class="inv-field">
<label for="ynkDonorMake">Marca del donador</label>
<input type="text" id="ynkDonorMake" placeholder="Ej: Nissan" autocomplete="off" />
</div>
<div class="inv-field">
<label for="ynkDonorModel">Modelo del donador</label>
<input type="text" id="ynkDonorModel" placeholder="Ej: Sentra" autocomplete="off" />
</div>
<div class="inv-field">
<label for="ynkDonorYear">Año del donador</label>
<input type="number" id="ynkDonorYear" min="1900" max="2100" placeholder="2015" />
</div>
<div class="inv-field">
<label for="ynkDonorKm">Km del donador</label>
<input type="number" id="ynkDonorKm" min="0" placeholder="120000" />
</div>
<!-- Inventory details -->
<div class="inv-field inv-field--full" style="border-top:1px solid var(--color-border);padding-top:var(--space-3);">
<label>Inventario</label>
</div>
<div class="inv-field">
<label for="ynkOwnership">Propiedad</label>
<select id="ynkOwnership">
<option value="propio" selected>Propio</option>
<option value="consigna">Consigna (de tercero)</option>
</select>
</div>
<div class="inv-field" id="ynkConsignmentWrap" style="display:none;">
<label for="ynkConsignmentId">Consignación</label>
<select id="ynkConsignmentId">
<option value="">— Selecciona consignante —</option>
</select>
</div>
<div class="inv-field">
<label for="ynkLocation">Ubicación</label>
<input type="text" id="ynkLocation" placeholder="Ej: YON-A-3-2" autocomplete="off" />
</div>
<div class="inv-field">
<label for="ynkWarranty">Cobertura (días)</label>
<input type="number" id="ynkWarranty" min="0" placeholder="30" />
<span class="ynk-hint">Se guarda como cobertura por defecto del módulo.</span>
</div>
<div class="inv-field">
<label for="ynkInitialStock">Stock inicial</label>
<input type="number" id="ynkInitialStock" min="0" placeholder="1" value="1" />
</div>
<div class="inv-field">
<label for="ynkSerial">No. de serie (opcional)</label>
<input type="text" id="ynkSerial" placeholder="Serie de la pieza" autocomplete="off" />
</div>
<!-- Photos -->
<div class="inv-field inv-field--full">
<label>Fotos de la pieza</label>
<div class="ynk-drop">
<input type="file" id="ynkPhotos" accept="image/jpeg,image/png,image/webp" multiple style="display:none;" />
<button class="btn btn--ghost btn--sm" type="button" onclick="document.getElementById('ynkPhotos').click()">
<svg viewBox="0 0 24 24"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>
Elegir fotos
</button>
<button class="btn btn--ghost btn--sm" type="button" id="ynkGradeSuggestBtn" onclick="ynkSuggestGradeFromPhoto()">
Sugerir grado por foto (IA)
</button>
<span class="ynk-hint" id="ynkPhotoCount" style="display:block;margin-top:6px;">Sin fotos seleccionadas</span>
<span class="ynk-hint" id="ynkGradeSuggestStatus" style="display:block;margin-top:4px;"></span>
</div>
<div class="ynk-photo-grid" id="ynkPhotoPreview"></div>
</div>
</div>
<div class="ynk-save-result" id="ynkSaveResult"></div>
<div style="display:flex;justify-content:flex-end;gap:var(--space-3);margin-top:var(--space-4);border-top:1px solid var(--color-border);padding-top:var(--space-4);">
<button class="btn btn--ghost" type="button" onclick="ynkResetPieceForm()">Limpiar</button>
<button class="btn btn--primary" type="button" id="ynkSaveBtn" onclick="ynkSubmitPiece()">Guardar pieza</button>
</div>
</div><!-- /#ynkNuevaSimple -->
<!-- ── Alta por donador completo (varias piezas) ────────────── -->
<div id="ynkNuevaFull" style="display:none;">
<p class="ynk-hint" style="margin-bottom:var(--space-3);">
Registra el vehículo donador y todas sus piezas de una sola vez. Cada fila crea una pieza usada con su SKU.
</p>
<div class="inv-form-grid">
<div class="inv-field inv-field--full">
<label for="ynkFullVin">VIN del donador *</label>
<div style="display:flex;gap:var(--space-2);">
<input type="text" id="ynkFullVin" placeholder="17 caracteres" maxlength="20" autocomplete="off" style="flex:1;text-transform:uppercase;" />
<button class="btn btn--secondary" type="button" onclick="ynkFullDecodeVin()" id="ynkFullDecodeBtn">Decodificar</button>
</div>
<span class="ynk-hint" id="ynkFullVinStatus">Decodifica el VIN para autocompletar marca, modelo y año.</span>
</div>
<div class="inv-field">
<label for="ynkFullMake">Marca del donador *</label>
<input type="text" id="ynkFullMake" placeholder="Ej: Nissan" autocomplete="off" />
</div>
<div class="inv-field">
<label for="ynkFullModel">Modelo del donador *</label>
<input type="text" id="ynkFullModel" placeholder="Ej: Sentra" autocomplete="off" />
</div>
<div class="inv-field">
<label for="ynkFullYear">Año *</label>
<input type="number" id="ynkFullYear" min="1900" max="2100" placeholder="2015" />
</div>
<div class="inv-field">
<label for="ynkFullMileage">Kilometraje</label>
<input type="number" id="ynkFullMileage" min="0" placeholder="120000" />
</div>
<div class="inv-field">
<label for="ynkFullColor">Color</label>
<input type="text" id="ynkFullColor" placeholder="Ej: Gris" autocomplete="off" />
</div>
<div class="inv-field">
<label for="ynkFullOwnership">Propiedad</label>
<select id="ynkFullOwnership">
<option value="propio" selected>Propio</option>
<option value="consigna">Consigna (de tercero)</option>
</select>
</div>
<div class="inv-field" id="ynkFullConsignmentWrap" style="display:none;">
<label for="ynkFullConsignmentId">Consignación</label>
<select id="ynkFullConsignmentId">
<option value="">— Selecciona consignante —</option>
</select>
</div>
<div class="inv-field inv-field--full">
<label for="ynkFullTotalCost">Costo total del vehículo (opcional)</label>
<input type="number" id="ynkFullTotalCost" step="0.01" min="0" placeholder="0.00" />
<span class="ynk-hint">Si lo capturas, se prorratea entre las piezas del donador.</span>
</div>
</div>
<div style="display:flex;justify-content:space-between;align-items:center;margin:var(--space-4) 0 var(--space-2);gap:var(--space-3);flex-wrap:wrap;">
<h4 style="margin:0;font-size:var(--text-caption);letter-spacing:var(--tracking-widest);text-transform:uppercase;color:var(--color-text-muted);">Piezas del donador</h4>
<div style="display:flex;gap:var(--space-2);">
<button class="btn btn--ghost btn--sm" type="button" onclick="ynkFullAddRow()">+ Agregar fila</button>
<button class="btn btn--ghost btn--sm" type="button" onclick="ynkFullClearRows()">Vaciar filas</button>
</div>
</div>
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th>Nombre de la pieza *</th>
<th style="width:130px;">Precio *</th>
<th style="width:130px;">Costo</th>
<th style="width:110px;">Grado</th>
<th style="width:150px;">Ubicación</th>
<th style="width:70px;"></th>
</tr>
</thead>
<tbody id="ynkFullPiecesBody"></tbody>
</table>
</div>
<div class="ynk-save-result" id="ynkFullResult"></div>
<div style="display:flex;justify-content:flex-end;gap:var(--space-3);margin-top:var(--space-4);border-top:1px solid var(--color-border);padding-top:var(--space-4);">
<button class="btn btn--ghost" type="button" onclick="ynkFullResetForm()">Limpiar piezas</button>
<button class="btn btn--primary" type="button" id="ynkFullSaveBtn" onclick="ynkSubmitDonorFull()">Registrar donador y piezas</button>
</div>
</div><!-- /#ynkNuevaFull -->
</div>
</div>
<!-- ===================================================================
TAB 3 — POR DONADOR
=================================================================== -->
<div class="tab-panel" id="panel-donador" role="tabpanel">
<div class="toolbar">
<div class="ynk-seg" role="tablist" aria-label="Vistas por donador">
<button type="button" id="ynkSegDonors" class="is-active" onclick="ynkSetDonorView('donors')">¿Qué tengo de este auto?</button>
<button type="button" id="ynkSegPieces" onclick="ynkSetDonorView('pieces')">Piezas por donador</button>
</div>
<div class="toolbar__spacer"></div>
<button class="btn btn--ghost btn--sm" onclick="ynkSearchDonors(1)">Actualizar</button>
</div>
<!-- View A: donors summary -->
<div id="ynkDonorViewDonors">
<div class="toolbar">
<div class="search-box">
<svg viewBox="0 0 24 24" stroke-linecap="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="ynkDonorQ" placeholder="Buscar por VIN, marca o modelo del donador…" autocomplete="off" />
</div>
<span class="ynk-hint">Agrupa las piezas registradas por vehículo donador.</span>
</div>
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th>VIN</th>
<th>Marca</th>
<th>Modelo</th>
<th>Año</th>
<th style="text-align:right">Piezas</th>
<th style="text-align:right">Valor</th>
<th>Acciones</th>
</tr>
</thead>
<tbody id="ynkDonorsBody">
<tr><td colspan="7" class="ynk-empty">Cargando donadores…</td></tr>
</tbody>
</table>
<div class="table-footer">
<span id="ynkDonorsFooter">—</span>
<div class="pagination" id="ynkDonorsPagination"></div>
</div>
</div>
</div>
<!-- View B: pieces of a donor -->
<div id="ynkDonorViewPieces" style="display:none;">
<div class="toolbar">
<input type="text" class="select-filter" id="ynkDonorMake" placeholder="Marca (donador)" autocomplete="off" style="width:150px;" />
<input type="text" class="select-filter" id="ynkDonorModel" placeholder="Modelo (donador)" autocomplete="off" style="width:150px;" />
<input type="number" class="select-filter" id="ynkDonorYear" placeholder="Año" min="1900" max="2100" style="width:100px;" />
<input type="text" class="select-filter" id="ynkDonorVin" placeholder="VIN donador" autocomplete="off" style="width:190px;text-transform:uppercase;" />
<input type="text" class="select-filter" id="ynkDonorPieceQ" placeholder="Nombre / No. parte" autocomplete="off" style="width:170px;" />
<button class="btn btn--primary btn--sm" onclick="ynkSearchDonorPieces(1)">
<svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
Buscar
</button>
<button class="btn btn--ghost btn--sm" onclick="ynkClearDonorPieceFilters()">Limpiar</button>
</div>
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th style="width:60px;">Foto</th>
<th>No. Parte</th>
<th>Nombre</th>
<th>Grado</th>
<th>Condición</th>
<th>Donador</th>
<th style="text-align:right">Precio</th>
<th style="text-align:right">Stock</th>
<th>Ubicación</th>
<th>Acciones</th>
</tr>
</thead>
<tbody id="ynkDonorPiecesBody">
<tr><td colspan="10" class="ynk-empty">Aplica un filtro o presiona Buscar.</td></tr>
</tbody>
</table>
<div class="table-footer">
<span id="ynkDonorPiecesFooter">—</span>
<div class="pagination" id="ynkDonorPiecesPagination"></div>
</div>
</div>
</div>
</div>
<!-- ===================================================================
TAB 4 — CONSIGNACIONES
=================================================================== -->
<div class="tab-panel" id="panel-consignaciones" role="tabpanel">
<div class="toolbar">
<div class="search-box">
<svg viewBox="0 0 24 24" stroke-linecap="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="ynkConsignQ" placeholder="Filtrar por consignante, RFC o teléfono…" autocomplete="off" />
</div>
<div class="toolbar__spacer"></div>
<button class="btn btn--ghost btn--sm" onclick="ynkLoadConsignments()">Actualizar</button>
<button class="btn btn--primary btn--sm" onclick="ynkOpenConsignmentModal()">
<svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
Nueva consigna
</button>
</div>
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th>#</th>
<th>Consignante</th>
<th>RFC</th>
<th>Teléfono</th>
<th style="text-align:right">Comisión</th>
<th style="text-align:right">Piezas</th>
<th style="text-align:right">Vendido</th>
<th style="text-align:right">Comisión $</th>
<th>Estado</th>
<th>Creada</th>
<th>Acciones</th>
</tr>
</thead>
<tbody id="ynkConsignBody">
<tr><td colspan="11" class="ynk-empty">Cargando consignaciones…</td></tr>
</tbody>
</table>
<div class="table-footer">
<span id="ynkConsignFooter">—</span>
<div class="pagination"></div>
</div>
</div>
</div>
<!-- ===================================================================
TAB 5 — REPORTES
=================================================================== -->
<div class="tab-panel" id="panel-reportes" role="tabpanel">
<div class="toolbar">
<div class="ynk-seg" role="tablist" aria-label="Reportes de Yonke">
<button type="button" id="ynkRepSegAging" class="is-active" onclick="ynkSetReportView('aging')">Antigüedad en patio</button>
<button type="button" id="ynkRepSegSummary" onclick="ynkSetReportView('summary')">Resumen</button>
<button type="button" id="ynkRepSegDup" onclick="ynkSetReportView('duplicates')">Duplicados</button>
</div>
<div class="toolbar__spacer"></div>
</div>
<!-- Aging -->
<div id="ynkRepAging">
<div class="toolbar">
<label class="ynk-hint" for="ynkAgingDays">Días mínimos en patio:</label>
<input type="number" class="select-filter" id="ynkAgingDays" value="90" min="0" style="width:100px;" />
<button class="btn btn--primary btn--sm" onclick="ynkLoadAging()">Consultar</button>
<div class="toolbar__spacer"></div>
<span class="ynk-hint" id="ynkAgingSummary"></span>
</div>
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th>ID</th>
<th>No. Parte</th>
<th>Nombre</th>
<th>Grado</th>
<th style="text-align:right">Precio</th>
<th style="text-align:right">Costo</th>
<th style="text-align:right">Días en patio</th>
</tr>
</thead>
<tbody id="ynkAgingBody">
<tr><td colspan="7" class="ynk-empty">Presiona "Consultar" para ver las piezas con más tiempo en patio.</td></tr>
</tbody>
</table>
<div class="table-footer">
<span id="ynkAgingFooter">—</span>
<div class="pagination" id="ynkAgingPagination"></div>
</div>
</div>
</div>
<!-- Summary -->
<div id="ynkRepSummary" style="display:none;">
<div class="toolbar">
<button class="btn btn--primary btn--sm" onclick="ynkLoadReportSummary()">Cargar resumen</button>
<div class="toolbar__spacer"></div>
<span class="ynk-hint" id="ynkRepSummaryUpdated"></span>
</div>
<div id="ynkRepSummaryBody" class="ynk-rep-grid">
<p class="ynk-hint">Presiona "Cargar resumen".</p>
</div>
</div>
<!-- Duplicates -->
<div id="ynkRepDup" style="display:none;">
<div class="toolbar">
<label class="ynk-hint" for="ynkDupThreshold">Umbral de similitud (0–1):</label>
<input type="number" class="select-filter" id="ynkDupThreshold" value="0.9" step="0.05" min="0" max="1" style="width:100px;" />
<button class="btn btn--primary btn--sm" onclick="ynkLoadDuplicates()">Buscar duplicados</button>
<div class="toolbar__spacer"></div>
<span class="ynk-hint">Coincidencias por nombre / No. de parte.</span>
</div>
<div class="table-wrapper">
<table class="data-table">
<thead id="ynkDupHead">
<tr><th>Grupo</th><th>Piezas</th><th>Detalle</th></tr>
</thead>
<tbody id="ynkDupBody">
<tr><td colspan="3" class="ynk-empty">Presiona "Buscar duplicados".</td></tr>
</tbody>
</table>
<div class="table-footer">
<span id="ynkDupFooter">—</span>
</div>
</div>
</div>
</div>
<!-- ===================================================================
TAB 6 — MAYOREO B2B
=================================================================== -->
<div class="tab-panel" id="panel-b2b" role="tabpanel">
<div class="toolbar">
<div class="ynk-seg" role="tablist" aria-label="Mayoreo B2B">
<button type="button" id="ynkB2bSegCatalog" class="is-active" aria-selected="true" onclick="ynkB2bView('catalog')" title="Comprar piezas usadas al mayoreo de otros negocios">Catálogo</button>
<button type="button" id="ynkB2bSegListings" aria-selected="false" onclick="ynkB2bView('listings')" title="Vender tus piezas usadas a otros negocios">Mis publicaciones</button>
<button type="button" id="ynkB2bSegOrders" aria-selected="false" onclick="ynkB2bView('orders')" title="Pedidos de mayoreo que recibes y que haces">Pedidos</button>
</div>
<div class="toolbar__spacer"></div>
<button class="btn btn--ghost btn--sm" onclick="ynkB2bRefresh()">
<svg viewBox="0 0 24 24"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-.38-4.93"/></svg>
Actualizar
</button>
</div>
<div class="ynk-b2b-help" role="note">
<strong>¿Cómo funciona el mayoreo?</strong>
Publicas una pieza usada en <em>Mis publicaciones</em> &rarr; otro negocio la compra en el <em>Catálogo</em> &rarr;
tú <strong>confirmas</strong> el pedido y lo marcas como <strong>enviado</strong> &rarr; el comprador lo marca como <strong>recibido</strong>.
En <em>Pedidos &rsaquo; Recibidos</em> gestionas lo que te compran; en <em>Pedidos &rsaquo; Mis compras</em>, lo que tú compras.
</div>
<!-- ── Sub-vista: Catálogo (comprar) ─────────────────────────── -->
<div id="ynkB2bCatalog">
<div class="toolbar">
<div class="search-box">
<svg viewBox="0 0 24 24" stroke-linecap="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="ynkB2bQ" placeholder="Buscar pieza, No. parte o vendedor…" autocomplete="off" />
</div>
<input type="text" class="select-filter" id="ynkB2bMake" placeholder="Marca donador" autocomplete="off" style="width:140px;" />
<input type="text" class="select-filter" id="ynkB2bModel" placeholder="Modelo donador" autocomplete="off" style="width:140px;" />
<input type="number" class="select-filter" id="ynkB2bYear" placeholder="Año" min="1900" max="2100" style="width:95px;" />
<select class="select-filter" id="ynkB2bGrade">
<option value="">Todo grado</option>
<option value="A">Grado A</option>
<option value="B">Grado B</option>
<option value="C">Grado C</option>
<option value="D">Grado D</option>
</select>
<button class="btn btn--primary btn--sm" onclick="loadB2bCatalogUi()">
<svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
Buscar
</button>
<button class="btn btn--ghost btn--sm" onclick="ynkB2bClearCatalogFilters()">Limpiar</button>
</div>
<div id="ynkB2bCatalogGrid" class="ynk-b2b-grid">
<p class="ynk-empty">Cargando catálogo…</p>
</div>
<div class="table-footer">
<span id="ynkB2bCatalogFooter">—</span>
<div class="pagination" id="ynkB2bCatalogPagination"></div>
</div>
</div>
<!-- ── Sub-vista: Mis publicaciones (vender) ─────────────────── -->
<div id="ynkB2bListings" style="display:none;">
<div class="toolbar">
<span class="ynk-hint" id="ynkB2bSellerStatus"></span>
<div class="toolbar__spacer"></div>
<button class="btn btn--primary btn--sm" id="ynkB2bPublishBtn" onclick="ynkB2bOpenPublish()">
<svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
Publicar pieza en mayoreo
</button>
</div>
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th style="width:60px;">Foto</th>
<th>No. Parte</th>
<th>Nombre</th>
<th style="text-align:center;">Grado</th>
<th>Donador</th>
<th style="text-align:right;">Precio mayoreo</th>
<th>Estado</th>
<th>Creada</th>
<th>Acciones</th>
</tr>
</thead>
<tbody id="ynkB2bListingsBody">
<tr><td colspan="9" class="ynk-empty">Cargando publicaciones…</td></tr>
</tbody>
</table>
<div class="table-footer">
<span id="ynkB2bListingsFooter">—</span>
<div class="pagination"></div>
</div>
</div>
</div>
<!-- ── Sub-vista: Pedidos ────────────────────────────────────── -->
<div id="ynkB2bOrders" style="display:none;">
<div class="toolbar">
<div class="ynk-seg" role="tablist" aria-label="Pedidos de mayoreo">
<button type="button" id="ynkB2bSegIncoming" class="is-active" aria-selected="true" onclick="ynkB2bOrdersView('incoming')" title="Pedidos que otros negocios te hacen">Recibidos</button>
<button type="button" id="ynkB2bSegPurchases" aria-selected="false" onclick="ynkB2bOrdersView('purchases')" title="Pedidos que tú hiciste a otros negocios">Mis compras</button>
</div>
<div class="toolbar__spacer"></div>
<span class="ynk-hint" id="ynkB2bOrdersHint"></span>
</div>
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th>#</th>
<th id="ynkB2bOrdersCounterpartyHead">Comprador</th>
<th>Piezas</th>
<th style="text-align:right;">Total</th>
<th>Estado</th>
<th>Creado</th>
<th>Acciones</th>
</tr>
</thead>
<tbody id="ynkB2bOrdersBody">
<tr><td colspan="7" class="ynk-empty">Cargando pedidos…</td></tr>
</tbody>
</table>
<div class="table-footer">
<span id="ynkB2bOrdersFooter">—</span>
<div class="pagination"></div>
</div>
</div>
</div>
</div><!-- /panel-b2b -->
</div><!-- /tab-panels -->
</main>
</div><!-- /app-shell -->
<!-- ══════════ Modal: nueva consignación ══════════ -->
<div class="inv-modal-overlay" id="ynkConsignModal">
<div class="inv-modal">
<div class="inv-modal__header">
<h3>Nueva consignación</h3>
<button class="inv-modal__close" onclick="ynkCloseModal('ynkConsignModal')">&times;</button>
</div>
<div class="inv-modal__body">
<div class="inv-form-grid">
<div class="inv-field inv-field--full">
<label for="ynkConsName">Nombre del consignante *</label>
<input type="text" id="ynkConsName" placeholder="Ej: Juan Pérez / Yonke El Sol" autocomplete="off" />
</div>
<div class="inv-field">
<label for="ynkConsRfc">RFC</label>
<input type="text" id="ynkConsRfc" placeholder="Opcional" maxlength="13" autocomplete="off" />
</div>
<div class="inv-field">
<label for="ynkConsPhone">Teléfono</label>
<input type="text" id="ynkConsPhone" placeholder="Opcional" autocomplete="off" />
</div>
<div class="inv-field">
<label for="ynkConsCommission">Comisión (%)</label>
<input type="number" id="ynkConsCommission" step="0.01" min="0" max="100" placeholder="0.00" />
</div>
<div class="inv-field inv-field--full">
<label for="ynkConsNotes">Notas</label>
<input type="text" id="ynkConsNotes" placeholder="Notas internas" autocomplete="off" />
</div>
</div>
<div class="ynk-save-result" id="ynkConsResult"></div>
</div>
<div class="inv-modal__footer">
<button class="btn btn--ghost" onclick="ynkCloseModal('ynkConsignModal')">Cancelar</button>
<button class="btn btn--primary" id="ynkConsCreateBtn" onclick="ynkCreateConsignment()">Crear consigna</button>
</div>
</div>
</div>
<!-- ══════════ Modal: detalle / liquidación de consignación ══════════ -->
<div class="inv-modal-overlay" id="ynkConsignDetailModal">
<div class="inv-modal inv-modal--wide">
<div class="inv-modal__header">
<h3 id="ynkConsignDetailTitle">Consignación</h3>
<button class="inv-modal__close" onclick="ynkCloseModal('ynkConsignDetailModal')">&times;</button>
</div>
<div class="inv-modal__body">
<div id="ynkConsignDetailInfo" class="ynk-hint" style="margin-bottom:var(--space-3);">Cargando…</div>
<div class="table-wrapper" style="margin-bottom:var(--space-4);">
<table class="data-table">
<thead>
<tr>
<th>ID Inv.</th>
<th>No. Parte</th>
<th>Pieza</th>
<th style="text-align:right">Precio acordado</th>
<th style="text-align:right">Comisión</th>
<th>Estado</th>
</tr>
</thead>
<tbody id="ynkConsignItemsBody">
<tr><td colspan="6" class="ynk-empty">Cargando piezas…</td></tr>
</tbody>
</table>
</div>
<div class="ynk-summary-card">
<h4>Agregar pieza a la consigna</h4>
<div class="inv-form-grid">
<div class="inv-field">
<label for="ynkItemInvId">ID de inventario *</label>
<input type="number" id="ynkItemInvId" min="1" placeholder="ID de la pieza" />
</div>
<div class="inv-field">
<label for="ynkItemAgreed">Precio acordado *</label>
<input type="number" id="ynkItemAgreed" step="0.01" min="0" placeholder="0.00" />
</div>
<div class="inv-field">
<label for="ynkItemCommission">Comisión</label>
<input type="number" id="ynkItemCommission" step="0.01" min="0" placeholder="0.00" />
</div>
<div class="inv-field" style="justify-content:flex-end;">
<button class="btn btn--secondary" type="button" onclick="ynkAddConsignmentItem()">Agregar pieza</button>
</div>
</div>
<div class="ynk-save-result" id="ynkConsItemResult"></div>
</div>
<div id="ynkSettleResult" style="margin-top:var(--space-4);"></div>
</div>
<div class="inv-modal__footer">
<button class="btn btn--ghost" onclick="ynkCloseModal('ynkConsignDetailModal')">Cerrar</button>
<button class="btn btn--primary" id="ynkSettleBtn" onclick="ynkSettleConsignment()">Liquidar consigna</button>
</div>
</div>
</div>
<!-- ══════════ Modal: editar pieza ══════════ -->
<div class="inv-modal-overlay" id="ynkEditModal">
<div class="inv-modal">
<div class="inv-modal__header">
<h3>Editar pieza usada</h3>
<button class="inv-modal__close" onclick="ynkCloseModal('ynkEditModal')">&times;</button>
</div>
<div class="inv-modal__body">
<input type="hidden" id="ynkEditId" />
<div class="inv-form-grid">
<div class="inv-field inv-field--full">
<label for="ynkEditName">Nombre *</label>
<input type="text" id="ynkEditName" />
</div>
<div class="inv-field">
<label for="ynkEditPart">No. de parte / SKU</label>
<input type="text" id="ynkEditPart" disabled />
<span class="ynk-hint">No editable desde Yonke.</span>
</div>
<div class="inv-field">
<label for="ynkEditBrand">Marca</label>
<input type="text" id="ynkEditBrand" disabled />
<span class="ynk-hint">No editable desde Yonke.</span>
</div>
<div class="inv-field">
<label for="ynkEditPrice">Precio *</label>
<input type="number" id="ynkEditPrice" step="0.01" min="0" />
</div>
<div class="inv-field">
<label for="ynkEditCost">Costo</label>
<input type="number" id="ynkEditCost" step="0.01" min="0" />
</div>
<div class="inv-field">
<label for="ynkEditCondition">Condición</label>
<select id="ynkEditCondition">
<option value="usado">Usado</option>
<option value="reconstruido">Reconstruido</option>
<option value="nuevo">Nuevo</option>
</select>
</div>
<div class="inv-field">
<label for="ynkEditGrade">Grado</label>
<select id="ynkEditGrade">
<option value="">Sin grado</option>
<option value="A">A</option>
<option value="B">B</option>
<option value="C">C</option>
<option value="D">D</option>
</select>
</div>
<div class="inv-field">
<label for="ynkEditOwnership">Propiedad</label>
<select id="ynkEditOwnership">
<option value="propio">Propio</option>
<option value="consigna">Consigna</option>
</select>
</div>
<div class="inv-field">
<label for="ynkEditLocation">Ubicación</label>
<input type="text" id="ynkEditLocation" />
</div>
<div class="inv-field inv-field--full">
<label for="ynkEditVin">VIN del donador</label>
<input type="text" id="ynkEditVin" style="text-transform:uppercase;" />
</div>
<div class="inv-field">
<label for="ynkEditMake">Marca del donador</label>
<input type="text" id="ynkEditMake" />
</div>
<div class="inv-field">
<label for="ynkEditModel">Modelo del donador</label>
<input type="text" id="ynkEditModel" />
</div>
<div class="inv-field">
<label for="ynkEditYear">Año del donador</label>
<input type="number" id="ynkEditYear" min="1900" max="2100" />
</div>
<div class="inv-field">
<label for="ynkEditKm">Km del donador</label>
<input type="number" id="ynkEditKm" min="0" />
</div>
<div class="inv-field inv-field--full" style="flex-direction:row;align-items:center;gap:var(--space-2);">
<input type="checkbox" id="ynkEditUnique" style="width:auto;" />
<label for="ynkEditUnique" style="text-transform:none;letter-spacing:0;font-size:var(--text-body-sm);">Pieza única (1 de 1)</label>
</div>
</div>
<div class="ynk-save-result" id="ynkEditResult"></div>
</div>
<div class="inv-modal__footer">
<button class="btn btn--ghost" onclick="ynkCloseModal('ynkEditModal')">Cancelar</button>
<button class="btn btn--primary" id="ynkEditSaveBtn" onclick="ynkSaveEditPiece()">Guardar cambios</button>
</div>
</div>
</div>
<!-- ══════════ Modal: fotos de la pieza ══════════ -->
<div class="inv-modal-overlay" id="ynkImagesModal">
<div class="inv-modal">
<div class="inv-modal__header">
<h3 id="ynkImagesTitle">Fotos de la pieza</h3>
<button class="inv-modal__close" onclick="ynkCloseModal('ynkImagesModal')">&times;</button>
</div>
<div class="inv-modal__body">
<div class="ynk-photo-grid" id="ynkImagesGrid"></div>
<div class="ynk-drop" style="margin-top:var(--space-4);">
<input type="file" id="ynkImagesInput" accept="image/jpeg,image/png,image/webp" multiple style="display:none;" />
<button class="btn btn--ghost btn--sm" type="button" onclick="document.getElementById('ynkImagesInput').click()">Agregar fotos</button>
<span class="ynk-hint" id="ynkImagesStatus" style="display:block;margin-top:6px;"></span>
</div>
</div>
<div class="inv-modal__footer">
<button class="btn btn--ghost" onclick="ynkCloseModal('ynkImagesModal')">Cerrar</button>
</div>
</div>
</div>
<!-- ══════════ Modal: ficha / acciones de pieza ══════════ -->
<div class="inv-modal-overlay" id="ynkPieceModal">
<div class="inv-modal inv-modal--wide">
<div class="inv-modal__header">
<h3 id="ynkPieceTitle">Pieza</h3>
<button class="inv-modal__close" onclick="ynkCloseModal('ynkPieceModal')">&times;</button>
</div>
<div class="inv-modal__body">
<div id="ynkPieceInfo" class="ynk-hint" style="margin-bottom:var(--space-3);">Cargando…</div>
<div class="ynk-summary-card">
<h4>Publicación y herramientas</h4>
<div style="display:flex;flex-wrap:wrap;gap:var(--space-2);">
<button class="btn btn--secondary btn--sm" type="button" onclick="ynkPublishMeli()">Publicar en MeLi</button>
<button class="btn btn--ghost btn--sm" type="button" onclick="ynkOpenLabel()">Etiqueta</button>
<button class="btn btn--ghost btn--sm" type="button" onclick="document.getElementById('ynkIdentifyInput').click()">Identificar por foto</button>
<input type="file" id="ynkIdentifyInput" accept="image/jpeg,image/png,image/webp" style="display:none;" />
<button class="btn btn--ghost btn--sm" type="button" onclick="ynkSuggestPrice()">Sugerir precio</button>
<button class="btn btn--ghost btn--sm" type="button" onclick="document.getElementById('ynkValidateInput').click()">Validar foto</button>
<input type="file" id="ynkValidateInput" accept="image/jpeg,image/png,image/webp" style="display:none;" />
</div>
<div class="inv-form-grid" style="margin-top:var(--space-3);">
<div class="inv-field">
<label for="ynkMeliCategory">Categoría MeLi (opcional)</label>
<input type="text" id="ynkMeliCategory" placeholder="Ej: MLM1747" autocomplete="off" />
</div>
<div class="inv-field">
<label for="ynkMeliPrice">Precio MeLi (opcional)</label>
<input type="number" id="ynkMeliPrice" step="0.01" min="0" placeholder="usa el precio de la pieza" />
</div>
<div class="inv-field">
<label for="ynkMeliStock">Stock MeLi (opcional)</label>
<input type="number" id="ynkMeliStock" min="0" placeholder="1" />
</div>
</div>
<div class="ynk-save-result" id="ynkPieceActionResult"></div>
</div>
<div class="ynk-summary-card" style="margin-top:var(--space-3);">
<h4>Compatibilidad — «sirve para»</h4>
<div id="ynkCompatList"><span class="ynk-hint">Cargando compatibilidad…</span></div>
<div class="inv-form-grid" style="margin-top:var(--space-3);">
<div class="inv-field">
<label for="ynkCompatMake">Marca</label>
<input type="text" id="ynkCompatMake" placeholder="Ej: Nissan" autocomplete="off" />
</div>
<div class="inv-field">
<label for="ynkCompatModel">Modelo</label>
<input type="text" id="ynkCompatModel" placeholder="Ej: Sentra" autocomplete="off" />
</div>
<div class="inv-field">
<label for="ynkCompatYear">Año</label>
<input type="number" id="ynkCompatYear" min="1900" max="2100" placeholder="2015" />
</div>
<div class="inv-field">
<label for="ynkCompatEngine">Motor (opcional)</label>
<input type="text" id="ynkCompatEngine" placeholder="Ej: 1.8L" autocomplete="off" />
</div>
<div class="inv-field" style="justify-content:flex-end;">
<button class="btn btn--secondary" type="button" onclick="ynkAddCompat()">Agregar compatibilidad</button>
</div>
</div>
<div class="ynk-save-result" id="ynkCompatResult"></div>
</div>
<div class="ynk-summary-card" style="margin-top:var(--space-3);">
<h4>Fotos</h4>
<div class="ynk-photo-grid" id="ynkPiecePhotos"></div>
</div>
</div>
<div class="inv-modal__footer">
<button class="btn btn--ghost" onclick="ynkCloseModal('ynkPieceModal')">Cerrar</button>
<button class="btn btn--secondary" type="button" onclick="ynkEditFromDetail()">Editar pieza</button>
</div>
</div>
</div>
<!-- ══════════ Modal: estado de cuenta de consignación ══════════ -->
<div class="inv-modal-overlay" id="ynkStatementModal">
<div class="inv-modal inv-modal--wide">
<div class="inv-modal__header">
<h3 id="ynkStatementTitle">Estado de cuenta</h3>
<button class="inv-modal__close" onclick="ynkCloseModal('ynkStatementModal')">&times;</button>
</div>
<div class="inv-modal__body">
<div class="inv-form-grid">
<div class="inv-field">
<label for="ynkStmtFrom">Desde</label>
<input type="date" id="ynkStmtFrom" />
</div>
<div class="inv-field">
<label for="ynkStmtTo">Hasta</label>
<input type="date" id="ynkStmtTo" />
</div>
<div class="inv-field" style="flex-direction:row;gap:var(--space-2);align-items:flex-end;">
<button class="btn btn--primary" type="button" onclick="ynkLoadStatement()">Consultar</button>
<button class="btn btn--ghost" type="button" onclick="ynkPrintStatement()">Imprimir</button>
</div>
</div>
<div id="ynkStatementInfo" class="ynk-hint" style="margin:var(--space-3) 0;"></div>
<div id="ynkStatementBody">
<p class="ynk-hint">Selecciona un periodo y presiona "Consultar".</p>
</div>
</div>
<div class="inv-modal__footer">
<button class="btn btn--ghost" onclick="ynkCloseModal('ynkStatementModal')">Cerrar</button>
</div>
</div>
</div>
<!-- ══════════ Modal: importar CSV de piezas ══════════ -->
<div class="inv-modal-overlay" id="ynkImportModal">
<div class="inv-modal">
<div class="inv-modal__header">
<h3>Importar piezas desde CSV</h3>
<button class="inv-modal__close" onclick="ynkCloseModal('ynkImportModal')">&times;</button>
</div>
<div class="inv-modal__body">
<p class="ynk-hint" style="margin-bottom:var(--space-3);">
Encabezados sugeridos: <code>name, price_1, cost, condition_grade, location, part_number, donor_vin, donor_make, donor_model, donor_year</code>.
La respuesta del servidor se muestra abajo (creadas y errores por fila).
</p>
<div class="ynk-drop">
<input type="file" id="ynkImportFile" accept=".csv,text/csv" style="display:none;" />
<button class="btn btn--ghost btn--sm" type="button" onclick="document.getElementById('ynkImportFile').click()">Elegir archivo CSV</button>
<span class="ynk-hint" id="ynkImportFileName" style="display:block;margin-top:6px;">Sin archivo seleccionado</span>
</div>
<div class="ynk-save-result" id="ynkImportResult"></div>
</div>
<div class="inv-modal__footer">
<button class="btn btn--ghost" onclick="ynkCloseModal('ynkImportModal')">Cancelar</button>
<button class="btn btn--primary" id="ynkImportBtn" onclick="ynkSubmitImport()">Importar</button>
</div>
</div>
</div>
<!-- ══════════ Modal: comprar en mayoreo ══════════ -->
<div class="inv-modal-overlay" id="ynkB2bBuyModal">
<div class="inv-modal">
<div class="inv-modal__header">
<h3 id="ynkB2bBuyTitle">Comprar en mayoreo</h3>
<button class="inv-modal__close" onclick="ynkCloseModal('ynkB2bBuyModal')">&times;</button>
</div>
<div class="inv-modal__body">
<div id="ynkB2bBuyInfo" class="ynk-hint" style="margin-bottom:var(--space-3);"></div>
<div class="inv-form-grid">
<div class="inv-field">
<label for="ynkB2bBuyQty">Cantidad *</label>
<input type="number" id="ynkB2bBuyQty" min="1" step="1" value="1" oninput="ynkB2bUpdateBuyTotal()" />
</div>
<div class="inv-field">
<label>Total estimado</label>
<div id="ynkB2bBuyTotal" style="font-family:var(--font-mono);font-weight:700;color:var(--color-primary);font-size:1.05rem;">$0.00</div>
</div>
<div class="inv-field inv-field--full">
<label for="ynkB2bBuyNotes">Notas para el vendedor</label>
<input type="text" id="ynkB2bBuyNotes" placeholder="Opcional" autocomplete="off" />
</div>
</div>
<div class="ynk-save-result" id="ynkB2bBuyResult"></div>
</div>
<div class="inv-modal__footer">
<button class="btn btn--ghost" onclick="ynkCloseModal('ynkB2bBuyModal')">Cerrar</button>
<button class="btn btn--primary" id="ynkB2bBuyBtn" onclick="ynkB2bSubmitOrder()">Crear orden</button>
</div>
</div>
</div>
<!-- ══════════ Modal: publicar pieza en mayoreo ══════════ -->
<div class="inv-modal-overlay" id="ynkB2bPublishModal">
<div class="inv-modal">
<div class="inv-modal__header">
<h3>Publicar pieza en mayoreo</h3>
<button class="inv-modal__close" onclick="ynkCloseModal('ynkB2bPublishModal')">&times;</button>
</div>
<div class="inv-modal__body">
<div class="inv-form-grid">
<div class="inv-field inv-field--full">
<label for="ynkB2bPublishPiece">Pieza usada del inventario *</label>
<select id="ynkB2bPublishPiece">
<option value="">Cargando piezas…</option>
</select>
<span class="ynk-hint">La pieza se ofrece a otros negocios al precio de mayoreo que definas.</span>
</div>
<div class="inv-field">
<label for="ynkB2bPublishPrice">Precio mayoreo *</label>
<input type="number" id="ynkB2bPublishPrice" step="0.01" min="0" placeholder="0.00" />
</div>
<div class="inv-field">
<label for="ynkB2bPublishNotes">Notas</label>
<input type="text" id="ynkB2bPublishNotes" placeholder="Opcional" autocomplete="off" />
</div>
</div>
<div class="ynk-save-result" id="ynkB2bPublishResult"></div>
</div>
<div class="inv-modal__footer">
<button class="btn btn--ghost" onclick="ynkCloseModal('ynkB2bPublishModal')">Cancelar</button>
<button class="btn btn--primary" id="ynkB2bPublishSubmitBtn" onclick="ynkB2bSubmitPublish()">Publicar</button>
</div>
</div>
</div>
<!-- ══════════ Modal: detalle de pedido mayoreo ══════════ -->
<div class="inv-modal-overlay" id="ynkB2bOrderModal">
<div class="inv-modal inv-modal--wide">
<div class="inv-modal__header">
<h3 id="ynkB2bOrderTitle">Pedido</h3>
<button class="inv-modal__close" onclick="ynkCloseModal('ynkB2bOrderModal')">&times;</button>
</div>
<div class="inv-modal__body">
<div id="ynkB2bOrderBody" class="ynk-hint">Cargando pedido…</div>
<div id="ynkB2bOrderActions" class="ynk-b2b-actions" style="margin-top:var(--space-4);"></div>
</div>
<div class="inv-modal__footer">
<button class="btn btn--ghost" onclick="ynkCloseModal('ynkB2bOrderModal')">Cerrar</button>
</div>
</div>
</div>
<!-- Offline Banner -->
<div id="offlineBanner" class="banner banner--warning" style="display:none;position:fixed;top:0;left:0;right:0;z-index:9999;border-radius:0;animation:none;">
<span class="banner__icon"></span>
<span class="banner__text" id="offlineBannerText"><strong>Modo offline</strong> — Funciones limitadas.</span>
<button class="banner__dismiss" onclick="document.getElementById('offlineBanner').style.display='none'" aria-label="Cerrar">&times;</button>
</div>
<!-- =========================================================================
SCRIPTS
========================================================================= -->
<script>
function setTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
document.querySelectorAll('.theme-btn').forEach(function(btn) {
btn.classList.toggle('is-active', btn.dataset.themeTarget === theme);
});
try { localStorage.setItem('pos_theme', theme); } catch (e) {}
}
(function() {
var saved;
try { saved = localStorage.getItem('pos_theme'); } catch (e) {}
if (saved === 'industrial' || saved === 'modern') setTheme(saved);
})();
</script>
<script src="/pos/static/js/i18n.js?v=39" defer></script>
<script src="/pos/static/js/app-init.js?v=9" defer></script>
<script src="/pos/static/js/splash-loader.js?v=33" defer></script>
<script src="/pos/static/js/pos-utils.js?v=33" defer></script>
<script src="/pos/static/js/sidebar.js?v=46" defer></script>
<script src="/pos/static/js/yonke.js?v=3" defer></script>
<script src="/pos/static/js/offline-banner.js" defer></script>
<script>if('serviceWorker' in navigator){navigator.serviceWorker.register('/pos/sw.js',{scope:'/pos/'});}</script>
<script src="/pos/static/js/pwa-install.js" defer></script>
<script src="/pos/static/js/chat.js" defer></script>
</body>
</html>