diff --git a/.copier-answers.yml b/.copier-answers.yml index bacf9aff6cf5..789617948313 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,15 +1,16 @@ # Do NOT update manually; changes here will be overwritten by Copier -_commit: v1.12.0 +_commit: v1.17.2 _src_path: gh:oca/oca-addons-repo-template ci: GitHub -dependency_installation_mode: PIP generate_requirements_txt: true github_check_license: true +github_ci_extra_env: {} github_enable_codecov: true github_enable_makepot: true github_enable_stale_action: true github_enforce_dev_status_compatibility: true include_wkhtmltopdf: false +odoo_test_flavor: Both odoo_version: 15.0 org_name: Odoo Community Association (OCA) org_slug: OCA @@ -18,6 +19,4 @@ repo_description: 'TODO: add repo description.' repo_name: web repo_slug: web repo_website: https://github.com/OCA/web -travis_apt_packages: [] -travis_apt_sources: [] diff --git a/.eslintrc.yml b/.eslintrc.yml index 9429bc688aab..fed88d70d23e 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -22,6 +22,7 @@ globals: odoo: readonly openerp: readonly owl: readonly + luxon: readonly # Styling is handled by Prettier, so we only need to enable AST rules; # see https://github.com/OCA/maintainer-quality-tools/pull/618#issuecomment-558576890 diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 9b5d86c250cb..745e3bbde2fa 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -11,10 +11,12 @@ on: jobs: pre-commit: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: actions/setup-python@v2 + with: + python-version: "3.11" - name: Get python version run: echo "PY=$(python -VV | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV - uses: actions/cache@v1 @@ -25,6 +27,15 @@ jobs: run: pip install pre-commit - name: Run pre-commit run: pre-commit run --all-files --show-diff-on-failure --color=always + env: + # Consider valid a PR that changes README fragments but doesn't + # change the README.rst file itself. It's not really a problem + # because the bot will update it anyway after merge. This way, we + # lower the barrier for functional contributors that want to fix the + # readme fragments, while still letting developers get README + # auto-generated (which also helps functionals when using runboat). + # DOCS https://pre-commit.com/#temporarily-disabling-hooks + SKIP: oca-gen-addon-readme - name: Check that all files generated by pre-commit are in git run: | newfiles="$(git ls-files --others --exclude-from=.gitignore)" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f23185845dbb..5c07e37d0806 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest name: Detect unreleased dependencies steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - run: | for reqfile in requirements.txt test-requirements.txt ; do if [ -f ${reqfile} ] ; then @@ -28,7 +28,7 @@ jobs: fi done test: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 container: ${{ matrix.container }} name: ${{ matrix.name }} strategy: @@ -36,10 +36,10 @@ jobs: matrix: include: - container: ghcr.io/oca/oca-ci/py3.8-odoo15.0:latest - makepot: "true" name: test with Odoo - container: ghcr.io/oca/oca-ci/py3.8-ocb15.0:latest name: test with OCB + makepot: "true" services: postgres: image: postgres:9.6 @@ -50,7 +50,7 @@ jobs: ports: - 5432:5432 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: persist-credentials: false - name: Install addons and dependencies diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 091277417ec0..96d713551a60 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,6 +12,8 @@ exclude: | /static/(src/)?lib/| # Repos using Sphinx to generate docs don't need prettying ^docs/_templates/.*\.html$| + # Don't bother non-technical authors with formatting issues in docs + readme/.*\.(rst|md)$| # You don't usually want a bot to modify your legal texts (LICENSE.*|COPYING.*) default_language_version: @@ -33,12 +35,24 @@ repos: language: fail files: '[a-zA-Z0-9_]*/i18n/en\.po$' - repo: https://github.com/oca/maintainer-tools - rev: dfba427ba03900b69e0a7f2c65890dc48921d36a + rev: 969238e47c07d0c40573acff81d170f63245d738 hooks: # update the NOT INSTALLABLE ADDONS section above - id: oca-update-pre-commit-excluded-addons - id: oca-fix-manifest-website args: ["https://github.com/OCA/web"] + - id: oca-gen-addon-readme + args: + - --addons-dir=. + - --branch=15.0 + - --org-name=OCA + - --repo-name=web + - --if-source-changed + - repo: https://github.com/OCA/odoo-pre-commit-hooks + rev: v0.0.25 + hooks: + - id: oca-checks-odoo-module + - id: oca-checks-po - repo: https://github.com/myint/autoflake rev: v1.4 hooks: @@ -101,7 +115,7 @@ repos: - id: pyupgrade args: ["--keep-percent-format"] - repo: https://github.com/PyCQA/isort - rev: 5.9.3 + rev: 5.12.0 hooks: - id: isort name: isort except __init__.py @@ -125,7 +139,7 @@ repos: name: flake8 additional_dependencies: ["flake8-bugbear==21.9.2"] - repo: https://github.com/OCA/pylint-odoo - rev: 7.0.2 + rev: 7.0.5 hooks: - id: pylint_odoo name: pylint with optional checks diff --git a/README.md b/README.md index 00b42e04df3b..6afdd39a8620 100644 --- a/README.md +++ b/README.md @@ -22,43 +22,58 @@ Available addons addon | version | maintainers | summary --- | --- | --- | --- [web_action_conditionable](web_action_conditionable/) | 15.0.1.0.0 | | web_action_conditionable -[web_advanced_search](web_advanced_search/) | 15.0.1.1.1 | [![ivantodorovich](https://github.com/ivantodorovich.png?size=30px)](https://github.com/ivantodorovich) | Easier and more powerful searching tools +[web_advanced_search](web_advanced_search/) | 15.0.1.1.5 | [![ivantodorovich](https://github.com/ivantodorovich.png?size=30px)](https://github.com/ivantodorovich) | Easier and more powerful searching tools [web_calendar_slot_duration](web_calendar_slot_duration/) | 15.0.1.0.0 | [![Yajo](https://github.com/Yajo.png?size=30px)](https://github.com/Yajo) | Customizable calendar slot durations [web_chatter_position](web_chatter_position/) | 15.0.1.0.0 | | Add an option to change the chatter position [web_company_color](web_company_color/) | 15.0.1.1.0 | | Web Company Color [web_copy_confirm](web_copy_confirm/) | 15.0.1.0.0 | | Show confirmation dialogue before copying records -[web_dialog_size](web_dialog_size/) | 15.0.1.0.1 | | A module that lets the user expand a dialog box to the full screen width. +[web_dialog_size](web_dialog_size/) | 15.0.1.1.1 | | A module that lets the user expand a dialog box to the full screen width. +[web_disable_export_group](web_disable_export_group/) | 15.0.2.0.0 | | Web Disable Export Group [web_domain_field](web_domain_field/) | 15.0.1.0.1 | | Use computed field as domain -[web_drop_target](web_drop_target/) | 15.0.1.0.1 | | Allows to drag files into Odoo +[web_drop_target](web_drop_target/) | 15.0.1.0.2 | | Allows to drag files into Odoo [web_environment_ribbon](web_environment_ribbon/) | 15.0.1.0.0 | | Web Environment Ribbon +[web_filter_header_button](web_filter_header_button/) | 15.0.1.1.1 | | Show selected filters as buttons in the control panel [web_group_expand](web_group_expand/) | 15.0.1.0.0 | | Group Expand Buttons +[web_hide_user_menu_item](web_hide_user_menu_item/) | 15.0.1.0.0 | | Web Hide User Menu Item [web_ir_actions_act_multi](web_ir_actions_act_multi/) | 15.0.1.0.0 | | Enables triggering of more than one action on ActionManager -[web_ir_actions_act_view_reload](web_ir_actions_act_view_reload/) | 15.0.1.0.0 | | Enables reload of the current view via ActionManager +[web_ir_actions_act_view_reload](web_ir_actions_act_view_reload/) | 15.0.1.0.1 | | Enables reload of the current view via ActionManager [web_ir_actions_act_window_message](web_ir_actions_act_window_message/) | 15.0.1.0.0 | | Show a message box to users [web_listview_range_select](web_listview_range_select/) | 15.0.1.0.0 | | Enables selecting a range of records using the shift key [web_m2x_options](web_m2x_options/) | 15.0.1.1.0 | | web_m2x_options [web_m2x_options_manager](web_m2x_options_manager/) | 15.0.1.0.0 | | Adds an interface to manage the "Create" and "Create and Edit" options for specific models and fields. [web_no_bubble](web_no_bubble/) | 15.0.1.0.0 | | Remove the bubbles from the web interface -[web_refresher](web_refresher/) | 15.0.1.0.0 | | Web Refresher +[web_notify](web_notify/) | 15.0.2.0.0 | | Send notification messages to user +[web_pivot_computed_measure](web_pivot_computed_measure/) | 15.0.1.0.5 | | Web Pivot Computed Measure +[web_pwa_oca](web_pwa_oca/) | 15.0.1.0.0 | [![eLBati](https://github.com/eLBati.png?size=30px)](https://github.com/eLBati) | Make Odoo a PWA +[web_refresher](web_refresher/) | 15.0.2.0.1 | | Web Refresher [web_remember_tree_column_width](web_remember_tree_column_width/) | 15.0.1.0.1 | [![frahikLV](https://github.com/frahikLV.png?size=30px)](https://github.com/frahikLV) [![luisg123v](https://github.com/luisg123v.png?size=30px)](https://github.com/luisg123v) | Remember the tree columns' widths across sessions. -[web_responsive](web_responsive/) | 15.0.1.1.4 | [![Yajo](https://github.com/Yajo.png?size=30px)](https://github.com/Yajo) [![Tardo](https://github.com/Tardo.png?size=30px)](https://github.com/Tardo) [![SplashS](https://github.com/SplashS.png?size=30px)](https://github.com/SplashS) | Responsive web client, community-supported -[web_search_with_and](web_search_with_and/) | 15.0.1.0.0 | | Use AND conditions on omnibar search +[web_responsive](web_responsive/) | 15.0.1.1.12 | [![Yajo](https://github.com/Yajo.png?size=30px)](https://github.com/Yajo) [![Tardo](https://github.com/Tardo.png?size=30px)](https://github.com/Tardo) [![SplashS](https://github.com/SplashS.png?size=30px)](https://github.com/SplashS) | Responsive web client, community-supported +[web_search_with_and](web_search_with_and/) | 15.0.1.0.2 | | Use AND conditions on omnibar search +[web_select_all_companies](web_select_all_companies/) | 15.0.1.0.0 | | Allows you to select all companies in one click. +[web_send_message_popup](web_send_message_popup/) | 15.0.1.0.0 | | Web Send Message as Popup [web_sheet_full_width](web_sheet_full_width/) | 15.0.1.0.1 | | Use the whole available screen width when displaying sheets -[web_timeline](web_timeline/) | 15.0.1.0.1 | [![tarteo](https://github.com/tarteo.png?size=30px)](https://github.com/tarteo) | Interactive visualization chart to show events in time -[web_tree_dynamic_colored_field](web_tree_dynamic_colored_field/) | 15.0.1.0.0 | | Allows you to dynamically color fields on tree views +[web_time_range_menu_custom](web_time_range_menu_custom/) | 15.0.1.0.1 | | Web Time Range Menu Custom +[web_timeline](web_timeline/) | 15.0.1.1.0 | [![tarteo](https://github.com/tarteo.png?size=30px)](https://github.com/tarteo) | Interactive visualization chart to show events in time +[web_tree_dynamic_colored_field](web_tree_dynamic_colored_field/) | 15.0.1.0.1 | | Allows you to dynamically color fields on tree views [web_tree_image_tooltip](web_tree_image_tooltip/) | 15.0.1.1.0 | | Show images in tree views via tooltip +[web_tree_many2one_clickable](web_tree_many2one_clickable/) | 15.0.1.0.0 | | Open the linked resource when clicking on their name [web_view_calendar_list](web_view_calendar_list/) | 15.0.1.0.0 | | Show calendars as a List [web_widget_bokeh_chart](web_widget_bokeh_chart/) | 15.0.1.1.1 | [![LoisRForgeFlow](https://github.com/LoisRForgeFlow.png?size=30px)](https://github.com/LoisRForgeFlow) [![ChrisOForgeFlow](https://github.com/ChrisOForgeFlow.png?size=30px)](https://github.com/ChrisOForgeFlow) | This widget allows to display charts using Bokeh library. +[web_widget_char_size](web_widget_char_size/) | 15.0.1.0.0 | | Add size option to Char widget [web_widget_child_selector](web_widget_child_selector/) | 15.0.1.0.0 | | Widget used for navigation on hierarchy fields -[web_widget_domain_editor_dialog](web_widget_domain_editor_dialog/) | 15.0.1.0.0 | | Recovers the Domain Editor Dialog functionality +[web_widget_domain_editor_dialog](web_widget_domain_editor_dialog/) | 15.0.1.0.1 | | Recovers the Domain Editor Dialog functionality [web_widget_dropdown_dynamic](web_widget_dropdown_dynamic/) | 15.0.1.0.0 | | This module adds support for dynamic dropdown widget [web_widget_image_download](web_widget_image_download/) | 15.0.1.0.0 | | Allows to download any image from its widget [web_widget_json_graph](web_widget_json_graph/) | 15.0.1.0.0 | [![luisg123v](https://github.com/luisg123v.png?size=30px)](https://github.com/luisg123v) [![frahikLV](https://github.com/frahikLV.png?size=30px)](https://github.com/frahikLV) | Draw json fields with graphs. [web_widget_many2one_simple](web_widget_many2one_simple/) | 15.0.1.1.0 | [![Tardo](https://github.com/Tardo.png?size=30px)](https://github.com/Tardo) | Simple many2one widget -[web_widget_numeric_step](web_widget_numeric_step/) | 15.0.1.0.0 | | Web Widget Numeric Step -[web_widget_open_tab](web_widget_open_tab/) | 15.0.1.0.0 | | Allow to open record from trees on new tab from tree views +[web_widget_mpld3_chart](web_widget_mpld3_chart/) | 15.0.1.0.0 | [![JordiBForgeFlow](https://github.com/JordiBForgeFlow.png?size=30px)](https://github.com/JordiBForgeFlow) [![ChrisOForgeFlow](https://github.com/ChrisOForgeFlow.png?size=30px)](https://github.com/ChrisOForgeFlow) | This widget allows to display charts using MPLD3 library. +[web_widget_numeric_step](web_widget_numeric_step/) | 15.0.1.1.0 | | Web Widget Numeric Step +[web_widget_one2many_tree_line_duplicate](web_widget_one2many_tree_line_duplicate/) | 15.0.1.0.0 | | Web Widget One2many Tree Line Duplicate +[web_widget_open_tab](web_widget_open_tab/) | 15.0.1.1.0 | | Allow to open record from trees on new tab from tree views +[web_widget_remote_measure](web_widget_remote_measure/) | 15.0.1.0.0 | [![chienandalu](https://github.com/chienandalu.png?size=30px)](https://github.com/chienandalu) | Allows to connect to remote devices to record measures +[web_widget_text_markdown](web_widget_text_markdown/) | 15.0.1.0.0 | | Widget to text fields that adds markdown support [web_widget_url_advanced](web_widget_url_advanced/) | 15.0.1.0.0 | | This module extends URL widget for displaying anchors with custom labels. -[web_widget_x2many_2d_matrix](web_widget_x2many_2d_matrix/) | 15.0.1.0.1 | [![ChrisOForgeFlow](https://github.com/ChrisOForgeFlow.png?size=30px)](https://github.com/ChrisOForgeFlow) | Show list fields as a matrix +[web_widget_x2many_2d_matrix](web_widget_x2many_2d_matrix/) | 15.0.1.0.3 | [![ChrisOForgeFlow](https://github.com/ChrisOForgeFlow.png?size=30px)](https://github.com/ChrisOForgeFlow) | Show list fields as a matrix [//]: # (end addons) diff --git a/requirements.txt b/requirements.txt index 7f8de6cf649b..f585937d3bbf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,4 @@ # generated from manifests external_dependencies +beautifulsoup4 bokeh==2.4.2 +mpld3==0.5.7 diff --git a/setup/_metapackage/VERSION.txt b/setup/_metapackage/VERSION.txt index 69b06f0a96c3..060d68bb307d 100644 --- a/setup/_metapackage/VERSION.txt +++ b/setup/_metapackage/VERSION.txt @@ -1 +1 @@ -15.0.20221122.0 \ No newline at end of file +15.0.20240709.0 \ No newline at end of file diff --git a/setup/_metapackage/setup.py b/setup/_metapackage/setup.py index a46c0aaee1bd..a6527ed021ca 100644 --- a/setup/_metapackage/setup.py +++ b/setup/_metapackage/setup.py @@ -15,10 +15,13 @@ 'odoo-addon-web_company_color>=15.0dev,<15.1dev', 'odoo-addon-web_copy_confirm>=15.0dev,<15.1dev', 'odoo-addon-web_dialog_size>=15.0dev,<15.1dev', + 'odoo-addon-web_disable_export_group>=15.0dev,<15.1dev', 'odoo-addon-web_domain_field>=15.0dev,<15.1dev', 'odoo-addon-web_drop_target>=15.0dev,<15.1dev', 'odoo-addon-web_environment_ribbon>=15.0dev,<15.1dev', + 'odoo-addon-web_filter_header_button>=15.0dev,<15.1dev', 'odoo-addon-web_group_expand>=15.0dev,<15.1dev', + 'odoo-addon-web_hide_user_menu_item>=15.0dev,<15.1dev', 'odoo-addon-web_ir_actions_act_multi>=15.0dev,<15.1dev', 'odoo-addon-web_ir_actions_act_view_reload>=15.0dev,<15.1dev', 'odoo-addon-web_ir_actions_act_window_message>=15.0dev,<15.1dev', @@ -26,24 +29,36 @@ 'odoo-addon-web_m2x_options>=15.0dev,<15.1dev', 'odoo-addon-web_m2x_options_manager>=15.0dev,<15.1dev', 'odoo-addon-web_no_bubble>=15.0dev,<15.1dev', + 'odoo-addon-web_notify>=15.0dev,<15.1dev', + 'odoo-addon-web_pivot_computed_measure>=15.0dev,<15.1dev', + 'odoo-addon-web_pwa_oca>=15.0dev,<15.1dev', 'odoo-addon-web_refresher>=15.0dev,<15.1dev', 'odoo-addon-web_remember_tree_column_width>=15.0dev,<15.1dev', 'odoo-addon-web_responsive>=15.0dev,<15.1dev', 'odoo-addon-web_search_with_and>=15.0dev,<15.1dev', + 'odoo-addon-web_select_all_companies>=15.0dev,<15.1dev', + 'odoo-addon-web_send_message_popup>=15.0dev,<15.1dev', 'odoo-addon-web_sheet_full_width>=15.0dev,<15.1dev', + 'odoo-addon-web_time_range_menu_custom>=15.0dev,<15.1dev', 'odoo-addon-web_timeline>=15.0dev,<15.1dev', 'odoo-addon-web_tree_dynamic_colored_field>=15.0dev,<15.1dev', 'odoo-addon-web_tree_image_tooltip>=15.0dev,<15.1dev', + 'odoo-addon-web_tree_many2one_clickable>=15.0dev,<15.1dev', 'odoo-addon-web_view_calendar_list>=15.0dev,<15.1dev', 'odoo-addon-web_widget_bokeh_chart>=15.0dev,<15.1dev', + 'odoo-addon-web_widget_char_size>=15.0dev,<15.1dev', 'odoo-addon-web_widget_child_selector>=15.0dev,<15.1dev', 'odoo-addon-web_widget_domain_editor_dialog>=15.0dev,<15.1dev', 'odoo-addon-web_widget_dropdown_dynamic>=15.0dev,<15.1dev', 'odoo-addon-web_widget_image_download>=15.0dev,<15.1dev', 'odoo-addon-web_widget_json_graph>=15.0dev,<15.1dev', 'odoo-addon-web_widget_many2one_simple>=15.0dev,<15.1dev', + 'odoo-addon-web_widget_mpld3_chart>=15.0dev,<15.1dev', 'odoo-addon-web_widget_numeric_step>=15.0dev,<15.1dev', + 'odoo-addon-web_widget_one2many_tree_line_duplicate>=15.0dev,<15.1dev', 'odoo-addon-web_widget_open_tab>=15.0dev,<15.1dev', + 'odoo-addon-web_widget_remote_measure>=15.0dev,<15.1dev', + 'odoo-addon-web_widget_text_markdown>=15.0dev,<15.1dev', 'odoo-addon-web_widget_url_advanced>=15.0dev,<15.1dev', 'odoo-addon-web_widget_x2many_2d_matrix>=15.0dev,<15.1dev', ], diff --git a/setup/web_disable_export_group/odoo/addons/web_disable_export_group b/setup/web_disable_export_group/odoo/addons/web_disable_export_group new file mode 120000 index 000000000000..d8a2f35c1a37 --- /dev/null +++ b/setup/web_disable_export_group/odoo/addons/web_disable_export_group @@ -0,0 +1 @@ +../../../../web_disable_export_group \ No newline at end of file diff --git a/setup/web_disable_export_group/setup.py b/setup/web_disable_export_group/setup.py new file mode 100644 index 000000000000..28c57bb64031 --- /dev/null +++ b/setup/web_disable_export_group/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/web_filter_header_button/odoo/addons/web_filter_header_button b/setup/web_filter_header_button/odoo/addons/web_filter_header_button new file mode 120000 index 000000000000..ff6d74942fc8 --- /dev/null +++ b/setup/web_filter_header_button/odoo/addons/web_filter_header_button @@ -0,0 +1 @@ +../../../../web_filter_header_button \ No newline at end of file diff --git a/setup/web_filter_header_button/setup.py b/setup/web_filter_header_button/setup.py new file mode 100644 index 000000000000..28c57bb64031 --- /dev/null +++ b/setup/web_filter_header_button/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/web_hide_user_menu_item/odoo/addons/web_hide_user_menu_item b/setup/web_hide_user_menu_item/odoo/addons/web_hide_user_menu_item new file mode 120000 index 000000000000..f17cbbee04a7 --- /dev/null +++ b/setup/web_hide_user_menu_item/odoo/addons/web_hide_user_menu_item @@ -0,0 +1 @@ +../../../../web_hide_user_menu_item \ No newline at end of file diff --git a/setup/web_hide_user_menu_item/setup.py b/setup/web_hide_user_menu_item/setup.py new file mode 100644 index 000000000000..28c57bb64031 --- /dev/null +++ b/setup/web_hide_user_menu_item/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/web_notify/odoo/addons/web_notify b/setup/web_notify/odoo/addons/web_notify new file mode 120000 index 000000000000..f778fe739b62 --- /dev/null +++ b/setup/web_notify/odoo/addons/web_notify @@ -0,0 +1 @@ +../../../../web_notify \ No newline at end of file diff --git a/setup/web_notify/setup.py b/setup/web_notify/setup.py new file mode 100644 index 000000000000..28c57bb64031 --- /dev/null +++ b/setup/web_notify/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/web_pivot_computed_measure/odoo/addons/web_pivot_computed_measure b/setup/web_pivot_computed_measure/odoo/addons/web_pivot_computed_measure new file mode 120000 index 000000000000..282ada6fd43e --- /dev/null +++ b/setup/web_pivot_computed_measure/odoo/addons/web_pivot_computed_measure @@ -0,0 +1 @@ +../../../../web_pivot_computed_measure \ No newline at end of file diff --git a/setup/web_pivot_computed_measure/setup.py b/setup/web_pivot_computed_measure/setup.py new file mode 100644 index 000000000000..28c57bb64031 --- /dev/null +++ b/setup/web_pivot_computed_measure/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/web_pwa_oca/odoo/addons/web_pwa_oca b/setup/web_pwa_oca/odoo/addons/web_pwa_oca new file mode 120000 index 000000000000..1b7095260fe6 --- /dev/null +++ b/setup/web_pwa_oca/odoo/addons/web_pwa_oca @@ -0,0 +1 @@ +../../../../web_pwa_oca \ No newline at end of file diff --git a/setup/web_pwa_oca/setup.py b/setup/web_pwa_oca/setup.py new file mode 100644 index 000000000000..28c57bb64031 --- /dev/null +++ b/setup/web_pwa_oca/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/web_select_all_companies/odoo/addons/web_select_all_companies b/setup/web_select_all_companies/odoo/addons/web_select_all_companies new file mode 120000 index 000000000000..012ec121cc9e --- /dev/null +++ b/setup/web_select_all_companies/odoo/addons/web_select_all_companies @@ -0,0 +1 @@ +../../../../web_select_all_companies \ No newline at end of file diff --git a/setup/web_select_all_companies/setup.py b/setup/web_select_all_companies/setup.py new file mode 100644 index 000000000000..28c57bb64031 --- /dev/null +++ b/setup/web_select_all_companies/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/web_send_message_popup/odoo/addons/web_send_message_popup b/setup/web_send_message_popup/odoo/addons/web_send_message_popup new file mode 120000 index 000000000000..6fb492b625aa --- /dev/null +++ b/setup/web_send_message_popup/odoo/addons/web_send_message_popup @@ -0,0 +1 @@ +../../../../web_send_message_popup \ No newline at end of file diff --git a/setup/web_send_message_popup/setup.py b/setup/web_send_message_popup/setup.py new file mode 100644 index 000000000000..28c57bb64031 --- /dev/null +++ b/setup/web_send_message_popup/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/web_time_range_menu_custom/odoo/addons/web_time_range_menu_custom b/setup/web_time_range_menu_custom/odoo/addons/web_time_range_menu_custom new file mode 120000 index 000000000000..ac6f76901ab0 --- /dev/null +++ b/setup/web_time_range_menu_custom/odoo/addons/web_time_range_menu_custom @@ -0,0 +1 @@ +../../../../web_time_range_menu_custom \ No newline at end of file diff --git a/setup/web_time_range_menu_custom/setup.py b/setup/web_time_range_menu_custom/setup.py new file mode 100644 index 000000000000..28c57bb64031 --- /dev/null +++ b/setup/web_time_range_menu_custom/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/web_tree_many2one_clickable/odoo/addons/web_tree_many2one_clickable b/setup/web_tree_many2one_clickable/odoo/addons/web_tree_many2one_clickable new file mode 120000 index 000000000000..7c50080240c8 --- /dev/null +++ b/setup/web_tree_many2one_clickable/odoo/addons/web_tree_many2one_clickable @@ -0,0 +1 @@ +../../../../web_tree_many2one_clickable \ No newline at end of file diff --git a/setup/web_tree_many2one_clickable/setup.py b/setup/web_tree_many2one_clickable/setup.py new file mode 100644 index 000000000000..28c57bb64031 --- /dev/null +++ b/setup/web_tree_many2one_clickable/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/web_widget_char_size/odoo/addons/web_widget_char_size b/setup/web_widget_char_size/odoo/addons/web_widget_char_size new file mode 120000 index 000000000000..abd6fe84bbba --- /dev/null +++ b/setup/web_widget_char_size/odoo/addons/web_widget_char_size @@ -0,0 +1 @@ +../../../../web_widget_char_size \ No newline at end of file diff --git a/setup/web_widget_char_size/setup.py b/setup/web_widget_char_size/setup.py new file mode 100644 index 000000000000..28c57bb64031 --- /dev/null +++ b/setup/web_widget_char_size/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/web_widget_mpld3_chart/odoo/addons/web_widget_mpld3_chart b/setup/web_widget_mpld3_chart/odoo/addons/web_widget_mpld3_chart new file mode 120000 index 000000000000..5b2ad03d233c --- /dev/null +++ b/setup/web_widget_mpld3_chart/odoo/addons/web_widget_mpld3_chart @@ -0,0 +1 @@ +../../../../web_widget_mpld3_chart \ No newline at end of file diff --git a/setup/web_widget_mpld3_chart/setup.py b/setup/web_widget_mpld3_chart/setup.py new file mode 100644 index 000000000000..28c57bb64031 --- /dev/null +++ b/setup/web_widget_mpld3_chart/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/web_widget_one2many_tree_line_duplicate/odoo/addons/web_widget_one2many_tree_line_duplicate b/setup/web_widget_one2many_tree_line_duplicate/odoo/addons/web_widget_one2many_tree_line_duplicate new file mode 120000 index 000000000000..8ba1faf8da0e --- /dev/null +++ b/setup/web_widget_one2many_tree_line_duplicate/odoo/addons/web_widget_one2many_tree_line_duplicate @@ -0,0 +1 @@ +../../../../web_widget_one2many_tree_line_duplicate \ No newline at end of file diff --git a/setup/web_widget_one2many_tree_line_duplicate/setup.py b/setup/web_widget_one2many_tree_line_duplicate/setup.py new file mode 100644 index 000000000000..28c57bb64031 --- /dev/null +++ b/setup/web_widget_one2many_tree_line_duplicate/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/web_widget_remote_measure/odoo/addons/web_widget_remote_measure b/setup/web_widget_remote_measure/odoo/addons/web_widget_remote_measure new file mode 120000 index 000000000000..8b24be62932c --- /dev/null +++ b/setup/web_widget_remote_measure/odoo/addons/web_widget_remote_measure @@ -0,0 +1 @@ +../../../../web_widget_remote_measure \ No newline at end of file diff --git a/setup/web_widget_remote_measure/setup.py b/setup/web_widget_remote_measure/setup.py new file mode 100644 index 000000000000..28c57bb64031 --- /dev/null +++ b/setup/web_widget_remote_measure/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/web_widget_text_markdown/odoo/addons/web_widget_text_markdown b/setup/web_widget_text_markdown/odoo/addons/web_widget_text_markdown new file mode 120000 index 000000000000..872e5f4adeb1 --- /dev/null +++ b/setup/web_widget_text_markdown/odoo/addons/web_widget_text_markdown @@ -0,0 +1 @@ +../../../../web_widget_text_markdown \ No newline at end of file diff --git a/setup/web_widget_text_markdown/setup.py b/setup/web_widget_text_markdown/setup.py new file mode 100644 index 000000000000..28c57bb64031 --- /dev/null +++ b/setup/web_widget_text_markdown/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/web_action_conditionable/README.rst b/web_action_conditionable/README.rst index a57f528ff210..69302fa48a60 100644 --- a/web_action_conditionable/README.rst +++ b/web_action_conditionable/README.rst @@ -2,10 +2,13 @@ web_action_conditionable ======================== -.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:75daa9e631bea1774ee5841652ddeca2ab2a80610ef8acb78d23a81871d98296 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status @@ -19,11 +22,11 @@ web_action_conditionable .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png :target: https://translation.odoo-community.org/projects/web-15-0/web-15-0-web_action_conditionable :alt: Translate me on Weblate -.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/162/15.0 - :alt: Try me on Runbot +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/web&target_branch=15.0 + :alt: Try me on Runboat -|badge1| |badge2| |badge3| |badge4| |badge5| +|badge1| |badge2| |badge3| |badge4| |badge5| Add support for conditions on create and delete actions on One2Many fields. @@ -54,7 +57,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. -If you spotted it first, help us smashing it by providing a detailed and welcomed +If you spotted it first, help us to smash it by providing a detailed and welcomed `feedback `_. Do not contact contributors directly about support or help with technical issues. diff --git a/web_action_conditionable/i18n/es.po b/web_action_conditionable/i18n/es.po index 7461f0845d7e..e69de29bb2d1 100644 --- a/web_action_conditionable/i18n/es.po +++ b/web_action_conditionable/i18n/es.po @@ -1,14 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 12.0\n" -"Report-Msgid-Bugs-To: \n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" -"Language: es\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: nplurals=2; plural=n != 1;\n" diff --git a/web_action_conditionable/i18n/hr.po b/web_action_conditionable/i18n/hr.po index 62cb4ae999e4..e69de29bb2d1 100644 --- a/web_action_conditionable/i18n/hr.po +++ b/web_action_conditionable/i18n/hr.po @@ -1,15 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 12.0\n" -"Report-Msgid-Bugs-To: \n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" -"Language: hr\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=" -"4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" diff --git a/web_action_conditionable/i18n/zh_CN.po b/web_action_conditionable/i18n/zh_CN.po index 4159be77e050..e69de29bb2d1 100644 --- a/web_action_conditionable/i18n/zh_CN.po +++ b/web_action_conditionable/i18n/zh_CN.po @@ -1,14 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 12.0\n" -"Report-Msgid-Bugs-To: \n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" -"Language: zh_CN\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: nplurals=1; plural=0;\n" diff --git a/web_action_conditionable/static/description/index.html b/web_action_conditionable/static/description/index.html index 2b6d26f5170a..7e32f242d8d6 100644 --- a/web_action_conditionable/static/description/index.html +++ b/web_action_conditionable/static/description/index.html @@ -1,20 +1,20 @@ - + - + web_action_conditionable + + +
+

