From 7549428f9183ec4fab925754a99328a807ec23f7 Mon Sep 17 00:00:00 2001 From: Enric Tobella Date: Wed, 28 Dec 2022 13:34:05 +0100 Subject: [PATCH 001/150] [ADD] spreadsheet_oca --- spreadsheet_oca/README.rst | 9 ++ spreadsheet_oca/__init__.py | 1 + spreadsheet_oca/__manifest__.py | 32 +++++ spreadsheet_oca/models/__init__.py | 3 + .../models/spreadsheet_abstract.py | 76 ++++++++++++ .../models/spreadsheet_oca_revision.py | 18 +++ .../models/spreadsheet_spreadsheet.py | 57 +++++++++ spreadsheet_oca/readme/CONTRIBUTORS.rst | 1 + spreadsheet_oca/readme/DESCRIPTION.rst | 1 + spreadsheet_oca/security/ir.model.access.csv | 3 + spreadsheet_oca/security/security.xml | 52 ++++++++ spreadsheet_oca/static/description/icon.png | Bin 0 -> 4207 bytes spreadsheet_oca/static/description/icon.svg | 1 + .../src/spreadsheet/bundle/spreadsheet.xml | 110 +++++++++++++++++ .../bundle/spreadsheet_action.esm.js | 98 +++++++++++++++ .../bundle/spreadsheet_controlpanel.esm.js | 41 +++++++ .../bundle/spreadsheet_renderer.esm.js | 113 ++++++++++++++++++ .../src/spreadsheet/pivot_controller.esm.js | 27 +++++ .../static/src/spreadsheet/spreadsheet.scss | 23 ++++ .../static/src/spreadsheet/spreadsheet.xml | 17 +++ .../src/spreadsheet/spreadsheet_action.esm.js | 21 ++++ .../views/spreadsheet_spreadsheet.xml | 78 ++++++++++++ 22 files changed, 782 insertions(+) create mode 100644 spreadsheet_oca/README.rst create mode 100644 spreadsheet_oca/__init__.py create mode 100644 spreadsheet_oca/__manifest__.py create mode 100644 spreadsheet_oca/models/__init__.py create mode 100644 spreadsheet_oca/models/spreadsheet_abstract.py create mode 100644 spreadsheet_oca/models/spreadsheet_oca_revision.py create mode 100644 spreadsheet_oca/models/spreadsheet_spreadsheet.py create mode 100644 spreadsheet_oca/readme/CONTRIBUTORS.rst create mode 100644 spreadsheet_oca/readme/DESCRIPTION.rst create mode 100644 spreadsheet_oca/security/ir.model.access.csv create mode 100644 spreadsheet_oca/security/security.xml create mode 100644 spreadsheet_oca/static/description/icon.png create mode 100644 spreadsheet_oca/static/description/icon.svg create mode 100644 spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml create mode 100644 spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_action.esm.js create mode 100644 spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_controlpanel.esm.js create mode 100644 spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_renderer.esm.js create mode 100644 spreadsheet_oca/static/src/spreadsheet/pivot_controller.esm.js create mode 100644 spreadsheet_oca/static/src/spreadsheet/spreadsheet.scss create mode 100644 spreadsheet_oca/static/src/spreadsheet/spreadsheet.xml create mode 100644 spreadsheet_oca/static/src/spreadsheet/spreadsheet_action.esm.js create mode 100644 spreadsheet_oca/views/spreadsheet_spreadsheet.xml diff --git a/spreadsheet_oca/README.rst b/spreadsheet_oca/README.rst new file mode 100644 index 00000000..854bfcc0 --- /dev/null +++ b/spreadsheet_oca/README.rst @@ -0,0 +1,9 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: https://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +=============== +Spreadsheet Oca +=============== + +Allow to edit spreadsheets diff --git a/spreadsheet_oca/__init__.py b/spreadsheet_oca/__init__.py new file mode 100644 index 00000000..0650744f --- /dev/null +++ b/spreadsheet_oca/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/spreadsheet_oca/__manifest__.py b/spreadsheet_oca/__manifest__.py new file mode 100644 index 00000000..41b10bb1 --- /dev/null +++ b/spreadsheet_oca/__manifest__.py @@ -0,0 +1,32 @@ +# Copyright 2022 CreuBlanca +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +{ + "name": "Spreadsheet Oca", + "summary": """ + Allow to edit spreadsheets""", + "version": "16.0.1.0.0", + "license": "AGPL-3", + "author": "CreuBlanca,Odoo Community Association (OCA)", + "website": "https://github.com/OCA/spreadsheet", + "depends": ["spreadsheet", "base_sparse_field", "bus"], + "data": [ + "security/security.xml", + "security/ir.model.access.csv", + "views/spreadsheet_spreadsheet.xml", + ], + "assets": { + "web.assets_backend": [ + "spreadsheet_oca/static/src/spreadsheet/spreadsheet.scss", + "spreadsheet_oca/static/src/spreadsheet/spreadsheet.xml", + "spreadsheet_oca/static/src/spreadsheet/spreadsheet_action.esm.js", + "spreadsheet_oca/static/src/spreadsheet/pivot_controller.esm.js", + ], + "spreadsheet.o_spreadsheet": [ + "spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml", + "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/models/__init__.py b/spreadsheet_oca/models/__init__.py new file mode 100644 index 00000000..ae532164 --- /dev/null +++ b/spreadsheet_oca/models/__init__.py @@ -0,0 +1,3 @@ +from . import spreadsheet_abstract +from . import spreadsheet_spreadsheet +from . import spreadsheet_oca_revision diff --git a/spreadsheet_oca/models/spreadsheet_abstract.py b/spreadsheet_oca/models/spreadsheet_abstract.py new file mode 100644 index 00000000..9f497c88 --- /dev/null +++ b/spreadsheet_oca/models/spreadsheet_abstract.py @@ -0,0 +1,76 @@ +# Copyright 2022 CreuBlanca +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import json + +from odoo import fields, models +from odoo.exceptions import AccessError + + +class SpreadsheetAbstract(models.AbstractModel): + _name = "spreadsheet.abstract" + _description = "Spreadsheet abstract for inheritance" + + name = fields.Char(required=True) + spreadsheet_raw = fields.Serialized() + spreadsheet_revision_ids = fields.One2many( + "spreadsheet.oca.revision", + inverse_name="res_id", + domain=lambda r: [("model", "=", r._name)], + ) + + def get_spreadsheet_data(self): + self.ensure_one() + mode = "normal" + try: + self.check_access_rights("write") + self.check_access_rule("write") + except AccessError: + mode = "readonly" + return { + "name": self.name, + "spreadsheet_raw": self.spreadsheet_raw, + "revisions": [ + { + "type": revision.type, + "clientId": revision.client_id, + "nextRevisionId": revision.next_revision_id, + "serverRevisionId": revision.server_revision_id, + "commands": json.loads(revision.commands), + } + for revision in self.spreadsheet_revision_ids + ], + "mode": mode, + } + + def open_spreadsheet(self): + self.ensure_one() + return { + "type": "ir.actions.client", + "tag": "action_spreadsheet_oca", + "params": {"spreadsheet_id": self.id, "model": self._name}, + } + + def send_spreadsheet_message(self, message): + self.ensure_one() + channel = "spreadsheet_oca;%s;%s" % (self._name, self.id) + message.update({"res_model": self._name, "res_id": self.id}) + if message["type"] in ["REVISION_UNDONE", "REMOTE_REVISION", "REVISION_REDONE"]: + self.env["spreadsheet.oca.revision"].create( + { + "model": self._name, + "res_id": self.id, + "type": message["type"], + "client_id": message["clientId"], + "next_revision_id": message["nextRevisionId"], + "server_revision_id": message["serverRevisionId"], + "commands": json.dumps(message["commands"]), + } + ) + self.env["bus.bus"]._sendone(channel, "spreadsheet_oca", message) + return True + + def write(self, vals): + if "spreadsheet_raw" in vals: + self.spreadsheet_revision_ids.unlink() + return super().write(vals) diff --git a/spreadsheet_oca/models/spreadsheet_oca_revision.py b/spreadsheet_oca/models/spreadsheet_oca_revision.py new file mode 100644 index 00000000..66197d9b --- /dev/null +++ b/spreadsheet_oca/models/spreadsheet_oca_revision.py @@ -0,0 +1,18 @@ +# Copyright 2022 CreuBlanca +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class SpreadsheetOcaRevision(models.Model): + + _name = "spreadsheet.oca.revision" + _description = "Spreadsheet Oca Revision" # TODO + + model = fields.Char(required=True) + res_id = fields.Integer(required=True, index=True) + type = fields.Char() + client_id = fields.Char() + server_revision_id = fields.Char() + next_revision_id = fields.Char() + commands = fields.Char() diff --git a/spreadsheet_oca/models/spreadsheet_spreadsheet.py b/spreadsheet_oca/models/spreadsheet_spreadsheet.py new file mode 100644 index 00000000..88b57380 --- /dev/null +++ b/spreadsheet_oca/models/spreadsheet_spreadsheet.py @@ -0,0 +1,57 @@ +# Copyright 2022 CreuBlanca +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import base64 +import json + +from odoo import _, api, fields, models + + +class SpreadsheetSpreadsheet(models.Model): + _name = "spreadsheet.spreadsheet" + _inherit = "spreadsheet.abstract" + _description = "Spreadsheet" + + data = fields.Binary() + filename = fields.Char(compute="_compute_filename") + spreadsheet_raw = fields.Serialized( + compute="_compute_spreadsheet_raw", inverse="_inverse_spreadsheet_raw" + ) + owner_id = fields.Many2one( + "res.users", required=True, default=lambda r: r.env.user.id + ) + contributor_ids = fields.Many2many( + "res.users", + relation="spreadsheet_contributor", + column1="spreadsheet_id", + column2="user_id", + string="Contributors", + ) + reader_ids = fields.Many2many( + "res.users", + relation="spreadsheet_reader", + column1="spreadsheet_id", + column2="user_id", + string="Readers", + ) + + @api.depends("name") + def _compute_filename(self): + for record in self: + record.filename = "%s.json" % (self.name or _("Unnamed")) + + @api.depends("data") + def _compute_spreadsheet_raw(self): + for dashboard in self: + if dashboard.data: + dashboard.spreadsheet_raw = json.loads( + base64.decodebytes(dashboard.data).decode("UTF-8") + ) + else: + dashboard.spreadsheet_raw = {} + + def _inverse_spreadsheet_raw(self): + for record in self: + record.data = base64.encodebytes( + json.dumps(record.spreadsheet_raw).encode("UTF-8") + ) diff --git a/spreadsheet_oca/readme/CONTRIBUTORS.rst b/spreadsheet_oca/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..85004765 --- /dev/null +++ b/spreadsheet_oca/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Enric Tobella diff --git a/spreadsheet_oca/readme/DESCRIPTION.rst b/spreadsheet_oca/readme/DESCRIPTION.rst new file mode 100644 index 00000000..7fbd3af7 --- /dev/null +++ b/spreadsheet_oca/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +This module adds a functionality for adding and editing Spreadsheets using Odoo CE. diff --git a/spreadsheet_oca/security/ir.model.access.csv b/spreadsheet_oca/security/ir.model.access.csv new file mode 100644 index 00000000..40c44338 --- /dev/null +++ b/spreadsheet_oca/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_spreadsheet_spreadsheet,access_spreadsheet_spreadsheet,model_spreadsheet_spreadsheet,group_user,1,1,1,1 +access_spreadsheet_oca_revision,access_spreadsheet_oca_revision,model_spreadsheet_oca_revision,base.group_user,1,1,1,1 diff --git a/spreadsheet_oca/security/security.xml b/spreadsheet_oca/security/security.xml new file mode 100644 index 00000000..ef8d22a4 --- /dev/null +++ b/spreadsheet_oca/security/security.xml @@ -0,0 +1,52 @@ + + + + + + Spreadsheet + 99 + + + User + + + + Manager + + + + + + Spreadsheet Owner + + + [('owner_id','=', user.id)] + + + Spreadsheet Contributor + + + [('contributor_ids','=', user.id)] + + + + + Spreadsheet Reader + + + [('reader_ids','=', user.id)] + + + + + + Spreadsheet Manager + + + [(1,'=', 1)] + + diff --git a/spreadsheet_oca/static/description/icon.png b/spreadsheet_oca/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..34b37abace243eb7b653213f3035ed5669f9e592 GIT binary patch literal 4207 zcmV-#5RmVQP)7lIG~0Yfsui*3zDz}QD#Jz0HPrQB2kne z3P#Kjei#B#5+Vxza6zIZDnURQK*A;?Fbg>IX6C)=e*Io|*XECYy;Rq^-KV>%dU;dl z&&>PkoO5sW{p!}KbI+}kP$&29y8ZH~owzO{M~+owC2iZbRDT?j^G^V9BY;Hcd9v+B z8%*0l{0(GDvQ68{8n*tXDHpbF9lHGqNBehwyhYQDh0i1uZ4&_4wuO>if??~hV6|+N z{X>zq)Jah8g104qclLRBvGxBqkRU;oRH%qRkyM9P3B^BUCw5+N>#_%@RRBmDi+*dv zK4Zu9yBO2|hrBu2ywMOFI4mJ)$S8A=k>_Bpe8d+s!z-Q!uvxly*X@^&m&Skb-qhQG zWV;;o(@BJwKUhqPVYRv*Dx#Mu$9!Yek9SPJw+66x1KE%QlbT81=ftAr=S;=F0$Nu2HW$9Ads>vfQpw7dsbX zUBKj43Lyz*Hzv1I5MkZuT!;mavG4yO31%lIdvz3K=Rz!atnDB$B*E;$Itl_q63iZ~ z;~+33!R)|11%V+61|Rbr1coFSY|NJ+FeJg?V!j1|AqfT(^EC(zNicYr??GTlg2BQ9 z2?9eB3=S4p5EznRFt9*_z>oy}j|CnCh9u~3tSdoaNP_;w)XE=?<-?s|aa%*mP6MEK z*|bw~E^y$6$Q?esT|ey7YGxbl^v{q=$f^?n{QLbo!BoE-895W*S$PjmT(Z%1!lAfD zH%qwl!<+C)WEX(mk-(QAP|q1SussV+O}cbLp(Ub3q1q7#HjqJ}o-=T8dk&Uvfxgp+ zA`+YU#Re(}EM@iT-~#}xkOJ+9ZQ6qy1XiwmTequQnJvTFkYO5lXZPE##_NR+3*9m| zJr9fKEOz1V?tTY`sdwhIPL_|XL@JSTUs}*b@HeMgRa0U;lG_ z{@BwThjxwa#3lEh*TlAYpX=MW@tj+*b@TO<7XSkk1nM~h2Vc$B2EVH4GG$9Uc>;k4 z!j_{YP-L}0gJhq%ZS;E(sOJofZ_mNf>3dg5f{Ht4pP9BnVk&p4C}j;yyjC9kW_pd1 zSQG;wc>M}D!2&>uySNGfh>t{}Y4JLHsu?Ol)9|-U-!qa+;%=B!ZJrpCqx5<8^8PPL z5-$kUb0)@LD@Xf{C)$bx(K*#Hqm7K38xsA1k(7#9)cHK!(ff4--H6Un+1bHn?aC@m zV4$}oXt^9*KllLu*_9Hut=FeX?$pX`h;8fPcF?QG!KOi=o-=W9d$#tRU(xF`5_lPm z!<_`eAh2@f+qzAk6nBJ}wzHiCWRS{jquZIlQr4&qev{tLCKAy8c(!~JF#UBAbJYau zIkOz?mn@gj;Yb2cv&aCDz!3yi2Y=?u=MF|MNzjdZ^K2$Tl>kQ0tR{I{8TE_=B9|cv zyaj*#YasZTmEeyXX}PrdLY zzr?V~viM1BH)6wTI*{#U5UAGuIV+2SzVqJa$;AVeho>0hkySN_D$?< zYTF72fFl==x(Kj8JBArPB0`7^D;A|%WrAL`|mjo&`rF*#@G|JH01ORWFH~|TfA%*t8ERMoR zvZ*bJHco{KUDKCYr?s>#x6=F`A*yNYX%eVf6Br-~lt|r)drUM@B1dG%X6;}*whrAU z{{8rlAz5@fBfTU+Z8?~pk2p|IR{~kK0(XP75*Rb$Sf?L{Wu?_^+cv|TnZN-g0rx}) znFLnJLi9gFxO?hBVfC5QgKF7i8OwA-b3WEig2^HOH(UI`#Uy&4@I-sK^so=s0GKkR2B{|#`n*9pbej5 za8+iFuzzOJ#QuLBa$NbM71l=Si{)Su31X2LHjSRu@+7>-scXIvnD0~~h0~8bGcf-E zAn6hcZ&-+zG{j@><3*zY-O}9$>ck5P)CLW2BnYJ~u?!0%A1%X^mpv-fx$D{6@cfSF z9GIVL0=_N>hti(p{yPP8n%INcAVDq%%U#L;heC>N(mSz8UG~#P2SH#^%fW~8hj7ET zE!DrtVksuLX~Sk*aoW``{N_il!}#QQQ`=TG3jlD-h2O#nt2Q{WJqLE<&d0w^>T?3L zO6ve{;b$(!xhG%XI@lja`hQsbz%8J9xuH0w~lHI8#M9hbtjWY?3M)J-6H&XPhb~r2kbO2JIBDY z^}WWREua40D*cQJRF{H>nSc^GBC%f6CXy;+MHM@$3@IwN@6QPes(t_^DmwwnC*4p> zB{nSt_kTa01XM52`WzArttr<8($lhAUsNKd3TgtA%VS6t+N(Px2b-)31ky()T^J>; zO2pIz-G6?jlfIgLdQV`mBxuK0wC{i;e(Y#==}{PUHP^Ls{GtuG^ix*?b*ig|*D@Mx zU;GOK`e+1X(YKnwkOX}3T2$h<_*N6x?Lb5O)FIhzwu#t%mre1ATuc!W(68lSS0>Q! zN40J|UUDDRypTYaWGu-n6CO{;(zSNk;^9fhClYjJIk+2-mtJZN{eE<7#~uGx#Ix0xgdHDOy+sR|?N>quG_|sdSg=M#1F>{Nv&sxO24Se;&wfOUOFPF7jOcGab+=9za zx*7nW%8};EK1yFN2g{O-bUed8nNDOVmTIlkp(wI~bUe+{jj3_ORQLCVx*-F#92`n} zLjKl)W&-_A{e~hGe)^oU^(hm`&82fluPoB9S+kW>`0KhpL&&LDT{!FO* z<$KS^>mO`KYPE*B$s<^O6N%R#u=!`>y6~t*ntIC8dGXh|Z7;>kOwjtGn~ATq2XGR&{_^HL}1wZ@Q8Y-*c`(TS*vC@~q@vT`0kRKHk} z=$r^Nc+VO5L4uj(VA{D0^E*$_BU>+NW8cN&?ZxZuKsxHXOKV5S3yn9W_v&i)b0&}_ z8P9y}IhS7L*rN0j@)!Udnx4R9DF*;Zs2b)b=LvQ9<@Q3iN&tYU62r*Qe4*~a{C-Ru z1w`ctMp6s#)}A+USN4XM=fcGrmV@Ej?LYwal`VB{%~t%HNhUFjOk$X&J3qC6?YHqU z(X>oomxG-%Koxa&y%pCA?Fb*#gz@}g8&bGjF# zl`mR5@Ph=hP$48i&q$CS)yQd|60aoaU^$rCi9Q<0K_WqBeG=NzIC~I9c)-iS%ue*t zKn?;4Dudt5)^8FJyMsd#^nwJL(IgT?z!x+LWN!oM#Wo~CMJ+R0yYjtE=bhO5upI2g zHY7oMo#vSKMS4Mzpl)|C(@{tQ-$;-i)!GX9f*?V|w}Y9ELK1i;L1plp`RfJ=h?hk( z9fc(5m;`B$0iT~F5bO?SItoe9HVM+BNoY%BLfdYWK(6mxLyvimLK3h^kQ&vhgWt@y zn*(kOVDBkRB~B>~v$m+aUpCcQDUUNCGhlDudrluU8~+C4xc{c(ZLhx74VH z#A5fJf_@-@Z2_`A;rXdD`z?z}FJ9fdj8*VHK@^%W_J7r0|K3 z(pX3WKWx+MwY~`WM3`ByuO z=Zvg*%G9I@NPrxbAt^xpNO8Axv)}daWiUv0Ts;!)*dHW7TM|R={b}UhpBCDRHkEMN zZTlj!%O9MwbQ^ZVm&eqE@<%JBUF>qP{Od<-HL~Vbt6;CQ4O^pn!u(0tweS2dZ}o2* z@rAW*VeQ(M)dJpQAi5v>#-caMxwtw9C_LG%E{|9#BaE<9Z=wtu@002ovPDHLk FV1lYu>i7Tv literal 0 HcmV?d00001 diff --git a/spreadsheet_oca/static/description/icon.svg b/spreadsheet_oca/static/description/icon.svg new file mode 100644 index 00000000..ba496774 --- /dev/null +++ b/spreadsheet_oca/static/description/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml new file mode 100644 index 00000000..a71c9910 --- /dev/null +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml @@ -0,0 +1,110 @@ + + + + +
+ + +
+
+ + + + + + + + name is required + + + + + + + + + + + + +
+
+
+ + + + + + + + + + +
+
+
+
+
diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_action.esm.js b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_action.esm.js new file mode 100644 index 00000000..663b4631 --- /dev/null +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_action.esm.js @@ -0,0 +1,98 @@ +/** @odoo-module **/ + +import PivotDataSource from "@spreadsheet/pivot/pivot_data_source"; +import {SpreadsheetControlPanel} from "./spreadsheet_controlpanel.esm"; +import {SpreadsheetRenderer} from "./spreadsheet_renderer.esm"; +import {registry} from "@web/core/registry"; +import spreadsheet from "@spreadsheet/o_spreadsheet/o_spreadsheet_extended"; +import {useService} from "@web/core/utils/hooks"; + +const uuidGenerator = new spreadsheet.helpers.UuidGenerator(); +const actionRegistry = registry.category("actions"); +const {Component, onMounted, onWillStart, useSubEnv} = owl; + +export class ActionSpreadsheetOca extends Component { + setup() { + this.router = useService("router"); + this.orm = useService("orm"); + const params = this.props.action.params || this.props.action.context.params; + this.spreadsheetId = params.spreadsheet_id; + this.model = params.model || "spreadsheet.spreadsheet"; + this.import_data = params.import_data || {}; + onMounted(() => { + this.router.pushState({ + spreadsheet_id: this.spreadsheetId, + model: this.model, + }); + }); + onWillStart(async () => { + this.record = + (await this.orm.call( + this.model, + "get_spreadsheet_data", + [[this.spreadsheetId]], + {context: {bin_size: false}} + )) || {}; + }); + useSubEnv({ + saveRecord: this.saveRecord.bind(this), + importData: this.importData.bind(this), + }); + } + async saveRecord(data) { + if (this.record.mode === "readonly") { + return; + } + if (this.spreadsheetId) { + this.orm.call(this.model, "write", [this.spreadsheetId, data]); + } else { + this.spreadsheetId = await this.orm.call(this.model, "create", [data]); + this.router.pushState({spreadsheet_id: this.spreadsheetId}); + } + } + async importDataPivot(spreadsheet_model) { + const sheetId = spreadsheet_model.getters.getActiveSheetId(); + const dataSourceId = uuidGenerator.uuidv4(); + const pivot_info = { + metaData: { + colGroupBys: this.import_data.metaData.colGroupBys, + rowGroupBys: this.import_data.metaData.rowGroupBys, + activeMeasures: this.import_data.metaData.activeMeasures, + resModel: this.import_data.metaData.resModel, + }, + searchParams: this.import_data.searchParams, + }; + const dataSource = spreadsheet_model.config.dataSources.add( + dataSourceId, + PivotDataSource, + pivot_info + ); + await dataSource.load(); + const {cols, rows, measures} = dataSource.getTableStructure().export(); + const table = { + cols, + rows, + measures, + }; + spreadsheet_model.dispatch("INSERT_PIVOT", { + sheetId, + col: 0, + row: 0, + id: spreadsheet_model.getters.getNextPivotId(), + table, + dataSourceId, + definition: pivot_info, + }); + } + async importData(spreadsheet_model) { + if (this.import_data.mode === "pivot") { + await this.importDataPivot(spreadsheet_model); + } + } +} +ActionSpreadsheetOca.template = "spreadsheet_oca.ActionSpreadsheetOca"; +ActionSpreadsheetOca.components = { + SpreadsheetRenderer, + SpreadsheetControlPanel, +}; +actionRegistry.add("action_spreadsheet_oca", ActionSpreadsheetOca, {force: true}); diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_controlpanel.esm.js b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_controlpanel.esm.js new file mode 100644 index 00000000..52d23556 --- /dev/null +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_controlpanel.esm.js @@ -0,0 +1,41 @@ +/** @odoo-module **/ + +import {_lt, _t} from "web.core"; +import {Component} from "@odoo/owl"; +import {ControlPanel} from "@web/search/control_panel/control_panel"; +import spreadsheet from "@spreadsheet/o_spreadsheet/o_spreadsheet_extended"; + +const {useState} = owl; +const {topbarMenuRegistry} = spreadsheet.registries; + +topbarMenuRegistry.add("file", {name: _t("File"), sequence: 10}); +topbarMenuRegistry.addChild("save", ["file"], { + name: _lt("Save"), + // Description: "Ctrl+S", // This is not working, so removing it from the view for now... + sequence: 10, + action: (env) => env.saveSpreadsheet(), +}); +export class SpreadsheetName extends Component { + setup() { + this.state = useState({ + name: this.props.name, + }); + } + _onNameChanged(ev) { + if (ev.target.value) { + this.env.saveRecord({name: ev.target.value}); + } + this.state.name = ev.target.value; + } +} +SpreadsheetName.template = "spreadsheet_oca.SpreadsheetName"; + +export class SpreadsheetControlPanel extends ControlPanel {} +SpreadsheetControlPanel.template = "spreadsheet_oca.SpreadsheetControlPanel"; +SpreadsheetControlPanel.props = { + ...ControlPanel.props, + record: Object, +}; +SpreadsheetControlPanel.components = { + SpreadsheetName, +}; diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_renderer.esm.js b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_renderer.esm.js new file mode 100644 index 00000000..6232ba2b --- /dev/null +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_renderer.esm.js @@ -0,0 +1,113 @@ +/** @odoo-module **/ + +import {Component} from "@odoo/owl"; +import {DataSources} from "@spreadsheet/data_sources/data_sources"; +import {Field} from "@web/views/fields/field"; +import {loadSpreadsheetDependencies} from "@spreadsheet/helpers/helpers"; +import {migrate} from "@spreadsheet/o_spreadsheet/migration"; +import spreadsheet from "@spreadsheet/o_spreadsheet/o_spreadsheet_extended"; +import {useService} from "@web/core/utils/hooks"; +import {useSetupAction} from "@web/webclient/actions/action_hook"; + +const {Spreadsheet, Model} = spreadsheet; +const {useSubEnv, onWillStart} = owl; +const uuidGenerator = new spreadsheet.helpers.UuidGenerator(); + +class SpreadsheetTransportService { + constructor(orm, bus_service, model, res_id) { + this.orm = orm; + this.bus_service = bus_service; + this.model = model; + this.res_id = res_id; + this.channel = "spreadsheet_oca;" + this.model + ";" + this.res_id; + this.bus_service.addChannel(this.channel); + this.bus_service.addEventListener( + "notification", + this.onNotification.bind(this) + ); + this.listeners = []; + } + onNotification({detail: notifications}) { + for (const {payload, type} of notifications) { + if ( + type === "spreadsheet_oca" && + payload.res_model === this.model && + payload.res_id === this.res_id + ) { + // What shall we do if no callback is defined (empty until onNewMessage...) :/ + for (const {callback} of this.listeners) { + callback(payload); + } + } + } + } + sendMessage(message) { + this.orm.call(this.model, "send_spreadsheet_message", [[this.res_id], message]); + } + onNewMessage(id, callback) { + this.listeners.push({id, callback}); + } + leave(id) { + this.listeners = this.listeners.filter((listener) => listener.id !== id); + } +} + +export class SpreadsheetRenderer extends Component { + setup() { + this.orm = useService("orm"); + this.bus_service = useService("bus_service"); + this.user = useService("user"); + const dataSources = new DataSources(this.orm); + this.spreadsheet_model = new Model( + migrate(this.props.record.spreadsheet_raw), + { + evalContext: {env: this.env, orm: this.orm}, + transportService: new SpreadsheetTransportService( + this.orm, + this.bus_service, + this.props.model, + this.props.res_id + ), + client: { + id: uuidGenerator.uuidv4(), + name: this.user.name, + }, + mode: this.props.record.mode, + dataSources, + }, + this.props.record.revisions + ); + useSubEnv({ + saveSpreadsheet: this.onSpreadsheetSaved.bind(this), + }); + onWillStart(async () => { + await loadSpreadsheetDependencies(); + await dataSources.waitForAllLoaded(); + await this.env.importData(this.spreadsheet_model); + }); + useSetupAction({ + beforeLeave: () => this.onSpreadsheetSaved(), + }); + dataSources.addEventListener("data-source-updated", () => { + const sheetId = this.spreadsheet_model.getters.getActiveSheetId(); + this.spreadsheet_model.dispatch("EVALUATE_CELLS", {sheetId}); + }); + } + onSpreadsheetSaved() { + const data = this.spreadsheet_model.exportData(); + this.env.saveRecord({spreadsheet_raw: data}); + this.spreadsheet_model.leaveSession(); + } +} + +SpreadsheetRenderer.template = "spreadsheet_oca.SpreadsheetRenderer"; +SpreadsheetRenderer.components = { + Spreadsheet, + Field, +}; +SpreadsheetRenderer.props = { + record: Object, + res_id: {type: Number, optional: true}, + model: String, + importData: {type: Function, optional: true}, +}; diff --git a/spreadsheet_oca/static/src/spreadsheet/pivot_controller.esm.js b/spreadsheet_oca/static/src/spreadsheet/pivot_controller.esm.js new file mode 100644 index 00000000..66b0310f --- /dev/null +++ b/spreadsheet_oca/static/src/spreadsheet/pivot_controller.esm.js @@ -0,0 +1,27 @@ +/** @odoo-module **/ +import {PivotController} from "@web/views/pivot/pivot_controller"; + +import {patch} from "web.utils"; + +patch( + PivotController.prototype, + "spreadsheet_oca/static/src/spreadsheet/pivot_controller.esm.js", + { + onSpreadsheetButtonClicked() { + this.actionService.doAction({ + type: "ir.actions.client", + tag: "action_spreadsheet_oca", + params: { + model: "spreadsheet.spreadsheet", + import_data: { + mode: "pivot", + metaData: JSON.parse(JSON.stringify(this.model.metaData)), + searchParams: JSON.parse( + JSON.stringify(this.model.searchParams) + ), + }, + }, + }); + }, + } +); diff --git a/spreadsheet_oca/static/src/spreadsheet/spreadsheet.scss b/spreadsheet_oca/static/src/spreadsheet/spreadsheet.scss new file mode 100644 index 00000000..33e55449 --- /dev/null +++ b/spreadsheet_oca/static/src/spreadsheet/spreadsheet.scss @@ -0,0 +1,23 @@ +.o_spreadsheet_oca_container { + position: relative; + display: -webkit-box; + display: -webkit-flex; + display: flex; + flex-flow: column; + height: 100%; + .o_spreadsheet_oca_name { + border: none; + padding: 0px 5px 0px 2px; + color: $breadcrumb-active-color; + border-color: var(--o-input-border-color); + &:focus { + border: $input-border-width solid var(--o-input-border-color); + border-width: 0 0 $input-border-width 0; + @include print-variable(o-input-border-color, $o-brand-primary); + @include print-variable(o-caret-color, $o-brand-primary); + } + } + .o_spreadsheet_oca_name_warning { + font-size: 0.8em; + } +} diff --git a/spreadsheet_oca/static/src/spreadsheet/spreadsheet.xml b/spreadsheet_oca/static/src/spreadsheet/spreadsheet.xml new file mode 100644 index 00000000..fee6b44c --- /dev/null +++ b/spreadsheet_oca/static/src/spreadsheet/spreadsheet.xml @@ -0,0 +1,17 @@ + + + + + + + + + { await loadSpreadsheetDependencies(); @@ -93,17 +101,32 @@ export class SpreadsheetRenderer extends Component { this.spreadsheet_model.dispatch("EVALUATE_CELLS", {sheetId}); }); } + closeDialog() { + this.state.dialogDisplayed = false; + this.state.dialogTitle = "Spreadsheet"; + this.state.dialogContent = undefined; + } onSpreadsheetSaved() { const data = this.spreadsheet_model.exportData(); this.env.saveRecord({spreadsheet_raw: data}); this.spreadsheet_model.leaveSession(); } + editText(title, callback, options) { + this.state.dialogContent = options.placeholder; + this.state.dialogTitle = title; + this.state.dialogDisplayed = true; + this.confirmDialog = () => { + callback(this.state.dialogContent); + this.closeDialog(); + }; + } } SpreadsheetRenderer.template = "spreadsheet_oca.SpreadsheetRenderer"; SpreadsheetRenderer.components = { Spreadsheet, Field, + Dialog, }; SpreadsheetRenderer.props = { record: Object, From 53178fa43f0e1d300997100817ed32c1f489d1db Mon Sep 17 00:00:00 2001 From: Enric Tobella Date: Fri, 7 Apr 2023 15:25:10 +0200 Subject: [PATCH 014/150] [IMP] spreadsheet_oca: Allow to edit filters --- spreadsheet_oca/__manifest__.py | 1 + .../src/spreadsheet/bundle/filter.esm.js | 175 ++++++++++++++++++ .../src/spreadsheet/bundle/spreadsheet.xml | 157 ++++++++++++++++ .../bundle/spreadsheet_controlpanel.esm.js | 10 - .../static/src/spreadsheet/spreadsheet.scss | 24 +++ 5 files changed, 357 insertions(+), 10 deletions(-) create mode 100644 spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js diff --git a/spreadsheet_oca/__manifest__.py b/spreadsheet_oca/__manifest__.py index faec7004..4793ca98 100644 --- a/spreadsheet_oca/__manifest__.py +++ b/spreadsheet_oca/__manifest__.py @@ -27,6 +27,7 @@ ], "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/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 new file mode 100644 index 00000000..f7bb6e3d --- /dev/null +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js @@ -0,0 +1,175 @@ +/** @odoo-module **/ + +import {Component, onWillStart, useState} from "@odoo/owl"; +import {_lt, _t} from "web.core"; + +import {FilterValue} from "@spreadsheet/global_filters/components/filter_value/filter_value"; +import {ModelFieldSelector} from "@web/core/model_field_selector/model_field_selector"; +import {ModelSelector} from "@web/core/model_selector/model_selector"; +import {RELATIVE_DATE_RANGE_TYPES} from "@spreadsheet/helpers/constants"; +import {globalFiltersFieldMatchers} from "@spreadsheet/global_filters/plugins/global_filters_core_plugin"; +import spreadsheet from "@spreadsheet/o_spreadsheet/o_spreadsheet_extended"; +import {useService} from "@web/core/utils/hooks"; + +const {topbarMenuRegistry} = spreadsheet.registries; +const uuidGenerator = new spreadsheet.helpers.UuidGenerator(); + +topbarMenuRegistry.add("file", {name: _t("File"), sequence: 10}); +topbarMenuRegistry.addChild("filters", ["file"], { + name: _t("Filters"), + sequence: 70, + action: (env) => env.openSidePanel("FilterPanel", {}), +}); +topbarMenuRegistry.addChild("save", ["file"], { + name: _t("Save"), + // Description: "Ctrl+S", // This is not working, so removing it from the view for now... + sequence: 10, + action: (env) => env.saveSpreadsheet(), +}); + +const {sidePanelRegistry} = spreadsheet.registries; + +export class FilterPanel extends Component { + onEditFilter(filter) { + this.env.openSidePanel("EditFilterPanel", {filter}); + } + onAddFilter(type) { + this.env.openSidePanel("EditFilterPanel", {filter: {type: type}}); + } +} + +FilterPanel.template = "spreadsheet_oca.FilterPanel"; +FilterPanel.components = { + FilterValue, +}; + +sidePanelRegistry.add("FilterPanel", { + title: "Filters", + Body: FilterPanel, +}); + +export class EditFilterPanel extends Component { + setup() { + this.filterId = this.props.filter; + this.orm = useService("orm"); + this.state = useState({ + label: this.props.filter.label, + type: this.props.filter.type, + defaultValue: this.props.filter.defaultValue, + defaultsToCurrentPeriod: this.props.filter.defaultsToCurrentPeriod, + rangeType: this.props.filter.rangeType || "year", + modelName: {technical: this.props.filter.modelName, label: null}, + objects: {}, + }); + this.relativeDateRangeTypes = RELATIVE_DATE_RANGE_TYPES; + onWillStart(this.willStart.bind(this)); + } + async willStart() { + if (this.state.modelName.technical !== undefined) { + const modelLabel = await this.orm.call("ir.model", "display_name_for", [ + [this.state.modelName.technical], + ]); + this.state.modelName.label = modelLabel[0] && modelLabel[0].display_name; + } + var ModelFields = []; + for (var [objectType, objectClass] of Object.entries( + globalFiltersFieldMatchers + )) { + for (const objectId of objectClass.geIds()) { + var fields = objectClass.getFields(objectId); + this.state.objects[objectType + "_" + objectId] = { + id: objectType + "_" + objectId, + objectId: objectId, + name: objectClass.getDisplayName(objectId), + tag: await objectClass.getTag(objectId), + fieldMatch: + objectClass.getFieldMatching(objectId, this.props.filter.id) || + {}, + fields: fields, + model: objectClass.getModel(objectId), + }; + ModelFields.push(fields); + } + } + console.log(this.state.objects); + this.models = [ + ...new Set( + ModelFields.map((field_items) => Object.values(field_items)) + .flat() + .filter((field) => field.relation) + .map((field) => field.relation) + ), + ]; + } + get dateRangeTypes() { + return [ + {type: "year", description: _t("Year")}, + {type: "quarter", description: _t("Quarter")}, + {type: "month", description: _t("Month")}, + {type: "relative", description: _t("Relative Period")}, + ]; + } + get dateOffset() { + return [ + {value: 0, name: ""}, + {value: -1, name: _lt("Previous")}, + {value: -2, name: _lt("Before Previous")}, + {value: 1, name: _lt("Next")}, + {value: 2, name: _lt("After next")}, + ]; + } + onChangeFieldMatchOffset(object, ev) { + this.state.objects[object.id].fieldMatch.offset = parseInt(ev.target.value, 10); + } + onModelSelected(ev) { + this.state.modelName.technical = ev.technical; + this.state.modelName.label = ev.label; + } + onDateRangeChange(ev) { + this.state.rangeType = ev.target.value; + } + onSave() { + const action = this.props.filter.id + ? "EDIT_GLOBAL_FILTER" + : "ADD_GLOBAL_FILTER"; + this.env.openSidePanel("FilterPanel", {}); + + var filter = { + id: this.props.filter.id || uuidGenerator.uuidv4(), + type: this.state.type, + label: this.state.label, + defaultValue: this.state.defaultValue, + rangeType: this.state.rangeType, + defaultsToCurrentPeriod: this.state.defaultsToCurrentPeriod, + modelName: this.state.modelName.technical, + }; + var filterMatching = {}; + Object.values(this.state.objects).forEach((object) => { + filterMatching[object.type] = filterMatching[object.type] || {}; + filterMatching[object.type][object.id] = {...object.fieldMatch}; + }); + this.env.model.dispatch(action, {id: filter.id, filter, ...filterMatching}); + + this.env.openSidePanel("FilterPanel", {}); + } + onCancel() { + this.env.openSidePanel("FilterPanel", {}); + } + onRemove() { + if (this.props.filter.id) { + this.env.model.dispatch("REMOVE_GLOBAL_FILTER", {id: this.props.filter.id}); + } + this.env.openSidePanel("FilterPanel", {}); + } + onFieldMatchUpdate(object, name) { + this.state.objects[object.id].fieldMatch.chain = name.chain; + } +} + +EditFilterPanel.template = "spreadsheet_oca.EditFilterPanel"; +EditFilterPanel.components = {ModelSelector, ModelFieldSelector}; + +sidePanelRegistry.add("EditFilterPanel", { + title: "Edit Filter", + Body: EditFilterPanel, +}); diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml index 62433e08..bb6d1cfe 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml @@ -12,7 +12,164 @@ /> + +
+
+ +
+
+ + +
+
+
+ + + +
+
+ +
+
+ Label +
+
+ +
+
+
+
Time range
+
+ +
+
+
+
Default value
+
+ + +
+
+ +
+ +
+
+
Related model
+
+ +
+
+
+
+ - +
+
+ +
+ +
+ +
+
+
+ + + +
+
env.saveSpreadsheet(), -}); export class SpreadsheetName extends Component { setup() { this.state = useState({ diff --git a/spreadsheet_oca/static/src/spreadsheet/spreadsheet.scss b/spreadsheet_oca/static/src/spreadsheet/spreadsheet.scss index 33e55449..a36e2732 100644 --- a/spreadsheet_oca/static/src/spreadsheet/spreadsheet.scss +++ b/spreadsheet_oca/static/src/spreadsheet/spreadsheet.scss @@ -20,4 +20,28 @@ .o_spreadsheet_oca_name_warning { font-size: 0.8em; } + .o_spreadsheet_oca_filter { + padding: 16px; + .spreadsheet_oca_filter_label { + font-weight: bold; + } + .spreadsheet_oca_filter_value { + display: flex; + .o_field_widget { + width: 100%; + .o_field_tags { + width: 100%; + } + } + .o_field_selector { + width: 100%; + } + .spreadsheet_oca_filter_value_edit { + width: 15px; + margin-left: 1rem; + padding: 0; + cursor: pointer; + } + } + } } From 5dac08938afdcd726322c43b59cb53dd995af727 Mon Sep 17 00:00:00 2001 From: oca-ci Date: Tue, 9 May 2023 22:36:54 +0000 Subject: [PATCH 015/150] [UPD] Update spreadsheet_oca.pot --- spreadsheet_oca/i18n/spreadsheet_oca.pot | 144 ++++++++++++++++++++++- 1 file changed, 141 insertions(+), 3 deletions(-) diff --git a/spreadsheet_oca/i18n/spreadsheet_oca.pot b/spreadsheet_oca/i18n/spreadsheet_oca.pot index 4d95a921..c23fa6c2 100644 --- a/spreadsheet_oca/i18n/spreadsheet_oca.pot +++ b/spreadsheet_oca/i18n/spreadsheet_oca.pot @@ -13,11 +13,32 @@ msgstr "" "Content-Transfer-Encoding: \n" "Plural-Forms: \n" +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 +#, python-format +msgid "Add date" +msgstr "" + +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 +#, python-format +msgid "Add relation" +msgstr "" + #. module: spreadsheet_oca #: model:spreadsheet.spreadsheet.import.mode,name:spreadsheet_oca.spreadsheet_import_mode_add_sheet msgid "Add sheet to spreadsheet" msgstr "" +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 +#, python-format +msgid "Add text" +msgstr "" + #. module: spreadsheet_oca #. odoo-javascript #: code:addons/spreadsheet_oca/static/src/spreadsheet/spreadsheet.xml:0 @@ -28,7 +49,32 @@ msgid "Add to spreadsheet" msgstr "" #. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js:0 +#, python-format +msgid "After next" +msgstr "" + +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 +#, python-format +msgid "Automatically filter on the current period" +msgstr "" + +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js:0 +#, python-format +msgid "Before Previous" +msgstr "" + +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 #: model_terms:ir.ui.view,arch_db:spreadsheet_oca.spreadsheet_spreadsheet_import_form_view +#, python-format msgid "Cancel" msgstr "" @@ -55,6 +101,13 @@ msgstr "" msgid "Commands" msgstr "" +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 +#, python-format +msgid "Confirm" +msgstr "" + #. module: spreadsheet_oca #: model:ir.model.fields,field_description:spreadsheet_oca.field_spreadsheet_spreadsheet__contributor_ids msgid "Contributors" @@ -86,6 +139,13 @@ msgstr "" msgid "Data" msgstr "" +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 +#, python-format +msgid "Default value" +msgstr "" + #. module: spreadsheet_oca #: model:ir.model.fields,field_description:spreadsheet_oca.field_spreadsheet_oca_revision__display_name #: model:ir.model.fields,field_description:spreadsheet_oca.field_spreadsheet_spreadsheet__display_name @@ -102,7 +162,7 @@ msgstr "" #. module: spreadsheet_oca #. odoo-javascript -#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_controlpanel.esm.js:0 +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js:0 #, python-format msgid "File" msgstr "" @@ -112,6 +172,13 @@ msgstr "" msgid "Filename" msgstr "" +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js:0 +#, python-format +msgid "Filters" +msgstr "" + #. module: spreadsheet_oca #: model:ir.model.fields,field_description:spreadsheet_oca.field_spreadsheet_spreadsheet_import_mode__group_ids msgid "Group" @@ -145,6 +212,13 @@ msgstr "" msgid "Import data to spreadsheet" msgstr "" +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 +#, python-format +msgid "Label" +msgstr "" + #. module: spreadsheet_oca #: model:ir.model.fields,field_description:spreadsheet_oca.field_spreadsheet_oca_revision____last_update #: model:ir.model.fields,field_description:spreadsheet_oca.field_spreadsheet_spreadsheet____last_update @@ -184,6 +258,13 @@ msgstr "" msgid "Model" msgstr "" +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js:0 +#, python-format +msgid "Month" +msgstr "" + #. module: spreadsheet_oca #: model:ir.model.fields,field_description:spreadsheet_oca.field_spreadsheet_abstract__name #: model:ir.model.fields,field_description:spreadsheet_oca.field_spreadsheet_spreadsheet__name @@ -193,6 +274,13 @@ msgstr "" msgid "Name" msgstr "" +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js:0 +#, python-format +msgid "Next" +msgstr "" + #. module: spreadsheet_oca #: model:ir.model.fields,field_description:spreadsheet_oca.field_spreadsheet_oca_revision__next_revision_id msgid "Next Revision" @@ -208,11 +296,46 @@ msgstr "" msgid "Owner" msgstr "" +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js:0 +#, python-format +msgid "Previous" +msgstr "" + +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js:0 +#, python-format +msgid "Quarter" +msgstr "" + #. module: spreadsheet_oca #: model:ir.model.fields,field_description:spreadsheet_oca.field_spreadsheet_spreadsheet__reader_ids msgid "Readers" msgstr "" +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 +#, python-format +msgid "Related model" +msgstr "" + +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js:0 +#, python-format +msgid "Relative Period" +msgstr "" + +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 +#, python-format +msgid "Remove" +msgstr "" + #. module: spreadsheet_oca #: model:ir.model.fields,field_description:spreadsheet_oca.field_spreadsheet_oca_revision__res_id msgid "Res" @@ -220,7 +343,8 @@ msgstr "" #. module: spreadsheet_oca #. odoo-javascript -#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_controlpanel.esm.js:0 +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js:0 +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 #, python-format msgid "Save" msgstr "" @@ -270,14 +394,21 @@ msgstr "" msgid "Spreadsheets" msgstr "" +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 +#, python-format +msgid "Time range" +msgstr "" + #. module: spreadsheet_oca #: model:ir.model.fields,field_description:spreadsheet_oca.field_spreadsheet_oca_revision__type msgid "Type" msgstr "" #. module: spreadsheet_oca -#. odoo-javascript #. odoo-python +#. odoo-javascript #: code:addons/spreadsheet_oca/models/spreadsheet_spreadsheet.py:0 #: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 #, python-format @@ -289,6 +420,13 @@ msgstr "" msgid "User" msgstr "" +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js:0 +#, python-format +msgid "Year" +msgstr "" + #. module: spreadsheet_oca #. odoo-javascript #: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 From 45fc9dba44a56ae954809cace68d433c8a9b3de2 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Tue, 9 May 2023 22:41:37 +0000 Subject: [PATCH 016/150] spreadsheet_oca 16.0.1.1.1 --- spreadsheet_oca/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spreadsheet_oca/__manifest__.py b/spreadsheet_oca/__manifest__.py index 4793ca98..0469625d 100644 --- a/spreadsheet_oca/__manifest__.py +++ b/spreadsheet_oca/__manifest__.py @@ -5,7 +5,7 @@ "name": "Spreadsheet Oca", "summary": """ Allow to edit spreadsheets""", - "version": "16.0.1.1.0", + "version": "16.0.1.1.1", "license": "AGPL-3", "author": "CreuBlanca,Odoo Community Association (OCA)", "website": "https://github.com/OCA/spreadsheet", From aabf643a897b464f89c623c3ca3d91d0d3d986b6 Mon Sep 17 00:00:00 2001 From: oca-ci Date: Tue, 9 May 2023 22:44:10 +0000 Subject: [PATCH 017/150] [UPD] Update spreadsheet_oca.pot --- spreadsheet_oca/i18n/spreadsheet_oca.pot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spreadsheet_oca/i18n/spreadsheet_oca.pot b/spreadsheet_oca/i18n/spreadsheet_oca.pot index c23fa6c2..a033ba85 100644 --- a/spreadsheet_oca/i18n/spreadsheet_oca.pot +++ b/spreadsheet_oca/i18n/spreadsheet_oca.pot @@ -407,8 +407,8 @@ msgid "Type" msgstr "" #. module: spreadsheet_oca -#. odoo-python #. odoo-javascript +#. odoo-python #: code:addons/spreadsheet_oca/models/spreadsheet_spreadsheet.py:0 #: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 #, python-format From 195ab580fbe3df830745b12327e86befefdef710 Mon Sep 17 00:00:00 2001 From: Enric Tobella Date: Tue, 6 Jun 2023 18:06:10 +0200 Subject: [PATCH 018/150] [FIX] spreadsheet_oca: Filters where failing --- spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js | 3 +-- spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js b/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js index f7bb6e3d..954f0f83 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js @@ -91,7 +91,6 @@ export class EditFilterPanel extends Component { ModelFields.push(fields); } } - console.log(this.state.objects); this.models = [ ...new Set( ModelFields.map((field_items) => Object.values(field_items)) @@ -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/spreadsheet.xml b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml index bb6d1cfe..13444b02 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml @@ -143,6 +143,7 @@ resModel="object.model" readonly="false" isDebugMode="!!env.debug" + t-on-change="() => this.onChangeFieldSelector(ev)" update="(name) => this.onFieldMatchUpdate(object, name)" /> From 6443af7a3178b51686def4feddd7e8e4623ce3bf Mon Sep 17 00:00:00 2001 From: Enric Tobella Date: Wed, 7 Jun 2023 15:08:49 +0200 Subject: [PATCH 019/150] [IMP] spreadsheet_oca: Alloe to edit Pivot data --- spreadsheet_oca/__manifest__.py | 1 + .../bundle/filter_panel_datasources.esm.js | 123 ++++++++++++++++++ .../src/spreadsheet/bundle/spreadsheet.xml | 55 ++++++++ .../static/src/spreadsheet/spreadsheet.scss | 13 ++ 4 files changed, 192 insertions(+) create mode 100644 spreadsheet_oca/static/src/spreadsheet/bundle/filter_panel_datasources.esm.js diff --git a/spreadsheet_oca/__manifest__.py b/spreadsheet_oca/__manifest__.py index 0469625d..a683edc9 100644 --- a/spreadsheet_oca/__manifest__.py +++ b/spreadsheet_oca/__manifest__.py @@ -28,6 +28,7 @@ "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_panel_datasources.esm.js b/spreadsheet_oca/static/src/spreadsheet/bundle/filter_panel_datasources.esm.js new file mode 100644 index 00000000..ebe46d56 --- /dev/null +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/filter_panel_datasources.esm.js @@ -0,0 +1,123 @@ +/** @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, + }) + ); + return children.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(), + }); + } +} + +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", + Body: PivotPanel, +}); diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml index 13444b02..e7df314b 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml @@ -12,6 +12,61 @@ /> + + + + +
+
+
Pivot name
+ +
+
+
Model
+
()
+
+
+
Domain
+ + +
+
+
Dimensions
+ +
+ +
+
+
Sorting
+
+
+
+
Measures
+ +
+ +
+
+ Last updated at +
+
+ +
Date: Sat, 10 Jun 2023 15:46:20 +0200 Subject: [PATCH 020/150] [IMP] spreadsheet_oca: Allow to import lists --- spreadsheet_oca/__manifest__.py | 2 + .../bundle/spreadsheet_action.esm.js | 48 ++++++++++++++- .../src/spreadsheet/list_controller.esm.js | 14 +++++ .../src/spreadsheet/list_renderer.esm.js | 58 +++++++++++++++++++ .../static/src/spreadsheet/spreadsheet.xml | 15 +++++ 5 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 spreadsheet_oca/static/src/spreadsheet/list_controller.esm.js create mode 100644 spreadsheet_oca/static/src/spreadsheet/list_renderer.esm.js diff --git a/spreadsheet_oca/__manifest__.py b/spreadsheet_oca/__manifest__.py index a683edc9..ca7f90ab 100644 --- a/spreadsheet_oca/__manifest__.py +++ b/spreadsheet_oca/__manifest__.py @@ -24,6 +24,8 @@ "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", 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.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 @@ + + + + +
From de7ed5e3d198d01c6e53bc1ee59524dec42580c8 Mon Sep 17 00:00:00 2001 From: Enric Tobella Date: Sun, 9 Jul 2023 18:42:08 +0200 Subject: [PATCH 023/150] [FIX] spreadsheet_oca: Fix filters --- spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js b/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js index 954f0f83..c4e41e2c 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js @@ -86,6 +86,7 @@ export class EditFilterPanel extends Component { objectClass.getFieldMatching(objectId, this.props.filter.id) || {}, fields: fields, + type: objectType, model: objectClass.getModel(objectId), }; ModelFields.push(fields); @@ -132,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, @@ -145,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}); From 17fcf7d83d9048a5eb43197e1d9f8572ac956754 Mon Sep 17 00:00:00 2001 From: oca-ci Date: Sun, 9 Jul 2023 17:30:43 +0000 Subject: [PATCH 024/150] [UPD] Update spreadsheet_oca.pot --- spreadsheet_oca/i18n/spreadsheet_oca.pot | 106 +++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/spreadsheet_oca/i18n/spreadsheet_oca.pot b/spreadsheet_oca/i18n/spreadsheet_oca.pot index a033ba85..775069a5 100644 --- a/spreadsheet_oca/i18n/spreadsheet_oca.pot +++ b/spreadsheet_oca/i18n/spreadsheet_oca.pot @@ -39,6 +39,13 @@ msgstr "" msgid "Add text" msgstr "" +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/spreadsheet.xml:0 +#, python-format +msgid "Add to spreadesheet" +msgstr "" + #. module: spreadsheet_oca #. odoo-javascript #: code:addons/spreadsheet_oca/static/src/spreadsheet/spreadsheet.xml:0 @@ -146,6 +153,13 @@ msgstr "" msgid "Default value" msgstr "" +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 +#, python-format +msgid "Dimensions" +msgstr "" + #. module: spreadsheet_oca #: model:ir.model.fields,field_description:spreadsheet_oca.field_spreadsheet_oca_revision__display_name #: model:ir.model.fields,field_description:spreadsheet_oca.field_spreadsheet_spreadsheet__display_name @@ -154,12 +168,28 @@ msgstr "" msgid "Display Name" msgstr "" +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 +#, python-format +msgid "Domain" +msgstr "" + #. module: spreadsheet_oca #: model_terms:ir.ui.view,arch_db:spreadsheet_oca.spreadsheet_spreadsheet_form_view #: model_terms:ir.ui.view,arch_db:spreadsheet_oca.spreadsheet_spreadsheet_tree_view msgid "Edit" msgstr "" +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 +#, python-format +msgid "Edit domain" +msgstr "" + #. module: spreadsheet_oca #. odoo-javascript #: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js:0 @@ -212,6 +242,13 @@ msgstr "" msgid "Import data to spreadsheet" msgstr "" +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 +#, python-format +msgid "Insert pivot" +msgstr "" + #. module: spreadsheet_oca #. odoo-javascript #: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 @@ -243,18 +280,44 @@ msgstr "" msgid "Last Updated on" msgstr "" +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 +#, python-format +msgid "Last updated at" +msgstr "" + +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 +#, python-format +msgid "List name" +msgstr "" + #. module: spreadsheet_oca #: model:res.groups,name:spreadsheet_oca.group_manager msgid "Manager" msgstr "" +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 +#, python-format +msgid "Measures" +msgstr "" + #. module: spreadsheet_oca #: model:ir.model.fields,field_description:spreadsheet_oca.field_spreadsheet_spreadsheet_import__mode_id msgid "Mode" msgstr "" #. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 #: model:ir.model.fields,field_description:spreadsheet_oca.field_spreadsheet_oca_revision__model +#, python-format msgid "Model" msgstr "" @@ -296,6 +359,13 @@ msgstr "" msgid "Owner" msgstr "" +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 +#, python-format +msgid "Pivot name" +msgstr "" + #. module: spreadsheet_oca #. odoo-javascript #: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js:0 @@ -315,6 +385,13 @@ msgstr "" msgid "Readers" msgstr "" +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/filter_panel_datasources.esm.js:0 +#, python-format +msgid "Refresh all data" +msgstr "" + #. module: spreadsheet_oca #. odoo-javascript #: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 @@ -359,6 +436,13 @@ msgstr "" msgid "Server Revision" msgstr "" +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 +#, python-format +msgid "Sorting" +msgstr "" + #. module: spreadsheet_oca #: model:ir.model,name:spreadsheet_oca.model_spreadsheet_spreadsheet #: model:ir.model.fields,field_description:spreadsheet_oca.field_spreadsheet_spreadsheet_import__spreadsheet_id @@ -427,6 +511,20 @@ msgstr "" msgid "Year" msgstr "" +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/filter_panel_datasources.esm.js:0 +#, python-format +msgid "ascending" +msgstr "" + +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/filter_panel_datasources.esm.js:0 +#, python-format +msgid "descending" +msgstr "" + #. module: spreadsheet_oca #. odoo-javascript #: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 @@ -434,6 +532,14 @@ msgstr "" msgid "name is required" msgstr "" +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/filter_panel_datasources.esm.js:0 +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/filter_panel_datasources.esm.js:0 +#, python-format +msgid "not updated" +msgstr "" + #. module: spreadsheet_oca #: model:ir.model,name:spreadsheet_oca.model_ir_websocket msgid "websocket message handling" From 8585d8ebea4f9d3229be8fea26dd5f38b7cbeca7 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Sun, 9 Jul 2023 17:32:31 +0000 Subject: [PATCH 025/150] spreadsheet_oca 16.0.1.1.2 --- spreadsheet_oca/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spreadsheet_oca/__manifest__.py b/spreadsheet_oca/__manifest__.py index ca7f90ab..0fa2033c 100644 --- a/spreadsheet_oca/__manifest__.py +++ b/spreadsheet_oca/__manifest__.py @@ -5,7 +5,7 @@ "name": "Spreadsheet Oca", "summary": """ Allow to edit spreadsheets""", - "version": "16.0.1.1.1", + "version": "16.0.1.1.2", "license": "AGPL-3", "author": "CreuBlanca,Odoo Community Association (OCA)", "website": "https://github.com/OCA/spreadsheet", From 623477e7fcbaf19d80eab988bd41c61a72b0b14c Mon Sep 17 00:00:00 2001 From: Enric Tobella Date: Sun, 9 Jul 2023 23:57:36 +0200 Subject: [PATCH 026/150] [IMP] spreadsheet_oca: Allow to edit Odoo graphs properly Fixes #9 --- spreadsheet_oca/__manifest__.py | 1 + .../src/spreadsheet/bundle/odoo_panels.esm.js | 76 +++++++++++++++++++ .../src/spreadsheet/bundle/spreadsheet.xml | 36 +++++++++ .../static/src/spreadsheet/spreadsheet.scss | 5 ++ 4 files changed, 118 insertions(+) create mode 100644 spreadsheet_oca/static/src/spreadsheet/bundle/odoo_panels.esm.js diff --git a/spreadsheet_oca/__manifest__.py b/spreadsheet_oca/__manifest__.py index 0fa2033c..73b8c97a 100644 --- a/spreadsheet_oca/__manifest__.py +++ b/spreadsheet_oca/__manifest__.py @@ -34,6 +34,7 @@ "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", + "spreadsheet_oca/static/src/spreadsheet/bundle/odoo_panels.esm.js", ], }, } diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/odoo_panels.esm.js b/spreadsheet_oca/static/src/spreadsheet/bundle/odoo_panels.esm.js new file mode 100644 index 00000000..970ef04e --- /dev/null +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/odoo_panels.esm.js @@ -0,0 +1,76 @@ +/** @odoo-module */ + +import {Domain} from "@web/core/domain"; +import {Many2OneField} from "@web/views/fields/many2one/many2one_field"; +import spreadsheet from "@spreadsheet/o_spreadsheet/o_spreadsheet_extended"; +import {useService} from "@web/core/utils/hooks"; + +const {chartSidePanelComponentRegistry} = spreadsheet.registries; +const {LineBarPieDesignPanel} = spreadsheet.components; +const {Component} = owl; + +export class OdooPanel extends Component { + setup() { + this.menus = useService("menu"); + } + get menuId() { + const menu = this.env.model.getters.getChartOdooMenu(this.props.figureId); + if (menu) { + return [menu.id, menu.name]; + } + return undefined; + } + updateMenu(menuId) { + if (!menuId) { + this.env.model.dispatch("LINK_ODOO_MENU_TO_CHART", { + chartId: this.props.figureId, + odooMenuId: undefined, + }); + return; + } + const menu = this.env.model.getters.getIrMenu(menuId[0]); + this.env.model.dispatch("LINK_ODOO_MENU_TO_CHART", { + chartId: this.props.figureId, + odooMenuId: menu.xmlid || menu.id, + }); + } + get record() { + const menus = this.menus + .getAll() + .map((menu) => menu.id) + .filter((menuId) => menuId !== "root"); + return { + getFieldDomain: function () { + return new Domain([["id", "in", menus]]); + }, + getFieldContext: function () { + return {}; + }, + }; + } +} +OdooPanel.template = "spreadsheet_oca.OdooPanel"; +OdooPanel.components = {Many2OneField}; + +class OdooStackablePanel extends OdooPanel { + onChangeStacked(ev) { + this.props.updateChart(this.props.figureId, { + stacked: ev.target.checked, + }); + } +} +OdooStackablePanel.template = "spreadsheet_oca.OdooStackablePanel"; + +chartSidePanelComponentRegistry + .add("odoo_line", { + configuration: OdooStackablePanel, + design: LineBarPieDesignPanel, + }) + .add("odoo_bar", { + configuration: OdooStackablePanel, + design: LineBarPieDesignPanel, + }) + .add("odoo_pie", { + configuration: OdooPanel, + design: LineBarPieDesignPanel, + }); diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml index 9bd67783..09784535 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml @@ -386,4 +386,40 @@
+ +
+
Link to Odoo menu
+
+ +
+
+
+ + +
+ +
+
+
diff --git a/spreadsheet_oca/static/src/spreadsheet/spreadsheet.scss b/spreadsheet_oca/static/src/spreadsheet/spreadsheet.scss index eecba723..aae0756b 100644 --- a/spreadsheet_oca/static/src/spreadsheet/spreadsheet.scss +++ b/spreadsheet_oca/static/src/spreadsheet/spreadsheet.scss @@ -57,4 +57,9 @@ } } } + .o-section-value { + .o_field_many2one_selection { + width: 100%; + } + } } From e4d697b01c54ca9c5097b5a7eb7c14ff847399f6 Mon Sep 17 00:00:00 2001 From: oca-ci Date: Wed, 12 Jul 2023 08:11:06 +0000 Subject: [PATCH 027/150] [UPD] Update spreadsheet_oca.pot --- spreadsheet_oca/i18n/spreadsheet_oca.pot | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/spreadsheet_oca/i18n/spreadsheet_oca.pot b/spreadsheet_oca/i18n/spreadsheet_oca.pot index 775069a5..1dc4bcd7 100644 --- a/spreadsheet_oca/i18n/spreadsheet_oca.pot +++ b/spreadsheet_oca/i18n/spreadsheet_oca.pot @@ -288,6 +288,13 @@ msgstr "" msgid "Last updated at" msgstr "" +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 +#, python-format +msgid "Link to Odoo menu" +msgstr "" + #. module: spreadsheet_oca #. odoo-javascript #: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 @@ -478,6 +485,13 @@ msgstr "" msgid "Spreadsheets" msgstr "" +#. module: spreadsheet_oca +#. odoo-javascript +#: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 +#, python-format +msgid "Stacked" +msgstr "" + #. module: spreadsheet_oca #. odoo-javascript #: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 From 18d58fc3512bfd5d2c0d2d5fb967e61bca471e75 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Wed, 12 Jul 2023 08:13:02 +0000 Subject: [PATCH 028/150] spreadsheet_oca 16.0.1.1.3 --- spreadsheet_oca/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spreadsheet_oca/__manifest__.py b/spreadsheet_oca/__manifest__.py index 73b8c97a..d70b5248 100644 --- a/spreadsheet_oca/__manifest__.py +++ b/spreadsheet_oca/__manifest__.py @@ -5,7 +5,7 @@ "name": "Spreadsheet Oca", "summary": """ Allow to edit spreadsheets""", - "version": "16.0.1.1.2", + "version": "16.0.1.1.3", "license": "AGPL-3", "author": "CreuBlanca,Odoo Community Association (OCA)", "website": "https://github.com/OCA/spreadsheet", From f9094ffcfcc3917668d0868b1e7ee1935dd94125 Mon Sep 17 00:00:00 2001 From: oca-ci Date: Wed, 12 Jul 2023 08:18:40 +0000 Subject: [PATCH 029/150] [UPD] Update spreadsheet_oca.pot --- spreadsheet_oca/i18n/spreadsheet_oca.pot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spreadsheet_oca/i18n/spreadsheet_oca.pot b/spreadsheet_oca/i18n/spreadsheet_oca.pot index 1dc4bcd7..2ea87c75 100644 --- a/spreadsheet_oca/i18n/spreadsheet_oca.pot +++ b/spreadsheet_oca/i18n/spreadsheet_oca.pot @@ -505,8 +505,8 @@ msgid "Type" msgstr "" #. module: spreadsheet_oca -#. odoo-javascript #. odoo-python +#. odoo-javascript #: code:addons/spreadsheet_oca/models/spreadsheet_spreadsheet.py:0 #: code:addons/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml:0 #, python-format From 1b1d51fc689ffc73e426f73f158278381b0cbd29 Mon Sep 17 00:00:00 2001 From: Enric Tobella Date: Sat, 19 Aug 2023 23:44:19 +0200 Subject: [PATCH 030/150] [IMP] spreadsheet_oca: Add roadmap for specific questions --- spreadsheet_oca/README.rst | 22 ++++++++-- spreadsheet_oca/readme/ROADMAP.rst | 6 +++ spreadsheet_oca/static/description/index.html | 40 +++++++++++++------ 3 files changed, 52 insertions(+), 16 deletions(-) create mode 100644 spreadsheet_oca/readme/ROADMAP.rst diff --git a/spreadsheet_oca/README.rst b/spreadsheet_oca/README.rst index 12ebd8ba..e0676273 100644 --- a/spreadsheet_oca/README.rst +++ b/spreadsheet_oca/README.rst @@ -2,10 +2,13 @@ Spreadsheet Oca =============== -.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:32b5b0c526375df2a635235ae4b4204334012bc326b4741f499614faaa7c6697 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status @@ -19,8 +22,11 @@ Spreadsheet Oca .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png :target: https://translation.odoo-community.org/projects/spreadsheet-16-0/spreadsheet-16-0-spreadsheet_oca :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/spreadsheet&target_branch=16.0 + :alt: Try me on Runboat -|badge1| |badge2| |badge3| |badge4| +|badge1| |badge2| |badge3| |badge4| |badge5| This module adds a functionality for adding and editing Spreadsheets using Odoo CE. @@ -29,12 +35,22 @@ This module adds a functionality for adding and editing Spreadsheets using Odoo .. contents:: :local: +Usage +===== + +Adding new lines on pivot tables +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When we add a pivot table, the number of rows is predefined according to the current data. + +In order to add new rows, we need to reinsert the pivot table. + Bug Tracker =========== Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. -If you spotted it first, help us smashing it by providing a detailed and welcomed +If you spotted it first, help us to smash it by providing a detailed and welcomed `feedback `_. Do not contact contributors directly about support or help with technical issues. diff --git a/spreadsheet_oca/readme/ROADMAP.rst b/spreadsheet_oca/readme/ROADMAP.rst new file mode 100644 index 00000000..5db690b8 --- /dev/null +++ b/spreadsheet_oca/readme/ROADMAP.rst @@ -0,0 +1,6 @@ +Adding new lines on pivot tables +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When we add a pivot table, the number of rows is predefined according to the current data. + +In order to add new rows, we need to reinsert the pivot table. diff --git a/spreadsheet_oca/static/description/index.html b/spreadsheet_oca/static/description/index.html index be8066a1..15c2004c 100644 --- a/spreadsheet_oca/static/description/index.html +++ b/spreadsheet_oca/static/description/index.html @@ -3,7 +3,7 @@ - + Spreadsheet Oca -
-

