first commit
This commit is contained in:
commit
7cc9f961f0
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
.DS_Store
|
||||
*~
|
||||
57
README.md
Normal file
57
README.md
Normal file
@ -0,0 +1,57 @@
|
||||
# POS & Kitchen Custom Access
|
||||
|
||||
A custom Odoo 19 module designed to restrict menu visibility and streamline the interface for **Point of Sale (POS)** and **Kitchen Display (KDS)** users. It cleans up the user interface by hiding unrelated apps and administrative menus while ensuring standard security rules are respected.
|
||||
|
||||
## 🌟 Key Features
|
||||
|
||||
1. **User Settings Integration**: Adds **"Is POS User?"** and **"Is Kitchen User?"** checkboxes directly inside Odoo's default User form (under the *Access Rights* tab).
|
||||
2. **Standard App Hiding**: When either restriction checkbox is checked, Odoo hides all unrelated root menus (such as *Discuss*, *Documents*, *CRM*, *Inventory*, etc.) from their navigation bar.
|
||||
3. **POS Administrative Submenu Hiding**: When **"Is POS User?"** is checked:
|
||||
* Only the Point of Sale **Dashboard** is visible.
|
||||
* Administrative submenus like *Orders*, *Products*, *Reporting*, and *Configuration* are completely hidden.
|
||||
4. **Kitchen Display Overview**: When **"Is Kitchen User?"** is checked:
|
||||
* The **Kitchen Display** root menu is made accessible.
|
||||
* We automatically reactivate the standard Odoo 19 Kitchen Display root menu (`pos_enterprise.menu_point_kitchen_display_root`) and open it up to the standard internal user group (`base.group_user`).
|
||||
5. **Smart Group Provisioning**:
|
||||
* To prevent "Access Error" messages or blank screens when a Kitchen User loads the KDS screen, our module automatically assigns the required underlying **"Point of Sale/User"** security group when **"Is Kitchen User?"** is checked.
|
||||
* If both checkboxes are subsequently unchecked, the group is automatically removed to keep your security configuration pristine.
|
||||
6. **Vanilla Behavior Preservation**: Standard, unrestricted non-manager users in the database still do *not* see the Kitchen Display root menu, strictly preserving default Odoo behavior.
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Installation & Setup
|
||||
|
||||
1. **Copy Module**: Ensure this directory (`pos_kitchen_custom_access`) is located within your Odoo custom addons directory.
|
||||
2. **Update Addons List**:
|
||||
* Activate developer mode in Odoo.
|
||||
* Navigate to **Apps** and click **Update Apps List**.
|
||||
3. **Install**: Search for `pos_kitchen_custom_access` and click **Activate** / **Upgrade**.
|
||||
4. **Configure Users**:
|
||||
* Navigate to **Settings > Users & Companies > Users**.
|
||||
* Open the target user, scroll down to the **Access Rights** tab, and check **"Is POS User?"** and/or **"Is Kitchen User?"**.
|
||||
* Save the form. The module automatically syncs group permissions in the background.
|
||||
|
||||
---
|
||||
|
||||
## 📂 Codebase Architecture
|
||||
|
||||
```bash
|
||||
pos_kitchen_custom_access/
|
||||
├── __init__.py # Python initialization
|
||||
├── __manifest__.py # Module manifest metadata and dependencies
|
||||
├── .gitignore # Git ignore files
|
||||
├── README.md # Documentation
|
||||
├── models/
|
||||
│ ├── __init__.py # Python models registration
|
||||
│ ├── ir_ui_menu.py # Extends _filter_visible_menus() to apply dynamic menu filtering rules
|
||||
│ └── res_users.py # Extends res.users with boolean checkboxes & auto-syncing of POS group
|
||||
└── views/
|
||||
└── res_users_views.xml # Inherits base.view_users_form to add UI fields and reactivates KDS root menu
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 Technical Notes
|
||||
|
||||
* **No Access Blocking**: This module strictly performs menu filtering to streamline the UI. It does not block backend controllers or standard API accesses, allowing background tasks and POS operations to run seamlessly.
|
||||
* **ORM Cache Invalidation**: The write and create operations on user records automatically flush Odoo's registry and menu cache to ensure that changes in access rights are reflected on the next page refresh without requiring a server reboot.
|
||||
1
__init__.py
Normal file
1
__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from . import models
|
||||
21
__manifest__.py
Normal file
21
__manifest__.py
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
'name': 'POS & Kitchen User Custom Access',
|
||||
'version': '19.0.1.0.0',
|
||||
'category': 'Point of Sale',
|
||||
'summary': 'Restrict menu visibility for POS and Kitchen users',
|
||||
'description': """
|
||||
Hides all top navigation menus except:
|
||||
- Point of Sale (for users marked as POS Users)
|
||||
- Kitchen Display (for users marked as Kitchen Users)
|
||||
|
||||
Does not block backend or API access.
|
||||
""",
|
||||
'author': 'Suherdy Yacob',
|
||||
'depends': ['base', 'point_of_sale', 'pos_enterprise'],
|
||||
'data': [
|
||||
'views/res_users_views.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'application': False,
|
||||
'license': 'LGPL-3',
|
||||
}
|
||||
2
models/__init__.py
Normal file
2
models/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
from . import res_users
|
||||
from . import ir_ui_menu
|
||||
60
models/ir_ui_menu.py
Normal file
60
models/ir_ui_menu.py
Normal file
@ -0,0 +1,60 @@
|
||||
from odoo import models, api
|
||||
|
||||
class IrUiMenu(models.Model):
|
||||
_inherit = 'ir.ui.menu'
|
||||
|
||||
def _filter_visible_menus(self):
|
||||
res = super()._filter_visible_menus()
|
||||
user = self.env.user
|
||||
|
||||
# Avoid restricting the superuser (id = 1) or public/portal users
|
||||
if user.id == 1 or user.share:
|
||||
return res
|
||||
|
||||
is_pos = user.is_pos_user
|
||||
is_kitchen = user.is_kitchen_user
|
||||
|
||||
if not is_pos and not is_kitchen:
|
||||
if not user.has_group('point_of_sale.group_pos_manager'):
|
||||
kitchen_root = self.env.ref('pos_enterprise.menu_point_kitchen_display_root', raise_if_not_found=False)
|
||||
if kitchen_root:
|
||||
kitchen_menus = self.env['ir.ui.menu'].sudo().search([('parent_path', 'like', kitchen_root.parent_path + '%')])
|
||||
kitchen_menu_ids = set(kitchen_menus.ids)
|
||||
kitchen_menu_ids.add(kitchen_root.id)
|
||||
return res.filtered(lambda menu: menu.id not in kitchen_menu_ids)
|
||||
return res
|
||||
|
||||
allowed_ids = set()
|
||||
|
||||
if is_pos:
|
||||
pos_root = self.env.ref('point_of_sale.menu_point_root', raise_if_not_found=False)
|
||||
if pos_root:
|
||||
pos_menus = self.env['ir.ui.menu'].sudo().search([('parent_path', 'like', pos_root.parent_path + '%')])
|
||||
pos_menu_ids = set(pos_menus.ids)
|
||||
pos_menu_ids.add(pos_root.id)
|
||||
|
||||
# Submenus to hide: Orders, Products, Reporting, Configuration
|
||||
exclude_xml_ids = [
|
||||
'point_of_sale.menu_point_of_sale',
|
||||
'point_of_sale.pos_config_menu_catalog',
|
||||
'point_of_sale.menu_point_rep',
|
||||
'point_of_sale.menu_point_config_product'
|
||||
]
|
||||
exclude_ids = set()
|
||||
for xml_id in exclude_xml_ids:
|
||||
menu = self.env.ref(xml_id, raise_if_not_found=False)
|
||||
if menu:
|
||||
descendants = self.env['ir.ui.menu'].sudo().search([('parent_path', 'like', menu.parent_path + '%')])
|
||||
exclude_ids.update(descendants.ids)
|
||||
exclude_ids.add(menu.id)
|
||||
|
||||
allowed_ids.update(pos_menu_ids - exclude_ids)
|
||||
|
||||
if is_kitchen:
|
||||
kitchen_root = self.env.ref('pos_enterprise.menu_point_kitchen_display_root', raise_if_not_found=False)
|
||||
if kitchen_root:
|
||||
kitchen_menus = self.env['ir.ui.menu'].sudo().search([('parent_path', 'like', kitchen_root.parent_path + '%')])
|
||||
allowed_ids.update(kitchen_menus.ids)
|
||||
allowed_ids.add(kitchen_root.id)
|
||||
|
||||
return res.filtered(lambda menu: menu.id in allowed_ids)
|
||||
42
models/res_users.py
Normal file
42
models/res_users.py
Normal file
@ -0,0 +1,42 @@
|
||||
from odoo import models, fields, api
|
||||
|
||||
class ResUsers(models.Model):
|
||||
_inherit = 'res.users'
|
||||
|
||||
is_pos_user = fields.Boolean(string="Is POS User?", default=False)
|
||||
is_kitchen_user = fields.Boolean(string="Is Kitchen User?", default=False)
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
pos_user_group = self.env.ref('point_of_sale.group_pos_user', raise_if_not_found=False)
|
||||
if pos_user_group:
|
||||
for vals in vals_list:
|
||||
if vals.get('is_kitchen_user') or vals.get('is_pos_user'):
|
||||
group_commands = vals.get('group_ids', [])
|
||||
if isinstance(group_commands, list):
|
||||
group_commands.append((4, pos_user_group.id))
|
||||
vals['group_ids'] = group_commands
|
||||
|
||||
res = super().create(vals_list)
|
||||
if any(vals.get('is_pos_user') or vals.get('is_kitchen_user') for vals in vals_list):
|
||||
self.env.registry.clear_cache()
|
||||
return res
|
||||
|
||||
def write(self, vals):
|
||||
pos_user_group = self.env.ref('point_of_sale.group_pos_user', raise_if_not_found=False)
|
||||
if pos_user_group and ('is_kitchen_user' in vals or 'is_pos_user' in vals):
|
||||
for user in self:
|
||||
is_kitchen = vals.get('is_kitchen_user', user.is_kitchen_user)
|
||||
is_pos = vals.get('is_pos_user', user.is_pos_user)
|
||||
if (is_kitchen or is_pos):
|
||||
if pos_user_group not in user.group_ids:
|
||||
super(ResUsers, user).write({'group_ids': [(4, pos_user_group.id)]})
|
||||
else:
|
||||
if pos_user_group in user.group_ids:
|
||||
super(ResUsers, user).write({'group_ids': [(3, pos_user_group.id)]})
|
||||
|
||||
res = super().write(vals)
|
||||
if 'is_pos_user' in vals or 'is_kitchen_user' in vals:
|
||||
self.env.registry.clear_cache()
|
||||
return res
|
||||
|
||||
22
views/res_users_views.xml
Normal file
22
views/res_users_views.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_users_form_inherit_pos_kitchen" model="ir.ui.view">
|
||||
<field name="name">res.users.form.inherit.pos.kitchen</field>
|
||||
<field name="model">res.users</field>
|
||||
<field name="inherit_id" ref="base.view_users_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//page[@name='access_rights']/group[1]" position="after">
|
||||
<group string="Special POS/Kitchen Restrictions">
|
||||
<field name="is_pos_user"/>
|
||||
<field name="is_kitchen_user"/>
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Activate Kitchen Display Menu and make it accessible to internal users -->
|
||||
<record id="pos_enterprise.menu_point_kitchen_display_root" model="ir.ui.menu">
|
||||
<field name="active" eval="True"/>
|
||||
<field name="group_ids" eval="[(6, 0, [ref('base.group_user')])]"/>
|
||||
</record>
|
||||
</odoo>
|
||||
Loading…
Reference in New Issue
Block a user