Skip to content

Commit 0358c80

Browse files
dnplkndllclaude
andcommitted
[ADD] dms_spreadsheet: create/edit OCA spreadsheets in DMS
Bridges OCA DMS (dms.file) with spreadsheet_oca so users can create and edit spreadsheets directly from the DMS file manager. Features: - dms.file gains a `handler` field (selection: 'spreadsheet') - Inherits spreadsheet.abstract for collaborative editing via bus + revisions - Auto-sets handler/mimetype on create when content is application/o-spreadsheet - open_spreadsheet() returns action_spreadsheet_oca client action - get_spreadsheet_data() bridges dms.file content to spreadsheet raw JSON - Storage bridge: _compute/_inverse_content delegates to spreadsheet_binary_data for spreadsheet-type files; standard DMS content path for all others - CreateSpreadsheetWizard: create named spreadsheet in any directory, opens editor immediately on confirm - Directory form + kanban: "New Spreadsheet" button pre-fills directory_id - File form: "Open Spreadsheet" button shown when handler == 'spreadsheet' - File list/search: "Spreadsheets" filter - Server action on dms.file for "New Spreadsheet" in file manager action menu Depends: dms, spreadsheet_oca (OCA/spreadsheet 18.0) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 380025c commit 0358c80

21 files changed

Lines changed: 705 additions & 0 deletions

dms_spreadsheet/README.rst

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
=================
2+
DMS Spreadsheet
3+
=================
4+
5+
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
6+
!! This file is generated by oca-gen-addon-readme !!
7+
!! changes will be overwritten. !!
8+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
9+
!! source digest: sha256:placeholder
10+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
11+
12+
.. |badge1| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
13+
:target: https://www.gnu.org/licenses/agpl
14+
:alt: License: AGPL-3
15+
16+
|badge1|
17+
18+
Create and edit OCA spreadsheets directly within the DMS file manager.
19+
20+
Requires ``dms`` and ``spreadsheet_oca``.
21+
22+
**Features**
23+
24+
* **New Spreadsheet** button in DMS directory views (form and kanban)
25+
* Opening a ``.o-spreadsheet`` file launches the full OCA spreadsheet editor
26+
* Spreadsheet data stored transparently via DMS file storage (database,
27+
attachment, or filesystem)
28+
* Read-only mode for users without write access to the DMS file
29+
* Revision history via ``spreadsheet.abstract`` mixin
30+
31+
**Table of contents**
32+
33+
.. contents::
34+
:local:
35+
36+
Usage
37+
=====
38+
39+
**Creating a new spreadsheet**
40+
41+
1. Open **DMS** → navigate to the target directory.
42+
2. Click the **New Spreadsheet** button (available in the form header and the
43+
kanban directory card).
44+
3. Enter a name and confirm — the OCA spreadsheet editor opens immediately.
45+
4. Edit your spreadsheet; changes are saved automatically via the revision
46+
protocol.
47+
48+
**Opening an existing spreadsheet**
49+
50+
1. Browse to the directory containing the file.
51+
2. Open the file record; click **Open Spreadsheet** in the form header.
52+
Alternatively, double-click the file in the kanban/list view — if the MIME
53+
type is ``application/o-spreadsheet`` the editor is launched directly.
54+
55+
**Read-only access**
56+
57+
Users who have read but not write access to a DMS file will have the
58+
spreadsheet opened in **read-only** mode (the editor toolbar is disabled).
59+
60+
Configuration
61+
=============
62+
63+
No additional configuration is required after installing the module.
64+
65+
Spreadsheet files are identified by the MIME type
66+
``application/o-spreadsheet`` and the ``handler`` field value ``spreadsheet``
67+
on ``dms.file`` records. Both are set automatically when a file is created
68+
through the **New Spreadsheet** wizard or when an existing DMS file's MIME
69+
type is set to ``application/o-spreadsheet``.
70+
71+
Access to individual spreadsheets is governed by the existing DMS permission
72+
system (storage-level groups and directory-level ACLs).
73+
74+
Known issues / Roadmap
75+
======================
76+
77+
* Template library: pre-built spreadsheet templates selectable from the
78+
creation wizard.
79+
* Import XLSX: convert an uploaded Excel file to an OCA spreadsheet in-place.
80+
* Thumbnail preview: render a small image of the spreadsheet content as the
81+
DMS file thumbnail.
82+
* Collaborative editing: expose the OCA collaborative-revision WebSocket
83+
endpoint for DMS files.
84+
85+
Bug Tracker
86+
===========
87+
88+
Bugs are tracked on `GitHub Issues <https://github.com/OCA/dms/issues>`_.
89+
90+
Credits
91+
=======
92+
93+
Authors
94+
~~~~~~~
95+
96+
* Ledo Enterprises
97+
98+
Contributors
99+
~~~~~~~~~~~~
100+
101+
* Ledo Enterprises <https://ledoweb.com>
102+
103+
Maintainers
104+
~~~~~~~~~~~
105+
106+
This module is part of the `OCA/dms <https://github.com/OCA/dms>`_ project.

