Adds a TransientModel wizard fusion.xlsx.export.wizard that lets users pick a report type, date range, and comparison mode, then runs the engine and produces an XLSX via xlsxwriter (in-memory). The wizard exposes a download field that becomes available after export finishes. Works on P&L, Balance Sheet, Trial Balance, and General Ledger. Comparison columns are written when the engine returns a comparison_period in the result. Also wires the controller's /fusion/reports/export_xlsx endpoint to drive the wizard and return base64-encoded XLSX bytes (replaces the not_implemented placeholder). Tests: 2 new (test_xlsx_export.py) + 1 controller test updated. Manifest declares xlsxwriter as an external_dependency. Made-with: Cursor
127 lines
4.2 KiB
Python
127 lines
4.2 KiB
Python
"""Controller tests using HttpCase for the 8 JSON-RPC endpoints."""
|
|
|
|
import json
|
|
|
|
from odoo.tests.common import HttpCase, new_test_user, tagged
|
|
|
|
|
|
@tagged('post_install', '-at_install')
|
|
class TestReportsController(HttpCase):
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.user = new_test_user(
|
|
self.env,
|
|
login='reports_test_user',
|
|
groups='base.group_user,account.group_account_invoice',
|
|
)
|
|
|
|
def _jsonrpc(self, endpoint, params):
|
|
self.authenticate('reports_test_user', 'reports_test_user')
|
|
url = f'/fusion/reports/{endpoint}'
|
|
body = {
|
|
'jsonrpc': '2.0',
|
|
'method': 'call',
|
|
'params': params,
|
|
'id': 1,
|
|
}
|
|
response = self.url_open(
|
|
url,
|
|
data=json.dumps(body),
|
|
headers={'Content-Type': 'application/json'},
|
|
)
|
|
self.assertEqual(
|
|
response.status_code, 200,
|
|
f"{endpoint} returned {response.status_code}: {response.text[:300]}",
|
|
)
|
|
result = response.json()
|
|
if 'error' in result:
|
|
self.fail(f"{endpoint} errored: {result['error']}")
|
|
return result.get('result', {})
|
|
|
|
def test_list_available(self):
|
|
result = self._jsonrpc('list_available', {
|
|
'company_id': self.env.company.id,
|
|
})
|
|
self.assertIn('reports', result)
|
|
codes = [r['code'] for r in result['reports']]
|
|
self.assertIn('pnl', codes)
|
|
|
|
def test_run_pnl(self):
|
|
result = self._jsonrpc('run', {
|
|
'report_type': 'pnl',
|
|
'date_from': '2026-01-01',
|
|
'date_to': '2026-12-31',
|
|
'company_id': self.env.company.id,
|
|
})
|
|
self.assertEqual(result.get('report_type'), 'pnl')
|
|
self.assertIn('rows', result)
|
|
|
|
def test_run_balance_sheet(self):
|
|
result = self._jsonrpc('run', {
|
|
'report_type': 'balance_sheet',
|
|
'date_from': '2026-01-01',
|
|
'date_to': '2026-12-31',
|
|
'company_id': self.env.company.id,
|
|
})
|
|
self.assertEqual(result.get('report_type'), 'balance_sheet')
|
|
|
|
def test_drill_down_returns_list(self):
|
|
line = self.env['account.move.line'].search(
|
|
[('parent_state', '=', 'posted')], limit=1,
|
|
)
|
|
if not line:
|
|
self.skipTest("No posted lines in DB")
|
|
result = self._jsonrpc('drill_down', {
|
|
'account_id': line.account_id.id,
|
|
'date_from': str(line.date),
|
|
'date_to': str(line.date),
|
|
'company_id': line.company_id.id,
|
|
})
|
|
self.assertIn('rows', result)
|
|
|
|
def test_get_anomalies_returns_list(self):
|
|
result = self._jsonrpc('get_anomalies', {
|
|
'report_type': 'pnl',
|
|
'date_from': '2026-01-01',
|
|
'date_to': '2026-12-31',
|
|
'comparison': 'previous_year',
|
|
'company_id': self.env.company.id,
|
|
})
|
|
self.assertIn('anomalies', result)
|
|
|
|
def test_get_commentary_returns_dict(self):
|
|
result = self._jsonrpc('get_commentary', {
|
|
'report_type': 'pnl',
|
|
'date_from': '2026-01-01',
|
|
'date_to': '2026-12-31',
|
|
'company_id': self.env.company.id,
|
|
})
|
|
self.assertIn('summary', result)
|
|
self.assertIn('highlights', result)
|
|
self.assertIn('concerns', result)
|
|
|
|
def test_export_pdf_returns_pdf(self):
|
|
result = self._jsonrpc('export_pdf', {
|
|
'report_type': 'pnl',
|
|
'date_from': '2026-01-01',
|
|
'date_to': '2026-12-31',
|
|
})
|
|
self.assertEqual(result.get('status'), 'ok')
|
|
self.assertIn('pdf_base64', result)
|
|
self.assertTrue(result.get('filename', '').endswith('.pdf'))
|
|
|
|
def test_export_xlsx_returns_xlsx(self):
|
|
try:
|
|
import xlsxwriter # noqa: F401
|
|
except ImportError:
|
|
self.skipTest("xlsxwriter not installed")
|
|
result = self._jsonrpc('export_xlsx', {
|
|
'report_type': 'pnl',
|
|
'date_from': '2026-01-01',
|
|
'date_to': '2026-12-31',
|
|
})
|
|
self.assertEqual(result.get('status'), 'ok')
|
|
self.assertTrue(result.get('xlsx_base64'))
|
|
self.assertTrue(result.get('filename', '').endswith('.xlsx'))
|