diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..bfd7ac53d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +# Configuration for known file extensions +[*.{css,js,json,less,md,py,rst,sass,scss,xml,yaml,yml}] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{json,yml,yaml,rst,md}] +indent_size = 2 + +# Do not configure editor for libs and autogenerated content +[{*/static/{lib,src/lib}/**,*/static/description/index.html,*/readme/../README.rst}] +charset = unset +end_of_line = unset +indent_size = unset +indent_style = unset +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 28a0808b3..000000000 --- a/.eslintrc +++ /dev/null @@ -1,291 +0,0 @@ -{ - "globals": { - "$": false, - "_": false, - "fuzzy": false, - "jQuery": false, - "moment": false, - "odoo": false, - "openerp": false, - "self": false - }, - "env": { - "browser": true - }, - "rules": { - "no-alert": "warn", - "no-array-constructor": "warn", - "no-bitwise": "off", - "no-caller": "warn", - "no-case-declarations": "warn", - "no-catch-shadow": "warn", - "no-class-assign": "warn", - "no-cond-assign": "warn", - "no-confusing-arrow": "warn", - "no-console": "off", - "no-const-assign": "warn", - "no-constant-condition": "warn", - "no-continue": "off", - "no-control-regex": "warn", - "no-debugger": "warn", - "no-delete-var": "warn", - "no-div-regex": "warn", - "no-dupe-args": "warn", - "no-dupe-class-members": "warn", - "no-dupe-keys": "warn", - "no-duplicate-case": "warn", - "no-duplicate-imports": "warn", - "no-else-return": "warn", - "no-empty": "warn", - "no-empty-character-class": "warn", - "no-empty-function": "warn", - "no-empty-pattern": "warn", - "no-eq-null": "warn", - "no-eval": "warn", - "no-ex-assign": "warn", - "no-extend-native": "warn", - "no-extra-bind": "warn", - "no-extra-boolean-cast": "warn", - "no-extra-label": "warn", - "no-extra-parens": "warn", - "no-extra-semi": "warn", - "no-fallthrough": "warn", - "no-floating-decimal": "warn", - "no-func-assign": "warn", - "no-implicit-coercion": ["warn", { - "allow": ["~"] - }], - "no-implicit-globals": "warn", - "no-implied-eval": "warn", - "no-inline-comments": "warn", - "no-inner-declarations": "warn", - "no-invalid-regexp": "warn", - "no-invalid-this": "off", - "no-irregular-whitespace": "warn", - "no-iterator": "warn", - "no-label-var": "warn", - "no-labels": "warn", - "no-lone-blocks": "warn", - "no-lonely-if": "warn", - "no-loop-func": "off", - "no-magic-numbers": "off", - "no-mixed-operators": "warn", - "no-mixed-requires": "warn", - "no-mixed-spaces-and-tabs": "warn", - "no-multi-spaces": "warn", - "no-multi-str": "warn", - "no-multiple-empty-lines": "warn", - "no-native-reassign": "warn", - "no-negated-condition": "warn", - "no-negated-in-lhs": "warn", - "no-nested-ternary": "off", - "no-new": "warn", - "no-new-func": "warn", - "no-new-object": "warn", - "no-new-require": "warn", - "no-new-symbol": "warn", - "no-new-wrappers": "warn", - "no-obj-calls": "warn", - "no-octal": "warn", - "no-octal-escape": "warn", - "no-param-reassign": "warn", - "no-path-concat": "warn", - "no-plusplus": "off", - "no-process-env": "warn", - "no-process-exit": "warn", - "no-proto": "warn", - "no-prototype-builtins": "warn", - "no-redeclare": "warn", - "no-regex-spaces": "warn", - "no-restricted-globals": "warn", - "no-restricted-imports": "warn", - "no-restricted-modules": "warn", - "no-restricted-syntax": "warn", - "no-return-assign": "warn", - "no-script-url": "warn", - "no-self-assign": "warn", - "no-self-compare": "warn", - "no-sequences": "warn", - "no-shadow": "warn", - "no-shadow-restricted-names": "warn", - "no-whitespace-before-property": "warn", - "no-spaced-func": "warn", - "no-sparse-arrays": "warn", - "no-sync": "warn", - "no-tabs": "warn", - "no-ternary": "off", - "no-trailing-spaces": "warn", - "no-this-before-super": "warn", - "no-throw-literal": "warn", - "no-undef": "warn", - "no-undef-init": "warn", - "no-undefined": "off", - "no-unexpected-multiline": "warn", - "no-underscore-dangle": "off", - "no-unmodified-loop-condition": "warn", - "no-unneeded-ternary": "warn", - "no-unreachable": "warn", - "no-unsafe-finally": "warn", - "no-unused-expressions": "warn", - "no-unused-labels": "warn", - "no-unused-vars": "warn", - "no-use-before-define": "warn", - "no-useless-call": "warn", - "no-useless-computed-key": "warn", - "no-useless-concat": "warn", - "no-useless-constructor": "warn", - "no-useless-escape": "warn", - "no-useless-rename": "warn", - "no-void": "warn", - "no-var": "off", - "no-warning-comments": "off", - "no-with": "warn", - "array-bracket-spacing": "off", - "array-callback-return": "warn", - "arrow-body-style": "warn", - "arrow-parens": "warn", - "arrow-spacing": "off", - "accessor-pairs": "warn", - "block-scoped-var": "off", - "block-spacing": ["warn", "always"], - "brace-style": "warn", - "callback-return": "warn", - "camelcase": "off", - "capitalized-comments": ["warn", "always", { - "ignoreConsecutiveComments": true, - "ignoreInlineComments": true - }], - "comma-dangle": ["warn", "always-multiline"], - "comma-spacing": ["warn", { - "before": false, - "after": true - }], - "comma-style": "warn", - "complexity": [ - "warn", - 15 - ], - "computed-property-spacing": "off", - "consistent-return": "off", - "consistent-this": "off", - "constructor-super": "warn", - "curly": "warn", - "default-case": "off", - "dot-location": ["warn", "property"], - "dot-notation": "warn", - "eol-last": "warn", - "eqeqeq": "warn", - "func-names": "off", - "func-style": "off", - "generator-star-spacing": "off", - "global-require": "warn", - "guard-for-in": "off", - "handle-callback-err": "warn", - "id-blacklist": "warn", - "id-length": "off", - "id-match": "warn", - "indent": "warn", - "init-declarations": "warn", - "jsx-quotes": "warn", - "key-spacing": "off", - "keyword-spacing": "warn", - "linebreak-style": [ - "warn", - "unix" - ], - "lines-around-comment": "warn", - "max-depth": "warn", - "max-len": ["warn", { - "code": 88, - "ignorePattern": "odoo\\.define\\(", - "tabWidth": 4 - }], - "max-lines": "off", - "max-nested-callbacks": "warn", - "max-params": "off", - "max-statements": "off", - "max-statements-per-line": "warn", - "multiline-ternary": "off", - "new-cap": "off", - "new-parens": "warn", - "newline-after-var": "off", - "newline-before-return": "off", - "newline-per-chained-call": "off", - "object-curly-newline": ["warn", { "consistent": true }], - "object-curly-spacing": ["warn", "never"], - "object-property-newline": ["warn", { - "allowAllPropertiesOnSameLine": true - }], - "object-shorthand": "off", - "one-var": "off", - "one-var-declaration-per-line": "off", - "operator-assignment": "warn", - "operator-linebreak": "warn", - "padded-blocks": "off", - "prefer-arrow-callback": "off", - "prefer-const": "warn", - "prefer-reflect": "off", - "prefer-rest-params": "off", - "prefer-spread": "off", - "prefer-template": "off", - "quote-props": "off", - "quotes": "off", - "radix": "warn", - "require-yield": "warn", - "rest-spread-spacing": "off", - "semi": [ - "warn", - "always" - ], - "semi-spacing": "warn", - "sort-imports": "warn", - "sort-vars": "off", - "space-before-blocks": "warn", - "space-before-function-paren": "warn", - "space-in-parens": "off", - "space-infix-ops": "off", - "space-unary-ops": "off", - "spaced-comment": ["warn", "always"], - "strict": ["warn", "function"], - "template-curly-spacing": "off", - "unicode-bom": "warn", - "use-isnan": "warn", - "valid-jsdoc": ["warn", { - "prefer": { - "arg": "param", - "argument": "param", - "augments": "extends", - "constructor": "class", - "exception": "throws", - "func": "function", - "method": "function", - "prop": "property", - "return": "returns", - "virtual": "abstract", - "yield": "yields" - }, - "preferType": { - "array": "Array", - "bool": "Boolean", - "boolean": "Boolean", - "number": "Number", - "object": "Object", - "str": "String", - "string": "String" - }, - "requireParamDescription": false, - "requireReturn": false, - "requireReturnDescription": false, - "requireReturnType": false - }], - "valid-typeof": "warn", - "vars-on-top": "off", - "wrap-iife": "warn", - "wrap-regex": "warn", - "yield-star-spacing": "off", - "yoda": "warn" - }, - "parserOptions": { - "ecmaVersion": 2017 - } -} diff --git a/.eslintrc.yml b/.eslintrc.yml new file mode 100644 index 000000000..88f2881b4 --- /dev/null +++ b/.eslintrc.yml @@ -0,0 +1,180 @@ +env: + browser: true + +# See https://github.com/OCA/odoo-community.org/issues/37#issuecomment-470686449 +parserOptions: + ecmaVersion: 2017 + +# Globals available in Odoo that shouldn't produce errorings +globals: + _: readonly + $: readonly + fuzzy: readonly + jQuery: readonly + moment: readonly + odoo: readonly + openerp: readonly + Promise: readonly + +# Styling is handled by Prettier, so we only need to enable AST rules; +# see https://github.com/OCA/maintainer-quality-tools/pull/618#issuecomment-558576890 +rules: + accessor-pairs: warn + array-callback-return: warn + callback-return: warn + capitalized-comments: + - warn + - always + - ignoreConsecutiveComments: true + ignoreInlineComments: true + complexity: + - warn + - 15 + constructor-super: warn + dot-notation: warn + eqeqeq: warn + global-require: warn + handle-callback-err: warn + id-blacklist: warn + id-match: warn + init-declarations: error + max-depth: warn + max-nested-callbacks: warn + max-statements-per-line: warn + no-alert: warn + no-array-constructor: warn + no-caller: warn + no-case-declarations: warn + no-class-assign: warn + no-cond-assign: error + no-const-assign: error + no-constant-condition: warn + no-control-regex: warn + no-debugger: error + no-delete-var: warn + no-div-regex: warn + no-dupe-args: error + no-dupe-class-members: error + no-dupe-keys: error + no-duplicate-case: error + no-duplicate-imports: error + no-else-return: warn + no-empty-character-class: warn + no-empty-function: error + no-empty-pattern: error + no-empty: warn + no-eq-null: error + no-eval: error + no-ex-assign: error + no-extend-native: warn + no-extra-bind: warn + no-extra-boolean-cast: warn + no-extra-label: warn + no-fallthrough: warn + no-func-assign: error + no-global-assign: error + no-implicit-coercion: + - warn + - allow: ["~"] + no-implicit-globals: warn + no-implied-eval: warn + no-inline-comments: warn + no-inner-declarations: warn + no-invalid-regexp: warn + no-irregular-whitespace: warn + no-iterator: warn + no-label-var: warn + no-labels: warn + no-lone-blocks: warn + no-lonely-if: error + no-mixed-requires: error + no-multi-str: warn + no-native-reassign: error + no-negated-condition: warn + no-negated-in-lhs: error + no-new-func: warn + no-new-object: warn + no-new-require: warn + no-new-symbol: warn + no-new-wrappers: warn + no-new: warn + no-obj-calls: warn + no-octal-escape: warn + no-octal: warn + no-param-reassign: warn + no-path-concat: warn + no-process-env: warn + no-process-exit: warn + no-proto: warn + no-prototype-builtins: warn + no-redeclare: warn + no-regex-spaces: warn + no-restricted-globals: warn + no-restricted-imports: warn + no-restricted-modules: warn + no-restricted-syntax: warn + no-return-assign: error + no-script-url: warn + no-self-assign: warn + no-self-compare: warn + no-sequences: warn + no-shadow-restricted-names: warn + no-shadow: warn + no-sparse-arrays: warn + no-sync: warn + no-this-before-super: warn + no-throw-literal: warn + no-undef-init: warn + no-undef: error + no-unmodified-loop-condition: warn + no-unneeded-ternary: error + no-unreachable: error + no-unsafe-finally: error + no-unused-expressions: error + no-unused-labels: error + no-unused-vars: error + no-use-before-define: error + no-useless-call: warn + no-useless-computed-key: warn + no-useless-concat: warn + no-useless-constructor: warn + no-useless-escape: warn + no-useless-rename: warn + no-void: warn + no-with: warn + operator-assignment: [error, always] + prefer-const: warn + radix: warn + require-yield: warn + sort-imports: warn + spaced-comment: [error, always] + strict: [error, function] + use-isnan: error + valid-jsdoc: + - warn + - prefer: + arg: param + argument: param + augments: extends + constructor: class + exception: throws + func: function + method: function + prop: property + return: returns + virtual: abstract + yield: yields + preferType: + array: Array + bool: Boolean + boolean: Boolean + number: Number + object: Object + str: String + string: String + requireParamDescription: false + requireReturn: false + requireReturnDescription: false + requireReturnType: false + valid-typeof: warn + yoda: warn diff --git a/.isort.cfg b/.isort.cfg index faa98e432..bd0a0dd68 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -10,3 +10,4 @@ known_odoo=odoo known_odoo_addons=odoo.addons sections=FUTURE,STDLIB,THIRDPARTY,ODOO,ODOO_ADDONS,FIRSTPARTY,LOCALFOLDER known_third_party=PyPDF2,mock,pkg_resources,requests,setuptools,werkzeug +default_section=THIRDPARTY diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 876d1e98e..750158169 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,68 +1,99 @@ -exclude: "^setup/|/static/lib/|/static/src/lib/" +exclude: | + (?x) + # Files and folders generated by bots, to avoid loops + ^setup/|/static/description/index\.html$| + # Maybe reactivate this when all README files include prettier ignore tags? + ^README\.md$| + # Library files can have extraneous formatting (even minimized) + /static/(src/)?lib/| + # Repos using Sphinx to generate docs don't need prettying + ^docs/_templates/.*\.html$| + # You don't usually want a bot to modify your legal texts + (LICENSE.*|COPYING.*) default_language_version: python: python3 repos: -- repo: https://github.com/psf/black - rev: 19.3b0 - hooks: - - id: black -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v2.3.0 - hooks: - - id: trailing-whitespace - # exclude autogenerated files - exclude: /README\.rst$|\.pot?$ - - id: end-of-file-fixer - # exclude autogenerated files - exclude: /README\.rst$|\.pot?$ - - id: debug-statements - - id: flake8 - name: flake8 except __init__.py - exclude: /__init__\.py$ - additional_dependencies: ["flake8-bugbear==19.8.0"] - - id: flake8 - name: flake8 only __init__.py - args: ["--extend-ignore=F401"] # ignore unused imports in __init__.py - files: /__init__\.py$ - additional_dependencies: ["flake8-bugbear==19.8.0"] - - id: fix-encoding-pragma - args: ["--remove"] - - id: check-case-conflict - - id: check-docstring-first - - id: check-executables-have-shebangs - - id: check-merge-conflict - - id: check-symlinks - - id: check-xml - - id: mixed-line-ending - args: ["--fix=lf"] -- repo: https://github.com/pre-commit/mirrors-pylint - rev: v2.3.1 - hooks: - - id: pylint - name: pylint with optional checks - args: ["--rcfile=.pylintrc", "--exit-zero"] - verbose: true - additional_dependencies: ["pylint-odoo==3.0.3"] - - id: pylint - name: pylint with mandatory checks - args: ["--rcfile=.pylintrc-mandatory"] - additional_dependencies: ["pylint-odoo==3.0.3"] -- repo: https://github.com/asottile/pyupgrade - rev: v1.24.0 - hooks: - - id: pyupgrade -- repo: https://github.com/asottile/seed-isort-config - rev: v1.9.3 - hooks: - - id: seed-isort-config -- repo: https://github.com/pre-commit/mirrors-isort - rev: v4.3.21 - hooks: - - id: isort - name: isort except __init__.py - exclude: /__init__\.py$ -- repo: https://github.com/pre-commit/mirrors-eslint - rev: v6.5.1 - hooks: - - id: eslint - verbose: true + - repo: https://github.com/psf/black + rev: 19.10b0 + hooks: + - id: black + - repo: https://github.com/prettier/prettier + rev: "1.19.1" + hooks: + - id: prettier + # TODO Avoid awebdeveloper/pre-commit-prettier if possible + # HACK https://github.com/prettier/prettier/issues/7407 + - repo: https://github.com/awebdeveloper/pre-commit-prettier + rev: v0.0.1 + hooks: + - id: prettier + name: prettier xml plugin + additional_dependencies: + - "prettier@1.19.1" + - "@prettier/plugin-xml@0.7.2" + files: \.xml$ + - repo: https://github.com/pre-commit/mirrors-eslint + rev: v6.8.0 + hooks: + - id: eslint + verbose: true + args: + - --color + - --fix + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.4.0 + hooks: + - id: trailing-whitespace + # exclude autogenerated files + exclude: /README\.rst$|\.pot?$ + - id: end-of-file-fixer + # exclude autogenerated files + exclude: /README\.rst$|\.pot?$ + - id: debug-statements + - id: flake8 + name: flake8 except __init__.py + exclude: /__init__\.py$ + additional_dependencies: ["flake8-bugbear==19.8.0"] + - id: flake8 + name: flake8 only __init__.py + args: ["--extend-ignore=F401"] # ignore unused imports in __init__.py + files: /__init__\.py$ + additional_dependencies: ["flake8-bugbear==19.8.0"] + - id: fix-encoding-pragma + args: ["--remove"] + - id: check-case-conflict + - id: check-docstring-first + - id: check-executables-have-shebangs + - id: check-merge-conflict + # exclude files where underlines are not distinguishable from merge conflicts + exclude: /README\.rst$|^docs/.*\.rst$ + - id: check-symlinks + - id: check-xml + - id: mixed-line-ending + args: ["--fix=lf"] + - repo: https://github.com/pre-commit/mirrors-pylint + rev: v2.3.1 + hooks: + - id: pylint + name: pylint with optional checks + args: ["--rcfile=.pylintrc", "--exit-zero"] + verbose: true + additional_dependencies: ["pylint-odoo==3.1.0"] + - id: pylint + name: pylint with mandatory checks + args: ["--rcfile=.pylintrc-mandatory"] + additional_dependencies: ["pylint-odoo==3.1.0"] + - repo: https://github.com/asottile/pyupgrade + rev: v1.26.2 + hooks: + - id: pyupgrade + - repo: https://github.com/pre-commit/mirrors-isort + rev: v4.3.21 + hooks: + - id: isort + name: isort except __init__.py + exclude: /__init__\.py$ + - repo: https://github.com/acsone/setuptools-odoo + rev: 2.5.2 + hooks: + - id: setuptools-odoo-make-default diff --git a/.prettierrc.yml b/.prettierrc.yml new file mode 100644 index 000000000..fc5f3a7c4 --- /dev/null +++ b/.prettierrc.yml @@ -0,0 +1,8 @@ +# Defaults for all prettier-supported languages. +# Prettier will complete this with settings from .editorconfig file. +bracketSpacing: false +printWidth: 88 +proseWrap: always +semi: true +trailingComma: "es5" +xmlWhitespaceSensitivity: "ignore" diff --git a/.travis.yml b/.travis.yml index 4429bc937..e341c3b52 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ language: python cache: directories: - - $HOME/.cache/pip - - $HOME/.cache/pre-commit + - $HOME/.cache/pip + - $HOME/.cache/pre-commit python: - "3.6" @@ -24,7 +24,7 @@ jobs: name: "pre-commit" before_install: install: pip install pre-commit - script: pre-commit run --all --show-diff-on-failure + script: pre-commit run --all --show-diff-on-failure --verbose --color always after_success: - stage: test env: @@ -35,10 +35,11 @@ jobs: env: global: - - VERSION="13.0" TESTS="0" LINT_CHECK="0" MAKEPOT="0" + - VERSION="13.0" TESTS="0" LINT_CHECK="0" MAKEPOT="0" install: - - git clone --depth=1 https://github.com/OCA/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools + - git clone --depth=1 https://github.com/OCA/maintainer-quality-tools.git + ${HOME}/maintainer-quality-tools - export PATH=${HOME}/maintainer-quality-tools/travis:${PATH} - travis_install_nightly diff --git a/report_batch/README.rst b/report_batch/README.rst new file mode 100644 index 000000000..d4e246526 --- /dev/null +++ b/report_batch/README.rst @@ -0,0 +1,132 @@ +===================== +Batch Report Printing +===================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Freporting--engine-lightgray.png?logo=github + :target: https://github.com/OCA/reporting-engine/tree/13.0/report_batch + :alt: OCA/reporting-engine +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/reporting-engine-13-0/reporting-engine-13-0-report_batch + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/143/13.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module will allow to to print multiple QWeb reports in a single batch. +For example, picking, packing, reports needs to be printed together +in a single batch. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +We can configure this module in following two ways: + +1st Way +======= + +* Create a Report or edit existing report from Settings > Users & Companies > + Batch Reports +* Add sequence number and select parent report or design your own batch report. +* Select subreport to add reports and save it. + +2st Way +======= + +* Active developer mode. Go to Settings > Technical > Actions > Reports. +* Create or edit report and select related model. +* Jump on "Batch report" tab and add subreport one by one. +* Drag up and down and arrange a sequence of report. Save it. + +Usage +===== + +If we configure it like following values: + +* Name = Picking Batch Report +* Model = Transfer +* Template Name = my.picking_batch_report +* Add subreport = Delivery Slip and Picking Operations +* Save it. + +To get results: + +* Go to Inventory > Operations > Transfers +* Open one record or select multi records. +* Click on "Print" option near by "Action". +* Print "Picking Batch Report" +* We will get delivery slip and picking operations report together. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Open Source Integrators + +Contributors +~~~~~~~~~~~~ + +* Bhavesh Odedra +* Balaji Kannan +* Daniel Reis + +Other credits +~~~~~~~~~~~~~ + +The development of this module has been financially supported by: + +* Open Source Integrators + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +.. |maintainer-bodedra| image:: https://github.com/bodedra.png?size=40px + :target: https://github.com/bodedra + :alt: bodedra + +Current `maintainer `__: + +|maintainer-bodedra| + +This module is part of the `OCA/reporting-engine `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/report_batch/__init__.py b/report_batch/__init__.py new file mode 100644 index 000000000..f1ef2417f --- /dev/null +++ b/report_batch/__init__.py @@ -0,0 +1,5 @@ +# Copyright (C) 2019 IBM Corp. +# Copyright (C) 2019 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import models diff --git a/report_batch/__manifest__.py b/report_batch/__manifest__.py new file mode 100644 index 000000000..f1fd28c7b --- /dev/null +++ b/report_batch/__manifest__.py @@ -0,0 +1,17 @@ +# Copyright (C) 2019 IBM Corp. +# Copyright (C) 2019 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Batch Report Printing", + "summary": "Ability to print multiple QWeb reports in a single batch.", + "version": "13.0.1.0.1", + "license": "AGPL-3", + "author": "Open Source Integrators, Odoo Community Association (OCA)", + "category": "Reporting", + "website": "https://github.com/OCA/reporting-engine", + "depends": ["stock"], + "data": ["security/ir.model.access.csv", "views/ir_action_report_view.xml"], + "installable": True, + "maintainers": ["bodedra"], +} diff --git a/report_batch/i18n/report_batch.pot b/report_batch/i18n/report_batch.pot new file mode 100644 index 000000000..d5fd5b31b --- /dev/null +++ b/report_batch/i18n/report_batch.pot @@ -0,0 +1,98 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * report_batch +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: report_batch +#: model_terms:ir.actions.act_window,help:report_batch.action_ir_actions_subreport +msgid "Allows to add multi QWeb reports in a single batch" +msgstr "" + +#. module: report_batch +#: model_terms:ir.ui.view,arch_db:report_batch.act_batch_report_xml_view +msgid "Batch Report" +msgstr "" + +#. module: report_batch +#: model:ir.actions.act_window,name:report_batch.action_ir_actions_subreport +#: model:ir.ui.menu,name:report_batch.menu_action_ir_actions_subreport +#: model_terms:ir.ui.view,arch_db:report_batch.act_batch_report_xml_view +#: model_terms:ir.ui.view,arch_db:report_batch.view_ir_actions_subreport_tree +msgid "Batch Reports" +msgstr "" + +#. module: report_batch +#: model:ir.model.fields,field_description:report_batch.field_ir_actions_report_subreport__create_uid +msgid "Created by" +msgstr "" + +#. module: report_batch +#: model:ir.model.fields,field_description:report_batch.field_ir_actions_report_subreport__create_date +msgid "Created on" +msgstr "" + +#. module: report_batch +#: model:ir.model.fields,field_description:report_batch.field_ir_actions_report_subreport__display_name +msgid "Display Name" +msgstr "" + +#. module: report_batch +#: model:ir.model.fields,field_description:report_batch.field_ir_actions_report_subreport__id +msgid "ID" +msgstr "" + +#. module: report_batch +#: model:ir.model.fields,field_description:report_batch.field_ir_actions_report_subreport____last_update +msgid "Last Modified on" +msgstr "" + +#. module: report_batch +#: model:ir.model.fields,field_description:report_batch.field_ir_actions_report_subreport__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: report_batch +#: model:ir.model.fields,field_description:report_batch.field_ir_actions_report_subreport__write_date +msgid "Last Updated on" +msgstr "" + +#. module: report_batch +#: model:ir.model.fields,field_description:report_batch.field_ir_actions_report_subreport__model +msgid "Model Name" +msgstr "" + +#. module: report_batch +#: model:ir.model.fields,field_description:report_batch.field_ir_actions_report_subreport__parent_report_id +msgid "Parent Report" +msgstr "" + +#. module: report_batch +#: model:ir.model,name:report_batch.model_ir_actions_report +msgid "Report Action" +msgstr "" + +#. module: report_batch +#: model:ir.model,name:report_batch.model_ir_actions_report_subreport +msgid "Report Subreport" +msgstr "" + +#. module: report_batch +#: model:ir.model.fields,field_description:report_batch.field_ir_actions_report_subreport__sequence +msgid "Sequence" +msgstr "" + +#. module: report_batch +#: model:ir.model.fields,field_description:report_batch.field_ir_actions_report__subreport_ids +#: model:ir.model.fields,field_description:report_batch.field_ir_actions_report_subreport__subreport_id +msgid "Subreport" +msgstr "" diff --git a/report_batch/models/__init__.py b/report_batch/models/__init__.py new file mode 100644 index 000000000..95d1c5045 --- /dev/null +++ b/report_batch/models/__init__.py @@ -0,0 +1,5 @@ +# Copyright (C) 2019 IBM Corp. +# Copyright (C) 2019 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import ir_action_report diff --git a/report_batch/models/ir_action_report.py b/report_batch/models/ir_action_report.py new file mode 100644 index 000000000..710f3e3ec --- /dev/null +++ b/report_batch/models/ir_action_report.py @@ -0,0 +1,108 @@ +# Copyright (C) 2019 IBM Corp. +# Copyright (C) 2019 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import random + +from odoo import api, fields, models + + +class IrActionsReportSubreport(models.Model): + _name = "ir.actions.report.subreport" + _description = "Report Subreport" + _order = "sequence" + + parent_report_id = fields.Many2one("ir.actions.report", ondelete="cascade") + sequence = fields.Integer(default=10) + model = fields.Char(related="parent_report_id.model") + subreport_id = fields.Many2one( + "ir.actions.report", string="Subreport", required=True + ) + + +class IrActionsReport(models.Model): + _inherit = "ir.actions.report" + + subreport_ids = fields.One2many("ir.actions.report.subreport", "parent_report_id") + + def generate_top_part(self): + return ( + """\n\t\n\t + """ + % self.report_name + ) + + def generate_bottom_part(self): + return """\n + \t\t\n\t\t""" + + def generate_custom_content(self, report_name): + return ( + """\n + \t""" + % report_name + ) + + def _generate_batch_qweb_report(self, update_batch_qweb=False): + report_name = self.report_name + if "." in report_name: + module = self.report_name.split(".")[0] + report_name = self.report_name.split(".")[1] + else: + # Generate random number to avoid IntegrityError + module = random.randint(1, 1000000) + self.report_name = "{}.{}".format(module, report_name) + if self.subreport_ids: + if update_batch_qweb: + report_name = self.report_name.split(".")[1] + # Delete old Qweb batch report + model_data = self.env["ir.model.data"].search( + [("res_id", "=", self.id)] + ) + model_data.unlink() + ui_view = self.env["ir.ui.view"].search([("name", "=", report_name)]) + ui_view.unlink() + template_header = self.generate_top_part() + template_footer = self.generate_bottom_part() + template_content = "" + + for subreport in self.subreport_ids: + template_content += self.generate_custom_content( + subreport.subreport_id.report_name + ) + data = "{}{}{}".format(template_header, template_content, template_footer) + ui_view = self.env["ir.ui.view"].create( + { + "name": report_name, + "type": "qweb", + "model": self.model, + "mode": "primary", + "arch_base": data, + } + ) + self.env["ir.model.data"].create( + { + "module": module, + "name": report_name, + "res_id": ui_view.id, + "model": "ir.ui.view", + } + ) + # Register batch report option + if not self.binding_model_id: + self.create_action() + return True + + @api.model + def create(self, vals): + res = super(IrActionsReport, self).create(vals) + for report in res: + report._generate_batch_qweb_report() + return res + + def write(self, vals): + res = super(IrActionsReport, self).write(vals) + if "subreport_ids" in vals or "model" in vals: + for report in self: + report._generate_batch_qweb_report(update_batch_qweb=True) + return res diff --git a/report_batch/readme/CONFIGURE.rst b/report_batch/readme/CONFIGURE.rst new file mode 100644 index 000000000..d936942ab --- /dev/null +++ b/report_batch/readme/CONFIGURE.rst @@ -0,0 +1,17 @@ +We can configure this module in following two ways: + +1st Way +======= + +* Create a Report or edit existing report from Settings > Users & Companies > + Batch Reports +* Add sequence number and select parent report or design your own batch report. +* Select subreport to add reports and save it. + +2st Way +======= + +* Active developer mode. Go to Settings > Technical > Actions > Reports. +* Create or edit report and select related model. +* Jump on "Batch report" tab and add subreport one by one. +* Drag up and down and arrange a sequence of report. Save it. diff --git a/report_batch/readme/CONTRIBUTORS.rst b/report_batch/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..910e80b9a --- /dev/null +++ b/report_batch/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* Bhavesh Odedra +* Balaji Kannan +* Daniel Reis diff --git a/report_batch/readme/CREDITS.rst b/report_batch/readme/CREDITS.rst new file mode 100644 index 000000000..8209266d9 --- /dev/null +++ b/report_batch/readme/CREDITS.rst @@ -0,0 +1,3 @@ +The development of this module has been financially supported by: + +* Open Source Integrators diff --git a/report_batch/readme/DESCRIPTION.rst b/report_batch/readme/DESCRIPTION.rst new file mode 100644 index 000000000..6010b45e6 --- /dev/null +++ b/report_batch/readme/DESCRIPTION.rst @@ -0,0 +1,3 @@ +This module will allow to to print multiple QWeb reports in a single batch. +For example, picking, packing, reports needs to be printed together +in a single batch. diff --git a/report_batch/readme/USAGE.rst b/report_batch/readme/USAGE.rst new file mode 100644 index 000000000..fbcd85bf1 --- /dev/null +++ b/report_batch/readme/USAGE.rst @@ -0,0 +1,15 @@ +If we configure it like following values: + +* Name = Picking Batch Report +* Model = Transfer +* Template Name = my.picking_batch_report +* Add subreport = Delivery Slip and Picking Operations +* Save it. + +To get results: + +* Go to Inventory > Operations > Transfers +* Open one record or select multi records. +* Click on "Print" option near by "Action". +* Print "Picking Batch Report" +* We will get delivery slip and picking operations report together. diff --git a/report_batch/security/ir.model.access.csv b/report_batch/security/ir.model.access.csv new file mode 100644 index 000000000..59dcd473b --- /dev/null +++ b/report_batch/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_ir_actions_subreport_all,ir_actions_report_subreport,model_ir_actions_report_subreport,,1,0,0,0 +access_ir_actions_subreport_group_system,ir_actions_report_subreport_group_system,model_ir_actions_report_subreport,base.group_system,1,1,1,1 diff --git a/report_batch/static/description/icon.png b/report_batch/static/description/icon.png new file mode 100644 index 000000000..3a0328b51 Binary files /dev/null and b/report_batch/static/description/icon.png differ diff --git a/report_batch/static/description/index.html b/report_batch/static/description/index.html new file mode 100644 index 000000000..51ba1ac0a --- /dev/null +++ b/report_batch/static/description/index.html @@ -0,0 +1,478 @@ + + + + + + +Batch Report Printing + + + +
+

