Skip to content

Commit ff24cd2

Browse files
committed
[ADD] Add new module iot_ux
1 parent d8b2a1b commit ff24cd2

11 files changed

Lines changed: 237 additions & 0 deletions

File tree

iot_ux/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import models

iot_ux/__manifest__.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
##############################################################################
2+
#
3+
# Copyright (C) 2026 ADHOC SA (http://www.adhoc.com.ar)
4+
# All Rights Reserved.
5+
#
6+
# This program is free software: you can redistribute it and/or modify
7+
# it under the terms of the GNU Affero General Public License as
8+
# published by the Free Software Foundation, either version 3 of the
9+
# License, or (at your option) any later version.
10+
#
11+
# This program is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
# GNU Affero General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU Affero General Public License
17+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
#
19+
##############################################################################
20+
{
21+
"name": "IoT UX Enhancements",
22+
"version": "19.0.1.0.0",
23+
"category": "Generic Modules/Base",
24+
"author": "ADHOC SA",
25+
"license": "AGPL-3",
26+
"depends": ["iot"],
27+
"data": [
28+
"security/ir.model.access.csv",
29+
"views/iot_views.xml",
30+
"views/ir_actions_report.xml",
31+
"views/res_users.xml",
32+
],
33+
'assets': {
34+
'web.assets_backend': [
35+
'iot_ux/static/src/*',
36+
],
37+
},
38+
"installable": True,
39+
}

iot_ux/models/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from . import ir_actions_report
2+
from . import res_users
3+
from . import iot_device

iot_ux/models/iot_device.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from odoo import fields, models
2+
3+
4+
class iotDevice(models.Model):
5+
_inherit = "iot.device"
6+
7+
iot_report_rule_ids = fields.One2many(
8+
"ir.actions.report.iot.rule",
9+
"device_id",
10+
)

iot_ux/models/ir_actions_report.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
from odoo import api, fields, models
2+
3+
4+
class IrActionsReport(models.Model):
5+
_inherit = "ir.actions.report"
6+
7+
iot_rule_ids = fields.One2many("ir.actions.report.iot.rule", "report_id", string="IoT Report Rules")
8+
9+
def report_action(self, docids, data=None, config=True):
10+
result = super().report_action(docids, data, config)
11+
if result.get("type") != "ir.actions.report":
12+
return result
13+
if user_rule := self.iot_rule_ids.filtered(lambda r: r.user_id == self.env.user):
14+
result["id"] = self.id
15+
result["device_ids"] = user_rule.device_id.mapped("identifier")
16+
elif self.env.user.iot_device_id:
17+
result["id"] = self.id
18+
result["device_ids"] = self.env.user.iot_device_id.mapped("identifier")
19+
20+
return result
21+
22+
class IrActionsReportIotRule(models.Model):
23+
_name = "ir.actions.report.iot.rule"
24+
_description = "Report IoT Rule"
25+
26+
report_id = fields.Many2one("ir.actions.report", required=True)
27+
user_id = fields.Many2one(
28+
"res.users",
29+
required=True,
30+
)
31+
device_id = fields.Many2one("iot.device", ondelete="set null", domain="[('type', '=', 'printer')]")
32+
skip_dialog = fields.Boolean(default=True)
33+
active = fields.Boolean(default=True)
34+
35+
_unique_report_users = models.Constraint(
36+
"UNIQUE(report_id, user_id)",
37+
"Only can have one IoT rule per report and user.",
38+
)
39+
40+
@api.model_create_multi
41+
def create(self, vals_list):
42+
res = super().create(vals_list)
43+
self.env['iot.channel'].sudo().send_message({
44+
'user_ids': res.mapped('user_id').ids,
45+
'report_ids': res.mapped('report_id').ids,
46+
}, 'clear_local_storage')
47+
return res
48+
49+
def write(self, vals):
50+
res = super().write(vals)
51+
self.env['iot.channel'].sudo().send_message({
52+
'user_ids': self.mapped('user_id').ids,
53+
'report_ids': self.mapped('report_id').ids,
54+
}, 'clear_local_storage')
55+
return res

