odoo18/custom_addons/dashboard/controllers/sale_margin_excel.py

146 lines
6.4 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import io
import json
import xlsxwriter
from odoo import http
from odoo.http import request
class SaleMarginController(http.Controller):
@http.route('/sale_margin/report_excel', type='http', auth='user', methods=['POST'], csrf=False)
def report_excel(self, **post):
try:
# Safely get JSON data from the POST body
data = json.loads(request.httprequest.data) or {}
col_model = data.get('colModel')
grid_data = data.get('gridData')
if not col_model or not grid_data:
return request.make_response('Missing data', [('Content-Type', 'text/plain')])
# ---------------------------------------------
# 1⃣ Create Excel in memory
# ---------------------------------------------
output = io.BytesIO()
workbook = xlsxwriter.Workbook(output, {'in_memory': True})
worksheet = workbook.add_worksheet('Sale Margin Report')
# ---------------------------------------------
# 2⃣ Define Excel formats
# ---------------------------------------------
title_fmt = workbook.add_format({
'bold': True, 'font_size': 16, 'font_color': 'white',
'align': 'center', 'valign': 'vcenter',
'bg_color': '#4F81BD', 'border': 1
})
header_fmt = workbook.add_format({
'bold': True, 'font_size': 11, 'font_color': 'white',
'align': 'center', 'valign': 'vcenter',
'bg_color': '#366092', 'border': 1
})
text_fmt = workbook.add_format({
'font_size': 10, 'align': 'left', 'valign': 'vcenter', 'border': 1,'text_wrap': False
})
float_fmt = workbook.add_format({
'font_size': 10, 'align': 'right', 'valign': 'vcenter',
'border': 1, 'num_format': '#,##0.00'
})
group_fmt = workbook.add_format({
'bold': True, 'font_size': 11, 'align': 'left',
'bg_color': '#DCE6F1', 'border': 1
})
subtotal_fmt = workbook.add_format({
'bold': True, 'font_size': 10, 'align': 'right',
'bg_color': '#EAF1DD', 'border': 1, 'num_format': '#,##0.00'
})
# ---------------------------------------------
# 3⃣ Title and headers
# ---------------------------------------------
visible_cols = [c for c in col_model if not c.get('hidden')]
visible_cols.pop(-1)
last_col = len(visible_cols) - 1
worksheet.merge_range(0, 0, 0, last_col, 'SALE MARGIN REPORT', title_fmt)
for col_idx, col in enumerate(visible_cols):
worksheet.write(1, col_idx, col.get('title', col.get('dataIndx', '')), header_fmt)
worksheet.set_column(col_idx, col_idx, 15)
# ---------------------------------------------
# 4⃣ Group data by "tag"
# ---------------------------------------------
group_field = 'tags' # <-- adjust if your grouping field has a different key name
grouped = {}
for row in grid_data:
group_key = row.get(group_field, 'Ungrouped')
grouped.setdefault(group_key, []).append(row)
# ---------------------------------------------
# 5⃣ Write grouped data + subtotal per group
# ---------------------------------------------
row_pos = 2
for group_name, rows in grouped.items():
# Group header
worksheet.merge_range(row_pos, 0, row_pos, last_col, f'Group: {group_name}', group_fmt)
row_pos += 1
# Track totals per numeric column
group_sums = [0.0] * len(visible_cols)
# Track margin_percent separately
margin_sum = 0
margin_count = 0
margin_idx = None
# Find index of margin_percent column
for idx, col in enumerate(visible_cols):
if col.get('dataIndx') == 'margin_percent':
margin_idx = idx
break
# Write group rows
for row_data in rows:
for col_idx, col in enumerate(visible_cols):
key = col.get('dataIndx')
val = row_data.get(key, '')
if isinstance(val, (int, float)):
if key == 'margin_percent':
margin_sum += val
margin_count += 1
worksheet.write_number(row_pos, col_idx, val, float_fmt)
else:
worksheet.write_number(row_pos, col_idx, val, float_fmt)
group_sums[col_idx] += val
else:
worksheet.write(row_pos, col_idx, str(val), text_fmt)
row_pos += 1
# Subtotal line
worksheet.write(row_pos, 0, f'{group_name} Total', subtotal_fmt)
for col_idx, total in enumerate(group_sums):
if col_idx > 0 and total != 0:
worksheet.write_number(row_pos, col_idx, total, subtotal_fmt)
# Write margin_percent average
if margin_idx is not None and margin_count > 0:
margin_avg = margin_sum / margin_count
worksheet.write_number(row_pos, margin_idx, margin_avg, subtotal_fmt)
row_pos += 2 # leave a blank line after subtotal
# ---------------------------------------------
# Finalize and return file
# ---------------------------------------------
workbook.close()
output.seek(0)
filecontent = output.read()
headers = [
('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'),
('Content-Disposition', 'attachment; filename="Sale_Margin_Report.xlsx"'),
]
return request.make_response(filecontent, headers=headers)
except Exception as e:
return request.make_response(f"Error: {str(e)}", [('Content-Type', 'text/plain')])