feat(fusion_accounting_reports): reports_service.js reactive frontend service
Made-with: Cursor
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
'name': 'Fusion Accounting Reports',
|
||||
'version': '19.0.1.0.22',
|
||||
'version': '19.0.1.0.23',
|
||||
'category': 'Accounting/Accounting',
|
||||
'summary': 'AI-augmented financial reports (P&L, balance sheet, trial balance, GL).',
|
||||
'description': """
|
||||
@@ -42,6 +42,7 @@ menu hides; the engine and AI tools remain available for the chat.
|
||||
'fusion_accounting_reports/static/src/scss/_variables.scss',
|
||||
'fusion_accounting_reports/static/src/scss/reports.scss',
|
||||
'fusion_accounting_reports/static/src/scss/dark_mode.scss',
|
||||
'fusion_accounting_reports/static/src/services/reports_service.js',
|
||||
],
|
||||
},
|
||||
'installable': True,
|
||||
|
||||
147
fusion_accounting_reports/static/src/services/reports_service.js
Normal file
147
fusion_accounting_reports/static/src/services/reports_service.js
Normal file
@@ -0,0 +1,147 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { reactive } from "@odoo/owl";
|
||||
|
||||
const ENDPOINT_BASE = "/fusion/reports";
|
||||
|
||||
export class ReportsService {
|
||||
constructor(env, services) {
|
||||
this.env = env;
|
||||
this.rpc = services.rpc;
|
||||
this.notification = services.notification;
|
||||
|
||||
this.state = reactive({
|
||||
availableReports: [],
|
||||
currentReportType: null,
|
||||
currentResult: null,
|
||||
currentAnomalies: [],
|
||||
currentCommentary: null,
|
||||
isLoading: false,
|
||||
isGeneratingCommentary: false,
|
||||
dateFrom: null,
|
||||
dateTo: null,
|
||||
comparison: 'none',
|
||||
companyId: null,
|
||||
drillDown: null,
|
||||
});
|
||||
}
|
||||
|
||||
async loadAvailableReports(companyId = null) {
|
||||
this.state.companyId = companyId;
|
||||
this.state.isLoading = true;
|
||||
try {
|
||||
const result = await this.rpc(`${ENDPOINT_BASE}/list_available`,
|
||||
{ company_id: companyId });
|
||||
this.state.availableReports = result.reports;
|
||||
} finally {
|
||||
this.state.isLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async runReport(reportType, dateFrom, dateTo, comparison = 'none') {
|
||||
this.state.isLoading = true;
|
||||
this.state.currentReportType = reportType;
|
||||
this.state.dateFrom = dateFrom;
|
||||
this.state.dateTo = dateTo;
|
||||
this.state.comparison = comparison;
|
||||
try {
|
||||
this.state.currentResult = await this.rpc(`${ENDPOINT_BASE}/run`, {
|
||||
report_type: reportType,
|
||||
date_from: dateFrom,
|
||||
date_to: dateTo,
|
||||
comparison: comparison,
|
||||
company_id: this.state.companyId,
|
||||
});
|
||||
if (comparison && comparison !== 'none') {
|
||||
this.fetchAnomalies();
|
||||
} else {
|
||||
this.state.currentAnomalies = [];
|
||||
}
|
||||
this.state.currentCommentary = null;
|
||||
return this.state.currentResult;
|
||||
} catch (err) {
|
||||
this.notification.add(`Run failed: ${err.message || err}`, { type: 'danger' });
|
||||
throw err;
|
||||
} finally {
|
||||
this.state.isLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async fetchAnomalies() {
|
||||
if (!this.state.currentReportType) return;
|
||||
try {
|
||||
const result = await this.rpc(`${ENDPOINT_BASE}/get_anomalies`, {
|
||||
report_type: this.state.currentReportType,
|
||||
date_from: this.state.dateFrom,
|
||||
date_to: this.state.dateTo,
|
||||
comparison: this.state.comparison,
|
||||
company_id: this.state.companyId,
|
||||
});
|
||||
this.state.currentAnomalies = result.anomalies || [];
|
||||
} catch (err) {
|
||||
this.state.currentAnomalies = [];
|
||||
}
|
||||
}
|
||||
|
||||
async generateCommentary({ forceRegenerate = false } = {}) {
|
||||
if (!this.state.currentReportType) return;
|
||||
this.state.isGeneratingCommentary = true;
|
||||
try {
|
||||
this.state.currentCommentary = await this.rpc(`${ENDPOINT_BASE}/get_commentary`, {
|
||||
report_type: this.state.currentReportType,
|
||||
date_from: this.state.dateFrom,
|
||||
date_to: this.state.dateTo,
|
||||
comparison: this.state.comparison,
|
||||
company_id: this.state.companyId,
|
||||
force_regenerate: forceRegenerate,
|
||||
});
|
||||
return this.state.currentCommentary;
|
||||
} catch (err) {
|
||||
this.notification.add(`Commentary failed: ${err.message || err}`, { type: 'danger' });
|
||||
throw err;
|
||||
} finally {
|
||||
this.state.isGeneratingCommentary = false;
|
||||
}
|
||||
}
|
||||
|
||||
async drillDown(accountId, label = null) {
|
||||
try {
|
||||
const result = await this.rpc(`${ENDPOINT_BASE}/drill_down`, {
|
||||
account_id: accountId,
|
||||
date_from: this.state.dateFrom,
|
||||
date_to: this.state.dateTo,
|
||||
company_id: this.state.companyId,
|
||||
});
|
||||
this.state.drillDown = {
|
||||
accountId, label, rows: result.rows || [],
|
||||
count: result.count, isOpen: true,
|
||||
};
|
||||
return result;
|
||||
} catch (err) {
|
||||
this.notification.add(`Drill failed: ${err.message || err}`, { type: 'danger' });
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
closeDrillDown() {
|
||||
if (this.state.drillDown) {
|
||||
this.state.drillDown.isOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
setComparison(mode) {
|
||||
this.state.comparison = mode;
|
||||
if (this.state.currentReportType) {
|
||||
return this.runReport(this.state.currentReportType,
|
||||
this.state.dateFrom, this.state.dateTo, mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const reportsService = {
|
||||
dependencies: ["rpc", "notification"],
|
||||
start(env, services) { return new ReportsService(env, services); },
|
||||
};
|
||||
|
||||
registry.category("services").add("fusion_reports", reportsService);
|
||||
Reference in New Issue
Block a user