Web Disable Export Group

+ + +

Beta License: AGPL-3 OCA/web Translate me on Weblate Try me on Runboat

+

The standard grants/prevents access to any UI export via Access to export feature +group.

+

This module adds a new group for the ‘Direct Export (xlsx)’ feature, leaving the +standard one for only the ‘Export All’ feature.

+

Admin users can always use the export option.

+

Table of contents

+ +
+

Configuration

+

Enable the group Direct Export to the users who are allowed to make use of the option +‘Export xlsx’ from the list view.

+
+
+

Usage

+
    +
  • Users in the Access to export feature group or admins can export in any way.
  • +
  • Users in the Direct Export (xlsx) group can only use the default export feature +from the list view.
  • +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Onestein
  • +
  • Tecnativa
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/web project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/web_disable_export_group/static/src/js/abstract_controller.esm.js b/web_disable_export_group/static/src/js/abstract_controller.esm.js new file mode 100644 index 000000000000..8576a18a20cd --- /dev/null +++ b/web_disable_export_group/static/src/js/abstract_controller.esm.js @@ -0,0 +1,26 @@ +/** @odoo-module **/ +/* Copyright 2016 Onestein + Copyright 2018 Tecnativa - David Vidal + Copyright 2021 Tecnativa - Alexandre Díaz + Copyright 2022 Tecnativa - Víctor Martínez + License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). */ +import AbstractController from "web.AbstractController"; +import session from "web.session"; + +AbstractController.include({ + /** + * @override + */ + is_action_enabled: function (action) { + if ( + !session.is_superuser && + action && + action === "export_xlsx" && + !session.group_xlsx_export_data + ) { + return false; + } + + return this._super.apply(this, arguments); + }, +}); diff --git a/web_disable_export_group/static/src/js/list_controller.esm.js b/web_disable_export_group/static/src/js/list_controller.esm.js new file mode 100644 index 000000000000..a38c4decff51 --- /dev/null +++ b/web_disable_export_group/static/src/js/list_controller.esm.js @@ -0,0 +1,19 @@ +/** @odoo-module **/ +/* Copyright 2018 Tecnativa - David Vidal + License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). */ +import ListController from "web.ListController"; +import session from "web.session"; + +ListController.include({ + init() { + this._super(...arguments); + this.isExportXlsEnable = false; + }, + async willStart() { + const res = await this._super(...arguments); + this.isExportXlsEnable = await session.user_has_group( + "web_disable_export_group.group_export_xlsx_data" + ); + return res; + }, +}); diff --git a/web_disable_export_group/static/src/tours/web_disable_export_group_tour.esm.js b/web_disable_export_group/static/src/tours/web_disable_export_group_tour.esm.js new file mode 100644 index 000000000000..1702df8d6cfd --- /dev/null +++ b/web_disable_export_group/static/src/tours/web_disable_export_group_tour.esm.js @@ -0,0 +1,33 @@ +/** @odoo-module **/ +/* Copyright 2020 Tecnativa - João Marques + Copyright 2022 Tecnativa - Víctor Martínez + License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). */ + +import tour from "web_tour.tour"; + +tour.register( + "export_tour_xlsx_button_ok", + { + test: true, + url: "/web#model=ir.ui.view&view_type=list&cids=&action=base.action_ui_view", + }, + [ + { + content: "Check if 'Export all' button exists", + trigger: ".o_list_buttons:has(.o_list_export_xlsx)", + }, + ] +); +tour.register( + "export_tour_xlsx_button_ko", + { + test: true, + url: "/web#model=ir.ui.view&view_type=list&cids=&action=base.action_ui_view", + }, + [ + { + content: "Check if 'Export all' button exists", + trigger: ".o_list_buttons:not(:has(.o_list_export_xlsx))", + }, + ] +); diff --git a/web_disable_export_group/static/src/xml/export_xls_views.xml b/web_disable_export_group/static/src/xml/export_xls_views.xml new file mode 100644 index 000000000000..84fecc27abf6 --- /dev/null +++ b/web_disable_export_group/static/src/xml/export_xls_views.xml @@ -0,0 +1,12 @@ + + + + + + widget.is_action_enabled('export_xlsx') and widget.isExportXlsEnable + + + + diff --git a/web_disable_export_group/tests/__init__.py b/web_disable_export_group/tests/__init__.py new file mode 100644 index 000000000000..f49429ef69a8 --- /dev/null +++ b/web_disable_export_group/tests/__init__.py @@ -0,0 +1 @@ +from . import test_tour diff --git a/web_disable_export_group/tests/test_tour.py b/web_disable_export_group/tests/test_tour.py new file mode 100644 index 000000000000..9bfaf27e7a38 --- /dev/null +++ b/web_disable_export_group/tests/test_tour.py @@ -0,0 +1,33 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +# Copyright 2020 Tecnativa - João Marques +# Copyright 2022 Tecnativa - Víctor Martínez +import odoo.tests +from odoo.tests import new_test_user + + +@odoo.tests.tagged("post_install", "-at_install") +class TestTour(odoo.tests.HttpCase): + def setUp(self): + super().setUp() + new_test_user( + self.env, + login="user_not_export", + password="user_not_export", + groups="base.group_user,base.group_system", + ) + new_test_user( + self.env, + login="user_export_xlsx", + password="user_export_xlsx", + groups="base.group_user,base.group_system,%s" + % ("web_disable_export_group.group_export_xlsx_data"), + ) + + def test_admin(self): + self.start_tour("/web", "export_tour_xlsx_button_ok", login="admin") + + def test_user_not_export(self): + self.start_tour("/web", "export_tour_xlsx_button_ko", login="user_not_export") + + def test_user_export_xlsx(self): + self.start_tour("/web", "export_tour_xlsx_button_ok", login="user_export_xlsx") diff --git a/web_domain_field/README.rst b/web_domain_field/README.rst index fd67312ed494..932f5f235c75 100644 --- a/web_domain_field/README.rst +++ b/web_domain_field/README.rst @@ -2,10 +2,13 @@ Web Domain Field ================ -.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:3f7d060f86b7af93de337bbdfe21db684a89efd18249d2a4b1e5f23167597704 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status @@ -19,11 +22,11 @@ Web Domain Field .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png :target: https://translation.odoo-community.org/projects/web-15-0/web-15-0-web_domain_field :alt: Translate me on Weblate -.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/162/15.0 - :alt: Try me on Runbot +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/web&target_branch=15.0 + :alt: Try me on Runboat -|badge1| |badge2| |badge3| |badge4| |badge5| +|badge1| |badge2| |badge3| |badge4| |badge5| When you define a view you can specify on the relational fields a domain attribute. This attribute is evaluated as filter to apply when displaying @@ -94,7 +97,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. -If you spotted it first, help us smashing it by providing a detailed and welcomed +If you spotted it first, help us to smash it by providing a detailed and welcomed `feedback `_. Do not contact contributors directly about support or help with technical issues. diff --git a/web_domain_field/static/description/index.html b/web_domain_field/static/description/index.html index 616d351faa1c..cd0cace0c705 100644 --- a/web_domain_field/static/description/index.html +++ b/web_domain_field/static/description/index.html @@ -1,20 +1,20 @@ - + - + Web Domain Field + + +
+

