From 1ba352f127b9778dbc088d3994cf2d1baa16b22a Mon Sep 17 00:00:00 2001 From: Mahdi Date: Sat, 24 Oct 2026 14:09:46 +0330 Subject: [PATCH] fix(tahesab): open invoice PDF window synchronously on click Awaiting settings before window.open left a blank tab; open about:blank on click then fill via blob URL. --- .../person-assets/PersonAssetsPanel.vue | 22 ++++++- apps/web/src/lib/tahesab-invoice-pdf.ts | 57 +++++++++++++++---- 2 files changed, 66 insertions(+), 13 deletions(-) diff --git a/apps/web/src/components/person-assets/PersonAssetsPanel.vue b/apps/web/src/components/person-assets/PersonAssetsPanel.vue index 77092cf..a325492 100644 --- a/apps/web/src/components/person-assets/PersonAssetsPanel.vue +++ b/apps/web/src/components/person-assets/PersonAssetsPanel.vue @@ -411,15 +411,35 @@ async function exportAsnadPdf(g: { rows: Record[]; }) { error.value = ''; + // باز کردن هم‌زمان با کلیک — وگرنه بعد از await پاپ‌آپ بلاک/خالی می‌شود + const preview = window.open('about:blank', '_blank'); + if (preview) { + try { + preview.document.write( + 'فاکتوردر حال آماده‌سازی فاکتور…', + ); + preview.document.close(); + } catch { + /* ignore */ + } + } + if (!invoiceSettings.value) { await loadInvoiceSettings(); } - printTahesabInvoicePdf({ + + const okPrint = printTahesabInvoicePdf({ group: g, settings: invoiceSettings.value, customerName: props.personName?.trim() || 'مشتری', moshtariCode: currentLink.value?.moshtariCode ?? null, + targetWindow: preview, }); + if (!okPrint) { + error.value = + 'خروجی فاکتور باز نشد. پاپ‌آپ مرورگر را برای این سایت مجاز کنید و دوباره بزنید.'; + preview?.close(); + } } async function loadAsnad() { diff --git a/apps/web/src/lib/tahesab-invoice-pdf.ts b/apps/web/src/lib/tahesab-invoice-pdf.ts index d6b0c52..0b6a4b1 100644 --- a/apps/web/src/lib/tahesab-invoice-pdf.ts +++ b/apps/web/src/lib/tahesab-invoice-pdf.ts @@ -62,8 +62,10 @@ export function printTahesabInvoicePdf(opts: { settings: TahesabInvoiceSettingsDto | null; customerName: string; moshtariCode?: number | null; -}) { - const { group, settings, customerName, moshtariCode } = opts; + /** پنجره‌ای که هم‌زمان با کلیک کاربر باز شده (برای دور زدن بلاک پاپ‌آپ) */ + targetWindow?: Window | null; +}): boolean { + const { group, settings, customerName, moshtariCode, targetWindow } = opts; const first = group.rows[0] ?? {}; const time = dash(first.Tarikh_Time ?? first.ZamanSabt, ''); const user = dash(first.User, ''); @@ -106,9 +108,6 @@ export function printTahesabInvoicePdf(opts: { ? `

${escapeHtml(settings.footerText.trim())}

` : ''; - const win = window.open('', '_blank', 'noopener,noreferrer,width=900,height=1200'); - if (!win) return; - const html = ` @@ -244,11 +243,45 @@ export function printTahesabInvoicePdf(opts: { `; - win.document.open(); - win.document.write(html); - win.document.close(); - win.focus(); - setTimeout(() => { - win.print(); - }, 300); + const blob = new Blob([html], { type: 'text/html;charset=utf-8' }); + const url = URL.createObjectURL(blob); + + let win: Window | null = + targetWindow && !targetWindow.closed ? targetWindow : null; + + if (win) { + try { + win.location.href = url; + } catch { + try { + win.document.open(); + win.document.write(html); + win.document.close(); + } catch { + URL.revokeObjectURL(url); + return false; + } + } + } else { + // بدون noopener تا reference داشته باشیم + win = window.open(url, '_blank', 'width=960,height=1200'); + if (!win) { + URL.revokeObjectURL(url); + return false; + } + } + + const triggerPrint = () => { + try { + win?.focus(); + win?.print(); + } catch { + /* ignore */ + } finally { + setTimeout(() => URL.revokeObjectURL(url), 60_000); + } + }; + + setTimeout(triggerPrint, 600); + return true; }