From b8ab02075406df6709b80861ccec32969737a03d Mon Sep 17 00:00:00 2001 From: Babak Alizadeh Date: Sun, 28 Sep 2025 12:28:41 +0330 Subject: [PATCH] bug fix in persons --- hesabixAPI/adapters/api/v1/persons.py | 38 ++++++++++++++++++- .../lib/pages/business/persons_page.dart | 1 + .../widgets/data_table/data_table_widget.dart | 33 ++++++++++++++-- 3 files changed, 66 insertions(+), 6 deletions(-) diff --git a/hesabixAPI/adapters/api/v1/persons.py b/hesabixAPI/adapters/api/v1/persons.py index 1e5645a..fc9a64e 100644 --- a/hesabixAPI/adapters/api/v1/persons.py +++ b/hesabixAPI/adapters/api/v1/persons.py @@ -147,6 +147,7 @@ async def export_persons_excel( import io import json import datetime + import re from openpyxl import Workbook from openpyxl.styles import Font, Alignment, PatternFill, Border, Side from fastapi.responses import Response @@ -243,7 +244,22 @@ async def export_persons_excel( wb.save(buffer) buffer.seek(0) - filename = f"persons_export_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx" + # Build meaningful filename + biz_name = "" + try: + b = db.query(Business).filter(Business.id == business_id).first() + if b is not None: + biz_name = b.name or "" + except Exception: + biz_name = "" + def slugify(text: str) -> str: + return re.sub(r"[^A-Za-z0-9_-]+", "_", text).strip("_") + base = "persons" + if biz_name: + base += f"_{slugify(biz_name)}" + if selected_only: + base += "_selected" + filename = f"{base}_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx" content = buffer.getvalue() return Response( content=content, @@ -251,6 +267,7 @@ async def export_persons_excel( headers={ "Content-Disposition": f"attachment; filename={filename}", "Content-Length": str(len(content)), + "Access-Control-Expose-Headers": "Content-Disposition", }, ) @@ -268,6 +285,7 @@ async def export_persons_pdf( ): import json import datetime + import re from fastapi.responses import Response from weasyprint import HTML, CSS from weasyprint.text.fonts import FontConfiguration @@ -448,13 +466,29 @@ async def export_persons_pdf( font_config = FontConfiguration() pdf_bytes = HTML(string=table_html).write_pdf(font_config=font_config) - filename = f"persons_export_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.pdf" + # Build meaningful filename + biz_name = "" + try: + b = db.query(Business).filter(Business.id == business_id).first() + if b is not None: + biz_name = b.name or "" + except Exception: + biz_name = "" + def slugify(text: str) -> str: + return re.sub(r"[^A-Za-z0-9_-]+", "_", text).strip("_") + base = "persons" + if biz_name: + base += f"_{slugify(biz_name)}" + if selected_only: + base += "_selected" + filename = f"{base}_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.pdf" return Response( content=pdf_bytes, media_type="application/pdf", headers={ "Content-Disposition": f"attachment; filename={filename}", "Content-Length": str(len(pdf_bytes)), + "Access-Control-Expose-Headers": "Content-Disposition", }, ) diff --git a/hesabixUI/hesabix_ui/lib/pages/business/persons_page.dart b/hesabixUI/hesabix_ui/lib/pages/business/persons_page.dart index 61ebb33..f215c72 100644 --- a/hesabixUI/hesabix_ui/lib/pages/business/persons_page.dart +++ b/hesabixUI/hesabix_ui/lib/pages/business/persons_page.dart @@ -60,6 +60,7 @@ class _PersonsPageState extends State { showTableIcon: false, showRowNumbers: true, enableRowSelection: true, + enableMultiRowSelection: true, columns: [ NumberColumn( 'code', diff --git a/hesabixUI/hesabix_ui/lib/widgets/data_table/data_table_widget.dart b/hesabixUI/hesabix_ui/lib/widgets/data_table/data_table_widget.dart index af453ad..9763e93 100644 --- a/hesabixUI/hesabix_ui/lib/widgets/data_table/data_table_widget.dart +++ b/hesabixUI/hesabix_ui/lib/widgets/data_table/data_table_widget.dart @@ -595,12 +595,37 @@ class _DataTableWidgetState extends State> { ); if (response.data != null) { + // Determine filename from Content-Disposition header if present + String? contentDisposition = response.headers.value('content-disposition'); + String filename = 'export_${DateTime.now().millisecondsSinceEpoch}.${format == 'pdf' ? 'pdf' : 'xlsx'}'; + if (contentDisposition != null) { + try { + final parts = contentDisposition.split(';').map((s) => s.trim()); + for (final p in parts) { + if (p.toLowerCase().startsWith('filename=')) { + var name = p.substring('filename='.length).trim(); + if (name.startsWith('"') && name.endsWith('"') && name.length >= 2) { + name = name.substring(1, name.length - 1); + } + if (name.isNotEmpty) { + filename = name; + } + break; + } + } + } catch (_) { + // Fallback to default filename + } + } + final expectedExt = format == 'pdf' ? '.pdf' : '.xlsx'; + if (!filename.toLowerCase().endsWith(expectedExt)) { + filename = '$filename$expectedExt'; + } + if (format == 'pdf') { - // Handle PDF download - await _downloadPdf(response.data, 'referrals_export_${DateTime.now().millisecondsSinceEpoch}.pdf'); + await _downloadPdf(response.data, filename); } else if (format == 'excel') { - // Handle Excel download - await _downloadExcel(response.data, 'referrals_export_${DateTime.now().millisecondsSinceEpoch}.xlsx'); + await _downloadExcel(response.data, filename); } if (mounted) {