dms_spreadsheet/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Copyright 2026 Ledo Enterprises
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
3+
4+
from . import models, wizard

dms_spreadsheet/__manifest__.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Copyright 2026 Ledo Enterprises
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
3+
4+
{
5+
"name": "DMS Spreadsheet",
6+
"summary": "Create and edit spreadsheets directly within the DMS file manager",
7+
"version": "18.0.1.0.0",
8+
"category": "Document Management",
9+
"license": "AGPL-3",
10+
"author": "Ledo Enterprises, Odoo Community Association (OCA)",
11+
"website": "https://github.com/OCA/dms",
12+
"depends": [
13+
"dms",
14+
"spreadsheet_oca",
15+
],
16+
"data": [
17+
"security/ir.model.access.csv",
18+
"wizard/dms_spreadsheet_create_views.xml",
19+
"views/dms_file_views.xml",
20+
"views/dms_directory_views.xml",
21+
],
22+
"installable": True,
23+
"auto_install": False,
24+
}

dms_spreadsheet/models/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Copyright 2026 Ledo Enterprises
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
3+
4+
from . import dms_directory, dms_file
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Copyright 2026 Ledo Enterprises
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
3+
4+
from odoo import models
5+
6+
7+
class DmsDirectory(models.Model):
8+
_inherit = "dms.directory"
9+
10+
def action_new_spreadsheet(self):
11+
"""Open the New Spreadsheet wizard pre-filled with this directory."""
12+
self.ensure_one()
13+
return {
14+
"type": "ir.actions.act_window",
15+
"name": "New Spreadsheet",
16+
"res_model": "dms.spreadsheet.create",
17+
"view_mode": "form",
18+
"target": "new",
19+
"context": {"default_directory_id": self.id},
20+
}