Batch Report Printing

+ + +

Beta License: AGPL-3 OCA/reporting-engine Translate me on Weblate Try me on Runbot

+

This module will allow to to print multiple QWeb reports in a single batch. +For example, picking, packing, reports needs to be printed together +in a single batch.

+

Table of contents

+ +
+

Configuration

+

We can configure this module in following two ways:

+
+
+

1st Way

+
    +
  • Create a Report or edit existing report from Settings > Users & Companies > +Batch Reports
  • +
  • Add sequence number and select parent report or design your own batch report.
  • +
  • Select subreport to add reports and save it.
  • +
+
+
+

2st Way

+
    +
  • Active developer mode. Go to Settings > Technical > Actions > Reports.
  • +
  • Create or edit report and select related model.
  • +
  • Jump on “Batch report” tab and add subreport one by one.
  • +
  • Drag up and down and arrange a sequence of report. Save it.
  • +
+
+
+

Usage

+

If we configure it like following values:

+
    +
  • Name = Picking Batch Report
  • +
  • Model = Transfer
  • +
  • Template Name = my.picking_batch_report
  • +
  • Add subreport = Delivery Slip and Picking Operations
  • +
  • Save it.
  • +
+

To get results:

+
    +
  • Go to Inventory > Operations > Transfers
  • +
  • Open one record or select multi records.
  • +
  • Click on “Print” option near by “Action”.
  • +
  • Print “Picking Batch Report”
  • +
  • We will get delivery slip and picking operations report together.
  • +
