/** * V.AI STUDIO — Order Store v18 * * CHANGES from v16: * - SYNC FROM SERVER: Khi page load → fetch /orders từ bot → merge vào localStorage * → Đơn từ thiết bị/user khác sẽ xuất hiện trong danh sách! * - Retry queue vẫn hoạt động (push to server) */ (function(){ 'use strict'; var STORE_KEY='vai_orders'; var QUEUE_KEY='vai_sync_queue'; var KETOAN_API='https://bep40-vaistudio-ketoan-bot.hf.space'; var KH_LIST=[];var KH_LOADED=false; // ===== SYNC FROM SERVER (NEW in v17) ===== function resolveOrderItemImage(it){ var img=it.image||it.img||'';if(img)return img; try{var data=(typeof D!=='undefined'&&D)||window._vaiGetD&&window._vaiGetD()||[];var code=(it.ma||it.model||'').toString().toLowerCase().replace(/[^a-z0-9]/g,'');var name=(it.ten||it.name||'').toString().toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/[^a-z0-9]/g,'');for(var i=0;i=0 && !so.overwrite && !so.savedAt)return; // Convert server format to local format var order={ code:code, customer:so.khach||so.customer||'', phone:so.dien_thoai||so.phone||'', email:so.email||'', addr:so.dia_chi||so.addr||'', date:so.date||so.ngay||'', items:(so.items||[]).map(function(it){return{name:it.ten||it.name||'',model:it.ma||it.model||'',brand:it.brand||'',image:resolveOrderItemImage(it),specs:orderItemInfo(it),info:orderItemInfo(it),dim_info:orderItemInfo(it),note:it.note||'',qty:it.sl||it.qty||1,price:it.listPrice||it.originalPrice||it.price||it.gia_goc||it.gia_niem_yet||it.gia_ban||0,discPrice:it.discPrice||it.gia_ban||it.price||0,total:(it.gia_ban||it.price||0)*(it.sl||it.qty||1)};}), fees:so.fees||[], deposit:so.deposit||0, discountPercent:so.discountPercent||0, grandTotal:so.grandTotal||0, remaining:so.remaining||0, status:so.status||(so.confirmedAt?'confirmed':'pending'), savedAt:so.savedAt||so.confirmedAt||'', confirmedAt:so.confirmedAt||'', ma_kh:so.ma_kh||'' }; if(existingIdx>=0)localOrders[existingIdx]=order;else localOrders.unshift(order); localCodes[code]=true; added++; }); if(added>0){ saveOrders(localOrders); console.log('[OS v18] ☁️ Synced '+added+' orders from server. Total local: '+localOrders.length); } }).catch(function(e){ console.log('[OS v18] Server sync skip (bot may be sleeping)'); }); } // Sync from server 3s after page load (give bot time to wake up) setTimeout(syncFromServer,3000); // Also sync every 60s setInterval(syncFromServer,60000); // ===== RETRY QUEUE ===== function getQueue(){try{return JSON.parse(localStorage.getItem(QUEUE_KEY)||'[]');}catch(e){return[];}} function saveQueue(q){localStorage.setItem(QUEUE_KEY,JSON.stringify(q));} function addToQueue(endpoint,payload){var q=getQueue();q.push({endpoint:endpoint,payload:payload,attempts:0,addedAt:Date.now()});saveQueue(q);} function processQueue(){ var q=getQueue();if(!q.length)return; var item=q[0]; fetch(KETOAN_API+item.endpoint,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(item.payload)}) .then(function(r){if(r.ok){q.shift();saveQueue(q);console.log('[OS v18] Queue sync OK: '+item.endpoint+' ('+q.length+' remaining)');if(q.length)setTimeout(processQueue,2000);}else throw new Error('HTTP '+r.status);}) .catch(function(){item.attempts=(item.attempts||0)+1;if(item.attempts>50){q.shift();}saveQueue(q);}); } setInterval(processQueue,30000); setTimeout(processQueue,5000); function syncOrderToBot(order){ var payload={ma_don:order.code,khach:order.customer||'',dien_thoai:order.phone||'',dia_chi:order.addr||'',items:(order.items||[]).map(function(it){return{ma:it.model||'',model:it.model||'',ten:it.name||'',name:it.name||'',brand:it.brand||'',image:resolveOrderItemImage(it),img:it.image||it.img||'',specs:orderItemInfo(it),info:orderItemInfo(it),dim_info:orderItemInfo(it),sl:it.qty||1,qty:it.qty||1,gia_ban:it.discPrice||it.price||0,price:it.listPrice||it.originalPrice||it.price||0,discPrice:it.discPrice||it.price||0};}),fees:order.fees||[],grandTotal:order.grandTotal||0,deposit:order.deposit||0,remaining:order.remaining||0,discountPercent:order.discountPercent||0,itemDiscounts:order.itemDiscounts||{},email:order.email||'',date:order.date||'',status:order.status||'pending',savedAt:order.savedAt||new Date().toISOString()}; if(order.confirmedAt)payload.confirmedAt=order.confirmedAt; fetch(KETOAN_API+'/order-saved',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)}) .then(function(r){if(!r.ok)throw new Error();return r.json();}) .catch(function(){addToQueue('/order-saved',payload);}); } function sendToKetoanBot(order){ var payload={ma_don:order.code,khach:order.customer||'',dien_thoai:order.phone||'',dia_chi:order.addr||'',items:(order.items||[]).map(function(it){return{ma:it.model||'',model:it.model||'',ten:it.name||'',name:it.name||'',brand:it.brand||'',image:resolveOrderItemImage(it),img:it.image||it.img||'',specs:orderItemInfo(it),info:orderItemInfo(it),dim_info:orderItemInfo(it),sl:it.qty||1,qty:it.qty||1,gia_ban:it.discPrice||it.price||0,price:it.listPrice||it.originalPrice||it.price||0,discPrice:it.discPrice||it.price||0};}),fees:order.fees||[],grandTotal:order.grandTotal||0,deposit:order.deposit||0,remaining:order.remaining||0,discountPercent:order.discountPercent||0,itemDiscounts:order.itemDiscounts||{},confirmedAt:order.confirmedAt||new Date().toISOString(),email:order.email||'',date:order.date||''}; fetch(KETOAN_API+'/order-confirmed',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)}) .then(function(r){if(!r.ok)throw new Error();return r.json();}) .then(function(d){if(d.ok&&typeof showToast==='function')showToast('📨 Đã gửi kế toán!');}) .catch(function(){addToQueue('/order-confirmed',payload);if(typeof showToast==='function')showToast('⏳ Bot đang khởi động, sẽ tự gửi lại...');}); } function loadKhachHang(){if(KH_LOADED)return Promise.resolve(KH_LIST);return fetch(KETOAN_API+'/khachhang',{method:'GET'}).then(function(r){return r.json();}).then(function(d){KH_LIST=d.data||[];KH_LOADED=true;return KH_LIST;}).catch(function(){KH_LOADED=true;return KH_LIST;});} setTimeout(loadKhachHang,1000); function findKH(query){if(!query||query.length<1)return[];var q=query.toLowerCase();return KH_LIST.filter(function(kh){return(kh.ma_kh||'').toLowerCase().includes(q)||(kh.ten||'').toLowerCase().includes(q)||(kh.sdt||'').includes(q);});} function getKHCKRate(kh,brand){if(!kh||!brand)return null;var b=brand.toLowerCase().replace(/\s/g,'');var codes=kh.ck_codes||[];for(var i=0;i=0)orders[idx]=order;else orders.unshift(order);saveOrders(orders);return orders;} function deleteOrder(code){saveOrders(getOrders().filter(function(o){return o.code!==code;}));} function searchOrders(q,statusFilter){var all=getOrders();if(statusFilter&&statusFilter!=='all')all=all.filter(function(o){return(o.status||'pending')===statusFilter;});if(!q||!q.trim())return all;var s=q.toLowerCase().trim();return all.filter(function(o){return(o.code||'').toLowerCase().includes(s)||(o.customer||'').toLowerCase().includes(s)||(o.phone||'').includes(s);});} function fmt(n){if(!n||isNaN(n))return'0đ';return Number(n).toLocaleString('vi-VN')+'đ';} function formatInfoVal(v){ if(v==null||v==='')return ''; if(typeof v==='string'){ var t=v.trim(); if(!t||t==='{}'||t==='[]')return ''; if((t[0]==='{'&&t[t.length-1]==='}')||(t[0]==='['&&t[t.length-1]===']')){try{return formatInfoVal(JSON.parse(t));}catch(e){return t;}} return t; } if(Array.isArray(v))return v.map(formatInfoVal).filter(Boolean).join('; '); if(typeof v==='object'){ var out=[];Object.keys(v).forEach(function(k){var val=v[k];if(val==null||val===''||(typeof val==='object'&&!Array.isArray(val)&&!Object.keys(val).length))return;out.push(k+': '+formatInfoVal(val));}); return out.join('; '); } return String(v); } function orderItemInfo(it){it=it||{};return formatInfoVal(it.info||it.thong_tin||it.dim_info||it.dimension_info||it.specs||it.summary||it.desc||'');} function compactKey(s){return (s||'').toString().normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/[đĐ]/g,'d').toLowerCase().replace(/[^a-z0-9]/g,'');} function itemKeys(it){var arr=[];['model','sku','code','ma','name'].forEach(function(k){if(it&&it[k])arr.push(compactKey(it[k]));});if(it&&it.name){String(it.name).split('|').forEach(function(x){arr.push(compactKey(x));});}return arr.filter(function(x){return x&&x.length>=3;});} function matchItemDiscount(it,itemDiscounts){itemDiscounts=itemDiscounts||{};var keys=itemKeys(it);for(var i=0;i=3&&k.length>=3&&(k.indexOf(code)!==-1||code.indexOf(k)!==-1))return itemDiscounts[code];}}return 0;} function parseItemDiscountLine(line,map){var lo=(line||'').toString().normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/[đĐ]/g,'d').toLowerCase();var re1=/\b([a-z]{1,8}[a-z0-9._\-\/]{1,30}\d[a-z0-9._\-\/]*)\b\s*(?:[:=\-]|\s+)?\s*(?:ck|chiet\s*khau|giam|discount)\s*([\d.,]+)\s*%?/gi;var re2=/(?:ck|chiet\s*khau|giam|discount)\s*\b([a-z]{1,8}[a-z0-9._\-\/]{1,30}\d[a-z0-9._\-\/]*)\b\s*([\d.,]+)\s*%?/gi;var m;while((m=re1.exec(lo))!==null){var v=parseFloat(m[2].replace(/\./g,'').replace(',','.'));if(v>0&&v<=100)map[compactKey(m[1])]=v;}while((m=re2.exec(lo))!==null){var v2=parseFloat(m[2].replace(/\./g,'').replace(',','.'));if(v2>0&&v2<=100)map[compactKey(m[1])]=v2;}} function esc(s){return(s||'').replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"');} function parsePrompt(text){ var fees=[],deposit=0,discountPercent=0,note='',itemDiscounts={}; if(!text)return{fees:fees,deposit:deposit,discountPercent:discountPercent,note:note,itemDiscounts:itemDiscounts}; text.split(/[,;\n]+/).forEach(function(line){ line=line.trim();if(!line)return; parseItemDiscountLine(line,itemDiscounts); if(Object.keys(itemDiscounts).length&&line.match(/\b[A-Za-z]{1,8}[A-Za-z0-9._\-\/]{1,30}\d[A-Za-z0-9._\-\/]*\b/i)&&line.match(/\b(ck|chiết khấu|chiet khau|giảm|giam|discount)\b/i))return; var lo=line.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/[đĐ]/g,'d'); // Ghi chú if(lo.match(/^(ghi chu|note|luu y)/)){note=line.replace(/^(ghi chú|ghi chu|note|lưu ý|luu y)[:\s]*/i,'').trim();return;} // CK var ck=lo.match(/^(ck|chiet khau|giam|discount)\s*([\d.,]+)\s*(%|k|tr)?/); if(ck){var v=parseFloat(ck[2].replace(/\./g,'').replace(',','.'));if((!ck[3]||ck[3]===''||ck[3]==='%')&&v>0&&v<=100)discountPercent=v;return;} // Cọc if(lo.match(/coc|dat coc|deposit/)){var dm=lo.match(/([\d.,]+)\s*(k|tr|trieu|%)?/);if(dm){var a=parseFloat(dm[1].replace(/\./g,'').replace(',','.'));var u2=(dm[2]||'').toLowerCase();if(u2==='%'){deposit=a;/* percent handled separately */}else{if(u2==='k')a*=1000;if(u2==='tr'||u2==='trieu')a*=1000000;deposit=a;}}return;} // Phụ phí: giao hàng, lắp đặt, vận chuyển, phí khác var feeMatch=lo.match(/^(giao hang|giao|lap dat|lap|van chuyen|ship|phi giao|phi lap|phi khac|phu phi|phi)\s*([\d.,]+)\s*(k|tr|trieu|m|nghin)?/); if(feeMatch){ var amt=parseFloat(feeMatch[2].replace(/\./g,'').replace(',','.')); var u=(feeMatch[3]||'').toLowerCase(); if(u==='k'||u==='nghin')amt*=1000;else if(u==='tr'||u==='trieu'||u==='m')amt*=1000000;else if(amt>0&&amt<500)amt*=1000; var labelMap={'giao hang':'Phí giao hàng','giao':'Phí giao hàng','lap dat':'Phí lắp đặt','lap':'Phí lắp đặt','van chuyen':'Phí vận chuyển','ship':'Phí vận chuyển','phi giao':'Phí giao hàng','phi lap':'Phí lắp đặt','phi khac':'Phí khác','phu phi':'Phụ phí','phi':'Phụ phí'}; fees.push({label:labelMap[feeMatch[1]]||'Phụ phí',amount:amt}); return; } // Fallback: line with number var m=lo.match(/([\d.,]+)\s*(k|tr|trieu|m|nghin)?/); if(m){var amt2=parseFloat(m[1].replace(/\./g,'').replace(',','.'));var u3=(m[2]||'').toLowerCase();if(u3==='k'||u3==='nghin')amt2*=1000;if(u3==='tr'||u3==='trieu'||u3==='m')amt2*=1000000;if(amt2>0){var lb=line.replace(m[0],'').trim();if(!lb||lb.length<2)lb='Phụ phí';fees.push({label:lb,amount:amt2});}} }); return{fees:fees,deposit:deposit,discountPercent:discountPercent,note:note,itemDiscounts:itemDiscounts}; } function applyDiscount(order){var pct=order.discountPercent||0;var itemDiscounts=order.itemDiscounts||{};(order.items||[]).forEach(function(it){var base=it.price||0;var ip=matchItemDiscount(it,itemDiscounts);if(ip>0)it.discPrice=Math.round(base*(1-ip/100));else if(pct>0)it.discPrice=Math.round(base*(1-pct/100));if(!it.discPrice)it.discPrice=base;it.total=(it.discPrice||base)*(it.qty||1);});} function recalcOrder(order){applyDiscount(order);var pt=0;(order.items||[]).forEach(function(it){pt+=it.total||0;});var sc=0;(order.fees||[]).forEach(function(f){sc+=(f.amount||0);});order.grandTotal=pt+sc;order.remaining=order.grandTotal-(order.deposit||0);if(order.remaining<0)order.remaining=0;} function saveCurrentOrder(){if(!window.VAI_QR||!window.VAI_QR.getData)return;var d=window.VAI_QR.getData();var qd=d.qd;var code=window.VAI_QR.getEffectiveOrderCode();var order={code:code,customer:qd.customer.name||'',phone:qd.customer.phone||'',email:qd.customer.email||'',addr:qd.customer.addr||'',date:qd.customer.date||new Date().toLocaleDateString('vi-VN'),items:qd.items||[],fees:d.fees||[],deposit:d.deposit||0,discountPercent:d.discountPercent||0,itemDiscounts:d.itemDiscounts||{},grandTotal:d.grandTotal||0,remaining:d.remaining||0,promptText:window.VAI_QR.getPromptText?window.VAI_QR.getPromptText():'',status:'pending',savedAt:new Date().toISOString()};addOrder(order);syncOrderToBot(order);if(typeof showToast==='function')showToast('✅ Đã lưu đơn '+code);} function confirmOrder(order){order.status='confirmed';order.confirmedAt=new Date().toISOString();addOrder(order);sendToKetoanBot(order);} function unconfirmOrder(order){order.status='pending';delete order.confirmedAt;addOrder(order);fetch(KETOAN_API+'/order-unconfirmed',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({ma_don:order.code})}).catch(function(){});} function orderToVAIData(order){recalcOrder(order);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:orderItemInfo(it),qty:it.qty||1,price:it.listPrice||it.originalPrice||it.price||0,discPrice:it.discPrice||it.price||0,total:it.total||0,note:it.note||''};}),grandTotal:order.grandTotal||0},fees:order.fees||[],notes:[],discount:null,discountPercent:order.discountPercent||0,itemDiscounts:order.itemDiscounts||{},deposit:order.deposit||0,productTotal:order.grandTotal-(order.fees||[]).reduce(function(s,f){return s+(f.amount||0);},0),grandTotal:order.grandTotal||0,remaining:order.remaining||0};} function _orderBW(opt){try{return !!(opt&&opt.bw)||!!(document.getElementById('od-bw')&&document.getElementById('od-bw').checked)||!!(document.getElementById('vai-bw-export')&&document.getElementById('vai-bw-export').checked);}catch(e){return !!(opt&&opt.bw);}} async function exportPDF(order,opt){if(window.VAI_QR&&window.VAI_QR.exportPDF){var d=orderToVAIData(order);await window.VAI_QR.exportPDF(d,order.code,_orderBW(opt));}} async function exportExcel(order,opt){if(typeof ExcelJS==='undefined')return;if(window.VAI_QR&&window.VAI_QR.exportExcel){var d=orderToVAIData(order),qd=d.qd,qrUrl=window.VAI_QR.getQRUrl(order.deposit>0?order.remaining:order.grandTotal,order.code);await window.VAI_QR.exportExcel(d,qd,order.code,qrUrl,_orderBW(opt));}} async function exportDeliveryPDF(order,opt){if(window.VAI_QR&&window.VAI_QR.exportDeliveryPDF){var qd=orderToVAIData(order).qd;await window.VAI_QR.exportDeliveryPDF(qd,order.code,_orderBW(opt));}} async function exportDeliveryExcel(order,opt){if(typeof ExcelJS==='undefined')return;if(window.VAI_QR&&window.VAI_QR.exportDeliveryExcel){var qd=orderToVAIData(order).qd;await window.VAI_QR.exportDeliveryExcel(qd,order.code,_orderBW(opt));}} function openSearchModal(){var old=document.getElementById('vai-order-search-modal');if(old)old.remove();var orders=getOrders();var pC=orders.filter(function(o){return(o.status||'pending')==='pending';}).length;var cC=orders.filter(function(o){return o.status==='confirmed';}).length;var cf='all';var ov=document.createElement('div');ov.id='vai-order-search-modal';ov.style.cssText='position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:9999;display:flex;align-items:flex-start;justify-content:center;padding:40px 16px;overflow-y:auto';ov.onclick=function(e){if(e.target===ov)ov.remove();};ov.innerHTML='
📋 Đơn hàng ('+orders.length+')
';document.body.appendChild(ov);var re=document.getElementById('vai-os-r');function rl(ls){if(!ls.length){re.innerHTML='
Trống
';return;}re.innerHTML=ls.map(function(o,i){var isC=o.status==='confirmed';return'
'+o.code+' '+(isC?'✅':'⏳')+'
'+esc(o.customer)+' • '+fmt(o.grandTotal)+'
';}).join('');re.onclick=function(e){var d=e.target.closest('.od');if(d){e.stopPropagation();if(confirm('Xóa?')){deleteOrder(d.dataset.c);df();}return;}var it=e.target.closest('.oi');if(it){ov.remove();openOrderDetail(ls[parseInt(it.dataset.i)]);}};}function df(){rl(searchOrders(document.getElementById('vai-os-q').value,cf));}rl(orders);document.getElementById('vai-os-close').onclick=function(){ov.remove();};document.getElementById('vai-os-q').oninput=df; // Refresh button — force sync from server document.getElementById('vai-os-refresh').onclick=function(){ this.textContent='⏳';var self=this; syncFromServer(); setTimeout(function(){self.textContent='🔄';ov.remove();openSearchModal();},2000); }; ov.querySelectorAll('.os-filter').forEach(function(b){b.onclick=function(e){e.stopPropagation();cf=this.dataset.f;ov.querySelectorAll('.os-filter').forEach(function(x){x.style.background='#fff';x.style.color='#003f62';});this.style.background='#003f62';this.style.color='#fff';df();};});} function openOrderDetail(order){var old=document.getElementById('vai-order-detail-modal');if(old)old.remove();order=JSON.parse(JSON.stringify(order));var ov=document.createElement('div');ov.id='vai-order-detail-modal';ov.style.cssText='position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:10000;display:flex;align-items:flex-start;justify-content:center;padding:16px;overflow-y:auto';ov.onclick=function(e){if(e.target===ov)ov.remove();};document.body.appendChild(ov);var modal=document.createElement('div');modal.style.cssText='background:#fff;border-radius:16px;max-width:750px;width:100%;max-height:92vh;overflow-y:auto';ov.appendChild(modal); function render(){recalcOrder(order);var isC=order.status==='confirmed'; var h='
📄 '+esc(order.code)+' '+(isC?'✅ Đã chốt':'')+'
' +'
KH:
SĐT:
ĐC:
' +'
👤 Mã KH
'+(order.ma_kh?'✅ '+order.ma_kh:'')+'
' +''; (order.items||[]).forEach(function(it,i){var ck=it.discPrice&&it.price&&it.discPrice';}); h+='
#ẢnhSPThông tinSLGiá niêm yếtGiá CKTT
'+(i+1)+''+(it.image?'':'')+''+esc((it.name||'').substring(0,28))+'
'+esc(it.model||'')+'
'+esc(orderItemInfo(it)).substring(0,260)+''+(it.qty||1)+''+fmt(it.price)+''+fmt(it.discPrice||it.price)+''+fmt(it.total)+'
'; if(order.fees&&order.fees.length){h+='
';order.fees.forEach(function(f){h+='
'+esc(f.label)+''+fmt(f.amount)+'
';});h+='
';} h+='
TỔNG'+fmt(order.grandTotal)+'
'; if(order.deposit>0)h+='
Cọc: '+fmt(order.deposit)+' | Còn: '+fmt(order.remaining)+'
'; h+='
' +'' +'
'; if(!isC)h+=''; else h+=''; h+='
';modal.innerHTML=h; document.getElementById('xc').onclick=function(){ov.remove();}; modal.querySelectorAll('.xd').forEach(function(b){b.onclick=function(e){e.stopPropagation();order.items.splice(parseInt(this.dataset.i),1);render();};}); function cc(){order.customer=document.getElementById('od-name').value;order.phone=document.getElementById('od-phone').value;order.addr=document.getElementById('od-addr').value;} var mI=document.getElementById('od-makh'),sB=document.getElementById('od-makh-suggest'),mInfo=document.getElementById('od-makh-info'); if(mI){mI.oninput=function(){var q=this.value.trim();if(q.length<1){sB.style.display='none';mInfo.textContent='';order.ma_kh='';return;}var ms=findKH(q);if(!ms.length){sB.style.display='none';mInfo.textContent='⚠️ Không tìm';order.ma_kh='';return;}var ex=ms.find(function(k){return k.ma_kh.toLowerCase()===q.toLowerCase();});if(ex){applyKH(ex);sB.style.display='none';return;}sB.style.display='block';sB.innerHTML=ms.slice(0,4).map(function(k){return'
'+esc(k.ma_kh)+' '+esc(k.ten)+'
';}).join('');sB.querySelectorAll('.kh-opt').forEach(function(o){o.onmousedown=function(e){e.preventDefault();var k=KH_LIST.find(function(x){return x.ma_kh===this.dataset.ma;}.bind(this));if(k){mI.value=k.ma_kh;applyKH(k);sB.style.display='none';}};});};mI.onblur=function(){setTimeout(function(){sB.style.display='none';},200);};} function applyKH(kh){order.ma_kh=kh.ma_kh;var ap=[];(order.items||[]).forEach(function(it){var br=it.brand||(it.name||'').match(/(malloca|eurogold|grob|garis)/i);if(br){var bn=typeof br==='string'?br:br[1];var rate=getKHCKRate(kh,bn);if(rate!==null){it.discPrice=Math.round((it.price||0)*(1-rate/100));it.total=it.discPrice*(it.qty||1);ap.push(bn+' '+rate+'%');}}});order.discountPercent=0;recalcOrder(order);mInfo.innerHTML='✅ '+esc(kh.ma_kh)+' — '+esc(kh.ten)+(ap.length?' | '+ap.join(', '):'');render();} document.getElementById('od-apply').onclick=function(){cc();var p=document.getElementById('od-prompt').value;order.promptText=p;var ps=parsePrompt(p);order.fees=ps.fees.length?ps.fees:(p.trim()?order.fees:[]);order.deposit=ps.deposit||0;if(ps.discountPercent)order.discountPercent=ps.discountPercent;order.itemDiscounts=ps.itemDiscounts||{};recalcOrder(order);render();}; document.getElementById('od-save').onclick=function(){cc();recalcOrder(order);order.savedAt=new Date().toISOString();addOrder(order);syncOrderToBot(order);alert('✅ Lưu!');}; document.getElementById('od-pdf').onclick=function(){cc();recalcOrder(order);exportPDF(order,{bw:!!(document.getElementById('od-bw')&&document.getElementById('od-bw').checked)});}; document.getElementById('od-xl').onclick=function(){cc();recalcOrder(order);exportExcel(order,{bw:!!(document.getElementById('od-bw')&&document.getElementById('od-bw').checked)});}; document.getElementById('od-gh').onclick=function(){cc();recalcOrder(order);exportDeliveryPDF(order,{bw:!!(document.getElementById('od-bw')&&document.getElementById('od-bw').checked)});}; document.getElementById('od-gh-xl').onclick=function(){cc();recalcOrder(order);exportDeliveryExcel(order,{bw:!!(document.getElementById('od-bw')&&document.getElementById('od-bw').checked)});}; var cB=document.getElementById('od-confirm');if(cB)cB.onclick=function(){cc();recalcOrder(order);if(!confirm('Chốt đơn '+order.code+'?'))return;confirmOrder(order);if(typeof showToast==='function')showToast('✅ Đã chốt '+order.code);render();}; var uB=document.getElementById('od-unconfirm');if(uB)uB.onclick=function(){if(!confirm('Bỏ chốt?'))return;unconfirmOrder(order);render();}; }render(); } function injectMaKHToQuote(){var modal=document.querySelector('.quote-overlay.open,.quote-modal.open,.quote-overlay[style*="block"],.quote-modal[style*="block"],[class*="quote"][class*="open"],[class*="quote"][style*="block"]');if(!modal)return;if(modal.querySelector('#vai-makh-section'))return;var pI=null;modal.querySelectorAll('input,textarea').forEach(function(el){var ph=(el.placeholder||'').toLowerCase();if(ph.includes('yêu cầu')||ph.includes('chiết khấu')||ph.includes('chiet khau'))pI=el;});if(!pI)return; var sec=document.createElement('div');sec.id='vai-makh-section';sec.style.cssText='margin:8px 16px;padding:8px 10px;background:#eff6ff;border-radius:8px;border:1px solid #bfdbfe;position:relative'; sec.innerHTML='
👤 Mã KH
'; pI.parentElement.insertBefore(sec,pI); var input=document.getElementById('vai-makh-input'),dd=document.getElementById('vai-makh-dropdown'),badge=document.getElementById('vai-makh-badge'); input.oninput=function(){var q=this.value.trim();if(q.length<1){dd.style.display='none';badge.textContent='';return;}loadKhachHang().then(function(){var ms=findKH(q);if(!ms.length){dd.style.display='none';badge.textContent='⚠️';badge.style.color='#dc2626';return;}var ex=ms.find(function(k){return k.ma_kh.toLowerCase()===q.toLowerCase();});if(ex){dd.style.display='none';badge.innerHTML='✅ '+esc(ex.ten);badge.style.color='#16a34a';applyKHToQuoteModal(ex);return;}dd.style.display='block';dd.innerHTML=ms.slice(0,4).map(function(k){return'
'+esc(k.ma_kh)+' '+esc(k.ten)+'
';}).join('');dd.querySelectorAll('.vai-kh-opt').forEach(function(o){o.onmousedown=function(e){e.preventDefault();var k=KH_LIST.find(function(x){return x.ma_kh===this.dataset.ma;}.bind(this));if(k){input.value=k.ma_kh;dd.style.display='none';badge.innerHTML='✅ '+esc(k.ten);badge.style.color='#16a34a';applyKHToQuoteModal(k);}};});});}; input.onblur=function(){setTimeout(function(){dd.style.display='none';},200);}; } function applyKHToQuoteModal(kh){var codes=kh.ck_codes||[];if(!codes.length)return;var pI=null;document.querySelectorAll('input,textarea').forEach(function(el){var ph=(el.placeholder||'').toLowerCase();if(ph.includes('yêu cầu')||ph.includes('chiết khấu')||ph.includes('chiet khau'))pI=el;});if(!pI)return;var ckVal=null;for(var i=0;i