iot_ux/models/res_users.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from odoo import fields, models
2+
3+
4+
class ResUsers(models.Model):
5+
_inherit = "res.users"
6+
7+
iot_device_id = fields.Many2one(
8+
comodel_name="iot.device",
9+
string="Default Printer",
10+
domain="[('type', '=', 'printer')]",
11+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
2+
access_iot_device_rule_user,iot rule user,model_ir_actions_report_iot_rule,base.group_user,1,0,0,0
3+
access_iot_device_rule_admin,iot rule admin,model_ir_actions_report_iot_rule,iot.group_iot_admin,1,1,1,1
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import {
2+
IOT_REPORT_PREFERENCE_LOCAL_STORAGE_KEY,
3+
setReportIdInBrowserLocalStorage,
4+
} from "@iot/client_action/delete_local_storage";
5+
import { browser } from "@web/core/browser/browser";
6+
import { registry } from "@web/core/registry";
7+
import { user } from "@web/core/user";
8+
9+
import { getSelectedPrintersForReport, printReport } from "@iot/iot_report_action";
10+
11+
12+
export async function getSelectedPrintersForReportUx(reportId, env) {
13+
const { orm, action, ui } = env.services;
14+
const deviceSettingsByReportId = JSON.parse(browser.localStorage.getItem(IOT_REPORT_PREFERENCE_LOCAL_STORAGE_KEY));
15+
const deviceSettings = deviceSettingsByReportId?.[reportId];
16+
if (!deviceSettings ) {
17+
const rule= await orm.call(
18+
"ir.actions.report.iot.rule",
19+
"search_read",
20+
[[['report_id', '=', reportId], ['user_id', '=', user.userId], ['device_id', '!=', false]],
21+
["skip_dialog", "device_id"]]);
22+
if (rule.length > 0) {
23+
const newDeviceSettings = {
24+
selectedDevices: [rule[0].device_id[0]],
25+
skipDialog: rule[0].skip_dialog,
26+
};
27+
setReportIdInBrowserLocalStorage(reportId, newDeviceSettings);
28+
}
29+
}
30+
return await getSelectedPrintersForReport(reportId, env);
31+
}
32+
33+
async function iotReportActionHandler(action, options, env) {
34+
if (action.device_ids && action.device_ids.length) {
35+
action.data ??= {};
36+
const args = [action.id, action.context.active_ids, action.data];
37+
const reportId = action.id;
38+
const printerIds = await getSelectedPrintersForReportUx(reportId, env);
39+
40+
if (!printerIds) {
41+
// If the user does not select any printer, fall back to normal printing
42+
return false;
43+
}
44+
45+
env.services.ui.block();
46+
// Try longpolling then websocket
47+
await printReport(env, args, printerIds);
48+
env.services.ui.unblock();
49+
50+
options.onClose?.();
51+
return true;
52+
}
53+
}
54+
55+
registry
56+
.category("ir.actions.report handlers")
57+
.add("iot_report_action_handler", iotReportActionHandler, {force: true});

iot_ux/views/iot_views.xml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<odoo>
2+
3+
<record id="iot_device_view_form" model="ir.ui.view">
4+
<field name="name">report print</field>
5+
<field name="model">iot.device</field>
6+
<field name="inherit_id" ref="iot.iot_device_view_form"/>
7+
<field name="arch" type="xml">
8+
<page string="Reports to Auto-Print">
9+
<field name="iot_report_rule_ids">
10+
<list editable="bottom">
11+
<field name="report_id"/>
12+
<field name="user_id"/>
13+
<field name="skip_dialog"/>
14+
</list>
15+
</field>
16+
</page>
17+
</field>
18+
</record>
19+
</odoo>

iot_ux/views/ir_actions_report.xml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<odoo>
2+
3+
<record id="act_report_xml_view" model="ir.ui.view">
4+
<field name="name">report xml rules</field>
5+
<field name="model">ir.actions.report</field>
6+
<field name="inherit_id" ref="base.act_report_xml_view"/>
7+
<field name="arch" type="xml">
8+
<page name="security" position="after">
9+
<page name="iot_printers" string="Iot Printers">
10+
<field name="iot_rule_ids">
11+
<list editable="bottom">
12+
<field name="user_id"/>
13+
<field name="device_id"/>
14+
<field name="skip_dialog"/>
15+
</list>
16+
</field>
17+
</page>
18+
</page>
19+
20+
</field>
21+
</record>
22+
23+
</odoo>

0 commit comments

Comments
 (0)