+
+
+

Bug Tracker

+

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

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Open Source Integrators
  • +
+
+ +
+

Other credits

+

The development of this module has been financially supported by:

+
    +
  • Open Source Integrators
  • +
+
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

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

+

Current maintainer:

+

bodedra

+

This module is part of the OCA/reporting-engine project on GitHub.

+

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

+
+
+
+ + diff --git a/report_batch/tests/__init__.py b/report_batch/tests/__init__.py new file mode 100644 index 000000000..b3bef7e31 --- /dev/null +++ b/report_batch/tests/__init__.py @@ -0,0 +1,5 @@ +# Copyright (C) 2020 IBM Corp. +# Copyright (C) 2020 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import test_report_batch diff --git a/report_batch/tests/test_report_batch.py b/report_batch/tests/test_report_batch.py new file mode 100644 index 000000000..ee9a9f437 --- /dev/null +++ b/report_batch/tests/test_report_batch.py @@ -0,0 +1,36 @@ +# Copyright (C) 2019 IBM Corp. +# Copyright (C) 2019 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import odoo.tests.common as test_common + + +class TestReportBatch(test_common.SingleTransactionCase): + def setUp(self): + super(TestReportBatch, self).setUp() + self.report_picking_operations = self.env.ref("stock.action_report_picking") + self.report_deliveryslip = self.env.ref("stock.report_deliveryslip") + + def test_report_batch(self): + report_batch = self.env["ir.actions.report"].create( + { + "name": "Batch Report", + "report_type": "qweb-pdf", + "model": "stock.picking", + "report_name": "my_custom_batch_report", + } + ) + report_batch.write( + { + "subreport_ids": [ + ( + 6, + 0, + [ + self.report_picking_operations.id, + self.report_deliveryslip.id, + ], + ) + ] + } + ) diff --git a/report_batch/views/ir_action_report_view.xml b/report_batch/views/ir_action_report_view.xml new file mode 100644 index 000000000..aedc27a02 --- /dev/null +++ b/report_batch/views/ir_action_report_view.xml @@ -0,0 +1,50 @@ + + + + ir.actions.batch.report.form + ir.actions.report + + + + + + + + + + + + + + + + + + + ir.actions.report.subreport.tree + ir.actions.report.subreport + + + + + + + + + + + Batch Reports + ir.actions.act_window + ir.actions.report.subreport + Allows to add multi QWeb reports in a single batch + + + diff --git a/report_csv/README.rst b/report_csv/README.rst new file mode 100644 index 000000000..a594624eb --- /dev/null +++ b/report_csv/README.rst @@ -0,0 +1,117 @@ +=============== +Base report csv +=============== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Freporting--engine-lightgray.png?logo=github + :target: https://github.com/OCA/reporting-engine/tree/13.0/report_csv + :alt: OCA/reporting-engine +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/reporting-engine-13-0/reporting-engine-13-0-report_csv + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/143/13.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module provides a basic report class to generate csv report. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +An example of CSV report for partners on a module called `module_name`: + +A python class :: + + from odoo import models + + class PartnerCSV(models.AbstractModel): + _name = 'report.report_csv.partner_csv' + _inherit = 'report.report_csv.abstract' + + def generate_csv_report(self, writer, data, partners): + writer.writeheader() + for obj in partners: + writer.writerow({ + 'name': obj.name, + 'email': obj.email, + }) + + def csv_report_options(self): + res = super().csv_report_options() + res['fieldnames'].append('name') + res['fieldnames'].append('email') + res['delimiter'] = ';' + res['quoting'] = csv.QUOTE_ALL + return res + + +A report XML record :: + + + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Creu Blanca + +Contributors +~~~~~~~~~~~~ + +* Enric Tobella +* Jaime Arroyo +* Rattapong Chokmasermkul + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/reporting-engine `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/report_csv/__init__.py b/report_csv/__init__.py new file mode 100644 index 000000000..9b6fa04ee --- /dev/null +++ b/report_csv/__init__.py @@ -0,0 +1,3 @@ +from . import controllers +from . import models +from . import report diff --git a/report_csv/__manifest__.py b/report_csv/__manifest__.py new file mode 100644 index 000000000..76e7bc8a0 --- /dev/null +++ b/report_csv/__manifest__.py @@ -0,0 +1,16 @@ +# Copyright 2019 Creu Blanca +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +{ + "name": "Base report csv", + "summary": "Base module to create csv report", + "author": "Creu Blanca, Odoo Community Association (OCA)", + "website": "https://github.com/oca/reporting-engine", + "category": "Reporting", + "version": "13.0.1.0.1", + "license": "AGPL-3", + "external_dependencies": {"python": ["csv"]}, + "depends": ["base", "web"], + "data": ["views/webclient_templates.xml"], + "demo": ["demo/report.xml"], + "installable": True, +} diff --git a/report_csv/controllers/__init__.py b/report_csv/controllers/__init__.py new file mode 100644 index 000000000..12a7e529b --- /dev/null +++ b/report_csv/controllers/__init__.py @@ -0,0 +1 @@ +from . import main diff --git a/report_csv/controllers/main.py b/report_csv/controllers/main.py new file mode 100644 index 000000000..700a75a56 --- /dev/null +++ b/report_csv/controllers/main.py @@ -0,0 +1,57 @@ +# Copyright (C) 2019 Creu Blanca +# License AGPL-3.0 or later (https://www.gnuorg/licenses/agpl.html). + +import json +import time + +from odoo.http import content_disposition, request, route +from odoo.tools.safe_eval import safe_eval + +from odoo.addons.web.controllers import main as report + + +class ReportController(report.ReportController): + @route() + def report_routes(self, reportname, docids=None, converter=None, **data): + if converter == "csv": + report = request.env["ir.actions.report"]._get_report_from_name(reportname) + context = dict(request.env.context) + if docids: + docids = [int(i) for i in docids.split(",")] + if data.get("options"): + data.update(json.loads(data.pop("options"))) + if data.get("context"): + # Ignore 'lang' here, because the context in data is the one + # from the webclient *but* if the user explicitely wants to + # change the lang, this mechanism overwrites it. + data["context"] = json.loads(data["context"]) + if data["context"].get("lang"): + del data["context"]["lang"] + context.update(data["context"]) + csv = report.with_context(context).render_csv(docids, data=data)[0] + filename = "{}.{}".format(report.name, "csv") + if docids: + obj = request.env[report.model].browse(docids) + if report.print_report_name and not len(obj) > 1: + report_name = safe_eval( + report.print_report_name, + {"object": obj, "time": time, "multi": False}, + ) + filename = "{}.{}".format(report_name, "csv") + # When we print multiple records we still allow a custom + # filename. + elif report.print_report_name and len(obj) > 1: + report_name = safe_eval( + report.print_report_name, + {"objects": obj, "time": time, "multi": True}, + ) + filename = "{}.{}".format(report_name, "csv") + csvhttpheaders = [ + ("Content-Type", "text/csv"), + ("Content-Length", len(csv)), + ("Content-Disposition", content_disposition(filename)), + ] + return request.make_response(csv, headers=csvhttpheaders) + return super(ReportController, self).report_routes( + reportname, docids, converter, **data + ) diff --git a/report_csv/demo/report.xml b/report_csv/demo/report.xml new file mode 100644 index 000000000..72c741dec --- /dev/null +++ b/report_csv/demo/report.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/report_csv/i18n/report_csv.pot b/report_csv/i18n/report_csv.pot new file mode 100644 index 000000000..f5b7cca52 --- /dev/null +++ b/report_csv/i18n/report_csv.pot @@ -0,0 +1,93 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * report_csv +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: report_csv +#: code:addons/report_csv/models/ir_report.py:0 +#, python-format +msgid "%s model was not found" +msgstr "" + +#. module: report_csv +#. openerp-web +#: code:addons/report_csv/static/src/js/report/qwebactionmanager.js:0 +#, python-format +msgid "" +"A popup window with your report was blocked. You may need to change your " +"browser settings to allow popup windows for this page." +msgstr "" + +#. module: report_csv +#: model:ir.model,name:report_csv.model_report_report_csv_abstract +msgid "Abstract Model for CSV reports" +msgstr "" + +#. module: report_csv +#: model:ir.model.fields,field_description:report_csv.field_report_report_csv_abstract__display_name +#: model:ir.model.fields,field_description:report_csv.field_report_report_csv_partner_csv__display_name +msgid "Display Name" +msgstr "" + +#. module: report_csv +#: model:ir.model.fields,field_description:report_csv.field_report_report_csv_abstract__id +#: model:ir.model.fields,field_description:report_csv.field_report_report_csv_partner_csv__id +msgid "ID" +msgstr "" + +#. module: report_csv +#: model:ir.model.fields,field_description:report_csv.field_report_report_csv_abstract____last_update +#: model:ir.model.fields,field_description:report_csv.field_report_report_csv_partner_csv____last_update +msgid "Last Modified on" +msgstr "" + +#. module: report_csv +#: model:ir.actions.report,name:report_csv.partner_csv +msgid "Print to CSV" +msgstr "" + +#. module: report_csv +#: model:ir.model,name:report_csv.model_ir_actions_report +msgid "Report Action" +msgstr "" + +#. module: report_csv +#: model:ir.model,name:report_csv.model_report_report_csv_partner_csv +msgid "Report Partner to CSV" +msgstr "" + +#. module: report_csv +#: model:ir.model.fields,field_description:report_csv.field_ir_actions_report__report_type +msgid "Report Type" +msgstr "" + +#. module: report_csv +#: model:ir.model.fields,help:report_csv.field_ir_actions_report__report_type +msgid "" +"The type of the report that will be rendered, each one having its own " +"rendering method. HTML means the report will be opened directly in your " +"browser PDF means the report will be rendered using Wkhtmltopdf and " +"downloaded by the user." +msgstr "" + +#. module: report_csv +#. openerp-web +#: code:addons/report_csv/static/src/js/report/qwebactionmanager.js:0 +#, python-format +msgid "Warning" +msgstr "" + +#. module: report_csv +#: model:ir.model.fields.selection,name:report_csv.selection__ir_actions_report__report_type__csv +msgid "csv" +msgstr "" diff --git a/report_csv/models/__init__.py b/report_csv/models/__init__.py new file mode 100644 index 000000000..54dbf3df6 --- /dev/null +++ b/report_csv/models/__init__.py @@ -0,0 +1 @@ +from . import ir_report diff --git a/report_csv/models/ir_report.py b/report_csv/models/ir_report.py new file mode 100644 index 000000000..9865cff75 --- /dev/null +++ b/report_csv/models/ir_report.py @@ -0,0 +1,35 @@ +# Copyright 2019 Creu Blanca +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import _, api, fields, models +from odoo.exceptions import UserError + + +class ReportAction(models.Model): + _inherit = "ir.actions.report" + + report_type = fields.Selection(selection_add=[("csv", "csv")]) + + @api.model + def render_csv(self, docids, data): + report_model_name = "report.%s" % self.report_name + report_model = self.env.get(report_model_name) + if report_model is None: + raise UserError(_("%s model was not found" % report_model_name)) + return report_model.with_context( + {"active_model": self.model} + ).create_csv_report(docids, data) + + @api.model + def _get_report_from_name(self, report_name): + res = super(ReportAction, self)._get_report_from_name(report_name) + if res: + return res + report_obj = self.env["ir.actions.report"] + qwebtypes = ["csv"] + conditions = [ + ("report_type", "in", qwebtypes), + ("report_name", "=", report_name), + ] + context = self.env["res.users"].context_get() + return report_obj.with_context(context).search(conditions, limit=1) diff --git a/report_csv/readme/CONTRIBUTORS.rst b/report_csv/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..1ee404f73 --- /dev/null +++ b/report_csv/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* Enric Tobella +* Jaime Arroyo +* Rattapong Chokmasermkul diff --git a/report_csv/readme/DESCRIPTION.rst b/report_csv/readme/DESCRIPTION.rst new file mode 100644 index 000000000..636b3884f --- /dev/null +++ b/report_csv/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +This module provides a basic report class to generate csv report. diff --git a/report_csv/readme/USAGE.rst b/report_csv/readme/USAGE.rst new file mode 100644 index 000000000..e5d9964cb --- /dev/null +++ b/report_csv/readme/USAGE.rst @@ -0,0 +1,38 @@ +An example of CSV report for partners on a module called `module_name`: + +A python class :: + + from odoo import models + + class PartnerCSV(models.AbstractModel): + _name = 'report.report_csv.partner_csv' + _inherit = 'report.report_csv.abstract' + + def generate_csv_report(self, writer, data, partners): + writer.writeheader() + for obj in partners: + writer.writerow({ + 'name': obj.name, + 'email': obj.email, + }) + + def csv_report_options(self): + res = super().csv_report_options() + res['fieldnames'].append('name') + res['fieldnames'].append('email') + res['delimiter'] = ';' + res['quoting'] = csv.QUOTE_ALL + return res + + +A report XML record :: + + diff --git a/report_csv/report/__init__.py b/report_csv/report/__init__.py new file mode 100644 index 000000000..941755038 --- /dev/null +++ b/report_csv/report/__init__.py @@ -0,0 +1,2 @@ +from . import report_csv +from . import report_partner_csv diff --git a/report_csv/report/report_csv.py b/report_csv/report/report_csv.py new file mode 100644 index 000000000..0d9aeffdd --- /dev/null +++ b/report_csv/report/report_csv.py @@ -0,0 +1,61 @@ +# Copyright 2019 Creu Blanca +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +import logging +from io import StringIO + +from odoo import models + +_logger = logging.getLogger(__name__) + +try: + import csv +except ImportError: + _logger.debug("Can not import csvwriter`.") + + +class ReportCSVAbstract(models.AbstractModel): + _name = "report.report_csv.abstract" + _description = "Abstract Model for CSV reports" + + def _get_objs_for_report(self, docids, data): + """ + Returns objects for csv report. From WebUI these + are either as docids taken from context.active_ids or + in the case of wizard are in data. Manual calls may rely + on regular context, setting docids, or setting data. + + :param docids: list of integers, typically provided by + qwebactionmanager for regular Models. + :param data: dictionary of data, if present typically provided + by qwebactionmanager for TransientModels. + :param ids: list of integers, provided by overrides. + :return: recordset of active model for ids. + """ + if docids: + ids = docids + elif data and "context" in data: + ids = data["context"].get("active_ids", []) + else: + ids = self.env.context.get("active_ids", []) + return self.env[self.env.context.get("active_model")].browse(ids) + + def create_csv_report(self, docids, data): + objs = self._get_objs_for_report(docids, data) + file_data = StringIO() + file = csv.DictWriter(file_data, **self.csv_report_options()) + self.generate_csv_report(file, data, objs) + file_data.seek(0) + return file_data.read(), "csv" + + def csv_report_options(self): + """ + :return: dictionary of parameters. At least return 'fieldnames', but + you can optionally return parameters that define the export format. + Valid parameters include 'delimiter', 'quotechar', 'escapechar', + 'doublequote', 'skipinitialspace', 'lineterminator', 'quoting'. + """ + return {"fieldnames": []} + + def generate_csv_report(self, file, data, objs): + raise NotImplementedError() diff --git a/report_csv/report/report_partner_csv.py b/report_csv/report/report_partner_csv.py new file mode 100644 index 000000000..247c906e1 --- /dev/null +++ b/report_csv/report/report_partner_csv.py @@ -0,0 +1,24 @@ +# Copyright 2019 Creu Blanca +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +import csv + +from odoo import models + + +class PartnerCSV(models.AbstractModel): + _name = "report.report_csv.partner_csv" + _inherit = "report.report_csv.abstract" + _description = "Report Partner to CSV" + + def generate_csv_report(self, writer, data, partners): + writer.writeheader() + for obj in partners: + writer.writerow({"name": obj.name, "email": obj.email}) + + def csv_report_options(self): + res = super().csv_report_options() + res["fieldnames"].append("name") + res["fieldnames"].append("email") + res["delimiter"] = ";" + res["quoting"] = csv.QUOTE_ALL + return res diff --git a/report_csv/static/description/icon.png b/report_csv/static/description/icon.png new file mode 100644 index 000000000..3a0328b51 Binary files /dev/null and b/report_csv/static/description/icon.png differ diff --git a/report_csv/static/description/index.html b/report_csv/static/description/index.html new file mode 100644 index 000000000..94ae1a3eb --- /dev/null +++ b/report_csv/static/description/index.html @@ -0,0 +1,462 @@ + + + + + + +Base report csv + + + +
+

