fix(catalog): descarga de plantilla de precios proveedor con token

El enlace <a> a /pos/api/supplier-catalog/prices/template fallaba con 401
porque la navegación normal no envía el header Authorization. Se reemplaza
por un botón que descarga el blob vía fetch con Bearer token y dispara la
descarga del cliente. También se corrige clase btn-primary -> btn--primary.
This commit is contained in:
2026-06-15 18:01:42 +00:00
parent b635e44302
commit 584b87f82c
2 changed files with 29 additions and 3 deletions

View File

@@ -2148,6 +2148,31 @@
} }
} }
async function downloadPriceTemplate() {
try {
var res = await fetch('/pos/api/supplier-catalog/prices/template', {
headers: { 'Authorization': 'Bearer ' + token }
});
if (!res.ok) {
var data = await res.json().catch(function() { return {}; });
if (uploadPricesStatus) uploadPricesStatus.innerHTML = '<span style="color:var(--color-error);">' + esc(data.error || 'Error al descargar plantilla') + '</span>';
return;
}
var blob = await res.blob();
var url = URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
a.download = 'supplier_prices_template.csv';
document.body.appendChild(a);
a.click();
a.remove();
URL.revokeObjectURL(url);
if (uploadPricesStatus) uploadPricesStatus.innerHTML = '<span style="color:var(--color-success);">✓ Plantilla descargada.</span>';
} catch (e) {
if (uploadPricesStatus) uploadPricesStatus.innerHTML = '<span style="color:var(--color-error);">Error de red: ' + esc(e.message) + '</span>';
}
}
function shouldShowUploadPricesButton() { function shouldShowUploadPricesButton() {
try { try {
var user = JSON.parse(localStorage.getItem('pos_employee') || '{}'); var user = JSON.parse(localStorage.getItem('pos_employee') || '{}');
@@ -2180,6 +2205,7 @@
openUploadPricesModal: openUploadPricesModal, openUploadPricesModal: openUploadPricesModal,
closeUploadPricesModal: closeUploadPricesModal, closeUploadPricesModal: closeUploadPricesModal,
submitUploadPrices: submitUploadPrices, submitUploadPrices: submitUploadPrices,
downloadPriceTemplate: downloadPriceTemplate,
}; };
// ─── INIT ─── // ─── INIT ───

View File

@@ -294,8 +294,8 @@
<input type="file" id="uploadPricesFile" accept=".csv,.xlsx,.xls" style="width:100%;" /> <input type="file" id="uploadPricesFile" accept=".csv,.xlsx,.xls" style="width:100%;" />
</div> </div>
<div style="display:flex;gap:var(--space-2);justify-content:flex-end;"> <div style="display:flex;gap:var(--space-2);justify-content:flex-end;">
<a href="/pos/api/supplier-catalog/prices/template" class="btn btn--ghost" style="text-decoration:none;">Descargar plantilla</a> <button class="btn btn--ghost" onclick="CatalogApp.downloadPriceTemplate()">Descargar plantilla</button>
<button class="btn btn-primary" onclick="CatalogApp.submitUploadPrices()">Subir precios</button> <button class="btn btn--primary" onclick="CatalogApp.submitUploadPrices()">Subir precios</button>
</div> </div>
<div id="uploadPricesStatus" style="margin-top:var(--space-3);font-size:var(--text-body-sm);"></div> <div id="uploadPricesStatus" style="margin-top:var(--space-3);font-size:var(--text-body-sm);"></div>
</div> </div>
@@ -321,7 +321,7 @@
<script src="/pos/static/js/splash-loader.js?v=1" defer></script> <script src="/pos/static/js/splash-loader.js?v=1" defer></script>
<script src="/pos/static/js/pos-utils.js?v=2" defer></script> <script src="/pos/static/js/pos-utils.js?v=2" defer></script>
<script src="/pos/static/js/sidebar.js" defer></script> <script src="/pos/static/js/sidebar.js" defer></script>
<script src="/pos/static/js/catalog.js?v=6" defer></script> <script src="/pos/static/js/catalog.js?v=7" defer></script>
<script src="/pos/static/js/offline-banner.js" defer></script> <script src="/pos/static/js/offline-banner.js" defer></script>
<script src="/pos/static/js/chat.js" defer></script> <script src="/pos/static/js/chat.js" defer></script>
<script src="/pos/static/js/sync-engine.js" defer></script> <script src="/pos/static/js/sync-engine.js" defer></script>