Filter Button

+ + +

Beta License: AGPL-3 OCA/web Translate me on Weblate Try me on Runboat

+

This module allows to add some selected filters as buttons in the header +control panel.

+

Table of contents

+ +
+

Use Cases / Context

+

This developement is aimed to ease the filter access for touch screens +users.

+
+
+

Configuration

+

To show a filter in the header of the views, it should have the a +context attribute with the key shown_in_panel.

+
+<filter
+    string="My filter"
+    name="my_filter"
+    domain="[('active', '!=', False)]"
+    context="{'shown_in_panel': True}"
+>
+
+

This will show the filter in the header with its name. You can customize +the button adding an icon or with a custom name passing an object to +that key:

+
+{'shown_in_panel': {'icon': 'fa-thumbs-up', 'name': 'Ok'}}
+
+

You might be interested in leaving just the icon. In that case, set an +empty string on the name property:

+
+{'shown_in_panel': {'icon': 'fa-thumbs-up', 'name': ''}}
+
+

You could also want to add a hotkey. In such case add the hotkey +property:

+
+{'shown_in_panel': {'icon': 'fa-thumbs-up', 'hotkey': 'F'}}
+
+

You can show filter, groups or even favorites.

+
+
+

Usage

+

There’s a demo implementation in Apps and you can play around +following the Configure section.

