Skip to content

Commit 65e775e

Browse files
dnplkndllclaude
andcommitted
[IMP] spreadsheet_oca: add headless XLSX export with server-side pivot rendering
Adds SpreadsheetXlsxExporter class that generates .xlsx files from spreadsheet records without requiring a browser. Static cells are written verbatim; ODOO pivots are re-computed from fresh database state via the pivot_data engine. Includes form button, JSON-RPC endpoint, attachment creation, and comprehensive tests covering static sheets, pivots, and cross-tabs. Co-Authored-By: Claude Opus 4.6 <[email protected]>
1 parent e63e44d commit 65e775e

6 files changed

Lines changed: 850 additions & 0 deletions

File tree

spreadsheet_oca/__manifest__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"security/security.xml",
1515
"security/ir.model.access.csv",
1616
"views/spreadsheet_spreadsheet.xml",
17+
"views/spreadsheet_xlsx_export_views.xml",
1718
"data/spreadsheet_spreadsheet_import_mode.xml",
1819
"wizards/spreadsheet_select_row_number.xml",
1920
"wizards/spreadsheet_spreadsheet_import.xml",

spreadsheet_oca/models/spreadsheet_spreadsheet.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,45 @@ def _compute_filename(self):
8181
for record in self:
8282
record.filename = f"{record.name or _('Unnamed')}.json"
8383

84+
# ── XLSX Export ──────────────────────────────────────────────────────────
85+
86+
def action_export_xlsx(self):
87+
"""Export this spreadsheet as .xlsx and return a download action."""
88+
from .spreadsheet_xlsx_export import SpreadsheetXlsxExporter
89+
90+
self.ensure_one()
91+
exporter = SpreadsheetXlsxExporter(self.env, self)
92+
xlsx_bytes = exporter.render()
93+
94+
filename = f"{self.name or 'spreadsheet'}.xlsx"
95+
attachment = self.env["ir.attachment"].create(
96+
{
97+
"name": filename,
98+
"type": "binary",
99+
"datas": base64.b64encode(xlsx_bytes),
100+
"mimetype": (
101+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
102+
),
103+
"res_model": self._name,
104+
"res_id": self.id,
105+
}
106+
)
107+
return {
108+
"type": "ir.actions.act_url",
109+
"url": f"/web/content/{attachment.id}?download=true",
110+
"target": "self",
111+
}
112+
113+
@api.model
114+
def get_xlsx_bytes(self, spreadsheet_id):
115+
"""Return raw .xlsx bytes (base64) for a spreadsheet."""
116+
from .spreadsheet_xlsx_export import SpreadsheetXlsxExporter
117+
118+
spreadsheet = self.browse(spreadsheet_id)
119+
spreadsheet.check_access("read")
120+
exporter = SpreadsheetXlsxExporter(self.env, spreadsheet)
121+
return base64.b64encode(exporter.render()).decode()
122+
84123
# ── Pivot Data ────────────────────────────────────────────────────────────
85124
@api.model
86125
def get_pivot_data(self, model_name, domain, context, row_dims, col_dims, measures):

0 commit comments

Comments
 (0)