├── .copier-answers.yml ├── .editorconfig ├── .github └── workflows │ ├── pre-commit.yml │ ├── stale.yml │ └── test.yml ├── .gitignore ├── .oca └── oca-port │ └── blacklist │ └── queue_job.json ├── .pre-commit-config.yaml ├── .pylintrc ├── .pylintrc-mandatory ├── .ruff.toml ├── LICENSE ├── README.md ├── base_import_async ├── README.rst ├── __init__.py ├── __manifest__.py ├── data │ └── queue_job_function_data.xml ├── i18n │ ├── base_import_async.pot │ ├── es.po │ ├── it.po │ └── tr.po ├── models │ ├── __init__.py │ ├── base_import_import.py │ └── queue_job.py ├── pyproject.toml ├── readme │ ├── CONTRIBUTORS.md │ ├── CREDITS.md │ ├── DESCRIPTION.md │ ├── HISTORY.md │ ├── ROADMAP.md │ └── USAGE.md ├── static │ ├── description │ │ ├── icon.png │ │ └── index.html │ └── src │ │ ├── js │ │ └── import_model.esm.js │ │ └── xml │ │ └── import_data_sidepanel.xml └── tests │ ├── __init__.py │ └── test_base_import_import.py ├── checklog-odoo.cfg ├── eslint.config.cjs ├── prettier.config.cjs ├── queue_job ├── README.rst ├── __init__.py ├── __manifest__.py ├── controllers │ ├── __init__.py │ └── main.py ├── data │ ├── queue_data.xml │ └── queue_job_function_data.xml ├── delay.py ├── exception.py ├── fields.py ├── i18n │ ├── de.po │ ├── es.po │ ├── it.po │ ├── queue_job.pot │ ├── tr.po │ ├── tr_TR.po │ └── zh_CN.po ├── job.py ├── jobrunner │ ├── __init__.py │ ├── __main__.py │ ├── channels.py │ └── runner.py ├── migrations │ └── 18.0.1.0.0 │ │ └── pre-migrate.py ├── models │ ├── __init__.py │ ├── base.py │ ├── ir_model_fields.py │ ├── queue_job.py │ ├── queue_job_channel.py │ └── queue_job_function.py ├── post_init_hook.py ├── post_load.py ├── pyproject.toml ├── readme │ ├── CONFIGURE.md │ ├── CONTRIBUTORS.md │ ├── CREDITS.md │ ├── DESCRIPTION.md │ ├── HISTORY.md │ ├── INSTALL.md │ ├── ROADMAP.md │ └── USAGE.md ├── security │ ├── ir.model.access.csv │ └── security.xml ├── static │ ├── description │ │ ├── icon.png │ │ ├── icon.svg │ │ └── index.html │ ├── lib │ │ └── vis │ │ │ ├── vis-network.min.css │ │ │ └── vis-network.min.js │ └── src │ │ └── views │ │ └── fields │ │ └── job_direct_graph │ │ ├── job_direct_graph.esm.js │ │ ├── job_direct_graph.scss │ │ └── job_direct_graph.xml ├── tests │ ├── __init__.py │ ├── common.py │ ├── test_delayable.py │ ├── test_delayable_split.py │ ├── test_json_field.py │ ├── test_model_job_channel.py │ ├── test_model_job_function.py │ ├── test_queue_job_protected_write.py │ ├── test_runner_channels.py │ ├── test_runner_runner.py │ └── test_wizards.py ├── utils.py ├── views │ ├── queue_job_channel_views.xml │ ├── queue_job_function_views.xml │ ├── queue_job_menus.xml │ └── queue_job_views.xml └── wizards │ ├── __init__.py │ ├── queue_jobs_to_cancelled.py │ ├── queue_jobs_to_cancelled_views.xml │ ├── queue_jobs_to_done.py │ ├── queue_jobs_to_done_views.xml │ ├── queue_requeue_job.py │ └── queue_requeue_job_views.xml ├── queue_job_batch ├── README.rst ├── __init__.py ├── __manifest__.py ├── controllers │ ├── __init__.py │ └── webclient.py ├── data │ ├── queue_job_channel_data.xml │ └── queue_job_function_data.xml ├── i18n │ ├── es.po │ ├── fr.po │ ├── it.po │ └── queue_job_batch.pot ├── migrations │ └── 18.0.1.0.0 │ │ └── pre-migrate.py ├── models │ ├── __init__.py │ ├── queue_job.py │ ├── queue_job_batch.py │ └── res_users.py ├── pyproject.toml ├── readme │ ├── CONTRIBUTORS.md │ ├── CREDITS.md │ ├── DESCRIPTION.md │ └── USAGE.md ├── security │ ├── ir.model.access.csv │ └── security.xml ├── static │ ├── description │ │ ├── icon.png │ │ ├── icon.svg │ │ └── index.html │ └── src │ │ ├── MailCoreWeb.esm.js │ │ ├── QueueJobBatchModel.esm.js │ │ ├── Store.esm.js │ │ └── components │ │ ├── QueueJobBatchMenu.esm.js │ │ ├── QueueJobBatchMenu.scss │ │ └── QueueJobBatchMenu.xml └── views │ ├── queue_job_batch_views.xml │ └── queue_job_views.xml ├── queue_job_cron ├── README.rst ├── __init__.py ├── __manifest__.py ├── data │ └── data.xml ├── i18n │ ├── de.po │ ├── es.po │ ├── it.po │ ├── queue_job_cron.pot │ └── zh_CN.po ├── models │ ├── __init__.py │ └── ir_cron.py ├── pyproject.toml ├── readme │ ├── CONFIGURATION.md │ ├── CONTRIBUTORS.md │ ├── CREDITS.md │ ├── DESCRIPTION.md │ ├── HISTORY.md │ ├── INSTALL.md │ ├── USAGE.md │ └── newsfragments │ │ └── .gitignore ├── static │ └── description │ │ ├── icon.png │ │ └── index.html ├── tests │ ├── __init__.py │ └── test_queue_job_cron.py └── views │ └── ir_cron_view.xml ├── queue_job_cron_jobrunner ├── README.rst ├── __init__.py ├── __manifest__.py ├── data │ └── ir_cron.xml ├── i18n │ ├── it.po │ ├── queue_job_cron_jobrunner.pot │ ├── ro.po │ └── zh_CN.po ├── models │ ├── __init__.py │ ├── ir_cron.py │ └── queue_job.py ├── pyproject.toml ├── readme │ ├── CONFIGURE.md │ ├── CONTRIBUTORS.md │ ├── DESCRIPTION.md │ └── ROADMAP.md ├── static │ └── description │ │ ├── icon.png │ │ └── index.html ├── tests │ ├── __init__.py │ └── test_queue_job.py └── views │ └── ir_cron.xml ├── queue_job_subscribe ├── README.rst ├── __init__.py ├── __manifest__.py ├── i18n │ ├── ar.po │ ├── bg.po │ ├── ca.po │ ├── da.po │ ├── de.po │ ├── el_GR.po │ ├── es.po │ ├── es_ES.po │ ├── fi.po │ ├── fr.po │ ├── fr_CH.po │ ├── hr.po │ ├── hr_HR.po │ ├── hu.po │ ├── it.po │ ├── nl.po │ ├── pt_BR.po │ ├── queue_job_subscribe.pot │ ├── sl.po │ ├── tr.po │ ├── tr_TR.po │ └── zh_CN.po ├── models │ ├── __init__.py │ ├── queue_job.py │ └── res_users.py ├── pyproject.toml ├── readme │ ├── CONTRIBUTORS.md │ ├── CREDITS.md │ ├── DESCRIPTION.md │ └── USAGE.md ├── static │ └── description │ │ ├── icon.png │ │ └── index.html ├── tests │ ├── __init__.py │ └── test_job_subscribe.py └── views │ └── res_users_view.xml ├── requirements.txt ├── setup └── _metapackage │ └── pyproject.toml ├── test_queue_job ├── README.rst ├── __init__.py ├── __manifest__.py ├── data │ ├── queue_job_channel_data.xml │ └── queue_job_function_data.xml ├── i18n │ ├── de.po │ ├── es.po │ ├── it.po │ ├── test_queue_job.pot │ └── zh_CN.po ├── models │ ├── __init__.py │ └── test_models.py ├── pyproject.toml ├── security │ └── ir.model.access.csv ├── static │ └── description │ │ └── icon.png └── tests │ ├── __init__.py │ ├── common.py │ ├── test_autovacuum.py │ ├── test_delay_mocks.py │ ├── test_delayable.py │ ├── test_dependencies.py │ ├── test_job.py │ ├── test_job_auto_delay.py │ ├── test_job_channels.py │ ├── test_job_function.py │ ├── test_json_field.py │ └── test_related_actions.py └── test_queue_job_batch ├── README.rst ├── __init__.py ├── __manifest__.py ├── i18n ├── fr.po ├── it.po └── test_queue_job_batch.pot ├── pyproject.toml ├── readme ├── CONTRIBUTORS.md ├── CREDITS.md └── DESCRIPTION.md ├── static └── description │ ├── icon.png │ ├── icon.svg │ └── index.html └── tests ├── __init__.py └── test_queue_job_batch.py /.copier-answers.yml: -------------------------------------------------------------------------------- 1 | # Do NOT update manually; changes here will be overwritten by Copier 2 | _commit: v1.29 3 | _src_path: git+https://github.com/OCA/oca-addons-repo-template 4 | additional_ruff_rules: [] 5 | ci: GitHub 6 | convert_readme_fragments_to_markdown: true 7 | enable_checklog_odoo: true 8 | generate_requirements_txt: true 9 | github_check_license: true 10 | github_ci_extra_env: {} 11 | github_enable_codecov: true 12 | github_enable_makepot: true 13 | github_enable_stale_action: true 14 | github_enforce_dev_status_compatibility: true 15 | include_wkhtmltopdf: false 16 | odoo_test_flavor: Both 17 | odoo_version: 18.0 18 | org_name: Odoo Community Association (OCA) 19 | org_slug: OCA 20 | rebel_module_groups: [] 21 | repo_description: queue 22 | repo_name: queue 23 | repo_slug: queue 24 | repo_website: https://github.com/OCA/queue 25 | use_pyproject_toml: true 26 | use_ruff: true 27 | 28 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Configuration for known file extensions 2 | [*.{css,js,json,less,md,py,rst,sass,scss,xml,yaml,yml}] 3 | charset = utf-8 4 | end_of_line = lf 5 | indent_size = 4 6 | indent_style = space 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | [*.{json,yml,yaml,rst,md}] 11 | indent_size = 2 12 | 13 | # Do not configure editor for libs and autogenerated content 14 | [{*/static/{lib,src/lib}/**,*/static/description/index.html,*/readme/../README.rst}] 15 | charset = unset 16 | end_of_line = unset 17 | indent_size = unset 18 | indent_style = unset 19 | insert_final_newline = false 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /.github/workflows/pre-commit.yml: -------------------------------------------------------------------------------- 1 | name: pre-commit 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - "18.0*" 7 | push: 8 | branches: 9 | - "18.0" 10 | - "18.0-ocabot-*" 11 | 12 | jobs: 13 | pre-commit: 14 | runs-on: ubuntu-22.04 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: actions/setup-python@v5 18 | with: 19 | python-version: "3.11" 20 | - name: Get python version 21 | run: echo "PY=$(python -VV | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV 22 | - uses: actions/cache@v4 23 | with: 24 | path: ~/.cache/pre-commit 25 | key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }} 26 | - name: Install pre-commit 27 | run: pip install pre-commit 28 | - name: Run pre-commit 29 | run: pre-commit run --all-files --show-diff-on-failure --color=always 30 | env: 31 | # Consider valid a PR that changes README fragments but doesn't 32 | # change the README.rst file itself. It's not really a problem 33 | # because the bot will update it anyway after merge. This way, we 34 | # lower the barrier for functional contributors that want to fix the 35 | # readme fragments, while still letting developers get README 36 | # auto-generated (which also helps functionals when using runboat). 37 | # DOCS https://pre-commit.com/#temporarily-disabling-hooks 38 | SKIP: oca-gen-addon-readme 39 | - name: Check that all files generated by pre-commit are in git 40 | run: | 41 | newfiles="$(git ls-files --others --exclude-from=.gitignore)" 42 | if [ "$newfiles" != "" ] ; then 43 | echo "Please check-in the following files:" 44 | echo "$newfiles" 45 | exit 1 46 | fi 47 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Mark stale issues and pull requests 2 | 3 | on: 4 | schedule: 5 | - cron: "0 12 * * 0" 6 | 7 | jobs: 8 | stale: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Stale PRs and issues policy 12 | uses: actions/stale@v9 13 | with: 14 | repo-token: ${{ secrets.GITHUB_TOKEN }} 15 | # General settings. 16 | ascending: true 17 | remove-stale-when-updated: true 18 | # Pull Requests settings. 19 | # 120+30 day stale policy for PRs 20 | # * Except PRs marked as "no stale" 21 | days-before-pr-stale: 120 22 | days-before-pr-close: 30 23 | exempt-pr-labels: "no stale" 24 | stale-pr-label: "stale" 25 | stale-pr-message: > 26 | There hasn't been any activity on this pull request in the past 4 months, so 27 | it has been marked as stale and it will be closed automatically if no 28 | further activity occurs in the next 30 days. 29 | 30 | If you want this PR to never become stale, please ask a PSC member to apply 31 | the "no stale" label. 32 | # Issues settings. 33 | # 180+30 day stale policy for open issues 34 | # * Except Issues marked as "no stale" 35 | days-before-issue-stale: 180 36 | days-before-issue-close: 30 37 | exempt-issue-labels: "no stale,needs more information" 38 | stale-issue-label: "stale" 39 | stale-issue-message: > 40 | There hasn't been any activity on this issue in the past 6 months, so it has 41 | been marked as stale and it will be closed automatically if no further 42 | activity occurs in the next 30 days. 43 | 44 | If you want this issue to never become stale, please ask a PSC member to 45 | apply the "no stale" label. 46 | 47 | # 15+30 day stale policy for issues pending more information 48 | # * Issues that are pending more information 49 | # * Except Issues marked as "no stale" 50 | - name: Needs more information stale issues policy 51 | uses: actions/stale@v9 52 | with: 53 | repo-token: ${{ secrets.GITHUB_TOKEN }} 54 | ascending: true 55 | only-labels: "needs more information" 56 | exempt-issue-labels: "no stale" 57 | days-before-stale: 15 58 | days-before-close: 30 59 | days-before-pr-stale: -1 60 | days-before-pr-close: -1 61 | remove-stale-when-updated: true 62 | stale-issue-label: "stale" 63 | stale-issue-message: > 64 | This issue needs more information and there hasn't been any activity 65 | recently, so it has been marked as stale and it will be closed automatically 66 | if no further activity occurs in the next 30 days. 67 | 68 | If you think this is a mistake, please ask a PSC member to remove the "needs 69 | more information" label. 70 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - "18.0*" 7 | push: 8 | branches: 9 | - "18.0" 10 | - "18.0-ocabot-*" 11 | 12 | jobs: 13 | unreleased-deps: 14 | runs-on: ubuntu-latest 15 | name: Detect unreleased dependencies 16 | steps: 17 | - uses: actions/checkout@v4 18 | - run: | 19 | for reqfile in requirements.txt test-requirements.txt ; do 20 | if [ -f ${reqfile} ] ; then 21 | result=0 22 | # reject non-comment lines that contain a / (i.e. URLs, relative paths) 23 | grep "^[^#].*/" ${reqfile} || result=$? 24 | if [ $result -eq 0 ] ; then 25 | echo "Unreleased dependencies found in ${reqfile}." 26 | exit 1 27 | fi 28 | fi 29 | done 30 | test: 31 | runs-on: ubuntu-22.04 32 | container: ${{ matrix.container }} 33 | name: ${{ matrix.name }} 34 | strategy: 35 | fail-fast: false 36 | matrix: 37 | include: 38 | - container: ghcr.io/oca/oca-ci/py3.10-odoo18.0:latest 39 | name: test with Odoo 40 | - container: ghcr.io/oca/oca-ci/py3.10-ocb18.0:latest 41 | name: test with OCB 42 | makepot: "true" 43 | services: 44 | postgres: 45 | image: postgres:12.0 46 | env: 47 | POSTGRES_USER: odoo 48 | POSTGRES_PASSWORD: odoo 49 | POSTGRES_DB: odoo 50 | ports: 51 | - 5432:5432 52 | env: 53 | OCA_ENABLE_CHECKLOG_ODOO: "1" 54 | steps: 55 | - uses: actions/checkout@v4 56 | with: 57 | persist-credentials: false 58 | - name: Install addons and dependencies 59 | run: oca_install_addons 60 | - name: Check licenses 61 | run: manifestoo -d . check-licenses 62 | - name: Check development status 63 | run: manifestoo -d . check-dev-status --default-dev-status=Beta 64 | - name: Initialize test db 65 | run: oca_init_test_database 66 | - name: Run tests 67 | run: oca_run_tests 68 | - uses: codecov/codecov-action@v4 69 | with: 70 | token: ${{ secrets.CODECOV_TOKEN }} 71 | - name: Update .pot files 72 | run: oca_export_and_push_pot https://x-access-token:${{ secrets.GIT_PUSH_TOKEN }}@github.com/${{ github.repository }} 73 | if: ${{ matrix.makepot == 'true' && github.event_name == 'push' && github.repository_owner == 'OCA' }} 74 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | /.venv 5 | /.pytest_cache 6 | /.ruff_cache 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | bin/ 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | eggs/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | *.eggs 27 | 28 | # Windows installers 29 | *.msi 30 | 31 | # Debian packages 32 | *.deb 33 | 34 | # Redhat packages 35 | *.rpm 36 | 37 | # MacOS packages 38 | *.dmg 39 | *.pkg 40 | 41 | # Installer logs 42 | pip-log.txt 43 | pip-delete-this-directory.txt 44 | 45 | # Unit test / coverage reports 46 | htmlcov/ 47 | .tox/ 48 | .coverage 49 | .cache 50 | nosetests.xml 51 | coverage.xml 52 | 53 | # Translations 54 | *.mo 55 | 56 | # Pycharm 57 | .idea 58 | 59 | # Eclipse 60 | .settings 61 | 62 | # Visual Studio cache/options directory 63 | .vs/ 64 | .vscode 65 | 66 | # OSX Files 67 | .DS_Store 68 | 69 | # Django stuff: 70 | *.log 71 | 72 | # Mr Developer 73 | .mr.developer.cfg 74 | .project 75 | .pydevproject 76 | 77 | # Rope 78 | .ropeproject 79 | 80 | # Sphinx documentation 81 | docs/_build/ 82 | 83 | # Backup files 84 | *~ 85 | *.swp 86 | 87 | # OCA rules 88 | !static/lib/ 89 | -------------------------------------------------------------------------------- /.oca/oca-port/blacklist/queue_job.json: -------------------------------------------------------------------------------- 1 | { 2 | "pull_requests": { 3 | "OCA/queue#440": "The changes from this PR are already present in the current source code.", 4 | "OCA/queue#511": "The changes from this PR are already present in the current source code.", 5 | "OCA/queue#537": "The changes from this PR are already present in the current source code.", 6 | "OCA/queue#728": "The changes from this PR are already present in the current source code." 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.pylintrc-mandatory: -------------------------------------------------------------------------------- 1 | 2 | [MASTER] 3 | load-plugins=pylint_odoo 4 | score=n 5 | 6 | [ODOOLINT] 7 | readme-template-url="https://github.com/OCA/maintainer-tools/blob/master/template/module/README.rst" 8 | manifest-required-authors=Odoo Community Association (OCA) 9 | manifest-required-keys=license 10 | manifest-deprecated-keys=description,active 11 | license-allowed=AGPL-3,GPL-2,GPL-2 or any later version,GPL-3,GPL-3 or any later version,LGPL-3 12 | valid-odoo-versions=18.0 13 | 14 | [MESSAGES CONTROL] 15 | disable=all 16 | 17 | enable=anomalous-backslash-in-string, 18 | api-one-deprecated, 19 | api-one-multi-together, 20 | assignment-from-none, 21 | attribute-deprecated, 22 | class-camelcase, 23 | dangerous-default-value, 24 | dangerous-view-replace-wo-priority, 25 | development-status-allowed, 26 | duplicate-id-csv, 27 | duplicate-key, 28 | duplicate-xml-fields, 29 | duplicate-xml-record-id, 30 | eval-referenced, 31 | eval-used, 32 | incoherent-interpreter-exec-perm, 33 | license-allowed, 34 | manifest-author-string, 35 | manifest-deprecated-key, 36 | manifest-required-author, 37 | manifest-required-key, 38 | manifest-version-format, 39 | method-compute, 40 | method-inverse, 41 | method-required-super, 42 | method-search, 43 | openerp-exception-warning, 44 | pointless-statement, 45 | pointless-string-statement, 46 | print-used, 47 | redundant-keyword-arg, 48 | redundant-modulename-xml, 49 | reimported, 50 | relative-import, 51 | return-in-init, 52 | rst-syntax-error, 53 | sql-injection, 54 | too-few-format-args, 55 | translation-field, 56 | translation-required, 57 | unreachable, 58 | use-vim-comment, 59 | wrong-tabs-instead-of-spaces, 60 | xml-syntax-error, 61 | attribute-string-redundant, 62 | character-not-valid-in-resource-link, 63 | consider-merging-classes-inherited, 64 | context-overridden, 65 | create-user-wo-reset-password, 66 | dangerous-filter-wo-user, 67 | dangerous-qweb-replace-wo-priority, 68 | deprecated-data-xml-node, 69 | deprecated-openerp-xml-node, 70 | duplicate-po-message-definition, 71 | except-pass, 72 | file-not-used, 73 | invalid-commit, 74 | manifest-maintainers-list, 75 | missing-newline-extrafiles, 76 | missing-readme, 77 | missing-return, 78 | odoo-addons-relative-import, 79 | old-api7-method-defined, 80 | po-msgstr-variables, 81 | po-syntax-error, 82 | renamed-field-parameter, 83 | resource-not-exist, 84 | str-format-used, 85 | test-folder-imported, 86 | translation-contains-variable, 87 | translation-positional-used, 88 | unnecessary-utf8-coding-comment, 89 | website-manifest-key-not-valid-uri, 90 | xml-attribute-translatable, 91 | xml-deprecated-qweb-directive, 92 | xml-deprecated-tree-attribute, 93 | external-request-timeout 94 | 95 | [REPORTS] 96 | msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg} 97 | output-format=colorized 98 | reports=no 99 | -------------------------------------------------------------------------------- /.ruff.toml: -------------------------------------------------------------------------------- 1 | 2 | target-version = "py310" 3 | fix = true 4 | 5 | [lint] 6 | extend-select = [ 7 | "B", 8 | "C90", 9 | "E501", # line too long (default 88) 10 | "I", # isort 11 | "UP", # pyupgrade 12 | ] 13 | extend-safe-fixes = ["UP008"] 14 | exclude = ["setup/*"] 15 | 16 | [format] 17 | exclude = ["setup/*"] 18 | 19 | [lint.per-file-ignores] 20 | "__init__.py" = ["F401", "I001"] # ignore unused and unsorted imports in __init__.py 21 | "__manifest__.py" = ["B018"] # useless expression 22 | 23 | [lint.isort] 24 | section-order = ["future", "standard-library", "third-party", "odoo", "odoo-addons", "first-party", "local-folder"] 25 | 26 | [lint.isort.sections] 27 | "odoo" = ["odoo"] 28 | "odoo-addons" = ["odoo.addons"] 29 | 30 | [lint.mccabe] 31 | max-complexity = 16 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [](https://runboat.odoo-community.org/builds?repo=OCA/queue&target_branch=18.0) 3 | [](https://github.com/OCA/queue/actions/workflows/pre-commit.yml?query=branch%3A18.0) 4 | [](https://github.com/OCA/queue/actions/workflows/test.yml?query=branch%3A18.0) 5 | [](https://codecov.io/gh/OCA/queue) 6 | [](https://translation.odoo-community.org/engage/queue-18-0/?utm_source=widget) 7 | 8 | 9 | 10 | # queue 11 | 12 | queue 13 | 14 | 15 | 16 | 17 | 18 | [//]: # (addons) 19 | 20 | Available addons 21 | ---------------- 22 | addon | version | maintainers | summary 23 | --- | --- | --- | --- 24 | [base_import_async](base_import_async/) | 18.0.1.0.0 | | Import CSV files in the background 25 | [queue_job](queue_job/) | 18.0.1.5.0 | | Job Queue 26 | [queue_job_batch](queue_job_batch/) | 18.0.1.0.0 | | Job Queue Batch 27 | [queue_job_cron](queue_job_cron/) | 18.0.1.1.1 | | Scheduled Actions as Queue Jobs 28 | [queue_job_cron_jobrunner](queue_job_cron_jobrunner/) | 18.0.1.0.0 | | Run jobs without a dedicated JobRunner 29 | [queue_job_subscribe](queue_job_subscribe/) | 18.0.1.0.0 | | Control which users are subscribed to queue job notifications 30 | [test_queue_job](test_queue_job/) | 18.0.1.0.1 | | Queue Job Tests 31 | [test_queue_job_batch](test_queue_job_batch/) | 18.0.1.0.0 | | Test Job Queue Batch 32 | 33 | [//]: # (end addons) 34 | 35 | 36 | 37 | ## Licenses 38 | 39 | This repository is licensed under [AGPL-3.0](LICENSE). 40 | 41 | However, each module can have a totally different license, as long as they adhere to Odoo Community Association (OCA) 42 | policy. Consult each module's `__manifest__.py` file, which contains a `license` key 43 | that explains its license. 44 | 45 | ---- 46 | OCA, or the [Odoo Community Association](http://odoo-community.org/), is a nonprofit 47 | organization whose mission is to support the collaborative development of Odoo features 48 | and promote its widespread use. 49 | -------------------------------------------------------------------------------- /base_import_async/__init__.py: -------------------------------------------------------------------------------- 1 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). 2 | 3 | from . import models 4 | -------------------------------------------------------------------------------- /base_import_async/__manifest__.py: -------------------------------------------------------------------------------- 1 | # @author Stéphane Bidoul 2 | # @author Sébastien BEAU 3 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). 4 | 5 | { 6 | "name": "Asynchronous Import", 7 | "summary": "Import CSV files in the background", 8 | "version": "18.0.1.0.0", 9 | "author": "Akretion, ACSONE SA/NV, Odoo Community Association (OCA)", 10 | "license": "AGPL-3", 11 | "website": "https://github.com/OCA/queue", 12 | "category": "Generic Modules", 13 | "depends": ["base_import", "queue_job"], 14 | "data": [ 15 | "data/queue_job_function_data.xml", 16 | ], 17 | "assets": { 18 | "web.assets_backend": [ 19 | "base_import_async/static/src/js/import_model.esm.js", 20 | "base_import_async/static/src/xml/import_data_sidepanel.xml", 21 | ], 22 | }, 23 | "installable": True, 24 | "development_status": "Production/Stable", 25 | } 26 | -------------------------------------------------------------------------------- /base_import_async/data/queue_job_function_data.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | _split_file 5 | 9 | 10 | 14 | 15 | _import_one_chunk 16 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /base_import_async/i18n/base_import_async.pot: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * base_import_async 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 18.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "Last-Translator: \n" 10 | "Language-Team: \n" 11 | "MIME-Version: 1.0\n" 12 | "Content-Type: text/plain; charset=UTF-8\n" 13 | "Content-Transfer-Encoding: \n" 14 | "Plural-Forms: \n" 15 | 16 | #. module: base_import_async 17 | #. odoo-python 18 | #: code:addons/base_import_async/models/queue_job.py:0 19 | msgid "Attachment" 20 | msgstr "" 21 | 22 | #. module: base_import_async 23 | #: model:ir.model,name:base_import_async.model_base_import_import 24 | msgid "Base Import" 25 | msgstr "" 26 | 27 | #. module: base_import_async 28 | #. odoo-python 29 | #: code:addons/base_import_async/models/base_import_import.py:0 30 | msgid "" 31 | "Import %(model)s from file %(file_name)s - #%(chunk)s - lines %(from)s to " 32 | "%(to)s" 33 | msgstr "" 34 | 35 | #. module: base_import_async 36 | #. odoo-python 37 | #: code:addons/base_import_async/models/base_import_import.py:0 38 | msgid "Import %(model)s from file %(from_file)s" 39 | msgstr "" 40 | 41 | #. module: base_import_async 42 | #. odoo-javascript 43 | #: code:addons/base_import_async/static/src/xml/import_data_sidepanel.xml:0 44 | msgid "Import in the background" 45 | msgstr "" 46 | 47 | #. module: base_import_async 48 | #: model:ir.model,name:base_import_async.model_queue_job 49 | msgid "Queue Job" 50 | msgstr "" 51 | 52 | #. module: base_import_async 53 | #. odoo-javascript 54 | #: code:addons/base_import_async/static/src/xml/import_data_sidepanel.xml:0 55 | msgid "" 56 | "When checked, the import will be executed as a background job, after " 57 | "splitting your file in small chunks that will be processed independently. " 58 | "Use this to import very large files." 59 | msgstr "" 60 | 61 | #. module: base_import_async 62 | #. odoo-javascript 63 | #: code:addons/base_import_async/static/src/js/import_model.esm.js:0 64 | msgid "You can check the status of this job in menu 'Queue / Jobs'." 65 | msgstr "" 66 | 67 | #. module: base_import_async 68 | #. odoo-javascript 69 | #: code:addons/base_import_async/static/src/js/import_model.esm.js:0 70 | msgid "Your request is being processed" 71 | msgstr "" 72 | -------------------------------------------------------------------------------- /base_import_async/i18n/es.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * base_import_async 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 16.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "PO-Revision-Date: 2023-09-25 09:34+0000\n" 10 | "Last-Translator: Asier Neira \n" 11 | "Language-Team: none\n" 12 | "Language: es\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: \n" 16 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 17 | "X-Generator: Weblate 4.17\n" 18 | 19 | #. module: base_import_async 20 | #. odoo-python 21 | #: code:addons/base_import_async/models/queue_job.py:0 22 | #, python-format 23 | msgid "Attachment" 24 | msgstr "Archivo adjunto" 25 | 26 | #. module: base_import_async 27 | #: model:ir.model,name:base_import_async.model_base_import_import 28 | msgid "Base Import" 29 | msgstr "Importación Base" 30 | 31 | #. module: base_import_async 32 | #. odoo-python 33 | #: code:addons/base_import_async/models/base_import_import.py:0 34 | #, python-format 35 | msgid "" 36 | "Import %(model)s from file %(file_name)s - #%(chunk)s - lines %(from)s to " 37 | "%(to)s" 38 | msgstr "" 39 | "Importar %(model)s del archivo %(file_name)s - #%(chunk)s - líneas %(from)s " 40 | "a %(to)s" 41 | 42 | #. module: base_import_async 43 | #. odoo-python 44 | #: code:addons/base_import_async/models/base_import_import.py:0 45 | #, python-format 46 | msgid "Import %(model)s from file %(from_file)s" 47 | msgstr "Importar %(model)s del fichero %(from_file)s" 48 | 49 | #. module: base_import_async 50 | #. odoo-javascript 51 | #: code:addons/base_import_async/static/src/xml/import.xml:0 52 | #, python-format 53 | msgid "Import in the background" 54 | msgstr "Importación en segundo plano" 55 | 56 | #. module: base_import_async 57 | #: model:ir.model,name:base_import_async.model_queue_job 58 | msgid "Queue Job" 59 | msgstr "Cola de trabajo" 60 | 61 | #. module: base_import_async 62 | #. odoo-javascript 63 | #: code:addons/base_import_async/static/src/xml/import.xml:0 64 | #, python-format 65 | msgid "" 66 | "When checked, the import will be executed as a background job, after " 67 | "splitting your file in small chunks that will be processed independently. " 68 | "Use this to import very large files." 69 | msgstr "" 70 | "Si esta opción está seleccionada, la importación se ejecutará como un " 71 | "trabajo en segundo plano, tras dividir el archivo en pequeños trozos que se " 72 | "procesarán de forma independiente. Utilícelo para importar archivos muy " 73 | "grandes." 74 | 75 | #. module: base_import_async 76 | #. odoo-javascript 77 | #: code:addons/base_import_async/static/src/js/import.js:0 78 | #, python-format 79 | msgid "You can check the status of this job in menu 'Queue / Jobs'." 80 | msgstr "" 81 | "Puede comprobar el estado de este trabajo en el menú 'Cola / Trabajos'." 82 | 83 | #. module: base_import_async 84 | #. odoo-javascript 85 | #: code:addons/base_import_async/static/src/js/import.js:0 86 | #, python-format 87 | msgid "Your request is being processed" 88 | msgstr "Su solicitud está siendo procesada" 89 | -------------------------------------------------------------------------------- /base_import_async/i18n/it.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * base_import_async 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 16.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "PO-Revision-Date: 2024-01-11 09:34+0000\n" 10 | "Last-Translator: mymage \n" 11 | "Language-Team: none\n" 12 | "Language: it\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: \n" 16 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 17 | "X-Generator: Weblate 4.17\n" 18 | 19 | #. module: base_import_async 20 | #. odoo-python 21 | #: code:addons/base_import_async/models/queue_job.py:0 22 | #, python-format 23 | msgid "Attachment" 24 | msgstr "Allegato" 25 | 26 | #. module: base_import_async 27 | #: model:ir.model,name:base_import_async.model_base_import_import 28 | msgid "Base Import" 29 | msgstr "Importazione base" 30 | 31 | #. module: base_import_async 32 | #. odoo-python 33 | #: code:addons/base_import_async/models/base_import_import.py:0 34 | #, python-format 35 | msgid "" 36 | "Import %(model)s from file %(file_name)s - #%(chunk)s - lines %(from)s to " 37 | "%(to)s" 38 | msgstr "" 39 | "Importa %(model)s dal file %(file_name)s - #%(chunk)s - linee %(from)s a " 40 | "%(to)s" 41 | 42 | #. module: base_import_async 43 | #. odoo-python 44 | #: code:addons/base_import_async/models/base_import_import.py:0 45 | #, python-format 46 | msgid "Import %(model)s from file %(from_file)s" 47 | msgstr "Importa %(model)s dal file %(from_file)s" 48 | 49 | #. module: base_import_async 50 | #. odoo-javascript 51 | #: code:addons/base_import_async/static/src/xml/import.xml:0 52 | #, python-format 53 | msgid "Import in the background" 54 | msgstr "Importa in background" 55 | 56 | #. module: base_import_async 57 | #: model:ir.model,name:base_import_async.model_queue_job 58 | msgid "Queue Job" 59 | msgstr "Lavoro in coda" 60 | 61 | #. module: base_import_async 62 | #. odoo-javascript 63 | #: code:addons/base_import_async/static/src/xml/import.xml:0 64 | #, python-format 65 | msgid "" 66 | "When checked, the import will be executed as a background job, after " 67 | "splitting your file in small chunks that will be processed independently. " 68 | "Use this to import very large files." 69 | msgstr "" 70 | "Quando selezionata, l'importazione verrà eseguita com un lavoro in " 71 | "backgroud, dopo la divisine del file in piccole parti che verranno " 72 | "processate indipendentemente. Utilizzare questa opzione per importare file " 73 | "di grandi dimensioni." 74 | 75 | #. module: base_import_async 76 | #. odoo-javascript 77 | #: code:addons/base_import_async/static/src/js/import.js:0 78 | #, python-format 79 | msgid "You can check the status of this job in menu 'Queue / Jobs'." 80 | msgstr "Si può controllare lo stato di questo lavoro nel menu 'Coda / Lavori'." 81 | 82 | #. module: base_import_async 83 | #. odoo-javascript 84 | #: code:addons/base_import_async/static/src/js/import.js:0 85 | #, python-format 86 | msgid "Your request is being processed" 87 | msgstr "La richiesta è in lavorazione" 88 | -------------------------------------------------------------------------------- /base_import_async/i18n/tr.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * base_import_async 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 15.0-20221029\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "POT-Creation-Date: 2022-11-24 07:21+0000\n" 10 | "PO-Revision-Date: 2022-11-24 10:25+0300\n" 11 | "Last-Translator: \n" 12 | "Language-Team: \n" 13 | "Language: tr_TR\n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 18 | "X-Generator: Poedit 2.4.2\n" 19 | 20 | #. module: base_import_async 21 | #. odoo-python 22 | #: code:addons/base_import_async/models/queue_job.py:0 23 | #, python-format 24 | msgid "Attachment" 25 | msgstr "Ek" 26 | 27 | #. module: base_import_async 28 | #: model:ir.model,name:base_import_async.model_base_import_import 29 | msgid "Base Import" 30 | msgstr "İçe aktarım" 31 | 32 | #. module: base_import_async 33 | #. odoo-python 34 | #: code:addons/base_import_async/models/base_import_import.py:0 35 | #, python-format 36 | msgid "" 37 | "Import %(model)s from file %(file_name)s - #%(chunk)s - lines %(from)s to " 38 | "%(to)s" 39 | msgstr "" 40 | 41 | #. module: base_import_async 42 | #. odoo-python 43 | #: code:addons/base_import_async/models/base_import_import.py:0 44 | #, python-format 45 | msgid "Import %(model)s from file %(from_file)s" 46 | msgstr "" 47 | 48 | #. module: base_import_async 49 | #. odoo-javascript 50 | #: code:addons/base_import_async/static/src/xml/import.xml:0 51 | #, python-format 52 | msgid "Import in the background" 53 | msgstr "Arka planda içe aktarım" 54 | 55 | #. module: base_import_async 56 | #: model:ir.model,name:base_import_async.model_queue_job 57 | msgid "Queue Job" 58 | msgstr "İş Kuyruğu" 59 | 60 | #. module: base_import_async 61 | #. odoo-javascript 62 | #: code:addons/base_import_async/static/src/xml/import.xml:0 63 | #, python-format 64 | msgid "" 65 | "When checked, the import will be executed as a background job, after " 66 | "splitting your file in small chunks that will be processed independently. " 67 | "Use this to import very large files." 68 | msgstr "" 69 | "İşaretlendiğinde, içe aktarım işlemi, dosyanızı bağımsız olarak işlenecek " 70 | "küçük parçalara ayırdıktan sonra bir arka plan işi olarak yürütülür. Çok " 71 | "büyük dosyaları içe aktarmak için bunu kullanın." 72 | 73 | #. module: base_import_async 74 | #. odoo-javascript 75 | #: code:addons/base_import_async/static/src/js/import.js:0 76 | #, python-format 77 | msgid "You can check the status of this job in menu 'Queue / Jobs'." 78 | msgstr "Bu işin durumunu 'Kuyruk / İşler' menüsünden kontrol edebilirsiniz." 79 | 80 | #. module: base_import_async 81 | #. odoo-javascript 82 | #: code:addons/base_import_async/static/src/js/import.js:0 83 | #, python-format 84 | msgid "Your request is being processed" 85 | msgstr "İsteğiniz işleniyor" 86 | 87 | #, python-format 88 | #~ msgid "Import %s from file %s" 89 | #~ msgstr "%s dosyasından %s içe aktar" 90 | 91 | #, python-format 92 | #~ msgid "Import %s from file %s - #%s - lines %s to %s" 93 | #~ msgstr "%s dosyasından %s - #%s - %s - %s satırlarını içe aktar" 94 | -------------------------------------------------------------------------------- /base_import_async/models/__init__.py: -------------------------------------------------------------------------------- 1 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). 2 | 3 | from . import base_import_import 4 | from . import queue_job 5 | -------------------------------------------------------------------------------- /base_import_async/models/queue_job.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 ACSONE SA/NV 2 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). 3 | 4 | from odoo import _, models 5 | 6 | 7 | class QueueJob(models.Model): 8 | """Job status and result""" 9 | 10 | _inherit = "queue.job" 11 | 12 | def _related_action_attachment(self): 13 | return { 14 | "name": _("Attachment"), 15 | "type": "ir.actions.act_window", 16 | "res_model": "ir.attachment", 17 | "view_mode": "form", 18 | "res_id": self.kwargs.get("att_id"), 19 | } 20 | -------------------------------------------------------------------------------- /base_import_async/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["whool"] 3 | build-backend = "whool.buildapi" 4 | -------------------------------------------------------------------------------- /base_import_async/readme/CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | Sébastien Beau (Akretion) authored the initial prototype. 2 | 3 | Stéphane Bidoul (ACSONE) extended it to version 1.0 to support 4 | multi-line records, store data to import as attachments and let the user 5 | control the asynchronous behaviour. 6 | 7 | Other contributors include: 8 | 9 | - Anthony Muschang (ACSONE) 10 | 11 | - David Béal (Akretion) 12 | 13 | - Jonathan Nemry (ACSONE) 14 | 15 | - Laurent Mignon (ACSONE) 16 | 17 | - Dennis Sluijk (Onestein) 18 | 19 | - Guewen Baconnier (Camptocamp) 20 | 21 | - [Trobz](https://trobz.com): 22 | - Dzung Tran \<\> 23 | - Do Anh Duy \<\> 24 | 25 | - Daniel Duque (FactorLibre) 26 | -------------------------------------------------------------------------------- /base_import_async/readme/CREDITS.md: -------------------------------------------------------------------------------- 1 | The migration of this module from 17.0 to 18.0 was financially supported 2 | by Camptocamp 3 | -------------------------------------------------------------------------------- /base_import_async/readme/DESCRIPTION.md: -------------------------------------------------------------------------------- 1 | This module extends the standard CSV import functionality to import 2 | files in the background using the OCA/queue framework. 3 | -------------------------------------------------------------------------------- /base_import_async/readme/HISTORY.md: -------------------------------------------------------------------------------- 1 | ## 13.0.1.0.0 (2019-12-20) 2 | 3 | - \[MIGRATION\] from 12.0 branched at rev. a7f8031 4 | -------------------------------------------------------------------------------- /base_import_async/readme/ROADMAP.md: -------------------------------------------------------------------------------- 1 | - There is currently no user interface to control the chunk size, which 2 | is currently 100 by default. Should this proves to be an issue, it is 3 | easy to add an option to extend the import screen. 4 | - Validation cannot be run in the background. 5 | -------------------------------------------------------------------------------- /base_import_async/readme/USAGE.md: -------------------------------------------------------------------------------- 1 | The user is presented with a new checkbox in the import screen. When 2 | selected, the import is delayed in a background job. 3 | 4 | This job in turn splits the CSV file in chunks of minimum 100 lines (or 5 | more to align with record boundaries). Each chunk is then imported in a 6 | separate background job. 7 | 8 | When an import fails, the job is marked as such and the user can read 9 | the error in the job status. The CSV chunk being imported is stored as 10 | an attachment to the job, making it easy to download it, fix it and run 11 | a new import, possibly in synchronous mode since the chunks are small. 12 | 13 | Any file that can be imported by the standard import mechanism can also 14 | be imported in the background. 15 | 16 | This module's scope is limited to making standard imports asynchronous. 17 | It does not attempt to transform the data nor automate ETL flows. 18 | 19 | Other modules may benefit from this infrastructure in the following way 20 | (as illustrated in the test suite): 21 | 22 | 1. create an instance of base_import.import and populate its fields 23 | (res_model, file, file_name), 24 | 2. invoke the do method with appropriate options (header, encoding, 25 | separator, quoting, use_queue, chunk_size). 26 | -------------------------------------------------------------------------------- /base_import_async/static/description/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OCA/queue/fe0bba38a93847d90486e9c89dfb4df55d9d5811/base_import_async/static/description/icon.png -------------------------------------------------------------------------------- /base_import_async/static/src/js/import_model.esm.js: -------------------------------------------------------------------------------- 1 | import {BaseImportModel} from "@base_import/import_model"; 2 | import {_t} from "@web/core/l10n/translation"; 3 | import {patch} from "@web/core/utils/patch"; 4 | 5 | const {document} = globalThis; 6 | 7 | patch(BaseImportModel.prototype, { 8 | get importOptions() { 9 | const options = super.importOptions; 10 | const checkbox = document.querySelector("#oe_import_queue"); 11 | options.use_queue = checkbox ? checkbox.checked : false; 12 | return options; 13 | }, 14 | 15 | async executeImport(isTest, totalSteps, importProgress) { 16 | const def = super.executeImport(isTest, totalSteps, importProgress); 17 | const checkbox = document.querySelector("#oe_import_queue"); 18 | if (checkbox && checkbox.checked && !isTest) { 19 | this._addMessage("warning", [ 20 | _t("Your request is being processed"), 21 | _t("You can check the status of this job in menu 'Queue / Jobs'."), 22 | ]); 23 | } 24 | return def; 25 | }, 26 | }); 27 | -------------------------------------------------------------------------------- /base_import_async/static/src/xml/import_data_sidepanel.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 15 | 20 | Import in the background 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /base_import_async/tests/__init__.py: -------------------------------------------------------------------------------- 1 | from . import test_base_import_import 2 | -------------------------------------------------------------------------------- /checklog-odoo.cfg: -------------------------------------------------------------------------------- 1 | [checklog-odoo] 2 | ignore= 3 | WARNING.* 0 failed, 0 error\(s\).* 4 | -------------------------------------------------------------------------------- /prettier.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('prettier').Config} */ 2 | 3 | const config = { 4 | // https://github.com/prettier/prettier/issues/15388#issuecomment-1717746872 5 | plugins: [require.resolve("@prettier/plugin-xml")], 6 | bracketSpacing: false, 7 | printWidth: 88, 8 | proseWrap: "always", 9 | semi: true, 10 | trailingComma: "es5", 11 | xmlWhitespaceSensitivity: "preserve", 12 | }; 13 | 14 | module.exports = config; 15 | -------------------------------------------------------------------------------- /queue_job/__init__.py: -------------------------------------------------------------------------------- 1 | from . import controllers 2 | from . import fields 3 | from . import models 4 | from . import wizards 5 | from . import jobrunner 6 | from .post_init_hook import post_init_hook 7 | from .post_load import post_load 8 | 9 | # shortcuts 10 | from .job import identity_exact 11 | -------------------------------------------------------------------------------- /queue_job/__manifest__.py: -------------------------------------------------------------------------------- 1 | # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) 2 | 3 | { 4 | "name": "Job Queue", 5 | "version": "18.0.1.5.0", 6 | "author": "Camptocamp,ACSONE SA/NV,Odoo Community Association (OCA)", 7 | "website": "https://github.com/OCA/queue", 8 | "license": "LGPL-3", 9 | "category": "Generic Modules", 10 | "depends": ["mail", "base_sparse_field", "web"], 11 | "external_dependencies": {"python": ["requests"]}, 12 | "data": [ 13 | "security/security.xml", 14 | "security/ir.model.access.csv", 15 | "views/queue_job_views.xml", 16 | "views/queue_job_channel_views.xml", 17 | "views/queue_job_function_views.xml", 18 | "wizards/queue_jobs_to_done_views.xml", 19 | "wizards/queue_jobs_to_cancelled_views.xml", 20 | "wizards/queue_requeue_job_views.xml", 21 | "views/queue_job_menus.xml", 22 | "data/queue_data.xml", 23 | "data/queue_job_function_data.xml", 24 | ], 25 | "assets": { 26 | "web.assets_backend": [ 27 | "/queue_job/static/src/views/**/*", 28 | ], 29 | }, 30 | "installable": True, 31 | "development_status": "Mature", 32 | "maintainers": ["guewen"], 33 | "post_init_hook": "post_init_hook", 34 | "post_load": "post_load", 35 | } 36 | -------------------------------------------------------------------------------- /queue_job/controllers/__init__.py: -------------------------------------------------------------------------------- 1 | from . import main 2 | -------------------------------------------------------------------------------- /queue_job/data/queue_data.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Jobs Garbage Collector 6 | 5 7 | minutes 8 | 9 | code 10 | model.requeue_stuck_jobs() 11 | 12 | 13 | 14 | Job failed 15 | queue.job 16 | 17 | 18 | 19 | AutoVacuum Job Queue 20 | 21 | 22 | 23 | 1 24 | days 25 | code 26 | model.autovacuum() 27 | 28 | 29 | 30 | 31 | root 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /queue_job/data/queue_job_function_data.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | _test_job 5 | 6 | 7 | -------------------------------------------------------------------------------- /queue_job/exception.py: -------------------------------------------------------------------------------- 1 | # Copyright 2012-2016 Camptocamp 2 | # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) 3 | 4 | 5 | class BaseQueueJobError(Exception): 6 | """Base queue job error""" 7 | 8 | 9 | class JobError(BaseQueueJobError): 10 | """A job had an error""" 11 | 12 | 13 | class NoSuchJobError(JobError): 14 | """The job does not exist.""" 15 | 16 | 17 | class FailedJobError(JobError): 18 | """A job had an error having to be resolved.""" 19 | 20 | 21 | class RetryableJobError(JobError): 22 | """A job had an error but can be retried. 23 | 24 | The job will be retried after the given number of seconds. If seconds is 25 | empty, it will be retried according to the ``retry_pattern`` of the job or 26 | by :const:`odoo.addons.queue_job.job.RETRY_INTERVAL` if nothing is defined. 27 | 28 | If ``ignore_retry`` is True, the retry counter will not be increased. 29 | """ 30 | 31 | def __init__(self, msg, seconds=None, ignore_retry=False): 32 | super().__init__(msg) 33 | self.seconds = seconds 34 | self.ignore_retry = ignore_retry 35 | 36 | 37 | class ChannelNotFound(BaseQueueJobError): 38 | """A channel could not be found""" 39 | -------------------------------------------------------------------------------- /queue_job/jobrunner/__main__.py: -------------------------------------------------------------------------------- 1 | import odoo 2 | 3 | from .runner import QueueJobRunner 4 | 5 | 6 | def main(): 7 | odoo.tools.config.parse_config() 8 | runner = QueueJobRunner.from_environ_or_config() 9 | runner.run() 10 | 11 | 12 | if __name__ == "__main__": 13 | main() 14 | -------------------------------------------------------------------------------- /queue_job/migrations/18.0.1.0.0/pre-migrate.py: -------------------------------------------------------------------------------- 1 | from openupgradelib import openupgrade 2 | 3 | from odoo.tools import SQL 4 | 5 | 6 | def migrate(cr, version): 7 | if not version: 8 | return 9 | 10 | # List of tables and their corresponding columns 11 | table_column_map = { 12 | "queue.job.function": ["retry_pattern", "related_action"], 13 | "queue.job": ["records", "args", "kwargs"], 14 | } 15 | 16 | for table, columns in table_column_map.items(): 17 | for column in columns: 18 | if openupgrade.column_exists(cr, table, column): 19 | cr.execute( 20 | SQL( 21 | """ 22 | UPDATE %(table)s 23 | SET %(column)s = %(column)s::jsonb 24 | WHERE %(column)s IS NOT NULL 25 | """, 26 | table=SQL.identifier(table), 27 | column=SQL.identifier(column), 28 | ) 29 | ) 30 | -------------------------------------------------------------------------------- /queue_job/models/__init__.py: -------------------------------------------------------------------------------- 1 | from . import base 2 | from . import ir_model_fields 3 | from . import queue_job 4 | from . import queue_job_channel 5 | from . import queue_job_function 6 | -------------------------------------------------------------------------------- /queue_job/models/ir_model_fields.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Camptocamp 2 | # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) 3 | 4 | from odoo import fields, models 5 | 6 | 7 | class IrModelFields(models.Model): 8 | _inherit = "ir.model.fields" 9 | 10 | ttype = fields.Selection( 11 | selection_add=[("job_serialized", "Job Serialized")], 12 | ondelete={"job_serialized": "cascade"}, 13 | ) 14 | -------------------------------------------------------------------------------- /queue_job/models/queue_job_channel.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013-2020 Camptocamp SA 2 | # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) 3 | 4 | 5 | from odoo import _, api, exceptions, fields, models 6 | 7 | 8 | class QueueJobChannel(models.Model): 9 | _name = "queue.job.channel" 10 | _description = "Job Channels" 11 | _rec_name = "complete_name" 12 | 13 | name = fields.Char() 14 | complete_name = fields.Char( 15 | compute="_compute_complete_name", store=True, readonly=True, recursive=True 16 | ) 17 | parent_id = fields.Many2one( 18 | comodel_name="queue.job.channel", string="Parent Channel", ondelete="restrict" 19 | ) 20 | job_function_ids = fields.One2many( 21 | comodel_name="queue.job.function", 22 | inverse_name="channel_id", 23 | string="Job Functions", 24 | ) 25 | removal_interval = fields.Integer( 26 | default=lambda self: self.env["queue.job"]._removal_interval, required=True 27 | ) 28 | 29 | _sql_constraints = [ 30 | ("name_uniq", "unique(complete_name)", "Channel complete name must be unique") 31 | ] 32 | 33 | @api.depends("name", "parent_id.complete_name") 34 | def _compute_complete_name(self): 35 | for record in self: 36 | if not record.name: 37 | complete_name = "" # new record 38 | elif record.parent_id: 39 | complete_name = ".".join([record.parent_id.complete_name, record.name]) 40 | else: 41 | complete_name = record.name 42 | record.complete_name = complete_name 43 | 44 | @api.constrains("parent_id", "name") 45 | def parent_required(self): 46 | for record in self: 47 | if record.name != "root" and not record.parent_id: 48 | raise exceptions.ValidationError(_("Parent channel required.")) 49 | 50 | @api.model_create_multi 51 | def create(self, vals_list): 52 | records = self.browse() 53 | if self.env.context.get("install_mode"): 54 | # installing a module that creates a channel: rebinds the channel 55 | # to an existing one (likely we already had the channel created by 56 | # the @job decorator previously) 57 | new_vals_list = [] 58 | for vals in vals_list: 59 | name = vals.get("name") 60 | parent_id = vals.get("parent_id") 61 | if name and parent_id: 62 | existing = self.search( 63 | [("name", "=", name), ("parent_id", "=", parent_id)] 64 | ) 65 | if existing: 66 | if not existing.get_metadata()[0].get("noupdate"): 67 | existing.write(vals) 68 | records |= existing 69 | continue 70 | new_vals_list.append(vals) 71 | vals_list = new_vals_list 72 | records |= super().create(vals_list) 73 | return records 74 | 75 | def write(self, values): 76 | for channel in self: 77 | if ( 78 | not self.env.context.get("install_mode") 79 | and channel.name == "root" 80 | and ("name" in values or "parent_id" in values) 81 | ): 82 | raise exceptions.UserError(_("Cannot change the root channel")) 83 | return super().write(values) 84 | 85 | def unlink(self): 86 | for channel in self: 87 | if channel.name == "root": 88 | raise exceptions.UserError(_("Cannot remove the root channel")) 89 | return super().unlink() 90 | -------------------------------------------------------------------------------- /queue_job/post_init_hook.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 ACSONE SA/NV 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | 4 | import logging 5 | 6 | logger = logging.getLogger(__name__) 7 | 8 | 9 | def post_init_hook(env): 10 | # this is the trigger that sends notifications when jobs change 11 | logger.info("Create queue_job_notify trigger") 12 | env.cr.execute( 13 | """ 14 | DROP TRIGGER IF EXISTS queue_job_notify ON queue_job; 15 | CREATE OR REPLACE 16 | FUNCTION queue_job_notify() RETURNS trigger AS $$ 17 | BEGIN 18 | IF TG_OP = 'DELETE' THEN 19 | IF OLD.state != 'done' THEN 20 | PERFORM pg_notify('queue_job', OLD.uuid); 21 | END IF; 22 | ELSE 23 | PERFORM pg_notify('queue_job', NEW.uuid); 24 | END IF; 25 | RETURN NULL; 26 | END; 27 | $$ LANGUAGE plpgsql; 28 | CREATE TRIGGER queue_job_notify 29 | AFTER INSERT OR UPDATE OR DELETE 30 | ON queue_job 31 | FOR EACH ROW EXECUTE PROCEDURE queue_job_notify(); 32 | """ 33 | ) 34 | -------------------------------------------------------------------------------- /queue_job/post_load.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from odoo import http 4 | 5 | _logger = logging.getLogger(__name__) 6 | 7 | 8 | def post_load(): 9 | _logger.info( 10 | "Apply Request._get_session_and_dbname monkey patch to capture db" 11 | " from request with multiple databases" 12 | ) 13 | _get_session_and_dbname_orig = http.Request._get_session_and_dbname 14 | 15 | def _get_session_and_dbname(self): 16 | session, dbname = _get_session_and_dbname_orig(self) 17 | if ( 18 | not dbname 19 | and self.httprequest.path == "/queue_job/runjob" 20 | and self.httprequest.args.get("db") 21 | ): 22 | dbname = self.httprequest.args["db"] 23 | return session, dbname 24 | 25 | http.Request._get_session_and_dbname = _get_session_and_dbname 26 | -------------------------------------------------------------------------------- /queue_job/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["whool"] 3 | build-backend = "whool.buildapi" 4 | -------------------------------------------------------------------------------- /queue_job/readme/CONFIGURE.md: -------------------------------------------------------------------------------- 1 | - Using environment variables and command line: 2 | - Adjust environment variables (optional): 3 | - `ODOO_QUEUE_JOB_CHANNELS=root:4` or any other channels 4 | configuration. The default is `root:1` 5 | - if `xmlrpc_port` is not set: `ODOO_QUEUE_JOB_PORT=8069` 6 | - Start Odoo with `--load=web,queue_job` and `--workers` greater than 7 | 1.[^1] 8 | - Using the Odoo configuration file: 9 | 10 | ``` ini 11 | [options] 12 | (...) 13 | workers = 6 14 | server_wide_modules = web,queue_job 15 | 16 | (...) 17 | [queue_job] 18 | channels = root:2 19 | ``` 20 | 21 | - Confirm the runner is starting correctly by checking the odoo log 22 | file: 23 | 24 | ``` 25 | ...INFO...queue_job.jobrunner.runner: starting 26 | ...INFO...queue_job.jobrunner.runner: initializing database connections 27 | ...INFO...queue_job.jobrunner.runner: queue job runner ready for db 28 | ...INFO...queue_job.jobrunner.runner: database connections ready 29 | ``` 30 | 31 | - Create jobs (eg using `base_import_async`) and observe they start 32 | immediately and in parallel. 33 | - Tip: to enable debug logging for the queue job, use 34 | `--log-handler=odoo.addons.queue_job:DEBUG` 35 | 36 | [^1]: It works with the threaded Odoo server too, although this way of 37 | running Odoo is obviously not for production purposes. 38 | -------------------------------------------------------------------------------- /queue_job/readme/CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | - Guewen Baconnier \<\> 2 | - Stéphane Bidoul \<\> 3 | - Matthieu Dietrich \<\> 4 | - Jos De Graeve \<\> 5 | - David Lefever \<\> 6 | - Laurent Mignon \<\> 7 | - Laetitia Gangloff \<\> 8 | - Cédric Pigeon \<\> 9 | - Tatiana Deribina \<\> 10 | - Souheil Bejaoui \<\> 11 | - Eric Antones \<\> 12 | - Simone Orsi \<\> 13 | - Nguyen Minh Chien \<\> 14 | - Tran Quoc Duong \<> 15 | - Vo Hong Thien \<> 16 | -------------------------------------------------------------------------------- /queue_job/readme/CREDITS.md: -------------------------------------------------------------------------------- 1 | The migration of this module from 17.0 to 18.0 was financially supported by Camptocamp. 2 | -------------------------------------------------------------------------------- /queue_job/readme/DESCRIPTION.md: -------------------------------------------------------------------------------- 1 | This addon adds an integrated Job Queue to Odoo. 2 | 3 | It allows to postpone method calls executed asynchronously. 4 | 5 | Jobs are executed in the background by a `Jobrunner`, in their own 6 | transaction. 7 | 8 | Example: 9 | 10 | ``` python 11 | from odoo import models, fields, api 12 | 13 | class MyModel(models.Model): 14 | _name = 'my.model' 15 | 16 | def my_method(self, a, k=None): 17 | _logger.info('executed with a: %s and k: %s', a, k) 18 | 19 | 20 | class MyOtherModel(models.Model): 21 | _name = 'my.other.model' 22 | 23 | def button_do_stuff(self): 24 | self.env['my.model'].with_delay().my_method('a', k=2) 25 | ``` 26 | 27 | In the snippet of code above, when we call `button_do_stuff`, a job 28 | **capturing the method and arguments** will be postponed. It will be 29 | executed as soon as the Jobrunner has a free bucket, which can be 30 | instantaneous if no other job is running. 31 | 32 | Features: 33 | 34 | - Views for jobs, jobs are stored in PostgreSQL 35 | - Jobrunner: execute the jobs, highly efficient thanks to PostgreSQL's 36 | NOTIFY 37 | - Channels: give a capacity for the root channel and its sub-channels 38 | and segregate jobs in them. Allow for instance to restrict heavy jobs 39 | to be executed one at a time while little ones are executed 4 at a 40 | times. 41 | - Retries: Ability to retry jobs by raising a type of exception 42 | - Retry Pattern: the 3 first tries, retry after 10 seconds, the 5 next 43 | tries, retry after 1 minutes, ... 44 | - Job properties: priorities, estimated time of arrival (ETA), custom 45 | description, number of retries 46 | - Related Actions: link an action on the job view, such as open the 47 | record concerned by the job 48 | -------------------------------------------------------------------------------- /queue_job/readme/HISTORY.md: -------------------------------------------------------------------------------- 1 | ## Next 2 | 3 | - \[ADD\] Run jobrunner as a worker process instead of a thread in the 4 | main process (when running with --workers \> 0) 5 | - \[REF\] `@job` and `@related_action` deprecated, any method can be 6 | delayed, and configured using `queue.job.function` records 7 | - \[MIGRATION\] from 13.0 branched at rev. e24ff4b 8 | -------------------------------------------------------------------------------- /queue_job/readme/INSTALL.md: -------------------------------------------------------------------------------- 1 | Be sure to have the `requests` library. 2 | -------------------------------------------------------------------------------- /queue_job/readme/ROADMAP.md: -------------------------------------------------------------------------------- 1 | - After creating a new database or installing `queue_job` on an existing 2 | database, Odoo must be restarted for the runner to detect it. 3 | - When Odoo shuts down normally, it waits for running jobs to finish. 4 | However, when the Odoo server crashes or is otherwise force-stopped, 5 | running jobs are interrupted while the runner has no chance to know 6 | they have been aborted. In such situations, jobs may remain in 7 | `started` or `enqueued` state after the Odoo server is halted. Since 8 | the runner has no way to know if they are actually running or not, and 9 | does not know for sure if it is safe to restart the jobs, it does not 10 | attempt to restart them automatically. Such stale jobs therefore fill 11 | the running queue and prevent other jobs to start. You must therefore 12 | requeue them manually, either from the Jobs view, or by running the 13 | following SQL statement *before starting Odoo*: 14 | 15 | ``` sql 16 | update queue_job set state='pending' where state in ('started', 'enqueued') 17 | ``` 18 | -------------------------------------------------------------------------------- /queue_job/security/ir.model.access.csv: -------------------------------------------------------------------------------- 1 | id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink 2 | access_queue_job_manager,queue job manager,queue_job.model_queue_job,queue_job.group_queue_job_manager,1,1,1,1 3 | access_queue_job_function_manager,queue job functions manager,queue_job.model_queue_job_function,queue_job.group_queue_job_manager,1,1,1,1 4 | access_queue_job_channel_manager,queue job channel manager,queue_job.model_queue_job_channel,queue_job.group_queue_job_manager,1,1,1,1 5 | access_queue_requeue_job,queue requeue job manager,queue_job.model_queue_requeue_job,queue_job.group_queue_job_manager,1,1,1,1 6 | access_queue_jobs_to_done,queue jobs to done manager,queue_job.model_queue_jobs_to_done,queue_job.group_queue_job_manager,1,1,1,1 7 | access_queue_jobs_to_cancelled,queue jobs to cancelled manager,queue_job.model_queue_jobs_to_cancelled,queue_job.group_queue_job_manager,1,1,1,1 8 | -------------------------------------------------------------------------------- /queue_job/security/security.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Job Queue 6 | 20 7 | 8 | 9 | Job Queue Manager 10 | 11 | 15 | 16 | 17 | 18 | 19 | Job Queue multi-company 20 | 21 | 22 | ['|', ('company_id', '=', False), ('company_id', 'in', company_ids)] 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /queue_job/static/description/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OCA/queue/fe0bba38a93847d90486e9c89dfb4df55d9d5811/queue_job/static/description/icon.png -------------------------------------------------------------------------------- /queue_job/static/description/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /queue_job/static/src/views/fields/job_direct_graph/job_direct_graph.scss: -------------------------------------------------------------------------------- 1 | .o_field_job_directed_graph { 2 | width: 600px; 3 | height: 400px; 4 | border: 1px solid lightgray; 5 | 6 | div.root_vis { 7 | height: 100%; 8 | width: 100%; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /queue_job/static/src/views/fields/job_direct_graph/job_direct_graph.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /queue_job/tests/__init__.py: -------------------------------------------------------------------------------- 1 | from . import test_runner_channels 2 | from . import test_runner_runner 3 | from . import test_delayable 4 | from . import test_delayable_split 5 | from . import test_json_field 6 | from . import test_model_job_channel 7 | from . import test_model_job_function 8 | from . import test_queue_job_protected_write 9 | from . import test_wizards 10 | -------------------------------------------------------------------------------- /queue_job/tests/test_model_job_channel.py: -------------------------------------------------------------------------------- 1 | # copyright 2018 Camptocamp 2 | # license lgpl-3.0 or later (http://www.gnu.org/licenses/lgpl.html) 3 | 4 | from psycopg2 import IntegrityError 5 | 6 | import odoo 7 | from odoo.tests import common 8 | 9 | 10 | class TestJobChannel(common.TransactionCase): 11 | def setUp(self): 12 | super().setUp() 13 | self.Channel = self.env["queue.job.channel"] 14 | self.root_channel = self.Channel.search([("name", "=", "root")]) 15 | 16 | def test_channel_new(self): 17 | channel = self.Channel.new() 18 | self.assertFalse(channel.name) 19 | self.assertFalse(channel.complete_name) 20 | 21 | def test_channel_create(self): 22 | channel = self.Channel.create( 23 | {"name": "test", "parent_id": self.root_channel.id} 24 | ) 25 | self.assertEqual(channel.name, "test") 26 | self.assertEqual(channel.complete_name, "root.test") 27 | channel2 = self.Channel.create({"name": "test", "parent_id": channel.id}) 28 | self.assertEqual(channel2.name, "test") 29 | self.assertEqual(channel2.complete_name, "root.test.test") 30 | 31 | @odoo.tools.mute_logger("odoo.sql_db") 32 | def test_channel_complete_name_uniq(self): 33 | channel = self.Channel.create( 34 | {"name": "test", "parent_id": self.root_channel.id} 35 | ) 36 | self.assertEqual(channel.name, "test") 37 | self.assertEqual(channel.complete_name, "root.test") 38 | 39 | self.Channel.create({"name": "test", "parent_id": self.root_channel.id}) 40 | 41 | # Flush process all the pending recomputations (or at least the 42 | # given field and flush the pending updates to the database. 43 | # It is normally called on commit. 44 | 45 | # The context manager 'with self.assertRaises(IntegrityError)' purposefully 46 | # not uses here due to its 'flush_all()' method inside it and exception raises 47 | # before the line 'self.env.flush_all()'. So, we are expecting an IntegrityError 48 | try: 49 | self.env.flush_all() 50 | except IntegrityError as ex: 51 | self.assertIn("queue_job_channel_name_uniq", ex.pgerror) 52 | else: 53 | self.assertEqual(True, False) 54 | 55 | def test_channel_display_name(self): 56 | channel = self.Channel.create( 57 | {"name": "test", "parent_id": self.root_channel.id} 58 | ) 59 | self.assertEqual(channel.display_name, channel.complete_name) 60 | -------------------------------------------------------------------------------- /queue_job/tests/test_model_job_function.py: -------------------------------------------------------------------------------- 1 | # copyright 2020 Camptocamp 2 | # license lgpl-3.0 or later (http://www.gnu.org/licenses/lgpl.html) 3 | 4 | from odoo import exceptions 5 | from odoo.tests import common 6 | 7 | 8 | class TestJobFunction(common.TransactionCase): 9 | def test_function_name_compute(self): 10 | function = self.env["queue.job.function"].create( 11 | {"model_id": self.env.ref("base.model_res_users").id, "method": "read"} 12 | ) 13 | self.assertEqual(function.name, ".read") 14 | 15 | def test_function_name_inverse(self): 16 | function = self.env["queue.job.function"].create({"name": ".read"}) 17 | self.assertEqual(function.model_id.model, "res.users") 18 | self.assertEqual(function.method, "read") 19 | 20 | def test_function_name_inverse_invalid_regex(self): 21 | with self.assertRaises(exceptions.UserError): 22 | self.env["queue.job.function"].create({"name": ".read"} 28 | ) 29 | 30 | def test_function_job_config(self): 31 | channel = self.env["queue.job.channel"].create( 32 | {"name": "foo", "parent_id": self.env.ref("queue_job.channel_root").id} 33 | ) 34 | job_function = self.env["queue.job.function"].create( 35 | { 36 | "model_id": self.env.ref("base.model_res_users").id, 37 | "method": "read", 38 | "channel_id": channel.id, 39 | "edit_retry_pattern": "{1: 2, 3: 4}", 40 | "edit_related_action": ( 41 | '{"enable": True,' 42 | ' "func_name": "related_action_foo",' 43 | ' "kwargs": {"b": 1}}' 44 | ), 45 | } 46 | ) 47 | self.assertEqual( 48 | self.env["queue.job.function"].job_config(".read"), 49 | self.env["queue.job.function"].JobConfig( 50 | channel="root.foo", 51 | retry_pattern={1: 2, 3: 4}, 52 | related_action_enable=True, 53 | related_action_func_name="related_action_foo", 54 | related_action_kwargs={"b": 1}, 55 | job_function_id=job_function.id, 56 | ), 57 | ) 58 | -------------------------------------------------------------------------------- /queue_job/tests/test_queue_job_protected_write.py: -------------------------------------------------------------------------------- 1 | # copyright 2020 Camptocamp 2 | # license lgpl-3.0 or later (http://www.gnu.org/licenses/lgpl.html) 3 | 4 | from odoo import exceptions 5 | from odoo.tests import common 6 | 7 | 8 | class TestJobWriteProtected(common.TransactionCase): 9 | def test_create_error(self): 10 | with self.assertRaises(exceptions.AccessError): 11 | self.env["queue.job"].create( 12 | {"uuid": "test", "model_name": "res.partner", "method_name": "write"} 13 | ) 14 | 15 | def test_write_protected_field_error(self): 16 | job_ = self.env["res.partner"].with_delay().create({"name": "test"}) 17 | db_job = job_.db_record() 18 | with self.assertRaises(exceptions.AccessError): 19 | db_job.method_name = "unlink" 20 | 21 | def test_write_allow_no_protected_field_error(self): 22 | job_ = self.env["res.partner"].with_delay().create({"name": "test"}) 23 | db_job = job_.db_record() 24 | db_job.priority = 30 25 | self.assertEqual(db_job.priority, 30) 26 | -------------------------------------------------------------------------------- /queue_job/tests/test_runner_channels.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015-2016 Camptocamp SA 2 | # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) 3 | 4 | # pylint: disable=odoo-addons-relative-import 5 | # we are testing, we want to test as we were an external consumer of the API 6 | from odoo.addons.queue_job.jobrunner import channels 7 | 8 | from .common import load_doctests 9 | 10 | load_tests = load_doctests(channels) 11 | -------------------------------------------------------------------------------- /queue_job/tests/test_runner_runner.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015-2016 Camptocamp SA 2 | # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) 3 | 4 | # pylint: disable=odoo-addons-relative-import 5 | # we are testing, we want to test as we were an external consumer of the API 6 | import os 7 | 8 | from odoo.tests import BaseCase, tagged 9 | 10 | from odoo.addons.queue_job.jobrunner import runner 11 | 12 | from .common import load_doctests 13 | 14 | load_tests = load_doctests(runner) 15 | 16 | 17 | @tagged("-at_install", "post_install") 18 | class TestRunner(BaseCase): 19 | @classmethod 20 | def _is_open_file_descriptor(cls, fd): 21 | try: 22 | os.fstat(fd) 23 | return True 24 | except OSError: 25 | return False 26 | 27 | def test_runner_file_descriptor(self): 28 | a_runner = runner.QueueJobRunner.from_environ_or_config() 29 | 30 | read_fd, write_fd = a_runner._stop_pipe 31 | self.assertTrue(self._is_open_file_descriptor(read_fd)) 32 | self.assertTrue(self._is_open_file_descriptor(write_fd)) 33 | 34 | del a_runner 35 | 36 | self.assertFalse(self._is_open_file_descriptor(read_fd)) 37 | self.assertFalse(self._is_open_file_descriptor(write_fd)) 38 | 39 | def test_runner_file_closed_read_descriptor(self): 40 | a_runner = runner.QueueJobRunner.from_environ_or_config() 41 | 42 | read_fd, write_fd = a_runner._stop_pipe 43 | os.close(read_fd) 44 | 45 | del a_runner 46 | 47 | self.assertFalse(self._is_open_file_descriptor(read_fd)) 48 | self.assertFalse(self._is_open_file_descriptor(write_fd)) 49 | 50 | def test_runner_file_closed_write_descriptor(self): 51 | a_runner = runner.QueueJobRunner.from_environ_or_config() 52 | 53 | read_fd, write_fd = a_runner._stop_pipe 54 | os.close(write_fd) 55 | 56 | del a_runner 57 | 58 | self.assertFalse(self._is_open_file_descriptor(read_fd)) 59 | self.assertFalse(self._is_open_file_descriptor(write_fd)) 60 | -------------------------------------------------------------------------------- /queue_job/tests/test_wizards.py: -------------------------------------------------------------------------------- 1 | # license lgpl-3.0 or later (http://www.gnu.org/licenses/lgpl.html) 2 | from odoo.tests import common 3 | 4 | 5 | class TestWizards(common.TransactionCase): 6 | def setUp(self): 7 | super().setUp() 8 | self.job = ( 9 | self.env["queue.job"] 10 | .with_context( 11 | _job_edit_sentinel=self.env["queue.job"].EDIT_SENTINEL, 12 | ) 13 | .create( 14 | { 15 | "uuid": "test", 16 | "user_id": self.env.user.id, 17 | "state": "failed", 18 | "model_name": "queue.job", 19 | "method_name": "write", 20 | "args": (), 21 | } 22 | ) 23 | ) 24 | 25 | def _wizard(self, model_name): 26 | return ( 27 | self.env[model_name] 28 | .with_context( 29 | active_model=self.job._name, 30 | active_ids=self.job.ids, 31 | ) 32 | .create({}) 33 | ) 34 | 35 | def test_01_requeue(self): 36 | wizard = self._wizard("queue.requeue.job") 37 | wizard.requeue() 38 | self.assertEqual(self.job.state, "pending") 39 | 40 | def test_02_cancel(self): 41 | wizard = self._wizard("queue.jobs.to.cancelled") 42 | wizard.set_cancelled() 43 | self.assertEqual(self.job.state, "cancelled") 44 | 45 | def test_03_done(self): 46 | wizard = self._wizard("queue.jobs.to.done") 47 | wizard.set_done() 48 | self.assertEqual(self.job.state, "done") 49 | -------------------------------------------------------------------------------- /queue_job/utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Camptocamp 2 | # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) 3 | 4 | import logging 5 | import os 6 | 7 | _logger = logging.getLogger(__name__) 8 | 9 | 10 | def must_run_without_delay(env): 11 | """Retrun true if jobs have to run immediately. 12 | 13 | :param env: `odoo.api.Environment` instance 14 | """ 15 | if os.getenv("QUEUE_JOB__NO_DELAY"): 16 | _logger.warning("`QUEUE_JOB__NO_DELAY` env var found. NO JOB scheduled.") 17 | return True 18 | 19 | if env.context.get("queue_job__no_delay"): 20 | _logger.info("`queue_job__no_delay` ctx key found. NO JOB scheduled.") 21 | return True 22 | -------------------------------------------------------------------------------- /queue_job/views/queue_job_channel_views.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | queue.job.channel.form 5 | queue.job.channel 6 | 7 | 8 | 9 | 14 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | queue.job.channel.tree 31 | queue.job.channel 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | queue.job.channel.search 41 | queue.job.channel 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | Channels 53 | queue.job.channel 54 | list,form 55 | {} 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /queue_job/views/queue_job_function_views.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | queue.job.function.form 5 | queue.job.function 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | queue.job.function.tree 22 | queue.job.function 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | queue.job.function.search 33 | queue.job.function 34 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | Job Functions 51 | queue.job.function 52 | list,form 53 | {} 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /queue_job/views/queue_job_menus.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 | 12 | 18 | 19 | 25 | 26 | 32 | 33 | -------------------------------------------------------------------------------- /queue_job/wizards/__init__.py: -------------------------------------------------------------------------------- 1 | from . import queue_requeue_job 2 | from . import queue_jobs_to_done 3 | from . import queue_jobs_to_cancelled 4 | -------------------------------------------------------------------------------- /queue_job/wizards/queue_jobs_to_cancelled.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013-2020 Camptocamp SA 2 | # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) 3 | 4 | from odoo import models 5 | 6 | 7 | class SetJobsToCancelled(models.TransientModel): 8 | _inherit = "queue.requeue.job" 9 | _name = "queue.jobs.to.cancelled" 10 | _description = "Cancel all selected jobs" 11 | 12 | def set_cancelled(self): 13 | jobs = self.job_ids.filtered( 14 | lambda x: x.state in ("pending", "failed", "enqueued") 15 | ) 16 | jobs.button_cancelled() 17 | return {"type": "ir.actions.act_window_close"} 18 | -------------------------------------------------------------------------------- /queue_job/wizards/queue_jobs_to_cancelled_views.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Cancel Jobs 5 | queue.jobs.to.cancelled 6 | 7 | 8 | 9 | 10 | 11 | 20 | 21 | 22 | 23 | 24 | 25 | Cancel jobs 26 | queue.jobs.to.cancelled 27 | form 28 | 29 | new 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /queue_job/wizards/queue_jobs_to_done.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013-2020 Camptocamp SA 2 | # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) 3 | 4 | from odoo import models 5 | 6 | 7 | class SetJobsToDone(models.TransientModel): 8 | _inherit = "queue.requeue.job" 9 | _name = "queue.jobs.to.done" 10 | _description = "Set all selected jobs to done" 11 | 12 | def set_done(self): 13 | jobs = self.job_ids 14 | jobs.button_done() 15 | return {"type": "ir.actions.act_window_close"} 16 | -------------------------------------------------------------------------------- /queue_job/wizards/queue_jobs_to_done_views.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Set Jobs to Done 5 | queue.jobs.to.done 6 | 7 | 8 | 9 | 10 | 11 | 20 | 21 | 22 | 23 | 24 | 25 | Set jobs to done 26 | queue.jobs.to.done 27 | form 28 | 29 | new 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /queue_job/wizards/queue_requeue_job.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013-2020 Camptocamp SA 2 | # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) 3 | 4 | from odoo import fields, models 5 | 6 | 7 | class QueueRequeueJob(models.TransientModel): 8 | _name = "queue.requeue.job" 9 | _description = "Wizard to requeue a selection of jobs" 10 | 11 | def _default_job_ids(self): 12 | res = False 13 | context = self.env.context 14 | if context.get("active_model") == "queue.job" and context.get("active_ids"): 15 | res = context["active_ids"] 16 | return res 17 | 18 | job_ids = fields.Many2many( 19 | comodel_name="queue.job", string="Jobs", default=lambda r: r._default_job_ids() 20 | ) 21 | 22 | def requeue(self): 23 | jobs = self.job_ids 24 | jobs.requeue() 25 | return {"type": "ir.actions.act_window_close"} 26 | -------------------------------------------------------------------------------- /queue_job/wizards/queue_requeue_job_views.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Requeue Jobs 5 | queue.requeue.job 6 | 7 | 8 | 9 | 10 | 11 | 20 | 21 | 22 | 23 | 24 | 25 | Requeue Jobs 26 | queue.requeue.job 27 | form 28 | 29 | new 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /queue_job_batch/__init__.py: -------------------------------------------------------------------------------- 1 | from . import controllers 2 | from . import models 3 | -------------------------------------------------------------------------------- /queue_job_batch/__manifest__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Creu Blanca 2 | # Copyright 2023 ForgeFlow S.L. (http://www.forgeflow.com) 3 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) 4 | 5 | { 6 | "name": "Job Queue Batch", 7 | "version": "18.0.1.0.0", 8 | "author": "Creu Blanca,Odoo Community Association (OCA)", 9 | "website": "https://github.com/OCA/queue", 10 | "license": "AGPL-3", 11 | "category": "Generic Modules", 12 | "depends": [ 13 | "queue_job", 14 | ], 15 | "data": [ 16 | # data 17 | "data/queue_job_channel_data.xml", 18 | "data/queue_job_function_data.xml", 19 | # security 20 | "security/security.xml", 21 | "security/ir.model.access.csv", 22 | # views 23 | "views/queue_job_views.xml", 24 | "views/queue_job_batch_views.xml", 25 | ], 26 | "assets": { 27 | "web.assets_backend": [ 28 | "queue_job_batch/static/src/**/*.js", 29 | "queue_job_batch/static/src/**/*.xml", 30 | "queue_job_batch/static/src/**/*.scss", 31 | ], 32 | }, 33 | } 34 | -------------------------------------------------------------------------------- /queue_job_batch/controllers/__init__.py: -------------------------------------------------------------------------------- 1 | from . import webclient 2 | -------------------------------------------------------------------------------- /queue_job_batch/controllers/webclient.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 Camptocamp SA (https://www.camptocamp.com). 2 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). 3 | 4 | from odoo.http import request 5 | 6 | from odoo.addons.mail.controllers.webclient import WebclientController 7 | 8 | 9 | class WebClient(WebclientController): 10 | def _process_request_for_internal_user(self, store, **kwargs): 11 | res = super()._process_request_for_internal_user(store, **kwargs) 12 | if kwargs.get("systray_get_queue_job_batches"): 13 | # sudo: bus.bus: reading non-sensitive last id 14 | bus_last_id = request.env["bus.bus"].sudo()._bus_last_id() 15 | batches = request.env.user._get_queue_job_batches() 16 | store.add(batches) 17 | store.add( 18 | { 19 | "queueJobBatchCounter": len(batches), 20 | "queueJobBatchCounterBusId": bus_last_id, 21 | } 22 | ) 23 | return res 24 | -------------------------------------------------------------------------------- /queue_job_batch/data/queue_job_channel_data.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | queue.job.batch 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /queue_job_batch/data/queue_job_function_data.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | check_state 6 | 7 | 8 | -------------------------------------------------------------------------------- /queue_job_batch/migrations/18.0.1.0.0/pre-migrate.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 Camptocamp SA (https://www.camptocamp.com). 2 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). 3 | 4 | 5 | def migrate(cr, version): 6 | cr.execute( 7 | """ 8 | UPDATE queue_job_batch 9 | SET state = 'pending' 10 | WHERE state = 'draft' 11 | """ 12 | ) 13 | cr.execute( 14 | """ 15 | UPDATE queue_job_batch 16 | SET is_read = FALSE 17 | WHERE state != 'finished' AND is_read 18 | """ 19 | ) 20 | -------------------------------------------------------------------------------- /queue_job_batch/models/__init__.py: -------------------------------------------------------------------------------- 1 | from . import queue_job_batch 2 | from . import queue_job 3 | from . import res_users 4 | -------------------------------------------------------------------------------- /queue_job_batch/models/queue_job.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Creu Blanca 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) 3 | 4 | from odoo import api, fields, models 5 | 6 | from odoo.addons.queue_job.job import identity_exact 7 | 8 | 9 | class QueueJob(models.Model): 10 | _inherit = "queue.job" 11 | 12 | job_batch_id = fields.Many2one("queue.job.batch") 13 | 14 | @api.model_create_multi 15 | def create(self, vals_list): 16 | batch = self.env.context.get("job_batch") 17 | if batch and isinstance(batch, models.Model): 18 | for vals in vals_list: 19 | vals.update({"job_batch_id": batch.id}) 20 | return super().create(vals_list) 21 | 22 | def write(self, vals): 23 | if vals.get("state", "") == "done": 24 | batches = self.env["queue.job.batch"] 25 | for record in self: 26 | if record.state != "done" and record.job_batch_id: 27 | batches |= record.job_batch_id 28 | for batch in batches: 29 | # We need to make it with delay in order to prevent two jobs 30 | # to work with the same batch 31 | batch.with_delay(identity_key=identity_exact).check_state() 32 | return super().write(vals) 33 | -------------------------------------------------------------------------------- /queue_job_batch/models/res_users.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 Camptocamp SA (https://www.camptocamp.com). 2 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). 3 | 4 | from odoo import models 5 | 6 | 7 | class Users(models.Model): 8 | _name = "res.users" 9 | _inherit = ["res.users"] 10 | 11 | def _init_store_data(self, store): 12 | res = super()._init_store_data(store) 13 | store.add( 14 | { 15 | "hasQueueJobBatchUserGroup": self.env.user.has_group( 16 | "queue_job_batch.group_queue_job_batch_user" 17 | ), 18 | } 19 | ) 20 | return res 21 | 22 | def _get_queue_job_batches(self): 23 | # See :meth:`controllers.webclient.WebClient._process_request_for_internal_user` 24 | self.ensure_one() 25 | return self.env["queue.job.batch"].search( 26 | [ 27 | ("user_id", "=", self.id), 28 | ("is_read", "=", False), 29 | ] 30 | ) 31 | -------------------------------------------------------------------------------- /queue_job_batch/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["whool"] 3 | build-backend = "whool.buildapi" 4 | -------------------------------------------------------------------------------- /queue_job_batch/readme/CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | - Enric Tobella \<\> 2 | 3 | - [Trobz](https://trobz.com): 4 | - Hoang Diep \<\> 5 | 6 | - [ForgeFlow](https://forgeflow.com): 7 | - Lois Rilo \<\> 8 | - Jasmin Solanki \<\> 9 | 10 | - [Camptocamp](https://camptocamp.com): 11 | - Maksym Yankin \<\> 12 | - Iván Todorovich \<\> 13 | -------------------------------------------------------------------------------- /queue_job_batch/readme/CREDITS.md: -------------------------------------------------------------------------------- 1 | The migration of this module from 12.0 to 14.0 was financially supported 2 | by Camptocamp 3 | -------------------------------------------------------------------------------- /queue_job_batch/readme/DESCRIPTION.md: -------------------------------------------------------------------------------- 1 | This addon adds an a grouper for queue jobs. 2 | 3 | It allows to show your jobs in a batched form in order to know better 4 | the results. 5 | 6 | Example: 7 | 8 | ``` python 9 | from odoo import models, fields, api 10 | 11 | 12 | class MyModel(models.Model): 13 | _name = 'my.model' 14 | 15 | def my_method(self, a, k=None): 16 | _logger.info('executed with a: %s and k: %s', a, k) 17 | 18 | 19 | class MyOtherModel(models.Model): 20 | _name = 'my.other.model' 21 | 22 | @api.multi 23 | def button_do_stuff(self): 24 | batch = self.env['queue.job.batch'].get_new_batch('Group') 25 | model = self.env['my.model'].with_context(job_batch=batch) 26 | for i in range(1, 100): 27 | model.with_delay().my_method('a', k=i) 28 | ``` 29 | 30 | In the snippet of code above, when we call `button_do_stuff`, 100 jobs 31 | capturing the method and arguments will be postponed. It will be 32 | executed as soon as the Jobrunner has a free bucket, which can be 33 | instantaneous if no other job is running. 34 | 35 | Once all the jobs have finished, the grouper will be marked as finished. 36 | -------------------------------------------------------------------------------- /queue_job_batch/readme/USAGE.md: -------------------------------------------------------------------------------- 1 | You can manage your batch jobs from the Systray. A new button will be 2 | shown with your currently executing job batches and the recently 3 | finished job groups. 4 | -------------------------------------------------------------------------------- /queue_job_batch/security/ir.model.access.csv: -------------------------------------------------------------------------------- 1 | id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink 2 | access_queue_job_batch_user,queue job manager,model_queue_job_batch,group_queue_job_batch_user,1,1,0,0 3 | access_queue_job_batch_manager,queue job manager,model_queue_job_batch,queue_job.group_queue_job_manager,1,1,1,1 4 | access_queue_job_queue_job_batch_user,queue job manager,queue_job.model_queue_job,group_queue_job_batch_user,1,0,0,0 5 | -------------------------------------------------------------------------------- /queue_job_batch/security/security.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Job Queue Batch User 6 | 7 | 8 | 9 | 13 | 14 | 15 | 16 | 17 | 18 | Job Queue batch multi-company 19 | 20 | 21 | ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] 24 | 25 | 26 | 27 | Job Queue batch user filter 28 | 29 | 33 | [('user_id', '=', user.id)] 34 | 35 | 36 | 37 | Job Queue batch manager 38 | 39 | 43 | [(1, '=', 1)] 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /queue_job_batch/static/description/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OCA/queue/fe0bba38a93847d90486e9c89dfb4df55d9d5811/queue_job_batch/static/description/icon.png -------------------------------------------------------------------------------- /queue_job_batch/static/src/MailCoreWeb.esm.js: -------------------------------------------------------------------------------- 1 | import {MailCoreWeb} from "@mail/core/web/mail_core_web_service"; 2 | import {patch} from "@web/core/utils/patch"; 3 | 4 | patch(MailCoreWeb.prototype, { 5 | setup() { 6 | super.setup(); 7 | this.busService.subscribe( 8 | "queue.job.batch/updated", 9 | (payload, {id: notifId}) => { 10 | if ( 11 | payload.batch_created && 12 | notifId > this.store.queueJobBatchCounterBusId 13 | ) { 14 | this.store.queueJobBatchCounter++; 15 | } 16 | if ( 17 | payload.batch_read && 18 | notifId > this.store.queueJobBatchCounterBusId 19 | ) { 20 | this.store.queueJobBatchCounter--; 21 | } 22 | } 23 | ); 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /queue_job_batch/static/src/QueueJobBatchModel.esm.js: -------------------------------------------------------------------------------- 1 | import {Record} from "@mail/core/common/record"; 2 | import {_t} from "@web/core/l10n/translation"; 3 | 4 | export class QueueJobBatch extends Record { 5 | static id = "id"; 6 | 7 | /** @type {Object.} */ 8 | static records = {}; 9 | 10 | /** @returns {QueueJobBatch} */ 11 | static get() { 12 | return super.get(...arguments); 13 | } 14 | 15 | /** @returns {QueueJobBatch|QueueJobBatch[]} */ 16 | static insert() { 17 | return super.insert(...arguments); 18 | } 19 | 20 | /** @type {Number} */ 21 | id; 22 | 23 | /** @type {String} */ 24 | name; 25 | 26 | /** @type {Number} */ 27 | job_count; 28 | 29 | /** @type {Number} */ 30 | completeness; 31 | 32 | /** @type {Number} */ 33 | failed_percentage; 34 | 35 | /** @type {Number} */ 36 | finished_job_count; 37 | 38 | /** @type {Number} */ 39 | failed_job_count; 40 | 41 | /** @type {'pending'|'enqueued'|'progress'|'finished'} */ 42 | state; 43 | 44 | async open() { 45 | this.store.env.services.action.doAction( 46 | { 47 | type: "ir.actions.act_window", 48 | name: _t("Job Batch"), 49 | res_model: "queue.job.batch", 50 | view_mode: "form", 51 | views: [[false, "form"]], 52 | res_id: this.id, 53 | target: "current", 54 | }, 55 | { 56 | clearBreadcrumbs: true, 57 | } 58 | ); 59 | } 60 | 61 | async markAsRead() { 62 | await this.store.env.services.orm.silent.call("queue.job.batch", "set_read", [ 63 | [this.id], 64 | ]); 65 | this.delete(); 66 | } 67 | } 68 | 69 | QueueJobBatch.register(); 70 | -------------------------------------------------------------------------------- /queue_job_batch/static/src/Store.esm.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Camptocamp SA (https://www.camptocamp.com). 3 | License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). 4 | */ 5 | import {Store, pyToJsModels} from "@mail/core/common/store_service"; 6 | import {patch} from "@web/core/utils/patch"; 7 | 8 | pyToJsModels["queue.job.batch"] = "QueueJobBatch"; 9 | 10 | patch(Store.prototype, { 11 | hasQueueJobBatchUserGroup: false, 12 | queueJobBatchCounterBusId: 0, 13 | queueJobBatchCounter: 0, 14 | 15 | /** @override */ 16 | get initMessagingParams() { 17 | return { 18 | ...super.initMessagingParams, 19 | systray_get_queue_job_batches: true, 20 | }; 21 | }, 22 | }); 23 | -------------------------------------------------------------------------------- /queue_job_batch/static/src/components/QueueJobBatchMenu.scss: -------------------------------------------------------------------------------- 1 | .o-queue-job-batch-QueueJobBatchMenu { 2 | width: 350px; 3 | max-height: 400px; 4 | min-height: 50px; 5 | 6 | .o-queue-job-batch-QueueJobBatchMenu-item { 7 | &:hover { 8 | .o-queue-job-batch-QueueJobBatchMenu-markAsRead { 9 | visibility: visible; 10 | } 11 | } 12 | 13 | .o-queue-job-batch-QueueJobBatchMenu-markAsRead { 14 | visibility: hidden; 15 | 16 | &:hover { 17 | color: $success; 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /queue_job_batch/views/queue_job_views.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | queue.job.form 5 | queue.job 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | queue.job.search 16 | queue.job 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /queue_job_cron/__init__.py: -------------------------------------------------------------------------------- 1 | from . import models 2 | -------------------------------------------------------------------------------- /queue_job_cron/__manifest__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ACSONE SA/NV () 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) 3 | 4 | { 5 | "name": "Scheduled Actions as Queue Jobs", 6 | "version": "18.0.1.1.1", 7 | "author": "ACSONE SA/NV,Odoo Community Association (OCA)", 8 | "website": "https://github.com/OCA/queue", 9 | "license": "AGPL-3", 10 | "category": "Generic Modules", 11 | "depends": ["queue_job"], 12 | "data": ["data/data.xml", "views/ir_cron_view.xml"], 13 | "installable": True, 14 | } 15 | -------------------------------------------------------------------------------- /queue_job_cron/data/data.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ir_cron 5 | 6 | 7 | 8 | 9 | _compute_run_as_queue_job 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /queue_job_cron/i18n/de.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * queue_job_cron 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 12.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "PO-Revision-Date: 2019-07-12 14:43+0000\n" 10 | "Last-Translator: Maria Sparenberg \n" 11 | "Language-Team: none\n" 12 | "Language: de\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: \n" 16 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 17 | "X-Generator: Weblate 3.7.1\n" 18 | 19 | #. module: queue_job_cron 20 | #: model:ir.model.fields,help:queue_job_cron.field_ir_cron__no_parallel_queue_job_run 21 | msgid "" 22 | "Avoid parallel run. If the cron job is already running, the new one will be " 23 | "skipped. By default, odoo never runs the same cron job in parallel. This " 24 | "option is therefore set to True by default when job is run as a queue job." 25 | msgstr "" 26 | 27 | #. module: queue_job_cron 28 | #: model:ir.model.fields,field_description:queue_job_cron.field_ir_cron__channel_id 29 | msgid "Channel" 30 | msgstr "Kanal" 31 | 32 | #. module: queue_job_cron 33 | #: model:ir.model.fields,field_description:queue_job_cron.field_ir_cron__no_parallel_queue_job_run 34 | msgid "No Parallel Queue Job Run" 35 | msgstr "" 36 | 37 | #. module: queue_job_cron 38 | #: model:ir.model.fields,field_description:queue_job_cron.field_ir_cron__run_as_queue_job 39 | msgid "Run As Queue Job" 40 | msgstr "als Warteschlangen-Job ausführen" 41 | 42 | #. module: queue_job_cron 43 | #: model:ir.model,name:queue_job_cron.model_ir_cron 44 | msgid "Scheduled Actions" 45 | msgstr "Eingeplante Aktionen" 46 | 47 | #. module: queue_job_cron 48 | #: model:ir.model.fields,help:queue_job_cron.field_ir_cron__run_as_queue_job 49 | msgid "Specify if this cron should be ran as a queue job" 50 | msgstr "" 51 | "Bitte spezifizieren, ob der Cron-Job als Warteschlangen-Job ausgeführt " 52 | "werden soll." 53 | -------------------------------------------------------------------------------- /queue_job_cron/i18n/es.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * queue_job_cron 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 16.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "PO-Revision-Date: 2023-09-03 13:40+0000\n" 10 | "Last-Translator: kikopeiro \n" 11 | "Language-Team: none\n" 12 | "Language: es\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: \n" 16 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 17 | "X-Generator: Weblate 4.17\n" 18 | 19 | #. module: queue_job_cron 20 | #: model:ir.model.fields,help:queue_job_cron.field_ir_cron__no_parallel_queue_job_run 21 | msgid "" 22 | "Avoid parallel run. If the cron job is already running, the new one will be " 23 | "skipped. By default, odoo never runs the same cron job in parallel. This " 24 | "option is therefore set to True by default when job is run as a queue job." 25 | msgstr "" 26 | 27 | #. module: queue_job_cron 28 | #: model:ir.model.fields,field_description:queue_job_cron.field_ir_cron__channel_id 29 | msgid "Channel" 30 | msgstr "Canal" 31 | 32 | #. module: queue_job_cron 33 | #: model:ir.model.fields,field_description:queue_job_cron.field_ir_cron__no_parallel_queue_job_run 34 | msgid "No Parallel Queue Job Run" 35 | msgstr "" 36 | 37 | #. module: queue_job_cron 38 | #: model:ir.model.fields,field_description:queue_job_cron.field_ir_cron__run_as_queue_job 39 | msgid "Run As Queue Job" 40 | msgstr "Ejecutar como trabajo en cola" 41 | 42 | #. module: queue_job_cron 43 | #: model:ir.model,name:queue_job_cron.model_ir_cron 44 | msgid "Scheduled Actions" 45 | msgstr "Acciones planificadas" 46 | 47 | #. module: queue_job_cron 48 | #: model:ir.model.fields,help:queue_job_cron.field_ir_cron__run_as_queue_job 49 | msgid "Specify if this cron should be ran as a queue job" 50 | msgstr "" 51 | "Especifica si esta acción planificada deberá ser ejecutada como un trabajo " 52 | "en cola" 53 | -------------------------------------------------------------------------------- /queue_job_cron/i18n/it.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * queue_job_cron 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 17.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "PO-Revision-Date: 2024-10-21 09:06+0000\n" 10 | "Last-Translator: mymage \n" 11 | "Language-Team: none\n" 12 | "Language: it\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: \n" 16 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 17 | "X-Generator: Weblate 5.6.2\n" 18 | 19 | #. module: queue_job_cron 20 | #: model:ir.model.fields,help:queue_job_cron.field_ir_cron__no_parallel_queue_job_run 21 | msgid "" 22 | "Avoid parallel run. If the cron job is already running, the new one will be " 23 | "skipped. By default, odoo never runs the same cron job in parallel. This " 24 | "option is therefore set to True by default when job is run as a queue job." 25 | msgstr "" 26 | "Evita esecuzioni parallele. Se il lavoro del cron è in esecuzione, il nuovo " 27 | "verrà saltato. In modo predefinito, Odoo non esegue lo stesso lavoro cron in " 28 | "parallelo. Di conseguenza questa opzione è impostata a True in modo " 29 | "predefinito quando un lavoro è eseguito come un lavoro in coda." 30 | 31 | #. module: queue_job_cron 32 | #: model:ir.model.fields,field_description:queue_job_cron.field_ir_cron__channel_id 33 | msgid "Channel" 34 | msgstr "Canale" 35 | 36 | #. module: queue_job_cron 37 | #: model:ir.model.fields,field_description:queue_job_cron.field_ir_cron__no_parallel_queue_job_run 38 | msgid "No Parallel Queue Job Run" 39 | msgstr "Nessun lavoro in coda parallelo in esecuzione" 40 | 41 | #. module: queue_job_cron 42 | #: model:ir.model.fields,field_description:queue_job_cron.field_ir_cron__run_as_queue_job 43 | msgid "Run As Queue Job" 44 | msgstr "Eseguire come lavoro in coda" 45 | 46 | #. module: queue_job_cron 47 | #: model:ir.model,name:queue_job_cron.model_ir_cron 48 | msgid "Scheduled Actions" 49 | msgstr "Azioni pianificate" 50 | 51 | #. module: queue_job_cron 52 | #: model:ir.model.fields,help:queue_job_cron.field_ir_cron__run_as_queue_job 53 | msgid "Specify if this cron should be ran as a queue job" 54 | msgstr "Indica se questo cron deve essere eseguito come un lavoro in coda" 55 | -------------------------------------------------------------------------------- /queue_job_cron/i18n/queue_job_cron.pot: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * queue_job_cron 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 18.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "Last-Translator: \n" 10 | "Language-Team: \n" 11 | "MIME-Version: 1.0\n" 12 | "Content-Type: text/plain; charset=UTF-8\n" 13 | "Content-Transfer-Encoding: \n" 14 | "Plural-Forms: \n" 15 | 16 | #. module: queue_job_cron 17 | #: model:ir.model.fields,help:queue_job_cron.field_ir_cron__no_parallel_queue_job_run 18 | msgid "" 19 | "Avoid parallel run. If the cron job is already running, the new one will be " 20 | "skipped. By default, odoo never runs the same cron job in parallel. This " 21 | "option is therefore set to True by default when job is run as a queue job." 22 | msgstr "" 23 | 24 | #. module: queue_job_cron 25 | #: model:ir.model.fields,field_description:queue_job_cron.field_ir_cron__channel_id 26 | msgid "Channel" 27 | msgstr "" 28 | 29 | #. module: queue_job_cron 30 | #: model:ir.model.fields,field_description:queue_job_cron.field_ir_cron__no_parallel_queue_job_run 31 | msgid "No Parallel Queue Job Run" 32 | msgstr "" 33 | 34 | #. module: queue_job_cron 35 | #: model:ir.model.fields,field_description:queue_job_cron.field_ir_cron__run_as_queue_job 36 | msgid "Run As Queue Job" 37 | msgstr "" 38 | 39 | #. module: queue_job_cron 40 | #: model:ir.model,name:queue_job_cron.model_ir_cron 41 | msgid "Scheduled Actions" 42 | msgstr "" 43 | 44 | #. module: queue_job_cron 45 | #: model:ir.model.fields,help:queue_job_cron.field_ir_cron__run_as_queue_job 46 | msgid "Specify if this cron should be ran as a queue job" 47 | msgstr "" 48 | -------------------------------------------------------------------------------- /queue_job_cron/i18n/zh_CN.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * queue_job_cron 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 12.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "PO-Revision-Date: 2019-07-25 17:43+0000\n" 10 | "Last-Translator: 黎伟杰 <674416404@qq.com>\n" 11 | "Language-Team: none\n" 12 | "Language: zh_CN\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: \n" 16 | "Plural-Forms: nplurals=1; plural=0;\n" 17 | "X-Generator: Weblate 3.7.1\n" 18 | 19 | #. module: queue_job_cron 20 | #: model:ir.model.fields,help:queue_job_cron.field_ir_cron__no_parallel_queue_job_run 21 | msgid "" 22 | "Avoid parallel run. If the cron job is already running, the new one will be " 23 | "skipped. By default, odoo never runs the same cron job in parallel. This " 24 | "option is therefore set to True by default when job is run as a queue job." 25 | msgstr "" 26 | 27 | #. module: queue_job_cron 28 | #: model:ir.model.fields,field_description:queue_job_cron.field_ir_cron__channel_id 29 | msgid "Channel" 30 | msgstr "频道" 31 | 32 | #. module: queue_job_cron 33 | #: model:ir.model.fields,field_description:queue_job_cron.field_ir_cron__no_parallel_queue_job_run 34 | msgid "No Parallel Queue Job Run" 35 | msgstr "" 36 | 37 | #. module: queue_job_cron 38 | #: model:ir.model.fields,field_description:queue_job_cron.field_ir_cron__run_as_queue_job 39 | msgid "Run As Queue Job" 40 | msgstr "作为队列作业运行" 41 | 42 | #. module: queue_job_cron 43 | #: model:ir.model,name:queue_job_cron.model_ir_cron 44 | msgid "Scheduled Actions" 45 | msgstr "安排的动作" 46 | 47 | #. module: queue_job_cron 48 | #: model:ir.model.fields,help:queue_job_cron.field_ir_cron__run_as_queue_job 49 | msgid "Specify if this cron should be ran as a queue job" 50 | msgstr "指定此cron是否应作为队列作业运行" 51 | -------------------------------------------------------------------------------- /queue_job_cron/models/__init__.py: -------------------------------------------------------------------------------- 1 | from . import ir_cron 2 | -------------------------------------------------------------------------------- /queue_job_cron/models/ir_cron.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ACSONE SA/NV () 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) 3 | import logging 4 | 5 | from odoo import api, fields, models 6 | 7 | from odoo.addons.queue_job.job import identity_exact 8 | 9 | _logger = logging.getLogger(__name__) 10 | 11 | 12 | class IrCron(models.Model): 13 | _inherit = "ir.cron" 14 | 15 | no_parallel_queue_job_run = fields.Boolean( 16 | help="Avoid parallel run. " 17 | "If the cron job is already running, the new one will be skipped. " 18 | "By default, odoo never runs the same cron job in parallel. This " 19 | "option is therefore set to True by default when job is run as a " 20 | "queue job.", 21 | default=True, 22 | ) 23 | 24 | run_as_queue_job = fields.Boolean( 25 | help="Specify if this cron should be ran as a queue job" 26 | ) 27 | channel_id = fields.Many2one( 28 | comodel_name="queue.job.channel", 29 | compute="_compute_run_as_queue_job", 30 | readonly=False, 31 | store=True, 32 | string="Channel", 33 | ) 34 | 35 | @api.depends("run_as_queue_job") 36 | def _compute_run_as_queue_job(self): 37 | for cron in self: 38 | if cron.channel_id: 39 | continue 40 | if cron.run_as_queue_job: 41 | cron.channel_id = self.env.ref("queue_job_cron.channel_root_ir_cron").id 42 | else: 43 | cron.channel_id = False 44 | 45 | def _run_job_as_queue_job(self, server_action): 46 | return server_action.run() 47 | 48 | def method_direct_trigger(self): 49 | for cron in self: 50 | if not cron.run_as_queue_job: 51 | super(IrCron, cron).method_direct_trigger() 52 | else: 53 | _cron = cron.with_user(cron.user_id).with_context( 54 | lastcall=cron.lastcall 55 | ) 56 | _cron._delay_run_job_as_queue_job( 57 | server_action=_cron.ir_actions_server_id 58 | ) 59 | return True 60 | 61 | def _callback(self, cron_name, server_action_id): 62 | self.ensure_one() 63 | if self.run_as_queue_job: 64 | server_action = self.env["ir.actions.server"].browse(server_action_id) 65 | return self._delay_run_job_as_queue_job(server_action=server_action) 66 | else: 67 | return super()._callback( 68 | cron_name=cron_name, server_action_id=server_action_id 69 | ) 70 | 71 | def _delay_run_job_as_queue_job(self, server_action): 72 | self.ensure_one() 73 | identity_key = None 74 | if self.no_parallel_queue_job_run: 75 | identity_key = identity_exact 76 | return self.with_delay( 77 | priority=self.priority, 78 | description=self.name, 79 | channel=self.channel_id.complete_name, 80 | identity_key=identity_key, 81 | )._run_job_as_queue_job(server_action=server_action) 82 | -------------------------------------------------------------------------------- /queue_job_cron/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["whool"] 3 | build-backend = "whool.buildapi" 4 | -------------------------------------------------------------------------------- /queue_job_cron/readme/CONFIGURATION.md: -------------------------------------------------------------------------------- 1 | To configure this module, you need to: 2 | 3 | #. Nothing special to do. 4 | -------------------------------------------------------------------------------- /queue_job_cron/readme/CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | - Cédric Pigeon \<\> 2 | - Nguyen Minh Chien \<\> 3 | - Tran Quoc duong \<\> 4 | - Vo Hong Thien \<\> 5 | -------------------------------------------------------------------------------- /queue_job_cron/readme/CREDITS.md: -------------------------------------------------------------------------------- 1 | The migration of this module from 17.0 to 18.0 was financially supported by Camptocamp. 2 | -------------------------------------------------------------------------------- /queue_job_cron/readme/DESCRIPTION.md: -------------------------------------------------------------------------------- 1 | This module extends the functionality of queue_job and allows to run an 2 | Odoo cron as a queue job. 3 | -------------------------------------------------------------------------------- /queue_job_cron/readme/HISTORY.md: -------------------------------------------------------------------------------- 1 | ## 18.0.1.1.0 (2025-01-16) 2 | 3 | ### Features 4 | 5 | - By default prevent parallel run of the same cron job when run as queue job. 6 | 7 | When a cron job is run by odoo, the odoo runner will prevent parallel run 8 | of the same cron job. Before this change, this was not the case when the 9 | cron job was run as a queue job. A new option is added to the cron job when 10 | run as a queue job to prevent parallel run. This option is set to True by 11 | default. In this way, the behavior is now the same as when the cron job is run 12 | by odoo but you keep the possibility to disable this restriction when run as 13 | a queue job. ([#612](https://github.com/OCA/queue/issues/612)) 14 | -------------------------------------------------------------------------------- /queue_job_cron/readme/INSTALL.md: -------------------------------------------------------------------------------- 1 | To install this module, you need to: 2 | 3 | 1. Just install it. 4 | -------------------------------------------------------------------------------- /queue_job_cron/readme/USAGE.md: -------------------------------------------------------------------------------- 1 | To use this module, you need to: 2 | 3 | #\. Go to a scheduled action, a flag "Run as queue job" will allow you 4 | to run the action as a queue job. You will also allowed to select a 5 | channel of its execution. To configure dedicated channels please refers 6 | to queue_job help: 7 | 8 | 9 | Channels can be used to manage sequential jobs and prevent concurrency 10 | accesses. To do that you just have to define a channel per cron limited 11 | to 1 at time. 12 | -------------------------------------------------------------------------------- /queue_job_cron/readme/newsfragments/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OCA/queue/fe0bba38a93847d90486e9c89dfb4df55d9d5811/queue_job_cron/readme/newsfragments/.gitignore -------------------------------------------------------------------------------- /queue_job_cron/static/description/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OCA/queue/fe0bba38a93847d90486e9c89dfb4df55d9d5811/queue_job_cron/static/description/icon.png -------------------------------------------------------------------------------- /queue_job_cron/tests/__init__.py: -------------------------------------------------------------------------------- 1 | from . import test_queue_job_cron 2 | -------------------------------------------------------------------------------- /queue_job_cron/views/ir_cron_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ir.cron.view.form (queue_job_cron) 5 | ir.cron 6 | 7 | 8 | 9 | 10 | 14 | 19 | 20 | 21 | 22 | 23 | ir.cron.view.tree (queue_job_cron) 24 | ir.cron 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | ir.cron.view.search (queue_job_cron) 34 | ir.cron 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /queue_job_cron_jobrunner/__init__.py: -------------------------------------------------------------------------------- 1 | from . import models 2 | -------------------------------------------------------------------------------- /queue_job_cron_jobrunner/__manifest__.py: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Queue Job Cron Jobrunner", 3 | "summary": "Run jobs without a dedicated JobRunner", 4 | "version": "18.0.1.0.0", 5 | "development_status": "Alpha", 6 | "author": "Camptocamp SA, Odoo Community Association (OCA)", 7 | "maintainers": ["ivantodorovich"], 8 | "website": "https://github.com/OCA/queue", 9 | "license": "AGPL-3", 10 | "category": "Others", 11 | "depends": ["queue_job"], 12 | "data": [ 13 | "data/ir_cron.xml", 14 | "views/ir_cron.xml", 15 | ], 16 | "installable": True, 17 | } 18 | -------------------------------------------------------------------------------- /queue_job_cron_jobrunner/data/ir_cron.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Queue Job Runner 5 | 6 | code 7 | model._job_runner() 8 | 9 | 10 | 1 11 | days 12 | 13 | 14 | -------------------------------------------------------------------------------- /queue_job_cron_jobrunner/i18n/it.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * queue_job_cron_jobrunner 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 17.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "PO-Revision-Date: 2024-02-12 11:39+0000\n" 10 | "Last-Translator: mymage \n" 11 | "Language-Team: none\n" 12 | "Language: it\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: \n" 16 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 17 | "X-Generator: Weblate 4.17\n" 18 | 19 | #. module: queue_job_cron_jobrunner 20 | #: model:ir.model.fields,help:queue_job_cron_jobrunner.field_ir_cron__queue_job_runner 21 | msgid "If checked, the cron is considered to be a queue.job runner." 22 | msgstr "Se selezionata, il cron è considerato un esecutore del queue.job." 23 | 24 | #. module: queue_job_cron_jobrunner 25 | #. odoo-python 26 | #: code:addons/queue_job_cron_jobrunner/models/queue_job.py:0 27 | #, python-format 28 | msgid "Job interrupted and set to Done: nothing to do." 29 | msgstr "Lavoro interrotto e impostato a completato: nulla da fare." 30 | 31 | #. module: queue_job_cron_jobrunner 32 | #: model:ir.model,name:queue_job_cron_jobrunner.model_queue_job 33 | msgid "Queue Job" 34 | msgstr "Lavoro in coda" 35 | 36 | #. module: queue_job_cron_jobrunner 37 | #: model:ir.actions.server,name:queue_job_cron_jobrunner.queue_job_cron_ir_actions_server 38 | #: model:ir.model.fields,field_description:queue_job_cron_jobrunner.field_ir_cron__queue_job_runner 39 | msgid "Queue Job Runner" 40 | msgstr "Esecutore lavoro in coda" 41 | 42 | #. module: queue_job_cron_jobrunner 43 | #: model:ir.model,name:queue_job_cron_jobrunner.model_ir_cron 44 | msgid "Scheduled Actions" 45 | msgstr "Azioni pianificate" 46 | -------------------------------------------------------------------------------- /queue_job_cron_jobrunner/i18n/queue_job_cron_jobrunner.pot: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * queue_job_cron_jobrunner 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 18.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "Last-Translator: \n" 10 | "Language-Team: \n" 11 | "MIME-Version: 1.0\n" 12 | "Content-Type: text/plain; charset=UTF-8\n" 13 | "Content-Transfer-Encoding: \n" 14 | "Plural-Forms: \n" 15 | 16 | #. module: queue_job_cron_jobrunner 17 | #: model:ir.model.fields,help:queue_job_cron_jobrunner.field_ir_cron__queue_job_runner 18 | msgid "If checked, the cron is considered to be a queue.job runner." 19 | msgstr "" 20 | 21 | #. module: queue_job_cron_jobrunner 22 | #: model:ir.model,name:queue_job_cron_jobrunner.model_queue_job 23 | msgid "Queue Job" 24 | msgstr "" 25 | 26 | #. module: queue_job_cron_jobrunner 27 | #: model:ir.actions.server,name:queue_job_cron_jobrunner.queue_job_cron_ir_actions_server 28 | #: model:ir.model.fields,field_description:queue_job_cron_jobrunner.field_ir_cron__queue_job_runner 29 | msgid "Queue Job Runner" 30 | msgstr "" 31 | 32 | #. module: queue_job_cron_jobrunner 33 | #: model:ir.model,name:queue_job_cron_jobrunner.model_ir_cron 34 | msgid "Scheduled Actions" 35 | msgstr "" 36 | -------------------------------------------------------------------------------- /queue_job_cron_jobrunner/i18n/ro.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * queue_job_cron_jobrunner 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 16.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "PO-Revision-Date: 2022-11-23 09:45+0000\n" 10 | "Last-Translator: Dorin Hongu \n" 11 | "Language-Team: none\n" 12 | "Language: ro\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: \n" 16 | "Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < " 17 | "20)) ? 1 : 2;\n" 18 | "X-Generator: Weblate 4.14.1\n" 19 | 20 | #. module: queue_job_cron_jobrunner 21 | #: model:ir.model.fields,help:queue_job_cron_jobrunner.field_ir_cron__queue_job_runner 22 | msgid "If checked, the cron is considered to be a queue.job runner." 23 | msgstr "Dacă este bifat, cron-ul este considerat a fi un runner queue.job." 24 | 25 | #. module: queue_job_cron_jobrunner 26 | #. odoo-python 27 | #: code:addons/queue_job_cron_jobrunner/models/queue_job.py:0 28 | #, python-format 29 | msgid "Job interrupted and set to Done: nothing to do." 30 | msgstr "Job întrerupt și setat la Terminat: nimic de făcut." 31 | 32 | #. module: queue_job_cron_jobrunner 33 | #: model:ir.model,name:queue_job_cron_jobrunner.model_queue_job 34 | msgid "Queue Job" 35 | msgstr "Coadă sarcini" 36 | 37 | #. module: queue_job_cron_jobrunner 38 | #: model:ir.actions.server,name:queue_job_cron_jobrunner.queue_job_cron_ir_actions_server 39 | #: model:ir.model.fields,field_description:queue_job_cron_jobrunner.field_ir_cron__queue_job_runner 40 | msgid "Queue Job Runner" 41 | msgstr "" 42 | 43 | #. module: queue_job_cron_jobrunner 44 | #: model:ir.model,name:queue_job_cron_jobrunner.model_ir_cron 45 | msgid "Scheduled Actions" 46 | msgstr "" 47 | -------------------------------------------------------------------------------- /queue_job_cron_jobrunner/i18n/zh_CN.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * queue_job_cron_jobrunner 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 17.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "PO-Revision-Date: 2024-07-02 09:47+0000\n" 10 | "Last-Translator: xtanuiha \n" 11 | "Language-Team: none\n" 12 | "Language: zh_CN\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: \n" 16 | "Plural-Forms: nplurals=1; plural=0;\n" 17 | "X-Generator: Weblate 4.17\n" 18 | 19 | #. module: queue_job_cron_jobrunner 20 | #: model:ir.model.fields,help:queue_job_cron_jobrunner.field_ir_cron__queue_job_runner 21 | msgid "If checked, the cron is considered to be a queue.job runner." 22 | msgstr "如果选中,cron任务将被视为队列.job的执行器。" 23 | 24 | #. module: queue_job_cron_jobrunner 25 | #. odoo-python 26 | #: code:addons/queue_job_cron_jobrunner/models/queue_job.py:0 27 | #, python-format 28 | msgid "Job interrupted and set to Done: nothing to do." 29 | msgstr "任务中断并设置为已完成:无需执行任何操作。" 30 | 31 | #. module: queue_job_cron_jobrunner 32 | #: model:ir.model,name:queue_job_cron_jobrunner.model_queue_job 33 | msgid "Queue Job" 34 | msgstr "队列任务" 35 | 36 | #. module: queue_job_cron_jobrunner 37 | #: model:ir.actions.server,name:queue_job_cron_jobrunner.queue_job_cron_ir_actions_server 38 | #: model:ir.model.fields,field_description:queue_job_cron_jobrunner.field_ir_cron__queue_job_runner 39 | msgid "Queue Job Runner" 40 | msgstr "队列任务执行器" 41 | 42 | #. module: queue_job_cron_jobrunner 43 | #: model:ir.model,name:queue_job_cron_jobrunner.model_ir_cron 44 | msgid "Scheduled Actions" 45 | msgstr "计划的动作" 46 | -------------------------------------------------------------------------------- /queue_job_cron_jobrunner/models/__init__.py: -------------------------------------------------------------------------------- 1 | from . import ir_cron 2 | from . import queue_job 3 | -------------------------------------------------------------------------------- /queue_job_cron_jobrunner/models/ir_cron.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Camptocamp SA (https://www.camptocamp.com). 2 | # @author Iván Todorovich 3 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). 4 | 5 | from odoo import fields, models 6 | 7 | 8 | class IrCron(models.Model): 9 | _inherit = "ir.cron" 10 | 11 | queue_job_runner = fields.Boolean( 12 | help="If checked, the cron is considered to be a queue.job runner.", 13 | ) 14 | -------------------------------------------------------------------------------- /queue_job_cron_jobrunner/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["whool"] 3 | build-backend = "whool.buildapi" 4 | -------------------------------------------------------------------------------- /queue_job_cron_jobrunner/readme/CONFIGURE.md: -------------------------------------------------------------------------------- 1 | Warning 2 | 3 | Don't use this module if you're already running the regular `queue_job` 4 | runner. 5 | 6 | For the easiest case, no configuration is required besides installing 7 | the module. 8 | 9 | To avoid CronWorker CPU timeout from abruptly stopping the job 10 | processing cron, it's recommended to launch Odoo with 11 | `--limit-time-real-cron=0`, to disable the CronWorker timeout 12 | altogether. 13 | 14 | Note 15 | 16 | In Odoo.sh, this is done by default. 17 | 18 | Parallel execution of jobs can be achieved by leveraging multiple 19 | `ir.cron` records: 20 | 21 | - Make sure you have enough CronWorkers available (Odoo CLI 22 | `--max-cron-threads`) 23 | - Duplicate the `queue_job_cron` cron record as many times as needed, 24 | until you have as much records as cron workers. 25 | -------------------------------------------------------------------------------- /queue_job_cron_jobrunner/readme/CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | - [Camptocamp](https://www.camptocamp.com) 2 | 3 | > - Iván Todorovich \<\> 4 | -------------------------------------------------------------------------------- /queue_job_cron_jobrunner/readme/DESCRIPTION.md: -------------------------------------------------------------------------------- 1 | This module implements a simple `queue.job` runner using `ir.cron` 2 | triggers. 3 | 4 | It's meant to be used on environments where the regular job runner can't 5 | be run, like on Odoo.sh. 6 | 7 | Unlike the regular job runner, where jobs are dispatched to the 8 | HttpWorkers, jobs are processed on the CronWorker threads by the job 9 | runner crons. This is a design decision because: 10 | 11 | - Odoo.sh puts HttpWorkers to sleep when there's no network activity 12 | - HttpWorkers are meant for traffic. Users shouldn't pay the price of 13 | background tasks. 14 | 15 | For now, it only implements the most basic features of the `queue_job` 16 | runner, notably no channel capacity nor priorities. Please check the 17 | ROADMAP for further details. 18 | -------------------------------------------------------------------------------- /queue_job_cron_jobrunner/readme/ROADMAP.md: -------------------------------------------------------------------------------- 1 | - Support channel capacity and priority. (See `_acquire_one_job`) 2 | - Gracefully handle CronWorker CPU timeouts. (See `_job_runner`) 3 | - Commit transaction after job state updated to started. (See 4 | `_process`) 5 | -------------------------------------------------------------------------------- /queue_job_cron_jobrunner/static/description/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OCA/queue/fe0bba38a93847d90486e9c89dfb4df55d9d5811/queue_job_cron_jobrunner/static/description/icon.png -------------------------------------------------------------------------------- /queue_job_cron_jobrunner/tests/__init__.py: -------------------------------------------------------------------------------- 1 | from . import test_queue_job 2 | -------------------------------------------------------------------------------- /queue_job_cron_jobrunner/tests/test_queue_job.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Camptocamp SA (https://www.camptocamp.com). 2 | # @author Iván Todorovich 3 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). 4 | 5 | from datetime import timedelta 6 | 7 | from freezegun import freeze_time 8 | 9 | from odoo import fields 10 | from odoo.tests.common import TransactionCase 11 | from odoo.tools import mute_logger 12 | 13 | 14 | class TestQueueJob(TransactionCase): 15 | @classmethod 16 | def setUpClass(cls): 17 | super().setUpClass() 18 | cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) 19 | cls.cron = cls.env.ref("queue_job_cron_jobrunner.queue_job_cron") 20 | # Cleanup triggers just in case 21 | cls.env["ir.cron.trigger"].search([]).unlink() 22 | 23 | def assertTriggerAt(self, at, message=None): 24 | """Ensures a cron trigger is created at the given time""" 25 | return self.assertTrue( 26 | self.env["ir.cron.trigger"].search([("call_at", "=", at)]), 27 | message, 28 | ) 29 | 30 | @freeze_time("2022-02-22 22:22:22") 31 | def test_queue_job_cron_trigger(self): 32 | """Test that ir.cron triggers are created for every queue.job""" 33 | job = self.env["res.partner"].with_delay().create({"name": "test"}) 34 | job_record = job.db_record() 35 | self.assertTriggerAt(fields.Datetime.now(), "Trigger should've been created") 36 | job_record.eta = fields.Datetime.now() + timedelta(hours=1) 37 | self.assertTriggerAt(job_record.eta, "A new trigger should've been created") 38 | 39 | @mute_logger("odoo.addons.queue_job_cron_jobrunner.models.queue_job") 40 | def test_queue_job_process(self): 41 | """Test that jobs are processed by the queue job cron""" 42 | # Create some jobs 43 | job1 = self.env["res.partner"].with_delay().create({"name": "test"}) 44 | job1_record = job1.db_record() 45 | job2 = self.env["res.partner"].with_delay().create(False) 46 | job2_record = job2.db_record() 47 | job3 = self.env["res.partner"].with_delay(eta=3600).create({"name": "Test"}) 48 | job3_record = job3.db_record() 49 | # Run the job processing cron 50 | self.env["queue.job"]._job_runner(commit=False) 51 | # Check that the jobs were processed 52 | self.assertEqual(job1_record.state, "done", "Processed OK") 53 | self.assertEqual(job2_record.state, "failed", "Has errors") 54 | self.assertEqual(job3_record.state, "pending", "Still pending, because of eta") 55 | 56 | @freeze_time("2022-02-22 22:22:22") 57 | def test_queue_job_cron_trigger_enqueue_dependencies(self): 58 | """Test that ir.cron execution enqueue waiting dependencies""" 59 | delayable = self.env["res.partner"].delayable().create({"name": "test"}) 60 | delayable2 = self.env["res.partner"].delayable().create({"name": "test2"}) 61 | delayable.on_done(delayable2) 62 | delayable.delay() 63 | job_record = delayable._generated_job.db_record() 64 | job_record_depends = delayable2._generated_job.db_record() 65 | 66 | self.env["queue.job"]._job_runner(commit=False) 67 | 68 | self.assertEqual(job_record.state, "done", "Processed OK") 69 | # if the state is "waiting_dependencies", it means the "enqueue_waiting()" 70 | # step has not been doen when the parent job has been done 71 | self.assertEqual(job_record_depends.state, "done", "Processed OK") 72 | -------------------------------------------------------------------------------- /queue_job_cron_jobrunner/views/ir_cron.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | ir.cron 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /queue_job_subscribe/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Cédric Pigeon 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | from . import models 4 | -------------------------------------------------------------------------------- /queue_job_subscribe/__manifest__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Cédric Pigeon 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | { 4 | "name": "Queue Job Subscribe", 5 | "version": "18.0.1.0.0", 6 | "author": "Acsone SA/NV, Odoo Community Association (OCA)", 7 | "website": "https://github.com/OCA/queue", 8 | "summary": "Control which users are subscribed to queue job notifications", 9 | "license": "AGPL-3", 10 | "category": "Generic Modules", 11 | "depends": ["queue_job"], 12 | "data": ["views/res_users_view.xml"], 13 | "installable": True, 14 | } 15 | -------------------------------------------------------------------------------- /queue_job_subscribe/i18n/ar.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * connector_job_subscribe 4 | # 5 | # Translators: 6 | # OCA Transbot , 2017 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: Odoo Server 9.0c\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2017-04-05 00:42+0000\n" 12 | "PO-Revision-Date: 2017-04-05 00:42+0000\n" 13 | "Last-Translator: OCA Transbot , 2017\n" 14 | "Language-Team: Arabic (https://www.transifex.com/oca/teams/23907/ar/)\n" 15 | "Language: ar\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: \n" 19 | "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " 20 | "&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" 21 | 22 | #. module: queue_job_subscribe 23 | #: model_terms:ir.ui.view,arch_db:queue_job_subscribe.view_user_connector_form 24 | msgid "Connectors" 25 | msgstr "" 26 | 27 | #. module: queue_job_subscribe 28 | #: model:ir.model.fields,help:queue_job_subscribe.field_res_users__subscribe_job 29 | msgid "" 30 | "If this flag is checked and the user is Connector Manager, he will receive " 31 | "job notifications." 32 | msgstr "" 33 | 34 | #. module: queue_job_subscribe 35 | #: model:ir.model.fields,field_description:queue_job_subscribe.field_res_users__subscribe_job 36 | msgid "Job Notifications" 37 | msgstr "" 38 | 39 | #. module: queue_job_subscribe 40 | #: model:ir.model,name:queue_job_subscribe.model_queue_job 41 | msgid "Queue Job" 42 | msgstr "" 43 | 44 | #. module: queue_job_subscribe 45 | #: model:ir.model,name:queue_job_subscribe.model_res_users 46 | msgid "User" 47 | msgstr "" 48 | 49 | #~ msgid "Users" 50 | #~ msgstr "المستخدمون" 51 | -------------------------------------------------------------------------------- /queue_job_subscribe/i18n/bg.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * connector_job_subscribe 4 | # 5 | # Translators: 6 | # Kaloyan Naumov , 2017 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: Odoo Server 9.0c\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2017-04-05 00:42+0000\n" 12 | "PO-Revision-Date: 2017-04-05 00:42+0000\n" 13 | "Last-Translator: Kaloyan Naumov , 2017\n" 14 | "Language-Team: Bulgarian (https://www.transifex.com/oca/teams/23907/bg/)\n" 15 | "Language: bg\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: \n" 19 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 20 | 21 | #. module: queue_job_subscribe 22 | #: model_terms:ir.ui.view,arch_db:queue_job_subscribe.view_user_connector_form 23 | msgid "Connectors" 24 | msgstr "Конектори" 25 | 26 | #. module: queue_job_subscribe 27 | #: model:ir.model.fields,help:queue_job_subscribe.field_res_users__subscribe_job 28 | msgid "" 29 | "If this flag is checked and the user is Connector Manager, he will receive " 30 | "job notifications." 31 | msgstr "" 32 | 33 | #. module: queue_job_subscribe 34 | #: model:ir.model.fields,field_description:queue_job_subscribe.field_res_users__subscribe_job 35 | msgid "Job Notifications" 36 | msgstr "" 37 | 38 | #. module: queue_job_subscribe 39 | #: model:ir.model,name:queue_job_subscribe.model_queue_job 40 | msgid "Queue Job" 41 | msgstr "" 42 | 43 | #. module: queue_job_subscribe 44 | #: model:ir.model,name:queue_job_subscribe.model_res_users 45 | msgid "User" 46 | msgstr "" 47 | -------------------------------------------------------------------------------- /queue_job_subscribe/i18n/ca.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * connector_job_subscribe 4 | # 5 | # Translators: 6 | # OCA Transbot , 2017 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: Odoo Server 9.0c\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2017-04-05 00:42+0000\n" 12 | "PO-Revision-Date: 2022-04-13 14:05+0000\n" 13 | "Last-Translator: Noel estudillo \n" 14 | "Language-Team: Catalan (https://www.transifex.com/oca/teams/23907/ca/)\n" 15 | "Language: ca\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: \n" 19 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 20 | "X-Generator: Weblate 4.3.2\n" 21 | 22 | #. module: queue_job_subscribe 23 | #: model_terms:ir.ui.view,arch_db:queue_job_subscribe.view_user_connector_form 24 | msgid "Connectors" 25 | msgstr "Connectors" 26 | 27 | #. module: queue_job_subscribe 28 | #: model:ir.model.fields,help:queue_job_subscribe.field_res_users__subscribe_job 29 | msgid "" 30 | "If this flag is checked and the user is Connector Manager, he will receive " 31 | "job notifications." 32 | msgstr "" 33 | "Si aquesta marca està marcada i l'usuari és Connector Manager, rebrà " 34 | "notificacions de feina." 35 | 36 | #. module: queue_job_subscribe 37 | #: model:ir.model.fields,field_description:queue_job_subscribe.field_res_users__subscribe_job 38 | msgid "Job Notifications" 39 | msgstr "Notificacions de feina" 40 | 41 | #. module: queue_job_subscribe 42 | #: model:ir.model,name:queue_job_subscribe.model_queue_job 43 | msgid "Queue Job" 44 | msgstr "Tasca de Cua" 45 | 46 | #. module: queue_job_subscribe 47 | #: model:ir.model,name:queue_job_subscribe.model_res_users 48 | msgid "User" 49 | msgstr "" 50 | 51 | #~ msgid "Users" 52 | #~ msgstr "Usuaris" 53 | -------------------------------------------------------------------------------- /queue_job_subscribe/i18n/da.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * connector_job_subscribe 4 | # 5 | # Translators: 6 | # OCA Transbot , 2017 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: Odoo Server 9.0c\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2017-04-05 00:42+0000\n" 12 | "PO-Revision-Date: 2017-04-05 00:42+0000\n" 13 | "Last-Translator: OCA Transbot , 2017\n" 14 | "Language-Team: Danish (https://www.transifex.com/oca/teams/23907/da/)\n" 15 | "Language: da\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: \n" 19 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 20 | 21 | #. module: queue_job_subscribe 22 | #: model_terms:ir.ui.view,arch_db:queue_job_subscribe.view_user_connector_form 23 | msgid "Connectors" 24 | msgstr "" 25 | 26 | #. module: queue_job_subscribe 27 | #: model:ir.model.fields,help:queue_job_subscribe.field_res_users__subscribe_job 28 | msgid "" 29 | "If this flag is checked and the user is Connector Manager, he will receive " 30 | "job notifications." 31 | msgstr "" 32 | 33 | #. module: queue_job_subscribe 34 | #: model:ir.model.fields,field_description:queue_job_subscribe.field_res_users__subscribe_job 35 | msgid "Job Notifications" 36 | msgstr "" 37 | 38 | #. module: queue_job_subscribe 39 | #: model:ir.model,name:queue_job_subscribe.model_queue_job 40 | msgid "Queue Job" 41 | msgstr "" 42 | 43 | #. module: queue_job_subscribe 44 | #: model:ir.model,name:queue_job_subscribe.model_res_users 45 | msgid "User" 46 | msgstr "" 47 | 48 | #~ msgid "Users" 49 | #~ msgstr "Brugere" 50 | -------------------------------------------------------------------------------- /queue_job_subscribe/i18n/de.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * connector_job_subscribe 4 | # 5 | # Translators: 6 | # OCA Transbot , 2017 7 | # Rudolf Schnapka , 2017 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Odoo Server 9.0c\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2017-06-24 00:51+0000\n" 13 | "PO-Revision-Date: 2020-07-22 12:20+0000\n" 14 | "Last-Translator: c2cdidier \n" 15 | "Language-Team: German (https://www.transifex.com/oca/teams/23907/de/)\n" 16 | "Language: de\n" 17 | "MIME-Version: 1.0\n" 18 | "Content-Type: text/plain; charset=UTF-8\n" 19 | "Content-Transfer-Encoding: \n" 20 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 21 | "X-Generator: Weblate 3.10\n" 22 | 23 | #. module: queue_job_subscribe 24 | #: model_terms:ir.ui.view,arch_db:queue_job_subscribe.view_user_connector_form 25 | msgid "Connectors" 26 | msgstr "Connector" 27 | 28 | #. module: queue_job_subscribe 29 | #: model:ir.model.fields,help:queue_job_subscribe.field_res_users__subscribe_job 30 | msgid "" 31 | "If this flag is checked and the user is Connector Manager, he will receive " 32 | "job notifications." 33 | msgstr "" 34 | "Wenn dieses Kennzeichen gesetzt ist und der Benutzer ist Verbindungsmanager, " 35 | "dann wird er Job-Hinweise erhalten." 36 | 37 | #. module: queue_job_subscribe 38 | #: model:ir.model.fields,field_description:queue_job_subscribe.field_res_users__subscribe_job 39 | msgid "Job Notifications" 40 | msgstr "Job Benachrichtigungen" 41 | 42 | #. module: queue_job_subscribe 43 | #: model:ir.model,name:queue_job_subscribe.model_queue_job 44 | msgid "Queue Job" 45 | msgstr "Warteschlange Job" 46 | 47 | #. module: queue_job_subscribe 48 | #: model:ir.model,name:queue_job_subscribe.model_res_users 49 | msgid "User" 50 | msgstr "" 51 | 52 | #~ msgid "Users" 53 | #~ msgstr "Benutzer" 54 | 55 | #~ msgid "Sale Pricelist" 56 | #~ msgstr "Verkaufspreisliste" 57 | 58 | #~ msgid "" 59 | #~ "This pricelist will be used, instead of the default one, for sales to the " 60 | #~ "current partner" 61 | #~ msgstr "" 62 | #~ "Diese Preisliste wird anstelle der Vorgabe bei Verkäufen an diesen " 63 | #~ "Partner verwendet" 64 | -------------------------------------------------------------------------------- /queue_job_subscribe/i18n/el_GR.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * connector_job_subscribe 4 | # 5 | # Translators: 6 | # OCA Transbot , 2017 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: Odoo Server 9.0c\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2017-04-05 00:42+0000\n" 12 | "PO-Revision-Date: 2017-04-05 00:42+0000\n" 13 | "Last-Translator: OCA Transbot , 2017\n" 14 | "Language-Team: Greek (Greece) (https://www.transifex.com/oca/teams/23907/" 15 | "el_GR/)\n" 16 | "Language: el_GR\n" 17 | "MIME-Version: 1.0\n" 18 | "Content-Type: text/plain; charset=UTF-8\n" 19 | "Content-Transfer-Encoding: \n" 20 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 21 | 22 | #. module: queue_job_subscribe 23 | #: model_terms:ir.ui.view,arch_db:queue_job_subscribe.view_user_connector_form 24 | msgid "Connectors" 25 | msgstr "" 26 | 27 | #. module: queue_job_subscribe 28 | #: model:ir.model.fields,help:queue_job_subscribe.field_res_users__subscribe_job 29 | msgid "" 30 | "If this flag is checked and the user is Connector Manager, he will receive " 31 | "job notifications." 32 | msgstr "" 33 | 34 | #. module: queue_job_subscribe 35 | #: model:ir.model.fields,field_description:queue_job_subscribe.field_res_users__subscribe_job 36 | msgid "Job Notifications" 37 | msgstr "" 38 | 39 | #. module: queue_job_subscribe 40 | #: model:ir.model,name:queue_job_subscribe.model_queue_job 41 | msgid "Queue Job" 42 | msgstr "" 43 | 44 | #. module: queue_job_subscribe 45 | #: model:ir.model,name:queue_job_subscribe.model_res_users 46 | msgid "User" 47 | msgstr "" 48 | 49 | #~ msgid "Users" 50 | #~ msgstr "Χρήστες" 51 | -------------------------------------------------------------------------------- /queue_job_subscribe/i18n/es.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * connector_job_subscribe 4 | # 5 | # Translators: 6 | # OCA Transbot , 2017 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: Odoo Server 9.0c\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2017-04-05 00:42+0000\n" 12 | "PO-Revision-Date: 2023-10-15 18:36+0000\n" 13 | "Last-Translator: Ivorra78 \n" 14 | "Language-Team: Spanish (https://www.transifex.com/oca/teams/23907/es/)\n" 15 | "Language: es\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: \n" 19 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 20 | "X-Generator: Weblate 4.17\n" 21 | 22 | #. module: queue_job_subscribe 23 | #: model_terms:ir.ui.view,arch_db:queue_job_subscribe.view_user_connector_form 24 | msgid "Connectors" 25 | msgstr "Conectores" 26 | 27 | #. module: queue_job_subscribe 28 | #: model:ir.model.fields,help:queue_job_subscribe.field_res_users__subscribe_job 29 | msgid "" 30 | "If this flag is checked and the user is Connector Manager, he will receive " 31 | "job notifications." 32 | msgstr "" 33 | "Si esta bandera está marcada y el usuario es Connector Manager, recibirá " 34 | "notificaciones de trabajo." 35 | 36 | #. module: queue_job_subscribe 37 | #: model:ir.model.fields,field_description:queue_job_subscribe.field_res_users__subscribe_job 38 | msgid "Job Notifications" 39 | msgstr "Notificaciones de trabajos" 40 | 41 | #. module: queue_job_subscribe 42 | #: model:ir.model,name:queue_job_subscribe.model_queue_job 43 | msgid "Queue Job" 44 | msgstr "Trabajo de Cola" 45 | 46 | #. module: queue_job_subscribe 47 | #: model:ir.model,name:queue_job_subscribe.model_res_users 48 | msgid "User" 49 | msgstr "Usuario" 50 | 51 | #~ msgid "Users" 52 | #~ msgstr "Usuarios" 53 | -------------------------------------------------------------------------------- /queue_job_subscribe/i18n/es_ES.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * connector_job_subscribe 4 | # 5 | # Translators: 6 | # OCA Transbot , 2017 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: Odoo Server 9.0c\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2017-04-05 00:42+0000\n" 12 | "PO-Revision-Date: 2017-04-05 00:42+0000\n" 13 | "Last-Translator: OCA Transbot , 2017\n" 14 | "Language-Team: Spanish (Spain) (https://www.transifex.com/oca/teams/23907/" 15 | "es_ES/)\n" 16 | "Language: es_ES\n" 17 | "MIME-Version: 1.0\n" 18 | "Content-Type: text/plain; charset=UTF-8\n" 19 | "Content-Transfer-Encoding: \n" 20 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 21 | 22 | #. module: queue_job_subscribe 23 | #: model_terms:ir.ui.view,arch_db:queue_job_subscribe.view_user_connector_form 24 | msgid "Connectors" 25 | msgstr "" 26 | 27 | #. module: queue_job_subscribe 28 | #: model:ir.model.fields,help:queue_job_subscribe.field_res_users__subscribe_job 29 | msgid "" 30 | "If this flag is checked and the user is Connector Manager, he will receive " 31 | "job notifications." 32 | msgstr "" 33 | 34 | #. module: queue_job_subscribe 35 | #: model:ir.model.fields,field_description:queue_job_subscribe.field_res_users__subscribe_job 36 | msgid "Job Notifications" 37 | msgstr "" 38 | 39 | #. module: queue_job_subscribe 40 | #: model:ir.model,name:queue_job_subscribe.model_queue_job 41 | msgid "Queue Job" 42 | msgstr "" 43 | 44 | #. module: queue_job_subscribe 45 | #: model:ir.model,name:queue_job_subscribe.model_res_users 46 | msgid "User" 47 | msgstr "" 48 | 49 | #~ msgid "Users" 50 | #~ msgstr "Usuarios" 51 | -------------------------------------------------------------------------------- /queue_job_subscribe/i18n/fi.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * connector_job_subscribe 4 | # 5 | # Translators: 6 | # OCA Transbot , 2017 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: Odoo Server 9.0c\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2017-04-05 00:42+0000\n" 12 | "PO-Revision-Date: 2017-04-05 00:42+0000\n" 13 | "Last-Translator: OCA Transbot , 2017\n" 14 | "Language-Team: Finnish (https://www.transifex.com/oca/teams/23907/fi/)\n" 15 | "Language: fi\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: \n" 19 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 20 | 21 | #. module: queue_job_subscribe 22 | #: model_terms:ir.ui.view,arch_db:queue_job_subscribe.view_user_connector_form 23 | msgid "Connectors" 24 | msgstr "" 25 | 26 | #. module: queue_job_subscribe 27 | #: model:ir.model.fields,help:queue_job_subscribe.field_res_users__subscribe_job 28 | msgid "" 29 | "If this flag is checked and the user is Connector Manager, he will receive " 30 | "job notifications." 31 | msgstr "" 32 | 33 | #. module: queue_job_subscribe 34 | #: model:ir.model.fields,field_description:queue_job_subscribe.field_res_users__subscribe_job 35 | msgid "Job Notifications" 36 | msgstr "" 37 | 38 | #. module: queue_job_subscribe 39 | #: model:ir.model,name:queue_job_subscribe.model_queue_job 40 | msgid "Queue Job" 41 | msgstr "" 42 | 43 | #. module: queue_job_subscribe 44 | #: model:ir.model,name:queue_job_subscribe.model_res_users 45 | msgid "User" 46 | msgstr "" 47 | 48 | #~ msgid "Users" 49 | #~ msgstr "Käyttäjät" 50 | -------------------------------------------------------------------------------- /queue_job_subscribe/i18n/fr.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * connector_job_subscribe 4 | # 5 | # Translators: 6 | # OCA Transbot , 2017 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: Odoo Server 9.0c\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2017-04-05 00:42+0000\n" 12 | "PO-Revision-Date: 2020-07-22 12:20+0000\n" 13 | "Last-Translator: c2cdidier \n" 14 | "Language-Team: French (https://www.transifex.com/oca/teams/23907/fr/)\n" 15 | "Language: fr\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: \n" 19 | "Plural-Forms: nplurals=2; plural=n > 1;\n" 20 | "X-Generator: Weblate 3.10\n" 21 | 22 | #. module: queue_job_subscribe 23 | #: model_terms:ir.ui.view,arch_db:queue_job_subscribe.view_user_connector_form 24 | msgid "Connectors" 25 | msgstr "Connecteurs" 26 | 27 | #. module: queue_job_subscribe 28 | #: model:ir.model.fields,help:queue_job_subscribe.field_res_users__subscribe_job 29 | msgid "" 30 | "If this flag is checked and the user is Connector Manager, he will receive " 31 | "job notifications." 32 | msgstr "" 33 | "Si cette case est cochée et que l'utilisateur est Administrateur Connecteur, " 34 | "il recevra les notifications des jobs." 35 | 36 | #. module: queue_job_subscribe 37 | #: model:ir.model.fields,field_description:queue_job_subscribe.field_res_users__subscribe_job 38 | msgid "Job Notifications" 39 | msgstr "Notifications des jobs" 40 | 41 | #. module: queue_job_subscribe 42 | #: model:ir.model,name:queue_job_subscribe.model_queue_job 43 | msgid "Queue Job" 44 | msgstr "Queue de jobs" 45 | 46 | #. module: queue_job_subscribe 47 | #: model:ir.model,name:queue_job_subscribe.model_res_users 48 | msgid "User" 49 | msgstr "" 50 | 51 | #~ msgid "Users" 52 | #~ msgstr "Utilisateurs" 53 | -------------------------------------------------------------------------------- /queue_job_subscribe/i18n/fr_CH.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * connector_job_subscribe 4 | # 5 | # Translators: 6 | # OCA Transbot , 2017 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: Odoo Server 9.0c\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2017-04-05 00:42+0000\n" 12 | "PO-Revision-Date: 2017-04-05 00:42+0000\n" 13 | "Last-Translator: OCA Transbot , 2017\n" 14 | "Language-Team: French (Switzerland) (https://www.transifex.com/oca/" 15 | "teams/23907/fr_CH/)\n" 16 | "Language: fr_CH\n" 17 | "MIME-Version: 1.0\n" 18 | "Content-Type: text/plain; charset=UTF-8\n" 19 | "Content-Transfer-Encoding: \n" 20 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 21 | 22 | #. module: queue_job_subscribe 23 | #: model_terms:ir.ui.view,arch_db:queue_job_subscribe.view_user_connector_form 24 | msgid "Connectors" 25 | msgstr "" 26 | 27 | #. module: queue_job_subscribe 28 | #: model:ir.model.fields,help:queue_job_subscribe.field_res_users__subscribe_job 29 | msgid "" 30 | "If this flag is checked and the user is Connector Manager, he will receive " 31 | "job notifications." 32 | msgstr "" 33 | 34 | #. module: queue_job_subscribe 35 | #: model:ir.model.fields,field_description:queue_job_subscribe.field_res_users__subscribe_job 36 | msgid "Job Notifications" 37 | msgstr "" 38 | 39 | #. module: queue_job_subscribe 40 | #: model:ir.model,name:queue_job_subscribe.model_queue_job 41 | msgid "Queue Job" 42 | msgstr "" 43 | 44 | #. module: queue_job_subscribe 45 | #: model:ir.model,name:queue_job_subscribe.model_res_users 46 | msgid "User" 47 | msgstr "" 48 | 49 | #~ msgid "Users" 50 | #~ msgstr "Utilisateurs" 51 | -------------------------------------------------------------------------------- /queue_job_subscribe/i18n/hr.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * connector_job_subscribe 4 | # 5 | # Translators: 6 | # OCA Transbot , 2017 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: Odoo Server 9.0c\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2017-04-05 00:42+0000\n" 12 | "PO-Revision-Date: 2017-04-05 00:42+0000\n" 13 | "Last-Translator: OCA Transbot , 2017\n" 14 | "Language-Team: Croatian (https://www.transifex.com/oca/teams/23907/hr/)\n" 15 | "Language: hr\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: \n" 19 | "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " 20 | "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" 21 | 22 | #. module: queue_job_subscribe 23 | #: model_terms:ir.ui.view,arch_db:queue_job_subscribe.view_user_connector_form 24 | msgid "Connectors" 25 | msgstr "" 26 | 27 | #. module: queue_job_subscribe 28 | #: model:ir.model.fields,help:queue_job_subscribe.field_res_users__subscribe_job 29 | msgid "" 30 | "If this flag is checked and the user is Connector Manager, he will receive " 31 | "job notifications." 32 | msgstr "" 33 | 34 | #. module: queue_job_subscribe 35 | #: model:ir.model.fields,field_description:queue_job_subscribe.field_res_users__subscribe_job 36 | msgid "Job Notifications" 37 | msgstr "" 38 | 39 | #. module: queue_job_subscribe 40 | #: model:ir.model,name:queue_job_subscribe.model_queue_job 41 | msgid "Queue Job" 42 | msgstr "" 43 | 44 | #. module: queue_job_subscribe 45 | #: model:ir.model,name:queue_job_subscribe.model_res_users 46 | msgid "User" 47 | msgstr "" 48 | 49 | #~ msgid "Users" 50 | #~ msgstr "Korisnici" 51 | -------------------------------------------------------------------------------- /queue_job_subscribe/i18n/hr_HR.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * connector_job_subscribe 4 | # 5 | # Translators: 6 | # OCA Transbot , 2017 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: Odoo Server 9.0c\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2017-04-05 00:42+0000\n" 12 | "PO-Revision-Date: 2017-04-05 00:42+0000\n" 13 | "Last-Translator: OCA Transbot , 2017\n" 14 | "Language-Team: Croatian (Croatia) (https://www.transifex.com/oca/teams/23907/" 15 | "hr_HR/)\n" 16 | "Language: hr_HR\n" 17 | "MIME-Version: 1.0\n" 18 | "Content-Type: text/plain; charset=UTF-8\n" 19 | "Content-Transfer-Encoding: \n" 20 | "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " 21 | "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" 22 | 23 | #. module: queue_job_subscribe 24 | #: model_terms:ir.ui.view,arch_db:queue_job_subscribe.view_user_connector_form 25 | msgid "Connectors" 26 | msgstr "" 27 | 28 | #. module: queue_job_subscribe 29 | #: model:ir.model.fields,help:queue_job_subscribe.field_res_users__subscribe_job 30 | msgid "" 31 | "If this flag is checked and the user is Connector Manager, he will receive " 32 | "job notifications." 33 | msgstr "" 34 | 35 | #. module: queue_job_subscribe 36 | #: model:ir.model.fields,field_description:queue_job_subscribe.field_res_users__subscribe_job 37 | msgid "Job Notifications" 38 | msgstr "" 39 | 40 | #. module: queue_job_subscribe 41 | #: model:ir.model,name:queue_job_subscribe.model_queue_job 42 | msgid "Queue Job" 43 | msgstr "" 44 | 45 | #. module: queue_job_subscribe 46 | #: model:ir.model,name:queue_job_subscribe.model_res_users 47 | msgid "User" 48 | msgstr "" 49 | 50 | #~ msgid "Users" 51 | #~ msgstr "Korisnici" 52 | -------------------------------------------------------------------------------- /queue_job_subscribe/i18n/hu.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * connector_job_subscribe 4 | # 5 | # Translators: 6 | # OCA Transbot , 2017 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: Odoo Server 9.0c\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2017-04-05 00:42+0000\n" 12 | "PO-Revision-Date: 2017-04-05 00:42+0000\n" 13 | "Last-Translator: OCA Transbot , 2017\n" 14 | "Language-Team: Hungarian (https://www.transifex.com/oca/teams/23907/hu/)\n" 15 | "Language: hu\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: \n" 19 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 20 | 21 | #. module: queue_job_subscribe 22 | #: model_terms:ir.ui.view,arch_db:queue_job_subscribe.view_user_connector_form 23 | msgid "Connectors" 24 | msgstr "Csatolók" 25 | 26 | #. module: queue_job_subscribe 27 | #: model:ir.model.fields,help:queue_job_subscribe.field_res_users__subscribe_job 28 | msgid "" 29 | "If this flag is checked and the user is Connector Manager, he will receive " 30 | "job notifications." 31 | msgstr "" 32 | 33 | #. module: queue_job_subscribe 34 | #: model:ir.model.fields,field_description:queue_job_subscribe.field_res_users__subscribe_job 35 | msgid "Job Notifications" 36 | msgstr "" 37 | 38 | #. module: queue_job_subscribe 39 | #: model:ir.model,name:queue_job_subscribe.model_queue_job 40 | msgid "Queue Job" 41 | msgstr "Sorbanálló feladatok" 42 | 43 | #. module: queue_job_subscribe 44 | #: model:ir.model,name:queue_job_subscribe.model_res_users 45 | msgid "User" 46 | msgstr "" 47 | -------------------------------------------------------------------------------- /queue_job_subscribe/i18n/it.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * connector_job_subscribe 4 | # 5 | # Translators: 6 | # OCA Transbot , 2017 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: Odoo Server 9.0c\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2017-04-05 00:42+0000\n" 12 | "PO-Revision-Date: 2024-02-05 00:26+0000\n" 13 | "Last-Translator: mymage \n" 14 | "Language-Team: Italian (https://www.transifex.com/oca/teams/23907/it/)\n" 15 | "Language: it\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: \n" 19 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 20 | "X-Generator: Weblate 4.17\n" 21 | 22 | #. module: queue_job_subscribe 23 | #: model_terms:ir.ui.view,arch_db:queue_job_subscribe.view_user_connector_form 24 | msgid "Connectors" 25 | msgstr "Connettori" 26 | 27 | #. module: queue_job_subscribe 28 | #: model:ir.model.fields,help:queue_job_subscribe.field_res_users__subscribe_job 29 | msgid "" 30 | "If this flag is checked and the user is Connector Manager, he will receive " 31 | "job notifications." 32 | msgstr "" 33 | "Se questa selezione è attiva e l'utente è un responsabile connettore, " 34 | "riceverà le notifiche di lavoro." 35 | 36 | #. module: queue_job_subscribe 37 | #: model:ir.model.fields,field_description:queue_job_subscribe.field_res_users__subscribe_job 38 | msgid "Job Notifications" 39 | msgstr "Notifiche lavoro" 40 | 41 | #. module: queue_job_subscribe 42 | #: model:ir.model,name:queue_job_subscribe.model_queue_job 43 | msgid "Queue Job" 44 | msgstr "Lavoro in coda" 45 | 46 | #. module: queue_job_subscribe 47 | #: model:ir.model,name:queue_job_subscribe.model_res_users 48 | msgid "User" 49 | msgstr "Utente" 50 | 51 | #~ msgid "Users" 52 | #~ msgstr "Utenti" 53 | -------------------------------------------------------------------------------- /queue_job_subscribe/i18n/nl.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * connector_job_subscribe 4 | # 5 | # Translators: 6 | # OCA Transbot , 2017 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: Odoo Server 9.0c\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2017-04-05 00:42+0000\n" 12 | "PO-Revision-Date: 2017-04-05 00:42+0000\n" 13 | "Last-Translator: OCA Transbot , 2017\n" 14 | "Language-Team: Dutch (https://www.transifex.com/oca/teams/23907/nl/)\n" 15 | "Language: nl\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: \n" 19 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 20 | 21 | #. module: queue_job_subscribe 22 | #: model_terms:ir.ui.view,arch_db:queue_job_subscribe.view_user_connector_form 23 | msgid "Connectors" 24 | msgstr "Connectors" 25 | 26 | #. module: queue_job_subscribe 27 | #: model:ir.model.fields,help:queue_job_subscribe.field_res_users__subscribe_job 28 | msgid "" 29 | "If this flag is checked and the user is Connector Manager, he will receive " 30 | "job notifications." 31 | msgstr "" 32 | 33 | #. module: queue_job_subscribe 34 | #: model:ir.model.fields,field_description:queue_job_subscribe.field_res_users__subscribe_job 35 | msgid "Job Notifications" 36 | msgstr "" 37 | 38 | #. module: queue_job_subscribe 39 | #: model:ir.model,name:queue_job_subscribe.model_queue_job 40 | msgid "Queue Job" 41 | msgstr "Geplande taken" 42 | 43 | #. module: queue_job_subscribe 44 | #: model:ir.model,name:queue_job_subscribe.model_res_users 45 | msgid "User" 46 | msgstr "" 47 | 48 | #~ msgid "Users" 49 | #~ msgstr "Gebruikers" 50 | -------------------------------------------------------------------------------- /queue_job_subscribe/i18n/pt_BR.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * connector_job_subscribe 4 | # 5 | # Translators: 6 | # OCA Transbot , 2017 7 | # Rodrigo de Almeida Sottomaior Macedo , 2017 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Odoo Server 9.0c\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2017-05-01 16:11+0000\n" 13 | "PO-Revision-Date: 2024-07-06 20:58+0000\n" 14 | "Last-Translator: Rodrigo Sottomaior Macedo " 15 | "\n" 16 | "Language-Team: Portuguese (Brazil) (https://www.transifex.com/oca/" 17 | "teams/23907/pt_BR/)\n" 18 | "Language: pt_BR\n" 19 | "MIME-Version: 1.0\n" 20 | "Content-Type: text/plain; charset=UTF-8\n" 21 | "Content-Transfer-Encoding: \n" 22 | "Plural-Forms: nplurals=2; plural=n > 1;\n" 23 | "X-Generator: Weblate 5.6.2\n" 24 | 25 | #. module: queue_job_subscribe 26 | #: model_terms:ir.ui.view,arch_db:queue_job_subscribe.view_user_connector_form 27 | msgid "Connectors" 28 | msgstr "Conectores" 29 | 30 | #. module: queue_job_subscribe 31 | #: model:ir.model.fields,help:queue_job_subscribe.field_res_users__subscribe_job 32 | msgid "" 33 | "If this flag is checked and the user is Connector Manager, he will receive " 34 | "job notifications." 35 | msgstr "" 36 | "Se este sinalizador estiver marcado e o usuário for Connector Manager, ele " 37 | "receberá notificações de tarefa." 38 | 39 | #. module: queue_job_subscribe 40 | #: model:ir.model.fields,field_description:queue_job_subscribe.field_res_users__subscribe_job 41 | msgid "Job Notifications" 42 | msgstr "Notificações de trabalho" 43 | 44 | #. module: queue_job_subscribe 45 | #: model:ir.model,name:queue_job_subscribe.model_queue_job 46 | msgid "Queue Job" 47 | msgstr "Fila de tarefas" 48 | 49 | #. module: queue_job_subscribe 50 | #: model:ir.model,name:queue_job_subscribe.model_res_users 51 | msgid "User" 52 | msgstr "Usuário" 53 | 54 | #~ msgid "Users" 55 | #~ msgstr "Usuários" 56 | 57 | #~ msgid "Sale Pricelist" 58 | #~ msgstr "Lista de Preços de Venda" 59 | -------------------------------------------------------------------------------- /queue_job_subscribe/i18n/queue_job_subscribe.pot: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * queue_job_subscribe 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 18.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "Last-Translator: \n" 10 | "Language-Team: \n" 11 | "MIME-Version: 1.0\n" 12 | "Content-Type: text/plain; charset=UTF-8\n" 13 | "Content-Transfer-Encoding: \n" 14 | "Plural-Forms: \n" 15 | 16 | #. module: queue_job_subscribe 17 | #: model_terms:ir.ui.view,arch_db:queue_job_subscribe.view_user_connector_form 18 | msgid "Connectors" 19 | msgstr "" 20 | 21 | #. module: queue_job_subscribe 22 | #: model:ir.model.fields,help:queue_job_subscribe.field_res_users__subscribe_job 23 | msgid "" 24 | "If this flag is checked and the user is Connector Manager, he will receive " 25 | "job notifications." 26 | msgstr "" 27 | 28 | #. module: queue_job_subscribe 29 | #: model:ir.model.fields,field_description:queue_job_subscribe.field_res_users__subscribe_job 30 | msgid "Job Notifications" 31 | msgstr "" 32 | 33 | #. module: queue_job_subscribe 34 | #: model:ir.model,name:queue_job_subscribe.model_queue_job 35 | msgid "Queue Job" 36 | msgstr "" 37 | 38 | #. module: queue_job_subscribe 39 | #: model:ir.model,name:queue_job_subscribe.model_res_users 40 | msgid "User" 41 | msgstr "" 42 | -------------------------------------------------------------------------------- /queue_job_subscribe/i18n/sl.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * connector_job_subscribe 4 | # 5 | # Translators: 6 | # OCA Transbot , 2017 7 | # Matjaž Mozetič , 2017 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Odoo Server 9.0c\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2017-04-05 00:42+0000\n" 13 | "PO-Revision-Date: 2017-04-05 00:42+0000\n" 14 | "Last-Translator: Matjaž Mozetič , 2017\n" 15 | "Language-Team: Slovenian (https://www.transifex.com/oca/teams/23907/sl/)\n" 16 | "Language: sl\n" 17 | "MIME-Version: 1.0\n" 18 | "Content-Type: text/plain; charset=UTF-8\n" 19 | "Content-Transfer-Encoding: \n" 20 | "Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || " 21 | "n%100==4 ? 2 : 3);\n" 22 | 23 | #. module: queue_job_subscribe 24 | #: model_terms:ir.ui.view,arch_db:queue_job_subscribe.view_user_connector_form 25 | msgid "Connectors" 26 | msgstr "Povezovalniki" 27 | 28 | #. module: queue_job_subscribe 29 | #: model:ir.model.fields,help:queue_job_subscribe.field_res_users__subscribe_job 30 | msgid "" 31 | "If this flag is checked and the user is Connector Manager, he will receive " 32 | "job notifications." 33 | msgstr "" 34 | "Če označeno, bo uporabnik, ki je upravitelj povezovalnikov prejemal " 35 | "obvestila o delu." 36 | 37 | #. module: queue_job_subscribe 38 | #: model:ir.model.fields,field_description:queue_job_subscribe.field_res_users__subscribe_job 39 | #, fuzzy 40 | msgid "Job Notifications" 41 | msgstr "Obvestila o delu" 42 | 43 | #. module: queue_job_subscribe 44 | #: model:ir.model,name:queue_job_subscribe.model_queue_job 45 | msgid "Queue Job" 46 | msgstr "Delo čakalne vrste" 47 | 48 | #. module: queue_job_subscribe 49 | #: model:ir.model,name:queue_job_subscribe.model_res_users 50 | msgid "User" 51 | msgstr "" 52 | 53 | #~ msgid "Users" 54 | #~ msgstr "Uporabniki" 55 | 56 | #~ msgid "Sale Pricelist" 57 | #~ msgstr "Prodajni cenik" 58 | 59 | #~ msgid "" 60 | #~ "This pricelist will be used, instead of the default one, for sales to the " 61 | #~ "current partner" 62 | #~ msgstr "" 63 | #~ "Ta cenik bo uporabljen namesto privzetega za prodajo trenutnemu partnerju." 64 | -------------------------------------------------------------------------------- /queue_job_subscribe/i18n/tr.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * connector_job_subscribe 4 | # 5 | # Translators: 6 | # OCA Transbot , 2017 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: Odoo Server 9.0c\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2017-04-05 00:42+0000\n" 12 | "PO-Revision-Date: 2017-04-05 00:42+0000\n" 13 | "Last-Translator: OCA Transbot , 2017\n" 14 | "Language-Team: Turkish (https://www.transifex.com/oca/teams/23907/tr/)\n" 15 | "Language: tr\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: \n" 19 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 20 | 21 | #. module: queue_job_subscribe 22 | #: model_terms:ir.ui.view,arch_db:queue_job_subscribe.view_user_connector_form 23 | msgid "Connectors" 24 | msgstr "" 25 | 26 | #. module: queue_job_subscribe 27 | #: model:ir.model.fields,help:queue_job_subscribe.field_res_users__subscribe_job 28 | msgid "" 29 | "If this flag is checked and the user is Connector Manager, he will receive " 30 | "job notifications." 31 | msgstr "" 32 | 33 | #. module: queue_job_subscribe 34 | #: model:ir.model.fields,field_description:queue_job_subscribe.field_res_users__subscribe_job 35 | msgid "Job Notifications" 36 | msgstr "" 37 | 38 | #. module: queue_job_subscribe 39 | #: model:ir.model,name:queue_job_subscribe.model_queue_job 40 | msgid "Queue Job" 41 | msgstr "" 42 | 43 | #. module: queue_job_subscribe 44 | #: model:ir.model,name:queue_job_subscribe.model_res_users 45 | msgid "User" 46 | msgstr "" 47 | 48 | #~ msgid "Users" 49 | #~ msgstr "Kullanıcılar" 50 | -------------------------------------------------------------------------------- /queue_job_subscribe/i18n/tr_TR.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * connector_job_subscribe 4 | # 5 | # Translators: 6 | # OCA Transbot , 2017 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: Odoo Server 9.0c\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2017-04-05 00:42+0000\n" 12 | "PO-Revision-Date: 2017-04-05 00:42+0000\n" 13 | "Last-Translator: OCA Transbot , 2017\n" 14 | "Language-Team: Turkish (Turkey) (https://www.transifex.com/oca/teams/23907/" 15 | "tr_TR/)\n" 16 | "Language: tr_TR\n" 17 | "MIME-Version: 1.0\n" 18 | "Content-Type: text/plain; charset=UTF-8\n" 19 | "Content-Transfer-Encoding: \n" 20 | "Plural-Forms: nplurals=1; plural=0;\n" 21 | 22 | #. module: queue_job_subscribe 23 | #: model_terms:ir.ui.view,arch_db:queue_job_subscribe.view_user_connector_form 24 | msgid "Connectors" 25 | msgstr "" 26 | 27 | #. module: queue_job_subscribe 28 | #: model:ir.model.fields,help:queue_job_subscribe.field_res_users__subscribe_job 29 | msgid "" 30 | "If this flag is checked and the user is Connector Manager, he will receive " 31 | "job notifications." 32 | msgstr "" 33 | 34 | #. module: queue_job_subscribe 35 | #: model:ir.model.fields,field_description:queue_job_subscribe.field_res_users__subscribe_job 36 | msgid "Job Notifications" 37 | msgstr "" 38 | 39 | #. module: queue_job_subscribe 40 | #: model:ir.model,name:queue_job_subscribe.model_queue_job 41 | msgid "Queue Job" 42 | msgstr "" 43 | 44 | #. module: queue_job_subscribe 45 | #: model:ir.model,name:queue_job_subscribe.model_res_users 46 | msgid "User" 47 | msgstr "" 48 | 49 | #~ msgid "Users" 50 | #~ msgstr "Kullanıcılar" 51 | -------------------------------------------------------------------------------- /queue_job_subscribe/i18n/zh_CN.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * connector_job_subscribe 4 | # 5 | # Translators: 6 | # OCA Transbot , 2017 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: Odoo Server 9.0c\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2017-04-05 00:42+0000\n" 12 | "PO-Revision-Date: 2024-07-02 09:47+0000\n" 13 | "Last-Translator: xtanuiha \n" 14 | "Language-Team: Chinese (China) (https://www.transifex.com/oca/teams/23907/" 15 | "zh_CN/)\n" 16 | "Language: zh_CN\n" 17 | "MIME-Version: 1.0\n" 18 | "Content-Type: text/plain; charset=UTF-8\n" 19 | "Content-Transfer-Encoding: \n" 20 | "Plural-Forms: nplurals=1; plural=0;\n" 21 | "X-Generator: Weblate 4.17\n" 22 | 23 | #. module: queue_job_subscribe 24 | #: model_terms:ir.ui.view,arch_db:queue_job_subscribe.view_user_connector_form 25 | msgid "Connectors" 26 | msgstr "连接器" 27 | 28 | #. module: queue_job_subscribe 29 | #: model:ir.model.fields,help:queue_job_subscribe.field_res_users__subscribe_job 30 | msgid "" 31 | "If this flag is checked and the user is Connector Manager, he will receive " 32 | "job notifications." 33 | msgstr "如果选中此标志并且用户是连接器管理,则他将收到作业通知。" 34 | 35 | #. module: queue_job_subscribe 36 | #: model:ir.model.fields,field_description:queue_job_subscribe.field_res_users__subscribe_job 37 | msgid "Job Notifications" 38 | msgstr "作业通知" 39 | 40 | #. module: queue_job_subscribe 41 | #: model:ir.model,name:queue_job_subscribe.model_queue_job 42 | msgid "Queue Job" 43 | msgstr "队列中作业" 44 | 45 | #. module: queue_job_subscribe 46 | #: model:ir.model,name:queue_job_subscribe.model_res_users 47 | msgid "User" 48 | msgstr "用户" 49 | 50 | #~ msgid "Users" 51 | #~ msgstr "用户" 52 | -------------------------------------------------------------------------------- /queue_job_subscribe/models/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Cédric Pigeon 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | 4 | from . import queue_job 5 | from . import res_users 6 | -------------------------------------------------------------------------------- /queue_job_subscribe/models/queue_job.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Cédric Pigeon 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | 4 | from odoo import models 5 | 6 | 7 | class QueueJob(models.Model): 8 | _inherit = "queue.job" 9 | 10 | def _subscribe_users_domain(self): 11 | domain = super()._subscribe_users_domain() 12 | domain.append(("subscribe_job", "=", True)) 13 | return domain 14 | -------------------------------------------------------------------------------- /queue_job_subscribe/models/res_users.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Cédric Pigeon 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | 4 | from odoo import fields, models 5 | 6 | 7 | class ResUsers(models.Model): 8 | _inherit = "res.users" 9 | 10 | subscribe_job = fields.Boolean( 11 | "Job Notifications", 12 | default=True, 13 | help="If this flag is checked and the " 14 | "user is Connector Manager, he will " 15 | "receive job notifications.", 16 | index=True, 17 | ) 18 | -------------------------------------------------------------------------------- /queue_job_subscribe/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["whool"] 3 | build-backend = "whool.buildapi" 4 | -------------------------------------------------------------------------------- /queue_job_subscribe/readme/CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | - Cédric Pigeon \<\> 2 | - Stéphane Bidoul \<\> 3 | - Tran Quoc Duong \<\> 4 | -------------------------------------------------------------------------------- /queue_job_subscribe/readme/CREDITS.md: -------------------------------------------------------------------------------- 1 | The migration of this module from 17.0 to 18.0 was financially supported by Camptocamp. 2 | -------------------------------------------------------------------------------- /queue_job_subscribe/readme/DESCRIPTION.md: -------------------------------------------------------------------------------- 1 | This module allows users to be member of group Job Queue Manager without becoming follower of failed jobs. 2 | This can avoid for some users to receive a lot of emails in case of failing jobs. 3 | -------------------------------------------------------------------------------- /queue_job_subscribe/readme/USAGE.md: -------------------------------------------------------------------------------- 1 | On the user configuration form, there is new Job Notifications checkbox. 2 | 3 | If checked, the user becomes follower of failed jobs if he/she is part of the Job Queue Manager group. 4 | 5 | If not checked, the user does not become follower of failed jobs. 6 | -------------------------------------------------------------------------------- /queue_job_subscribe/static/description/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OCA/queue/fe0bba38a93847d90486e9c89dfb4df55d9d5811/queue_job_subscribe/static/description/icon.png -------------------------------------------------------------------------------- /queue_job_subscribe/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Cédric Pigeon 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | 4 | from . import test_job_subscribe 5 | -------------------------------------------------------------------------------- /queue_job_subscribe/views/res_users_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | res.user.connector.form 5 | res.users 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # generated from manifests external_dependencies 2 | requests 3 | -------------------------------------------------------------------------------- /setup/_metapackage/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "odoo-addons-oca-queue" 3 | version = "18.0.20250415.0" 4 | dependencies = [ 5 | "odoo-addon-base_import_async==18.0.*", 6 | "odoo-addon-queue_job==18.0.*", 7 | "odoo-addon-queue_job_batch==18.0.*", 8 | "odoo-addon-queue_job_cron==18.0.*", 9 | "odoo-addon-queue_job_cron_jobrunner==18.0.*", 10 | "odoo-addon-queue_job_subscribe==18.0.*", 11 | "odoo-addon-test_queue_job==18.0.*", 12 | "odoo-addon-test_queue_job_batch==18.0.*", 13 | ] 14 | classifiers=[ 15 | "Programming Language :: Python", 16 | "Framework :: Odoo", 17 | "Framework :: Odoo :: 18.0", 18 | ] 19 | -------------------------------------------------------------------------------- /test_queue_job/README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://img.shields.io/badge/licence-LGPL--3-blue.svg 2 | :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html 3 | :alt: License: LGPL-3 4 | 5 | ============== 6 | Test Job Queue 7 | ============== 8 | 9 | This addon is not meant to be used. It extends the Odoo Models 10 | in order to run automated tests on the jobs queue. 11 | 12 | The basic tests are integrated with the ``queue_job`` addon, 13 | but for the tests that need several job methods are done in 14 | this module to avoid to pollute the Models. 15 | 16 | Installation 17 | ============ 18 | 19 | Nothing particular. 20 | 21 | Usage 22 | ===== 23 | 24 | This module only contains Python tests. 25 | 26 | Known issues / Roadmap 27 | ====================== 28 | 29 | Bug Tracker 30 | =========== 31 | 32 | Bugs are tracked on `GitHub Issues 33 | `_. In case of trouble, please 34 | check there if your issue has already been reported. If you spotted it first, 35 | help us smashing it by providing a detailed and welcomed feedback. 36 | 37 | Credits 38 | ======= 39 | 40 | Images 41 | ------ 42 | 43 | * Odoo Community Association: `Icon `_. 44 | 45 | Contributors 46 | ------------ 47 | 48 | * Guewen Baconnier 49 | * Stéphane Bidoul 50 | * Matthieu Dietrich 51 | * Jos De Graeve 52 | * David Lefever 53 | * Laurent Mignon 54 | * Laetitia Gangloff 55 | 56 | Maintainer 57 | ---------- 58 | 59 | .. image:: https://odoo-community.org/logo.png 60 | :alt: Odoo Community Association 61 | :target: https://odoo-community.org 62 | 63 | This module is maintained by the OCA. 64 | 65 | OCA, or the Odoo Community Association, is a nonprofit organization whose 66 | mission is to support the collaborative development of Odoo features and 67 | promote its widespread use. 68 | 69 | To contribute to this module, please visit https://odoo-community.org. 70 | 71 | -------------------------------------------------------------------------------- /test_queue_job/__init__.py: -------------------------------------------------------------------------------- 1 | from . import models 2 | -------------------------------------------------------------------------------- /test_queue_job/__manifest__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Camptocamp SA 2 | # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) 3 | 4 | { 5 | "name": "Queue Job Tests", 6 | "version": "18.0.1.0.1", 7 | "author": "Camptocamp,Odoo Community Association (OCA)", 8 | "license": "LGPL-3", 9 | "category": "Generic Modules", 10 | "depends": ["queue_job"], 11 | "website": "https://github.com/OCA/queue", 12 | "data": [ 13 | "data/queue_job_channel_data.xml", 14 | "data/queue_job_function_data.xml", 15 | "security/ir.model.access.csv", 16 | ], 17 | "installable": True, 18 | } 19 | -------------------------------------------------------------------------------- /test_queue_job/data/queue_job_channel_data.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | sub 4 | 5 | 6 | 7 | subsub 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /test_queue_job/data/queue_job_function_data.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | testing_method 5 | 6 | 7 | 11 | 12 | job_with_retry_pattern 13 | 14 | 15 | 19 | 20 | job_with_retry_pattern__no_zero 21 | 22 | 23 | 27 | 28 | job_sub_channel 29 | 30 | 31 | 32 | 33 | job_a 34 | 35 | 39 | 40 | testing_related_action__return_none 41 | 42 | 43 | 47 | 48 | testing_related_action__kwargs 49 | 53 | 54 | 58 | 59 | testing_related_action__store 60 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /test_queue_job/i18n/test_queue_job.pot: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * test_queue_job 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 18.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "Last-Translator: \n" 10 | "Language-Team: \n" 11 | "MIME-Version: 1.0\n" 12 | "Content-Type: text/plain; charset=UTF-8\n" 13 | "Content-Transfer-Encoding: \n" 14 | "Plural-Forms: \n" 15 | 16 | #. module: test_queue_job 17 | #: model:ir.model.fields,field_description:test_queue_job.field_queue_job__additional_info 18 | msgid "Additional Info" 19 | msgstr "" 20 | 21 | #. module: test_queue_job 22 | #: model:ir.model.fields,field_description:test_queue_job.field_test_queue_channel__create_uid 23 | #: model:ir.model.fields,field_description:test_queue_job.field_test_queue_job__create_uid 24 | #: model:ir.model.fields,field_description:test_queue_job.field_test_related_action__create_uid 25 | msgid "Created by" 26 | msgstr "" 27 | 28 | #. module: test_queue_job 29 | #: model:ir.model.fields,field_description:test_queue_job.field_test_queue_channel__create_date 30 | #: model:ir.model.fields,field_description:test_queue_job.field_test_queue_job__create_date 31 | #: model:ir.model.fields,field_description:test_queue_job.field_test_related_action__create_date 32 | msgid "Created on" 33 | msgstr "" 34 | 35 | #. module: test_queue_job 36 | #: model:ir.model.fields,field_description:test_queue_job.field_test_queue_channel__display_name 37 | #: model:ir.model.fields,field_description:test_queue_job.field_test_queue_job__display_name 38 | #: model:ir.model.fields,field_description:test_queue_job.field_test_related_action__display_name 39 | msgid "Display Name" 40 | msgstr "" 41 | 42 | #. module: test_queue_job 43 | #: model:ir.model.fields,field_description:test_queue_job.field_test_queue_channel__id 44 | #: model:ir.model.fields,field_description:test_queue_job.field_test_queue_job__id 45 | #: model:ir.model.fields,field_description:test_queue_job.field_test_related_action__id 46 | msgid "ID" 47 | msgstr "" 48 | 49 | #. module: test_queue_job 50 | #: model:ir.model.fields,field_description:test_queue_job.field_test_queue_channel__write_uid 51 | #: model:ir.model.fields,field_description:test_queue_job.field_test_queue_job__write_uid 52 | #: model:ir.model.fields,field_description:test_queue_job.field_test_related_action__write_uid 53 | msgid "Last Updated by" 54 | msgstr "" 55 | 56 | #. module: test_queue_job 57 | #: model:ir.model.fields,field_description:test_queue_job.field_test_queue_channel__write_date 58 | #: model:ir.model.fields,field_description:test_queue_job.field_test_queue_job__write_date 59 | #: model:ir.model.fields,field_description:test_queue_job.field_test_related_action__write_date 60 | msgid "Last Updated on" 61 | msgstr "" 62 | 63 | #. module: test_queue_job 64 | #: model:ir.model.fields,field_description:test_queue_job.field_test_queue_job__name 65 | msgid "Name" 66 | msgstr "" 67 | 68 | #. module: test_queue_job 69 | #: model:ir.model,name:test_queue_job.model_queue_job 70 | msgid "Queue Job" 71 | msgstr "" 72 | 73 | #. module: test_queue_job 74 | #: model:ir.model,name:test_queue_job.model_test_queue_channel 75 | msgid "Test model for queue.channel" 76 | msgstr "" 77 | 78 | #. module: test_queue_job 79 | #: model:ir.model,name:test_queue_job.model_test_queue_job 80 | msgid "Test model for queue.job" 81 | msgstr "" 82 | 83 | #. module: test_queue_job 84 | #: model:ir.model,name:test_queue_job.model_test_related_action 85 | msgid "Test model for related actions" 86 | msgstr "" 87 | -------------------------------------------------------------------------------- /test_queue_job/models/__init__.py: -------------------------------------------------------------------------------- 1 | from . import test_models 2 | -------------------------------------------------------------------------------- /test_queue_job/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["whool"] 3 | build-backend = "whool.buildapi" 4 | -------------------------------------------------------------------------------- /test_queue_job/security/ir.model.access.csv: -------------------------------------------------------------------------------- 1 | id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink 2 | access_test_queue_job,access_test_queue_job,model_test_queue_job,queue_job.group_queue_job_manager,1,1,1,1 3 | access_test_queue_channel,access_test_queue_channel,model_test_queue_channel,queue_job.group_queue_job_manager,1,1,1,1 4 | access_test_related_action,access_test_related_action,model_test_related_action,queue_job.group_queue_job_manager,1,1,1,1 5 | -------------------------------------------------------------------------------- /test_queue_job/static/description/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OCA/queue/fe0bba38a93847d90486e9c89dfb4df55d9d5811/test_queue_job/static/description/icon.png -------------------------------------------------------------------------------- /test_queue_job/tests/__init__.py: -------------------------------------------------------------------------------- 1 | from . import test_autovacuum 2 | from . import test_delayable 3 | from . import test_dependencies 4 | from . import test_job 5 | from . import test_job_auto_delay 6 | from . import test_job_channels 7 | from . import test_job_function 8 | from . import test_related_actions 9 | from . import test_delay_mocks 10 | -------------------------------------------------------------------------------- /test_queue_job/tests/common.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016-2019 Camptocamp SA 2 | # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) 3 | 4 | from odoo.tests import common 5 | 6 | from odoo.addons.queue_job.job import Job 7 | 8 | 9 | class JobCommonCase(common.TransactionCase): 10 | @classmethod 11 | def setUpClass(cls): 12 | super().setUpClass() 13 | cls.queue_job = cls.env["queue.job"] 14 | cls.user = cls.env["res.users"] 15 | cls.method = cls.env["test.queue.job"].testing_method 16 | 17 | def _create_job(self): 18 | test_job = Job(self.method) 19 | test_job.store() 20 | stored = Job.db_records_from_uuids(self.env, [test_job.uuid]) 21 | self.assertEqual(len(stored), 1) 22 | return stored 23 | -------------------------------------------------------------------------------- /test_queue_job/tests/test_autovacuum.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Versada UAB 2 | # License LGPL-3 or later (https://www.gnu.org/licenses/lgpl). 3 | 4 | from datetime import datetime, timedelta 5 | 6 | from .common import JobCommonCase 7 | 8 | 9 | class TestQueueJobAutovacuumCronJob(JobCommonCase): 10 | @classmethod 11 | def setUpClass(cls): 12 | super().setUpClass() 13 | cls.cron_job = cls.env.ref("queue_job.ir_cron_autovacuum_queue_jobs") 14 | 15 | def test_old_jobs_are_deleted_by_cron_job(self): 16 | """Old jobs are deleted by the autovacuum cron job.""" 17 | date_done = datetime.now() - timedelta( 18 | days=self.queue_job._removal_interval + 1 19 | ) 20 | stored = self._create_job() 21 | stored.write({"date_done": date_done}) 22 | self.cron_job.method_direct_trigger() 23 | self.assertFalse(stored.exists()) 24 | 25 | def test_autovacuum(self): 26 | # test default removal interval 27 | stored = self._create_job() 28 | date_done = datetime.now() - timedelta(days=29) 29 | stored.write({"date_done": date_done}) 30 | self.env["queue.job"].autovacuum() 31 | self.assertEqual(len(self.env["queue.job"].search([])), 1) 32 | 33 | date_done = datetime.now() - timedelta(days=31) 34 | stored.write({"date_done": date_done}) 35 | self.env["queue.job"].autovacuum() 36 | self.assertEqual(len(self.env["queue.job"].search([])), 0) 37 | 38 | def test_autovacuum_multi_channel(self): 39 | root_channel = self.env.ref("queue_job.channel_root") 40 | channel_60days = self.env["queue.job.channel"].create( 41 | {"name": "60days", "removal_interval": 60, "parent_id": root_channel.id} 42 | ) 43 | date_done = datetime.now() - timedelta(days=31) 44 | job_root = self._create_job() 45 | job_root.write({"date_done": date_done}) 46 | job_60days = self._create_job() 47 | job_60days.write( 48 | {"channel": channel_60days.complete_name, "date_done": date_done} 49 | ) 50 | 51 | self.assertEqual(len(self.env["queue.job"].search([])), 2) 52 | self.env["queue.job"].autovacuum() 53 | self.assertEqual(len(self.env["queue.job"].search([])), 1) 54 | 55 | date_done = datetime.now() - timedelta(days=61) 56 | job_60days.write({"date_done": date_done}) 57 | self.env["queue.job"].autovacuum() 58 | self.assertEqual(len(self.env["queue.job"].search([])), 0) 59 | -------------------------------------------------------------------------------- /test_queue_job/tests/test_job_auto_delay.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Camptocamp SA 2 | # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) 3 | 4 | from odoo.tests.common import tagged 5 | 6 | from odoo.addons.queue_job.job import Job 7 | 8 | from .common import JobCommonCase 9 | 10 | 11 | @tagged("post_install", "-at_install") 12 | class TestJobAutoDelay(JobCommonCase): 13 | """Test auto delay of jobs""" 14 | 15 | def test_auto_delay(self): 16 | """method decorated by @job_auto_delay is automatically delayed""" 17 | result = self.env["test.queue.job"].delay_me(1, kwarg=2) 18 | self.assertTrue(isinstance(result, Job)) 19 | self.assertEqual(result.args, (1,)) 20 | self.assertEqual(result.kwargs, {"kwarg": 2}) 21 | 22 | def test_auto_delay_options(self): 23 | """method automatically delayed une _job_options arguments""" 24 | result = self.env["test.queue.job"].delay_me_options() 25 | self.assertTrue(isinstance(result, Job)) 26 | self.assertEqual(result.identity_key, "my_job_identity") 27 | 28 | def test_auto_delay_inside_job(self): 29 | """when a delayed job is processed, it must not delay itself""" 30 | job_ = self.env["test.queue.job"].delay_me(1, kwarg=2) 31 | self.assertTrue(job_.perform(), (1, 2)) 32 | 33 | def test_auto_delay_force_sync(self): 34 | """method forced to run synchronously""" 35 | result = self.env["test.queue.job"].delay_me(1, kwarg=2) 36 | self.assertTrue(result, (1, 2)) 37 | 38 | def test_auto_delay_context_key_set(self): 39 | """patched with context_key delays only if context keys is set""" 40 | result = ( 41 | self.env["test.queue.job"] 42 | .with_context(auto_delay_delay_me_context_key=True) 43 | .delay_me_context_key() 44 | ) 45 | self.assertTrue(isinstance(result, Job)) 46 | 47 | def test_auto_delay_context_key_unset(self): 48 | """patched with context_key do not delay if context keys is not set""" 49 | result = self.env["test.queue.job"].delay_me_context_key() 50 | self.assertEqual(result, "ok") 51 | -------------------------------------------------------------------------------- /test_queue_job/tests/test_job_function.py: -------------------------------------------------------------------------------- 1 | import odoo.tests.common as common 2 | from odoo import exceptions 3 | 4 | 5 | class TestJobFunction(common.TransactionCase): 6 | def setUp(self): 7 | super().setUp() 8 | self.test_function_model = self.env.ref( 9 | "queue_job.job_function_queue_job__test_job" 10 | ) 11 | 12 | def test_check_retry_pattern_randomized_case(self): 13 | randomized_pattern = "{1: (10, 20), 2: (20, 40)}" 14 | self.test_function_model.edit_retry_pattern = randomized_pattern 15 | self.assertEqual( 16 | self.test_function_model.edit_retry_pattern, randomized_pattern 17 | ) 18 | 19 | def test_check_retry_pattern_fixed_case(self): 20 | fixed_pattern = "{1: 10, 2: 20}" 21 | self.test_function_model.edit_retry_pattern = fixed_pattern 22 | self.assertEqual(self.test_function_model.edit_retry_pattern, fixed_pattern) 23 | 24 | def test_check_retry_pattern_invalid_cases(self): 25 | invalid_time_value_pattern = "{1: a, 2: 20}" 26 | with self.assertRaises(exceptions.UserError): 27 | self.test_function_model.edit_retry_pattern = invalid_time_value_pattern 28 | 29 | invalid_retry_count_pattern = "{a: 10, 2: 20}" 30 | with self.assertRaises(exceptions.UserError): 31 | self.test_function_model.edit_retry_pattern = invalid_retry_count_pattern 32 | 33 | invalid_randomized_pattern = "{1: (1, 2, 3), 2: 20}" 34 | with self.assertRaises(exceptions.UserError): 35 | self.test_function_model.edit_retry_pattern = invalid_randomized_pattern 36 | -------------------------------------------------------------------------------- /test_queue_job/tests/test_json_field.py: -------------------------------------------------------------------------------- 1 | # copyright 2022 Guewen Baconnier 2 | # license lgpl-3.0 or later (http://www.gnu.org/licenses/lgpl.html) 3 | 4 | import json 5 | 6 | from odoo.tests import common 7 | 8 | # pylint: disable=odoo-addons-relative-import 9 | # we are testing, we want to test as if we were an external consumer of the API 10 | from odoo.addons.queue_job.fields import JobEncoder 11 | 12 | 13 | class TestJsonField(common.TransactionCase): 14 | # TODO: when migrating to 16.0, adapt checks in queue_job/tests/test_json_field.py 15 | # to verify the context keys are encoded and remove these 16 | def test_encoder_recordset_store_context(self): 17 | demo_user = self.env.ref("base.user_demo") 18 | user_context = {"lang": "en_US", "tz": "Europe/Brussels"} 19 | test_model = self.env(user=demo_user, context=user_context)["test.queue.job"] 20 | value_json = json.dumps(test_model, cls=JobEncoder) 21 | self.assertEqual(json.loads(value_json)["context"], user_context) 22 | 23 | def test_encoder_recordset_context_filter_keys(self): 24 | demo_user = self.env.ref("base.user_demo") 25 | user_context = {"lang": "en_US", "tz": "Europe/Brussels"} 26 | tampered_context = dict(user_context, foo=object()) 27 | test_model = self.env(user=demo_user, context=tampered_context)[ 28 | "test.queue.job" 29 | ] 30 | value_json = json.dumps(test_model, cls=JobEncoder) 31 | self.assertEqual(json.loads(value_json)["context"], user_context) 32 | -------------------------------------------------------------------------------- /test_queue_job_batch/README.rst: -------------------------------------------------------------------------------- 1 | ==================== 2 | Test Job Queue Batch 3 | ==================== 4 | 5 | .. 6 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 7 | !! This file is generated by oca-gen-addon-readme !! 8 | !! changes will be overwritten. !! 9 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 10 | !! source digest: sha256:e01f2caa1024599ec16d5cf1ae3a33cd9559ffaa5786195f2be98a5fc09710e2 11 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 12 | 13 | .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png 14 | :target: https://odoo-community.org/page/development-status 15 | :alt: Beta 16 | .. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png 17 | :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html 18 | :alt: License: AGPL-3 19 | .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fqueue-lightgray.png?logo=github 20 | :target: https://github.com/OCA/queue/tree/18.0/test_queue_job_batch 21 | :alt: OCA/queue 22 | .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png 23 | :target: https://translation.odoo-community.org/projects/queue-18-0/queue-18-0-test_queue_job_batch 24 | :alt: Translate me on Weblate 25 | .. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png 26 | :target: https://runboat.odoo-community.org/builds?repo=OCA/queue&target_branch=18.0 27 | :alt: Try me on Runboat 28 | 29 | |badge1| |badge2| |badge3| |badge4| |badge5| 30 | 31 | This addon is used to test the queue job batch functionality 32 | 33 | **Table of contents** 34 | 35 | .. contents:: 36 | :local: 37 | 38 | Bug Tracker 39 | =========== 40 | 41 | Bugs are tracked on `GitHub Issues `_. 42 | In case of trouble, please check there if your issue has already been reported. 43 | If you spotted it first, help us to smash it by providing a detailed and welcomed 44 | `feedback `_. 45 | 46 | Do not contact contributors directly about support or help with technical issues. 47 | 48 | Credits 49 | ======= 50 | 51 | Authors 52 | ------- 53 | 54 | * Creu Blanca 55 | 56 | Contributors 57 | ------------ 58 | 59 | - Enric Tobella 60 | 61 | - Lois Rilo 62 | 63 | - `Trobz `__: 64 | 65 | - Hoang Diep 66 | 67 | Other credits 68 | ------------- 69 | 70 | The migration of this module from 12.0 to 14.0 was financially supported 71 | by Camptocamp 72 | 73 | Maintainers 74 | ----------- 75 | 76 | This module is maintained by the OCA. 77 | 78 | .. image:: https://odoo-community.org/logo.png 79 | :alt: Odoo Community Association 80 | :target: https://odoo-community.org 81 | 82 | OCA, or the Odoo Community Association, is a nonprofit organization whose 83 | mission is to support the collaborative development of Odoo features and 84 | promote its widespread use. 85 | 86 | This module is part of the `OCA/queue `_ project on GitHub. 87 | 88 | You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. 89 | -------------------------------------------------------------------------------- /test_queue_job_batch/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OCA/queue/fe0bba38a93847d90486e9c89dfb4df55d9d5811/test_queue_job_batch/__init__.py -------------------------------------------------------------------------------- /test_queue_job_batch/__manifest__.py: -------------------------------------------------------------------------------- 1 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) 2 | 3 | 4 | { 5 | "name": "Test Job Queue Batch", 6 | "version": "18.0.1.0.0", 7 | "author": "Creu Blanca,Odoo Community Association (OCA)", 8 | "website": "https://github.com/OCA/queue", 9 | "license": "AGPL-3", 10 | "category": "Generic Modules", 11 | "depends": [ 12 | "queue_job_batch", 13 | "test_queue_job", 14 | ], 15 | } 16 | -------------------------------------------------------------------------------- /test_queue_job_batch/i18n/fr.po: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OCA/queue/fe0bba38a93847d90486e9c89dfb4df55d9d5811/test_queue_job_batch/i18n/fr.po -------------------------------------------------------------------------------- /test_queue_job_batch/i18n/it.po: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OCA/queue/fe0bba38a93847d90486e9c89dfb4df55d9d5811/test_queue_job_batch/i18n/it.po -------------------------------------------------------------------------------- /test_queue_job_batch/i18n/test_queue_job_batch.pot: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # 4 | msgid "" 5 | msgstr "" 6 | "Project-Id-Version: Odoo Server 18.0\n" 7 | "Report-Msgid-Bugs-To: \n" 8 | "Last-Translator: \n" 9 | "Language-Team: \n" 10 | "MIME-Version: 1.0\n" 11 | "Content-Type: text/plain; charset=UTF-8\n" 12 | "Content-Transfer-Encoding: \n" 13 | "Plural-Forms: \n" 14 | -------------------------------------------------------------------------------- /test_queue_job_batch/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["whool"] 3 | build-backend = "whool.buildapi" 4 | -------------------------------------------------------------------------------- /test_queue_job_batch/readme/CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | - Enric Tobella \<\> 2 | 3 | - Lois Rilo \<\> 4 | 5 | - [Trobz](https://trobz.com): 6 | - Hoang Diep \<\> 7 | -------------------------------------------------------------------------------- /test_queue_job_batch/readme/CREDITS.md: -------------------------------------------------------------------------------- 1 | The migration of this module from 12.0 to 14.0 was financially supported 2 | by Camptocamp 3 | -------------------------------------------------------------------------------- /test_queue_job_batch/readme/DESCRIPTION.md: -------------------------------------------------------------------------------- 1 | This addon is used to test the queue job batch functionality 2 | -------------------------------------------------------------------------------- /test_queue_job_batch/static/description/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OCA/queue/fe0bba38a93847d90486e9c89dfb4df55d9d5811/test_queue_job_batch/static/description/icon.png -------------------------------------------------------------------------------- /test_queue_job_batch/tests/__init__.py: -------------------------------------------------------------------------------- 1 | from . import test_queue_job_batch 2 | -------------------------------------------------------------------------------- /test_queue_job_batch/tests/test_queue_job_batch.py: -------------------------------------------------------------------------------- 1 | from odoo.tests.common import TransactionCase 2 | 3 | from odoo.addons.queue_job.job import Job 4 | 5 | 6 | class TestQueueJobGroup(TransactionCase): 7 | def test_batch(self): 8 | self.cr.execute("delete from queue_job") 9 | batch = self.env["queue.job.batch"].get_new_batch("TEST") 10 | self.assertFalse(batch.job_ids) 11 | model = self.env["test.queue.job"].with_context(job_batch=batch) 12 | job_1 = model.with_delay().testing_method() 13 | self.assertEqual(job_1.db_record().state, "pending") 14 | job_2 = model.with_delay().testing_method() 15 | self.assertEqual(job_2.db_record().state, "pending") 16 | jobs = job_1.db_record() 17 | jobs |= job_2.db_record() 18 | self.assertEqual(jobs, batch.job_ids) 19 | batch.set_read() 20 | self.assertFalse(batch.is_read, "Not marked as read because not yet finished") 21 | self.assertEqual(batch.state, "pending") 22 | self.assertEqual(job_2.state, "pending") 23 | self.assertEqual(job_2.state, "pending") 24 | job_domain = [ 25 | ("uuid", "!=", job_1.uuid), 26 | ("uuid", "!=", job_2.uuid), 27 | ] 28 | update = self.env["queue.job"].search(job_domain) 29 | self.assertFalse(update) 30 | job = Job.load(self.env, job_1.uuid) 31 | job.perform() 32 | job.set_done() 33 | job.store() 34 | update = self.env["queue.job"].search(job_domain) 35 | self.assertTrue(update) 36 | self.assertEqual(1, len(update)) 37 | job = Job.load(self.env, update.uuid) 38 | job.perform() 39 | job.set_done() 40 | job.store() 41 | self.assertEqual(batch.state, "progress") 42 | self.assertEqual(batch.completeness, 0.5) 43 | job_domain.append(("uuid", "!=", update.uuid)) 44 | update = self.env["queue.job"].search(job_domain) 45 | self.assertFalse(update) 46 | job = Job.load(self.env, job_2.uuid) 47 | job.perform() 48 | job.set_done() 49 | job.store() 50 | update = self.env["queue.job"].search(job_domain) 51 | self.assertTrue(update) 52 | self.assertEqual(1, len(update)) 53 | job = Job.load(self.env, update.uuid) 54 | job.perform() 55 | job.set_done() 56 | job.store() 57 | self.assertEqual(batch.state, "finished") 58 | self.assertEqual(batch.completeness, 1) 59 | self.assertFalse(batch.is_read) 60 | batch.set_read() 61 | self.assertTrue(batch.is_read, "Marked as read because it's finished") 62 | --------------------------------------------------------------------------------