+
+
+

Known issues / Roadmap

+
    +
  • Group filters by kind
  • +
  • As we use the context attribute, the inheritance could be +limiting in some cases. Keep it in mind or use +base_view_inheritance_extension if you want to use proper context +inheritance.
  • +
  • Another nice to have would be to be able to hide the filters in the +filter list to be able to show them just in the header, although +there’s not a straigh forward way to do it and it could lead to side +effects.
  • +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Tecnativa
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/web project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/web_filter_header_button/static/src/control_panel/control_panel.esm.js b/web_filter_header_button/static/src/control_panel/control_panel.esm.js new file mode 100644 index 000000000000..75b54f56dc99 --- /dev/null +++ b/web_filter_header_button/static/src/control_panel/control_panel.esm.js @@ -0,0 +1,11 @@ +/** @odoo-module **/ +import {FilterButton} from "../filter_button/filter_button.esm"; +import LegacyControlPanel from "web.ControlPanel"; +import {patch} from "web.utils"; + +patch(LegacyControlPanel, "filter_button.ControlPanel", { + components: { + ...LegacyControlPanel.components, + FilterButton, + }, +}); diff --git a/web_filter_header_button/static/src/control_panel/control_panel.xml b/web_filter_header_button/static/src/control_panel/control_panel.xml new file mode 100644 index 000000000000..8b3654da51cc --- /dev/null +++ b/web_filter_header_button/static/src/control_panel/control_panel.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/web_filter_header_button/static/src/control_panel/control_panel_model_extension.esm.js b/web_filter_header_button/static/src/control_panel/control_panel_model_extension.esm.js new file mode 100644 index 000000000000..c08124e43e82 --- /dev/null +++ b/web_filter_header_button/static/src/control_panel/control_panel_model_extension.esm.js @@ -0,0 +1,48 @@ +/** @odoo-module **/ +import LegacyControlPanelModelExtension from "web/static/src/js/control_panel/control_panel_model_extension.js"; +import {patch} from "web.utils"; + +patch( + LegacyControlPanelModelExtension.prototype, + "filter_button.ControlPanelModelExtension", + { + /** + * Clean the `show_in_panel` context to avoid showing it in the panel + * @private + * @override + * @param {Object} preFilter + * @returns {Promise} + */ + async _saveQuery() { + const preFilter = await this._super(...arguments); + if (preFilter && preFilter.context) { + delete preFilter.context.shown_in_panel; + } + return preFilter; + }, + /** + * Clear the `show_in_panel` context to prevent it being saved with this context + * @override + * @returns {Object} + */ + getIrFilterValues() { + const preFilter = this._super(...arguments); + if (preFilter && preFilter.context) { + delete preFilter.context.shown_in_panel; + } + return preFilter; + }, + /** + * Allow groupBy filters to show up as buttons + * @override + * @param {Object} filter + * @param {Object} attrs + */ + _extractAttributes(filter, attrs) { + this._super(...arguments); + if (filter.type === "groupBy" && attrs.context.shown_in_panel) { + filter.context = attrs.context; + } + }, + } +); diff --git a/web_filter_header_button/static/src/filter_button/filter_button.esm.js b/web_filter_header_button/static/src/filter_button/filter_button.esm.js new file mode 100644 index 000000000000..b0881432fff9 --- /dev/null +++ b/web_filter_header_button/static/src/filter_button/filter_button.esm.js @@ -0,0 +1,53 @@ +/** @odoo-module **/ +const {Component} = owl; + +export class FilterButton extends Component { + setup() { + this.model = this.env.searchModel; + } + /** + * Filter flagged filters to be shown in the control panel. + * + * @param {Array} filters + * @returns {Array} + */ + shownFilters(filters) { + return filters.filter((filter) => { + return filter.context && filter.context.shown_in_panel; + }); + } + /** + * Return custom properties depending on the filter properties + * + * @param {Object} filter + * @returns {Object} + */ + mapFilterType(filter) { + const mapping = { + filter: { + color: "primary", + }, + favorite: { + color: "warning", + }, + groupBy: { + color: "info", + }, + }; + return mapping[filter.type]; + } + /** + * Clear filters + */ + onClickReset() { + this.model.dispatch("clearQuery"); + } + /** + * Set / unset filter + * @param {Object} filter + */ + onToggleFilter(filter) { + this.model.dispatch("toggleFilter", filter.id); + } +} +FilterButton.template = "filter_button.FilterButton"; diff --git a/web_filter_header_button/static/src/filter_button/filter_button.xml b/web_filter_header_button/static/src/filter_button/filter_button.xml new file mode 100644 index 000000000000..fcdf2f2b4cb0 --- /dev/null +++ b/web_filter_header_button/static/src/filter_button/filter_button.xml @@ -0,0 +1,40 @@ + + + + + +
+
+ + + + + + + + + + +
+
+
+
diff --git a/web_group_expand/README.rst b/web_group_expand/README.rst index 340574ce1742..6f7f58369349 100644 --- a/web_group_expand/README.rst +++ b/web_group_expand/README.rst @@ -2,10 +2,13 @@ Group Expand Buttons ==================== -.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:1e707b0c988c388ee8ae35b00354343f1e961eb524838d3fd56348df767e4ac8 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status @@ -19,11 +22,11 @@ Group Expand Buttons .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png :target: https://translation.odoo-community.org/projects/web-15-0/web-15-0-web_group_expand :alt: Translate me on Weblate -.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/162/15.0 - :alt: Try me on Runbot +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/web&target_branch=15.0 + :alt: Try me on Runboat -|badge1| |badge2| |badge3| |badge4| |badge5| +|badge1| |badge2| |badge3| |badge4| |badge5| When grouping a list by a field, this module adds two buttons to expand or collapse all the groups at once. @@ -42,7 +45,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. -If you spotted it first, help us smashing it by providing a detailed and welcomed +If you spotted it first, help us to smash it by providing a detailed and welcomed `feedback `_. Do not contact contributors directly about support or help with technical issues. diff --git a/web_group_expand/i18n/es.po b/web_group_expand/i18n/es.po index d8a65f3d41df..b6902b674194 100644 --- a/web_group_expand/i18n/es.po +++ b/web_group_expand/i18n/es.po @@ -1,6 +1,6 @@ # Translation of Odoo Server. # This file contains the translation of the following modules: -# * web_group_expand +# * web_group_expand # msgid "" msgstr "" @@ -18,14 +18,14 @@ msgstr "" #. module: web_group_expand #. openerp-web -#: code:addons/web_group_expand/static/src/xml/expand_buttons.xml:12 +#: code:addons/web_group_expand/static/src/xml/expand_buttons.xml:0 #, python-format msgid "Collapse groups" msgstr "Colapsar grupos" #. module: web_group_expand #. openerp-web -#: code:addons/web_group_expand/static/src/xml/expand_buttons.xml:8 +#: code:addons/web_group_expand/static/src/xml/expand_buttons.xml:0 #, python-format msgid "Expand groups" msgstr "Expandir grupos" diff --git a/web_group_expand/i18n/nl.po b/web_group_expand/i18n/nl.po index 6c919760b069..774f3d95969e 100644 --- a/web_group_expand/i18n/nl.po +++ b/web_group_expand/i18n/nl.po @@ -18,14 +18,14 @@ msgstr "" #. module: web_group_expand #. openerp-web -#: code:addons/web_group_expand/static/src/xml/expand_buttons.xml:12 +#: code:addons/web_group_expand/static/src/xml/expand_buttons.xml:0 #, python-format msgid "Collapse groups" msgstr "Groepen inklappen" #. module: web_group_expand #. openerp-web -#: code:addons/web_group_expand/static/src/xml/expand_buttons.xml:8 +#: code:addons/web_group_expand/static/src/xml/expand_buttons.xml:0 #, python-format msgid "Expand groups" msgstr "Groepen uitvouwen" diff --git a/web_group_expand/i18n/pt_BR.po b/web_group_expand/i18n/pt_BR.po index 7d5bed5a2469..cb6fd5cd9712 100644 --- a/web_group_expand/i18n/pt_BR.po +++ b/web_group_expand/i18n/pt_BR.po @@ -1,6 +1,6 @@ # Translation of Odoo Server. # This file contains the translation of the following modules: -# * web_group_expand +# * web_group_expand # msgid "" msgstr "" @@ -18,14 +18,14 @@ msgstr "" #. module: web_group_expand #. openerp-web -#: code:addons/web_group_expand/static/src/xml/expand_buttons.xml:12 +#: code:addons/web_group_expand/static/src/xml/expand_buttons.xml:0 #, python-format msgid "Collapse groups" msgstr "Recolher grupos" #. module: web_group_expand #. openerp-web -#: code:addons/web_group_expand/static/src/xml/expand_buttons.xml:8 +#: code:addons/web_group_expand/static/src/xml/expand_buttons.xml:0 #, python-format msgid "Expand groups" msgstr "Expandir grupos" diff --git a/web_group_expand/static/description/index.html b/web_group_expand/static/description/index.html index 5e91c6b7faf7..995e7940bebe 100644 --- a/web_group_expand/static/description/index.html +++ b/web_group_expand/static/description/index.html @@ -1,20 +1,20 @@ - + - + Group Expand Buttons + + +
+

