Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions iot_ux/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
39 changes: 39 additions & 0 deletions iot_ux/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
##############################################################################
#
# Copyright (C) 2026 ADHOC SA (http://www.adhoc.com.ar)
# All Rights Reserved.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
"name": "IoT UX Enhancements",
"version": "19.0.1.0.0",
"category": "Generic Modules/Base",
"author": "ADHOC SA",
"license": "AGPL-3",
"depends": ["iot"],
"data": [
"security/ir.model.access.csv",
"views/iot_views.xml",
"views/ir_actions_report.xml",
"views/res_users.xml",
],
"assets": {
"web.assets_backend": [
"iot_ux/static/src/*",
],
},
"installable": True,
}
3 changes: 3 additions & 0 deletions iot_ux/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import ir_actions_report
from . import res_users
from . import iot_device
10 changes: 10 additions & 0 deletions iot_ux/models/iot_device.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from odoo import fields, models


class iotDevice(models.Model):
_inherit = "iot.device"

iot_report_rule_ids = fields.One2many(
"ir.actions.report.iot.rule",
"device_id",
)
62 changes: 62 additions & 0 deletions iot_ux/models/ir_actions_report.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from odoo import api, fields, models


class IrActionsReport(models.Model):
_inherit = "ir.actions.report"

iot_rule_ids = fields.One2many("ir.actions.report.iot.rule", "report_id", string="IoT Report Rules")

def report_action(self, docids, data=None, config=True):
result = super().report_action(docids, data, config)
if result.get("type") != "ir.actions.report":
return result
if user_rule := self.iot_rule_ids.filtered(lambda r: r.user_id == self.env.user):
result["id"] = self.id
result["device_ids"] = user_rule.device_id.mapped("identifier")
elif self.env.user.iot_device_id:
result["id"] = self.id
result["device_ids"] = self.env.user.iot_device_id.mapped("identifier")

return result


class IrActionsReportIotRule(models.Model):
_name = "ir.actions.report.iot.rule"
_description = "Report IoT Rule"

report_id = fields.Many2one("ir.actions.report", required=True)
user_id = fields.Many2one(
"res.users",
required=True,
)
device_id = fields.Many2one("iot.device", ondelete="set null", domain="[('type', '=', 'printer')]")
skip_dialog = fields.Boolean(default=True)
active = fields.Boolean(default=True)

_unique_report_users = models.Constraint(
"UNIQUE(report_id, user_id)",
"Only can have one IoT rule per report and user.",
)

@api.model_create_multi
def create(self, vals_list):
res = super().create(vals_list)
self.env["iot.channel"].sudo().send_message(
{
"user_ids": res.mapped("user_id").ids,
"report_ids": res.mapped("report_id").ids,
},
"clear_local_storage",
)
return res

def write(self, vals):
res = super().write(vals)
self.env["iot.channel"].sudo().send_message(
{
"user_ids": self.mapped("user_id").ids,
"report_ids": self.mapped("report_id").ids,
},
"clear_local_storage",
)
return res
11 changes: 11 additions & 0 deletions iot_ux/models/res_users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from odoo import fields, models


class ResUsers(models.Model):
_inherit = "res.users"

iot_device_id = fields.Many2one(
comodel_name="iot.device",
string="Default Printer",
domain="[('type', '=', 'printer')]",
)
3 changes: 3 additions & 0 deletions iot_ux/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_iot_device_rule_user,iot rule user,model_ir_actions_report_iot_rule,base.group_user,1,0,0,0
access_iot_device_rule_admin,iot rule admin,model_ir_actions_report_iot_rule,iot.group_iot_admin,1,1,1,1
57 changes: 57 additions & 0 deletions iot_ux/static/src/iot_report_action.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import {
IOT_REPORT_PREFERENCE_LOCAL_STORAGE_KEY,
setReportIdInBrowserLocalStorage,
} from "@iot/client_action/delete_local_storage";
import { browser } from "@web/core/browser/browser";
import { registry } from "@web/core/registry";
import { user } from "@web/core/user";

import { getSelectedPrintersForReport, printReport } from "@iot/iot_report_action";


export async function getSelectedPrintersForReportUx(reportId, env) {
const { orm, action, ui } = env.services;
const deviceSettingsByReportId = JSON.parse(browser.localStorage.getItem(IOT_REPORT_PREFERENCE_LOCAL_STORAGE_KEY));
const deviceSettings = deviceSettingsByReportId?.[reportId];
if (!deviceSettings ) {
const rule= await orm.call(
"ir.actions.report.iot.rule",
"search_read",
[[['report_id', '=', reportId], ['user_id', '=', user.userId], ['device_id', '!=', false]],
["skip_dialog", "device_id"]]);
if (rule.length > 0) {
const newDeviceSettings = {
selectedDevices: [rule[0].device_id[0]],
skipDialog: rule[0].skip_dialog,
};
setReportIdInBrowserLocalStorage(reportId, newDeviceSettings);
}
}
return await getSelectedPrintersForReport(reportId, env);
}

async function iotReportActionHandler(action, options, env) {
if (action.device_ids && action.device_ids.length) {
action.data ??= {};
const args = [action.id, action.context.active_ids, action.data];
const reportId = action.id;
const printerIds = await getSelectedPrintersForReportUx(reportId, env);

if (!printerIds) {
// If the user does not select any printer, fall back to normal printing
return false;
}

env.services.ui.block();
// Try longpolling then websocket
await printReport(env, args, printerIds);
env.services.ui.unblock();

options.onClose?.();
return true;
}
}

registry
.category("ir.actions.report handlers")
.add("iot_report_action_handler", iotReportActionHandler, {force: true});
19 changes: 19 additions & 0 deletions iot_ux/views/iot_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<odoo>

<record id="iot_device_view_form" model="ir.ui.view">
<field name="name">report print</field>
<field name="model">iot.device</field>
<field name="inherit_id" ref="iot.iot_device_view_form"/>
<field name="arch" type="xml">
<page string="Reports to Auto-Print">
<field name="iot_report_rule_ids">
<list editable="bottom">
<field name="report_id"/>
<field name="user_id"/>
<field name="skip_dialog"/>
</list>
</field>
</page>
</field>
</record>
</odoo>
23 changes: 23 additions & 0 deletions iot_ux/views/ir_actions_report.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<odoo>

<record id="act_report_xml_view" model="ir.ui.view">
<field name="name">report xml rules</field>
<field name="model">ir.actions.report</field>
<field name="inherit_id" ref="base.act_report_xml_view"/>
<field name="arch" type="xml">
<page name="security" position="after">
<page name="iot_printers" string="Iot Printers">
<field name="iot_rule_ids">
<list editable="bottom">
<field name="user_id"/>
<field name="device_id"/>
<field name="skip_dialog"/>
</list>
</field>
</page>
</page>

</field>
</record>

</odoo>
16 changes: 16 additions & 0 deletions iot_ux/views/res_users.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<odoo>

<record id="view_users_form_simple_modif" model="ir.ui.view">
<field name="name">res.users.preferences.form</field>
<field name="model">res.users</field>
<field name="inherit_id" ref="base.view_users_form_simple_modif"/>
<field name="arch" type="xml">
<group name="other_preferences" position="after">
<group>
<field name="iot_device_id"/>
</group>
</group>
</field>
</record>

</odoo>