/** * V.AI STUDIO - Auto Patch GH Excel/PDF v1046 * * TỰ ĐỘNG PATCH mà không cần sửa index.html * Chỉ cần thêm dòng: vào index.html * * FIX: * - exportDeliveryExcel/PDF nhận order parameter đúng * - Click protection (chống bấm nhiều lần) * - Blob retention 5 phút (tải nhiều lần được) */ (function() { 'use strict'; // === WAIT FOR VAI_QR READY === function initPatch() { if (typeof window.VAI_QR === 'undefined') { setTimeout(initPatch, 500); return; } // === STORE ORIGINAL FOR REFERENCE === var origExportDE = window.exportDeliveryExcel; var origExportDP = window.exportDeliveryPDF; // === HELPER: Convert order to quote data === function orderToVAIData(order) { if (!order || !order.items) return null; return { qd: { customer: { name: order.customer || '', phone: order.phone || '', email: order.email || '', addr: order.addr || '', date: order.date || '' }, items: (order.items || []).map(function(it, i) { return { stt: i + 1, image: it.image || '', name: it.name || '', model: it.model || '', specs: it.specs || it.info || '', qty: it.qty || 1, price: it.price || it.listPrice || it.discPrice || 0, discPrice: it.discPrice || it.price || 0, total: it.total || 0, note: it.note || '' }; }), grandTotal: order.grandTotal || 0 } }; } // === CLICK STATE === var activeExportBtn = null; function setBtnState(btn, exporting) { if (!btn) return; if (exporting) { btn.disabled = true; btn.dataset.vaiExporting = '1'; btn._origHtml = btn.innerHTML; btn.innerHTML = ''; } else { btn.disabled = false; btn.dataset.vaiExporting = '0'; if (btn._origHtml) btn.innerHTML = btn._origHtml; } } // === PATCH EXPORT DELIVERY EXCEL === window.exportDeliveryExcel = async function(order, opt) { var btn = document.activeElement; if (btn && btn.dataset.vaiExporting === '1') { console.log('[AutoPatch] Already exporting, ignoring...'); return; } if (btn) setBtnState(btn, true); try { // If order provided, use it if (order && order.items && order.items.length) { var vaData = orderToVAIData(order); var code = order.code || 'GH-' + Date.now(); // Use qr-payment.js internal function if available if (window.VAI_QR && window.VAI_QR._doExportDeliveryExcel) { await window.VAI_QR._doExportDeliveryExcel(vaData.qd, code); console.log('[AutoPatch] GH Excel done for order: ' + code); return; } // Fallback: rebuild HTML manually if (window.VAI_QR && window.VAI_QR.buildDeliveryHTML) { let html = window.VAI_QR.buildDeliveryHTML(vaData.qd, code); let div = document.createElement('div'); div.style.cssText = 'position:absolute;left:-9999px;top:0;width:700px;background:#fff;padding:28px'; div.innerHTML = html; document.body.appendChild(div); await new Promise(r => setTimeout(r, 300)); if (window.ExcelJS) { let wb = new ExcelJS.Workbook(); let ws = wb.addWorksheet('Giao hàng'); ws.views = [{showGridLines: false}]; ws.columns = [{width: 5}, {width: 34}, {width: 14}, {width: 8}, {width: 20}]; ws.getCell('A1').value = 'PHIẾU GIAO HÀNG'; ws.getCell('A2').value = 'KH: ' + (vaData.qd.customer.name || ''); ws.getCell('D2').value = 'Mã: ' + code; ws.getCell('A3').value = 'SĐT: ' + (vaData.qd.customer.phone || ''); ws.getCell('A4').value = 'Địa chỉ: ' + (vaData.qd.customer.addr || ''); let headerRow = ws.getRow(6); ['STT', 'Tên sản phẩm', 'Mã SP', 'SL', 'Ghi chú'].forEach(function(h, i) { let c = headerRow.getCell(i + 1); c.value = h; c.fill = {type: 'pattern', pattern: 'solid', fgColor: {argb: 'FF003F62'}}; c.font = {bold: true, color: {argb: 'FFFFFFFF'}}; }); let cr = 7; (vaData.qd.items || []).forEach(function(it) { let row = ws.getRow(cr); row.getCell(1).value = it.stt; row.getCell(2).value = it.name; row.getCell(3).value = it.model; row.getCell(4).value = it.qty; row.getCell(5).value = it.note; cr++; }); let buf = await wb.xlsx.writeBuffer(); let blob = new Blob([buf], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'}); let url = URL.createObjectURL(blob); let a = document.createElement('a'); a.href = url; a.download = 'GH-' + code + '.xlsx'; a.click(); // Keep blob for 5 minutes setTimeout(() => URL.revokeObjectURL(url), 300000); } div.remove(); return; } } // Fallback to original if (origExportDE) { await origExportDE.call(this, order, opt); } } catch (e) { console.error('[AutoPatch] Export error:', e); if (typeof showToast === 'function') { showToast('❌ Xuất file thất bại: ' + (e.message || e)); } } finally { setTimeout(() => { if (btn) setBtnState(btn, false); }, 1000); } }; // === PATCH EXPORT DELIVERY PDF === window.exportDeliveryPDF = async function(order, opt) { var btn = document.activeElement; if (btn && btn.dataset.vaiExporting === '1') { console.log('[AutoPatch] Already exporting, ignoring...'); return; } if (btn) setBtnState(btn, true); try { if (order && order.items && order.items.length) { var vaData = orderToVAIData(order); var code = order.code || 'GH-' + Date.now(); if (window.VAI_QR && window.VAI_QR._doExportDeliveryPDF) { await window.VAI_QR._doExportDeliveryPDF(vaData.qd, code); console.log('[AutoPatch] GH PDF done for order: ' + code); return; } if (window.VAI_QR && window.VAI_QR.buildDeliveryHTML) { let html = window.VAI_QR.buildDeliveryHTML(vaData.qd, code); let div = document.createElement('div'); div.style.cssText = 'position:absolute;left:-9999px;top:0;width:700px;background:#fff;padding:28px'; div.innerHTML = html; document.body.appendChild(div); await new Promise(r => setTimeout(r, 300)); if (window.html2canvas && window.jspdf) { let canvas = await html2canvas(div, {scale: 2, useCORS: true, backgroundColor: '#fff'}); let imgData = canvas.toDataURL('image/png'); let {jsPDF} = window.jspdf; let pdf = new jsPDF({orientation: 'p', unit: 'pt', format: 'a4'}); let margin = 25; let usableW = 595 - margin * 2; let ratio = canvas.height / canvas.width; let imgH = usableW * ratio; pdf.addImage(imgData, 'PNG', margin, margin, usableW, imgH > 842 - margin * 2 ? 842 - margin * 2 : imgH); pdf.save('GH-' + code + '.pdf'); } div.remove(); return; } } if (origExportDP) { await origExportDP.call(this, order, opt); } } catch (e) { console.error('[AutoPatch] Export error:', e); if (typeof showToast === 'function') { showToast('❌ Xuất file thất bại: ' + (e.message || e)); } } finally { setTimeout(() => { if (btn) setBtnState(btn, false); }, 1000); } }; console.log('[AutoPatch v1046] GH Excel/PDF patched - ready!'); } // Start patching if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initPatch); } else { initPatch(); } // Also run after delays (HF Spaces loads scripts dynamically) setTimeout(initPatch, 2000); setTimeout(initPatch, 5000); })();