4
4
from openpyxl .utils import get_column_letter
5
5
from openpyxl .drawing .image import Image
6
6
from frappe .utils .file_manager import save_file
7
-
7
+ from openpyxl import load_workbook
8
+ import pandas as pd
9
+ from io import BytesIO
8
10
9
11
@frappe .whitelist ()
10
12
def export_to_xf_excel (
11
- doctype , doc_name , excel_format = None , attach = False , download = False
13
+ doctype , doc_name , excel_format = None , attach = False , download = False , is_preview = False
12
14
):
13
15
14
16
doc = frappe .get_doc (doctype , doc_name )
@@ -59,6 +61,10 @@ def export_to_xf_excel(
59
61
60
62
file_name = f"XF_Excel_{ doctype } _{ doc_name } .xlsx"
61
63
64
+ if is_preview :
65
+ html_preview = generate_excel_preview (output )
66
+ return {"preview" : html_preview }
67
+
62
68
if attach :
63
69
file_doc = save_file (file_name , output .read (), doctype , doc_name , is_private = 1 )
64
70
return file_doc .file_url
@@ -79,3 +85,55 @@ def get_default_excel_format(doctype):
79
85
"XF Excel Format" , {"doc_type" : doctype , "is_default" : 1 }, "name"
80
86
)
81
87
return default_format
88
+
89
+
90
+ def rgb_to_hex (rgb ):
91
+ """Convert Excel's ARGB color to HEX"""
92
+ if rgb is None or rgb == "00000000" :
93
+ return "#FFFFFF"
94
+ return f"#{ rgb [- 6 :]} "
95
+
96
+ def generate_excel_preview (output ):
97
+ output .seek (0 )
98
+ wb = load_workbook (BytesIO (output .read ()))
99
+ ws = wb .active
100
+
101
+ merged_cells = {}
102
+ for merged_range in ws .merged_cells .ranges :
103
+ min_row , min_col , max_row , max_col = merged_range .min_row , merged_range .min_col , merged_range .max_row , merged_range .max_col
104
+ merged_cells [(min_row , min_col )] = (max_row - min_row + 1 , max_col - min_col + 1 )
105
+
106
+
107
+ html = "<table border='1' style='border-collapse: collapse; width: 100%; text-align: center;'>"
108
+
109
+ for row_idx , row in enumerate (ws .iter_rows (), start = 1 ):
110
+ html += "<tr>"
111
+ for col_idx , cell in enumerate (row , start = 1 ):
112
+ if any ((row_idx , col_idx ) in merged_range for merged_range in merged_cells if (row_idx , col_idx ) != merged_range ):
113
+ continue
114
+
115
+ rowspan , colspan = merged_cells .get ((row_idx , col_idx ), (1 , 1 ))
116
+ rowspan_attr = f' rowspan="{ rowspan } "' if rowspan > 1 else ""
117
+ colspan_attr = f' colspan="{ colspan } "' if colspan > 1 else ""
118
+
119
+
120
+ bold = "font-weight: bold;" if cell .font and cell .font .bold else ""
121
+ bg_color = rgb_to_hex (cell .fill .start_color .rgb )
122
+ text_align = "center"
123
+ if cell .alignment :
124
+ if cell .alignment .horizontal :
125
+ text_align = cell .alignment .horizontal
126
+ elif isinstance (cell .alignment , Alignment ):
127
+ text_align = cell .alignment .horizontal or "center"
128
+
129
+
130
+ value = cell .value if cell .value is not None else ""
131
+
132
+ cell_style = f"background-color: { bg_color } ; text-align: { text_align } ; padding: 5px; border: 1px solid black; { bold } " .strip ()
133
+ html += f"<td style=\" { cell_style } \" { rowspan_attr } { colspan_attr } >{ value } </td>"
134
+ html += "</tr>"
135
+
136
+ html += "</table>"
137
+
138
+ return html
139
+
0 commit comments