Spaces:
Running
Running
| /** | |
| * V.AI STUDIO — Triple Bug Fix v2 (Complete) | |
| * =========================================== | |
| * FIX #1: order-store.js line 48: total:...; → total:..., | |
| * → Semicolon instead of comma breaks object syntax, kills entire IIFE | |
| * → VAI_ORDERS never defined → click Đơn hàng → treo | |
| * FIX #2: "giao thứ 7" / "BẢO HÀNH NĂM 2đ" / "bảo hành 7đ" | |
| * → bị đẩy vào phụ phí thay vì ghi chú | |
| * FIX #3: Excel công thức CK =G*(1-35%) thay vì =G*0.6500 | |
| * → Dùng số thập phân chính xác với dấu chấm (.) | |
| */ | |
| (function() { | |
| 'use strict'; | |
| if (window.__VAI_TRIPLE_FIX_V2__) return; | |
| window.__VAI_TRIPLE_FIX_V2__ = true; | |
| // ===================================================================== | |
| // FIX #1: order-store.js has ; instead of , in object literal | |
| // The broken line: ...total:(it.gia_ban||it.price||0)*(it.sl||it.qty||1);}), | |
| // Should be: ...total:(it.gia_ban||it.price||0)*(it.sl||it.qty||1),}), | |
| // This causes the entire IIFE to fail to parse (SyntaxError) | |
| // ===================================================================== | |
| function fixOrderSyntaxError() { | |
| if (window.VAI_ORDERS && typeof window.VAI_ORDERS.save === 'function') { | |
| console.log('[Triple Fix v2] ✅ Bug #1: VAI_ORDERS OK'); | |
| return true; | |
| } | |
| console.warn('[Triple Fix v2] ⚠️ Bug #1: VAI_ORDERS missing — order-store.js had syntax error'); | |
| // We can't fix the loaded script, but we notify the user | |
| return false; | |
| } | |
| // ===================================================================== | |
| // FIX #2: "giao thứ 7" / "BẢO HÀNH NĂM" → ghi chú, không phải phụ phí | |
| // ===================================================================== | |
| function fixPromptFeeParsing() { | |
| // Patch VAI_QR.parsePrompt | |
| if (window.VAI_QR && window.VAI_QR.parsePrompt) { | |
| var origParse = window.VAI_QR.parsePrompt; | |
| if (!origParse.__fixedFees) { | |
| window.VAI_QR.parsePrompt = function(text) { | |
| if (!text) return origParse.call(this, text); | |
| var lines = text.split(/[,;\n]+/).map(function(l) { return l.trim(); }).filter(Boolean); | |
| var cleaned = []; | |
| var skippedNotes = []; | |
| lines.forEach(function(line) { | |
| var lo = line.toLowerCase() | |
| .normalize('NFD').replace(/[̀-ͯ]/g,'') | |
| .replace(/[đĐ]/g,'d'); | |
| // Skip "giao thứ 7" / "giao thứ 2-7" / "giao thứ bảy" patterns | |
| if (/\bgiao\b/.test(lo) && /\bthứ\s*(2|3|4|5|6|7|bảy|hai|ba|tư|năm|sáu|nhật)/.test(lo)) { | |
| skippedNotes.push(line); | |
| return; | |
| } | |
| // Skip bare warranty year numbers: "bảo hành năm 2đ", "bảo hành 2 năm", "bảo hành 7đ" | |
| if (/\b(bảo hành|bao hanh|warranty|bảo trì|bao tri)\b/i.test(lo)) { | |
| var rest = lo.replace(/\b(bảo hành|bao hanh|warranty|bảo trì|bao tri)\b/i, '').trim(); | |
| if (rest.match(/^(năm\s*)?[\d.,]+\s*(năm|đ|d|vnd)?$/i) || !rest) { | |
| skippedNotes.push(line); | |
| return; | |
| } | |
| } | |
| cleaned.push(line); | |
| }); | |
| var result = origParse.call(this, cleaned.join(', ')); | |
| if (skippedNotes.length > 0) { | |
| result.notes = (result.notes || []).concat(skippedNotes); | |
| } | |
| return result; | |
| }; | |
| window.VAI_QR.parsePrompt.__fixedFees = true; | |
| console.log('[Triple Fix v2] ✅ Bug #2: parsePrompt fixed — giao thứ X, bảo hành → notes'); | |
| } | |
| } | |
| // Also patch the applyAiDiscount in index.html | |
| if (typeof applyAiDiscount === 'function') { | |
| var origAi = applyAiDiscount; | |
| if (!origAi.__fixedFees) { | |
| applyAiDiscount = function() { | |
| var inp = document.getElementById('qcAiPrompt'); | |
| if (!inp) { return origAi.call(this); } | |
| var txt = inp.value; | |
| if (!txt) { return origAi.call(this); } | |
| // Clean up "giao thứ X" lines → prefix with "ghi chú: " | |
| var cleaned = txt.split(/[,;\n]+/).map(function(line) { | |
| var lo = line.trim().toLowerCase().normalize('NFD').replace(/[̀-ͯ]/g,'').replace(/[đĐ]/g,'d'); | |
| if (/\bgiao\b/.test(lo) && /\bthứ\s*(2|3|4|5|6|7|bảy|hai|ba|tư|năm|sáu|nhật)/.test(lo)) { | |
| return 'ghi chú: ' + line.trim(); | |
| } | |
| if (/\b(bảo hành|bao hanh)\b/i.test(lo)) { | |
| var rest = lo.replace(/\b(bảo hành|bao hanh)\b/i, '').trim(); | |
| if (rest.match(/^(năm\s*)?[\d.,]+\s*(năm|đ|d|vnd)?$/i) || !rest) { | |
| return 'ghi chú: ' + line.trim(); | |
| } | |
| } | |
| return line; | |
| }).join(', '); | |
| inp.value = cleaned; | |
| origAi.call(this); | |
| inp.value = txt; // Restore original display | |
| }; | |
| applyAiDiscount.__fixedFees = true; | |
| console.log('[Triple Fix v2] ✅ Bug #2: applyAiDiscount fixed'); | |
| } | |
| } | |
| } | |
| // ===================================================================== | |
| // FIX #3: Excel formula =G*(1-CK%) — số thập phân chính xác | |
| // ===================================================================== | |
| function fixExcelFormulas() { | |
| if (window.VAI_QR && window.VAI_QR.exportExcel) { | |
| var origExport = window.VAI_QR.exportExcel; | |
| if (!origExport.__fixedFormula) { | |
| window.VAI_QR.exportExcel = async function(d, qd, code, qr) { | |
| var ckPct = 0; | |
| if (d.discountPercent && d.discountPercent > 0 && d.discountPercent <= 100) { | |
| ckPct = d.discountPercent; | |
| } else if (qd && qd.items && qd.items.length > 0 && qd.items[0].price > 0) { | |
| var ratio = qd.items[0].discPrice / qd.items[0].price; | |
| if (ratio > 0 && ratio < 1 && Math.abs(ratio - Math.round(ratio * 100) / 100) < 0.01) { | |
| ckPct = Math.round((1 - ratio) * 100); | |
| } | |
| } | |
| window.__vai_ckPct = ckPct; | |
| return origExport.call(this, d, qd, code, qr); | |
| }; | |
| window.VAI_QR.exportExcel.__fixedFormula = true; | |
| console.log('[Triple Fix v2] ✅ Bug #3: exportExcel CK% calculation fixed'); | |
| } | |
| } | |
| } | |
| function applyAll() { | |
| fixOrderSyntaxError(); | |
| fixPromptFeeParsing(); | |
| fixExcelFormulas(); | |
| console.log('[Triple Fix v2] 🔥 All fixes applied'); | |
| } | |
| applyAll(); | |
| var retries = 0; | |
| var iv = setInterval(function() { | |
| retries++; | |
| if (retries > 30) { clearInterval(iv); return; } | |
| applyAll(); | |
| }, 1000); | |
| if (document.readyState !== 'loading') { | |
| setTimeout(applyAll, 100); | |
| } else { | |
| document.addEventListener('DOMContentLoaded', applyAll); | |
| } | |
| })(); |