Base report csv

+ + +

Beta License: AGPL-3 OCA/reporting-engine Translate me on Weblate Try me on Runbot

+

This module provides a basic report class to generate csv report.

+

Table of contents

+ +
+

Usage

+

An example of CSV report for partners on a module called module_name:

+

A python class

+
+from odoo import models
+
+class PartnerCSV(models.AbstractModel):
+    _name = 'report.report_csv.partner_csv'
+    _inherit = 'report.report_csv.abstract'
+
+    def generate_csv_report(self, writer, data, partners):
+        writer.writeheader()
+        for obj in partners:
+            writer.writerow({
+                'name': obj.name,
+                'email': obj.email,
+            })
+
+    def csv_report_options(self):
+        res = super().csv_report_options()
+        res['fieldnames'].append('name')
+        res['fieldnames'].append('email')
+        res['delimiter'] = ';'
+        res['quoting'] = csv.QUOTE_ALL
+        return res
+
+

A report XML record

+
+<report
+    id="partner_csv"
+    model="res.partner"
+    string="Print to CSV"
+    report_type="csv"
+    name="module_name.report_name"
+    file="res_partner"
+    attachment_use="False"
+/>
+
+
+
+

Bug Tracker

+

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

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Creu Blanca
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

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

+

This module is part of the OCA/reporting-engine project on GitHub.

