diff --git a/spreadsheet_oca/__manifest__.py b/spreadsheet_oca/__manifest__.py index 0469625d..ca7f90ab 100644 --- a/spreadsheet_oca/__manifest__.py +++ b/spreadsheet_oca/__manifest__.py @@ -24,10 +24,13 @@ "spreadsheet_oca/static/src/spreadsheet/spreadsheet_action.esm.js", "spreadsheet_oca/static/src/spreadsheet/pivot_controller.esm.js", "spreadsheet_oca/static/src/spreadsheet/graph_controller.esm.js", + "spreadsheet_oca/static/src/spreadsheet/list_controller.esm.js", + "spreadsheet_oca/static/src/spreadsheet/list_renderer.esm.js", ], "spreadsheet.o_spreadsheet": [ "spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml", "spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js", + "spreadsheet_oca/static/src/spreadsheet/bundle/filter_panel_datasources.esm.js", "spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_renderer.esm.js", "spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_controlpanel.esm.js", "spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_action.esm.js", diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js b/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js index f7bb6e3d..c4e41e2c 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js @@ -86,12 +86,12 @@ export class EditFilterPanel extends Component { objectClass.getFieldMatching(objectId, this.props.filter.id) || {}, fields: fields, + type: objectType, model: objectClass.getModel(objectId), }; ModelFields.push(fields); } } - console.log(this.state.objects); this.models = [ ...new Set( ModelFields.map((field_items) => Object.values(field_items)) @@ -133,7 +133,6 @@ export class EditFilterPanel extends Component { ? "EDIT_GLOBAL_FILTER" : "ADD_GLOBAL_FILTER"; this.env.openSidePanel("FilterPanel", {}); - var filter = { id: this.props.filter.id || uuidGenerator.uuidv4(), type: this.state.type, @@ -146,7 +145,7 @@ export class EditFilterPanel extends Component { var filterMatching = {}; Object.values(this.state.objects).forEach((object) => { filterMatching[object.type] = filterMatching[object.type] || {}; - filterMatching[object.type][object.id] = {...object.fieldMatch}; + filterMatching[object.type][object.objectId] = {...object.fieldMatch}; }); this.env.model.dispatch(action, {id: filter.id, filter, ...filterMatching}); @@ -162,7 +161,7 @@ export class EditFilterPanel extends Component { this.env.openSidePanel("FilterPanel", {}); } onFieldMatchUpdate(object, name) { - this.state.objects[object.id].fieldMatch.chain = name.chain; + this.state.objects[object.id].fieldMatch.chain = name; } } diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/filter_panel_datasources.esm.js b/spreadsheet_oca/static/src/spreadsheet/bundle/filter_panel_datasources.esm.js new file mode 100644 index 00000000..8b5e9388 --- /dev/null +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/filter_panel_datasources.esm.js @@ -0,0 +1,217 @@ +/** @odoo-module **/ + +import {Component, onWillStart, onWillUpdateProps} from "@odoo/owl"; +import {Domain} from "@web/core/domain"; +import {DomainSelector} from "@web/core/domain_selector/domain_selector"; +import {DomainSelectorDialog} from "@web/core/domain_selector_dialog/domain_selector_dialog"; +import {_t} from "web.core"; +import spreadsheet from "@spreadsheet/o_spreadsheet/o_spreadsheet_extended"; +import {time_to_str} from "web.time"; +import {useService} from "@web/core/utils/hooks"; + +const {sidePanelRegistry, topbarMenuRegistry} = spreadsheet.registries; +const {createFullMenuItem} = spreadsheet.helpers; + +topbarMenuRegistry.addChild("data_sources", ["data"], (env) => { + const children = env.model.getters.getPivotIds().map((pivotId, index) => + createFullMenuItem(`data_source_pivot_ ${pivotId}`, { + name: env.model.getters.getPivotDisplayName(pivotId), + sequence: 100, + action: (child_env) => { + child_env.model.dispatch("SELECT_PIVOT", { + pivotId: pivotId, + }); + child_env.openSidePanel("PivotPanel", {}); + }, + icon: "fa fa-table", + separator: index === env.model.getters.getPivotIds().length - 1, + }) + ); + const lists = env.model.getters.getListIds().map((listId, index) => { + return createFullMenuItem(`data_source_list_${listId}`, { + name: env.model.getters.getListDisplayName(listId), + sequence: 1010, + action: (child_env) => { + child_env.model.dispatch("SELECT_ODOO_LIST", {listId: listId}); + child_env.openSidePanel("ListPanel", {}); + }, + icon: "fa fa-list", + separator: index === env.model.getters.getListIds().length - 1, + }); + }); + return children.concat(lists).concat([ + createFullMenuItem(`refresh_all_data`, { + name: _t("Refresh all data"), + sequence: 110, + action: (child_env) => { + child_env.model.dispatch("REFRESH_ALL_DATA_SOURCES"); + }, + separator: true, + }), + ]); +}); + +export class PivotPanelDisplay extends Component { + setup() { + this.dialog = useService("dialog"); + onWillStart(this.modelData.bind(this)); + onWillUpdateProps(this.modelData.bind(this)); + } + async modelData() { + this.PivotDataSource = await this.env.model.getters.getAsyncPivotDataSource( + this.props.pivotId + ); + this.modelLabel = await this.PivotDataSource.getModelLabel(); + } + get domain() { + return new Domain(this.props.pivotDefinition.domain).toString(); + } + get pivotDimensions() { + return [ + ...this.props.pivotDefinition.rowGroupBys, + ...this.props.pivotDefinition.colGroupBys, + ].map((fieldName) => this.PivotDataSource.getFormattedGroupBy(fieldName)); + } + get sortInformation() { + const sortedColumn = this.props.pivotDefinition.sortedColumn; + const orderTranslate = + sortedColumn.order === "asc" ? _t("ascending") : _t("descending"); + const GroupByDisplayLabel = this.PivotDataSource.getGroupByDisplayLabel( + "measure", + sortedColumn.measure + ); + return `${GroupByDisplayLabel} (${orderTranslate})`; + } + get lastUpdate() { + const lastUpdate = this.PivotDataSource.lastUpdate; + if (lastUpdate) { + return time_to_str(new Date(lastUpdate)); + } + return _t("not updated"); + } + editDomain() { + this.dialog.add(DomainSelectorDialog, { + resModel: this.props.pivotDefinition.model, + initialValue: this.domain, + readonly: false, + isDebugMode: Boolean(this.env.debug), + onSelected: this.onSelectDomain.bind(this), + }); + } + onSelectDomain(domain) { + this.env.model.dispatch("UPDATE_ODOO_PIVOT_DOMAIN", { + pivotId: this.props.pivotId, + domain: new Domain(domain).toList(), + }); + } + async insertPivot() { + const datasourceModel = await this.env.model.getters + .getPivotDataSource(this.props.pivotId) + .copyModelWithOriginalDomain(); + const tableStructure = datasourceModel.getTableStructure().export(); + const selectedZone = this.env.model.getters.getSelectedZone(); + this.env.model.dispatch("RE_INSERT_PIVOT", { + id: this.props.pivotId, + col: selectedZone.left, + row: selectedZone.top, + sheetId: this.env.model.getters.getActiveSheetId(), + table: tableStructure, + }); + this.env.model.dispatch("REFRESH_PIVOT", {id: this.props.pivotId}); + } +} + +PivotPanelDisplay.template = "spreadsheet_oca.PivotPanelDisplay"; +PivotPanelDisplay.components = { + DomainSelector, +}; +PivotPanelDisplay.properties = { + pivotId: String, + pivotDefinition: Object, +}; + +export class PivotPanel extends Component { + get pivotId() { + return this.env.model.getters.getSelectedPivotId(); + } + get pivotDefinition() { + return this.env.model.getters.getPivotDefinition(this.pivotId); + } +} + +PivotPanel.template = "spreadsheet_oca.PivotPanel"; +PivotPanel.components = { + PivotPanelDisplay, +}; + +sidePanelRegistry.add("PivotPanel", { + title: "Pivot table information", + Body: PivotPanel, +}); + +export class ListPanelDisplay extends Component { + setup() { + this.dialog = useService("dialog"); + onWillStart(this.modelData.bind(this)); + onWillUpdateProps(this.modelData.bind(this)); + } + async modelData() { + this.ListDataSource = await this.env.model.getters.getAsyncListDataSource( + this.props.listId + ); + this.modelLabel = await this.ListDataSource.getModelLabel(); + } + get domain() { + return new Domain(this.props.listDefinition.domain).toString(); + } + get lastUpdate() { + const lastUpdate = this.ListDataSource.lastUpdate; + if (lastUpdate) { + return time_to_str(new Date(lastUpdate)); + } + return _t("not updated"); + } + editDomain() { + this.dialog.add(DomainSelectorDialog, { + resModel: this.props.listDefinition.model, + initialValue: this.domain, + readonly: false, + isDebugMode: Boolean(this.env.debug), + onSelected: this.onSelectDomain.bind(this), + }); + } + onSelectDomain(domain) { + this.env.model.dispatch("UPDATE_ODOO_LIST_DOMAIN", { + listId: this.props.listId, + domain: new Domain(domain).toList(), + }); + } +} + +ListPanelDisplay.template = "spreadsheet_oca.ListPanelDisplay"; +ListPanelDisplay.components = { + DomainSelector, +}; +ListPanelDisplay.properties = { + listId: String, + listDefinition: Object, +}; + +export class ListPanel extends Component { + get listId() { + return this.env.model.getters.getSelectedListId(); + } + get listDefinition() { + return this.env.model.getters.getListDefinition(this.listId); + } +} + +ListPanel.template = "spreadsheet_oca.ListPanel"; +ListPanel.components = { + ListPanelDisplay, +}; + +sidePanelRegistry.add("ListPanel", { + title: "List information", + Body: ListPanel, +}); diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml index bb6d1cfe..9bd67783 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml @@ -12,6 +12,110 @@ /> + + + + +
+
+
Pivot name
+ +
+
+
Model
+
()
+
+
+
Domain
+ + +
+
+
Dimensions
+ +
+ +
+
+
Sorting
+
+
+
+
Measures
+ +
+ +
+
+ Last updated at +
+
+ +
+
+ + + + + + + +
+
+
List name
+ +
+
+
Model
+
()
+
+
+
Domain
+ + +
+
+ Last updated at +
+
+ +
diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_action.esm.js b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_action.esm.js index 8b461110..71805fc1 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_action.esm.js +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_action.esm.js @@ -1,5 +1,6 @@ /** @odoo-module **/ +import ListDataSource from "@spreadsheet/list/list_data_source"; import PivotDataSource from "@spreadsheet/pivot/pivot_data_source"; import {SpreadsheetControlPanel} from "./spreadsheet_controlpanel.esm"; import {SpreadsheetRenderer} from "./spreadsheet_renderer.esm"; @@ -91,7 +92,7 @@ export class ActionSpreadsheetOca extends Component { definition, }); } - async importDataPivot(spreadsheet_model) { + importCreateOrReuseSheet(spreadsheet_model) { var sheetId = spreadsheet_model.getters.getActiveSheetId(); var row = 0; if (this.import_data.new === undefined && this.import_data.new_sheet) { @@ -128,6 +129,48 @@ export class ActionSpreadsheetOca extends Component { } row += 1; } + return {sheetId, row}; + } + async importDataList(spreadsheet_model) { + var {sheetId, row} = this.importCreateOrReuseSheet(spreadsheet_model); + const dataSourceId = uuidGenerator.uuidv4(); + var list_info = { + metaData: { + resModel: this.import_data.metaData.model, + columns: this.import_data.metaData.columns.map((column) => column.name), + fields: this.import_data.metaData.fields, + }, + searchParams: { + domain: this.import_data.metaData.domain, + context: this.import_data.metaData.context, + orderBy: this.import_data.metaData.orderBy, + }, + name: this.import_data.metaData.name, + }; + const dataSource = spreadsheet_model.config.dataSources.add( + dataSourceId, + ListDataSource, + list_info + ); + await dataSource.load(); + spreadsheet_model.dispatch("INSERT_ODOO_LIST", { + sheetId, + col: 0, + row: row, + id: spreadsheet_model.getters.getNextListId(), + dataSourceId, + definition: list_info, + linesNumber: this.import_data.metaData.threshold, + columns: this.import_data.metaData.columns, + }); + const columns = []; + for (let col = 0; col < this.import_data.metaData.columns.length; col++) { + columns.push(col); + } + spreadsheet_model.dispatch("AUTORESIZE_COLUMNS", {sheetId, cols: columns}); + } + async importDataPivot(spreadsheet_model) { + var {sheetId, row} = this.importCreateOrReuseSheet(spreadsheet_model); const dataSourceId = uuidGenerator.uuidv4(); const pivot_info = { metaData: { @@ -167,6 +210,9 @@ export class ActionSpreadsheetOca extends Component { if (this.import_data.mode === "graph") { await this.importDataGraph(spreadsheet_model); } + if (this.import_data.mode === "list") { + await this.importDataList(spreadsheet_model); + } } } ActionSpreadsheetOca.template = "spreadsheet_oca.ActionSpreadsheetOca"; diff --git a/spreadsheet_oca/static/src/spreadsheet/list_controller.esm.js b/spreadsheet_oca/static/src/spreadsheet/list_controller.esm.js new file mode 100644 index 00000000..0229b2dc --- /dev/null +++ b/spreadsheet_oca/static/src/spreadsheet/list_controller.esm.js @@ -0,0 +1,14 @@ +/** @odoo-module **/ +import {ListController} from "@web/views/list/list_controller"; + +import {patch} from "web.utils"; + +patch( + ListController.prototype, + "spreadsheet_oca/static/src/spreadsheet/list_controller.esm.js", + { + onSpreadsheetButtonClicked() { + this.env.bus.trigger("addListOnSpreadsheet"); + }, + } +); diff --git a/spreadsheet_oca/static/src/spreadsheet/list_renderer.esm.js b/spreadsheet_oca/static/src/spreadsheet/list_renderer.esm.js new file mode 100644 index 00000000..b123e8f7 --- /dev/null +++ b/spreadsheet_oca/static/src/spreadsheet/list_renderer.esm.js @@ -0,0 +1,58 @@ +/** @odoo-module **/ +import {useBus, useService} from "@web/core/utils/hooks"; +import {ListRenderer} from "@web/views/list/list_renderer"; +import {omit} from "@web/core/utils/objects"; +import {patch} from "web.utils"; + +patch( + ListRenderer.prototype, + "spreadsheet_oca/static/src/spreadsheet/list_renderer.esm.js", + { + setup() { + this._super(...arguments); + this.userService = useService("user"); + this.actionService = useService("action"); + useBus( + this.env.bus, + "addListOnSpreadsheet", + this.onAddListOnSpreadsheet.bind(this) + ); + }, + onAddListOnSpreadsheet() { + const model = this.env.model.root; + this.actionService.doAction( + "spreadsheet_oca.spreadsheet_spreadsheet_import_act_window", + { + additionalContext: { + default_name: this.env.config.getDisplayName(), + default_import_data: { + mode: "list", + metaData: { + model: model.resModel, + domain: model.domain, + orderBy: model.orderBy, + context: omit( + model.context, + ...Object.keys(this.userService.context) + ), + columns: this.getSpreadsheetColumns(), + fields: model.fields, + name: this.env.config.getDisplayName(), + threshold: Math.min(model.count, model.limit), + }, + }, + }, + } + ); + }, + getSpreadsheetColumns() { + const fields = this.env.model.root.fields; + return this.state.columns + .filter( + (col) => col.type === "field" && fields[col.name].type !== "binary" + // We want to avoid binary fields + ) + .map((col) => ({name: col.name, type: fields[col.name].type})); + }, + } +); diff --git a/spreadsheet_oca/static/src/spreadsheet/spreadsheet.scss b/spreadsheet_oca/static/src/spreadsheet/spreadsheet.scss index a36e2732..eecba723 100644 --- a/spreadsheet_oca/static/src/spreadsheet/spreadsheet.scss +++ b/spreadsheet_oca/static/src/spreadsheet/spreadsheet.scss @@ -20,6 +20,19 @@ .o_spreadsheet_oca_name_warning { font-size: 0.8em; } + + .o_spreadsheet_oca_datasource_panel { + padding: 16px; + .o_spreadsheet_oca_datasource_panel_field { + .o_spreadsheet_oca_datasource_panel_field_title { + font-weight: bold; + } + } + } + .o_spreadsheet_oca_pivot_panel_info { + font-style: italic; + color: $o-gray-600; + } .o_spreadsheet_oca_filter { padding: 16px; .spreadsheet_oca_filter_label { diff --git a/spreadsheet_oca/static/src/spreadsheet/spreadsheet.xml b/spreadsheet_oca/static/src/spreadsheet/spreadsheet.xml index 92f977c6..04f3d337 100644 --- a/spreadsheet_oca/static/src/spreadsheet/spreadsheet.xml +++ b/spreadsheet_oca/static/src/spreadsheet/spreadsheet.xml @@ -14,6 +14,21 @@
+ + + +