From a14050004b4cd92a7b89d1ee94650dde80443b90 Mon Sep 17 00:00:00 2001 From: Suherdy Yacob Date: Fri, 29 May 2026 20:17:40 +0700 Subject: [PATCH] first commit --- .gitignore | 10 ++++ README.md | 26 ++++++++++ __init__.py | 2 + __manifest__.py | 16 ++++++ models/__init__.py | 3 ++ models/purchase_order.py | 93 ++++++++++++++++++++++++++++++++++ models/res_users.py | 15 ++++++ views/purchase_order_views.xml | 4 ++ views/res_users_views.xml | 18 +++++++ 9 files changed, 187 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 __init__.py create mode 100644 __manifest__.py create mode 100644 models/__init__.py create mode 100644 models/purchase_order.py create mode 100644 models/res_users.py create mode 100644 views/purchase_order_views.xml create mode 100644 views/res_users_views.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..89b5acc --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# Python compiled files and caches +*.pyc +*.pyo +*.pyd +__pycache__/ + +# System files +.DS_Store +Thumbs.db +*~ diff --git a/README.md b/README.md new file mode 100644 index 0000000..174e89c --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +# Purchase Custom Form Visibility + +Custom Odoo 19 module to dynamically manage the visibility of buttons and financial fields on the Purchase Order form on a per-user basis. + +## Features +1. **Per-User Restricting Options**: Toggle restriction flags directly on individual user accounts. +2. **Hide RFQ & Confirmation Buttons**: Hide "Confirm Order" and "Send RFQ" buttons from the purchase order header for restricted users. +3. **Hide Financial Fields**: Hide unit price, subtotal, and totals on purchase lists, forms, line kanbans, and footer groups for restricted users. +4. **Flexible and Stateless**: Instant dynamically computed restrictions without storing redundant data in the database. + +## Installation +1. Move the `purchase_custom_visibility` directory to Odoo's custom addons path. +2. Upgrade/Update Odoo addons list. +3. Install the module. + +## Configuration (Per-User) +1. Go to **Settings** -> **Users & Companies** -> **Users**. +2. Select a User record and click **Edit**. +3. Open the **Purchase Restrictions** tab. +4. Check the desired restrictions: + - **Hide Confirm Order & Send RFQ** + - **Hide Price, Subtotal & Total** +5. Save the user record. + +## Author +- **Suherdy Yacob** diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..a0fdc10 --- /dev/null +++ b/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from . import models diff --git a/__manifest__.py b/__manifest__.py new file mode 100644 index 0000000..361af3b --- /dev/null +++ b/__manifest__.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +{ + 'name': 'Purchase Custom Form Visibility', + 'version': '19.0.1.0.0', + 'summary': 'Configure visibility of buttons and prices/totals on Purchase Order form per user.', + 'category': 'Inventory/Purchase', + 'author': 'Suherdy Yacob', + 'depends': ['purchase'], + 'data': [ + 'views/res_users_views.xml', + 'views/purchase_order_views.xml', + ], + 'installable': True, + 'application': False, + 'license': 'LGPL-3', +} diff --git a/models/__init__.py b/models/__init__.py new file mode 100644 index 0000000..8f72eb1 --- /dev/null +++ b/models/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- +from . import res_users +from . import purchase_order diff --git a/models/purchase_order.py b/models/purchase_order.py new file mode 100644 index 0000000..a64e9a7 --- /dev/null +++ b/models/purchase_order.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- +from odoo import models, fields, api +from lxml import etree + +class PurchaseOrder(models.Model): + _inherit = 'purchase.order' + + hide_confirm_order_send_rfq = fields.Boolean( + compute='_compute_hide_fields', + store=False, + help="Technical field computed from user settings to determine button visibility" + ) + hide_purchase_prices = fields.Boolean( + compute='_compute_hide_fields', + store=False, + help="Technical field computed from user settings to determine price visibility" + ) + + def _compute_hide_fields(self): + # Retrieve the configuration settings dynamically from the current user + current_user = self.env.user + hide_buttons = current_user.hide_confirm_order_send_rfq + hide_prices = current_user.hide_purchase_prices + for order in self: + order.hide_confirm_order_send_rfq = hide_buttons + order.hide_purchase_prices = hide_prices + + @api.model + def get_views(self, views, options=None): + res = super().get_views(views, options) + + # Check current user configuration settings + current_user = self.env.user + hide_buttons = current_user.hide_confirm_order_send_rfq + hide_prices = current_user.hide_purchase_prices + + if not (hide_buttons or hide_prices): + return res + + for view_type, view_data in res.get('views', {}).items(): + arch_str = view_data.get('arch') + if not arch_str: + continue + + try: + doc = etree.fromstring(arch_str.encode('utf-8')) + except Exception: + continue + + modified = False + + # 1. Dynamically hide Confirm Order and Send RFQ buttons + if hide_buttons: + for btn in doc.xpath("//button[@name='action_rfq_send' or @name='button_confirm']"): + btn.set('invisible', '1') + modified = True + + # 2. Dynamically hide all price, subtotal, taxes, and total elements + if hide_prices: + price_fields = { + 'price_unit', 'taxes_id', 'price_subtotal', 'price_total', 'price_tax', + 'amount_untaxed', 'amount_tax', 'amount_total', 'amount_total_cc', + 'tax_totals', 'discount' + } + + # Check all tags + for field in doc.xpath("//field"): + fname = field.get('name') + if fname in price_fields: + # Determine if this field is inside a list or tree view (columns) + is_column = False + p = field.getparent() + while p is not None: + if p.tag in ('tree', 'list'): + is_column = True + break + p = p.getparent() + + if is_column: + field.set('column_invisible', 'True') + else: + field.set('invisible', '1') + modified = True + + # Hide any subtotal footer layouts + for el in doc.xpath("//group[contains(@class, 'oe_subtotal_footer')] | //div[contains(@class, 'oe_subtotal_footer')]"): + el.set('invisible', '1') + modified = True + + if modified: + view_data['arch'] = etree.tostring(doc, encoding='utf-8').decode('utf-8') + + return res diff --git a/models/res_users.py b/models/res_users.py new file mode 100644 index 0000000..7add53b --- /dev/null +++ b/models/res_users.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +from odoo import models, fields + +class ResUsers(models.Model): + _inherit = 'res.users' + + hide_confirm_order_send_rfq = fields.Boolean( + string="Hide Confirm Order & Send RFQ", + help="Hide Confirm Order and Send RFQ buttons on Purchase Order form for this user." + ) + + hide_purchase_prices = fields.Boolean( + string="Hide Price, Subtotal & Total", + help="Hide Unit Price, Subtotal, and Totals on Purchase Order form for this user." + ) diff --git a/views/purchase_order_views.xml b/views/purchase_order_views.xml new file mode 100644 index 0000000..5902707 --- /dev/null +++ b/views/purchase_order_views.xml @@ -0,0 +1,4 @@ + + + + diff --git a/views/res_users_views.xml b/views/res_users_views.xml new file mode 100644 index 0000000..265341e --- /dev/null +++ b/views/res_users_views.xml @@ -0,0 +1,18 @@ + + + + res.users.form.inherit.purchase.hide + res.users + + + + + + + + + + + + +