Use qr-payment-v1783485884.js (new click handler)
Browse files- 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=
|
| 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>
|