dms_spreadsheet/models/dms_file.py

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# Copyright 2026 Ledo Enterprises
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
3+
4+
import base64
5+
import json
6+
import logging
7+
8+
from odoo import api, fields, models
9+
from odoo.exceptions import AccessError
10+
11+
_logger = logging.getLogger(__name__)
12+
13+
SPREADSHEET_MIMETYPE = "application/o-spreadsheet"
14+
15+
16+
class DmsFile(models.Model):
17+
_name = "dms.file"
18+
_inherit = ["dms.file", "spreadsheet.abstract"]
19+
20+
handler = fields.Selection(
21+
selection=[("spreadsheet", "Spreadsheet")],
22+
index=True,
23+
)
24+
25+
# ── Lifecycle ────────────────────────────────────────────────────────────
26+
27+
@api.model_create_multi
28+
def create(self, vals_list):
29+
for vals in vals_list:
30+
if vals.get("mimetype") == SPREADSHEET_MIMETYPE:
31+
vals["handler"] = "spreadsheet"
32+
if vals.get("content") and not vals.get("spreadsheet_binary_data"):
33+
vals["spreadsheet_binary_data"] = vals["content"]
34+
return super().create(vals_list)
35+
36+
def write(self, vals):
37+
if vals.get("mimetype") == SPREADSHEET_MIMETYPE:
38+
vals["handler"] = "spreadsheet"
39+
return super().write(vals)
40+
41+
# ── Storage bridge ───────────────────────────────────────────────────────
42+
43+
def _compute_content(self):
44+
"""For spreadsheet files, content mirrors spreadsheet_binary_data."""
45+
spreadsheets = self.filtered(lambda f: f.handler == "spreadsheet")
46+
for rec in spreadsheets:
47+
rec.content = rec.spreadsheet_binary_data
48+
return super(DmsFile, self - spreadsheets)._compute_content()
49+
50+
def _inverse_content(self):
51+
"""For spreadsheet files, write-through to spreadsheet_binary_data."""
52+
spreadsheets = self.filtered(lambda f: f.handler == "spreadsheet")
53+
for rec in spreadsheets:
54+
rec.spreadsheet_binary_data = rec.content
55+
return super(DmsFile, self - spreadsheets)._inverse_content()
56+
57+
# ── spreadsheet.abstract interface ───────────────────────────────────────
58+
59+
def get_spreadsheet_data(self):
60+
"""Return data payload consumed by action_spreadsheet_oca client action."""
61+
self.ensure_one()
62+
mode = "normal"
63+
try:
64+
self.check_access("write")
65+
except AccessError:
66+
mode = "readonly"
67+
68+
raw = {}
69+
if self.spreadsheet_binary_data:
70+
try:
71+
raw = json.loads(
72+
base64.decodebytes(self.spreadsheet_binary_data).decode("utf-8")
73+
)
74+
except (ValueError, UnicodeDecodeError):
75+
_logger.warning(
76+
"Could not decode spreadsheet data for dms.file %s", self.id
77+
)
78+
79+
revisions = []
80+
for rev in self.spreadsheet_revision_ids:
81+
try:
82+
revisions.append(
83+
dict(
84+
json.loads(rev.commands),
85+
nextRevisionId=rev.next_revision_id,
86+
serverRevisionId=rev.server_revision_id,
87+
)
88+
)
89+
except (ValueError, UnicodeDecodeError):
90+
_logger.warning(
91+
"Skipping malformed revision %s on dms.file %s", rev.id, self.id
92+
)
93+
94+
return {
95+
"name": self.name,
96+
"spreadsheet_raw": raw,
97+
"revisions": revisions,
98+
"mode": mode,
99+
"default_currency": self.env[
100+
"res.currency"
101+
].get_company_currency_for_spreadsheet(),
102+
"user_locale": self.env["res.lang"]._get_user_spreadsheet_locale(),
103+
}
104+
105+
def open_spreadsheet(self):
106+
self.ensure_one()
107+
return {
108+
"type": "ir.actions.client",
109+
"tag": "action_spreadsheet_oca",
110+
"params": {"spreadsheet_id": self.id, "model": self._name},
111+
}
112+
113+
@api.model
114+
def action_open_new_spreadsheet(self):
115+
"""Open the New Spreadsheet wizard, pre-filling directory from context.
116+
117+
Called from the server action bound to the file kanban/list views so
118+
that 'New Spreadsheet' appears in the Files ⚙ action menu.
119+
"""
120+
ctx = self.env.context
121+
directory_id = ctx.get("default_directory_id") or ctx.get("active_id")
122+
return {
123+
"type": "ir.actions.act_window",
124+
"res_model": "dms.spreadsheet.create",
125+
"view_mode": "form",
126+
"target": "new",
127+
"context": {"default_directory_id": directory_id},
128+
}
129+
130+
# ── Computed helpers ─────────────────────────────────────────────────────
131+
132+
@api.depends("handler")
133+
def _compute_extension(self):
134+
"""Spreadsheet files use 'oss' extension (MIME not in Python stdlib)."""
135+
spreadsheets = self.filtered(lambda f: f.handler == "spreadsheet")
136+
for rec in spreadsheets:
137+
rec.extension = "oss"
138+
return super(DmsFile, self - spreadsheets)._compute_extension()
139+
140+
@api.depends("handler")
141+
def _compute_mimetype(self):
142+
spreadsheets = self.filtered(lambda f: f.handler == "spreadsheet")
143+
for rec in spreadsheets:
144+
rec.mimetype = SPREADSHEET_MIMETYPE
145+
return super(DmsFile, self - spreadsheets)._compute_mimetype()

dms_spreadsheet/pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[build-system]
2+
requires = ["whool"]
3+
build-backend = "whool.buildapi"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
No additional configuration is required after installing the module.
2+
3+
Spreadsheet files are identified by the MIME type `application/o-spreadsheet` and the
4+
`handler` field value `spreadsheet` on `dms.file` records. Both are set automatically
5+
when a file is created through the **New Spreadsheet** wizard or when an existing DMS
6+
file's MIME type is set to `application/o-spreadsheet`.
7+
8+
Access to individual spreadsheets is governed by the existing DMS permission system
9+
(storage-level groups and directory-level ACLs).
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Ledo Enterprises <https://ledoweb.com>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Create and edit OCA spreadsheets directly within the DMS file manager.
2+
3+
Requires `dms` and `spreadsheet_oca`.
4+
5+
**Features**
6+
7+
- **New Spreadsheet** button in DMS directory views (form and kanban)
8+
- Opening a `.o-spreadsheet` file launches the full OCA spreadsheet editor
9+
- Spreadsheet data stored transparently via DMS file storage (database, attachment, or filesystem)
10+
- Read-only mode for users without write access to the DMS file
11+
- Revision history via `spreadsheet.abstract` mixin

0 commit comments

Comments
 (0)