Spreadsheet Oca

+
+ + +Odoo Community Association + +
+

Spreadsheet Oca

-

Beta License: AGPL-3 OCA/spreadsheet Translate me on Weblate Try me on Runboat

+

Beta License: AGPL-3 OCA/spreadsheet Translate me on Weblate Try me on Runboat

This module adds a functionality for adding and editing Spreadsheets using Odoo CE.

It is an alternative to the proprietary module spreadsheet_edition @@ -397,9 +402,9 @@

Spreadsheet Oca

-

Usage

+

Usage

-

Create a new spreadsheet

+

Create a new spreadsheet

-

Development

+

Development

If you want to develop custom business functions, you can add others, based on the file https://github.com/odoo/odoo/blob/16.0/addons/spreadsheet_account/static/src/accounting_functions.js

-

Known issues / Roadmap

+

Known issues / Roadmap

-

Adding new lines on pivot tables

+

Adding new lines on pivot tables

When we add a pivot table, the number of rows is predefined according to the current data.

In order to add new rows, we need to reinsert the pivot table.

-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed @@ -510,15 +515,15 @@

Bug Tracker

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • CreuBlanca
-

Contributors

+

Contributors

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -545,5 +550,6 @@

Maintainers

+
From d3fb68992b4a61fb649204413addb3dad18c8eb2 Mon Sep 17 00:00:00 2001 From: CarlosRoca13 Date: Wed, 17 Sep 2025 12:52:50 +0200 Subject: [PATCH 145/150] [FIX] spreadsheet_oca: Make use of _zip_xslx_files that spreadsheet module offers Reduce the logic of the controller by using the function that spreadsheet module offers and avoid error when rendering images on the xlsx file. --- spreadsheet_oca/controllers/main.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/spreadsheet_oca/controllers/main.py b/spreadsheet_oca/controllers/main.py index ef77af1d..dcc35bc7 100644 --- a/spreadsheet_oca/controllers/main.py +++ b/spreadsheet_oca/controllers/main.py @@ -1,8 +1,6 @@ # Copyright 2024 Tecnativa - Carlos Roca # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). import json -from io import BytesIO -from zipfile import ZipFile from odoo.http import Controller, content_disposition, request, route @@ -13,13 +11,9 @@ def download_spreadsheet_xlsx(self, zip_name, files, **kw): if hasattr(files, "read"): files = files.read().decode("utf-8") files = json.loads(files) - file_bytes = BytesIO() - with ZipFile(file_bytes, "w") as zip_file: - for file in files: - zip_file.writestr(file["path"], file["content"]) - file_content = file_bytes.getvalue() + file_content = request.env["spreadsheet.mixin"]._zip_xslx_files(files) return request.make_response( - file_bytes.getvalue(), + file_content, [ ("Content-Length", len(file_content)), ("Content-Type", "application/vnd.ms-excel"), From 81aa777283eab41ab8f63b350234cf08a3484f5d Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Wed, 17 Sep 2025 21:57:29 +0000 Subject: [PATCH 146/150] [BOT] post-merge updates --- spreadsheet_oca/README.rst | 2 +- spreadsheet_oca/__manifest__.py | 2 +- spreadsheet_oca/static/description/index.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spreadsheet_oca/README.rst b/spreadsheet_oca/README.rst index e6b74834..869c655f 100644 --- a/spreadsheet_oca/README.rst +++ b/spreadsheet_oca/README.rst @@ -11,7 +11,7 @@ Spreadsheet Oca !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:97d72e6ea5d8c3d2efea4dc8febd611db0fa13076f55d7eb9298216ded7aae92 + !! source digest: sha256:53f2ac387d2b0a0a0d10edfbb2702f176f39205810a3eff890d1bb146bf33829 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png diff --git a/spreadsheet_oca/__manifest__.py b/spreadsheet_oca/__manifest__.py index 0515c56b..9a892368 100644 --- a/spreadsheet_oca/__manifest__.py +++ b/spreadsheet_oca/__manifest__.py @@ -5,7 +5,7 @@ "name": "Spreadsheet Oca", "summary": """ Allow to edit spreadsheets""", - "version": "17.0.1.0.1", + "version": "17.0.1.0.2", "license": "AGPL-3", "author": "CreuBlanca,Odoo Community Association (OCA)", "website": "https://github.com/OCA/spreadsheet", diff --git a/spreadsheet_oca/static/description/index.html b/spreadsheet_oca/static/description/index.html index f4a54955..8d8a205c 100644 --- a/spreadsheet_oca/static/description/index.html +++ b/spreadsheet_oca/static/description/index.html @@ -372,7 +372,7 @@

