//@ts-check import { EvaluationError, CellErrorType } from "@odoo/o-spreadsheet"; import { RPCError } from "@web/core/network/rpc"; import { KeepLast } from "@web/core/utils/concurrency"; import { getFields, LOADING_ERROR, ModelNotFoundError, } from "@spreadsheet/data_sources/data_source"; import { _t } from "@web/core/l10n/translation"; /** * @typedef {import("@spreadsheet").OdooFields} OdooFields * @typedef {import("@spreadsheet/data_sources/odoo_data_provider").OdooDataProvider} OdooDataProvider */ export class OdooPivotLoader { /** * @param {OdooDataProvider} odooDataProvider * @param {Function} load Function to fetch data */ constructor(odooDataProvider, load) { /** @private @type {OdooDataProvider} */ this.odooDataProvider = odooDataProvider; /** @protected */ this.loadFn = load; /** * Last time that this dataSource has been updated */ this.lastUpdate = undefined; /** @protected */ this.concurrency = new KeepLast(); /** @protected */ this.loadPromise = undefined; /** @protected */ this.isFullyLoaded = false; /** @protected */ this._isValid = true; /** @protected */ this.loadError = undefined; /** @protected */ this._isModelValid = true; } /** * Load data in the model * @param {object} [options] options for fetching data * @param {boolean} [options.reload=false] Force the reload of the data * * @returns {Promise} Resolved when data are fetched. */ async load(options) { if (options && options.reload) { this.odooDataProvider.cancelPromise(this.loadPromise); this.loadPromise = undefined; } if (!this.loadPromise) { this.isFullyLoaded = false; this._isValid = true; this.loadError = undefined; this.loadPromise = this.concurrency .add(this.loadFn()) .catch((e) => { this._isValid = false; if (e instanceof ModelNotFoundError) { this._isModelValid = false; this.loadError = Object.assign( new EvaluationError( _t(`The model "%(model)s" does not exist.`, { model: e.message }) ), { cause: e, } ); return; } this.loadError = Object.assign( new EvaluationError(e instanceof RPCError ? e.data.message : e.message), { cause: e } ); }) .finally(() => { this.lastUpdate = Date.now(); this.isFullyLoaded = true; }); await this.odooDataProvider.notifyWhenPromiseResolves(this.loadPromise); } return this.loadPromise; } /** * @param {string} model Technical name of the model * @returns {Promise} Fields of the model */ async getFields(model) { return getFields(this.odooDataProvider.serverData, model); } /** * @param {string} model Technical name of the model * @returns {Promise} Display name of the model */ async getModelLabel(model) { const result = await this.odooDataProvider.serverData.fetch( "ir.model", "display_name_for", [[model]] ); return (result[0] && result[0].display_name) || ""; } isModelValid() { return this.isFullyLoaded && this._isModelValid; } isValid() { return this.isFullyLoaded && this._isValid; } hasEverBeenLoaded() { return this.loadPromise !== undefined; } assertIsValid({ throwOnError } = { throwOnError: true }) { if (!this.isFullyLoaded) { this.load(); if (throwOnError) { throw LOADING_ERROR; } return LOADING_ERROR; } if (!this.isValid()) { if (throwOnError) { throw this.loadError; } return { value: CellErrorType.GenericError, message: this.loadError.message }; } } }