130 lines
5.2 KiB
Python
130 lines
5.2 KiB
Python
# Fusion Accounting - HTTP Controllers
|
|
# Provides web endpoints for report file generation and attachment downloads
|
|
|
|
import json
|
|
|
|
from werkzeug.exceptions import InternalServerError
|
|
|
|
from odoo import http
|
|
from odoo.addons.fusion_accounting.models.account_report import AccountReportFileDownloadException
|
|
from odoo.addons.account.controllers.download_docs import _get_headers
|
|
from odoo.http import content_disposition, request
|
|
from odoo.models import check_method_name
|
|
from odoo.tools.misc import html_escape
|
|
|
|
|
|
class AccountReportController(http.Controller):
|
|
"""Handles HTTP requests for generating and downloading
|
|
accounting report files in various formats."""
|
|
|
|
@http.route('/fusion_accounting', type='http', auth='user', methods=['POST'], csrf=False)
|
|
def get_report(self, options, file_generator, **kwargs):
|
|
"""Generate a report file based on the provided options and generator method.
|
|
|
|
:param options: JSON-encoded report configuration options
|
|
:param file_generator: name of the method that produces the file
|
|
:returns: HTTP response with the generated file content
|
|
"""
|
|
current_uid = request.uid
|
|
parsed_options = json.loads(options)
|
|
|
|
# Determine which companies are in scope for this report
|
|
company_ids = request.env['account.report'].get_report_company_ids(parsed_options)
|
|
if not company_ids:
|
|
cookie_cids = request.cookies.get('cids', str(request.env.user.company_id.id))
|
|
company_ids = [int(cid) for cid in cookie_cids.split('-')]
|
|
|
|
target_report = (
|
|
request.env['account.report']
|
|
.with_user(current_uid)
|
|
.with_context(allowed_company_ids=company_ids)
|
|
.browse(parsed_options['report_id'])
|
|
)
|
|
|
|
try:
|
|
check_method_name(file_generator)
|
|
file_data = target_report.dispatch_report_action(parsed_options, file_generator)
|
|
|
|
raw_content = file_data['file_content']
|
|
output_type = file_data['file_type']
|
|
resp_headers = self._build_response_headers(
|
|
output_type, file_data['file_name'], raw_content,
|
|
)
|
|
|
|
if output_type == 'xlsx':
|
|
# Stream binary spreadsheet data
|
|
http_response = request.make_response(None, headers=resp_headers)
|
|
http_response.stream.write(raw_content)
|
|
else:
|
|
http_response = request.make_response(raw_content, headers=resp_headers)
|
|
|
|
if output_type in ('zip', 'xaf'):
|
|
# Enable streaming for large archive files to avoid
|
|
# loading the entire content into memory at once
|
|
http_response.direct_passthrough = True
|
|
|
|
return http_response
|
|
|
|
except AccountReportFileDownloadException as exc:
|
|
if exc.content:
|
|
exc.content['file_content'] = exc.content['file_content'].decode()
|
|
error_payload = {
|
|
'name': type(exc).__name__,
|
|
'arguments': [exc.errors, exc.content],
|
|
}
|
|
raise InternalServerError(
|
|
response=self._format_error_response(error_payload)
|
|
) from exc
|
|
|
|
except Exception as exc: # noqa: BLE001
|
|
error_payload = http.serialize_exception(exc)
|
|
raise InternalServerError(
|
|
response=self._format_error_response(error_payload)
|
|
) from exc
|
|
|
|
def _format_error_response(self, error_data):
|
|
"""Wrap error details into a JSON response matching the Odoo RPC error format."""
|
|
envelope = {
|
|
'code': 200,
|
|
'message': 'Odoo Server Error',
|
|
'data': error_data,
|
|
}
|
|
return request.make_response(html_escape(json.dumps(envelope)))
|
|
|
|
def _build_response_headers(self, file_type, file_name, raw_content):
|
|
"""Construct HTTP response headers appropriate for the given file type."""
|
|
mime_type = request.env['account.report'].get_export_mime_type(file_type)
|
|
header_list = [
|
|
('Content-Type', mime_type),
|
|
('Content-Disposition', content_disposition(file_name)),
|
|
]
|
|
|
|
# Include Content-Length for text-based formats
|
|
if file_type in ('xml', 'txt', 'csv', 'kvr'):
|
|
header_list.append(('Content-Length', len(raw_content)))
|
|
|
|
return header_list
|
|
|
|
@http.route(
|
|
'/fusion_accounting/download_attachments/<models("ir.attachment"):attachments>',
|
|
type='http',
|
|
auth='user',
|
|
)
|
|
def download_report_attachments(self, attachments):
|
|
"""Download one or more report attachments, packaging them
|
|
into a zip archive when multiple files are requested."""
|
|
attachments.check_access('read')
|
|
assert all(
|
|
att.res_id and att.res_model == 'res.partner'
|
|
for att in attachments
|
|
)
|
|
|
|
if len(attachments) == 1:
|
|
single = attachments
|
|
resp_headers = _get_headers(single.name, single.mimetype, single.raw)
|
|
return request.make_response(single.raw, resp_headers)
|
|
else:
|
|
zip_data = attachments._build_zip_from_attachments()
|
|
resp_headers = _get_headers('attachments.zip', 'zip', zip_data)
|
|
return request.make_response(zip_data, resp_headers)
|