Web Hide User Menu Item

+ + +

Beta License: LGPL-3 OCA/web Translate me on Weblate Try me on Runboat

+

This module hides documentation, support, shortcuts, account menu items on the top-right corner of the webclient.

+

Table of contents

+ +
+

Usage

+
+
Upon installing the module, user menu item presentation will be stripped down to look like the below image:
+
https://raw.githubusercontent.com/OCA/web/15.0/web_hide_user_menu_item/static/img/userscreen.png +
+
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Quartile Limited
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/web project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/web_hide_user_menu_item/static/img/userscreen.png b/web_hide_user_menu_item/static/img/userscreen.png new file mode 100644 index 000000000000..f0918d887119 Binary files /dev/null and b/web_hide_user_menu_item/static/img/userscreen.png differ diff --git a/web_hide_user_menu_item/static/src/webclient/user_menu/user_menu.xml b/web_hide_user_menu_item/static/src/webclient/user_menu/user_menu.xml new file mode 100644 index 000000000000..0c2f7350c9f0 --- /dev/null +++ b/web_hide_user_menu_item/static/src/webclient/user_menu/user_menu.xml @@ -0,0 +1,10 @@ + + + + + !element.hide and !['documentation', 'support', 'shortcuts', 'account'].includes(element.id) + + + diff --git a/web_ir_actions_act_multi/README.rst b/web_ir_actions_act_multi/README.rst index 69be81f450bf..020f9ef504e9 100644 --- a/web_ir_actions_act_multi/README.rst +++ b/web_ir_actions_act_multi/README.rst @@ -2,10 +2,13 @@ Web Actions Multi ================= -.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:66b06abd26db8fc8d1ca1ad5195158dce9c3b9feed4bce0a9bb2ab564aa7520e + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status @@ -19,11 +22,11 @@ Web Actions Multi .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png :target: https://translation.odoo-community.org/projects/web-15-0/web-15-0-web_ir_actions_act_multi :alt: Translate me on Weblate -.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/162/15.0 - :alt: Try me on Runbot +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/web&target_branch=15.0 + :alt: Try me on Runboat -|badge1| |badge2| |badge3| |badge4| |badge5| +|badge1| |badge2| |badge3| |badge4| |badge5| This module provides a way to trigger more than one action on ActionManager @@ -54,7 +57,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. -If you spotted it first, help us smashing it by providing a detailed and welcomed +If you spotted it first, help us to smash it by providing a detailed and welcomed `feedback `_. Do not contact contributors directly about support or help with technical issues. diff --git a/web_ir_actions_act_multi/i18n/es.po b/web_ir_actions_act_multi/i18n/es.po index cdd0dca4a857..d2bee2ebc06d 100644 --- a/web_ir_actions_act_multi/i18n/es.po +++ b/web_ir_actions_act_multi/i18n/es.po @@ -5,10 +5,105 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 13.0\n" "Report-Msgid-Bugs-To: \n" -"Last-Translator: Automatically generated\n" +"PO-Revision-Date: 2023-11-19 19:33+0000\n" +"Last-Translator: Ivorra78 \n" "Language-Team: none\n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: \n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi__help +msgid "Action Description" +msgstr "Descripción de la acción" + +#. module: web_ir_actions_act_multi +#: model:ir.model,name:web_ir_actions_act_multi.model_ir_actions_act_multi +msgid "Action Mulit" +msgstr "Acción Mulit" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi__type +msgid "Action Type" +msgstr "Tipo de Acción" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi__binding_model_id +msgid "Binding Model" +msgstr "Modelo de encuadernación" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi__binding_type +msgid "Binding Type" +msgstr "Tipo de encuadernación" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi__binding_view_types +msgid "Binding View Types" +msgstr "Tipos de vistas vinculantes" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi__create_uid +msgid "Created by" +msgstr "creado por" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi__create_date +msgid "Created on" +msgstr "Creado el" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi__display_name +msgid "Display Name" +msgstr "Mostrar Nombre" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi__xml_id +msgid "External ID" +msgstr "ID Externa" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi__id +msgid "ID" +msgstr "ID(identificación)" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi____last_update +msgid "Last Modified on" +msgstr "Última Modificación en" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi__write_uid +msgid "Last Updated by" +msgstr "Última actualización Por" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi__write_date +msgid "Last Updated on" +msgstr "Última Actualización el" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi__name +msgid "Name" +msgstr "Nombre" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,help:web_ir_actions_act_multi.field_ir_actions_act_multi__help +msgid "" +"Optional help text for the users with a description of the target view, such " +"as its usage and purpose." +msgstr "" +"Texto de ayuda opcional para los usuarios con una descripción de la vista de " +"destino, como su uso y propósito." + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,help:web_ir_actions_act_multi.field_ir_actions_act_multi__binding_model_id +msgid "" +"Setting a value makes this action available in the sidebar for the given " +"model." +msgstr "" +"Establecer un valor hace que esta acción esté disponible en la barra lateral " +"para el modelo dado." diff --git a/web_ir_actions_act_multi/i18n/zh_CN.po b/web_ir_actions_act_multi/i18n/zh_CN.po index 4159be77e050..697f2833970e 100644 --- a/web_ir_actions_act_multi/i18n/zh_CN.po +++ b/web_ir_actions_act_multi/i18n/zh_CN.po @@ -12,3 +12,92 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: \n" "Plural-Forms: nplurals=1; plural=0;\n" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi__help +msgid "Action Description" +msgstr "" + +#. module: web_ir_actions_act_multi +#: model:ir.model,name:web_ir_actions_act_multi.model_ir_actions_act_multi +msgid "Action Mulit" +msgstr "" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi__type +msgid "Action Type" +msgstr "" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi__binding_model_id +msgid "Binding Model" +msgstr "" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi__binding_type +msgid "Binding Type" +msgstr "" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi__binding_view_types +msgid "Binding View Types" +msgstr "" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi__create_uid +msgid "Created by" +msgstr "" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi__create_date +msgid "Created on" +msgstr "" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi__display_name +msgid "Display Name" +msgstr "" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi__xml_id +msgid "External ID" +msgstr "" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi__id +msgid "ID" +msgstr "" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi____last_update +msgid "Last Modified on" +msgstr "" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi__write_date +msgid "Last Updated on" +msgstr "" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,field_description:web_ir_actions_act_multi.field_ir_actions_act_multi__name +msgid "Name" +msgstr "" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,help:web_ir_actions_act_multi.field_ir_actions_act_multi__help +msgid "" +"Optional help text for the users with a description of the target view, such " +"as its usage and purpose." +msgstr "" + +#. module: web_ir_actions_act_multi +#: model:ir.model.fields,help:web_ir_actions_act_multi.field_ir_actions_act_multi__binding_model_id +msgid "" +"Setting a value makes this action available in the sidebar for the given " +"model." +msgstr "" diff --git a/web_ir_actions_act_multi/static/description/index.html b/web_ir_actions_act_multi/static/description/index.html index 78cac1418c5d..c5c654a9d65c 100644 --- a/web_ir_actions_act_multi/static/description/index.html +++ b/web_ir_actions_act_multi/static/description/index.html @@ -1,20 +1,20 @@ - + - + Web Actions Multi + + +
+

