File size: 44,738 Bytes
f4b1d37 29fd739 f4b1d37 29fd739 f4b1d37 29fd739 f4b1d37 29fd739 f4b1d37 29fd739 f4b1d37 29fd739 f4b1d37 29fd739 f4b1d37 29fd739 f4b1d37 29fd739 f4b1d37 29fd739 f4b1d37 29fd739 f4b1d37 29fd739 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 | /**
* V.AI STUDIO — Order Store v19
*
* CHANGES from v18:
* - ADD PRODUCT TO ORDER: Nút "➕ Thêm SP" trong modal sửa đơn
* → Hiện thanh tìm kiếm SP → Chọn SP → Tự động thêm vào đơn + lưu
*/
(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(/[̀-ͯ]/g,'').replace(/[^a-z0-9]/g,'');for(var i=0;i<data.length;i++){var p=data[i]||{};var pc=((p.sku||p.mod||p.model||'')+' '+(p.slug||'')).toLowerCase().replace(/[^a-z0-9]/g,'');var pn=(p.name||p.n||'').toLowerCase().normalize('NFD').replace(/[̀-ͯ]/g,'').replace(/[^a-z0-9]/g,'');if((code&&pc.indexOf(code)!==-1)||(name&&pn.indexOf(name.slice(0,20))!==-1))return p.image||p.img||p.i||'';}}
catch(e){}
return '';
}
function syncFromServer(){
fetch(KETOAN_API+'/orders',{method:'GET'}).then(function(r){return r.json();}).then(function(d){
var serverOrders=d.data||[];
if(!serverOrders.length)return;
var localOrders=getOrders();
var localCodes={};
localOrders.forEach(function(o){if(o.code)localCodes[o.code]=true;});
var added=0;
serverOrders.forEach(function(so){
var code=so.ma_don||so.code||'';
if(!code)return;
var existingIdx=localOrders.findIndex(function(o){return o.code===code;});
if(existingIdx>=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 v19] ☁️ Synced '+added+' orders from server. Total local: '+localOrders.length);
}
}).catch(function(e){
console.log('[OS v19] 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 v19] 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<codes.length;i++){var c=codes[i].toLowerCase().replace(/\s/g,'');if(c.includes(b)){var m=c.match(/ck(\d+)/);if(m)return parseInt(m[1]);}}return null;}
function getOrders(){try{return JSON.parse(localStorage.getItem(STORE_KEY)||'[]');}catch(e){return[];}}
function saveOrders(orders){localStorage.setItem(STORE_KEY,JSON.stringify(orders));}
function addOrder(order){var orders=getOrders();var idx=orders.findIndex(function(o){return o.code===order.code;});if(idx>=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(/[̀-ͯ]/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<keys.length;i++){if(itemDiscounts[keys[i]])return itemDiscounts[keys[i]];}for(var code in itemDiscounts){for(var j=0;j<keys.length;j++){var k=keys[j];if(code.length>=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(/[̀-ͯ]/g,'').replace(/[đĐ]/g,'d').toLowerCase();var re1=/([a-z]{1,8}[a-z0-9._\-\/]{1,30}\d[a-z0-9._\-\/]*)\s*(?:[:=\-]|\s+)?\s*(?:ck|chiet\s*khau|giam|discount)\s*([\d.,]+)\s*%?/gi;var re2=/(?:ck|chiet\s*khau|giam|discount)\s*([a-z]{1,8}[a-z0-9._\-\/]{1,30}\d[a-z0-9._\-\/]*)\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&&v<=100)map[compactKey(m[1])]=v2;}}
function esc(s){return(s||'').replace(/&/g,'&').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(/[,;
]+/).forEach(function(line){
line=line.trim();if(!line)return;
parseItemDiscountLine(line,itemDiscounts);
if(Object.keys(itemDiscounts).length&&line.match(/[A-Za-z]{1,8}[A-Za-z0-9._\-\/]{1,30}\d[A-Za-z0-9._\-\/]*/i)&&line.match(/(ck|chiết khấu|chiet khau|giảm|giam|discount)/i))return;
var lo=line.toLowerCase().normalize('NFD').replace(/[̀-ͯ]/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||{},notes:d.notes||[],grandTotal:d.grandTotal||0,remaining:d.remaining||0,promptText:window.VAI_QR.getPromptText?window.VAI_QR.getPromptText():'',status:'pending',savedAt:new Date().toISOString()};recalcOrder(order);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:order.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='<div style="background:#fff;border-radius:16px;max-width:750px;width:100%;max-height:85vh;overflow:hidden;display:flex;flex-direction:column"><div style="padding:14px 18px;background:#003f62;display:flex;align-items:center"><div style="font-size:16px;font-weight:800;color:#fff;flex:1">📋 Đơn hàng ('+orders.length+')</div><button id="vai-os-refresh" style="background:rgba(255,255,255,.2);border:none;color:#fff;padding:4px 10px;border-radius:6px;cursor:pointer;font-size:11px;margin-right:8px">🔄</button><button id="vai-os-close" style="background:none;border:none;color:#fff;font-size:20px;cursor:pointer">✕</button></div><div style="padding:10px 18px;border-bottom:1px solid #e2e8f0"><div style="display:flex;gap:6px;margin-bottom:8px"><button class="os-filter" data-f="all" style="padding:5px 12px;border-radius:20px;border:2px solid #003f62;background:#003f62;color:#fff;font-size:11px;font-weight:700;cursor:pointer">Tất cả ('+orders.length+')</button><button class="os-filter" data-f="pending" style="padding:5px 12px;border-radius:20px;border:2px solid #f59e0b;background:#fffbeb;color:#92400e;font-size:11px;font-weight:700;cursor:pointer">⏳ ('+pC+')</button><button class="os-filter" data-f="confirmed" style="padding:5px 12px;border-radius:20px;border:2px solid #16a34a;background:#f0fdf4;color:#166534;font-size:11px;font-weight:700;cursor:pointer">✅ ('+cC+')</button></div><input id="vai-os-q" placeholder="Tìm mã đơn, tên KH, SĐT..." style="width:100%;padding:9px;border:2px solid #e2e8f0;border-radius:8px;font-size:12px;box-sizing:border-box"></div><div id="vai-os-r" style="flex:1;overflow-y:auto;padding:10px 18px"></div></div>';document.body.appendChild(ov);var re=document.getElementById('vai-os-r');function rl(ls){if(!ls.length){re.innerHTML='<div style="text-align:center;padding:30px;color:#94a3b8">Trống</div>';return;}re.innerHTML=ls.map(function(o,i){var isC=o.status==='confirmed';return'<div class="oi" data-i="'+i+'" style="padding:10px;border:1px solid #e2e8f0;border-radius:8px;margin-bottom:6px;cursor:pointer;border-left:3px solid '+(isC?'#16a34a':'#f59e0b')+'"><div style="display:flex;justify-content:space-between"><div><b style="color:#003f62">'+o.code+'</b> '+(isC?'✅':'⏳')+'</div><button class="od" data-c="'+esc(o.code)+'" style="background:#fee2e2;border:none;color:#dc2626;padding:2px 6px;border-radius:4px;cursor:pointer;font-size:10px">🗑</button></div><div style="font-size:11px;color:#64748b;margin-top:3px">'+esc(o.customer)+' • '+fmt(o.grandTotal)+'</div></div>';}).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);
var _searchOpen=false;
function addProductToOrder(product){
var pn=product.priceNum||product.pn||0;
var item={name:product.name||product.n||'',model:product.sku||product.model||product.mod||'',brand:product.brand||'',image:product.image||product.img||product.i||'',specs:product.specs||'',info:product.info||product.thong_tin||'',dim_info:product.dim_info||product.dimension_info||'',qty:1,price:pn,discPrice:pn,total:pn,note:''};
order.items.push(item);
recalcOrder(order);
order.savedAt=new Date().toISOString();
addOrder(order);
syncOrderToBot(order);
if(typeof showToast==='function')showToast('✅ Đã thêm "'+((product.name||product.n||'').substring(0,25))+'"');
_searchOpen=false;
render();
}
function nrm(s){return(s||'').toString().normalize('NFD').replace(/[̀-ͯ]/g,'').replace(/[đĐ]/g,'d').toLowerCase();}
function searchProducts(q){
if(!q||q.length<1)return[];
var db=[];if(typeof D!=='undefined'&&Array.isArray(D))db=D;if(typeof VAI_URL_ADDED_PRODUCTS!=='undefined'&&Array.isArray(VAI_URL_ADDED_PRODUCTS))db=db.concat(VAI_URL_ADDED_PRODUCTS);
var nq=nrm(q);var terms=nq.split(/\s+/).filter(function(t){return t.length>1;});
var scored=[];
for(var i=0;i<db.length;i++){
var p=db[i];if(!p)continue;
var name=nrm(p.name||p.n||'');var sku=nrm(p.sku||p.model||p.mod||'');var brand=nrm(p.brand||'');
var score=0;
for(var j=0;j<terms.length;j++){
var t=terms[j];
if(name.indexOf(t)!==-1)score+=3;
if(sku.indexOf(t)!==-1)score+=5;
if(brand.indexOf(t)!==-1)score+=2;
if(name===t||sku===t)score+=10;
}
if(score>0)scored.push({p:p,s:score});
}
scored.sort(function(a,b){return b.s-a.s;});
return scored.slice(0,20).map(function(x){return x.p;});
}
function toggleSearchPanel(){
_searchOpen=!_searchOpen;
render();
if(_searchOpen){
setTimeout(function(){
var inp=document.getElementById('od-sp-search');
if(inp)inp.focus();
},50);
}
}
function renderSearchPanel(){
if(!_searchOpen)return '';
return '<div id="od-sp-panel" style="margin-top:8px;padding:10px;background:#f8fafc;border-radius:8px;border:2px solid #003f62">'
+'<div style="display:flex;align-items:center;gap:6px;margin-bottom:6px">'
+'<span style="font-size:12px;font-weight:700;color:#003f62">🔍 Thêm sản phẩm</span>'
+'<button id="od-sp-close" style="margin-left:auto;background:none;border:none;color:#64748b;font-size:16px;cursor:pointer;padding:0 4px">✕</button></div>'
+'<input id="od-sp-search" placeholder="Tìm tên SP, mã SP, thương hiệu..." style="width:100%;padding:8px 10px;border:2px solid #003f62;border-radius:8px;font-size:12px;box-sizing:border-box;outline:none" onfocus="this.style.borderColor=\'#005a8c\'" onblur="this.style.borderColor=\'#003f62\'">'
+'<div id="od-sp-results" style="max-height:260px;overflow-y:auto;margin-top:6px"></div></div>';
}
function renderSearchResults(){
if(!_searchOpen)return;
var q=document.getElementById('od-sp-search');
if(!q)return;
var results=searchProducts(q.value);
var el=document.getElementById('od-sp-results');
if(!el)return;
if(!q.value.trim()){el.innerHTML='<div style="text-align:center;padding:16px;color:#94a3b8;font-size:11px">Nhập từ khóa để tìm sản phẩm</div>';return;}
if(!results.length){el.innerHTML='<div style="text-align:center;padding:16px;color:#94a3b8;font-size:11px">Không tìm thấy sản phẩm</div>';return;}
el.innerHTML=results.map(function(p){
var nm=p.name||p.n||'';
var sku=p.sku||p.model||p.mod||'';
var br=p.brand||'';
var img=p.image||p.img||p.i||'';
var price=p.priceNum||p.pn||0;
return '<div class="od-sp-item" style="display:flex;align-items:center;gap:8px;padding:6px;border:1px solid #e2e8f0;border-radius:6px;margin-bottom:4px;cursor:pointer;background:#fff" onmouseover="this.style.background=\'#eff6ff\';this.style.borderColor=\'#93c5fd\'" onmouseout="this.style.background=\'#fff\';this.style.borderColor=\'#e2e8f0\'">'
+'<div style="width:44px;height:44px;flex-shrink:0;background:#f1f5f9;border-radius:4px;display:flex;align-items:center;justify-content:center;overflow:hidden">'+(img?'<img src="'+img+'" style="max-width:40px;max-height:40px;object-fit:contain" referrerpolicy="no-referrer" onerror="this.style.display=\'none\'">':'<span style="font-size:16px;color:#94a3b8">📦</span>')+'</div>'
+'<div style="flex:1;min-width:0"><div style="font-size:11px;font-weight:700;color:#0f172a;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">'+esc(nm)+'</div><div style="font-size:9px;color:#64748b">'+esc(sku)+(br?' • '+esc(br):'')+'</div></div>'
+'<div style="font-size:11px;font-weight:700;color:#003f62;white-space:nowrap">'+fmt(price)+'</div>'
+'<button class="od-sp-add" data-name="'+esc(nm)+'" style="padding:4px 8px;background:#003f62;color:#fff;border:none;border-radius:5px;font-size:10px;font-weight:700;cursor:pointer;white-space:nowrap">+ Thêm</button></div>';
}).join('');
// Bind click events
el.querySelectorAll('.od-sp-item').forEach(function(item){
item.onclick=function(e){
if(e.target.closest('.od-sp-add'))return;
var nm=this.querySelector('.od-sp-add').dataset.name;
var idx=results.findIndex(function(p){return(p.name||p.n||'')===nm;});
if(idx>=0)addProductToOrder(results[idx]);
};
});
el.querySelectorAll('.od-sp-add').forEach(function(btn){
btn.onclick=function(e){
e.stopPropagation();
var nm=this.dataset.name;
var idx=results.findIndex(function(p){return(p.name||p.n||'')===nm;});
if(idx>=0)addProductToOrder(results[idx]);
};
});
}
function render(){recalcOrder(order);var isC=order.status==='confirmed';
var h='<div style="padding:12px 18px;background:#003f62;border-radius:16px 16px 0 0;display:flex;justify-content:space-between;align-items:center"><div><b style="color:#fff">📄 '+esc(order.code)+'</b> '+(isC?'<span style="background:#dcfce7;color:#16a34a;padding:2px 8px;border-radius:6px;font-size:10px;margin-left:8px">✅ Đã chốt</span>':'<span style="background:#fef9c3;color:#92400e;padding:2px 8px;border-radius:6px;font-size:10px;margin-left:8px">⏳</span>')+'</div><button id="xc" style="background:none;border:none;color:#fff;font-size:20px;cursor:pointer">✕</button></div><div style="padding:14px 18px">'
+'<div style="display:grid;grid-template-columns:1fr 1fr;gap:6px;font-size:11px;margin-bottom:10px;padding:10px;background:#f8fafc;border-radius:8px"><div><b>KH:</b> <input id="od-name" value="'+esc(order.customer||'')+'" style="border:1px solid #ddd;padding:3px 6px;border-radius:4px;width:55%;font-size:11px"></div><div><b>SĐT:</b> <input id="od-phone" value="'+esc(order.phone||'')+'" style="border:1px solid #ddd;padding:3px 6px;border-radius:4px;width:50%;font-size:11px"></div><div style="grid-column:1/-1"><b>ĐC:</b> <input id="od-addr" value="'+esc(order.addr||'')+'" style="border:1px solid #ddd;padding:3px 6px;border-radius:4px;width:85%;font-size:11px"></div></div>'
+'<div style="margin-bottom:10px;padding:8px 10px;background:#eff6ff;border-radius:8px;border:1px solid #bfdbfe;position:relative"><div style="font-size:10px;font-weight:700;color:#1e40af;margin-bottom:4px">👤 Mã KH</div><input id="od-makh" value="'+esc(order.ma_kh||'')+'" placeholder="Mã KH hoặc tên..." style="width:100%;padding:6px 10px;border:1px solid #93c5fd;border-radius:6px;font-size:11px;box-sizing:border-box"><div id="od-makh-suggest" style="position:absolute;left:10px;right:10px;top:58px;background:#fff;border:1px solid #e2e8f0;border-radius:6px;max-height:100px;overflow-y:auto;display:none;z-index:10;box-shadow:0 4px 12px rgba(0,0,0,.1)"></div><div id="od-makh-info" style="font-size:9px;margin-top:3px;color:#64748b">'+(order.ma_kh?'✅ '+order.ma_kh:'')+'</div></div>'
+'<table style="width:100%;border-collapse:collapse;font-size:11px;margin-bottom:8px"><thead><tr style="background:#003f62;color:#fff"><th style="padding:5px">#</th><th style="padding:5px">Ảnh</th><th style="padding:5px;text-align:left">SP</th><th style="padding:5px;text-align:left">Thông tin</th><th style="padding:5px">SL</th><th style="padding:5px;text-align:right">Giá niêm yết</th><th style="padding:5px;text-align:right">Giá CK</th><th style="padding:5px;text-align:right">TT</th><th></th></tr></thead><tbody>';
(order.items||[]).forEach(function(it,i){var ck=it.discPrice&&it.price&&it.discPrice<it.price;h+='<tr style="border-bottom:1px solid #f1f5f9"><td style="padding:3px;text-align:center">'+(i+1)+'</td><td style="padding:3px">'+(it.image?'<img src="'+it.image+'" style="width:40px;height:40px;object-fit:contain;border-radius:4px" referrerpolicy="no-referrer" onerror="this.style.display=\'none\'">':'')+'</td><td style="padding:3px"><b style="font-size:10px">'+esc((it.name||'').substring(0,28))+'</b><br><span style="font-size:9px;color:#64748b">'+esc(it.model||'')+'</span></td><td style="padding:3px;font-size:9px;color:#64748b;max-width:150px;white-space:normal">'+esc(orderItemInfo(it)).substring(0,260)+'</td><td style="padding:3px;text-align:center">'+(it.qty||1)+'</td><td style="padding:3px;text-align:right">'+fmt(it.price)+'</td><td style="padding:3px;text-align:right;'+(ck?'color:#dc3545;font-weight:700':'')+'">'+fmt(it.discPrice||it.price)+'</td><td style="padding:3px;text-align:right;font-weight:700">'+fmt(it.total)+'</td><td style="padding:3px"><button class="xd" data-i="'+i+'" style="background:#fee2e2;border:none;color:#dc2626;padding:1px 4px;border-radius:3px;cursor:pointer;font-size:9px">✕</button></td></tr>';});
h+='</tbody></table>';
if(order.fees&&order.fees.length){h+='<div style="padding:4px 10px;background:#fffbeb;border-radius:6px;margin-bottom:6px;font-size:10px">';order.fees.forEach(function(f){h+='<div style="display:flex;justify-content:space-between"><span style="color:#92400e">'+esc(f.label)+'</span><b>'+fmt(f.amount)+'</b></div>';});h+='</div>';}
h+='<div style="padding:8px 12px;background:#003f62;border-radius:6px;display:flex;justify-content:space-between;margin-bottom:6px"><span style="color:#fff;font-weight:800">TỔNG</span><span style="color:#f0b840;font-weight:900;font-size:15px">'+fmt(order.grandTotal)+'</span></div>';
if(order.deposit>0)h+='<div style="padding:4px 10px;background:#f0fdf4;border-radius:6px;font-size:10px;margin-bottom:6px"><span>Cọc: <b>'+fmt(order.deposit)+'</b></span> | <span>Còn: <b style="color:#dc2626">'+fmt(order.remaining)+'</b></span></div>';
h+='<div style="margin:8px 0;padding:8px;background:#f0f9ff;border-radius:8px;border:1px solid #bae6fd"><input id="od-prompt" value="'+esc(order.promptText||'')+'" placeholder="mh70btc ck 30%, CK 10, giao 200k, lắp 300k, cọc 5tr" style="width:100%;padding:6px;border:1px solid #e2e8f0;border-radius:6px;font-size:11px;box-sizing:border-box"><button id="od-apply" style="margin-top:4px;padding:4px 10px;background:#0369a1;color:#fff;border:none;border-radius:5px;font-weight:700;cursor:pointer;font-size:10px">⚡ Áp dụng</button></div>'
+'<label style="display:inline-flex;align-items:center;gap:5px;margin-top:8px;padding:6px 9px;background:#fff;color:#111;border:1px solid #111;border-radius:8px;font-weight:700;font-size:10px"><input id="od-bw" type="checkbox"> Trắng đen</label>'
+'<div style="display:flex;gap:5px;margin-top:8px;flex-wrap:wrap"><button id="od-add-sp" style="padding:7px 12px;background:#0891b2;color:#fff;border:none;border-radius:8px;font-weight:700;cursor:pointer;font-size:10px">➕ Thêm SP</button><button id="od-save" style="padding:7px 12px;background:#7c3aed;color:#fff;border:none;border-radius:8px;font-weight:700;cursor:pointer;font-size:10px">💾 Lưu</button><button id="od-pdf" style="padding:7px 10px;background:#db9815;color:#fff;border:none;border-radius:8px;font-weight:700;cursor:pointer;font-size:10px">PDF</button><button id="od-xl" style="padding:7px 10px;background:#b45309;color:#fff;border:none;border-radius:8px;font-weight:700;cursor:pointer;font-size:10px">Excel</button><button id="od-gh" style="padding:7px 10px;background:#0d9488;color:#fff;border:none;border-radius:8px;font-weight:700;cursor:pointer;font-size:10px">GH PDF</button><button id="od-gh-xl" style="padding:7px 10px;background:#059669;color:#fff;border:none;border-radius:8px;font-weight:700;cursor:pointer;font-size:10px">GH Excel</button>';
if(!isC)h+='<button id="od-confirm" style="padding:7px 14px;background:#16a34a;color:#fff;border:none;border-radius:8px;font-weight:700;cursor:pointer;font-size:11px;margin-left:auto">✅ CHỐT</button>';
else h+='<button id="od-unconfirm" style="padding:7px 10px;background:#fef2f2;color:#dc2626;border:2px solid #fca5a5;border-radius:8px;font-weight:700;cursor:pointer;font-size:10px;margin-left:auto">↩️ Bỏ chốt</button>';
h+='</div>';
h+=renderSearchPanel();
h+='</div>';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();};});
// Add SP button
var addSpBtn=document.getElementById('od-add-sp');
if(addSpBtn){addSpBtn.onclick=toggleSearchPanel;}
// Close search panel
var closeSpBtn=document.getElementById('od-sp-close');
if(closeSpBtn){closeSpBtn.onclick=function(){_searchOpen=false;render();};}
// Search input
var searchInput=document.getElementById('od-sp-search');
if(searchInput){searchInput.oninput=function(){renderSearchResults();};searchInput.onkeydown=function(e){if(e.key==='Escape'){_searchOpen=false;render();}};}
// Render search results if panel is open
if(_searchOpen){renderSearchResults();}
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'<div class="kh-opt" data-ma="'+esc(k.ma_kh)+'" style="padding:5px 8px;cursor:pointer;border-bottom:1px solid #f1f5f9;font-size:10px"><b>'+esc(k.ma_kh)+'</b> '+esc(k.ten)+'</div>';}).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||{};order.notes=ps.notes||[];recalcOrder(order);render();};
document.getElementById('od-save').onclick=function(){cc();var p=document.getElementById('od-prompt').value;if(p&&p!==order.promptText){order.promptText=p;var ps=parsePrompt(p);order.fees=ps.fees.length?ps.fees:[];order.deposit=ps.deposit||0;if(ps.discountPercent)order.discountPercent=ps.discountPercent;order.itemDiscounts=ps.itemDiscounts||{};order.notes=ps.notes||[];recalcOrder(order);}else{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='<div style="display:flex;align-items:center;gap:6px;margin-bottom:4px"><span style="font-size:10px;font-weight:700;color:#1e40af">👤 Mã KH</span><span id="vai-makh-badge" style="font-size:9px;color:#64748b"></span></div><input id="vai-makh-input" placeholder="Mã KH hoặc tên..." style="width:100%;padding:6px 10px;border:1px solid #93c5fd;border-radius:6px;font-size:11px;box-sizing:border-box"><div id="vai-makh-dropdown" style="position:absolute;left:10px;right:10px;top:54px;background:#fff;border:1px solid #e2e8f0;border-radius:6px;max-height:100px;overflow-y:auto;display:none;z-index:100;box-shadow:0 4px 12px rgba(0,0,0,.1)"></div>';
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'<div class="vai-kh-opt" data-ma="'+esc(k.ma_kh)+'" style="padding:5px 8px;cursor:pointer;border-bottom:1px solid #f1f5f9;font-size:10px"><b>'+esc(k.ma_kh)+'</b> '+esc(k.ten)+'</div>';}).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<codes.length;i++){var m=codes[i].match(/ck(\d+)/i);if(m){ckVal=parseInt(m[1]);break;}}if(!ckVal)return;var cur=pI.value||'';cur=cur.replace(/^(ck|chiết khấu|chiet khau|giảm|giam|discount)\s*[\d.,]+\s*%?\s*[,;]?\s*/gim,'').trim();pI.value=('CK '+ckVal+(cur?', '+cur:'')).trim();pI.dispatchEvent(new Event('input',{bubbles:true}));}
var _mO=new MutationObserver(function(){setTimeout(injectMaKHToQuote,500);});_mO.observe(document.body,{childList:true,subtree:true,attributes:true,attributeFilter:['class','style']});setInterval(injectMaKHToQuote,3000);
window.VAI_ORDERS={save:saveCurrentOrder,search:searchOrders,openSearch:openSearchModal,openDetail:openOrderDetail,getAll:getOrders,getByCode:function(c){return getOrders().find(function(o){return o.code===c;});},add:addOrder,delete:deleteOrder,confirm:confirmOrder,unconfirm:unconfirmOrder,toVAIData:orderToVAIData,loadKH:loadKhachHang,findKH:findKH,processQueue:processQueue,syncFromServer:syncFromServer};
var qLen=getQueue().length;
console.log('[OS v19] '+getOrders().length+' orders'+(qLen?' | ⏳ '+qLen+' pending sync':'')+' | ☁️ Server sync enabled | ➕ Add product to order enabled');
})();
|