+

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

+
+
+
+ + diff --git a/report_csv/static/src/js/report/qwebactionmanager.js b/report_csv/static/src/js/report/qwebactionmanager.js new file mode 100644 index 000000000..889ac4471 --- /dev/null +++ b/report_csv/static/src/js/report/qwebactionmanager.js @@ -0,0 +1,97 @@ +// © 2019 Creu Blanca +// License AGPL-3.0 or later (https://www.gnuorg/licenses/agpl.html). +odoo.define("report_csv.report", function(require) { + "use strict"; + + var core = require("web.core"); + var ActionManager = require("web.ActionManager"); + var framework = require("web.framework"); + var session = require("web.session"); + var _t = core._t; + + ActionManager.include({ + _downloadReportCSV: function(url, actions) { + var self = this; + framework.blockUI(); + var type = "csv"; + var cloned_action = _.clone(actions); + + if ( + _.isUndefined(cloned_action.data) || + _.isNull(cloned_action.data) || + (_.isObject(cloned_action.data) && _.isEmpty(cloned_action.data)) + ) { + if (cloned_action.context.active_ids) { + url += "/" + cloned_action.context.active_ids.join(","); + } + } else { + url += + "?options=" + + encodeURIComponent(JSON.stringify(cloned_action.data)); + url += + "&context=" + + encodeURIComponent(JSON.stringify(cloned_action.context)); + } + + return new Promise(function(resolve, reject) { + var blocked = !session.get_file({ + url: url, + data: { + data: JSON.stringify([url, type]), + }, + success: resolve, + error: error => { + self.call("crash_manager", "rpc_error", error); + reject(); + }, + complete: framework.unblockUI, + }); + if (blocked) { + // AAB: this check should be done in get_file service directly, + // should not be the concern of the caller (and that way, get_file + // could return a deferred) + var message = _t( + "A popup window with your report was blocked. You " + + "may need to change your browser settings to allow " + + "popup windows for this page." + ); + this.do_warn(_t("Warning"), message, true); + } + }); + }, + + _triggerDownload: function(action, options, type) { + var self = this; + var reportUrls = this._makeReportUrls(action); + if (type === "csv") { + return this._downloadReportCSV(reportUrls[type], action).then( + function() { + if (action.close_on_report_download) { + var closeAction = {type: "ir.actions.act_window_close"}; + return self.doAction( + closeAction, + _.pick(options, "on_close") + ); + } + return options.on_close(); + } + ); + } + return this._super.apply(this, arguments); + }, + + _makeReportUrls: function(action) { + var reportUrls = this._super.apply(this, arguments); + reportUrls.csv = "/report/csv/" + action.report_name; + return reportUrls; + }, + + _executeReportAction: function(action, options) { + var self = this; + if (action.report_type === "csv") { + return self._triggerDownload(action, options, "csv"); + } + return this._super.apply(this, arguments); + }, + }); +}); diff --git a/report_csv/tests/__init__.py b/report_csv/tests/__init__.py new file mode 100644 index 000000000..32ae3c2c3 --- /dev/null +++ b/report_csv/tests/__init__.py @@ -0,0 +1 @@ +from . import test_report diff --git a/report_csv/tests/test_report.py b/report_csv/tests/test_report.py new file mode 100644 index 000000000..32239262f --- /dev/null +++ b/report_csv/tests/test_report.py @@ -0,0 +1,58 @@ +# Copyright 2019 Creu Blanca +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +import logging +from io import StringIO + +from odoo.tests import common + +_logger = logging.getLogger(__name__) +try: + import csv +except ImportError: + _logger.debug("Can not import csv.") + + +class TestReport(common.TransactionCase): + def setUp(self): + super().setUp() + report_object = self.env["ir.actions.report"] + self.csv_report = self.env["report.report_csv.abstract"].with_context( + active_model="res.partner" + ) + self.report_name = "report_csv.partner_csv" + self.report = report_object._get_report_from_name(self.report_name) + self.docs = self.env["res.company"].search([], limit=1).partner_id + + def test_report(self): + # Test if not res: + self.env["ir.actions.report"]._get_report_from_name("TEST") + report = self.report + self.assertEqual(report.report_type, "csv") + rep = report.render(self.docs.ids, {}) + str_io = StringIO(rep[0]) + dict_report = list(csv.DictReader(str_io, delimiter=";", quoting=csv.QUOTE_ALL)) + self.assertEqual(self.docs.name, dict(dict_report[0])["name"]) + + def test_id_retrieval(self): + + # Typical call from WebUI with wizard + objs = self.csv_report._get_objs_for_report( + False, {"context": {"active_ids": self.docs.ids}} + ) + self.assertEquals(objs, self.docs) + + # Typical call from within code not to report_action + objs = self.csv_report.with_context( + active_ids=self.docs.ids + )._get_objs_for_report(False, False) + self.assertEquals(objs, self.docs) + + # Typical call from WebUI + objs = self.csv_report._get_objs_for_report( + self.docs.ids, {"data": [self.report_name, self.report.report_type]} + ) + self.assertEquals(objs, self.docs) + + # Typical call from render + objs = self.csv_report._get_objs_for_report(self.docs.ids, {}) + self.assertEquals(objs, self.docs) diff --git a/report_csv/views/webclient_templates.xml b/report_csv/views/webclient_templates.xml new file mode 100644 index 000000000..62b6f161d --- /dev/null +++ b/report_csv/views/webclient_templates.xml @@ -0,0 +1,15 @@ + + + +