Web Notify

+ + +

Production/Stable License: AGPL-3 OCA/web Translate me on Weblate Try me on Runboat

+

Send instant notification messages to the user in live.

+

This technical module allows you to send instant notification messages from the server to the user in live. +Two kinds of notification are supported.

+
    +
  • Success: Displayed in a success theme color flying popup div
  • +
  • Danger: Displayed in a danger theme color flying popup div
  • +
  • Warning: Displayed in a warning theme color flying popup div
  • +
  • Information: Displayed in a info theme color flying popup div
  • +
  • Default: Displayed in a default theme color flying popup div
  • +
+

Table of contents

+ +
+

Installation

+

This module is based on the Instant Messaging Bus. To work properly, the server must be launched in gevent mode.

+
+
+

Usage

+

To send a notification to the user you just need to call one of the new methods defined on res.users:

+
+self.env.user.notify_success(message='My success message')
+
+

or

+
+self.env.user.notify_danger(message='My danger message')
+
+

or

+
+self.env.user.notify_warning(message='My warning message')
+
+

or

+
+self.env.user.notify_info(message='My information message')
+
+

or

+
+self.env.user.notify_default(message='My default message')
+
+

The notifications can bring interactivity with some buttons.

+
    +
  • One allowing to refresh the active view
  • +
  • Another allowing to send a window / client action
  • +
+

The reload button is activated when sending the notification with:

+

The action can be used using the action keyword:

+
+ action = self.env["ir.actions.act_window"]._for_xml_id('sale.action_orders')
+ action.update({
+     'res_id': self.id,
+     'views': [(False, 'form')],
+ })
+self.env.user.notify_info('My information message', action=action)
+
+
+Sample notifications +
+

You can test the behaviour of the notifications by installing this module in a demo database. +Access the users form through Settings -> Users & Companies. You’ll see a tab called “Test web notify”, here you’ll find two buttons that’ll allow you test the module.