Spreadsheet Oca

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:97d72e6ea5d8c3d2efea4dc8febd611db0fa13076f55d7eb9298216ded7aae92 +!! source digest: sha256:53f2ac387d2b0a0a0d10edfbb2702f176f39205810a3eff890d1bb146bf33829 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Beta License: AGPL-3 OCA/spreadsheet Translate me on Weblate Try me on Runboat

This module adds a functionality for adding and editing Spreadsheets From ea01139c68f05991079f08a398696eda00fcc033 Mon Sep 17 00:00:00 2001 From: CarlosRoca13 Date: Thu, 18 Sep 2025 10:35:55 +0200 Subject: [PATCH 147/150] [FIX] spreadsheet_oca: Filters don't take care about field type --- spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js | 1 + 1 file changed, 1 insertion(+) diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js b/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js index a6d1723b..99f9677d 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js @@ -181,6 +181,7 @@ export class EditFilterPanel extends Component { } onFieldMatchUpdate(object, name) { this.state.objects[object.id].fieldMatch.chain = name; + this.state.objects[object.id].fieldMatch.type = object.fields[name]?.type; } toggleDateDefaultValue(ev) { this.state.defaultValue = ev.target.checked ? "this_month" : undefined; From cf2b3e5dbdcc057c9d8cb309322813adf14bbd2f Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Thu, 18 Sep 2025 08:42:51 +0000 Subject: [PATCH 148/150] [BOT] post-merge updates --- spreadsheet_oca/README.rst | 2 +- spreadsheet_oca/__manifest__.py | 2 +- spreadsheet_oca/static/description/index.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spreadsheet_oca/README.rst b/spreadsheet_oca/README.rst index 869c655f..a0cab53c 100644 --- a/spreadsheet_oca/README.rst +++ b/spreadsheet_oca/README.rst @@ -11,7 +11,7 @@ Spreadsheet Oca !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:53f2ac387d2b0a0a0d10edfbb2702f176f39205810a3eff890d1bb146bf33829 + !! source digest: sha256:2e54effb5771467ddab39e872bcddddc5e7a900f7afb88dd3bb87ac26d195ca5 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png diff --git a/spreadsheet_oca/__manifest__.py b/spreadsheet_oca/__manifest__.py index 9a892368..553f0364 100644 --- a/spreadsheet_oca/__manifest__.py +++ b/spreadsheet_oca/__manifest__.py @@ -5,7 +5,7 @@ "name": "Spreadsheet Oca", "summary": """ Allow to edit spreadsheets""", - "version": "17.0.1.0.2", + "version": "17.0.1.0.3", "license": "AGPL-3", "author": "CreuBlanca,Odoo Community Association (OCA)", "website": "https://github.com/OCA/spreadsheet", diff --git a/spreadsheet_oca/static/description/index.html b/spreadsheet_oca/static/description/index.html index 8d8a205c..7bae2017 100644 --- a/spreadsheet_oca/static/description/index.html +++ b/spreadsheet_oca/static/description/index.html @@ -372,7 +372,7 @@

Spreadsheet Oca

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:53f2ac387d2b0a0a0d10edfbb2702f176f39205810a3eff890d1bb146bf33829 +!! source digest: sha256:2e54effb5771467ddab39e872bcddddc5e7a900f7afb88dd3bb87ac26d195ca5 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Beta License: AGPL-3 OCA/spreadsheet Translate me on Weblate Try me on Runboat

This module adds a functionality for adding and editing Spreadsheets From 06cecb1392ecdd7da96cf08d5ab8f65cdfc9c935 Mon Sep 17 00:00:00 2001 From: Chris Mann Date: Wed, 24 Sep 2025 14:12:27 +0100 Subject: [PATCH 149/150] [IMP] spreadsheet_oca: pre-commit auto fixes --- spreadsheet_dashboard_purchase_oca/README.rst | 10 +- .../static/description/index.html | 26 +- .../README.rst | 16 +- .../static/description/index.html | 28 +- spreadsheet_oca/README.rst | 134 ++-- .../spreadsheet_spreadsheet_import_mode.xml | 33 +- spreadsheet_oca/demo/demo_spreadsheet.json | 30 +- .../demo/spreadsheet_spreadsheet.xml | 9 +- spreadsheet_oca/security/security.xml | 33 +- .../static/src/pivot/pivot_table.esm.js | 120 ++-- .../src/spreadsheet/bundle/chart_panel.esm.js | 66 +- .../spreadsheet/bundle/chart_panels.esm.js | 153 ++-- .../src/spreadsheet/bundle/filter.esm.js | 318 +++++---- .../bundle/filter_panel_datasources.esm.js | 501 +++++++------ .../src/spreadsheet/bundle/odoo_panels.esm.js | 164 ++--- .../src/spreadsheet/bundle/spreadsheet.xml | 659 ++++++++---------- .../bundle/spreadsheet_action.esm.js | 535 +++++++------- .../bundle/spreadsheet_controlpanel.esm.js | 32 +- .../bundle/spreadsheet_renderer.esm.js | 333 +++++---- .../src/spreadsheet/graph_controller.esm.js | 38 +- .../src/spreadsheet/graph_controller.xml | 13 +- .../src/spreadsheet/list_controller.esm.js | 10 +- .../src/spreadsheet/list_controller.xml | 23 +- .../src/spreadsheet/list_renderer.esm.js | 108 +-- .../src/spreadsheet/pivot_controller.esm.js | 126 ++-- .../src/spreadsheet/pivot_controller.xml | 12 +- .../static/src/spreadsheet/spreadsheet.scss | 112 +-- .../src/spreadsheet/spreadsheet_action.esm.js | 26 +- .../utils/dynamic_generators.esm.js | 98 ++- .../spreadsheet_tree_view.esm.js | 122 ++-- .../spreadsheet_tree_view.xml | 36 +- .../views/spreadsheet_spreadsheet.xml | 88 +-- .../wizards/spreadsheet_select_row_number.xml | 18 +- .../spreadsheet_spreadsheet_import.xml | 83 +-- 34 files changed, 1977 insertions(+), 2136 deletions(-) diff --git a/spreadsheet_dashboard_purchase_oca/README.rst b/spreadsheet_dashboard_purchase_oca/README.rst index 3e6cceea..3097a8d1 100644 --- a/spreadsheet_dashboard_purchase_oca/README.rst +++ b/spreadsheet_dashboard_purchase_oca/README.rst @@ -1,7 +1,3 @@ -.. image:: https://odoo-community.org/readme-banner-image - :target: https://odoo-community.org/get-involved?utm_source=readme - :alt: Odoo Community Association - ================================= Spreadsheet dashboard for vendors ================================= @@ -17,7 +13,7 @@ Spreadsheet dashboard for vendors .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status :alt: Beta -.. |badge2| image:: https://img.shields.io/badge/license-LGPL--3-blue.png +.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html :alt: License: LGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fspreadsheet-lightgray.png?logo=github @@ -61,9 +57,9 @@ Authors Contributors ------------ -- `Tecnativa `__: +- `Tecnativa `__: - - Pedro M. Baeza + - Pedro M. Baeza Other credits ------------- diff --git a/spreadsheet_dashboard_purchase_oca/static/description/index.html b/spreadsheet_dashboard_purchase_oca/static/description/index.html index d58cda69..328930b3 100644 --- a/spreadsheet_dashboard_purchase_oca/static/description/index.html +++ b/spreadsheet_dashboard_purchase_oca/static/description/index.html @@ -3,7 +3,7 @@ -README.rst +Spreadsheet dashboard for vendors -

+
+

Spreadsheet dashboard for vendors

- - -Odoo Community Association - -
-

Spreadsheet dashboard for vendors

-

Beta License: LGPL-3 OCA/spreadsheet Translate me on Weblate Try me on Runboat

+

Beta License: LGPL-3 OCA/spreadsheet Translate me on Weblate Try me on Runboat

Spreadsheet dashboard for vendors.

Table of contents

@@ -390,7 +385,7 @@

Spreadsheet dashboard for vendors

-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed @@ -398,16 +393,16 @@

Bug Tracker

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • Odoo S.A.
  • Tecnativa
-

Contributors

+

Contributors

-

Other credits

+

Other credits

This module is a forward-port from Odoo SA and as such, it is not included in the OCA CLA. That means we do not have a copy of the copyright on it like all other OCA modules.

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -435,6 +430,5 @@

Maintainers

-
diff --git a/spreadsheet_dashboard_purchase_stock_oca/README.rst b/spreadsheet_dashboard_purchase_stock_oca/README.rst index 49b2d217..fd22b650 100644 --- a/spreadsheet_dashboard_purchase_stock_oca/README.rst +++ b/spreadsheet_dashboard_purchase_stock_oca/README.rst @@ -1,7 +1,3 @@ -.. image:: https://odoo-community.org/readme-banner-image - :target: https://odoo-community.org/get-involved?utm_source=readme - :alt: Odoo Community Association - =================================== Spreadsheet dashboard for purchases =================================== @@ -17,7 +13,7 @@ Spreadsheet dashboard for purchases .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status :alt: Beta -.. |badge2| image:: https://img.shields.io/badge/license-LGPL--3-blue.png +.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html :alt: License: LGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fspreadsheet-lightgray.png?logo=github @@ -42,9 +38,9 @@ Spreadsheet dashboard for purchases. Known issues / Roadmap ====================== -- Modernize style and use new v18 features. -- Unify both purchase modules into one, and remove duplicated - information. +- Modernize style and use new v18 features. +- Unify both purchase modules into one, and remove duplicated + information. Bug Tracker =========== @@ -68,9 +64,9 @@ Authors Contributors ------------ -- `Tecnativa `__: +- `Tecnativa `__: - - Pedro M. Baeza + - Pedro M. Baeza Other credits ------------- diff --git a/spreadsheet_dashboard_purchase_stock_oca/static/description/index.html b/spreadsheet_dashboard_purchase_stock_oca/static/description/index.html index eab375b6..a0ae7aa4 100644 --- a/spreadsheet_dashboard_purchase_stock_oca/static/description/index.html +++ b/spreadsheet_dashboard_purchase_stock_oca/static/description/index.html @@ -3,7 +3,7 @@ -README.rst +Spreadsheet dashboard for purchases -
+
+

Spreadsheet dashboard for purchases

- - -Odoo Community Association - -
-

Spreadsheet dashboard for purchases

-

Beta License: LGPL-3 OCA/spreadsheet Translate me on Weblate Try me on Runboat

+

Beta License: LGPL-3 OCA/spreadsheet Translate me on Weblate Try me on Runboat

Spreadsheet dashboard for purchases.

Table of contents

@@ -391,7 +386,7 @@

Spreadsheet dashboard for purchases

-

Known issues / Roadmap

+

Known issues / Roadmap

  • Modernize style and use new v18 features.
  • Unify both purchase modules into one, and remove duplicated @@ -399,7 +394,7 @@

    Known issues / Roadmap

-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed @@ -407,16 +402,16 @@

Bug Tracker

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • Odoo S.A.
  • Tecnativa
-

Contributors

+

Contributors

-

Other credits

+

Other credits

This module is a forward-port from Odoo SA and as such, it is not included in the OCA CLA. That means we do not have a copy of the copyright on it like all other OCA modules.

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -444,6 +439,5 @@

Maintainers

