bep40 commited on
Commit
22d968e
·
verified ·
1 Parent(s): 0affd41

Use qr-payment-v1783485884.js (new click handler)

Browse files
Files changed (1) hide show
  1. index.html +1 -238
index.html CHANGED
@@ -2064,246 +2064,9 @@ function _vaiExcelParseExtras(){
2064
  return out;
2065
  }
2066
 
2067
- async function exportExcel(){
2068
- exportExcel.__vaiOriginal = true;
2069
- if(typeof ExcelJS==='undefined'){
2070
- showToast('⚠️ Thư viện Excel chưa tải xong. Vui lòng chờ 2-3 giây rồi thử lại.');
2071
- return;
2072
- }
2073
- let qd=getQuoteData();
2074
- let extras=_vaiExcelParseExtras();
2075
- let wb=new ExcelJS.Workbook();
2076
- wb.creator='V.AI STUDIO';
2077
- let ws=wb.addWorksheet('Báo Giá');
2078
- let MONEY_FMT='#,##0"đ"', NUM_FMT='#,##0';
2079
- ws.columns=[
2080
- {key:'stt',width:6},{key:'name',width:32},{key:'model',width:16},{key:'specs',width:28},
2081
- {key:'image',width:18},{key:'qty',width:8},{key:'price',width:16},{key:'disc',width:16},
2082
- {key:'total',width:18},{key:'note',width:18}
2083
- ];
2084
- // Header
2085
- ws.mergeCells('A1:J1');
2086
- let h1=ws.getCell('A1');
2087
- h1.value='V.AI STUDIO - Niềm tin khách hàng là tài sản của chúng tôi';
2088
- h1.font={name:'Arial',bold:true,size:14,color:{argb:'FF003F62'}};
2089
- h1.alignment={horizontal:'center',vertical:'middle'};
2090
- h1.fill={type:'pattern',pattern:'solid',fgColor:{argb:'FFF0F7FF'}};
2091
- ws.getRow(1).height=30;
2092
- ws.mergeCells('A2:J2');
2093
- let h2=ws.getCell('A2');
2094
- h2.value='BẢNG BÁO GIÁ';
2095
- h2.font={name:'Arial',bold:true,size:16,color:{argb:'FFDB9815'}};
2096
- h2.alignment={horizontal:'center',vertical:'middle'};
2097
- ws.getRow(2).height=28;
2098
-
2099
- // Customer info
2100
- let r=4;
2101
- let ci=[['Khách hàng:',qd.customer.name],['SĐT:',qd.customer.phone],['Email:',qd.customer.email],
2102
- ['Địa chỉ:',qd.customer.addr],['Mã đơn hàng:',qd.customer.orderCode],['Ngày:',qd.customer.date]];
2103
- ci.forEach(pair=>{ws.getCell('A'+r).value=pair[0];ws.getCell('A'+r).font={bold:true,size:10};ws.getCell('B'+r).value=pair[1]||'';ws.getCell('B'+r).font={size:10};r++;});
2104
- r++;
2105
-
2106
- // Table header
2107
- let headers=['STT','Tên sản phẩm','Mã SP','Thông tin SP','Hình ảnh','SL','Đơn giá','Đơn giá CK','Thành tiền','Ghi chú'];
2108
- let headerRow=ws.getRow(r);
2109
- headers.forEach((h,i)=>{headerRow.getCell(i+1).value=h});
2110
- headerRow.height=26;
2111
- headerRow.eachCell(cell=>{cell.font={bold:true,color:{argb:'FFFFFFFF'},name:'Arial',size:9};cell.fill={type:'pattern',pattern:'solid',fgColor:{argb:'FF003F62'}};cell.border={top:{style:'thin'},left:{style:'thin'},bottom:{style:'thin'},right:{style:'thin'}};cell.alignment={horizontal:'center',vertical:'middle',wrapText:true};});
2112
- r++;
2113
- let dataStartR=r;
2114
-
2115
- // Determine discount: explicit CK35% wins; otherwise infer ratio from Giá CK/Đơn giá.
2116
- let ckPct=Number(extras.discountPercent||0);
2117
- if(!(ckPct>0&&ckPct<=100)&&qd.items.length>0&&qd.items[0].price>0){
2118
- let ratio=(Number(qd.items[0].discPrice||qd.items[0].price)||0)/(Number(qd.items[0].price)||1);
2119
- let allSameRatio=qd.items.every(it=>!it.price||Math.abs((Number(it.discPrice||it.price)/Number(it.price))-ratio)<0.005);
2120
- if(allSameRatio&&ratio<1&&ratio>0)ckPct=Math.round((1-ratio)*100);
2121
- }
2122
-
2123
- // Store CK% in cell K1 for formula reference
2124
- let ckLabel='';
2125
- if(ckPct>0&&ckPct<=100){ckLabel='CK '+ckPct+'%';}
2126
-
2127
- qd.items.forEach((item,idx)=>{
2128
- let row=ws.getRow(r);row.height=64;
2129
- row.getCell(1).value=Number(item.stt||idx+1);row.getCell(1).numFmt=NUM_FMT;row.getCell(1).alignment={horizontal:'center',vertical:'middle'};row.getCell(1).font={size:10};
2130
- row.getCell(2).value=item.name||'';row.getCell(2).alignment={vertical:'middle',wrapText:true};row.getCell(2).font={size:10,bold:true,color:{argb:'FF1A1A1A'}};
2131
- row.getCell(3).value=item.model||'';row.getCell(3).alignment={horizontal:'center',vertical:'middle'};row.getCell(3).font={size:9,color:{argb:'FF003F62'}};
2132
- row.getCell(4).value=item.specs||'';row.getCell(4).alignment={vertical:'middle',wrapText:true};row.getCell(4).font={size:8,color:{argb:'FF888888'}};
2133
- row.getCell(5).value='';row.getCell(5).alignment={horizontal:'center',vertical:'middle'};
2134
- try{let imgEl=document.querySelectorAll('.qt-img')[idx];if(imgEl&&imgEl.complete&&imgEl.naturalWidth>0){let cvs=document.createElement('canvas');cvs.width=Math.min(imgEl.naturalWidth,120);cvs.height=Math.min(imgEl.naturalHeight,120);let ctx=cvs.getContext('2d');ctx.drawImage(imgEl,0,0,cvs.width,cvs.height);let b64=cvs.toDataURL('image/jpeg',0.7).split(',')[1];let imgId=wb.addImage({base64:b64,extension:'jpeg'});ws.addImage(imgId,{tl:{col:4,row:r-1},ext:{width:70,height:70}});}}catch(ie){}
2135
- row.getCell(6).value=Number(item.qty||1);row.getCell(6).numFmt=NUM_FMT;row.getCell(6).alignment={horizontal:'center',vertical:'middle'};row.getCell(6).font={size:10,bold:true};
2136
- row.getCell(7).value=Number(item.price)||0;row.getCell(7).numFmt=MONEY_FMT;row.getCell(7).alignment={horizontal:'right',vertical:'middle'};row.getCell(7).font={size:10};
2137
- let cH=row.getCell(8);
2138
- // Formula uses cell K1 (e.g. 0.65 for 35% off) so user can edit CK% and all rows recalculate
2139
- if(ckPct>0&&ckPct<100){cH.value={formula:`ROUND(G${r}*$K$1,0)`,result:Math.round((Number(item.price)||0)*(1-ckPct/100))};}
2140
- else{cH.value=Number(item.discPrice)||Number(item.price)||0;}
2141
- cH.numFmt=MONEY_FMT;cH.alignment={horizontal:'right',vertical:'middle'};if((Number(item.discPrice)||0)<(Number(item.price)||0)){cH.font={size:10,color:{argb:'FFDC3545'},bold:true}}else{cH.font={size:10}}
2142
- let cI=row.getCell(9);cI.value={formula:`F${r}*H${r}`,result:(Number(item.qty||1))*(Number(item.discPrice)||Number(item.price)||0)};cI.numFmt=MONEY_FMT;cI.alignment={horizontal:'right',vertical:'middle'};cI.font={size:10,bold:true,color:{argb:'FF003F62'}};
2143
- row.getCell(10).value=item.note||'';row.getCell(10).alignment={vertical:'middle',wrapText:true};row.getCell(10).font={size:9};
2144
- for(let c=1;c<=10;c++){row.getCell(c).border={top:{style:'thin',color:{argb:'FFD0D5DD'}},left:{style:'thin',color:{argb:'FFD0D5DD'}},bottom:{style:'thin',color:{argb:'FFD0D5DD'}},right:{style:'thin',color:{argb:'FFD0D5DD'}}};}
2145
- if((idx+1)%2===0){for(let c=1;c<=10;c++){row.getCell(c).fill={type:'pattern',pattern:'solid',fgColor:{argb:'FFF8FAFC'}}}}
2146
- r++;
2147
- });
2148
- let dataEndR=r-1;
2149
- ws.getColumn(6).numFmt=NUM_FMT;ws.getColumn(7).numFmt=MONEY_FMT;ws.getColumn(8).numFmt=MONEY_FMT;ws.getColumn(9).numFmt=MONEY_FMT;
2150
- // Write CK% factor into K1 (e.g. 0.65 = 35% off) with label
2151
- if(ckPct>0&&ckPct<100){
2152
- ws.getCell('K1').value=Number((1-ckPct/100).toFixed(4));
2153
- ws.getCell('K1').numFmt='0.00%';
2154
- ws.getCell('K1').font={size:8,color:{argb:'FF888888'}};
2155
- ws.getColumn(11).width=10;
2156
- ws.getCell('K2').value='CK '+ckPct+'%';
2157
- ws.getCell('K2').font={bold:true,size:9,color:{argb:'FFDC3545'}};
2158
- }
2159
- // BẢO HÀNH row (always)
2160
- r++;
2161
- ws.mergeCells(r,1,r,8);
2162
- ws.getCell('A'+r).value='✅ Bảo hành 2 năm (sản phẩm) — Bảo hành hoen gỉ vĩnh viễn (rổ SUS304)';
2163
- ws.getCell('A'+r).font={size:9,color:{argb:'FF059669'},italic:true};
2164
- ws.getCell('A'+r).alignment={vertical:'middle'};
2165
- ws.getCell('I'+r).value='';
2166
- for(let c=1;c<=10;c++){ws.getRow(r).getCell(c).fill={type:'pattern',pattern:'solid',fgColor:{argb:'FFF0FDF4'}};}
2167
- // PHỤ PHÍ rows — included in TỔNG CỘNG
2168
- let totalFormulaParts=[`SUM(I${dataStartR}:I${dataEndR})`];
2169
- (extras.fees||[]).forEach(f=>{
2170
- ws.mergeCells(r,1,r,8);
2171
- ws.getCell('A'+r).value='⊕ '+(f.label||'Phụ phí');ws.getCell('A'+r).font={size:10,color:{argb:'FF92400E'}};ws.getCell('A'+r).alignment={horizontal:'right',vertical:'middle'};
2172
- ws.getCell('I'+r).value=Number(f.amount||0);ws.getCell('I'+r).numFmt=MONEY_FMT;ws.getCell('I'+r).font={bold:true,size:10,color:{argb:'FF92400E'}};ws.getCell('I'+r).alignment={horizontal:'right',vertical:'middle'};
2173
- for(let c=1;c<=10;c++){ws.getRow(r).getCell(c).fill={type:'pattern',pattern:'solid',fgColor:{argb:'FFFFFBEB'}};ws.getRow(r).getCell(c).border={top:{style:'thin',color:{argb:'FFD0D5DD'}},bottom:{style:'thin',color:{argb:'FFD0D5DD'}}};}
2174
- totalFormulaParts.push(`I${r}`);r++;
2175
- });
2176
-
2177
- // TỔNG CỘNG = SUM sản phẩm + phụ phí nếu có
2178
- let totalR=r;
2179
- ws.mergeCells(totalR,1,totalR,8);
2180
- ws.getCell('A'+totalR).value='TỔNG CỘNG:';ws.getCell('A'+totalR).font={bold:true,size:12,color:{argb:'FFFFFFFF'}};ws.getCell('A'+totalR).alignment={horizontal:'right',vertical:'middle'};
2181
- // Tổng cộng số lượng nằm CÙNG DÒNG tổng cộng, tại cột SL (F)
2182
- ws.getCell('F'+totalR).value={formula:`SUM(F${dataStartR}:F${dataEndR})`,result:qd.items.reduce((s,it)=>s+Number(it.qty||1),0)};
2183
- ws.getCell('F'+totalR).numFmt=NUM_FMT;ws.getCell('F'+totalR).font={bold:true,size:12,color:{argb:'FFFFFFFF'}};ws.getCell('F'+totalR).alignment={horizontal:'center',vertical:'middle'};
2184
- ws.getCell('I'+totalR).value={formula:totalFormulaParts.join('+'),result:(qd.items.reduce((s,it)=>s+(Number(it.qty||1))*(Number(it.discPrice)||Number(it.price)||0),0)+(extras.fees||[]).reduce((s,f)=>s+Number(f.amount||0),0))};
2185
- ws.getCell('I'+totalR).numFmt=MONEY_FMT;ws.getCell('I'+totalR).font={bold:true,size:13,color:{argb:'FFFFFFFF'}};ws.getCell('I'+totalR).alignment={horizontal:'right',vertical:'middle'};
2186
- for(let c=1;c<=10;c++){ws.getRow(totalR).getCell(c).fill={type:'pattern',pattern:'solid',fgColor:{argb:'FF003F62'}};ws.getRow(totalR).getCell(c).border={top:{style:'medium',color:{argb:'FF003F62'}},bottom:{style:'thin',color:{argb:'FF003F62'}}};}
2187
- ws.getRow(totalR).height=30;r++;
2188
-
2189
- // CỌC if có
2190
- let depositR=null;
2191
- if(Number(extras.deposit||0)>0){
2192
- depositR=r;ws.mergeCells(r,1,r,8);
2193
- ws.getCell('A'+r).value='Đã cọc:';ws.getCell('A'+r).font={bold:true,size:11,color:{argb:'FF166534'}};ws.getCell('A'+r).alignment={horizontal:'right',vertical:'middle'};
2194
- ws.getCell('I'+r).value=Number(extras.deposit||0);ws.getCell('I'+r).numFmt=MONEY_FMT;ws.getCell('I'+r).font={bold:true,size:11,color:{argb:'FF166534'}};ws.getCell('I'+r).alignment={horizontal:'right',vertical:'middle'};
2195
- for(let c=1;c<=10;c++){ws.getRow(r).getCell(c).fill={type:'pattern',pattern:'solid',fgColor:{argb:'FFF0FDF4'}};ws.getRow(r).getCell(c).border={top:{style:'thin',color:{argb:'FFD0D5DD'}},bottom:{style:'thin',color:{argb:'FFD0D5DD'}}};}
2196
- r++;
2197
- ws.mergeCells(r,1,r,8);
2198
- ws.getCell('A'+r).value='Còn lại:';ws.getCell('A'+r).font={bold:true,size:11,color:{argb:'FFDC2626'}};ws.getCell('A'+r).alignment={horizontal:'right',vertical:'middle'};
2199
- ws.getCell('I'+r).value={formula:`MAX(I${totalR}-I${depositR},0)`,result:Math.max((qd.items.reduce((s,it)=>s+(Number(it.qty||1))*(Number(it.discPrice)||Number(it.price)||0),0)+(extras.fees||[]).reduce((s,f)=>s+Number(f.amount||0),0))-Number(extras.deposit||0),0)};
2200
- ws.getCell('I'+r).numFmt=MONEY_FMT;ws.getCell('I'+r).font={bold:true,size:12,color:{argb:'FFDC2626'}};ws.getCell('I'+r).alignment={horizontal:'right',vertical:'middle'};
2201
- for(let c=1;c<=10;c++){ws.getRow(r).getCell(c).fill={type:'pattern',pattern:'solid',fgColor:{argb:'FFFEF2F2'}};ws.getRow(r).getCell(c).border={top:{style:'thin',color:{argb:'FFD0D5DD'}},bottom:{style:'thin',color:{argb:'FFD0D5DD'}}};}
2202
- r++;
2203
- }
2204
-
2205
- // Optional notes
2206
- if(extras.notes&&extras.notes.length){ws.mergeCells(r,1,r,10);ws.getCell('A'+r).value='📝 Ghi chú: '+extras.notes.join('; ');ws.getCell('A'+r).font={italic:true,size:9,color:{argb:'FF166534'}};ws.getCell('A'+r).alignment={wrapText:true};r++;}
2207
- r++;
2208
- ws.mergeCells(r,1,r,10);ws.getCell('A'+r).value='📌 Miễn phí giao hàng trong TPHCM. Giá đã bao gồm VAT (Hỗ trợ trị giá đơn hàng VAT xuất theo đúng số tiền khác mã).';ws.getCell('A'+r).font={italic:true,size:9,color:{argb:'FF888888'}};ws.getCell('A'+r).alignment={wrapText:true};r++;
2209
- ws.mergeCells(r,1,r,10);ws.getCell('A'+r).value='💬 Zalo: Zalo Bot | ';ws.getCell('A'+r).font={italic:true,size:9,color:{argb:'FF888888'}};ws.getCell('A'+r).alignment={wrapText:true};
2210
-
2211
- wb.calcProperties.fullCalcOnLoad=true;
2212
- try{
2213
- let buf=await wb.xlsx.writeBuffer();
2214
- let blob=new Blob([buf],{type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
2215
- let url=URL.createObjectURL(blob);
2216
- let a=document.createElement('a');a.href=url;a.download=(qd.customer.orderCode||'BG')+'.xlsx';a.click();setTimeout(function(){URL.revokeObjectURL(url);},60000);
2217
- showToast('Đã xuất file Excel số + công thức!');
2218
- }catch(e){
2219
- console.error('ExcelJS error:',e);
2220
- showToast('⚠️ Xuất Excel thất bại, chuyển sang CSV...');
2221
- setTimeout(function(){exportQuoteCSV(qd);},300);
2222
- }
2223
- }
2224
 
2225
 
2226
  // ===== PDF EXPORT (html2canvas for Vietnamese) =====
2227
- async function exportPDF(){
2228
- exportPDF.__vaiOriginal = true;
2229
- let qd=getQuoteData();
2230
- // Build a hidden HTML table for rendering
2231
- let div=document.createElement('div');
2232
- div.id='pdfRender';
2233
- div.style.cssText='position:fixed;top:-9999px;left:0;width:1100px;background:#fff;padding:40px;font-family:Inter,Arial,sans-serif';
2234
- let itemsHtml=qd.items.map(it=>`<tr style="border-bottom:1px solid #e2e8f0">
2235
- <td style="padding:10px 6px;text-align:center;font-size:13px">${it.stt}</td>
2236
- <td style="padding:10px 6px"><img src="${it.image}" style="width:80px;height:80px;object-fit:contain;border-radius:6px;border:1px solid #e2e8f0;background:#f8fafc" onerror="_imgFallback(this)"></td>
2237
- <td style="padding:10px 6px;font-size:12px;font-weight:600">${it.name}</td>
2238
- <td style="padding:10px 6px;text-align:center;font-size:12px">${it.model}</td>
2239
- <td style="padding:10px 6px;font-size:11px;color:#64748b;max-width:180px">${it.specs}</td>
2240
- <td style="padding:10px 6px;text-align:center;font-size:13px">${it.qty}</td>
2241
- <td style="padding:10px 6px;text-align:right;font-size:12px">${fmtVND(it.price)}</td>
2242
- <td style="padding:10px 6px;text-align:right;font-size:12px;${it.discPrice<it.price?'color:#dc3545;font-weight:700':''}">${fmtVND(it.discPrice)}</td>
2243
- <td style="padding:10px 6px;text-align:right;font-size:13px;font-weight:700">${fmtVND(it.total)}</td>
2244
- <td style="padding:10px 6px;font-size:11px">${it.note}</td>
2245
- </tr>`).join('');
2246
-
2247
- div.innerHTML=`
2248
- <div style="text-align:center;margin-bottom:20px">
2249
- <img src="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/logo_main.png" alt="V.AI STUDIO" style="height:40px;object-fit:contain">
2250
- <div style="font-size:11px;color:#64748b;letter-spacing:2px;font-style:italic">❝ Niềm tin khách hàng là tài sản của chúng tôi ❞</div>
2251
- <div style="font-size:20px;font-weight:800;color:#db9815;margin-top:10px">BẢNG BÁO GIÁ</div>
2252
- </div>
2253
- <div style="display:flex;justify-content:space-between;margin-bottom:20px;font-size:12px;color:#0f172a">
2254
- <div><strong>Khách hàng:</strong> ${qd.customer.name}<br><strong>SĐT:</strong> ${qd.customer.phone}<br><strong>Email:</strong> ${qd.customer.email}<br><strong>Địa chỉ:</strong> ${qd.customer.addr}</div>
2255
- <div style="text-align:right"><strong>Mã đơn hàng:</strong> ${qd.customer.orderCode}<br><strong>Ngày:</strong> ${qd.customer.date}<br><strong>Zalo:</strong> <a href="https://bot.zaloplatforms.com/bots/4013442364902370344" target="_blank">Zalo Bot</a></div>
2256
- </div>
2257
- <table style="width:100%;border-collapse:collapse">
2258
- <thead><tr style="background:#003f62;color:#fff">
2259
- <th style="padding:10px 6px;font-size:11px">STT</th><th style="padding:10px 6px;font-size:11px">Hình ảnh</th><th style="padding:10px 6px;font-size:11px;text-align:left">Tên sản phẩm</th><th style="padding:10px 6px;font-size:11px">Mã SP</th><th style="padding:10px 6px;font-size:11px">Thông tin SP</th><th style="padding:10px 6px;font-size:11px">SL</th><th style="padding:10px 6px;font-size:11px">Đơn giá</th><th style="padding:10px 6px;font-size:11px">Đơn giá CK</th><th style="padding:10px 6px;font-size:11px">Thành tiền</th><th style="padding:10px 6px;font-size:11px">Ghi chú</th>
2260
- </tr></thead>
2261
- <tbody>${itemsHtml}</tbody>
2262
- <tfoot><tr style="border-top:3px solid #003f62">
2263
- <td colspan="8" style="padding:12px 6px;text-align:right;font-size:14px;font-weight:800;color:#003f62">TỔNG CỘNG:</td>
2264
- <td style="padding:12px 6px;text-align:right;font-size:15px;font-weight:900;color:#003f62">${fmtVND(qd.grandTotal)}</td><td></td>
2265
- </tr></tfoot>
2266
- </table>
2267
- <div style="margin-top:20px;font-size:11px;color:#64748b;font-style:italic">
2268
- Miễn phí giao hàng trong TPHCM. | Giá đã bao gồm VAT (Hỗ trợ trị giá đơn hàng VAT xuất theo đúng số tiền khác mã).
2269
- </div>`;
2270
-
2271
- document.body.appendChild(div);
2272
- // Wait for images and fonts
2273
- await new Promise(r=>setTimeout(r,500));
2274
-
2275
- try{
2276
- let canvas=await html2canvas(div,{scale:2,useCORS:true,allowTaint:true,logging:false,backgroundColor:'#fff'});
2277
- let imgData=canvas.toDataURL('image/jpeg',0.95);
2278
- if(typeof window.jspdf==='undefined'||!window.jspdf.jsPDF){
2279
- showToast('⚠️ Thư viện PDF chưa tải. Vui lòng chờ 2-3 giây rồi thử lại.');
2280
- div.remove();return;
2281
- }
2282
- let {jsPDF}=window.jspdf;
2283
- let pdf=new jsPDF({orientation:'landscape',unit:'mm',format:'a4'});
2284
- let pdfW=pdf.internal.pageSize.getWidth();
2285
- let pdfH=pdf.internal.pageSize.getHeight();
2286
- let imgW=canvas.width;let imgH=canvas.height;
2287
- let ratio=pdfW/imgW;// html2canvas scale:2
2288
- let totalH=imgH*ratio;
2289
- let pageH=pdfH-10;let y=5;let srcY=0;
2290
- let sliceH=pageH/ratio;
2291
- // Multi-page support
2292
- while(srcY<imgH){
2293
- if(srcY>0)pdf.addPage();
2294
- let clipCanvas=document.createElement('canvas');
2295
- clipCanvas.width=imgW;clipCanvas.height=Math.min(sliceH,imgH-srcY);
2296
- clipCanvas.getContext('2d').drawImage(canvas,0,srcY,imgW,clipCanvas.height,0,0,imgW,clipCanvas.height);
2297
- let sliceData=clipCanvas.toDataURL('image/jpeg',0.95);
2298
- let sliceRatio=pdfW/imgW;
2299
- pdf.addImage(sliceData,'JPEG',0,y,pdfW,clipCanvas.height*sliceRatio);
2300
- srcY+=sliceH;
2301
- }
2302
- pdf.save((qd.customer.orderCode||'BG')+'.pdf');
2303
- showToast('Đã xuất file PDF!');
2304
- }catch(e){console.error('PDF error:',e);showToast('Lỗi xuất PDF. Vui lòng thử lại.')}
2305
- div.remove();
2306
- }
2307
 
2308
  function printQuotation(){
2309
  exportPDF();// Reuse PDF then user can print from PDF viewer
@@ -2560,7 +2323,7 @@ Miễn phí giao hàng trong TPHCM. | Giá đã bao gồm VAT.
2560
  <script src="url-added-live-loader.js?v=1019"></script>
2561
  <script src="order-store.js?v=19"></script>
2562
  <script src="quote-ui.js?v=8"></script>
2563
- <script src="qr-payment.js?v=26"></script>
2564
  <script src="ai-search.js?v=1"></script>
2565
  <script src="ai-search-fix.js?v=4"></script>
2566
  <script src="ui-fixes.js?v=4"></script>
 
2064
  return out;
2065
  }
2066
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2067
 
2068
 
2069
  // ===== PDF EXPORT (html2canvas for Vietnamese) =====
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2070
 
2071
  function printQuotation(){
2072
  exportPDF();// Reuse PDF then user can print from PDF viewer
 
2323
  <script src="url-added-live-loader.js?v=1019"></script>
2324
  <script src="order-store.js?v=19"></script>
2325
  <script src="quote-ui.js?v=8"></script>
2326
+ <script src="qr-payment-v1783485884.js?v=1"></script>
2327
  <script src="ai-search.js?v=1"></script>
2328
  <script src="ai-search-fix.js?v=4"></script>
2329
  <script src="ui-fixes.js?v=4"></script>