+
+Sample notifications +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • ACSONE SA/NV
  • +
  • AdaptiveCity
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/web project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/web_notify/static/description/notifications_screenshot.gif b/web_notify/static/description/notifications_screenshot.gif new file mode 100644 index 000000000000..c82aa3e7e0df Binary files /dev/null and b/web_notify/static/description/notifications_screenshot.gif differ diff --git a/web_notify/static/description/test_notifications_demo.png b/web_notify/static/description/test_notifications_demo.png new file mode 100644 index 000000000000..9047fba0136b Binary files /dev/null and b/web_notify/static/description/test_notifications_demo.png differ diff --git a/web_notify/static/src/js/services/notification.esm.js b/web_notify/static/src/js/services/notification.esm.js new file mode 100644 index 000000000000..489faddd31e6 --- /dev/null +++ b/web_notify/static/src/js/services/notification.esm.js @@ -0,0 +1,12 @@ +/** @odoo-module */ +import {Notification} from "@web/core/notifications/notification"; +import {patch} from "web.utils"; + +patch(Notification.props, "webNotifyProps", { + type: { + type: String, + optional: true, + validate: (t) => + ["warning", "danger", "success", "info", "default"].includes(t), + }, +}); diff --git a/web_notify/static/src/js/services/notification_services.esm.js b/web_notify/static/src/js/services/notification_services.esm.js new file mode 100644 index 000000000000..259b1e4fc77c --- /dev/null +++ b/web_notify/static/src/js/services/notification_services.esm.js @@ -0,0 +1,59 @@ +/** @odoo-module **/ +import {Markup} from "web.utils"; +import {browser} from "@web/core/browser/browser"; +import {registry} from "@web/core/registry"; + +export const webNotificationService = { + dependencies: ["notification", "action"], + + start(env, {notification, action}) { + let webNotifTimeouts = {}; + /** + * Displays the web notification on user's screen + */ + function displaywebNotification(notifications) { + Object.values(webNotifTimeouts).forEach((notif) => + browser.clearTimeout(notif) + ); + webNotifTimeouts = {}; + notifications.forEach(function (notif) { + browser.setTimeout(function () { + let buttons = []; + + if (notif.action) { + buttons = [ + { + name: env._t("Open"), + primary: true, + onClick: async () => { + await action.doAction(notif.action); + }, + }, + ]; + } + notification.add(Markup(notif.message), { + title: notif.title, + type: notif.type, + sticky: notif.sticky, + className: notif.className, + messageIsHtml: notif.html, + buttons: buttons, + }); + }); + }); + } + env.bus.on("WEB_CLIENT_READY", null, async () => { + const legacyEnv = owl.Component.env; + legacyEnv.services.bus_service.onNotification(this, (notifications) => { + for (const {payload, type} of notifications) { + if (type === "web.notify") { + displaywebNotification(payload); + } + } + }); + legacyEnv.services.bus_service.startPolling(); + }); + }, +}; + +registry.category("services").add("webNotification", webNotificationService); diff --git a/web_notify/tests/__init__.py b/web_notify/tests/__init__.py new file mode 100644 index 000000000000..f759b968bf73 --- /dev/null +++ b/web_notify/tests/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import test_res_users diff --git a/web_notify/tests/test_res_users.py b/web_notify/tests/test_res_users.py new file mode 100644 index 000000000000..4d65973dab48 --- /dev/null +++ b/web_notify/tests/test_res_users.py @@ -0,0 +1,119 @@ +# Copyright 2016 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import json + +from odoo import exceptions +from odoo.tests import common + +from ..models.res_users import DANGER, DEFAULT, INFO, SUCCESS, WARNING + + +class TestResUsers(common.TransactionCase): + def test_notify_success(self): + bus_bus = self.env["bus.bus"] + domain = [("channel", "=", self.env.user.notify_success_channel_name)] + existing = bus_bus.search(domain) + test_msg = { + "message": "message", + "title": "title", + "sticky": True, + "html": False, + "action": None, + } + self.env.user.notify_success(**test_msg) + news = bus_bus.search(domain) - existing + self.assertEqual(1, len(news)) + test_msg.update({"type": SUCCESS}) + payload = json.loads(news.message)["payload"][0] + self.assertDictEqual(test_msg, payload) + + def test_notify_danger(self): + bus_bus = self.env["bus.bus"] + domain = [("channel", "=", self.env.user.notify_danger_channel_name)] + existing = bus_bus.search(domain) + test_msg = { + "message": "message", + "title": "title", + "sticky": True, + "html": False, + "action": None, + } + self.env.user.notify_danger(**test_msg) + news = bus_bus.search(domain) - existing + self.assertEqual(1, len(news)) + test_msg.update({"type": DANGER}) + payload = json.loads(news.message)["payload"][0] + self.assertDictEqual(test_msg, payload) + + def test_notify_warning(self): + bus_bus = self.env["bus.bus"] + domain = [("channel", "=", self.env.user.notify_warning_channel_name)] + existing = bus_bus.search(domain) + test_msg = { + "message": "message", + "title": "title", + "sticky": True, + "html": False, + "action": None, + } + self.env.user.notify_warning(**test_msg) + news = bus_bus.search(domain) - existing + self.assertEqual(1, len(news)) + test_msg.update({"type": WARNING}) + payload = json.loads(news.message)["payload"][0] + self.assertDictEqual(test_msg, payload) + + def test_notify_info(self): + bus_bus = self.env["bus.bus"] + domain = [("channel", "=", self.env.user.notify_info_channel_name)] + existing = bus_bus.search(domain) + test_msg = { + "message": "message", + "title": "title", + "sticky": True, + "html": False, + "action": None, + } + self.env.user.notify_info(**test_msg) + news = bus_bus.search(domain) - existing + self.assertEqual(1, len(news)) + test_msg.update({"type": INFO}) + payload = json.loads(news.message)["payload"][0] + self.assertDictEqual(test_msg, payload) + + def test_notify_default(self): + bus_bus = self.env["bus.bus"] + domain = [("channel", "=", self.env.user.notify_default_channel_name)] + existing = bus_bus.search(domain) + test_msg = { + "message": "message", + "title": "title", + "sticky": True, + "html": False, + "action": None, + } + self.env.user.notify_default(**test_msg) + news = bus_bus.search(domain) - existing + self.assertEqual(1, len(news)) + test_msg.update({"type": DEFAULT}) + payload = json.loads(news.message)["payload"][0] + self.assertDictEqual(test_msg, payload) + + def test_notify_many(self): + # check that the notification of a list of users is done with + # a single call to the bus + users = self.env.user.search([(1, "=", 1)]) + + self.assertTrue(len(users) > 1) + self.env.user.notify_warning(message="message", target=users.partner_id) + + def test_notify_other_user(self): + other_user = self.env.ref("base.user_demo") + other_user_model = self.env["res.users"].with_user(other_user) + with self.assertRaises(exceptions.UserError): + other_user_model.browse(self.env.uid).notify_info(message="hello") + + def test_notify_admin_allowed_other_user(self): + other_user = self.env.ref("base.user_demo") + other_user.notify_info(message="hello") diff --git a/web_notify/views/res_users_demo.xml b/web_notify/views/res_users_demo.xml new file mode 100644 index 000000000000..d8f286ef04bf --- /dev/null +++ b/web_notify/views/res_users_demo.xml @@ -0,0 +1,62 @@ + + + + + res.users.form.simple.view.modif.inherit + res.users + + + + + + + + + + + + + + + diff --git a/web_pivot_computed_measure/static/src/helpers/utils.esm.js b/web_pivot_computed_measure/static/src/helpers/utils.esm.js new file mode 100644 index 000000000000..df95c84bccfd --- /dev/null +++ b/web_pivot_computed_measure/static/src/helpers/utils.esm.js @@ -0,0 +1,124 @@ +/** @odoo-module **/ +/* Copyright 2023 Tecnativa - Carlos Roca + * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) */ + +/** + * Function that traverse the text given character by character + * + * @param {String} text + * @returns {Array} + */ +function getTokensFromText(text) { + const symbols = ["+", "-", "*", "/", "(", ")"]; + const tokens = []; + let token = ""; + for (let i = 0; i < text.length; i++) { + const c = text[i]; + if (c === " ") continue; + if (symbols.includes(c)) { + if (token !== "") { + tokens.push(token); + token = ""; + } + tokens.push(c); + } else { + token += c; + } + } + if (token !== "") { + tokens.push(token); + } + return tokens; +} + +/** + * Function that executes an operation between the last two operands in the operands stack + * and the last operator in the operators stack, and saves the result in the operands stack. + * + * @param {Array} operands + * @param {Array} operators + */ +function executeOperation(operands, operators) { + const b = operands.pop(); + const a = operands.pop(); + const op = operators.pop(); + switch (op) { + case "+": + operands.push(a + b); + break; + case "-": + operands.push(a - b); + break; + case "*": + operands.push(a * b); + break; + case "/": + operands.push(a / b); + break; + } +} + +/** + * Function that returns the precedence of an operator + * + * @param {String} op + * @returns {Number} + */ +function precedence(op) { + if (op === "+" || op === "-") { + return 1; + } + if (op === "*" || op === "/") { + return 2; + } + if (op === "(" || op === ")") { + return 0; + } +} + +/** + * Helper function that takes a mathematical expression in text form and an object + * of variable values, evaluates the expression, and returns the result. + * + * @param {String} text + * @param {Object} values + * @returns {any} + */ +export function evalOperation(text, values) { + const tokens = getTokensFromText(text); + const operands = []; + const operators = []; + for (const token of tokens) { + if (!isNaN(token)) { + // If the token is a number, convert it to a number and add it to the operands stack + operands.push(Number(token)); + } else if (token in values) { + // If the token is a variable, get its value from the object and add it to the operands stack + operands.push(values[token]); + } else if (token === "(") { + // If the token is an open parenthesis, add it to the operators stack + operators.push(token); + } else if (token === ")") { + // If the token is a closing parenthesis, pop and execute operators from the stack until an open parenthesis is found + while (operators.length > 0 && operators[operators.length - 1] !== "(") { + executeOperation(operands, operators); + } + // Pop the open parenthesis from the operators stack + operators.pop(); + } else { + // If the token is an operator, pop and execute operators from the stack while they have equal or higher precedence than the token + while ( + operators.length > 0 && + precedence(operators[operators.length - 1]) >= precedence(token) + ) { + executeOperation(operands, operators); + } + // Add the token to the operators stack + operators.push(token); + } + } + while (operators.length > 0) { + executeOperation(operands, operators); + } + return operands.pop(); +} diff --git a/web_pivot_computed_measure/static/src/pivot/pivot_model.esm.js b/web_pivot_computed_measure/static/src/pivot/pivot_model.esm.js new file mode 100644 index 000000000000..76f02bb69f4d --- /dev/null +++ b/web_pivot_computed_measure/static/src/pivot/pivot_model.esm.js @@ -0,0 +1,296 @@ +/** @odoo-module **/ +/* Copyright 2020 Tecnativa - Alexandre Díaz + * Copyright 2022 Tecnativa - Carlos Roca + * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) */ + +import {PivotModel} from "@web/views/pivot/pivot_model"; +import {patch} from "web.utils"; +import {computeReportMeasures} from "@web/views/helpers/utils"; +import {evalOperation} from "../helpers/utils.esm"; + +patch(PivotModel.prototype, "web_pivot_computed_measure.PivotModel", { + /** + * Add _computed_measures to avoid recompute them until page is recharged + * + * @override + */ + setup() { + this._super(...arguments); + this._computed_measures = []; + }, + + /** + * Create a new computed measure + * + * @param {String} id + * @param {String} field1 + * @param {String} field2 + * @param {String} operation + * @param {String} name + * @param {String} format + * @returns a promise + */ + addComputedMeasure(id, field1, field2, operation, name, format) { + const measure = _.find(this._computed_measures, (item) => { + return ( + item.field1 === field1 && + item.field2 === field2 && + item.operation === operation + ); + }); + if (measure) { + return Promise.resolve(); + } + const fieldM1 = this.metaData.fields[field1]; + const fieldM2 = this.metaData.fields[field2]; + const cmId = "__computed_" + id; + const oper = operation.replace(/m1/g, field1).replace(/m2/g, field2); + const oper_human = operation + .replace( + /m1/g, + fieldM1.__computed_id ? "(" + fieldM1.string + ")" : fieldM1.string + ) + .replace( + /m2/g, + fieldM2.__computed_id ? "(" + fieldM2.string + ")" : fieldM2.string + ); + const cmTotal = this._computed_measures.push({ + field1: field1, + field2: field2, + operation: oper, + name: name || oper_human, + id: cmId, + format: format, + }); + return this._createVirtualMeasure(this._computed_measures[cmTotal - 1]); + }, + + /** + * Create and enable a measure based on a 'fake' field + * + * @private + * @param {Object} cmDef + * @param {List} fields *Optional* + * @returns a promise + */ + _createVirtualMeasure(cmDef, fields) { + this._createVirtualField(cmDef, fields); + // Activate computed field + return this.toggleMeasure(cmDef.id); + }, + _createVirtualField(cmDef, fields, config) { + const arrFields = fields || this.metaData.fields; + // This is a minimal 'fake' field info + arrFields[cmDef.id] = { + // Used to format the value + type: cmDef.format, + // Used to print the header name + string: cmDef.name, + // Referenced on payload prop at DropdownItem, used to interact with + // created measures + name: cmDef.id, + // Used to know if is a computed measure field + __computed_id: cmDef.id, + // Operator used for group the measure added. + group_operator: "sum", + }; + const metaData = (config && config.metaData) || this.metaData; + metaData.measures[cmDef.id] = arrFields[cmDef.id]; + }, + /** + * Active the measures related to the 'fake' field + * + * @private + * @param {List of Strings} fields + */ + async _activeMeasures(fields) { + let needLoad = false; + for (const field of fields) { + if (await !this._isMeasureEnabled(field)) { + this.metaData.activeMeasures.push(field); + needLoad = true; + } + } + if (needLoad) { + const config = {metaData: this.metaData, data: this.data}; + + return this._loadData(config).then(() => { + // Notify changes to renderer for show it on the pivot view + this.notify(); + }); + } + return Promise.resolve(); + }, + + /** + * Check if the measure is enabled + * + * @private + * @param {String} field + */ + _isMeasureEnabled(field, config) { + const activeMeasures = + (config && config.metaData.activeMeasures) || + this.metaData.activeMeasures || + []; + return _.contains(activeMeasures, field); + }, + + /** + * Helper function to add computed measure fields data into a 'subGroupData' + * + * @private + * @param {Object} subGroupData + */ + _fillComputedMeasuresData(subGroupData, config) { + for (const cm of this._computed_measures) { + if (!this._isMeasureEnabled(cm.id, config)) continue; + if (subGroupData.__count === 0) { + subGroupData[cm.id] = false; + } else { + // eslint-disable-next-line no-undef + subGroupData[cm.id] = evalOperation(cm.operation, subGroupData); + } + } + }, + + /** + * Fill the groupSubdivisions with the computed measures and their values + * + * @override + */ + _prepareData(group, groupSubdivisions, config) { + for (const groupSubdivision of groupSubdivisions) { + for (const subGroup of groupSubdivision.subGroups) { + this._fillComputedMeasuresData(subGroup, config); + } + } + this._super(...arguments); + }, + + /** + * _getGroupSubdivision method invokes the read_group method of the + * model via rpc and the passed 'fields' argument is the list of + * measure names that is in this.metaData.activeMeasures, so we remove the + * computed measures form this.metaData.activeMeasures before calling _super + * to prevent any possible exception. + * + * @override + */ + _getGroupSubdivision(group, rowGroupBy, colGroupBy, config) { + const computed_measures = []; + for (let i = 0; i < config.metaData.activeMeasures.length; i++) + if (config.metaData.activeMeasures[i].startsWith("__computed_")) { + computed_measures.push(config.metaData.activeMeasures[i]); + config.metaData.activeMeasures.splice(i, 1); + i--; + } + const res = this._super(...arguments); + $.merge(config.metaData.activeMeasures, computed_measures); + return res; + }, + + /** + * Adds a rule to deny that measures can be disabled if are being used by a computed measure. + * In the other hand, when enables a measure analyzes it to active all involved measures. + * + * @override + */ + toggleMeasure(fieldName) { + if (this._isMeasureEnabled(fieldName)) { + // Mesaure is enabled + const umeasures = _.filter(this._computed_measures, (item) => { + return item.field1 === fieldName || item.field2 === fieldName; + }); + if (umeasures.length && this._isMeasureEnabled(umeasures[0].id)) { + return Promise.reject( + this.env._t( + "This measure is currently used by a 'computed measure'. Please, disable the computed measure first." + ) + ); + } + } else { + // Measure is disabled + const toEnable = []; + const toAnalyze = [fieldName]; + while (toAnalyze.length) { + // Analyze all items involved on computed measures to enable them + const afield = toAnalyze.shift(); + const fieldDef = this.metaData.fields[afield]; + // Need to check if fieldDef exists to avoid problems with __count + if (fieldDef && fieldDef.__computed_id) { + const cm = _.find(this._computed_measures, { + id: fieldDef.__computed_id, + }); + toAnalyze.push(cm.field1, cm.field2); + const toEnableFields = []; + if (!this.metaData.fields[cm.field1].__computed_id) { + toEnableFields.push(cm.field1); + } + if (!this.metaData.fields[cm.field2].__computed_id) { + toEnableFields.push(cm.field2); + } + toEnableFields.push(afield); + toEnable.push(toEnableFields); + } + } + if (toEnable.length) { + this._activeMeasures( + // Transform the array of arrays to a simple array. + // [1, [2, 3]] => [1, 2, 3] + _.flatten(toEnable.reverse()) + ); + } + } + return this._super(...arguments); + }, + /** + * Load the measures added to selected favorite filters + * + * @override + */ + async load(searchParams) { + var _super = this._super.bind(this); + var config = {metaData: this.metaData, data: this.data}; + if (!this.metaData.measures) { + const metaData = this._buildMetaData(); + metaData.measures = computeReportMeasures( + metaData.fields, + metaData.fieldAttrs, + metaData.activeMeasures, + metaData.additionalMeasures + ); + config = {metaData, data: this.data}; + } + if ("context" in searchParams) { + this._computed_measures = + searchParams.context.pivot_computed_measures || + searchParams.computed_measures || + []; + } + for (const cmDef of this._computed_measures) { + if (this._isMeasureEnabled(cmDef.id, config)) { + continue; + } + await this._createVirtualField(cmDef, undefined, config); + } + const fieldNames = Object.keys(this.metaData.fields); + for (const fieldName of fieldNames) { + const field = this.metaData.fields[fieldName]; + if (field.__computed_id) { + const cm = _.find(this._computed_measures, { + id: field.__computed_id, + }); + if (!cm) { + delete this.metaData.fields[fieldName]; + delete this.metaData.measures[fieldName]; + this.metaData.activeMeasures = _.without( + this.metaData.activeMeasures, + fieldName + ); + } + } + } + return _super(...arguments); + }, +}); diff --git a/web_pivot_computed_measure/static/src/pivot/pivot_renderer.esm.js b/web_pivot_computed_measure/static/src/pivot/pivot_renderer.esm.js new file mode 100644 index 000000000000..ab1fa80131da --- /dev/null +++ b/web_pivot_computed_measure/static/src/pivot/pivot_renderer.esm.js @@ -0,0 +1,21 @@ +/** @odoo-module **/ +/* Copyright 2022 Tecnativa - Carlos Roca + * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) */ + +import {PivotRenderer} from "@web/views/pivot/pivot_renderer"; +import {patch} from "web.utils"; + +patch(PivotRenderer.prototype, "web_pivot_computed_measure.PivotRenderer", { + getFormattedValue(cell) { + if (Math.abs(cell.value) === Infinity) { + return "-"; + } + return this._super(...arguments); + }, + getFormattedVariation(cell) { + if (Math.abs(cell.value) === Infinity) { + return "-"; + } + return this._super(...arguments); + }, +}); diff --git a/web_pivot_computed_measure/static/src/pivot/pivot_view.esm.js b/web_pivot_computed_measure/static/src/pivot/pivot_view.esm.js new file mode 100644 index 000000000000..5242dbe11135 --- /dev/null +++ b/web_pivot_computed_measure/static/src/pivot/pivot_view.esm.js @@ -0,0 +1,20 @@ +/** @odoo-module **/ +/* Copyright 2022 Tecnativa - Carlos Roca + * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) */ + +import {PivotView} from "@web/views/pivot/pivot_view"; +import {patch} from "web.utils"; + +patch(PivotView.prototype, "web_pivot_computed_measure.PivotView", { + /** + * Add computed_measures to context key to avoid loosing info when saving the + * filter to favorites. + * + * @override + */ + getContext() { + var res = this._super(...arguments); + res.pivot_computed_measures = this.model._computed_measures; + return res; + }, +}); diff --git a/web_pivot_computed_measure/static/src/pivot/pivot_view.xml b/web_pivot_computed_measure/static/src/pivot/pivot_view.xml new file mode 100644 index 000000000000..b8ae60aafcff --- /dev/null +++ b/web_pivot_computed_measure/static/src/pivot/pivot_view.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/web_pivot_computed_measure/static/src/test/test.esm.js b/web_pivot_computed_measure/static/src/test/test.esm.js new file mode 100644 index 000000000000..faa2824fccea --- /dev/null +++ b/web_pivot_computed_measure/static/src/test/test.esm.js @@ -0,0 +1,65 @@ +/** @odoo-module **/ +/* Copyright 2022 Tecnativa - Carlos Roca + * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) */ + +import tour from "web_tour.tour"; + +tour.register( + "web_pivot_computed_measure_tour", + { + url: "/web", + test: true, + }, + [ + tour.stepUtils.showAppsMenuItem(), + { + trigger: 'a[data-menu-xmlid="base.menu_administration"]', + }, + { + trigger: 'button[data-menu-xmlid="base.menu_users"]', + }, + { + trigger: 'a[data-menu-xmlid="base.menu_action_res_users"]', + }, + { + trigger: "button.o_pivot", + }, + { + trigger: 'button:contains(" Measures ")', + }, + { + trigger: 'a:contains(" Computed Measure ")', + }, + { + trigger: "select#computed_measure_field_1", + run: "text user_year_now", + }, + { + trigger: "select#computed_measure_field_2", + run: "text user_year_born", + }, + { + trigger: "select#computed_measure_operation", + run: "text m1-m2", + }, + { + trigger: "select#computed_measure_format", + run: "text integer", + }, + { + trigger: "button.o_add_computed_measure", + }, + { + trigger: 'th.o_pivot_measure_row:contains("User Year Now")', + extra_trigger: 'div.o_value:contains("2,022")', + }, + { + trigger: 'th.o_pivot_measure_row:contains("User Year Born")', + extra_trigger: 'div.o_value:contains("1,998")', + }, + { + trigger: 'th.o_pivot_measure_row:contains("User Year Now-User Year Born")', + extra_trigger: 'div.o_value:contains("24")', + }, + ] +); diff --git a/web_pivot_computed_measure/static/src/view.xml b/web_pivot_computed_measure/static/src/view.xml new file mode 100644 index 000000000000..f779b8943b93 --- /dev/null +++ b/web_pivot_computed_measure/static/src/view.xml @@ -0,0 +1,27 @@ + + + + + !measure.startsWith('__computed_') + + +