I overrided the default js method onDownloadButtonClicked (odoo/addons/web/static/src/views/pivot/pivot_render.js) for downloading xlsx files
(it`s my method) statis/src/js/views/my_render.js
/** @odoo-module **/
import { patch } from "@web/core/utils/patch";
import { PivotRenderer } from "@web/views/pivot/pivot_renderer";
import { _t } from "@web/core/l10n/translation";
import { download } from "@web/core/network/download";
patch(PivotRenderer.prototype, {
onDownloadButtonClicked() {
if (this.model.getTableWidth() > 16384) {
throw new Error(
_t(
"For Excel compatibility, data cannot be exported if there are more than 16384 columns.nnTip: try to flip axis, filter further or reduce the number of measures."
)
);
}
const table = this.model.exportData();
download({
url: "/web/pivot/export_xlsx1",
data: { data: JSON.stringify(table) },
});
},
});
I need this to add a string field to a report generated from a pivot table (it is not possible to add a string field to it directly in views).
Therefore, I wrote my own http.route(export_xlsx1) (copied from the original odoo/addons/web/controllers/pivot.py and modified).
controllers/my_pivot.py
from collections import deque
import io
import json
from werkzeug.datastructures import FileStorage
from odoo import http, _
from odoo.http import content_disposition, request
from odoo.tools import osutil
from odoo.tools.misc import xlsxwriter
from my_import import get_all_picking_notes
class TableExporter(http.Controller):
@http.route('/web/pivot/export_xlsx1', type='http', auth="user", readonly=True)
def export_xlsx(self, data, **kw):
jdata = json.load(data) if isinstance(data, FileStorage) else json.loads(data)
output = io.BytesIO()
workbook = xlsxwriter.Workbook(output, {'in_memory': True})
worksheet = workbook.add_worksheet(jdata['title'])
header_bold = workbook.add_format({'bold': True, 'pattern': 1, 'bg_color': '#AAAAAA'})
header_plain = workbook.add_format({'pattern': 1, 'bg_color': '#AAAAAA'})
bold = workbook.add_format({'bold': True})
measure_count = jdata['measure_count']
origin_count = jdata['origin_count']
# Step 1: writing col group headers
col_group_headers = jdata['col_group_headers']
# x,y: current coordinates
# carry: queue containing cell information when a cell has a >= 2 height
# and the drawing code needs to add empty cells below
x, y, carry = 1, 0, deque()
for i, header_row in enumerate(col_group_headers):
worksheet.write(i, 0, '', header_plain)
for header in header_row:
while (carry and carry[0]['x'] == x):
cell = carry.popleft()
for j in range(measure_count * (2 * origin_count - 1)):
worksheet.write(y, x+j, '', header_plain)
if cell['height'] > 1:
carry.append({'x': x, 'height': cell['height'] - 1})
x = x + measure_count * (2 * origin_count - 1)
for j in range(header['width']):
worksheet.write(y, x + j, header['title'] if j == 0 else '', header_plain)
if header['height'] > 1:
carry.append({'x': x, 'height': header['height'] - 1})
x = x + header['width']
while (carry and carry[0]['x'] == x):
cell = carry.popleft()
for j in range(measure_count * (2 * origin_count - 1)):
worksheet.write(y, x+j, '', header_plain)
if cell['height'] > 1:
carry.append({'x': x, 'height': cell['height'] - 1})
x = x + measure_count * (2 * origin_count - 1)
x, y = 1, y + 1
# Step 2: writing measure headers
measure_headers = jdata['measure_headers']
measure_headers.append({'title': 'My header','width': 1, 'height': 1, 'is_bold': True}) <--custom
if measure_headers:
worksheet.write(y, 0, '', header_plain)
for measure in measure_headers:
style = header_bold if measure['is_bold'] else header_plain
worksheet.write(y, x, measure['title'], style)
for i in range(1, 2 * origin_count - 1):
worksheet.write(y, x+i, '', header_plain)
x = x + (2 * origin_count - 1)
x, y = 1, y + 1
# set minimum width of cells to 16 which is around 88px
worksheet.set_column(0, len(measure_headers), 16)
# Step 3: writing origin headers
origin_headers = jdata['origin_headers']
if origin_headers:
worksheet.write(y, 0, '', header_plain)
for origin in origin_headers:
style = header_bold if origin['is_bold'] else header_plain
worksheet.write(y, x, origin['title'], style)
x = x + 1
y = y + 1
notes = get_all_picking_notes(request.env) <--custom
# Step 4: writing data
x = 0
for row in jdata['rows']:
worksheet.write(y, x, f"{row['indent'] * ' '}{row['title']}", header_plain)
if row['title'] in notes: <-- custom
row['values'].append({'is_bold': True, 'value': notes[row['title']]})
for cell in row['values']:
x = x + 1
if cell.get('is_bold', False):
worksheet.write(y, x, cell['value'], bold)
else:
worksheet.write(y, x, cell['value'])
x, y = 0, y + 1
workbook.close()
xlsx_data = output.getvalue()
filename = osutil.clean_filename(_("Pivot %(title)s (%(model_name)s)", title=jdata['title'], model_name=jdata['model']))
response = request.make_response(xlsx_data,
headers=[('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'),
('Content-Disposition', content_disposition(filename + '.xlsx'))],
)
return response
In devtools console only one error, and it exists even if i off debug mode (this error occurs even if remove my_render from manifest file)
Content Security Policy of your site blocks the use of 'eval' in JavaScript`
The Content Security Policy (CSP) prevents the evaluation of arbitrary strings as JavaScript to make it more difficult for an attacker to inject unathorized code on your site.
To solve this issue, avoid using eval(), new Function(), setTimeout([string], ...) and setInterval([string], ...) for evaluating strings.
If you absolutely must: you can enable string evaluation by adding unsafe-eval as an allowed source in a script-src directive.
⚠️ Allowing string evaluation comes at the risk of inline script injection.
1 directive
Source location Directive Status
script-src blocked
An error in the client (maybe module conflict or somthing else)
web.assets_web.min.js:26 The following modules could not be loaded because they have unmet dependencies, this is a secondary error which is likely caused by one of the above problems:
Array(1)
0
:
"@module_name/js/views/my_render.js"
length
:
1
[[Prototype]]
:
Array(0)
at
:
ƒ at()
concat
:
ƒ concat()
constructor
:
ƒ Array()
copyWithin
:
ƒ copyWithin()
entries
:
ƒ entries()
every
:
ƒ every()
fill
:
ƒ fill()
filter
:
ƒ filter()
find
:
ƒ find()
findIndex
:
ƒ findIndex()
findLast
:
ƒ findLast()
findLastIndex
:
ƒ findLastIndex()
flat
:
ƒ flat()
flatMap
:
ƒ flatMap()
forEach
:
ƒ forEach()
includes
:
ƒ includes()
indexOf
:
ƒ indexOf()
join
:
ƒ join()
keys
:
ƒ keys()
lastIndexOf
:
ƒ lastIndexOf()
length
:
0
map
:
ƒ map()
pop
:
ƒ pop()
push
:
ƒ push()
reduce
:
ƒ reduce()
reduceRight
:
ƒ reduceRight()
reverse
:
ƒ reverse()
shift
:
ƒ shift()
slice
:
ƒ slice()
some
:
ƒ some()
sort
:
ƒ sort()
splice
:
ƒ splice()
toLocaleString
:
ƒ toLocaleString()
toReversed
:
ƒ toReversed()
toSorted
:
ƒ toSorted()
toSpliced
:
ƒ toSpliced()
toString
:
ƒ toString()
unshift
:
ƒ unshift()
values
:
ƒ values()
with
:
ƒ with()
Symbol(Symbol.iterator)
:
ƒ values()
Symbol(Symbol.unscopables)
:
{at: true, copyWithin: true, entries: true, fill: true, find: true, …}
[[Prototype]]
:
Object
Maybe someone know how to fix this problem, or know different method to add strings to pivot value