-
diff --git a/spreadsheet_oca/README.rst b/spreadsheet_oca/README.rst index a0cab53c..8ddb4f8f 100644 --- a/spreadsheet_oca/README.rst +++ b/spreadsheet_oca/README.rst @@ -1,7 +1,3 @@ -.. image:: https://odoo-community.org/readme-banner-image - :target: https://odoo-community.org/get-involved?utm_source=readme - :alt: Odoo Community Association - =============== Spreadsheet Oca =============== @@ -17,17 +13,17 @@ Spreadsheet Oca .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status :alt: Beta -.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fspreadsheet-lightgray.png?logo=github - :target: https://github.com/OCA/spreadsheet/tree/17.0/spreadsheet_oca + :target: https://github.com/OCA/spreadsheet/tree/18.0/spreadsheet_oca :alt: OCA/spreadsheet .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/spreadsheet-17-0/spreadsheet-17-0-spreadsheet_oca + :target: https://translation.odoo-community.org/projects/spreadsheet-18-0/spreadsheet-18-0-spreadsheet_oca :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png - :target: https://runboat.odoo-community.org/builds?repo=OCA/spreadsheet&target_branch=17.0 + :target: https://runboat.odoo-community.org/builds?repo=OCA/spreadsheet&target_branch=18.0 :alt: Try me on Runboat |badge1| |badge2| |badge3| |badge4| |badge5| @@ -49,86 +45,86 @@ Usage **Create a new spreadsheet** ---------------------------- -- Go to 'Spreadsheet' menu -- Click on 'Create' -- Put a name, then click on the "Edit" button +- Go to 'Spreadsheet' menu +- Click on 'Create' +- Put a name, then click on the "Edit" button |image1| -- At this point you switch to spreadsheet editing mode. The editor is - named ``o-spreadsheet`` and looks like another common spreadsheet web - editors. (OnlyOffice, Ethercalc, Google Sheets (non-free)). +- At this point you switch to spreadsheet editing mode. The editor is + named ``o-spreadsheet`` and looks like another common spreadsheet web + editors. (OnlyOffice, Ethercalc, Google Sheets (non-free)). |image2| -- You can use common functions ``SUM()``, ``AVERAGE()``, etc. in the - cells. For a complete list of functions and their syntax, Refer to the - documentation https://github.com/odoo/o-spreadsheet/ or go to - https://odoo.github.io/o-spreadsheet/ and click on "Insert > - Function". +- You can use common functions ``SUM()``, ``AVERAGE()``, etc. in the + cells. For a complete list of functions and their syntax, Refer to + the documentation https://github.com/odoo/o-spreadsheet/ or go to + https://odoo.github.io/o-spreadsheet/ and click on "Insert > + Function". |image3| -- Note: Business Odoo module can add "business functions". This is - currently the case for the accounting module, which adds the following - features: - - - ``ODOO.CREDIT(account_codes, date_range)``: Get the total credit - for the specified account(s) and period. - - ``ODOO.DEBIT(account_codes, date_range)``: Get the total debit - for the specified account(s) and period. - - ``ODOO.BALANCE(account_codes, date_range)``: Get the total - balance for the specified account(s) and period. - - ``ODOO.FISCALYEAR.START(day)``: Returns the starting date of the - fiscal year encompassing the provided date. - - ``ODOO.FISCALYEAR.END(day)``: Returns the ending date of the - fiscal year encompassing the provided date. - - ``ODOO.ACCOUNT.GROUP(type)``: Returns the account ids of a given - group where type should be a value of the ``account_type`` field - of ``account.account`` model. (``income``, ``asset_receivable``, - etc.) +- Note: Business Odoo module can add "business functions". This is + currently the case for the accounting module, which adds the + following features: + + - ``ODOO.CREDIT(account_codes, date_range)``: Get the total + credit for the specified account(s) and period. + - ``ODOO.DEBIT(account_codes, date_range)``: Get the total debit + for the specified account(s) and period. + - ``ODOO.BALANCE(account_codes, date_range)``: Get the total + balance for the specified account(s) and period. + - ``ODOO.FISCALYEAR.START(day)``: Returns the starting date of + the fiscal year encompassing the provided date. + - ``ODOO.FISCALYEAR.END(day)``: Returns the ending date of the + fiscal year encompassing the provided date. + - ``ODOO.ACCOUNT.GROUP(type)``: Returns the account ids of a + given group where type should be a value of the + ``account_type`` field of ``account.account`` model. + (``income``, ``asset_receivable``, etc.) **Create a new dynamic spreadsheet from pivot** ----------------------------------------------- -- Go to any pivot -- Press on insert button -- Select the dynamic rows or dynamic columns option and set a number of - rows/columns +- Go to any pivot +- Press on insert button +- Select the dynamic rows or dynamic columns option and set a number of + rows/columns A new table that will be updated with the actual or filtered values will be added. -- Note: When a pivot has multiple levels of aggrupations in the rows or - the columns, the number of rows/columns selected will be transfered to - each level. +- Note: When a pivot has multiple levels of aggrupations in the rows or + the columns, the number of rows/columns selected will be transfered + to each level. - Example: number of groups -> 2 number of rows -> 3 + Example: number of groups -> 2 number of rows -> 3 - - val1 + - val1 - - subval1.1 - - subval1.2 - - subval1.3 + - subval1.1 + - subval1.2 + - subval1.3 - - val2 + - val2 - - subval2.1 - - subval2.2 - - subval2.3 + - subval2.1 + - subval2.2 + - subval2.3 - - val3 + - val3 - - subval3.1 - - subval3.2 - - subval3.3 + - subval3.1 + - subval3.2 + - subval3.3 -https://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/Hhttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/ehttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/rhttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/ehttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/ https://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/ihttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/shttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/ https://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/ahttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/ https://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/vhttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/ihttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/shttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/uhttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/ahttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/lhttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/ https://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/ehttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/xhttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/ahttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/phttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/lhttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/ehttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/ https://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/ohttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/fhttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/ https://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/uhttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/shttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/ehttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/:https://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/ https://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/.https://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/.https://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/ https://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/fhttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/ihttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/ghttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/uhttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/rhttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/ehttps://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/:https://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/:https://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/ -https://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/../static/description/spreadsheetdynamic_table.gif +https://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/Hhttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/ehttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/rhttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/ehttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/ https://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/ihttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/shttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/ https://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/ahttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/ https://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/vhttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/ihttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/shttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/uhttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/ahttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/lhttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/ https://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/ehttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/xhttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/ahttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/phttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/lhttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/ehttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/ https://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/ohttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/fhttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/ https://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/uhttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/shttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/ehttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/:https://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/ https://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/.https://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/.https://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/ https://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/fhttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/ihttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/ghttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/uhttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/rhttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/ehttps://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/:https://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/:https://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/ +https://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/../static/description/spreadsheetdynamic_table.gif -.. |image1| image:: https://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/static/description/spreadsheet_create.png -.. |image2| image:: https://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/static/description/spreadsheet_edit.png -.. |image3| image:: https://raw.githubusercontent.com/OCA/spreadsheet/17.0/spreadsheet_oca/static/description/o-spreadsheet.png +.. |image1| image:: https://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/static/description/spreadsheet_create.png +.. |image2| image:: https://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/static/description/spreadsheet_edit.png +.. |image3| image:: https://raw.githubusercontent.com/OCA/spreadsheet/18.0/spreadsheet_oca/static/description/o-spreadsheet.png Development =========== @@ -154,7 +150,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -169,14 +165,14 @@ Authors Contributors ------------ -- Enric Tobella -- `Tecnativa `__: +- Enric Tobella +- `Tecnativa `__: - - Carlos Roca + - Carlos Roca -- `Open User Systems `__: +- `Open User Systems `__: - - Chris Mann + - Chris Mann Maintainers ----------- @@ -191,6 +187,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -This module is part of the `OCA/spreadsheet `_ project on GitHub. +This module is part of the `OCA/spreadsheet `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/spreadsheet_oca/data/spreadsheet_spreadsheet_import_mode.xml b/spreadsheet_oca/data/spreadsheet_spreadsheet_import_mode.xml index 14335c7f..1ee1db44 100644 --- a/spreadsheet_oca/data/spreadsheet_spreadsheet_import_mode.xml +++ b/spreadsheet_oca/data/spreadsheet_spreadsheet_import_mode.xml @@ -1,39 +1,30 @@ + id="spreadsheet_import_mode_new" + model="spreadsheet.spreadsheet.import.mode" + > new Create spreadsheet - + + id="spreadsheet_import_mode_add" + model="spreadsheet.spreadsheet.import.mode" + > add Add to spreadsheet - + + id="spreadsheet_import_mode_add_sheet" + model="spreadsheet.spreadsheet.import.mode" + > add_sheet Add sheet to spreadsheet - + diff --git a/spreadsheet_oca/demo/demo_spreadsheet.json b/spreadsheet_oca/demo/demo_spreadsheet.json index fde6c594..79a0811d 100644 --- a/spreadsheet_oca/demo/demo_spreadsheet.json +++ b/spreadsheet_oca/demo/demo_spreadsheet.json @@ -10,17 +10,17 @@ "cols": {}, "merges": [], "cells": { - "A1": { "style": 1, "content": "First Value", "border": 1 }, - "A2": { "format": 1, "content": "30.55", "border": 1 }, - "B1": { "style": 1, "content": "Second Value", "border": 1 }, - "B2": { "format": 1, "content": "50", "border": 1 }, - "C1": { "style": 1, "content": "Total", "border": 1 }, - "C2": { "format": 1, "content": "=sum(A2:B2)", "border": 1 }, - "A3": { "border": 2 }, - "B3": { "border": 2 }, - "C3": { "border": 2 }, - "D1": { "border": 3 }, - "D2": { "border": 3 } + "A1": {"style": 1, "content": "First Value", "border": 1}, + "A2": {"format": 1, "content": "30.55", "border": 1}, + "B1": {"style": 1, "content": "Second Value", "border": 1}, + "B2": {"format": 1, "content": "50", "border": 1}, + "C1": {"style": 1, "content": "Total", "border": 1}, + "C2": {"format": 1, "content": "=sum(A2:B2)", "border": 1}, + "A3": {"border": 2}, + "B3": {"border": 2}, + "C3": {"border": 2}, + "D1": {"border": 3}, + "D2": {"border": 3} }, "conditionalFormats": [], "figures": [], @@ -30,8 +30,8 @@ } ], "entities": {}, - "styles": { "1": { "bold": true, "align": "center" } }, - "formats": { "1": "[$$]#,##0.00" }, + "styles": {"1": {"bold": true, "align": "center"}}, + "formats": {"1": "[$$]#,##0.00"}, "borders": { "1": { "top": ["thin", "#000"], @@ -39,8 +39,8 @@ "left": ["thin", "#000"], "right": ["thin", "#000"] }, - "2": { "top": ["thin", "#000"] }, - "3": { "left": ["thin", "#000"] } + "2": {"top": ["thin", "#000"]}, + "3": {"left": ["thin", "#000"]} }, "revisionId": "START_REVISION", "uniqueFigureIds": true, diff --git a/spreadsheet_oca/demo/spreadsheet_spreadsheet.xml b/spreadsheet_oca/demo/spreadsheet_spreadsheet.xml index 798da876..11222ed5 100644 --- a/spreadsheet_oca/demo/spreadsheet_spreadsheet.xml +++ b/spreadsheet_oca/demo/spreadsheet_spreadsheet.xml @@ -3,10 +3,9 @@ Demo spreadsheet - + name="spreadsheet_binary_data" + type="base64" + file="spreadsheet_oca/demo/demo_spreadsheet.json" + /> diff --git a/spreadsheet_oca/security/security.xml b/spreadsheet_oca/security/security.xml index 2f37faa8..aa94100a 100644 --- a/spreadsheet_oca/security/security.xml +++ b/spreadsheet_oca/security/security.xml @@ -2,7 +2,6 @@ - Spreadsheet 99 @@ -16,16 +15,14 @@ + name="users" + eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]" + /> Spreadsheet Company Rule - [('company_id', 'in', company_ids + [False])] + [('company_id', 'in', company_ids + [False])] Spreadsheet Owner @@ -38,8 +35,8 @@ ['|', ('contributor_ids','=', user.id), ('contributor_group_ids','in', user.groups_id.ids)] + name="domain_force" + >['|', ('contributor_ids','=', user.id), ('contributor_group_ids','in', user.groups_id.ids)] @@ -48,8 +45,8 @@ ['|', ('reader_ids','=', user.id), ('reader_group_ids','in', user.groups_id.ids)] + name="domain_force" + >['|', ('reader_ids','=', user.id), ('reader_group_ids','in', user.groups_id.ids)] @@ -57,20 +54,12 @@ Spreadsheet Manager - + [(1,'=', 1)] Spreadsheet Import mode - - [('group_ids','in', user.groups_id.ids)] + + [('group_ids','in', user.groups_id.ids)] diff --git a/spreadsheet_oca/static/src/pivot/pivot_table.esm.js b/spreadsheet_oca/static/src/pivot/pivot_table.esm.js index 7dc50bd8..3202d542 100644 --- a/spreadsheet_oca/static/src/pivot/pivot_table.esm.js +++ b/spreadsheet_oca/static/src/pivot/pivot_table.esm.js @@ -1,67 +1,67 @@ /** @odoo-module */ /* Copyright 2024 Tecnativa - Carlos Roca * License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). */ -import { SpreadsheetPivotTable } from "@spreadsheet/pivot/pivot_table"; -import { patch } from "@web/core/utils/patch"; +import {SpreadsheetPivotTable} from "@spreadsheet/pivot/pivot_table"; +import {patch} from "@web/core/utils/patch"; patch(SpreadsheetPivotTable.prototype, { - get _dynamic_cols() { - return this._cols[this._cols.length - 1] === "dynamic_cols"; - }, - getNumberOfMeasures() { - if (this._dynamic_cols) { - // We have disabled the computation of the last column to prevent showing - // the totalization of the columns when they are of a dynamic type. If - // shown, it would display the total of all records corresponding to the - // row, rather than what is being displayed on the screen. - return 1; - } - return this._super(...arguments); - }, - getColHeaders() { - if (this._dynamic_cols) { - // We return a copy of this._cols without the last entry of the array, - // which indicates that these are dynamic columns. - var cols = [...this._cols]; - cols.pop(); - return cols; - } - return this._super(...arguments); - }, - getMeasureHeaders() { - if (this._dynamic_cols) { - return this._cols[this._cols.length - 2]; - } - return this._super(...arguments); - }, - getColWidth() { - if (this._dynamic_cols) { - return this._cols[this._cols.length - 2].length; - } - return this._super(...arguments); - }, - getColHeight() { - if (this._dynamic_cols) { - return this._cols.length - 1; - } - return this._super(...arguments); - }, - getColMeasureIndex(values) { - if (this._dynamic_cols) { - var cols = [...this._cols]; - cols.pop(); - const vals = JSON.stringify(values); - const maxLength = Math.max(...cols.map((col) => col.length)); - for (let i = 0; i < maxLength; i++) { - const cellValues = cols.map((col) => - JSON.stringify((col[i] || {}).values) - ); - if (cellValues.includes(vals)) { - return i; + get _dynamic_cols() { + return this._cols[this._cols.length - 1] === "dynamic_cols"; + }, + getNumberOfMeasures() { + if (this._dynamic_cols) { + // We have disabled the computation of the last column to prevent showing + // the totalization of the columns when they are of a dynamic type. If + // shown, it would display the total of all records corresponding to the + // row, rather than what is being displayed on the screen. + return 1; } - } - return -1; - } - return this._super(...arguments); - }, + return this._super(...arguments); + }, + getColHeaders() { + if (this._dynamic_cols) { + // We return a copy of this._cols without the last entry of the array, + // which indicates that these are dynamic columns. + var cols = [...this._cols]; + cols.pop(); + return cols; + } + return this._super(...arguments); + }, + getMeasureHeaders() { + if (this._dynamic_cols) { + return this._cols[this._cols.length - 2]; + } + return this._super(...arguments); + }, + getColWidth() { + if (this._dynamic_cols) { + return this._cols[this._cols.length - 2].length; + } + return this._super(...arguments); + }, + getColHeight() { + if (this._dynamic_cols) { + return this._cols.length - 1; + } + return this._super(...arguments); + }, + getColMeasureIndex(values) { + if (this._dynamic_cols) { + var cols = [...this._cols]; + cols.pop(); + const vals = JSON.stringify(values); + const maxLength = Math.max(...cols.map((col) => col.length)); + for (let i = 0; i < maxLength; i++) { + const cellValues = cols.map((col) => + JSON.stringify((col[i] || {}).values) + ); + if (cellValues.includes(vals)) { + return i; + } + } + return -1; + } + return this._super(...arguments); + }, }); diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/chart_panel.esm.js b/spreadsheet_oca/static/src/spreadsheet/bundle/chart_panel.esm.js index c4c8b689..a98cf157 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/chart_panel.esm.js +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/chart_panel.esm.js @@ -1,43 +1,43 @@ /** @odoo-module */ import * as spreadsheet from "@odoo/o-spreadsheet"; -import { patch } from "@web/core/utils/patch"; +import {patch} from "@web/core/utils/patch"; -const { chartRegistry } = spreadsheet.registries; -const { ChartPanel } = spreadsheet.components; +const {chartRegistry} = spreadsheet.registries; +const {ChartPanel} = spreadsheet.components; export function isOdooKey(code) { - return code.startsWith("odoo_"); + return code.startsWith("odoo_"); } patch(ChartPanel.prototype, { - get chartTypes() { - return this.filterChartTypes(isOdooKey(this.getChartDefinition().type)); - }, + get chartTypes() { + return this.filterChartTypes(isOdooKey(this.getChartDefinition().type)); + }, - filterChartTypes(isOdoo) { - var result = {}; - for (const key of chartRegistry.getKeys()) { - if ((isOdoo && isOdooKey(key)) || (!isOdoo && !isOdooKey(key))) { - result[key] = chartRegistry.get(key).name; - } - } - return result; - }, - onTypeChange(type) { - if (isOdooKey(this.getChartDefinition().type)) { - const definition = { - stacked: false, - verticalAxisPosition: "left", - ...this.env.model.getters.getChartDefinition(this.figureId), - type, - }; - this.env.model.dispatch("UPDATE_CHART", { - definition, - id: this.figureId, - sheetId: this.env.model.getters.getActiveSheetId(), - }); - } else { - super.onTypeChange(type); - } - }, + filterChartTypes(isOdoo) { + var result = {}; + for (const key of chartRegistry.getKeys()) { + if ((isOdoo && isOdooKey(key)) || (!isOdoo && !isOdooKey(key))) { + result[key] = chartRegistry.get(key).name; + } + } + return result; + }, + onTypeChange(type) { + if (isOdooKey(this.getChartDefinition().type)) { + const definition = { + stacked: false, + verticalAxisPosition: "left", + ...this.env.model.getters.getChartDefinition(this.figureId), + type, + }; + this.env.model.dispatch("UPDATE_CHART", { + definition, + id: this.figureId, + sheetId: this.env.model.getters.getActiveSheetId(), + }); + } else { + super.onTypeChange(type); + } + }, }); diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/chart_panels.esm.js b/spreadsheet_oca/static/src/spreadsheet/bundle/chart_panels.esm.js index 47f60691..8bda4972 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/chart_panels.esm.js +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/chart_panels.esm.js @@ -1,99 +1,96 @@ /** @odoo-module */ import * as spreadsheet from "@odoo/o-spreadsheet"; -import { Domain } from "@web/core/domain"; +import {Domain} from "@web/core/domain"; -import { Many2XAutocomplete } from "@web/views/fields/relational_utils"; -import { patch } from "@web/core/utils/patch"; -import { useService } from "@web/core/utils/hooks"; -import { _t } from "@web/core/l10n/translation"; +import {Many2XAutocomplete} from "@web/views/fields/relational_utils"; +import {patch} from "@web/core/utils/patch"; +import {useService} from "@web/core/utils/hooks"; +import {_t} from "@web/core/l10n/translation"; -const { - LineBarPieConfigPanel, - ScorecardChartConfigPanel, - GaugeChartConfigPanel, -} = spreadsheet.components; +const {LineBarPieConfigPanel, ScorecardChartConfigPanel, GaugeChartConfigPanel} = + spreadsheet.components; const menuChartProps = { - setup() { - super.setup(...arguments); - this.menus = useService("menu"); - }, - get menuProps() { - const menu = this.env.model.getters.getChartOdooMenu(this.props.figureId); - var result = { - fieldString: _t("Menu Items"), - resModel: "ir.ui.menu", - update: this.updateMenu.bind(this), - activeActions: {}, - getDomain: this.getDomain.bind(this), - }; - if (menu) { - result.value = menu.name; - result.id = menu.id; - } - return result; - }, + setup() { + super.setup(...arguments); + this.menus = useService("menu"); + }, + get menuProps() { + const menu = this.env.model.getters.getChartOdooMenu(this.props.figureId); + var result = { + fieldString: _t("Menu Items"), + resModel: "ir.ui.menu", + update: this.updateMenu.bind(this), + activeActions: {}, + getDomain: this.getDomain.bind(this), + }; + if (menu) { + result.value = menu.name; + result.id = menu.id; + } + return result; + }, - getDomain() { - const menus = this.menus - .getAll() - .map((menu) => menu.id) - .filter((menuId) => menuId !== "root"); - return [["id", "in", menus]]; - }, - get menuId() { - const menu = this.env.model.getters.getChartOdooMenu(this.props.figureId); - if (menu) { - return [menu.id, menu.name]; - } - return false; - }, - updateMenu(menuId) { - if (!menuId) { - this.env.model.dispatch("LINK_ODOO_MENU_TO_CHART", { - chartId: this.props.figureId, - odooMenuId: false, - }); - return; - } - const menu = this.env.model.getters.getIrMenu(menuId[0].id); - console.log(menu); - this.env.model.dispatch("LINK_ODOO_MENU_TO_CHART", { - chartId: this.props.figureId, - odooMenuId: menu.xmlid || menu.id, - }); - }, - get record() { - const menus = this.menus - .getAll() - .map((menu) => menu.id) - .filter((menuId) => menuId !== "root"); - return { - getFieldDomain: function () { - return new Domain([["id", "in", menus]]); - }, - getFieldContext: function () { - return {}; - }, - }; - }, + getDomain() { + const menus = this.menus + .getAll() + .map((menu) => menu.id) + .filter((menuId) => menuId !== "root"); + return [["id", "in", menus]]; + }, + get menuId() { + const menu = this.env.model.getters.getChartOdooMenu(this.props.figureId); + if (menu) { + return [menu.id, menu.name]; + } + return false; + }, + updateMenu(menuId) { + if (!menuId) { + this.env.model.dispatch("LINK_ODOO_MENU_TO_CHART", { + chartId: this.props.figureId, + odooMenuId: false, + }); + return; + } + const menu = this.env.model.getters.getIrMenu(menuId[0].id); + console.log(menu); + this.env.model.dispatch("LINK_ODOO_MENU_TO_CHART", { + chartId: this.props.figureId, + odooMenuId: menu.xmlid || menu.id, + }); + }, + get record() { + const menus = this.menus + .getAll() + .map((menu) => menu.id) + .filter((menuId) => menuId !== "root"); + return { + getFieldDomain: function () { + return new Domain([["id", "in", menus]]); + }, + getFieldContext: function () { + return {}; + }, + }; + }, }; patch(LineBarPieConfigPanel.prototype, menuChartProps); LineBarPieConfigPanel.components = { - ...LineBarPieConfigPanel.components, - Many2XAutocomplete, + ...LineBarPieConfigPanel.components, + Many2XAutocomplete, }; patch(ScorecardChartConfigPanel.prototype, menuChartProps); ScorecardChartConfigPanel.components = { - ...ScorecardChartConfigPanel.components, - Many2XAutocomplete, + ...ScorecardChartConfigPanel.components, + Many2XAutocomplete, }; patch(GaugeChartConfigPanel.prototype, menuChartProps); GaugeChartConfigPanel.components = { - ...GaugeChartConfigPanel.components, - Many2XAutocomplete, + ...GaugeChartConfigPanel.components, + Many2XAutocomplete, }; diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js b/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js index 99f9677d..7076f88f 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js @@ -1,197 +1,195 @@ /** @odoo-module **/ import * as spreadsheet from "@odoo/o-spreadsheet"; -import { Component, onWillStart, useState } from "@odoo/owl"; -import { _lt, _t } from "@web/core/l10n/translation"; -import { FilterValue } from "@spreadsheet/global_filters/components/filter_value/filter_value"; -import { ModelFieldSelector } from "@web/core/model_field_selector/model_field_selector"; -import { ModelSelector } from "@web/core/model_selector/model_selector"; -import { RELATIVE_DATE_RANGE_TYPES } from "@spreadsheet/helpers/constants"; -import { globalFiltersFieldMatchers } from "@spreadsheet/global_filters/plugins/global_filters_core_plugin"; -import { useService } from "@web/core/utils/hooks"; +import {Component, onWillStart, useState} from "@odoo/owl"; +import {_lt, _t} from "@web/core/l10n/translation"; +import {FilterValue} from "@spreadsheet/global_filters/components/filter_value/filter_value"; +import {ModelFieldSelector} from "@web/core/model_field_selector/model_field_selector"; +import {ModelSelector} from "@web/core/model_selector/model_selector"; +import {RELATIVE_DATE_RANGE_TYPES} from "@spreadsheet/helpers/constants"; +import {globalFiltersFieldMatchers} from "@spreadsheet/global_filters/plugins/global_filters_core_plugin"; +import {useService} from "@web/core/utils/hooks"; -const { topbarMenuRegistry } = spreadsheet.registries; +const {topbarMenuRegistry} = spreadsheet.registries; const uuidGenerator = new spreadsheet.helpers.UuidGenerator(); -topbarMenuRegistry.add("file", { name: _t("File"), sequence: 10 }); +topbarMenuRegistry.add("file", {name: _t("File"), sequence: 10}); topbarMenuRegistry.addChild("filters", ["file"], { - name: _t("Filters"), - sequence: 70, - execute: (env) => env.openSidePanel("FilterPanel", {}), - icon: "o-spreadsheet-Icon.GLOBAL_FILTERS", + name: _t("Filters"), + sequence: 70, + execute: (env) => env.openSidePanel("FilterPanel", {}), + icon: "o-spreadsheet-Icon.GLOBAL_FILTERS", }); topbarMenuRegistry.addChild("save", ["file"], { - name: _t("Save"), - // Description: "Ctrl+S", // This is not working, so removing it from the view for now... - sequence: 10, - execute: (env) => env.saveSpreadsheet(), - icon: "o-spreadsheet-Icon.DOWNLOAD", + name: _t("Save"), + // Description: "Ctrl+S", // This is not working, so removing it from the view for now... + sequence: 10, + execute: (env) => env.saveSpreadsheet(), + icon: "o-spreadsheet-Icon.DOWNLOAD", }); topbarMenuRegistry.addChild("download", ["file"], { - name: _t("Download XLSX"), - sequence: 20, - execute: (env) => env.downloadAsXLXS(), - icon: "o-spreadsheet-Icon.EXPORT_XLSX", + name: _t("Download XLSX"), + sequence: 20, + execute: (env) => env.downloadAsXLXS(), + icon: "o-spreadsheet-Icon.EXPORT_XLSX", }); topbarMenuRegistry.addChild("settings", ["file"], { - name: _t("Settings"), - sequence: 100, - execute: (env) => env.openSidePanel("Settings"), - icon: "o-spreadsheet-Icon.COG", + name: _t("Settings"), + sequence: 100, + execute: (env) => env.openSidePanel("Settings"), + icon: "o-spreadsheet-Icon.COG", }); -const { sidePanelRegistry } = spreadsheet.registries; +const {sidePanelRegistry} = spreadsheet.registries; export class FilterPanel extends Component { - onEditFilter(filter) { - this.env.openSidePanel("EditFilterPanel", { filter }); - } - onAddFilter(type) { - this.env.openSidePanel("EditFilterPanel", { filter: { type: type } }); - } + onEditFilter(filter) { + this.env.openSidePanel("EditFilterPanel", {filter}); + } + onAddFilter(type) { + this.env.openSidePanel("EditFilterPanel", {filter: {type: type}}); + } } FilterPanel.template = "spreadsheet_oca.FilterPanel"; FilterPanel.components = { - FilterValue, + FilterValue, }; sidePanelRegistry.add("FilterPanel", { - title: "Filters", - Body: FilterPanel, + title: "Filters", + Body: FilterPanel, }); export class EditFilterPanel extends Component { - setup() { - this.filterId = this.props.filter; - this.orm = useService("orm"); - this.state = useState({ - label: this.props.filter.label, - type: this.props.filter.type, - defaultValue: this.props.filter.defaultValue, - rangeType: this.props.filter.rangeType || "year", - modelName: { technical: this.props.filter.modelName, label: null }, - objects: {}, - }); - this.relativeDateRangeTypes = RELATIVE_DATE_RANGE_TYPES; - onWillStart(this.willStart.bind(this)); - } - async willStart() { - if (this.state.modelName.technical !== undefined) { - const modelLabel = await this.orm.call("ir.model", "display_name_for", [ - [this.state.modelName.technical], - ]); - this.state.modelName.label = modelLabel[0] && modelLabel[0].display_name; + setup() { + this.filterId = this.props.filter; + this.orm = useService("orm"); + this.state = useState({ + label: this.props.filter.label, + type: this.props.filter.type, + defaultValue: this.props.filter.defaultValue, + rangeType: this.props.filter.rangeType || "year", + modelName: {technical: this.props.filter.modelName, label: null}, + objects: {}, + }); + this.relativeDateRangeTypes = RELATIVE_DATE_RANGE_TYPES; + onWillStart(this.willStart.bind(this)); } - var ModelFields = []; - for (var [objectType, objectClass] of Object.entries( - globalFiltersFieldMatchers - )) { - for (const objectId of objectClass.getIds()) { - var fields = objectClass.getFields(objectId); - this.state.objects[objectType + "_" + objectId] = { - id: objectType + "_" + objectId, - objectId: objectId, - name: objectClass.getDisplayName(objectId), - tag: await objectClass.getTag(objectId), - fieldMatch: - objectClass.getFieldMatching(objectId, this.props.filter.id) || {}, - fields: fields, - type: objectType, - model: objectClass.getModel(objectId), - }; - ModelFields.push(fields); - } + async willStart() { + if (this.state.modelName.technical !== undefined) { + const modelLabel = await this.orm.call("ir.model", "display_name_for", [ + [this.state.modelName.technical], + ]); + this.state.modelName.label = modelLabel[0] && modelLabel[0].display_name; + } + var ModelFields = []; + for (var [objectType, objectClass] of Object.entries( + globalFiltersFieldMatchers + )) { + for (const objectId of objectClass.getIds()) { + var fields = objectClass.getFields(objectId); + this.state.objects[objectType + "_" + objectId] = { + id: objectType + "_" + objectId, + objectId: objectId, + name: objectClass.getDisplayName(objectId), + tag: await objectClass.getTag(objectId), + fieldMatch: + objectClass.getFieldMatching(objectId, this.props.filter.id) || + {}, + fields: fields, + type: objectType, + model: objectClass.getModel(objectId), + }; + ModelFields.push(fields); + } + } + this.models = [ + ...new Set( + ModelFields.map((field_items) => Object.values(field_items)) + .flat() + .filter((field) => field.relation) + .map((field) => field.relation) + ), + ]; + } + get dateRangeTypes() { + return [ + {type: "fixedPeriod", description: _t("Month / Quarter")}, + {type: "relative", description: _t("Relative Period")}, + {type: "from_to", description: _t("From / To")}, + ]; + } + get dateOffset() { + return [ + {value: 0, name: ""}, + {value: -1, name: _lt("Previous")}, + {value: -2, name: _lt("Before Previous")}, + {value: 1, name: _lt("Next")}, + {value: 2, name: _lt("After next")}, + ]; + } + onChangeFieldMatchOffset(object, ev) { + this.state.objects[object.id].fieldMatch.offset = parseInt(ev.target.value, 10); + } + onModelSelected(ev) { + this.state.modelName.technical = ev.technical; + this.state.modelName.label = ev.label; } - this.models = [ - ...new Set( - ModelFields.map((field_items) => Object.values(field_items)) - .flat() - .filter((field) => field.relation) - .map((field) => field.relation) - ), - ]; - } - get dateRangeTypes() { - return [ - { type: "fixedPeriod", description: _t("Month / Quarter") }, - { type: "relative", description: _t("Relative Period") }, - { type: "from_to", description: _t("From / To") }, - ]; - } - get dateOffset() { - return [ - { value: 0, name: "" }, - { value: -1, name: _lt("Previous") }, - { value: -2, name: _lt("Before Previous") }, - { value: 1, name: _lt("Next") }, - { value: 2, name: _lt("After next") }, - ]; - } - onChangeFieldMatchOffset(object, ev) { - this.state.objects[object.id].fieldMatch.offset = parseInt( - ev.target.value, - 10 - ); - } - onModelSelected(ev) { - this.state.modelName.technical = ev.technical; - this.state.modelName.label = ev.label; - } - onDateRangeChange(ev) { - this.state.rangeType = ev.target.value; - this.state.defaultValue = undefined; - } - onSave() { - const action = this.props.filter.id - ? "EDIT_GLOBAL_FILTER" - : "ADD_GLOBAL_FILTER"; - this.env.openSidePanel("FilterPanel", {}); - var filter = { - id: this.props.filter.id || uuidGenerator.uuidv4(), - type: this.state.type, - label: this.state.label, - defaultValue: this.state.defaultValue, - rangeType: this.state.rangeType, - modelName: this.state.modelName.technical, - }; - var filterMatching = {}; - Object.values(this.state.objects).forEach((object) => { - filterMatching[object.type] = filterMatching[object.type] || {}; - filterMatching[object.type][object.objectId] = { ...object.fieldMatch }; - }); - this.env.model.dispatch(action, { - id: filter.id, - filter, - ...filterMatching, - }); + onDateRangeChange(ev) { + this.state.rangeType = ev.target.value; + this.state.defaultValue = undefined; + } + onSave() { + const action = this.props.filter.id + ? "EDIT_GLOBAL_FILTER" + : "ADD_GLOBAL_FILTER"; + this.env.openSidePanel("FilterPanel", {}); + var filter = { + id: this.props.filter.id || uuidGenerator.uuidv4(), + type: this.state.type, + label: this.state.label, + defaultValue: this.state.defaultValue, + rangeType: this.state.rangeType, + modelName: this.state.modelName.technical, + }; + var filterMatching = {}; + Object.values(this.state.objects).forEach((object) => { + filterMatching[object.type] = filterMatching[object.type] || {}; + filterMatching[object.type][object.objectId] = {...object.fieldMatch}; + }); + this.env.model.dispatch(action, { + id: filter.id, + filter, + ...filterMatching, + }); - this.env.openSidePanel("FilterPanel", {}); - } - onCancel() { - this.env.openSidePanel("FilterPanel", {}); - } - onRemove() { - if (this.props.filter.id) { - this.env.model.dispatch("REMOVE_GLOBAL_FILTER", { - id: this.props.filter.id, - }); + this.env.openSidePanel("FilterPanel", {}); + } + onCancel() { + this.env.openSidePanel("FilterPanel", {}); + } + onRemove() { + if (this.props.filter.id) { + this.env.model.dispatch("REMOVE_GLOBAL_FILTER", { + id: this.props.filter.id, + }); + } + this.env.openSidePanel("FilterPanel", {}); + } + onFieldMatchUpdate(object, name) { + this.state.objects[object.id].fieldMatch.chain = name; + this.state.objects[object.id].fieldMatch.type = object.fields[name]?.type; + } + toggleDateDefaultValue(ev) { + this.state.defaultValue = ev.target.checked ? "this_month" : undefined; } - this.env.openSidePanel("FilterPanel", {}); - } - onFieldMatchUpdate(object, name) { - this.state.objects[object.id].fieldMatch.chain = name; - this.state.objects[object.id].fieldMatch.type = object.fields[name]?.type; - } - toggleDateDefaultValue(ev) { - this.state.defaultValue = ev.target.checked ? "this_month" : undefined; - } } EditFilterPanel.template = "spreadsheet_oca.EditFilterPanel"; -EditFilterPanel.components = { ModelSelector, ModelFieldSelector }; +EditFilterPanel.components = {ModelSelector, ModelFieldSelector}; sidePanelRegistry.add("EditFilterPanel", { - title: "Edit Filter", - Body: EditFilterPanel, + title: "Edit Filter", + Body: EditFilterPanel, }); 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 index 5e764e1a..26844d77 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/filter_panel_datasources.esm.js +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/filter_panel_datasources.esm.js @@ -1,304 +1,303 @@ /** @odoo-module **/ import * as spreadsheet from "@odoo/o-spreadsheet"; -import { Component, onWillStart, onWillUpdateProps } from "@odoo/owl"; -import { - makeDynamicCols, - makeDynamicRows, -} from "../utils/dynamic_generators.esm"; -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 { FormViewDialog } from "@web/views/view_dialogs/form_view_dialog"; -import { _t } from "@web/core/l10n/translation"; -import { formatDate } from "@web/core/l10n/dates"; -import { useService } from "@web/core/utils/hooks"; +import {Component, onWillStart, onWillUpdateProps} from "@odoo/owl"; +import {makeDynamicCols, makeDynamicRows} from "../utils/dynamic_generators.esm"; +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 {FormViewDialog} from "@web/views/view_dialogs/form_view_dialog"; +import {_t} from "@web/core/l10n/translation"; +import {formatDate} from "@web/core/l10n/dates"; +import {useService} from "@web/core/utils/hooks"; -const { DateTime } = luxon; -const { sidePanelRegistry, topbarMenuRegistry } = spreadsheet.registries; +const {DateTime} = luxon; +const {sidePanelRegistry, topbarMenuRegistry} = spreadsheet.registries; topbarMenuRegistry.addChild("data_sources", ["data"], (env) => { - let sequence = 100; - const children = env.model.getters.getPivotIds().map((pivotId, index) => ({ - id: `data_source_pivot_ ${pivotId}`, - name: env.model.getters.getPivotDisplayName(pivotId), - sequence: sequence++, - execute: (child_env) => { - child_env.model.dispatch("SELECT_PIVOT", { - pivotId: pivotId, - }); - child_env.openSidePanel("PivotPanel", {}); - }, - icon: "spreadsheet_oca.PivotIcon", - separator: index === env.model.getters.getPivotIds().length - 1, - })); - const lists = env.model.getters.getListIds().map((listId, index) => ({ - id: `data_source_list_${listId}`, - name: env.model.getters.getListDisplayName(listId), - sequence: sequence++, - execute: (child_env) => { - child_env.model.dispatch("SELECT_ODOO_LIST", { listId: listId }); - child_env.openSidePanel("ListPanel", {}); - }, - icon: "spreadsheet_oca.ListIcon", - separator: index === env.model.getters.getListIds().length - 1, - })); - return children.concat(lists).concat([ - { - id: "refresh_all_data", - name: _t("Refresh all data"), - sequence: 110, - execute: (child_env) => { - child_env.model.dispatch("REFRESH_ALL_DATA_SOURCES"); - }, - separator: true, - }, - ]); + let sequence = 100; + const children = env.model.getters.getPivotIds().map((pivotId, index) => ({ + id: `data_source_pivot_ ${pivotId}`, + name: env.model.getters.getPivotDisplayName(pivotId), + sequence: sequence++, + execute: (child_env) => { + child_env.model.dispatch("SELECT_PIVOT", { + pivotId: pivotId, + }); + child_env.openSidePanel("PivotPanel", {}); + }, + icon: "spreadsheet_oca.PivotIcon", + separator: index === env.model.getters.getPivotIds().length - 1, + })); + const lists = env.model.getters.getListIds().map((listId, index) => ({ + id: `data_source_list_${listId}`, + name: env.model.getters.getListDisplayName(listId), + sequence: sequence++, + execute: (child_env) => { + child_env.model.dispatch("SELECT_ODOO_LIST", {listId: listId}); + child_env.openSidePanel("ListPanel", {}); + }, + icon: "spreadsheet_oca.ListIcon", + separator: index === env.model.getters.getListIds().length - 1, + })); + return children.concat(lists).concat([ + { + id: "refresh_all_data", + name: _t("Refresh all data"), + sequence: 110, + execute: (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.getMeasureDisplayName( - sortedColumn.measure - ); - return `${GroupByDisplayLabel} (${orderTranslate})`; - } - get lastUpdate() { - const lastUpdate = this.PivotDataSource.lastUpdate; - if (lastUpdate) { - return formatDate(DateTime.fromMillis(lastUpdate)); + setup() { + this.dialog = useService("dialog"); + onWillStart(this.modelData.bind(this)); + onWillUpdateProps(this.modelData.bind(this)); } - return _t("not updated"); - } - editDomain() { - this.dialog.add(DomainSelectorDialog, { - resModel: this.props.pivotDefinition.model, - domain: this.domain, - readonly: false, - isDebugMode: Boolean(this.env.debug), - onConfirm: 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 }); - } - - async insertDynamicPivot() { - const datasourceModel = await this.env.model.getters - .getPivotDataSource(this.props.pivotId) - .copyModelWithOriginalDomain(); - var { cols, rows, measures } = datasourceModel.getTableStructure().export(); - const { dynamic_rows, number_of_rows, dynamic_cols, number_of_cols } = - await new Promise((resolve) => { - this.dialog.add( - FormViewDialog, - { - title: _t("Select the quantity of rows"), - resModel: "spreadsheet.select.row.number", - context: { - default_can_have_dynamic_cols: Boolean(cols[0][0].fields.length), - }, - onRecordSaved: async (record) => { - resolve({ - dynamic_rows: record.data.dynamic_rows, - number_of_rows: record.data.number_of_rows, - dynamic_cols: record.data.dynamic_cols, - number_of_cols: record.data.number_of_cols, - }); - }, - }, - { onClose: () => resolve(false) } + async modelData() { + this.PivotDataSource = await this.env.model.getters.getAsyncPivotDataSource( + this.props.pivotId ); - }); - if (!dynamic_rows && !dynamic_cols) { - return; + this.modelLabel = await this.PivotDataSource.getModelLabel(); } - if (dynamic_rows) { - const indentations = rows.map((r) => r.indent); - const max_indentation = Math.max(...indentations); - rows = makeDynamicRows( - this.props.pivotDefinition.rowGroupBys, - number_of_rows, - 1, - max_indentation - ); + get domain() { + return new Domain(this.props.pivotDefinition.domain).toString(); } - if (dynamic_cols) { - cols = makeDynamicCols( - this.props.pivotDefinition.colGroupBys, - number_of_cols, - this.props.pivotDefinition.measures - ); + get pivotDimensions() { + return [ + ...this.props.pivotDefinition.rowGroupBys, + ...this.props.pivotDefinition.colGroupBys, + ].map((fieldName) => this.PivotDataSource.getFormattedGroupBy(fieldName)); } - const table = { - cols, - rows, - measures, - }; - 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, - }); - this.env.model.dispatch("REFRESH_PIVOT", { id: this.props.pivotId }); - } - delete() { - this.env.askConfirmation( - _t("Are you sure you want to delete this pivot?"), - () => { - this.env.model.dispatch("REMOVE_PIVOT", { - pivotId: this.props.pivotId, + get sortInformation() { + const sortedColumn = this.props.pivotDefinition.sortedColumn; + const orderTranslate = + sortedColumn.order === "asc" ? _t("ascending") : _t("descending"); + const GroupByDisplayLabel = this.PivotDataSource.getMeasureDisplayName( + sortedColumn.measure + ); + return `${GroupByDisplayLabel} (${orderTranslate})`; + } + get lastUpdate() { + const lastUpdate = this.PivotDataSource.lastUpdate; + if (lastUpdate) { + return formatDate(DateTime.fromMillis(lastUpdate)); + } + return _t("not updated"); + } + editDomain() { + this.dialog.add(DomainSelectorDialog, { + resModel: this.props.pivotDefinition.model, + domain: this.domain, + readonly: false, + isDebugMode: Boolean(this.env.debug), + onConfirm: 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}); + } + + async insertDynamicPivot() { + const datasourceModel = await this.env.model.getters + .getPivotDataSource(this.props.pivotId) + .copyModelWithOriginalDomain(); + var {cols, rows, measures} = datasourceModel.getTableStructure().export(); + const {dynamic_rows, number_of_rows, dynamic_cols, number_of_cols} = + await new Promise((resolve) => { + this.dialog.add( + FormViewDialog, + { + title: _t("Select the quantity of rows"), + resModel: "spreadsheet.select.row.number", + context: { + default_can_have_dynamic_cols: Boolean( + cols[0][0].fields.length + ), + }, + onRecordSaved: async (record) => { + resolve({ + dynamic_rows: record.data.dynamic_rows, + number_of_rows: record.data.number_of_rows, + dynamic_cols: record.data.dynamic_cols, + number_of_cols: record.data.number_of_cols, + }); + }, + }, + {onClose: () => resolve(false)} + ); + }); + if (!dynamic_rows && !dynamic_cols) { + return; + } + if (dynamic_rows) { + const indentations = rows.map((r) => r.indent); + const max_indentation = Math.max(...indentations); + rows = makeDynamicRows( + this.props.pivotDefinition.rowGroupBys, + number_of_rows, + 1, + max_indentation + ); + } + if (dynamic_cols) { + cols = makeDynamicCols( + this.props.pivotDefinition.colGroupBys, + number_of_cols, + this.props.pivotDefinition.measures + ); + } + const table = { + cols, + rows, + measures, + }; + 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, + }); + this.env.model.dispatch("REFRESH_PIVOT", {id: this.props.pivotId}); + } + delete() { + this.env.askConfirmation( + _t("Are you sure you want to delete this pivot?"), + () => { + this.env.model.dispatch("REMOVE_PIVOT", { + pivotId: this.props.pivotId, + }); + } + ); + } } PivotPanelDisplay.template = "spreadsheet_oca.PivotPanelDisplay"; PivotPanelDisplay.components = { - DomainSelector, + DomainSelector, }; PivotPanelDisplay.properties = { - pivotId: String, - pivotDefinition: Object, + 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); - } + 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, + PivotPanelDisplay, }; sidePanelRegistry.add("PivotPanel", { - title: "Pivot table information", - Body: 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 formatDate(DateTime.fromMillis(lastUpdate)); + 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 formatDate(DateTime.fromMillis(lastUpdate)); + } + return _t("not updated"); } - return _t("not updated"); - } - editDomain() { - this.dialog.add(DomainSelectorDialog, { - resModel: this.props.listDefinition.model, - domain: this.domain, - readonly: false, - isDebugMode: Boolean(this.env.debug), - onConfirm: this.onSelectDomain.bind(this), - }); - } - onSelectDomain(domain) { - this.env.model.dispatch("UPDATE_ODOO_LIST_DOMAIN", { - listId: this.props.listId, - domain: new Domain(domain).toList(), - }); - } - delete() { - this.env.askConfirmation( - _t("Are you sure you want to delete this list?"), - () => { - this.env.model.dispatch("REMOVE_ODOO_LIST", { - listId: this.props.listId, + editDomain() { + this.dialog.add(DomainSelectorDialog, { + resModel: this.props.listDefinition.model, + domain: this.domain, + readonly: false, + isDebugMode: Boolean(this.env.debug), + onConfirm: this.onSelectDomain.bind(this), }); - } - ); - } + } + onSelectDomain(domain) { + this.env.model.dispatch("UPDATE_ODOO_LIST_DOMAIN", { + listId: this.props.listId, + domain: new Domain(domain).toList(), + }); + } + delete() { + this.env.askConfirmation( + _t("Are you sure you want to delete this list?"), + () => { + this.env.model.dispatch("REMOVE_ODOO_LIST", { + listId: this.props.listId, + }); + } + ); + } } ListPanelDisplay.template = "spreadsheet_oca.ListPanelDisplay"; ListPanelDisplay.components = { - DomainSelector, + DomainSelector, }; ListPanelDisplay.properties = { - listId: String, - listDefinition: Object, + 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); - } + 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, + ListPanelDisplay, }; sidePanelRegistry.add("ListPanel", { - title: "List information", - Body: ListPanel, + title: "List information", + Body: ListPanel, }); diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/odoo_panels.esm.js b/spreadsheet_oca/static/src/spreadsheet/bundle/odoo_panels.esm.js index 7bb60767..92785576 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/odoo_panels.esm.js +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/odoo_panels.esm.js @@ -1,99 +1,99 @@ /** @odoo-module */ import * as spreadsheet from "@odoo/o-spreadsheet"; -import { Domain } from "@web/core/domain"; -import { Many2XAutocomplete } from "@web/views/fields/relational_utils"; -import { useService } from "@web/core/utils/hooks"; -import { _t } from "@web/core/l10n/translation"; +import {Domain} from "@web/core/domain"; +import {Many2XAutocomplete} from "@web/views/fields/relational_utils"; +import {useService} from "@web/core/utils/hooks"; +import {_t} from "@web/core/l10n/translation"; -const { chartSidePanelComponentRegistry } = spreadsheet.registries; -const { LineBarPieDesignPanel } = spreadsheet.components; -const { Component } = owl; +const {chartSidePanelComponentRegistry} = spreadsheet.registries; +const {LineBarPieDesignPanel} = spreadsheet.components; +const {Component} = owl; export class OdooPanel extends Component { - setup() { - this.menus = useService("menu"); - } - get menuProps() { - const menu = this.env.model.getters.getChartOdooMenu(this.props.figureId); - var result = { - fieldString: _t("Menu Items"), - resModel: "ir.ui.menu", - update: this.updateMenu.bind(this), - activeActions: {}, - getDomain: this.getDomain.bind(this), - }; - if (menu) { - result.value = menu.name; - result.id = menu.id; + setup() { + this.menus = useService("menu"); } - return result; - } - getDomain() { - const menus = this.menus - .getAll() - .map((menu) => menu.id) - .filter((menuId) => menuId !== "root"); - return [["id", "in", menus]]; - } - get menuId() { - const menu = this.env.model.getters.getChartOdooMenu(this.props.figureId); - if (menu) { - return [menu.id, menu.name]; + get menuProps() { + const menu = this.env.model.getters.getChartOdooMenu(this.props.figureId); + var result = { + fieldString: _t("Menu Items"), + resModel: "ir.ui.menu", + update: this.updateMenu.bind(this), + activeActions: {}, + getDomain: this.getDomain.bind(this), + }; + if (menu) { + result.value = menu.name; + result.id = menu.id; + } + return result; } - return false; - } - updateMenu(menuId) { - if (!menuId) { - this.env.model.dispatch("LINK_ODOO_MENU_TO_CHART", { - chartId: this.props.figureId, - odooMenuId: false, - }); - return; + getDomain() { + const menus = this.menus + .getAll() + .map((menu) => menu.id) + .filter((menuId) => menuId !== "root"); + return [["id", "in", menus]]; + } + get menuId() { + const menu = this.env.model.getters.getChartOdooMenu(this.props.figureId); + if (menu) { + return [menu.id, menu.name]; + } + return false; + } + updateMenu(menuId) { + if (!menuId) { + this.env.model.dispatch("LINK_ODOO_MENU_TO_CHART", { + chartId: this.props.figureId, + odooMenuId: false, + }); + return; + } + const menu = this.env.model.getters.getIrMenu(menuId[0].id); + this.env.model.dispatch("LINK_ODOO_MENU_TO_CHART", { + chartId: this.props.figureId, + odooMenuId: menu.xmlid || menu.id, + }); + } + get record() { + const menus = this.menus + .getAll() + .map((menu) => menu.id) + .filter((menuId) => menuId !== "root"); + return { + getFieldDomain: function () { + return new Domain([["id", "in", menus]]); + }, + getFieldContext: function () { + return {}; + }, + }; } - const menu = this.env.model.getters.getIrMenu(menuId[0].id); - this.env.model.dispatch("LINK_ODOO_MENU_TO_CHART", { - chartId: this.props.figureId, - odooMenuId: menu.xmlid || menu.id, - }); - } - get record() { - const menus = this.menus - .getAll() - .map((menu) => menu.id) - .filter((menuId) => menuId !== "root"); - return { - getFieldDomain: function () { - return new Domain([["id", "in", menus]]); - }, - getFieldContext: function () { - return {}; - }, - }; - } } OdooPanel.template = "spreadsheet_oca.OdooPanel"; -OdooPanel.components = { Many2XAutocomplete }; +OdooPanel.components = {Many2XAutocomplete}; class OdooStackablePanel extends OdooPanel { - onChangeStacked(ev) { - this.props.updateChart(this.props.figureId, { - stacked: ev.target.checked, - }); - } + onChangeStacked(ev) { + this.props.updateChart(this.props.figureId, { + stacked: ev.target.checked, + }); + } } OdooStackablePanel.template = "spreadsheet_oca.OdooStackablePanel"; chartSidePanelComponentRegistry - .add("odoo_line", { - configuration: OdooStackablePanel, - design: LineBarPieDesignPanel, - }) - .add("odoo_bar", { - configuration: OdooStackablePanel, - design: LineBarPieDesignPanel, - }) - .add("odoo_pie", { - configuration: OdooPanel, - design: LineBarPieDesignPanel, - }); + .add("odoo_line", { + configuration: OdooStackablePanel, + design: LineBarPieDesignPanel, + }) + .add("odoo_bar", { + configuration: OdooStackablePanel, + design: LineBarPieDesignPanel, + }) + .add("odoo_pie", { + configuration: OdooPanel, + design: LineBarPieDesignPanel, + }); diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml index 5b15b9e1..45549754 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml @@ -3,146 +3,122 @@
- - + +
- - - + +
Pivot name
- + class="o_spreadsheet_oca_datasource_panel_field_title" + >Pivot name
+
-
Model
+
Model
()
+ t-esc="props.pivotDefinition.model" + />)
-
Domain
+
Domain
+ resModel="props.pivotDefinition.model" + domain="domain" + t-key="'pivot_' + props.pivotId" + />
Dimensions
- + class="o_spreadsheet_oca_datasource_panel_field_title" + >Dimensions
+
+ class="o_spreadsheet_oca_datasource_panel_field" + t-if="props.pivotDefinition.sortedColumn" + >
Sorting
+ class="o_spreadsheet_oca_datasource_panel_field_title" + >Sorting
Measures
+ class="o_spreadsheet_oca_datasource_panel_field_title" + >Measures
+ t-foreach="props.pivotDefinition.measures" + t-as="measure" + t-key="measure" + >
Last updated at
-
+
+ t-on-click="insertPivot" + class="btn btn-info" + >Insert pivot - + t-on-click="insertDynamicPivot" + class="btn btn-success" + >Insert dynamic pivot +
-
- - +
List name
- + class="o_spreadsheet_oca_datasource_panel_field_title" + >List name
+
-
Model
+
Model
()
+ t-esc="props.listDefinition.model" + />)
-
Domain
+
Domain
+ resModel="props.listDefinition.model" + domain="domain" + t-key="'list_' + props.listId" + /> @@ -150,48 +126,45 @@
Last updated at
-
+
+ t-on-click="() => this.delete()" + class="btn btn-danger" + >Delete
-
+ t-foreach="env.model.getters.getGlobalFilters()" + t-as="filter" + t-key="filter.id" + class="o_spreadsheet_oca_filter" + >
+ class="fa fa-cog btn btn-link text-muted spreadsheet_oca_filter_value_edit" + t-on-click="() => this.onEditFilter(filter)" + />
+ t-on-click="() => this.onAddFilter('date')" + class="btn" + >Add date + t-on-click="() => this.onAddFilter('relation')" + class="btn" + >Add relation + t-on-click="() => this.onAddFilter('text')" + class="btn" + >Add text
@@ -207,19 +180,15 @@
Time range
+ type="checkbox" + class="me-2" + id="default_to_current_period" + t-att-checked="state.defaultValue" + t-on-change="toggleDateDefaultValue" + /> + for="default_to_current_period" + > Automatically filter on the current period
+ class="pt-3" + t-if="state.defaultValue and state.type === 'date' and state.rangeType !== 'relative'" + >
+ t-if="state.type === 'date' and state.rangeType === 'relative'" + class="spreadsheet_oca_filter_value" + >
+ t-if="state.type === 'text'" + type="text" + class="o_input o_global_filter_default_value" + t-model="state.defaultValue" + />
Related model
+ value="state.modelName.label or ''" + models="models" + onModelSelected.bind="onModelSelected" + />
+ class="o_spreadsheet_oca_filter" + t-foreach="Object.values(state.objects)" + t-as="object" + t-key="object.id" + t-if="object.model" + >
-
-
+
+ path="object.fieldMatch.chain || ''" + resModel="object.model" + readonly="false" + allowEmpty="true" + isDebugMode="!!env.debug" + update="(name) => this.onFieldMatchUpdate(object, name)" + t-key="object_index" + />
-
+
+ type="text" + class="o_spreadsheet_oca_name" + title="Change name" + t-ref="spreadsheetName" + t-att-value="state.name" + t-on-change="_onNameChanged" + /> name is required + class="text-danger o_spreadsheet_oca_name_warning" + t-if="! state.name" + > name is required @@ -398,82 +352,79 @@ - +
+ t-if="collapsedBreadcrumbs.length || visiblePathBreadcrumbs.length" + class="o_breadcrumb d-flex flex-row flex-md-column align-self-stretch justify-content-between min-w-0" + > + t-set="previousBreadcrumb" + t-value="visiblePathBreadcrumbs.slice(-1)" + />
+ class="o_last_breadcrumb_item active d-flex gap-2 align-items-center min-w-0 lh-sm" + > + name="props.record.name" + isReadonly="false" + onChanged="onSpreadsheetNameChanged" + />
@@ -481,13 +432,13 @@
+ class="o_last_breadcrumb_item active d-flex fs-4 min-w-0 align-items-center" + > + name="props.record.name" + isReadonly="false" + onChanged="onSpreadsheetNameChanged" + />
@@ -530,39 +481,35 @@
+ class="o_control_panel d-flex flex-column gap-3 gap-lg-1 px-3 pt-2 pb-3" + t-ref="root" + data-command-category="actions" + >
+ class="o_control_panel_main d-flex flex-wrap flex-lg-nowrap justify-content-between align-items-lg-start gap-3 flex-grow-1" + >
+ class="o_control_panel_breadcrumbs d-flex align-items-center gap-1 order-0 h-lg-100" + >
+ class="o_control_panel_main_buttons d-flex gap-1 d-empty-none d-print-none" + t-ref="mainButtons" + t-on-keydown="onMainButtonsKeydown" + >
+ class="btn-group d-xl-none o_control_panel_collapsed_create" + > -
-
+
@@ -586,70 +531,68 @@ - + +
+ class="o_control_panel_actions d-empty-none d-flex align-items-center justify-content-start justify-content-lg-around order-2 order-lg-1 w-100 w-lg-auto" + >
+ class="o_control_panel_navigation d-flex flex-wrap flex-md-nowrap justify-content-end gap-3 gap-lg-1 gap-xl-3 order-1 order-lg-2 flex-grow-1" + > + class="'o_cp_switch_buttons d-xl-none btn-group'" + togglerClass="'btn btn-secondary'" + showCaret="true" + > - + t-set="activeView" + t-value="env.config.viewSwitcherEntries.find((view) => view.active)" + /> + + t-foreach="env.config.viewSwitcherEntries" + t-as="view" + t-key="view.type" + > + onSelected="() => this.onViewClicked(view.type)" + class="view.active ? 'selected' : ''" + >
+ t-name="spreadsheet_oca.OdooStackablePanel" + t-inherit="spreadsheet_oca.OdooPanel" + t-inherit-mode="primary" + owl="1" + >
@@ -691,10 +634,10 @@ + t-inherit="spreadsheet.o-spreadsheet-BarConfigPanel" + t-inherit-mode="extension" + owl="1" + >
Link to Odoo menu
@@ -705,10 +648,10 @@ + t-inherit="spreadsheet.o-spreadsheet-LineConfigPanel" + t-inherit-mode="extension" + owl="1" + >
Link to Odoo menu
@@ -719,10 +662,10 @@ + t-inherit="spreadsheet.o-spreadsheet-LineBarPieConfigPanel" + t-inherit-mode="extension" + owl="1" + >
Link to Odoo menu
@@ -733,10 +676,10 @@ + t-inherit="spreadsheet.o-spreadsheet-GaugeChartConfigPanel" + t-inherit-mode="extension" + owl="1" + >
Link to Odoo menu
@@ -747,10 +690,10 @@ + t-inherit="spreadsheet.o-spreadsheet-ScorecardChartConfigPanel" + t-inherit-mode="extension" + owl="1" + >
Link to Odoo menu
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 b91f92d1..d14d7b36 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_action.esm.js +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_action.esm.js @@ -1,294 +1,291 @@ /** @odoo-module **/ import * as spreadsheet from "@odoo/o-spreadsheet"; -import { - makeDynamicCols, - makeDynamicRows, -} from "../utils/dynamic_generators.esm"; -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"; -import { registry } from "@web/core/registry"; -import { useService } from "@web/core/utils/hooks"; +import {makeDynamicCols, makeDynamicRows} from "../utils/dynamic_generators.esm"; +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"; +import {registry} from "@web/core/registry"; +import {useService} from "@web/core/utils/hooks"; const uuidGenerator = new spreadsheet.helpers.UuidGenerator(); const actionRegistry = registry.category("actions"); -const { Component, onMounted, onWillStart, useSubEnv } = owl; +const {Component, onMounted, onWillStart, useSubEnv} = owl; export class ActionSpreadsheetOca extends Component { - setup() { - this.router = useService("router"); - this.orm = useService("orm"); - this.notification = useService("notification"); - const params = this.props.action.params || this.props.action.context.params; - this.spreadsheetId = params.spreadsheet_id; - this.model = params.model || "spreadsheet.spreadsheet"; - this.import_data = params.import_data || {}; - onMounted(() => { - this.router.pushState({ - spreadsheet_id: this.spreadsheetId, - model: this.model, - }); - }); - onWillStart(async () => { - // We need to load in case the data comes from an XLSX - this.record = - spreadsheet.load( - await this.orm.call( - this.model, - "get_spreadsheet_data", - [[this.spreadsheetId]], - { context: { bin_size: false } } - ) - ) || {}; - }); - useSubEnv({ - saveRecord: this.saveRecord.bind(this), - importData: this.importData.bind(this), - notifyUser: this.notifyUser.bind(this), - }); - } - notifyUser(notification) { - this.notification.add(notification.text, { - type: notification.type, - sticky: notification.sticky, - }); - } - async saveRecord(data) { - if (this.record.mode === "readonly") { - return; + setup() { + this.router = useService("router"); + this.orm = useService("orm"); + this.notification = useService("notification"); + const params = this.props.action.params || this.props.action.context.params; + this.spreadsheetId = params.spreadsheet_id; + this.model = params.model || "spreadsheet.spreadsheet"; + this.import_data = params.import_data || {}; + onMounted(() => { + this.router.pushState({ + spreadsheet_id: this.spreadsheetId, + model: this.model, + }); + }); + onWillStart(async () => { + // We need to load in case the data comes from an XLSX + this.record = + spreadsheet.load( + await this.orm.call( + this.model, + "get_spreadsheet_data", + [[this.spreadsheetId]], + {context: {bin_size: false}} + ) + ) || {}; + }); + useSubEnv({ + saveRecord: this.saveRecord.bind(this), + importData: this.importData.bind(this), + notifyUser: this.notifyUser.bind(this), + }); } - if (this.spreadsheetId) { - this.orm.call(this.model, "write", [this.spreadsheetId, data]); - } else { - this.spreadsheetId = await this.orm.call(this.model, "create", [data]); - this.router.pushState({ spreadsheet_id: this.spreadsheetId }); + notifyUser(notification) { + this.notification.add(notification.text, { + type: notification.type, + sticky: notification.sticky, + }); } - } - /** - * Clean SearchParams of conflictive keys. - * - * 1. Removed from context pivot conflictive keys. - * 2. Removed from context graph conflictive keys. - * - * @returns {Object} Formated searchParams. - */ - cleanSearchParams() { - const searchParams = this.import_data.searchParams; - const context = {}; - for (var key of Object.keys(searchParams.context)) { - if (key.startsWith("pivot_") || key.startsWith("graph_")) { - continue; - } - context[key] = searchParams.context[key]; - } - return { ...searchParams, context }; - } - async importDataGraph(spreadsheet_model) { - var sheetId = spreadsheet_model.getters.getActiveSheetId(); - var y = 0; - if (this.import_data.new === undefined && this.import_data.new_sheet) { - sheetId = uuidGenerator.uuidv4(); - spreadsheet_model.dispatch("CREATE_SHEET", { - sheetId, - position: spreadsheet_model.getters.getSheetIds().length, - }); - // We want to open the new sheet - const sheetIdFrom = spreadsheet_model.getters.getActiveSheetId(); - spreadsheet_model.dispatch("ACTIVATE_SHEET", { - sheetIdFrom, - sheetIdTo: sheetId, - }); - } else if (this.import_data.new === undefined) { - // TODO: Add a way to detect the last row total height - } - const dataSourceId = uuidGenerator.uuidv4(); - const definition = { - title: this.import_data.name, - type: "odoo_" + this.import_data.metaData.mode, - background: "#FFFFFF", - stacked: this.import_data.metaData.stacked, - metaData: this.import_data.metaData, - searchParams: this.cleanSearchParams(), - dataSourceId: dataSourceId, - legendPosition: "top", - verticalAxisPosition: "left", - }; - spreadsheet_model.dispatch("CREATE_CHART", { - sheetId, - id: dataSourceId, - position: { - x: 0, - y: y, - }, - definition, - }); - } - importCreateOrReuseSheet(spreadsheet_model) { - var sheetId = spreadsheet_model.getters.getActiveSheetId(); - var row = 0; - if (this.import_data.new === undefined && this.import_data.new_sheet) { - sheetId = uuidGenerator.uuidv4(); - spreadsheet_model.dispatch("CREATE_SHEET", { - sheetId, - position: spreadsheet_model.getters.getSheetIds().length, - }); - // We want to open the new sheet - const sheetIdFrom = spreadsheet_model.getters.getActiveSheetId(); - spreadsheet_model.dispatch("ACTIVATE_SHEET", { - sheetIdFrom, - sheetIdTo: sheetId, - }); - } else if (this.import_data.new === undefined) { - row = spreadsheet_model.getters.getNumberRows(sheetId); - var maxcols = spreadsheet_model.getters.getNumberCols(sheetId); - var filled = false; - while (row >= 0) { - for (var col = maxcols; col >= 0; col--) { - if ( - spreadsheet_model.getters.getCell(sheetId, col, row) !== - undefined && - !spreadsheet_model.getters.getCell(sheetId, col, row).isEmpty() - ) { - filled = true; - break; - } + async saveRecord(data) { + if (this.record.mode === "readonly") { + return; } - if (filled) { - break; + if (this.spreadsheetId) { + this.orm.call(this.model, "write", [this.spreadsheetId, data]); + } else { + this.spreadsheetId = await this.orm.call(this.model, "create", [data]); + this.router.pushState({spreadsheet_id: this.spreadsheetId}); } - row -= 1; - } - 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.name, - }; - const dataSource = spreadsheet_model.config.custom.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.dyn_number_of_rows, - 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 colGroupBys = this.import_data.metaData.colGroupBys.concat( - this.import_data.metaData.expandedColGroupBys - ); - const rowGroupBys = this.import_data.metaData.rowGroupBys.concat( - this.import_data.metaData.expandedRowGroupBys - ); - const pivot_info = { - metaData: { - colGroupBys, - rowGroupBys, - activeMeasures: this.import_data.metaData.activeMeasures, - resModel: this.import_data.metaData.resModel, - sortedColumn: this.import_data.metaData.sortedColumn, - }, - searchParams: this.cleanSearchParams(), - name: this.import_data.name, - }; - const dataSource = spreadsheet_model.config.custom.dataSources.add( - dataSourceId, - PivotDataSource, - pivot_info - ); - await dataSource.load(); - var { cols, rows, measures } = dataSource.getTableStructure().export(); - if (this.import_data.dyn_number_of_rows) { - const indentations = rows.map((r) => r.indent); - const max_indentation = Math.max(...indentations); - rows = makeDynamicRows( - rowGroupBys, - this.import_data.dyn_number_of_rows, - 1, - max_indentation - ); + /** + * Clean SearchParams of conflictive keys. + * + * 1. Removed from context pivot conflictive keys. + * 2. Removed from context graph conflictive keys. + * + * @returns {Object} Formated searchParams. + */ + cleanSearchParams() { + const searchParams = this.import_data.searchParams; + const context = {}; + for (var key of Object.keys(searchParams.context)) { + if (key.startsWith("pivot_") || key.startsWith("graph_")) { + continue; + } + context[key] = searchParams.context[key]; + } + return {...searchParams, context}; } - if (this.import_data.dyn_number_of_cols) { - cols = makeDynamicCols( - colGroupBys, - this.import_data.dyn_number_of_cols, - this.import_data.metaData.activeMeasures - ); + async importDataGraph(spreadsheet_model) { + var sheetId = spreadsheet_model.getters.getActiveSheetId(); + var y = 0; + if (this.import_data.new === undefined && this.import_data.new_sheet) { + sheetId = uuidGenerator.uuidv4(); + spreadsheet_model.dispatch("CREATE_SHEET", { + sheetId, + position: spreadsheet_model.getters.getSheetIds().length, + }); + // We want to open the new sheet + const sheetIdFrom = spreadsheet_model.getters.getActiveSheetId(); + spreadsheet_model.dispatch("ACTIVATE_SHEET", { + sheetIdFrom, + sheetIdTo: sheetId, + }); + } else if (this.import_data.new === undefined) { + // TODO: Add a way to detect the last row total height + } + const dataSourceId = uuidGenerator.uuidv4(); + const definition = { + title: this.import_data.name, + type: "odoo_" + this.import_data.metaData.mode, + background: "#FFFFFF", + stacked: this.import_data.metaData.stacked, + metaData: this.import_data.metaData, + searchParams: this.cleanSearchParams(), + dataSourceId: dataSourceId, + legendPosition: "top", + verticalAxisPosition: "left", + }; + spreadsheet_model.dispatch("CREATE_CHART", { + sheetId, + id: dataSourceId, + position: { + x: 0, + y: y, + }, + definition, + }); } - const table = { - cols, - rows, - measures, - }; - spreadsheet_model.dispatch("INSERT_PIVOT", { - sheetId, - col: 0, - row: row, - id: spreadsheet_model.getters.getNextPivotId(), - table, - dataSourceId, - definition: pivot_info, - }); - const columns = []; - for (let col = 0; col < table.cols[table.cols.length - 1].length; col++) { - columns.push(col); + importCreateOrReuseSheet(spreadsheet_model) { + var sheetId = spreadsheet_model.getters.getActiveSheetId(); + var row = 0; + if (this.import_data.new === undefined && this.import_data.new_sheet) { + sheetId = uuidGenerator.uuidv4(); + spreadsheet_model.dispatch("CREATE_SHEET", { + sheetId, + position: spreadsheet_model.getters.getSheetIds().length, + }); + // We want to open the new sheet + const sheetIdFrom = spreadsheet_model.getters.getActiveSheetId(); + spreadsheet_model.dispatch("ACTIVATE_SHEET", { + sheetIdFrom, + sheetIdTo: sheetId, + }); + } else if (this.import_data.new === undefined) { + row = spreadsheet_model.getters.getNumberRows(sheetId); + var maxcols = spreadsheet_model.getters.getNumberCols(sheetId); + var filled = false; + while (row >= 0) { + for (var col = maxcols; col >= 0; col--) { + if ( + spreadsheet_model.getters.getCell(sheetId, col, row) !== + undefined && + !spreadsheet_model.getters.getCell(sheetId, col, row).isEmpty() + ) { + filled = true; + break; + } + } + if (filled) { + break; + } + row -= 1; + } + row += 1; + } + return {sheetId, row}; } - spreadsheet_model.dispatch("AUTORESIZE_COLUMNS", { - sheetId, - cols: columns, - }); - } - async importData(spreadsheet_model) { - if (this.import_data.mode === "pivot") { - await this.importDataPivot(spreadsheet_model); + 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.name, + }; + const dataSource = spreadsheet_model.config.custom.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.dyn_number_of_rows, + 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, + }); } - if (this.import_data.mode === "graph") { - await this.importDataGraph(spreadsheet_model); + async importDataPivot(spreadsheet_model) { + var {sheetId, row} = this.importCreateOrReuseSheet(spreadsheet_model); + const dataSourceId = uuidGenerator.uuidv4(); + const colGroupBys = this.import_data.metaData.colGroupBys.concat( + this.import_data.metaData.expandedColGroupBys + ); + const rowGroupBys = this.import_data.metaData.rowGroupBys.concat( + this.import_data.metaData.expandedRowGroupBys + ); + const pivot_info = { + metaData: { + colGroupBys, + rowGroupBys, + activeMeasures: this.import_data.metaData.activeMeasures, + resModel: this.import_data.metaData.resModel, + sortedColumn: this.import_data.metaData.sortedColumn, + }, + searchParams: this.cleanSearchParams(), + name: this.import_data.name, + }; + const dataSource = spreadsheet_model.config.custom.dataSources.add( + dataSourceId, + PivotDataSource, + pivot_info + ); + await dataSource.load(); + var {cols, rows, measures} = dataSource.getTableStructure().export(); + if (this.import_data.dyn_number_of_rows) { + const indentations = rows.map((r) => r.indent); + const max_indentation = Math.max(...indentations); + rows = makeDynamicRows( + rowGroupBys, + this.import_data.dyn_number_of_rows, + 1, + max_indentation + ); + } + if (this.import_data.dyn_number_of_cols) { + cols = makeDynamicCols( + colGroupBys, + this.import_data.dyn_number_of_cols, + this.import_data.metaData.activeMeasures + ); + } + const table = { + cols, + rows, + measures, + }; + spreadsheet_model.dispatch("INSERT_PIVOT", { + sheetId, + col: 0, + row: row, + id: spreadsheet_model.getters.getNextPivotId(), + table, + dataSourceId, + definition: pivot_info, + }); + const columns = []; + for (let col = 0; col < table.cols[table.cols.length - 1].length; col++) { + columns.push(col); + } + spreadsheet_model.dispatch("AUTORESIZE_COLUMNS", { + sheetId, + cols: columns, + }); } - if (this.import_data.mode === "list") { - await this.importDataList(spreadsheet_model); + async importData(spreadsheet_model) { + if (this.import_data.mode === "pivot") { + await this.importDataPivot(spreadsheet_model); + } + 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"; ActionSpreadsheetOca.components = { - SpreadsheetRenderer, - SpreadsheetControlPanel, + SpreadsheetRenderer, + SpreadsheetControlPanel, }; actionRegistry.add("action_spreadsheet_oca", ActionSpreadsheetOca, { - force: true, + force: true, }); diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_controlpanel.esm.js b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_controlpanel.esm.js index 37a92b5b..9a3cecd1 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_controlpanel.esm.js +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_controlpanel.esm.js @@ -1,31 +1,31 @@ /** @odoo-module **/ -import { Component } from "@odoo/owl"; -import { ControlPanel } from "@web/search/control_panel/control_panel"; +import {Component} from "@odoo/owl"; +import {ControlPanel} from "@web/search/control_panel/control_panel"; -const { useState } = owl; +const {useState} = owl; export class SpreadsheetName extends Component { - setup() { - this.state = useState({ - name: this.props.name, - }); - } - _onNameChanged(ev) { - if (ev.target.value) { - this.env.saveRecord({ name: ev.target.value }); + setup() { + this.state = useState({ + name: this.props.name, + }); + } + _onNameChanged(ev) { + if (ev.target.value) { + this.env.saveRecord({name: ev.target.value}); + } + this.state.name = ev.target.value; } - this.state.name = ev.target.value; - } } SpreadsheetName.template = "spreadsheet_oca.SpreadsheetName"; export class SpreadsheetControlPanel extends ControlPanel {} SpreadsheetControlPanel.template = "spreadsheet_oca.SpreadsheetControlPanel"; SpreadsheetControlPanel.props = { - ...ControlPanel.props, - record: Object, + ...ControlPanel.props, + record: Object, }; SpreadsheetControlPanel.components = { - SpreadsheetName, + SpreadsheetName, }; diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_renderer.esm.js b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_renderer.esm.js index bf586890..1627274e 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_renderer.esm.js +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_renderer.esm.js @@ -1,188 +1,185 @@ /** @odoo-module **/ import * as spreadsheet from "@odoo/o-spreadsheet"; -import { Component } from "@odoo/owl"; -import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog"; -import { DataSources } from "@spreadsheet/data_sources/data_sources"; -import { Dialog } from "@web/core/dialog/dialog"; -import { Field } from "@web/views/fields/field"; -import { _t } from "@web/core/l10n/translation"; -import { loadSpreadsheetDependencies } from "@spreadsheet/assets_backend/helpers"; -import { migrate } from "@spreadsheet/o_spreadsheet/migration"; -import { useService } from "@web/core/utils/hooks"; -import { useSetupAction } from "@web/webclient/actions/action_hook"; -import { waitForDataLoaded } from "@spreadsheet/helpers/model"; -import { createDefaultCurrencyFormat } from "@spreadsheet/currency/helpers"; +import {Component} from "@odoo/owl"; +import {ConfirmationDialog} from "@web/core/confirmation_dialog/confirmation_dialog"; +import {DataSources} from "@spreadsheet/data_sources/data_sources"; +import {Dialog} from "@web/core/dialog/dialog"; +import {Field} from "@web/views/fields/field"; +import {_t} from "@web/core/l10n/translation"; +import {loadSpreadsheetDependencies} from "@spreadsheet/assets_backend/helpers"; +import {migrate} from "@spreadsheet/o_spreadsheet/migration"; +import {useService} from "@web/core/utils/hooks"; +import {useSetupAction} from "@web/webclient/actions/action_hook"; +import {waitForDataLoaded} from "@spreadsheet/helpers/model"; +import {createDefaultCurrencyFormat} from "@spreadsheet/currency/helpers"; -const { Spreadsheet, Model } = spreadsheet; -const { useSubEnv, onWillStart } = owl; +const {Spreadsheet, Model} = spreadsheet; +const {useSubEnv, onWillStart} = owl; const uuidGenerator = new spreadsheet.helpers.UuidGenerator(); class SpreadsheetTransportService { - constructor(orm, bus_service, model, res_id) { - this.orm = orm; - this.bus_service = bus_service; - this.model = model; - this.res_id = res_id; - this.channel = "spreadsheet_oca;" + this.model + ";" + this.res_id; - this.bus_service.addChannel(this.channel); - this.bus_service.addEventListener( - "notification", - this.onNotification.bind(this) - ); - this.listeners = []; - } - onNotification({ detail: notifications }) { - for (const { payload, type } of notifications) { - if ( - type === "spreadsheet_oca" && - payload.res_model === this.model && - payload.res_id === this.res_id - ) { - // What shall we do if no callback is defined (empty until onNewMessage...) :/ - for (const { callback } of this.listeners) { - callback(payload); + constructor(orm, bus_service, model, res_id) { + this.orm = orm; + this.bus_service = bus_service; + this.model = model; + this.res_id = res_id; + this.channel = "spreadsheet_oca;" + this.model + ";" + this.res_id; + this.bus_service.addChannel(this.channel); + this.bus_service.addEventListener( + "notification", + this.onNotification.bind(this) + ); + this.listeners = []; + } + onNotification({detail: notifications}) { + for (const {payload, type} of notifications) { + if ( + type === "spreadsheet_oca" && + payload.res_model === this.model && + payload.res_id === this.res_id + ) { + // What shall we do if no callback is defined (empty until onNewMessage...) :/ + for (const {callback} of this.listeners) { + callback(payload); + } + } } - } } - } - sendMessage(message) { - this.orm.call(this.model, "send_spreadsheet_message", [ - [this.res_id], - message, - ]); - } - onNewMessage(id, callback) { - this.listeners.push({ id, callback }); - } - leave(id) { - this.listeners = this.listeners.filter((listener) => listener.id !== id); - } + sendMessage(message) { + this.orm.call(this.model, "send_spreadsheet_message", [[this.res_id], message]); + } + onNewMessage(id, callback) { + this.listeners.push({id, callback}); + } + leave(id) { + this.listeners = this.listeners.filter((listener) => listener.id !== id); + } } export class SpreadsheetRenderer extends Component { - getLocales() { - const orm = useService("orm"); - return async () => { - return orm.call("res.lang", "get_locales_for_spreadsheet", []); - }; - } - getCurrencies() { - const orm = useService("orm"); - return async () => { - const odooCurrencies = await orm.searchRead( - "res.currency", - [], - ["symbol", "full_name", "position", "name", "decimal_places"], - { - order: "active DESC, full_name ASC", - context: { active_test: false }, - } - ); - return odooCurrencies.map((currency) => { - return { - code: currency.name, - symbol: currency.symbol, - position: currency.position || "after", - name: currency.full_name || _t("Currency"), - decimalPlaces: currency.decimal_places || 2, + getLocales() { + const orm = useService("orm"); + return async () => { + return orm.call("res.lang", "get_locales_for_spreadsheet", []); + }; + } + getCurrencies() { + const orm = useService("orm"); + return async () => { + const odooCurrencies = await orm.searchRead( + "res.currency", + [], + ["symbol", "full_name", "position", "name", "decimal_places"], + { + order: "active DESC, full_name ASC", + context: {active_test: false}, + } + ); + return odooCurrencies.map((currency) => { + return { + code: currency.name, + symbol: currency.symbol, + position: currency.position || "after", + name: currency.full_name || _t("Currency"), + decimalPlaces: currency.decimal_places || 2, + }; + }); }; - }); - }; - } - setup() { - this.orm = useService("orm"); - this.bus_service = this.env.services.bus_service; - this.user = useService("user"); - this.ui = useService("ui"); - this.action = useService("action"); - this.dialog = useService("dialog"); - const dataSources = new DataSources(this.env); - this.confirmDialog = this.closeDialog; - this.loadCurrencies = this.getCurrencies(); - this.loadLocales = this.getLocales(); - const defaultCurrency = this.props.record.default_currency; - const defaultCurrencyFormat = defaultCurrency - ? createDefaultCurrencyFormat(defaultCurrency) - : undefined; - this.spreadsheet_model = new Model( - migrate(this.props.record.spreadsheet_raw), - { - custom: { env: this.env, orm: this.orm, dataSources }, - defaultCurrencyFormat, - external: { - loadCurrencies: this.loadCurrencies, - loadLocales: this.loadLocales, - }, - transportService: new SpreadsheetTransportService( - this.orm, - this.bus_service, - this.props.model, - this.props.res_id - ), - client: { - id: uuidGenerator.uuidv4(), - name: this.user.name, - }, - mode: this.props.record.mode, - }, - this.props.record.revisions - ); - useSubEnv({ - saveSpreadsheet: this.onSpreadsheetSaved.bind(this), - askConfirmation: this.askConfirmation.bind(this), - downloadAsXLXS: this.downloadAsXLXS.bind(this), - }); - onWillStart(async () => { - await loadSpreadsheetDependencies(); - await dataSources.waitForAllLoaded(); - await this.env.importData(this.spreadsheet_model); - }); - useSetupAction({ - beforeLeave: () => this.onSpreadsheetSaved(), - }); - dataSources.addEventListener("data-source-updated", () => { - const sheetId = this.spreadsheet_model.getters.getActiveSheetId(); - this.spreadsheet_model.dispatch("EVALUATE_CELLS", { sheetId }); - }); - } - onSpreadsheetSaved() { - const data = this.spreadsheet_model.exportData(); - this.env.saveRecord({ spreadsheet_raw: data }); - this.spreadsheet_model.leaveSession(); - } - askConfirmation(content, confirm) { - this.dialog.add(ConfirmationDialog, { - title: _t("Odoo Spreadsheet"), - body: content, - confirm, - confirmLabel: _t("Confirm"), - }); - } - async downloadAsXLXS() { - this.ui.block(); - await waitForDataLoaded(this.spreadsheet_model); - await this.action.doAction({ - type: "ir.actions.client", - tag: "action_download_spreadsheet", - params: { - name: this.props.record.name, - xlsxData: this.spreadsheet_model.exportXLSX(), - }, - }); - this.ui.unblock(); - } + } + setup() { + this.orm = useService("orm"); + this.bus_service = this.env.services.bus_service; + this.user = useService("user"); + this.ui = useService("ui"); + this.action = useService("action"); + this.dialog = useService("dialog"); + const dataSources = new DataSources(this.env); + this.confirmDialog = this.closeDialog; + this.loadCurrencies = this.getCurrencies(); + this.loadLocales = this.getLocales(); + const defaultCurrency = this.props.record.default_currency; + const defaultCurrencyFormat = defaultCurrency + ? createDefaultCurrencyFormat(defaultCurrency) + : undefined; + this.spreadsheet_model = new Model( + migrate(this.props.record.spreadsheet_raw), + { + custom: {env: this.env, orm: this.orm, dataSources}, + defaultCurrencyFormat, + external: { + loadCurrencies: this.loadCurrencies, + loadLocales: this.loadLocales, + }, + transportService: new SpreadsheetTransportService( + this.orm, + this.bus_service, + this.props.model, + this.props.res_id + ), + client: { + id: uuidGenerator.uuidv4(), + name: this.user.name, + }, + mode: this.props.record.mode, + }, + this.props.record.revisions + ); + useSubEnv({ + saveSpreadsheet: this.onSpreadsheetSaved.bind(this), + askConfirmation: this.askConfirmation.bind(this), + downloadAsXLXS: this.downloadAsXLXS.bind(this), + }); + onWillStart(async () => { + await loadSpreadsheetDependencies(); + await dataSources.waitForAllLoaded(); + await this.env.importData(this.spreadsheet_model); + }); + useSetupAction({ + beforeLeave: () => this.onSpreadsheetSaved(), + }); + dataSources.addEventListener("data-source-updated", () => { + const sheetId = this.spreadsheet_model.getters.getActiveSheetId(); + this.spreadsheet_model.dispatch("EVALUATE_CELLS", {sheetId}); + }); + } + onSpreadsheetSaved() { + const data = this.spreadsheet_model.exportData(); + this.env.saveRecord({spreadsheet_raw: data}); + this.spreadsheet_model.leaveSession(); + } + askConfirmation(content, confirm) { + this.dialog.add(ConfirmationDialog, { + title: _t("Odoo Spreadsheet"), + body: content, + confirm, + confirmLabel: _t("Confirm"), + }); + } + async downloadAsXLXS() { + this.ui.block(); + await waitForDataLoaded(this.spreadsheet_model); + await this.action.doAction({ + type: "ir.actions.client", + tag: "action_download_spreadsheet", + params: { + name: this.props.record.name, + xlsxData: this.spreadsheet_model.exportXLSX(), + }, + }); + this.ui.unblock(); + } } SpreadsheetRenderer.template = "spreadsheet_oca.SpreadsheetRenderer"; SpreadsheetRenderer.components = { - Spreadsheet, - Field, - Dialog, + Spreadsheet, + Field, + Dialog, }; SpreadsheetRenderer.props = { - record: Object, - res_id: { type: Number, optional: true }, - model: String, - importData: { type: Function, optional: true }, + record: Object, + res_id: {type: Number, optional: true}, + model: String, + importData: {type: Function, optional: true}, }; diff --git a/spreadsheet_oca/static/src/spreadsheet/graph_controller.esm.js b/spreadsheet_oca/static/src/spreadsheet/graph_controller.esm.js index e0de39dc..fac9eeaa 100644 --- a/spreadsheet_oca/static/src/spreadsheet/graph_controller.esm.js +++ b/spreadsheet_oca/static/src/spreadsheet/graph_controller.esm.js @@ -1,23 +1,25 @@ /** @odoo-module **/ -import { GraphRenderer } from "@web/views/graph/graph_renderer"; +import {GraphRenderer} from "@web/views/graph/graph_renderer"; -import { patch } from "@web/core/utils/patch"; +import {patch} from "@web/core/utils/patch"; patch(GraphRenderer.prototype, { - onSpreadsheetButtonClicked() { - this.actionService.doAction( - "spreadsheet_oca.spreadsheet_spreadsheet_import_act_window", - { - additionalContext: { - default_name: this.model.metaData.title, - default_datasource_name: this.model.metaData.title, - default_import_data: { - mode: "graph", - metaData: JSON.parse(JSON.stringify(this.model.metaData)), - searchParams: JSON.parse(JSON.stringify(this.model.searchParams)), - }, - }, - } - ); - }, + onSpreadsheetButtonClicked() { + this.actionService.doAction( + "spreadsheet_oca.spreadsheet_spreadsheet_import_act_window", + { + additionalContext: { + default_name: this.model.metaData.title, + default_datasource_name: this.model.metaData.title, + default_import_data: { + mode: "graph", + metaData: JSON.parse(JSON.stringify(this.model.metaData)), + searchParams: JSON.parse( + JSON.stringify(this.model.searchParams) + ), + }, + }, + } + ); + }, }); diff --git a/spreadsheet_oca/static/src/spreadsheet/graph_controller.xml b/spreadsheet_oca/static/src/spreadsheet/graph_controller.xml index 553bae42..37b6e49a 100644 --- a/spreadsheet_oca/static/src/spreadsheet/graph_controller.xml +++ b/spreadsheet_oca/static/src/spreadsheet/graph_controller.xml @@ -3,13 +3,12 @@ diff --git a/spreadsheet_oca/views/spreadsheet_spreadsheet.xml b/spreadsheet_oca/views/spreadsheet_spreadsheet.xml index 56571699..52e7d30f 100644 --- a/spreadsheet_oca/views/spreadsheet_spreadsheet.xml +++ b/spreadsheet_oca/views/spreadsheet_spreadsheet.xml @@ -2,58 +2,45 @@ - - spreadsheet.spreadsheet.search (in spreadsheet_oca) + spreadsheet.spreadsheet.search (in spreadsheet_oca) spreadsheet.spreadsheet - - - + - spreadsheet.spreadsheet.tree (in spreadsheet_oca) + spreadsheet.spreadsheet.tree (in spreadsheet_oca) spreadsheet.spreadsheet

@@ -62,33 +49,27 @@ + name="company_id" + groups="base.group_multi_company" + /> + - + name="contributor_group_ids" + widget="many2many_tags" + /> - + + name="spreadsheet_binary_data" + filename="filename" + groups="base.group_no_one" + /> @@ -97,10 +78,7 @@ - + Spreadsheets spreadsheet.spreadsheet tree,form @@ -111,14 +89,8 @@ Spreadsheets - spreadsheet_oca,static/description/icon.png - + spreadsheet_oca,static/description/icon.png + - diff --git a/spreadsheet_oca/wizards/spreadsheet_select_row_number.xml b/spreadsheet_oca/wizards/spreadsheet_select_row_number.xml index 8c1d060f..538c0756 100644 --- a/spreadsheet_oca/wizards/spreadsheet_select_row_number.xml +++ b/spreadsheet_oca/wizards/spreadsheet_select_row_number.xml @@ -1,6 +1,5 @@ - spreadsheet.select.row.number.form spreadsheet.select.row.number @@ -9,23 +8,16 @@ - + - + name="dynamic_cols" + invisible='not can_have_dynamic_cols' + /> + - diff --git a/spreadsheet_oca/wizards/spreadsheet_spreadsheet_import.xml b/spreadsheet_oca/wizards/spreadsheet_spreadsheet_import.xml index 1f4fdf9c..a89c78ed 100644 --- a/spreadsheet_oca/wizards/spreadsheet_spreadsheet_import.xml +++ b/spreadsheet_oca/wizards/spreadsheet_spreadsheet_import.xml @@ -2,11 +2,10 @@ - spreadsheet.spreadsheet.import.form (in spreadsheet_oca) + name="name" + >spreadsheet.spreadsheet.import.form (in spreadsheet_oca) spreadsheet.spreadsheet.import
@@ -14,73 +13,67 @@ + name="name" + invisible="mode != 'new'" + required="mode == 'new'" + /> + name="spreadsheet_id" + domain="['|', ('owner_id', '=', uid), ('contributor_ids', '=', uid)]" + invisible="mode not in ['add', 'add_sheet']" + required="mode in ['add', 'add_sheet']" + options="{'no_create': True}" + /> + name="dynamic" + force_save="1" + invisible='not can_be_dynamic' + readonly='is_tree' + /> + name="number_of_rows" + invisible='not dynamic' + required='dynamic' + /> + name="dynamic_cols" + force_save="1" + invisible='not can_have_dynamic_cols or is_tree' + /> + name="number_of_cols" + invisible='not dynamic_cols' + required='dynamic_cols' + />
+ model="ir.actions.act_window" + id="spreadsheet_spreadsheet_import_act_window" + > Import Pivot spreadsheet.spreadsheet.import form {} new - -
From 69ae03cd655914a0aa96be5a279100e57e554fc7 Mon Sep 17 00:00:00 2001 From: Chris Mann Date: Wed, 24 Sep 2025 14:16:26 +0100 Subject: [PATCH 150/150] [MIG] spreadsheet_oca: Migration to 18.0 --- spreadsheet_oca/README.rst | 2 +- spreadsheet_oca/__manifest__.py | 27 +-- .../migrations/17.0.1.0.0/pre-migrate.py | 17 -- spreadsheet_oca/models/ir_websocket.py | 2 +- .../models/spreadsheet_abstract.py | 48 +++- spreadsheet_oca/static/description/index.html | 80 ++++--- .../static/src/pivot/pivot_table.esm.js | 3 +- .../src/spreadsheet/bundle/chart_panel.esm.js | 2 - .../spreadsheet/bundle/chart_panels.esm.js | 33 ++- .../src/spreadsheet/bundle/filter.esm.js | 72 ++++-- .../bundle/filter_panel_datasources.esm.js | 205 ++++++++---------- .../bundle/image_file_store.esm.js | 33 +++ .../src/spreadsheet/bundle/odoo_panels.esm.js | 81 ++++++- .../src/spreadsheet/bundle/spreadsheet.xml | 99 +++------ .../bundle/spreadsheet_action.esm.js | 200 ++++++++--------- .../bundle/spreadsheet_controlpanel.esm.js | 25 ++- .../bundle/spreadsheet_renderer.esm.js | 122 +++++++---- .../src/spreadsheet/graph_controller.esm.js | 1 - .../src/spreadsheet/graph_controller.xml | 7 +- .../src/spreadsheet/list_controller.esm.js | 1 - .../src/spreadsheet/list_controller.xml | 15 +- .../src/spreadsheet/list_renderer.esm.js | 19 +- .../src/spreadsheet/pivot_controller.esm.js | 16 +- .../src/spreadsheet/pivot_controller.xml | 7 +- .../src/spreadsheet/spreadsheet_action.esm.js | 22 +- .../utils/dynamic_generators.esm.js | 3 +- .../spreadsheet_tree_view.esm.js | 2 - .../views/spreadsheet_spreadsheet.xml | 6 +- .../wizards/spreadsheet_spreadsheet_import.py | 2 +- .../spreadsheet_spreadsheet_import.xml | 8 +- 30 files changed, 661 insertions(+), 499 deletions(-) delete mode 100644 spreadsheet_oca/migrations/17.0.1.0.0/pre-migrate.py create mode 100644 spreadsheet_oca/static/src/spreadsheet/bundle/image_file_store.esm.js diff --git a/spreadsheet_oca/README.rst b/spreadsheet_oca/README.rst index 8ddb4f8f..7908f90c 100644 --- a/spreadsheet_oca/README.rst +++ b/spreadsheet_oca/README.rst @@ -7,7 +7,7 @@ Spreadsheet Oca !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:2e54effb5771467ddab39e872bcddddc5e7a900f7afb88dd3bb87ac26d195ca5 + !! source digest: sha256:a7edc105b2730d126fc787ef8ef2c8c1d293b8e18e94402ff7db7d044ee863cd !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png diff --git a/spreadsheet_oca/__manifest__.py b/spreadsheet_oca/__manifest__.py index 553f0364..f065bd7f 100644 --- a/spreadsheet_oca/__manifest__.py +++ b/spreadsheet_oca/__manifest__.py @@ -5,7 +5,7 @@ "name": "Spreadsheet Oca", "summary": """ Allow to edit spreadsheets""", - "version": "17.0.1.0.3", + "version": "18.0.1.0.0", "license": "AGPL-3", "author": "CreuBlanca,Odoo Community Association (OCA)", "website": "https://github.com/OCA/spreadsheet", @@ -25,28 +25,19 @@ "spreadsheet_oca/static/src/spreadsheet_tree/spreadsheet_tree_view.xml", "spreadsheet_oca/static/src/spreadsheet/spreadsheet.scss", "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", - ( - "after", - "web/static/src/views/graph/graph_controller.xml", - "spreadsheet_oca/static/src/spreadsheet/graph_controller.xml", - ), - ( - "after", - "web/static/src/views/list/list_controller.xml", - "spreadsheet_oca/static/src/spreadsheet/list_controller.xml", - ), - ( - "after", - "web/static/src/views/pivot/pivot_controller.xml", - "spreadsheet_oca/static/src/spreadsheet/pivot_controller.xml", - ), + "spreadsheet_oca/static/src/spreadsheet/list_controller.xml", + ], + "web.assets_backend_lazy": [ + "spreadsheet_oca/static/src/spreadsheet/pivot_controller.esm.js", + "spreadsheet_oca/static/src/spreadsheet/graph_controller.esm.js", + "spreadsheet_oca/static/src/spreadsheet/pivot_controller.xml", + "spreadsheet_oca/static/src/spreadsheet/graph_controller.xml", ], "spreadsheet.o_spreadsheet": [ "spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml", + "spreadsheet_oca/static/src/spreadsheet/bundle/image_file_store.esm.js", "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", diff --git a/spreadsheet_oca/migrations/17.0.1.0.0/pre-migrate.py b/spreadsheet_oca/migrations/17.0.1.0.0/pre-migrate.py deleted file mode 100644 index 8e329591..00000000 --- a/spreadsheet_oca/migrations/17.0.1.0.0/pre-migrate.py +++ /dev/null @@ -1,17 +0,0 @@ -from openupgradelib import openupgrade - - -@openupgrade.migrate() -def migrate(env, version): - # Renamed field data to spreadsheet_binary_data in V17 - openupgrade.rename_fields( - env, - [ - ( - "spreadsheet.spreadsheet", - "spreadsheet_spreadsheet", - "data", - "spreadsheet_binary_data", - ), - ], - ) diff --git a/spreadsheet_oca/models/ir_websocket.py b/spreadsheet_oca/models/ir_websocket.py index 1987b564..005f41c5 100644 --- a/spreadsheet_oca/models/ir_websocket.py +++ b/spreadsheet_oca/models/ir_websocket.py @@ -58,9 +58,9 @@ def _build_bus_channel_list(self, channels): channels.append( ( self.env.registry.db_name, - "spreadsheet_oca", model_name, res_id, + "spreadsheet_oca", ) ) return super()._build_bus_channel_list(channels) diff --git a/spreadsheet_oca/models/spreadsheet_abstract.py b/spreadsheet_oca/models/spreadsheet_abstract.py index 0e1280b5..c43be6bb 100644 --- a/spreadsheet_oca/models/spreadsheet_abstract.py +++ b/spreadsheet_oca/models/spreadsheet_abstract.py @@ -3,14 +3,18 @@ import base64 import json +from typing import Any from odoo import _, api, fields, models from odoo.exceptions import AccessError +CollaborationMessage = dict[str, Any] + class SpreadsheetAbstract(models.AbstractModel): _name = "spreadsheet.abstract" _description = "Spreadsheet abstract for inheritance" + _inherit = ["bus.listener.mixin"] name = fields.Char(required=True) spreadsheet_binary_data = fields.Binary( @@ -76,8 +80,7 @@ def get_spreadsheet_data(self): self.ensure_one() mode = "normal" try: - self.check_access_rights("write") - self.check_access_rule("write") + self.check_access("write") except AccessError: mode = "readonly" return { @@ -106,11 +109,31 @@ def open_spreadsheet(self): "params": {"spreadsheet_id": self.id, "model": self._name}, } - def send_spreadsheet_message(self, message): + def send_spreadsheet_message( + self, message: CollaborationMessage, access_token=None + ): self.ensure_one() - channel = (self.env.cr.dbname, "spreadsheet_oca", self._name, self.id) - message.update({"res_model": self._name, "res_id": self.id}) if message["type"] in ["REVISION_UNDONE", "REMOTE_REVISION", "REVISION_REDONE"]: + self._check_access_spreadsheet("write") + self.env["spreadsheet.oca.revision"].create( + { + "model": self._name, + "res_id": self.id, + "type": message["type"], + "client_id": message.get("clientId"), + "next_revision_id": message["nextRevisionId"], + "server_revision_id": message["serverRevisionId"], + "commands": json.dumps( + self._build_spreadsheet_revision_commands_data(message) + ), + } + ) + self._bus_send( + "notification", dict(message, id=self.id), subchannel="spreadsheet_oca" + ) + return True + elif message["type"] == "SNAPSHOT": + self._check_access_spreadsheet("write") self.env["spreadsheet.oca.revision"].create( { "model": self._name, @@ -124,7 +147,20 @@ def send_spreadsheet_message(self, message): ), } ) - self.env["bus.bus"]._sendone(channel, "spreadsheet_oca", message) + return True + elif message["type"] in ["CLIENT_JOINED", "CLIENT_LEFT", "CLIENT_MOVED"]: + self._check_access_spreadsheet("read") + self._bus_send( + "notification", dict(message, id=self.id), subchannel="spreadsheet_oca" + ) + return True + return False + + def _check_access_spreadsheet(self, operation: str): + try: + self.check_access(operation) + except AccessError as e: + raise e return True @api.model diff --git a/spreadsheet_oca/static/description/index.html b/spreadsheet_oca/static/description/index.html index 7bae2017..b9fe162e 100644 --- a/spreadsheet_oca/static/description/index.html +++ b/spreadsheet_oca/static/description/index.html @@ -3,7 +3,7 @@ -README.rst +Spreadsheet Oca -
+
+

Spreadsheet Oca

- - -Odoo Community Association - -
-

Spreadsheet Oca

-

Beta License: AGPL-3 OCA/spreadsheet Translate me on Weblate Try me on Runboat

+

Beta License: AGPL-3 OCA/spreadsheet Translate me on Weblate Try me on Runboat

This module adds a functionality for adding and editing Spreadsheets using Odoo CE.

It is an alternative to the proprietary module spreadsheet_edition @@ -402,56 +397,56 @@

Spreadsheet Oca

-

Usage

+

Usage

-

Create a new spreadsheet

+

Create a new spreadsheet

  • Go to ‘Spreadsheet’ menu
  • Click on ‘Create’
  • Put a name, then click on the “Edit” button
-

image1

+

image1

  • At this point you switch to spreadsheet editing mode. The editor is named o-spreadsheet and looks like another common spreadsheet web editors. (OnlyOffice, Ethercalc, Google Sheets (non-free)).
-

image2

+

image2

-

image3

+

image3

  • Note: Business Odoo module can add “business functions”. This is -currently the case for the accounting module, which adds the following -features:

    +currently the case for the accounting module, which adds the +following features:

      -
    • ODOO.CREDIT(account_codes, date_range): Get the total credit -for the specified account(s) and period.
    • +
    • ODOO.CREDIT(account_codes, date_range): Get the total +credit for the specified account(s) and period.
    • ODOO.DEBIT(account_codes, date_range): Get the total debit for the specified account(s) and period.
    • ODOO.BALANCE(account_codes, date_range): Get the total balance for the specified account(s) and period.
    • -
    • ODOO.FISCALYEAR.START(day): Returns the starting date of the -fiscal year encompassing the provided date.
    • +
    • ODOO.FISCALYEAR.START(day): Returns the starting date of +the fiscal year encompassing the provided date.
    • ODOO.FISCALYEAR.END(day): Returns the ending date of the fiscal year encompassing the provided date.
    • -
    • ODOO.ACCOUNT.GROUP(type): Returns the account ids of a given -group where type should be a value of the account_type field -of account.account model. (income, asset_receivable, -etc.)
    • +
    • ODOO.ACCOUNT.GROUP(type): Returns the account ids of a +given group where type should be a value of the +account_type field of account.account model. +(income, asset_receivable, etc.)
-

Create a new dynamic spreadsheet from pivot

+

Create a new dynamic spreadsheet from pivot

-

Development

+

Development

If you want to develop custom business functions, you can add others, based on the file https://github.com/odoo/odoo/blob/16.0/addons/spreadsheet_account/static/src/accounting_functions.js

-

Known issues / Roadmap

+

Known issues / Roadmap

-

Adding new lines on pivot tables

+

Adding new lines on pivot tables

When we add a pivot table, the number of rows is predefined according to the current data.

In order to add new rows, we need to reinsert the pivot table.

-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed -feedback.

+feedback.

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • CreuBlanca
-

Contributors

+

Contributors

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -545,11 +540,10 @@

Maintainers

OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

-

This module is part of the OCA/spreadsheet project on GitHub.

+

This module is part of the OCA/spreadsheet project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

-
diff --git a/spreadsheet_oca/static/src/pivot/pivot_table.esm.js b/spreadsheet_oca/static/src/pivot/pivot_table.esm.js index 3202d542..0ce658c8 100644 --- a/spreadsheet_oca/static/src/pivot/pivot_table.esm.js +++ b/spreadsheet_oca/static/src/pivot/pivot_table.esm.js @@ -1,7 +1,6 @@ -/** @odoo-module */ /* Copyright 2024 Tecnativa - Carlos Roca * License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). */ -import {SpreadsheetPivotTable} from "@spreadsheet/pivot/pivot_table"; +import {SpreadsheetPivotTable} from "@odoo/o-spreadsheet"; import {patch} from "@web/core/utils/patch"; patch(SpreadsheetPivotTable.prototype, { diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/chart_panel.esm.js b/spreadsheet_oca/static/src/spreadsheet/bundle/chart_panel.esm.js index a98cf157..b6a1d23b 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/chart_panel.esm.js +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/chart_panel.esm.js @@ -1,5 +1,3 @@ -/** @odoo-module */ - import * as spreadsheet from "@odoo/o-spreadsheet"; import {patch} from "@web/core/utils/patch"; diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/chart_panels.esm.js b/spreadsheet_oca/static/src/spreadsheet/bundle/chart_panels.esm.js index 8bda4972..e42f6706 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/chart_panels.esm.js +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/chart_panels.esm.js @@ -1,15 +1,17 @@ -/** @odoo-module */ - import * as spreadsheet from "@odoo/o-spreadsheet"; import {Domain} from "@web/core/domain"; - import {Many2XAutocomplete} from "@web/views/fields/relational_utils"; +import {_t} from "@web/core/l10n/translation"; import {patch} from "@web/core/utils/patch"; import {useService} from "@web/core/utils/hooks"; -import {_t} from "@web/core/l10n/translation"; -const {LineBarPieConfigPanel, ScorecardChartConfigPanel, GaugeChartConfigPanel} = - spreadsheet.components; +const { + GenericChartConfigPanel, + LineConfigPanel, + BarConfigPanel, + ScorecardChartConfigPanel, + GaugeChartConfigPanel, +} = spreadsheet.components; const menuChartProps = { setup() { @@ -55,7 +57,6 @@ const menuChartProps = { return; } const menu = this.env.model.getters.getIrMenu(menuId[0].id); - console.log(menu); this.env.model.dispatch("LINK_ODOO_MENU_TO_CHART", { chartId: this.props.figureId, odooMenuId: menu.xmlid || menu.id, @@ -77,9 +78,21 @@ const menuChartProps = { }, }; -patch(LineBarPieConfigPanel.prototype, menuChartProps); -LineBarPieConfigPanel.components = { - ...LineBarPieConfigPanel.components, +patch(GenericChartConfigPanel.prototype, menuChartProps); +GenericChartConfigPanel.components = { + ...GenericChartConfigPanel.components, + Many2XAutocomplete, +}; + +patch(LineConfigPanel.prototype, menuChartProps); +LineConfigPanel.components = { + ...LineConfigPanel.components, + Many2XAutocomplete, +}; + +patch(BarConfigPanel.prototype, menuChartProps); +BarConfigPanel.components = { + ...BarConfigPanel.components, Many2XAutocomplete, }; diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js b/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js index 7076f88f..a5fabc63 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js @@ -1,12 +1,12 @@ -/** @odoo-module **/ - import * as spreadsheet from "@odoo/o-spreadsheet"; import {Component, onWillStart, useState} from "@odoo/owl"; -import {_lt, _t} from "@web/core/l10n/translation"; + import {FilterValue} from "@spreadsheet/global_filters/components/filter_value/filter_value"; import {ModelFieldSelector} from "@web/core/model_field_selector/model_field_selector"; import {ModelSelector} from "@web/core/model_selector/model_selector"; import {RELATIVE_DATE_RANGE_TYPES} from "@spreadsheet/helpers/constants"; + +import {_t} from "@web/core/l10n/translation"; import {globalFiltersFieldMatchers} from "@spreadsheet/global_filters/plugins/global_filters_core_plugin"; import {useService} from "@web/core/utils/hooks"; @@ -123,10 +123,10 @@ export class EditFilterPanel extends Component { get dateOffset() { return [ {value: 0, name: ""}, - {value: -1, name: _lt("Previous")}, - {value: -2, name: _lt("Before Previous")}, - {value: 1, name: _lt("Next")}, - {value: 2, name: _lt("After next")}, + {value: -1, name: _t("Previous")}, + {value: -2, name: _t("Before Previous")}, + {value: 1, name: _t("Next")}, + {value: 2, name: _t("After next")}, ]; } onChangeFieldMatchOffset(object, ev) { @@ -144,8 +144,8 @@ export class EditFilterPanel extends Component { const action = this.props.filter.id ? "EDIT_GLOBAL_FILTER" : "ADD_GLOBAL_FILTER"; - this.env.openSidePanel("FilterPanel", {}); - var filter = { + + const filter = { id: this.props.filter.id || uuidGenerator.uuidv4(), type: this.state.type, label: this.state.label, @@ -153,17 +153,16 @@ export class EditFilterPanel extends Component { rangeType: this.state.rangeType, modelName: this.state.modelName.technical, }; - var filterMatching = {}; + const filterMatching = {}; Object.values(this.state.objects).forEach((object) => { filterMatching[object.type] = filterMatching[object.type] || {}; - filterMatching[object.type][object.objectId] = {...object.fieldMatch}; + const fieldMatch = object.fieldMatch ? {...object.fieldMatch} : {}; + filterMatching[object.type][object.objectId] = fieldMatch; }); this.env.model.dispatch(action, { - id: filter.id, filter, ...filterMatching, }); - this.env.openSidePanel("FilterPanel", {}); } onCancel() { @@ -177,13 +176,54 @@ export class EditFilterPanel extends Component { } this.env.openSidePanel("FilterPanel", {}); } - onFieldMatchUpdate(object, name) { - this.state.objects[object.id].fieldMatch.chain = name; - this.state.objects[object.id].fieldMatch.type = object.fields[name]?.type; + onFieldMatchUpdate(object, path, fieldInfo) { + if (!path) { + // Clear the field match if no path selected + this.state.objects[object.id].fieldMatch = {}; + return; + } + // Extract field definition from fieldInfo (V18> structure) + const fieldDef = + fieldInfo && fieldInfo.fieldDef ? fieldInfo.fieldDef : fieldInfo; + this.state.objects[object.id].fieldMatch = { + chain: path, + type: fieldDef?.type || "", + }; } toggleDateDefaultValue(ev) { this.state.defaultValue = ev.target.checked ? "this_month" : undefined; } + getModelField(fieldMatch) { + if (!fieldMatch || !fieldMatch.chain) { + return ""; + } + return fieldMatch.chain; + } + filterModelFieldSelectorField(field, path, coModel) { + if (!field.searchable) { + return false; + } + + // TODO: Define allowed field types based on filter type + const ALLOWED_FIELD_TYPES = [ + "char", + "text", + "selection", + "many2one", + "date", + "datetime", + ]; + + if (field.name === "id" && this.state.type === "relation") { + const paths = path.split("."); + const lastField = paths.at(-2); + if (!lastField || (lastField.relation && lastField.relation === coModel)) { + return true; + } + return false; + } + return ALLOWED_FIELD_TYPES.includes(field.type) || Boolean(field.relation); + } } EditFilterPanel.template = "spreadsheet_oca.EditFilterPanel"; 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 index 26844d77..0706956c 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/filter_panel_datasources.esm.js +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/filter_panel_datasources.esm.js @@ -1,46 +1,30 @@ -/** @odoo-module **/ - import * as spreadsheet from "@odoo/o-spreadsheet"; -import {Component, onWillStart, onWillUpdateProps} from "@odoo/owl"; -import {makeDynamicCols, makeDynamicRows} from "../utils/dynamic_generators.esm"; +import {Component, onWillStart, onWillUpdateProps, useState} 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 {FormViewDialog} from "@web/views/view_dialogs/form_view_dialog"; import {_t} from "@web/core/l10n/translation"; import {formatDate} from "@web/core/l10n/dates"; import {useService} from "@web/core/utils/hooks"; const {DateTime} = luxon; -const {sidePanelRegistry, topbarMenuRegistry} = spreadsheet.registries; +const {sidePanelRegistry, topbarMenuRegistry, pivotSidePanelRegistry} = + spreadsheet.registries; topbarMenuRegistry.addChild("data_sources", ["data"], (env) => { - let sequence = 100; - const children = env.model.getters.getPivotIds().map((pivotId, index) => ({ - id: `data_source_pivot_ ${pivotId}`, - name: env.model.getters.getPivotDisplayName(pivotId), - sequence: sequence++, - execute: (child_env) => { - child_env.model.dispatch("SELECT_PIVOT", { - pivotId: pivotId, - }); - child_env.openSidePanel("PivotPanel", {}); - }, - icon: "spreadsheet_oca.PivotIcon", - separator: index === env.model.getters.getPivotIds().length - 1, - })); + let sequence = 53; const lists = env.model.getters.getListIds().map((listId, index) => ({ id: `data_source_list_${listId}`, name: env.model.getters.getListDisplayName(listId), sequence: sequence++, execute: (child_env) => { child_env.model.dispatch("SELECT_ODOO_LIST", {listId: listId}); - child_env.openSidePanel("ListPanel", {}); + child_env.openSidePanel("ListPanel", {listId}); }, icon: "spreadsheet_oca.ListIcon", separator: index === env.model.getters.getListIds().length - 1, })); - return children.concat(lists).concat([ + return lists.concat([ { id: "refresh_all_data", name: _t("Refresh all data"), @@ -60,28 +44,32 @@ export class PivotPanelDisplay extends Component { onWillUpdateProps(this.modelData.bind(this)); } async modelData() { - this.PivotDataSource = await this.env.model.getters.getAsyncPivotDataSource( - this.props.pivotId - ); + this.PivotDataSource = this.env.model.getters.getPivot(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)); + const {rows = [], columns = []} = this.props.pivotDefinition; + return [...rows, ...columns].map((dim) => { + const label = dim.displayName || dim.fieldName; + return dim.granularity ? `${label} (${dim.granularity})` : label; + }); } get sortInformation() { const sortedColumn = this.props.pivotDefinition.sortedColumn; const orderTranslate = sortedColumn.order === "asc" ? _t("ascending") : _t("descending"); - const GroupByDisplayLabel = this.PivotDataSource.getMeasureDisplayName( - sortedColumn.measure - ); - return `${GroupByDisplayLabel} (${orderTranslate})`; + + let label = null; + if (sortedColumn.measure) { + const measure = this.PivotDataSource.getMeasure(sortedColumn.measure); + label = measure ? measure.displayName : sortedColumn.measure; + } else if (sortedColumn.groupBy) { + label = this.PivotDataSource.getFormattedGroupBy(sortedColumn.groupBy); + } + return `${label} (${orderTranslate})`; } get lastUpdate() { const lastUpdate = this.PivotDataSource.lastUpdate; @@ -106,84 +94,50 @@ export class PivotPanelDisplay extends Component { }); } 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, + const pivotId = this.props.pivotId; + const {type} = this.env.model.getters.getPivotCoreDefinition(pivotId); + const position = this.env.model.getters.getActivePosition(); + let table = null; + if (type === "ODOO") { + const dataSource = this.env.model.getters.getPivot(pivotId); + const model = await dataSource.copyModelWithOriginalDomain(); + table = model.getTableStructure().export(); + } else { + table = this.env.model.getters + .getPivot(pivotId) + .getTableStructure() + .export(); + } + this.env.model.dispatch("INSERT_PIVOT_WITH_TABLE", { + ...position, + pivotId, + table, + pivotMode: "static", }); - this.env.model.dispatch("REFRESH_PIVOT", {id: this.props.pivotId}); + this.env.model.dispatch("REFRESH_PIVOT", {id: pivotId}); } - async insertDynamicPivot() { - const datasourceModel = await this.env.model.getters - .getPivotDataSource(this.props.pivotId) - .copyModelWithOriginalDomain(); - var {cols, rows, measures} = datasourceModel.getTableStructure().export(); - const {dynamic_rows, number_of_rows, dynamic_cols, number_of_cols} = - await new Promise((resolve) => { - this.dialog.add( - FormViewDialog, - { - title: _t("Select the quantity of rows"), - resModel: "spreadsheet.select.row.number", - context: { - default_can_have_dynamic_cols: Boolean( - cols[0][0].fields.length - ), - }, - onRecordSaved: async (record) => { - resolve({ - dynamic_rows: record.data.dynamic_rows, - number_of_rows: record.data.number_of_rows, - dynamic_cols: record.data.dynamic_cols, - number_of_cols: record.data.number_of_cols, - }); - }, - }, - {onClose: () => resolve(false)} - ); - }); - if (!dynamic_rows && !dynamic_cols) { - return; - } - if (dynamic_rows) { - const indentations = rows.map((r) => r.indent); - const max_indentation = Math.max(...indentations); - rows = makeDynamicRows( - this.props.pivotDefinition.rowGroupBys, - number_of_rows, - 1, - max_indentation - ); + const pivotId = this.props.pivotId; + const {type} = this.env.model.getters.getPivotCoreDefinition(pivotId); + const position = this.env.model.getters.getActivePosition(); + let table = null; + if (type === "ODOO") { + const dataSource = this.env.model.getters.getPivot(this.props.pivotId); + const model = await dataSource.copyModelWithOriginalDomain(); + table = model.getTableStructure().export(); + } else { + table = this.env.model.getters + .getPivot(this.props.pivotId) + .getTableStructure() + .export(); } - if (dynamic_cols) { - cols = makeDynamicCols( - this.props.pivotDefinition.colGroupBys, - number_of_cols, - this.props.pivotDefinition.measures - ); - } - const table = { - cols, - rows, - measures, - }; - 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(), + this.env.model.dispatch("INSERT_PIVOT_WITH_TABLE", { + ...position, + pivotId, table, + pivotMode: "dynamic", }); - this.env.model.dispatch("REFRESH_PIVOT", {id: this.props.pivotId}); + this.env.model.dispatch("REFRESH_PIVOT", {id: pivotId}); } delete() { this.env.askConfirmation( @@ -208,10 +162,14 @@ PivotPanelDisplay.properties = { export class PivotPanel extends Component { get pivotId() { - return this.env.model.getters.getSelectedPivotId(); + return this.props.pivotId; + } + get pivotType() { + return this.env.model.getters.getPivotCoreDefinition(this.pivotId).type; } get pivotDefinition() { - return this.env.model.getters.getPivotDefinition(this.pivotId); + const dataSource = this.env.model.getters.getPivot(this.pivotId); + return dataSource ? dataSource.definition || {} : {}; } } @@ -220,13 +178,13 @@ PivotPanel.components = { PivotPanelDisplay, }; -sidePanelRegistry.add("PivotPanel", { - title: "Pivot table information", - Body: PivotPanel, +pivotSidePanelRegistry.add("ODOO", { + editor: PivotPanel, }); export class ListPanelDisplay extends Component { setup() { + this.state = useState({listRows: undefined}); this.dialog = useService("dialog"); onWillStart(this.modelData.bind(this)); onWillUpdateProps(this.modelData.bind(this)); @@ -262,6 +220,26 @@ export class ListPanelDisplay extends Component { domain: new Domain(domain).toList(), }); } + async insertList() { + const listId = this.props.listId; + const zone = this.env.model.getters.getSelectedZone(); + const dataSource = await this.env.model.getters.getAsyncListDataSource(listId); + const totalRows = parseInt(this.state.listRows, 10) || dataSource.maxPosition; + const list = this.env.model.getters.getListDefinition(listId); + const sheetId = this.env.model.getters.getActiveSheetId(); + const columns = list.columns.map((name) => ({ + name, + type: dataSource.getField(name).type, + })); + this.env.model.dispatch("RE_INSERT_ODOO_LIST_WITH_TABLE", { + sheetId: sheetId, + col: zone.left, + row: zone.top, + id: listId, + linesNumber: totalRows, + columns: columns, + }); + } delete() { this.env.askConfirmation( _t("Are you sure you want to delete this list?"), @@ -269,6 +247,7 @@ export class ListPanelDisplay extends Component { this.env.model.dispatch("REMOVE_ODOO_LIST", { listId: this.props.listId, }); + this.env.openSidePanel("ListPanel", {}); } ); } @@ -278,17 +257,17 @@ ListPanelDisplay.template = "spreadsheet_oca.ListPanelDisplay"; ListPanelDisplay.components = { DomainSelector, }; -ListPanelDisplay.properties = { +ListPanelDisplay.props = { listId: String, listDefinition: Object, }; export class ListPanel extends Component { get listId() { - return this.env.model.getters.getSelectedListId(); + return this.props.listId; } get listDefinition() { - return this.env.model.getters.getListDefinition(this.listId); + return this.env.model.getters.getListDefinition(this.listId) || {}; } } diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/image_file_store.esm.js b/spreadsheet_oca/static/src/spreadsheet/bundle/image_file_store.esm.js new file mode 100644 index 00000000..919524e8 --- /dev/null +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/image_file_store.esm.js @@ -0,0 +1,33 @@ +export class ImageFileStore { + constructor(resModel, resId, http, orm) { + this.resModel = resModel; + this.resId = resId; + this.http = http; + this.orm = orm; + } + + async upload(file) { + const route = "/web/binary/upload_attachment"; + const params = { + ufile: [file], + csrf_token: odoo.csrf_token, + model: this.resModel, + id: this.resId, + }; + const fileData = JSON.parse(await this.http.post(route, params, "text"))[0]; + const [accessToken] = await this.orm.call( + "ir.attachment", + "generate_access_token", + [fileData.id] + ); + return `/web/image/${fileData.id}?access_token=${accessToken}`; + } + + async delete(path) { + const attachmentId = path.split("/").pop(); + if (Number.isNaN(attachmentId)) { + throw new Error("Invalid path: " + path); + } + await this.orm.unlink("ir.attachment", [parseInt(attachmentId, 10)]); + } +} diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/odoo_panels.esm.js b/spreadsheet_oca/static/src/spreadsheet/bundle/odoo_panels.esm.js index 92785576..dfbe770a 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/odoo_panels.esm.js +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/odoo_panels.esm.js @@ -1,13 +1,12 @@ -/** @odoo-module */ - import * as spreadsheet from "@odoo/o-spreadsheet"; + import {Domain} from "@web/core/domain"; import {Many2XAutocomplete} from "@web/views/fields/relational_utils"; -import {useService} from "@web/core/utils/hooks"; import {_t} from "@web/core/l10n/translation"; +import {useService} from "@web/core/utils/hooks"; -const {chartSidePanelComponentRegistry} = spreadsheet.registries; -const {LineBarPieDesignPanel} = spreadsheet.components; +const {chartSidePanelComponentRegistry, chartSubtypeRegistry} = spreadsheet.registries; +const {PieChartDesignPanel} = spreadsheet.components; const {Component} = owl; export class OdooPanel extends Component { @@ -87,13 +86,79 @@ OdooStackablePanel.template = "spreadsheet_oca.OdooStackablePanel"; chartSidePanelComponentRegistry .add("odoo_line", { configuration: OdooStackablePanel, - design: LineBarPieDesignPanel, + design: PieChartDesignPanel, }) .add("odoo_bar", { configuration: OdooStackablePanel, - design: LineBarPieDesignPanel, + design: PieChartDesignPanel, }) .add("odoo_pie", { configuration: OdooPanel, - design: LineBarPieDesignPanel, + design: PieChartDesignPanel, }); + +chartSubtypeRegistry.add("odoo_line", { + matcher: (definition) => + definition.type === "odoo_line" && !definition.stacked && !definition.fillArea, + subtypeDefinition: {stacked: false, fillArea: false}, + displayName: _t("Line"), + chartSubtype: "odoo_line", + chartType: "odoo_line", + category: "line", + preview: "o-spreadsheet-ChartPreview.LINE_CHART", +}); +chartSubtypeRegistry.add("odoo_stacked_line", { + matcher: (definition) => + definition.type === "odoo_line" && definition.stacked && !definition.fillArea, + subtypeDefinition: {stacked: true, fillArea: false}, + displayName: _t("Stacked Line"), + chartSubtype: "odoo_stacked_line", + chartType: "odoo_line", + category: "line", + preview: "o-spreadsheet-ChartPreview.STACKED_LINE_CHART", +}); +chartSubtypeRegistry.add("odoo_area", { + matcher: (definition) => + definition.type === "odoo_line" && !definition.stacked && definition.fillArea, + subtypeDefinition: {stacked: false, fillArea: true}, + displayName: _t("Area"), + chartSubtype: "odoo_area", + chartType: "odoo_line", + category: "area", + preview: "o-spreadsheet-ChartPreview.AREA_CHART", +}); +chartSubtypeRegistry.add("odoo_stacked_area", { + matcher: (definition) => + definition.type === "odoo_line" && definition.stacked && definition.fillArea, + subtypeDefinition: {stacked: true, fillArea: true}, + displayName: _t("Stacked Area"), + chartSubtype: "odoo_stacked_area", + chartType: "odoo_line", + category: "area", + preview: "o-spreadsheet-ChartPreview.STACKED_AREA_CHART", +}); +chartSubtypeRegistry.add("odoo_bar", { + matcher: (definition) => definition.type === "odoo_bar" && !definition.stacked, + subtypeDefinition: {stacked: false}, + displayName: _t("Column"), + chartSubtype: "odoo_bar", + chartType: "odoo_bar", + category: "column", + preview: "o-spreadsheet-ChartPreview.COLUMN_CHART", +}); +chartSubtypeRegistry.add("odoo_stacked_bar", { + matcher: (definition) => definition.type === "odoo_bar" && definition.stacked, + subtypeDefinition: {stacked: true}, + displayName: _t("Stacked Column"), + chartSubtype: "odoo_stacked_bar", + chartType: "odoo_bar", + category: "column", + preview: "o-spreadsheet-ChartPreview.STACKED_COLUMN_CHART", +}); +chartSubtypeRegistry.add("odoo_pie", { + displayName: _t("Pie"), + chartSubtype: "odoo_pie", + chartType: "odoo_pie", + category: "pie", + preview: "o-spreadsheet-ChartPreview.PIE_CHART", +}); diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml index 45549754..242124ba 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml @@ -67,14 +67,11 @@ -
+
-
- Last updated at -
+
+ Last updated at +
@@ -123,15 +123,32 @@ Edit domain

-
- Last updated at -
+
+
+ + +
+
+ Last updated at +
@@ -276,12 +293,13 @@
@@ -311,7 +329,7 @@
- + @@ -444,34 +463,6 @@
- @@ -633,11 +624,7 @@
- +
Link to Odoo menu
@@ -647,11 +634,7 @@
- +
Link to Odoo menu
@@ -661,11 +644,7 @@
- +
Link to Odoo menu
@@ -675,11 +654,7 @@
- +
Link to Odoo menu
@@ -689,11 +664,7 @@
- +
Link to Odoo menu
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 d14d7b36..5055a15c 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_action.esm.js +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_action.esm.js @@ -1,37 +1,45 @@ -/** @odoo-module **/ - import * as spreadsheet from "@odoo/o-spreadsheet"; -import {makeDynamicCols, makeDynamicRows} from "../utils/dynamic_generators.esm"; -import {ListDataSource} from "@spreadsheet/list/list_data_source"; -import {PivotDataSource} from "@spreadsheet/pivot/pivot_data_source"; + +import {Domain} from "@web/core/domain"; import {SpreadsheetControlPanel} from "./spreadsheet_controlpanel.esm"; import {SpreadsheetRenderer} from "./spreadsheet_renderer.esm"; +import {deepCopy} from "@web/core/utils/objects"; +import {helpers} from "@odoo/o-spreadsheet"; import {registry} from "@web/core/registry"; +import {standardActionServiceProps} from "@web/webclient/actions/action_service"; import {useService} from "@web/core/utils/hooks"; +const {load} = spreadsheet; + const uuidGenerator = new spreadsheet.helpers.UuidGenerator(); const actionRegistry = registry.category("actions"); -const {Component, onMounted, onWillStart, useSubEnv} = owl; +const {Component, onWillStart, useSubEnv} = owl; +const {parseDimension, isDateOrDatetimeField} = helpers; + +function normalizeGroupBys(dimensions, fields) { + return dimensions.map((dimension) => { + if ( + isDateOrDatetimeField(fields[dimension.fieldName]) && + !dimension.granularity + ) { + return {granularity: "month", ...dimension}; + } + return dimension; + }); +} export class ActionSpreadsheetOca extends Component { setup() { - this.router = useService("router"); this.orm = useService("orm"); this.notification = useService("notification"); const params = this.props.action.params || this.props.action.context.params; - this.spreadsheetId = params.spreadsheet_id; + this.spreadsheetId = params.spreadsheet_id || params.active_id; this.model = params.model || "spreadsheet.spreadsheet"; this.import_data = params.import_data || {}; - onMounted(() => { - this.router.pushState({ - spreadsheet_id: this.spreadsheetId, - model: this.model, - }); - }); onWillStart(async () => { // We need to load in case the data comes from an XLSX this.record = - spreadsheet.load( + load( await this.orm.call( this.model, "get_spreadsheet_data", @@ -46,6 +54,7 @@ export class ActionSpreadsheetOca extends Component { notifyUser: this.notifyUser.bind(this), }); } + notifyUser(notification) { this.notification.add(notification.text, { type: notification.type, @@ -60,7 +69,6 @@ export class ActionSpreadsheetOca extends Component { this.orm.call(this.model, "write", [this.spreadsheetId, data]); } else { this.spreadsheetId = await this.orm.call(this.model, "create", [data]); - this.router.pushState({spreadsheet_id: this.spreadsheetId}); } } /** @@ -84,7 +92,6 @@ export class ActionSpreadsheetOca extends Component { } async importDataGraph(spreadsheet_model) { var sheetId = spreadsheet_model.getters.getActiveSheetId(); - var y = 0; if (this.import_data.new === undefined && this.import_data.new_sheet) { sheetId = uuidGenerator.uuidv4(); spreadsheet_model.dispatch("CREATE_SHEET", { @@ -101,23 +108,29 @@ export class ActionSpreadsheetOca extends Component { // TODO: Add a way to detect the last row total height } const dataSourceId = uuidGenerator.uuidv4(); + const chartType = `odoo_${this.import_data.metaData.mode}`; const definition = { - title: this.import_data.name, - type: "odoo_" + this.import_data.metaData.mode, + title: {text: this.import_data.name}, + type: chartType, + fillArea: chartType === "odoo_line", background: "#FFFFFF", stacked: this.import_data.metaData.stacked, metaData: this.import_data.metaData, searchParams: this.cleanSearchParams(), dataSourceId: dataSourceId, + id: uuidGenerator.uuidv4(), + cumulative: this.import_data.metaData.cumulated, + cumulatedStart: this.import_data.metaData.cumulatedStart, legendPosition: "top", verticalAxisPosition: "left", + actionXmlId: this.import_data.actionXmlId, }; spreadsheet_model.dispatch("CREATE_CHART", { sheetId, id: dataSourceId, position: { x: 0, - y: y, + y: 0, }, definition, }); @@ -159,115 +172,107 @@ export class ActionSpreadsheetOca extends Component { } row += 1; } - return {sheetId, row}; + return sheetId; } async importDataList(spreadsheet_model) { - var {sheetId, row} = this.importCreateOrReuseSheet(spreadsheet_model); - const dataSourceId = uuidGenerator.uuidv4(); - var list_info = { + var sheetId = this.importCreateOrReuseSheet(spreadsheet_model); + if (!sheetId) { + const sheetIds = spreadsheet_model.getters.getSheetIds(); + sheetId = sheetIds.length ? sheetIds[0] : uuidGenerator.uuidv4(); + } + const listId = spreadsheet_model.getters.getNextListId(); + const 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, + domain: new Domain(this.import_data.metaData.domain).toJson(), context: this.import_data.metaData.context, orderBy: this.import_data.metaData.orderBy, }, name: this.import_data.name, + actionXmlId: this.import_data.actionXmlId, }; - const dataSource = spreadsheet_model.config.custom.dataSources.add( - dataSourceId, - ListDataSource, - list_info - ); - await dataSource.load(); - spreadsheet_model.dispatch("INSERT_ODOO_LIST", { + const columns = this.import_data.metaData.columns.map((c) => ({ + name: c.name, + type: this.import_data.metaData.fields[c.name].type, + })); + spreadsheet_model.dispatch("INSERT_ODOO_LIST_WITH_TABLE", { sheetId, col: 0, - row: row, - id: spreadsheet_model.getters.getNextListId(), - dataSourceId, + row: 0, + id: listId, definition: list_info, linesNumber: this.import_data.dyn_number_of_rows, - columns: this.import_data.metaData.columns, + columns: columns, }); - const columns = []; - for (let col = 0; col < this.import_data.metaData.columns.length; col++) { - columns.push(col); - } + const dataSource = spreadsheet_model.getters.getListDataSource(listId); + await dataSource.load(); spreadsheet_model.dispatch("AUTORESIZE_COLUMNS", { sheetId, - cols: columns, + cols: Array.from({length: columns.length}, (_, i) => i), }); } async importDataPivot(spreadsheet_model) { - var {sheetId, row} = this.importCreateOrReuseSheet(spreadsheet_model); - const dataSourceId = uuidGenerator.uuidv4(); - const colGroupBys = this.import_data.metaData.colGroupBys.concat( - this.import_data.metaData.expandedColGroupBys + var sheetId = this.importCreateOrReuseSheet(spreadsheet_model); + const pivotId = uuidGenerator.uuidv4(); + const fields = this.import_data.metaData.fields || {}; + const activeMeasures = this.import_data.metaData.activeMeasures; + const measures = activeMeasures.map((measure) => ({ + id: fields[measure]?.aggregator + ? `${measure}:${fields[measure].aggregator}` + : measure, + fieldName: measure, + aggregator: fields[measure]?.aggregator, + })); + const sortedMeasure = this.import_data.metaData.sortedColumn?.measure; + const sortedColumn = activeMeasures.includes(sortedMeasure) + ? this.import_data.metaData.sortedColumn + : null; + const colGroupBys = (this.import_data.metaData.colGroupBys || []).concat( + this.import_data.metaData.expandedColGroupBys || [] ); - const rowGroupBys = this.import_data.metaData.rowGroupBys.concat( - this.import_data.metaData.expandedRowGroupBys + const rowGroupBys = (this.import_data.metaData.rowGroupBys || []).concat( + this.import_data.metaData.expandedRowGroupBys || [] ); - const pivot_info = { - metaData: { - colGroupBys, - rowGroupBys, - activeMeasures: this.import_data.metaData.activeMeasures, - resModel: this.import_data.metaData.resModel, - sortedColumn: this.import_data.metaData.sortedColumn, - }, - searchParams: this.cleanSearchParams(), - name: this.import_data.name, - }; - const dataSource = spreadsheet_model.config.custom.dataSources.add( - dataSourceId, - PivotDataSource, - pivot_info - ); - await dataSource.load(); - var {cols, rows, measures} = dataSource.getTableStructure().export(); - if (this.import_data.dyn_number_of_rows) { - const indentations = rows.map((r) => r.indent); - const max_indentation = Math.max(...indentations); - rows = makeDynamicRows( - rowGroupBys, - this.import_data.dyn_number_of_rows, - 1, - max_indentation - ); - } - if (this.import_data.dyn_number_of_cols) { - cols = makeDynamicCols( - colGroupBys, - this.import_data.dyn_number_of_cols, - this.import_data.metaData.activeMeasures - ); - } - const table = { - cols, - rows, + const pivot_info = deepCopy({ + type: "ODOO", + domain: new Domain(this.import_data.searchParams.domain).toJson(), + context: this.import_data.searchParams.context, + sortedColumn, measures, - }; - spreadsheet_model.dispatch("INSERT_PIVOT", { + model: this.import_data.metaData.resModel, + columns: normalizeGroupBys(colGroupBys.map(parseDimension), fields), + rows: normalizeGroupBys(rowGroupBys.map(parseDimension), fields), + name: this.import_data.name, + actionXmlId: this.import_data.actionXmlId, + }); + spreadsheet_model.dispatch("ADD_PIVOT", { + pivotId, + pivot: pivot_info, + }); + const ds = spreadsheet_model.getters.getPivot(pivotId); + await ds.load(); + const table = ds.getTableStructure(); + spreadsheet_model.dispatch("INSERT_PIVOT_WITH_TABLE", { sheetId, col: 0, - row: row, - id: spreadsheet_model.getters.getNextPivotId(), - table, - dataSourceId, - definition: pivot_info, + row: 0, + pivotId, + table: table.export(), + pivotMode: "dynamic", }); const columns = []; - for (let col = 0; col < table.cols[table.cols.length - 1].length; col++) { + for ( + let col = 0; + col <= table.columns[table.columns.length - 1].length; + col++ + ) { columns.push(col); } - spreadsheet_model.dispatch("AUTORESIZE_COLUMNS", { - sheetId, - cols: columns, - }); + spreadsheet_model.dispatch("AUTORESIZE_COLUMNS", {sheetId, cols: columns}); } async importData(spreadsheet_model) { if (this.import_data.mode === "pivot") { @@ -286,6 +291,7 @@ ActionSpreadsheetOca.components = { SpreadsheetRenderer, SpreadsheetControlPanel, }; +ActionSpreadsheetOca.props = {...standardActionServiceProps}; actionRegistry.add("action_spreadsheet_oca", ActionSpreadsheetOca, { force: true, }); diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_controlpanel.esm.js b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_controlpanel.esm.js index 9a3cecd1..4b5d8647 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_controlpanel.esm.js +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_controlpanel.esm.js @@ -1,7 +1,6 @@ -/** @odoo-module **/ - import {Component} from "@odoo/owl"; import {ControlPanel} from "@web/search/control_panel/control_panel"; +import {useService} from "@web/core/utils/hooks"; const {useState} = owl; @@ -12,15 +11,35 @@ export class SpreadsheetName extends Component { }); } _onNameChanged(ev) { + if (this.props.isReadonly) { + return; + } if (ev.target.value) { this.env.saveRecord({name: ev.target.value}); } this.state.name = ev.target.value; + if (this.props.onChanged) { + this.props.onChanged(ev); + } } } SpreadsheetName.template = "spreadsheet_oca.SpreadsheetName"; +SpreadsheetName.props = { + name: String, + isReadonly: Boolean, + onChanged: {type: Function, optional: true}, +}; -export class SpreadsheetControlPanel extends ControlPanel {} +export class SpreadsheetControlPanel extends ControlPanel { + setup() { + super.setup(); + this.actionService = useService("action"); + } + + onBreadcrumbClicked(jsId) { + this.actionService.restore(jsId); + } +} SpreadsheetControlPanel.template = "spreadsheet_oca.SpreadsheetControlPanel"; SpreadsheetControlPanel.props = { ...ControlPanel.props, diff --git a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_renderer.esm.js b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_renderer.esm.js index 1627274e..24cc65ed 100644 --- a/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_renderer.esm.js +++ b/spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_renderer.esm.js @@ -1,21 +1,20 @@ -/** @odoo-module **/ - import * as spreadsheet from "@odoo/o-spreadsheet"; + import {Component} from "@odoo/owl"; -import {ConfirmationDialog} from "@web/core/confirmation_dialog/confirmation_dialog"; -import {DataSources} from "@spreadsheet/data_sources/data_sources"; -import {Dialog} from "@web/core/dialog/dialog"; -import {Field} from "@web/views/fields/field"; +import {ImageFileStore} from "./image_file_store.esm"; +import {OdooDataProvider} from "@spreadsheet/data_sources/odoo_data_provider"; +import {SpreadsheetComponent} from "@spreadsheet/actions/spreadsheet_component"; import {_t} from "@web/core/l10n/translation"; import {loadSpreadsheetDependencies} from "@spreadsheet/assets_backend/helpers"; -import {migrate} from "@spreadsheet/o_spreadsheet/migration"; import {useService} from "@web/core/utils/hooks"; -import {useSetupAction} from "@web/webclient/actions/action_hook"; +import {useSetupAction} from "@web/search/action_hook"; +import {user} from "@web/core/user"; import {waitForDataLoaded} from "@spreadsheet/helpers/model"; -import {createDefaultCurrencyFormat} from "@spreadsheet/currency/helpers"; -const {Spreadsheet, Model} = spreadsheet; +const {Model, load} = spreadsheet; + const {useSubEnv, onWillStart} = owl; +const {useStoreProvider, ModelStore} = spreadsheet.stores; const uuidGenerator = new spreadsheet.helpers.UuidGenerator(); class SpreadsheetTransportService { @@ -26,11 +25,14 @@ class SpreadsheetTransportService { this.res_id = res_id; this.channel = "spreadsheet_oca;" + this.model + ";" + this.res_id; this.bus_service.addChannel(this.channel); - this.bus_service.addEventListener( - "notification", - this.onNotification.bind(this) - ); + this.dialog = useService("dialog"); + this.bus_service.subscribe("notification", (payload) => { + if (payload.id === this.res_id) { + this._handleNotification(payload); + } + }); this.listeners = []; + this._listener = null; } onNotification({detail: notifications}) { for (const {payload, type} of notifications) { @@ -46,18 +48,46 @@ class SpreadsheetTransportService { } } } - sendMessage(message) { - this.orm.call(this.model, "send_spreadsheet_message", [[this.res_id], message]); + async sendMessage(message) { + const isAccepted = await this.orm.call(this.model, "send_spreadsheet_message", [ + [this.res_id], + message, + this.accessToken, + ]); + if (isAccepted) { + this._handleNotification(message); + } } onNewMessage(id, callback) { - this.listeners.push({id, callback}); + this._listener = callback; + for (const message of this.listeners) { + callback(message); + } + this.listeners = []; } leave(id) { this.listeners = this.listeners.filter((listener) => listener.id !== id); } + _handleNotification(payload) { + if (!this._listener) { + this.listeners.push(payload); + } else { + this._listener(payload); + } + } } export class SpreadsheetRenderer extends Component { + createDefaultCurrency(currency) { + if (!currency) { + return undefined; + } + return { + symbol: currency.symbol, + position: currency.position, + decimalPlaces: currency.decimal_places, + }; + } getLocales() { const orm = useService("orm"); return async () => { @@ -89,27 +119,33 @@ export class SpreadsheetRenderer extends Component { } setup() { this.orm = useService("orm"); + this.http = useService("http"); this.bus_service = this.env.services.bus_service; - this.user = useService("user"); this.ui = useService("ui"); this.action = useService("action"); this.dialog = useService("dialog"); - const dataSources = new DataSources(this.env); - this.confirmDialog = this.closeDialog; + this.notifications = useService("notification"); + const odooDataProvider = new OdooDataProvider(this.env); this.loadCurrencies = this.getCurrencies(); this.loadLocales = this.getLocales(); const defaultCurrency = this.props.record.default_currency; - const defaultCurrencyFormat = defaultCurrency - ? createDefaultCurrencyFormat(defaultCurrency) - : undefined; + this.fileStore = new ImageFileStore( + this.props.model, + this.props.res_id, + this.http, + this.orm + ); + this.stores = useStoreProvider(); + // The o-spreadsheet Model handles currency formatting internally this.spreadsheet_model = new Model( - migrate(this.props.record.spreadsheet_raw), + load(this.props.record.spreadsheet_raw), { - custom: {env: this.env, orm: this.orm, dataSources}, - defaultCurrencyFormat, + custom: {env: this.env, orm: this.orm, odooDataProvider}, + defaultCurrency: this.createDefaultCurrency(defaultCurrency), external: { loadCurrencies: this.loadCurrencies, loadLocales: this.loadLocales, + fileStore: this.fileStore, }, transportService: new SpreadsheetTransportService( this.orm, @@ -119,7 +155,8 @@ export class SpreadsheetRenderer extends Component { ), client: { id: uuidGenerator.uuidv4(), - name: this.user.name, + name: user.name, + userId: user.userId, }, mode: this.props.record.mode, }, @@ -127,18 +164,22 @@ export class SpreadsheetRenderer extends Component { ); useSubEnv({ saveSpreadsheet: this.onSpreadsheetSaved.bind(this), - askConfirmation: this.askConfirmation.bind(this), downloadAsXLXS: this.downloadAsXLXS.bind(this), }); onWillStart(async () => { await loadSpreadsheetDependencies(); - await dataSources.waitForAllLoaded(); + await waitForDataLoaded(this.spreadsheet_model); await this.env.importData(this.spreadsheet_model); + this.spreadsheet_model.joinSession(); + this.stores.inject(ModelStore, this.spreadsheet_model); }); useSetupAction({ - beforeLeave: () => this.onSpreadsheetSaved(), + beforeLeave: () => { + this.onSpreadsheetSaved(); + return Promise.resolve(); + }, }); - dataSources.addEventListener("data-source-updated", () => { + odooDataProvider.addEventListener("data-source-updated", () => { const sheetId = this.spreadsheet_model.getters.getActiveSheetId(); this.spreadsheet_model.dispatch("EVALUATE_CELLS", {sheetId}); }); @@ -147,14 +188,7 @@ export class SpreadsheetRenderer extends Component { const data = this.spreadsheet_model.exportData(); this.env.saveRecord({spreadsheet_raw: data}); this.spreadsheet_model.leaveSession(); - } - askConfirmation(content, confirm) { - this.dialog.add(ConfirmationDialog, { - title: _t("Odoo Spreadsheet"), - body: content, - confirm, - confirmLabel: _t("Confirm"), - }); + this.spreadsheet_model.off("update", this); } async downloadAsXLXS() { this.ui.block(); @@ -172,14 +206,10 @@ export class SpreadsheetRenderer extends Component { } SpreadsheetRenderer.template = "spreadsheet_oca.SpreadsheetRenderer"; -SpreadsheetRenderer.components = { - Spreadsheet, - Field, - Dialog, -}; +SpreadsheetRenderer.components = {SpreadsheetComponent}; SpreadsheetRenderer.props = { record: Object, - res_id: {type: Number, optional: true}, + res_id: Number, model: String, - importData: {type: Function, optional: true}, + importData: Function, }; diff --git a/spreadsheet_oca/static/src/spreadsheet/graph_controller.esm.js b/spreadsheet_oca/static/src/spreadsheet/graph_controller.esm.js index fac9eeaa..04238fa6 100644 --- a/spreadsheet_oca/static/src/spreadsheet/graph_controller.esm.js +++ b/spreadsheet_oca/static/src/spreadsheet/graph_controller.esm.js @@ -1,4 +1,3 @@ -/** @odoo-module **/ import {GraphRenderer} from "@web/views/graph/graph_renderer"; import {patch} from "@web/core/utils/patch"; diff --git a/spreadsheet_oca/static/src/spreadsheet/graph_controller.xml b/spreadsheet_oca/static/src/spreadsheet/graph_controller.xml index 37b6e49a..f54f8868 100644 --- a/spreadsheet_oca/static/src/spreadsheet/graph_controller.xml +++ b/spreadsheet_oca/static/src/spreadsheet/graph_controller.xml @@ -1,6 +1,10 @@ - + diff --git a/spreadsheet_oca/static/src/spreadsheet/list_renderer.esm.js b/spreadsheet_oca/static/src/spreadsheet/list_renderer.esm.js index 09fe11d1..beccbe38 100644 --- a/spreadsheet_oca/static/src/spreadsheet/list_renderer.esm.js +++ b/spreadsheet_oca/static/src/spreadsheet/list_renderer.esm.js @@ -1,13 +1,14 @@ -/** @odoo-module **/ import {useBus, useService} from "@web/core/utils/hooks"; + +import {HandleField} from "@web/views/fields/handle/handle_field"; import {ListRenderer} from "@web/views/list/list_renderer"; import {omit} from "@web/core/utils/objects"; import {patch} from "@web/core/utils/patch"; +import {user} from "@web/core/user"; patch(ListRenderer.prototype, { setup() { super.setup(...arguments); - this.userService = useService("user"); this.actionService = useService("action"); useBus( this.env.bus, @@ -34,8 +35,8 @@ patch(ListRenderer.prototype, { domain: model.domain, orderBy: model.orderBy, context: omit( - model.context, - ...Object.keys(this.userService.context) + model.searchParams?.context || {}, + ...Object.keys(user.context) ), columns: this.getSpreadsheetColumns(), fields: model.fields, @@ -48,10 +49,14 @@ patch(ListRenderer.prototype, { }, getSpreadsheetColumns() { const fields = this.env.model.root.fields; - return this.state.columns + return this.columns .filter( - (col) => col.type === "field" && fields[col.name].type !== "binary" - // We want to avoid binary fields + (col) => + col.type === "field" && + col.field.component !== HandleField && + !col.relatedPropertyField && + !["binary", "json"].includes(fields[col.name].type) + // We want to avoid binary or json fields ) .map((col) => ({name: col.name, type: fields[col.name].type})); }, diff --git a/spreadsheet_oca/static/src/spreadsheet/pivot_controller.esm.js b/spreadsheet_oca/static/src/spreadsheet/pivot_controller.esm.js index 3423a670..7b787a73 100644 --- a/spreadsheet_oca/static/src/spreadsheet/pivot_controller.esm.js +++ b/spreadsheet_oca/static/src/spreadsheet/pivot_controller.esm.js @@ -1,7 +1,5 @@ -/** @odoo-module **/ - import {PivotRenderer} from "@web/views/pivot/pivot_renderer"; -import {_lt} from "@web/core/l10n/translation"; +import {_t} from "@web/core/l10n/translation"; import {patch} from "@web/core/utils/patch"; patch(PivotRenderer.prototype, { @@ -38,11 +36,11 @@ patch(PivotRenderer.prototype, { ); }, getSpreadsheetInsertionTooltip() { - var message = _lt("Add to spreadsheet"); + var message = _t("Add to spreadsheet"); if (this.containsDuplicatedGroupBys()) { - message = _lt("Duplicated groupbys in pivot are not supprted"); + message = _t("Duplicated groupbys in pivot are not supported"); } else if (this.isComparingInfo()) { - message = _lt("Comparisons in pivot are not supprted"); + message = _t("Comparisons in pivot are not supported"); } return message; }, @@ -53,8 +51,10 @@ patch(PivotRenderer.prototype, { additionalContext: { default_name: this.model.metaData.title, default_datasource_name: this.model.metaData.title, - default_can_be_dynamic: true, - default_can_have_dynamic_cols: this.containsColGroupBys(), + default_can_be_dynamic: false, + default_can_have_dynamic_cols: false, + // Default_can_be_dynamic: true, + // default_can_have_dynamic_cols: this.containsColGroupBys(), default_import_data: { mode: "pivot", metaData: JSON.parse(JSON.stringify(this.model.metaData)), diff --git a/spreadsheet_oca/static/src/spreadsheet/pivot_controller.xml b/spreadsheet_oca/static/src/spreadsheet/pivot_controller.xml index 8aa73c8a..2eebc450 100644 --- a/spreadsheet_oca/static/src/spreadsheet/pivot_controller.xml +++ b/spreadsheet_oca/static/src/spreadsheet/pivot_controller.xml @@ -1,6 +1,10 @@ - +