diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000000000000000000000000000000000..bfd7ac53df9f103f6dc8853738c63fd364445fde --- /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.yml b/.eslintrc.yml new file mode 100644 index 0000000000000000000000000000000000000000..d4cc423ccda9db9691205c9da83307af97b2670f --- /dev/null +++ b/.eslintrc.yml @@ -0,0 +1,187 @@ +env: + browser: true + es6: true + +# See https://github.com/OCA/odoo-community.org/issues/37#issuecomment-470686449 +parserOptions: + ecmaVersion: 2017 + +overrides: + - files: + - "**/*.esm.js" + parserOptions: + sourceType: module + +# Globals available in Odoo that shouldn't produce errorings +globals: + _: readonly + $: readonly + fuzzy: readonly + jQuery: readonly + moment: readonly + odoo: readonly + openerp: readonly + owl: 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/.flake8 b/.flake8 new file mode 100644 index 0000000000000000000000000000000000000000..e397e8ed4e3e7f7fe7785dd391bb80aa6d85575e --- /dev/null +++ b/.flake8 @@ -0,0 +1,12 @@ +[flake8] +max-line-length = 88 +max-complexity = 16 +# B = bugbear +# B9 = bugbear opinionated (incl line length) +select = C,E,F,W,B,B9 +# E203: whitespace before ':' (black behaviour) +# E501: flake8 line length (covered by bugbear B950) +# W503: line break before binary operator (black behaviour) +ignore = E203,E501,W503 +per-file-ignores= + __init__.py:F401 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..818770fb1bdc0a144e924c9a5940f0b035df8a0d --- /dev/null +++ b/.gitignore @@ -0,0 +1,75 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +/.venv +/.pytest_cache + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +bin/ +build/ +develop-eggs/ +dist/ +eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg +*.eggs + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.cache +nosetests.xml +coverage.xml + +# Translations +*.mo + +# Pycharm +.idea + +# Eclipse +.settings + +# Visual Studio cache/options directory +.vs/ +.vscode + +# OSX Files +.DS_Store + +# Django stuff: +*.log + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject + +# Rope +.ropeproject + +# Sphinx documentation +docs/_build/ + +# Backup files +*~ +*.swp + +# OCA rules +!static/lib/ diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 0000000000000000000000000000000000000000..0ec187efd1bf802844749f508cda0c8f138970f9 --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,13 @@ +[settings] +; see https://github.com/psf/black +multi_line_output=3 +include_trailing_comma=True +force_grid_wrap=0 +combine_as_imports=True +use_parentheses=True +line_length=88 +known_odoo=odoo +known_odoo_addons=odoo.addons +sections=FUTURE,STDLIB,THIRDPARTY,ODOO,ODOO_ADDONS,FIRSTPARTY,LOCALFOLDER +default_section=THIRDPARTY +ensure_newline_before_comments = True diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1c6434bfc2274c73d98dbf62b6de9baccdc353ce --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,127 @@ +exclude: | + (?x) + # NOT INSTALLABLE ADDONS + # END NOT INSTALLABLE ADDONS + # Files and folders generated by bots, to avoid loops + ^setup/|/static/description/index\.html$| + # We don't want to mess with tool-generated files + .svg$|/tests/([^/]+/)?cassettes/|^.copier-answers.yml$|^.github/| + # 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 + node: "14.13.0" +repos: + - repo: local + hooks: + # These files are most likely copier diff rejection junks; if found, + # review them manually, fix the problem (if needed) and remove them + - id: forbidden-files + name: forbidden files + entry: found forbidden files; remove them + language: fail + files: "\\.rej$" + - repo: https://github.com/oca/maintainer-tools + rev: ab1d7f6 + hooks: + # update the NOT INSTALLABLE ADDONS section above + - id: oca-update-pre-commit-excluded-addons + - id: oca-fix-manifest-website + args: ["https://le-filament.com"] + - repo: https://github.com/myint/autoflake + rev: v1.4 + hooks: + - id: autoflake + args: + - --expand-star-imports + - --ignore-init-module-imports + - --in-place + - --remove-all-unused-imports + - --remove-duplicate-keys + - --remove-unused-variables + - repo: https://github.com/psf/black + rev: 20.8b1 + hooks: + - id: black + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v2.1.2 + hooks: + - id: prettier + name: prettier (with plugin-xml) + additional_dependencies: + - "prettier@2.1.2" + - "@prettier/plugin-xml@0.12.0" + args: + - --plugin=@prettier/plugin-xml + files: \.(css|htm|html|js|json|jsx|less|md|scss|toml|ts|xml|yaml|yml)$ + - repo: https://github.com/pre-commit/mirrors-eslint + rev: v7.8.1 + hooks: + - id: eslint + verbose: true + args: + - --color + - --fix + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.2.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: 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/asottile/pyupgrade + rev: v2.7.2 + hooks: + - id: pyupgrade + args: ["--keep-percent-format"] + - repo: https://github.com/PyCQA/isort + rev: 5.5.1 + hooks: + - id: isort + name: isort except __init__.py + args: + - --settings=. + exclude: /__init__\.py$ + - repo: https://gitlab.com/PyCQA/flake8 + rev: 3.8.3 + hooks: + - id: flake8 + name: flake8 + additional_dependencies: ["flake8-bugbear==20.1.4"] + - repo: https://github.com/PyCQA/pylint + rev: pylint-2.5.3 + hooks: + - id: pylint + name: pylint with optional checks + args: + - --rcfile=.pylintrc + - --exit-zero + verbose: true + additional_dependencies: &pylint_deps + - pylint-odoo==3.5.0 + - id: pylint + name: pylint with mandatory checks + args: + - --rcfile=.pylintrc-mandatory + additional_dependencies: *pylint_deps diff --git a/.prettierrc.yml b/.prettierrc.yml new file mode 100644 index 0000000000000000000000000000000000000000..5b6d4b361ace92f3877993bf2848fac190d8fab6 --- /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: "strict" diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000000000000000000000000000000000000..30c91fa46625e252a409ca61b98f72145209f319 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,87 @@ +[MASTER] +load-plugins=pylint_odoo +score=n + +[ODOOLINT] +readme_template_url="https://github.com/OCA/maintainer-tools/blob/master/template/module/README.rst" +manifest_required_authors=Le Filament +manifest_required_keys=license +manifest_deprecated_keys=description,active +license_allowed=AGPL-3,GPL-2,GPL-2 or any later version,GPL-3,GPL-3 or any later version,LGPL-3 +valid_odoo_versions=12.0 + +[MESSAGES CONTROL] +disable=all + +# This .pylintrc contains optional AND mandatory checks and is meant to be +# loaded in an IDE to have it check everything, in the hope this will make +# optional checks more visible to contributors who otherwise never look at a +# green travis to see optional checks that failed. +# .pylintrc-mandatory containing only mandatory checks is used the pre-commit +# config as a blocking check. + +enable=anomalous-backslash-in-string, + api-one-deprecated, + api-one-multi-together, + assignment-from-none, + attribute-deprecated, + class-camelcase, + dangerous-default-value, + dangerous-view-replace-wo-priority, + development-status-allowed, + duplicate-id-csv, + duplicate-key, + duplicate-xml-fields, + duplicate-xml-record-id, + eval-referenced, + eval-used, + incoherent-interpreter-exec-perm, + license-allowed, + manifest-author-string, + manifest-deprecated-key, + manifest-required-author, + manifest-required-key, + manifest-version-format, + method-compute, + method-inverse, + method-required-super, + method-search, + openerp-exception-warning, + pointless-statement, + pointless-string-statement, + print-used, + redundant-keyword-arg, + redundant-modulename-xml, + reimported, + relative-import, + return-in-init, + rst-syntax-error, + sql-injection, + too-few-format-args, + translation-field, + translation-required, + unreachable, + use-vim-comment, + wrong-tabs-instead-of-spaces, + xml-syntax-error, + # messages that do not cause the lint step to fail + consider-merging-classes-inherited, + create-user-wo-reset-password, + dangerous-filter-wo-user, + deprecated-module, + file-not-used, + invalid-commit, + missing-manifest-dependency, + missing-newline-extrafiles, + no-utf8-coding-comment, + odoo-addons-relative-import, + old-api7-method-defined, + redefined-builtin, + too-complex, + unnecessary-utf8-coding-comment + + +[REPORTS] +msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg} +output-format=colorized +reports=no diff --git a/.pylintrc-mandatory b/.pylintrc-mandatory new file mode 100644 index 0000000000000000000000000000000000000000..e37fa6552376d2e484d5a26370b249ac7384a87b --- /dev/null +++ b/.pylintrc-mandatory @@ -0,0 +1,64 @@ +[MASTER] +load-plugins=pylint_odoo +score=n + +[ODOOLINT] +readme_template_url="https://github.com/OCA/maintainer-tools/blob/master/template/module/README.rst" +manifest_required_authors=Le Filament +manifest_required_keys=license +manifest_deprecated_keys=description,active +license_allowed=AGPL-3,GPL-2,GPL-2 or any later version,GPL-3,GPL-3 or any later version,LGPL-3 +valid_odoo_versions=12.0 + +[MESSAGES CONTROL] +disable=all + +enable=anomalous-backslash-in-string, + api-one-deprecated, + api-one-multi-together, + assignment-from-none, + attribute-deprecated, + class-camelcase, + dangerous-default-value, + dangerous-view-replace-wo-priority, + development-status-allowed, + duplicate-id-csv, + duplicate-key, + duplicate-xml-fields, + duplicate-xml-record-id, + eval-referenced, + eval-used, + incoherent-interpreter-exec-perm, + license-allowed, + manifest-author-string, + manifest-deprecated-key, + manifest-required-author, + manifest-required-key, + manifest-version-format, + method-compute, + method-inverse, + method-required-super, + method-search, + openerp-exception-warning, + pointless-statement, + pointless-string-statement, + print-used, + redundant-keyword-arg, + redundant-modulename-xml, + reimported, + relative-import, + return-in-init, + rst-syntax-error, + sql-injection, + too-few-format-args, + translation-field, + translation-required, + unreachable, + use-vim-comment, + wrong-tabs-instead-of-spaces, + xml-syntax-error + +[REPORTS] +msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg} +output-format=colorized +reports=no diff --git a/README.rst b/README.rst index 69dc941fb85a9976a976c386e54644c714092c6a..d3f52f14f75a2f28e7ca7bd9b7879fbf376b77d6 100644 --- a/README.rst +++ b/README.rst @@ -34,4 +34,3 @@ Maintainer :target: https://le-filament.com This module is maintained by Le Filament - diff --git a/__init__.py b/__init__.py index 2b437a72dc1dcc1ea99e20cecdff0ba5c109e765..26b229a546ec2e35b66cf8eec13ce881119c77f9 100644 --- a/__init__.py +++ b/__init__.py @@ -1,5 +1,4 @@ -# Copyright 2020 Le Filament (<http://www.le-filament.com>) +# Copyright 2020-2022 Le Filament (<http://www.le-filament.com>) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from . import models -from . import controllers \ No newline at end of file +from . import controllers, models, report, wizard diff --git a/__manifest__.py b/__manifest__.py index 59f02278382b27d555db1f055bf88d53c98877df..254e8fa7cea774fb040809d9a5ac9d29bd639e3c 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -1,25 +1,28 @@ { - 'name': "ADEFPAT - Gestion des projets", - 'summary': """Gestion des projets - ADEFPAT""", - 'author': "Le Filament", - 'website': "https://www.le-filament.com", - 'version': '12.0.1.0.1', - 'license': "AGPL-3", - 'depends': [ - 'adefpat_partner', - 'contacts', - 'project', 'project_status', 'hr_timesheet'], - 'data': [ - 'security/ir.model.access.csv', - 'datas/adefpat.type.convention.csv', - 'datas/adefpat.project.statut.csv', - 'datas/project_status_datas.xml', - 'datas/project_task_type_datas.xml', - 'views/project_assets.xml', - 'views/project_views.xml', - 'views/project_task_views.xml', - 'views/res_partner_views.xml', - 'views/res_config_settings_views.xml', + "name": "ADEFPAT - Gestion des projets", + "summary": """Gestion des projets - ADEFPAT""", + "author": "Le Filament", + "website": "https://www.le-filament.com", + "version": "12.0.1.0.1", + "license": "AGPL-3", + "depends": [ + "adefpat_partner", + "contacts", + "project", + "project_status", + "hr_timesheet", ], - -} \ No newline at end of file + "data": [ + "security/ir.model.access.csv", + "datas/adefpat.type.convention.csv", + "datas/adefpat.project.statut.csv", + "datas/project_status_datas.xml", + "datas/project_task_type_datas.xml", + "views/project_views.xml", + "views/project_task_views.xml", + "views/res_partner_views.xml", + "views/res_config_settings_views.xml", + "wizard/adefpat_project_report_wizard.xml", + "report/formateur_project_report.xml", + ], +} diff --git a/controllers/__init__.py b/controllers/__init__.py index 8249a7961149ff052bc90eb2900bd84e8b12be2e..dbd1a01998b7c5ac8cf86efdd20eeb943325c8ce 100644 --- a/controllers/__init__.py +++ b/controllers/__init__.py @@ -1,4 +1,4 @@ # Copyright 2020 Le Filament (<http://www.le-filament.com>) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from . import main \ No newline at end of file +from . import main diff --git a/controllers/main.py b/controllers/main.py index df9f0a5c6f25339883a1d24dd7e66fc293b11155..ba86de1aa14f74f8ccf7a930e17e0f776ee137d3 100644 --- a/controllers/main.py +++ b/controllers/main.py @@ -1,21 +1,22 @@ -# -*- coding: utf-8 -*- -# © 2020 Le Filament (<http://www.le-filament.com>) +# © 2020-2022 Le Filament (<http://www.le-filament.com>) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from io import BytesIO from odoo import http from odoo.http import request -from odoo.addons.web.controllers.main import content_disposition -from odoo.addons.web.controllers.main import serialize_exception + +from odoo.addons.web.controllers.main import content_disposition, serialize_exception class GapExport(http.Controller): - @http.route(['/web/export_gap/'], type='http', auth="user") + @http.route(["/web/export_gap/"], type="http", auth="user") @serialize_exception - def datas_export(self, filename_, project_id, type, **kw): + def datas_export(self, filename_, project_id, partner_type, **kw): - workbook = request.env['project.project'].get_workbook(filename_, project_id, type) + workbook = request.env["project.project"].get_workbook( + filename_, project_id, partner_type + ) fp = BytesIO() workbook.save(fp) @@ -23,11 +24,11 @@ class GapExport(http.Controller): data = fp.read() fp.close() - filename = "Export_" + type + "_" + filename_ + '.xls' + filename = "Export_" + partner_type + "_" + filename_ + ".xls" xlshttpheaders = [ - ('Content-Type', 'text/csv;charset=utf8'), - ('Content-Disposition', content_disposition(filename)), + ("Content-Type", "text/csv;charset=utf8"), + ("Content-Disposition", content_disposition(filename)), ] return request.make_response(data, headers=xlshttpheaders) diff --git a/datas/adefpat.project.statut.csv b/datas/adefpat.project.statut.csv index d48e68607a79a485fc52e90a8afc812d98b405f0..70f6a9ec18b613a70e365c42de6939bd26185a4b 100644 --- a/datas/adefpat.project.statut.csv +++ b/datas/adefpat.project.statut.csv @@ -6,4 +6,4 @@ adefpat_project_statut_4,"Demandeur d'emploi" adefpat_project_statut_5,"Dirigeant" adefpat_project_statut_6,"Élu" adefpat_project_statut_7,"Inactif" -adefpat_project_statut_8,"Salariés" \ No newline at end of file +adefpat_project_statut_8,"Salariés" diff --git a/datas/adefpat.type.convention.csv b/datas/adefpat.type.convention.csv index c575b2f90b3aa0901d08150ddcba4d0440440ad8..6dd90e59865f18920cb358d1cce1d39d448c209b 100644 --- a/datas/adefpat.type.convention.csv +++ b/datas/adefpat.type.convention.csv @@ -6,4 +6,4 @@ adefpat_type_convention_4,"OPTER LR" adefpat_type_convention_5,"FEADER MP 2020" adefpat_type_convention_6,"FEADER LR 2020" adefpat_type_convention_7,"REGION" -adefpat_type_convention_8,"VIVEA" \ No newline at end of file +adefpat_type_convention_8,"VIVEA" diff --git a/datas/project_status_datas.xml b/datas/project_status_datas.xml index 11149392ce03ffc0eca9bbee6fa06c9478827a1a..0d9ac21d45b706e71d2e6ac49f43a0fc7a181221 100644 --- a/datas/project_status_datas.xml +++ b/datas/project_status_datas.xml @@ -19,4 +19,4 @@ <field name="name">Clôturé</field> </record> </data> -</odoo> \ No newline at end of file +</odoo> diff --git a/datas/project_task_type_datas.xml b/datas/project_task_type_datas.xml index f1549f5e6e33ca0ecc2dbb3cbc49ee5ca933af90..20f128ae5c7f3dd41126502a9eca52b458ad7f31 100644 --- a/datas/project_task_type_datas.xml +++ b/datas/project_task_type_datas.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8" ?> <odoo> <data> @@ -27,4 +27,4 @@ <field name="name">Payé</field> </record> </data> -</odoo> \ No newline at end of file +</odoo> diff --git a/i18n/fr.po b/i18n/fr.po index be1bad508a25f041d0d6401965652888a8ab4944..b87ddf807fcf099efb0a3c9aba1756bfd236b78f 100644 --- a/i18n/fr.po +++ b/i18n/fr.po @@ -1,7 +1,7 @@ # Translation of Odoo Server. # This file contains the translation of the following modules: # * project -# +# msgid "" msgstr "" "Project-Id-Version: Odoo Server 12.0\n" @@ -477,4 +477,4 @@ msgstr "CFD" #: model:ir.model.fields,field_description:project.field_project_task__date_deadline #: model:ir.model.fields,field_description:project.field_report_project_task_user__date_deadline msgid "Deadline" -msgstr "Date de séance" \ No newline at end of file +msgstr "Date de séance" diff --git a/models/__init__.py b/models/__init__.py index d56dbfc64a02625087e0c8c4534157b185384a63..86f4bf5bd5cab605927f6259d197cd9629441086 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -1,8 +1,8 @@ -# Copyright 2020 Le Filament (<http://www.le-filament.com>) +# Copyright 2020-2022 Le Filament (<http://www.le-filament.com>) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from . import res_company from . import res_config_settings from . import project from . import project_task -from . import res_partner \ No newline at end of file +from . import res_partner diff --git a/models/project.py b/models/project.py index 8a47412be90c96c0f0d849e09925488b8231c96a..51b3d9ad9e0e87562c13b00fa8e15ae570ae1077 100644 --- a/models/project.py +++ b/models/project.py @@ -1,23 +1,22 @@ -# Copyright 2020 Le Filament (<http://www.le-filament.com>) +# Copyright 2020-2022 Le Filament (<http://www.le-filament.com>) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from datetime import datetime, timedelta -from odoo import models, fields, api -from odoo.tools.misc import xlwt -import csv -import base64 +from odoo import api, fields, models +from odoo.exceptions import ValidationError +from odoo.tools.misc import xlwt HEADER_MEMBER = [ - 'Nom', - 'Prénom', - 'Fonction', - 'Structure', - 'Commune', - 'Mobile', - 'Fixe', - 'Email' - ] + "Nom", + "Prénom", + "Fonction", + "Structure", + "Commune", + "Mobile", + "Fixe", + "Email", +] class Project(models.Model): @@ -48,136 +47,148 @@ class Project(models.Model): if self.env.user.company_id.modalites_modif_marche: return self.env.user.company_id.modalites_modif_marche - privacy_visibility = fields.Selection([ - ('followers', 'On invitation only'), - ('employees', 'Visible by all employees'), - ('portal', 'Visible by following customers'), + privacy_visibility = fields.Selection( + [ + ("followers", "On invitation only"), + ("employees", "Visible by all employees"), + ("portal", "Visible by following customers"), ], - string='Confidentialité', required=True, - default='employees') + string="Confidentialité", + required=True, + default="employees", + ) # Champs étape "Demande" => Vue générale name_subtitle = fields.Char("Sous-titre") first_contact_id = fields.Many2one( - 'res.partner', - string='1er contact', - domain=[ - ('active', '=', True), - ('is_company', '=', False)], - on_delete='restrict') - type_contact = fields.Selection([ - ('adherent', 'Adhérent'), - ('no_adherent', 'Non adhérent'), - ('pdp', 'Porteur de projet'), + "res.partner", + string="1er contact", + domain=[("active", "=", True), ("is_company", "=", False)], + ondelete="restrict", + ) + type_contact = fields.Selection( + [ + ("adherent", "Adhérent"), + ("no_adherent", "Non adhérent"), + ("pdp", "Porteur de projet"), ], - string="Type contact") - date_first_contact = fields.Date( - "Date de 1er contact", - default=fields.Date.today) + string="Type contact", + ) + date_first_contact = fields.Date("Date de 1er contact", default=fields.Date.today) prescripteur_id = fields.Many2one( - 'res.partner', - string='Prescripteur', - domain=[ - ('active', '=', True), - ('is_company', '=', False)], - on_delete='restrict') + "res.partner", + string="Prescripteur", + domain=[("active", "=", True), ("is_company", "=", False)], + ondelete="restrict", + ) porteurs_projets_ids = fields.One2many( - 'res.partner.porteur.project', - 'project_id', - string="Porteurs de projet") + "res.partner.porteur.project", "project_id", string="Porteurs de projet" + ) description = fields.Text("Description") territoire_id = fields.Many2one( - 'res.partner', - string='Territoire', + "res.partner", + string="Territoire", domain=[ - ('active', '=', True), - ('is_company', '=', True), - '|', - ('type_structure_id.name', '=', 'ComCom'), - ('type_structure_id.name', '=', 'PETR/PNR')], - on_delete='restrict') + ("active", "=", True), + ("is_company", "=", True), + "|", + ("type_structure_id.name", "=", "ComCom"), + ("type_structure_id.name", "=", "PETR/PNR"), + ], + ondelete="restrict", + ) departement_ids = fields.Many2many( - comodel_name='res.partner', - relation='res_partner_departement_rel', - column1='partner_id', - column2='departement_id', + comodel_name="res.partner", + relation="res_partner_departement_rel", + column1="partner_id", + column2="departement_id", domain=[ - ('active', '=', True), - ('is_company', '=', True), - ('type_structure_id.name', '=', 'Département')], + ("active", "=", True), + ("is_company", "=", True), + ("type_structure_id.name", "=", "Département"), + ], string="Départements", - related='territoire_id.departement_ids') + related="territoire_id.departement_ids", + ) region_ids = fields.Many2many( - comodel_name='res.partner', - relation='res_partner_region_rel', - column1='partner_id', - column2='region_id', + comodel_name="res.partner", + relation="res_partner_region_rel", + column1="partner_id", + column2="region_id", domain=[ - ('active', '=', True), - ('is_company', '=', True), - ('type_structure_id.name', '=', 'Région')], + ("active", "=", True), + ("is_company", "=", True), + ("type_structure_id.name", "=", "Région"), + ], string="Régions", - related='territoire_id.region_ids') + related="territoire_id.region_ids", + ) petr_ids = fields.Many2many( - comodel_name='res.partner', - relation='res_partner_petr_rel', - column1='partner_id', - column2='petr_id', + comodel_name="res.partner", + relation="res_partner_petr_rel", + column1="partner_id", + column2="petr_id", domain=[ - ('active', '=', True), - ('is_company', '=', True), - ('type_structure_id.name', '=', 'PETR/PNR')], + ("active", "=", True), + ("is_company", "=", True), + ("type_structure_id.name", "=", "PETR/PNR"), + ], string="PETR/PNR", - related='territoire_id.petr_ids') - departement = fields.Selection([ - ('12', '12'), - ('34', '34'), - ('46', '46'), - ('48', '48'), - ('81', '81'), - ('82', '82') - ], string="Département du projet") + related="territoire_id.petr_ids", + ) + departement = fields.Selection( + [ + ("12", "12"), + ("34", "34"), + ("46", "46"), + ("48", "48"), + ("81", "81"), + ("82", "82"), + ], + string="Département du projet", + ) user_id = fields.Many2one( - 'res.users', - string='CFD', - default=lambda self: self.env.user, - track_visibility="onchange") + "res.users", + string="CFD", + default=lambda self: self.env.user, + track_visibility="onchange", + ) # Champs étape "Instruction" => Vue générale od_ids = fields.Many2many( - comodel_name='res.partner', - column1='project_id', - column2='od_id', - relation='res_partner_od_rel', - string='OD', - domain=[ - ('active', '=', True), - ('is_company', '=', True)], - on_delete='restrict') + comodel_name="res.partner", + column1="project_id", + column2="od_id", + relation="res_partner_od_rel", + string="OD", + domain=[("active", "=", True), ("is_company", "=", True)], + ondelete="restrict", + ) elu_id = fields.Many2one( - 'res.partner', - string='Elu qui présente le dossier', - domain=[ - ('active', '=', True), - ('is_company', '=', False)], - on_delete='restrict') + "res.partner", + string="Elu qui présente le dossier", + domain=[("active", "=", True), ("is_company", "=", False)], + ondelete="restrict", + ) animateur_ids = fields.Many2many( - comodel_name = 'res.partner', - column1='project_id', - column2='anim_id', - relation='res_partner_anim_rel', - string='Animateurs', - domain=[ - ('active', '=', True), - ('is_company', '=', False)], - on_delete='restrict') + comodel_name="res.partner", + column1="project_id", + column2="anim_id", + relation="res_partner_anim_rel", + string="Animateurs", + domain=[("active", "=", True), ("is_company", "=", False)], + ondelete="restrict", + ) type_convention_id = fields.Many2one( - 'adefpat.type.convention', + "adefpat.type.convention", domain=[ - '|', ('date_end_validity', '>=', fields.Date.today()), - ('date_end_validity', '=', False)], - string="Type de convention de financement") + "|", + ("date_end_validity", ">=", fields.Date.today()), + ("date_end_validity", "=", False), + ], + string="Type de convention de financement", + ) date_ca = fields.Date("Date de CA") - date_ca_next = fields.Date("Lendemain date de CA", compute='_compute_date_ca_next') + date_ca_next = fields.Date("Lendemain date de CA", compute="_compute_date_ca_next") date_demarrage = fields.Date("Date de démarrage prévisionnel") # Champs étape "Instruction" => Onglet Dossier @@ -189,83 +200,126 @@ class Project(models.Model): competences_requises = fields.Text("Compétences du CF") secteurs_requis = fields.Text("Secteurs d’activités") modalites_intervention = fields.Text( - "Modalités d'intervention", - default=_default_modalites_intervention) + "Modalités d'intervention", default=_default_modalites_intervention + ) modalites_facturation = fields.Text( - "Modalités de facturation", - default=_default_modalites_facturation) + "Modalités de facturation", default=_default_modalites_facturation + ) modalites_reponse = fields.Text( - "Modalités de réponse", - default=_default_modalites_reponse) + "Modalités de réponse", default=_default_modalites_reponse + ) modalites_modif_marche = fields.Text( - "Modalités de modification du marché", - default=_default_modalites_modif_marche) + "Modalités de modification du marché", default=_default_modalites_modif_marche + ) # Infos dossier CA - type_beneficiaire = fields.Char("Type de bénéficiares", compute='_compute_type_beneficiaire') + type_beneficiaire = fields.Char( + "Type de bénéficiares", compute="_compute_type_beneficiaire" + ) objectif_projet = fields.Selection( [ - ('creation', "Création d'entreprise"), - ('creation_activite', 'Création nouvelle activité'), - ('consolidation', 'Consolidation'), - ('emergence', 'Émergence'), - ('operation', "Opération territoriale pour les entreprises"), - ('post_creation', 'Post-création'), - ('post_reprise', 'Post-reprise'), - ('projet_inter', 'Projet inter-entreprise'), - ('projet_structurant', 'Projet structurant territoire'), - ('reprise', 'Reprise'), - ('transmission', 'Transmission'), + ("creation", "Création d'entreprise"), + ("creation_activite", "Création nouvelle activité"), + ("consolidation", "Consolidation"), + ("emergence", "Émergence"), + ("operation", "Opération territoriale pour les entreprises"), + ("post_creation", "Post-création"), + ("post_reprise", "Post-reprise"), + ("projet_inter", "Projet inter-entreprise"), + ("projet_structurant", "Projet structurant territoire"), + ("reprise", "Reprise"), + ("transmission", "Transmission"), ], - string="Objectif projet") + string="Objectif projet", + ) secteur_crit = fields.Selection( [ - ('agriculture', 'Agriculture'), - ('agro_alimentaire', 'Agro-alimentaire'), - ('artisanat', 'Artisanat - Commerce - Industrie'), - ('culture', 'Culture'), - ('environnement', 'Environnement'), - ('intersectoriel', 'Intersectoriel'), - ('service', 'Service'), - ('service', 'Services à la population'), - ('tourisme', 'Tourisme - Hôtellerie - Restauration'), + ("agriculture", "Agriculture"), + ("agro_alimentaire", "Agro-alimentaire"), + ("artisanat", "Artisanat - Commerce - Industrie"), + ("culture", "Culture"), + ("environnement", "Environnement"), + ("intersectoriel", "Intersectoriel"), + ("service", "Service"), + ("service", "Services à la population"), + ("tourisme", "Tourisme - Hôtellerie - Restauration"), ], - string="Secteur d'activité") + string="Secteur d'activité", + ) taille = fields.Selection( - [ - ('inf_10', "De 10 à 250"), - ('sup_10', "< 10 salariés") - ], - string="Taille entreprise") + [("inf_10", "De 10 à 250"), ("sup_10", "< 10 salariés")], + string="Taille entreprise", + ) objectif_formation = fields.Selection( [ - ('def_projet', "Définition de projet"), - ('def_modele_eco', "Définition d'un modèle économique"), - ('def_strat_com', "Définition d'une stratégie de développement"), - ('diagnostic', 'Diagnostic stratégique'), - ('gestion_prod', 'Gestion de production'), - ('gestion_financ', 'Gestion financière'), - ('marketing', "Marketing - Commercialisation - Promotion - Communication"), - ('management', 'Management - Organisation - RH'), - ('orga_collective', 'Organisation collective'), + ("def_projet", "Définition de projet"), + ("def_modele_eco", "Définition d'un modèle économique"), + ("def_strat_com", "Définition d'une stratégie de développement"), + ("diagnostic", "Diagnostic stratégique"), + ("gestion_prod", "Gestion de production"), + ("gestion_financ", "Gestion financière"), + ("marketing", "Marketing - Commercialisation - Promotion - Communication"), + ("management", "Management - Organisation - RH"), + ("orga_collective", "Organisation collective"), ], - string="Objectif formation") + string="Objectif formation", + ) type_formation = fields.Selection( [ - ('individuelle', 'Générale individuelle'), - ('collective', 'Générale collective'), - ('indiv_collect', 'Générale collective individualisée'), + ("individuelle", "Générale individuelle"), + ("collective", "Générale collective"), + ("indiv_collect", "Générale collective individualisée"), ], - string="Type formation") + string="Type formation", + ) encadrement = fields.Selection( [ - ('minimis', 'De minimis'), - ('neant', 'Neant'), - ('encadrement', 'Encadrement des aides à la formation') + ("minimis", "De minimis"), + ("neant", "Neant"), + ("encadrement", "Encadrement des aides à la formation"), ], - string="Encadrement Aides") + string="Encadrement Aides", + ) nb_activité = fields.Integer("Nombre d’activités ou entreprises concernées") + nb_coll = fields.Integer( + "Nombre de collectivités accompagnées", + help="Nombre de collectivités représentées dans le groupe projet " + "(par ex 2 mairies, 1 com com et 1 petr)", + ) + nb_citoyens = fields.Integer( + "Nombre de citoyens participant à l'accompagnement", + help="Ensemble des participants moins les élus et les chargés de développement, " + "les agents de collectivités", + ) + nb_entreprises = fields.Integer( + "Nombre d'entreprises mobilisées dans le cadre " "d'une démarche collective", + help="Entreprises (y compris les associations avec une activité économique " + "régulière) participants à l'accompagnement d'un collectif inter entreprises " + "ou à un projet territorial", + ) nb_emplois = fields.Integer("Nombre d’emplois concernés") + bpi = fields.Boolean( + "BPI", help="projet à valoriser danS le cadre de l'appel à projet BPI" + ) + type_accompagnement = fields.Selection( + [ + ("individualise", "Individualisé"), + ("interentreprises", "Inter-Entreprises"), + ("territoriaux", "Territoriaux"), + ], + string="Type d'accompagnement", + help="Individualisé : accompagne d'entreprise en individuel ou collectif " + "mais qui porte sur le developpement de chaque entreprise\n" + "Inter-Entreprises : accompagnement de plusieurs entreprises autour " + "d'un projet collectif\n" + "Territoriaux : accompagnement d'un groupe multi-type d'acteurs dont " + "au moins une collectivité", + ) + est_suite = fields.Boolean( + "Est ce que la sollicitation d'accompagnement est " + "issue d'un autre accompagnement précédent sur la zone Adefpat" + ) + lieu = fields.Text("Lieu") periode_realisation = fields.Text("Période de réalisation") contenu_formation = fields.Text("Contenu des séances") @@ -276,92 +330,181 @@ class Project(models.Model): nb_jours_adefpat = fields.Float("Nombre de jours CFD") cout_jour_adefpat = fields.Float("Coût jour CFD") nb_jour_theorique_tot = fields.Float( - "Nombre de jours théoriques total", - compute='_compute_nb_jour_theorique_tot') + "Nombre de jours théoriques total", compute="_compute_nb_jour_theorique_tot" + ) nb_jour_pratique_tot = fields.Float( - "Nombre de jours pratiques total", - compute='_compute_nb_jour_pratique_tot') + "Nombre de jours pratiques total", compute="_compute_nb_jour_pratique_tot" + ) nb_jour_plann = fields.Float( - "Nombre de jours plannifiés", - compute='_compute_nb_jour_plann') + "Nombre de jours plannifiés", compute="_compute_nb_jour_plann" + ) nb_jour_rest = fields.Float( - "Nombre de jours restants", - compute='_compute_nb_jour_rest') + "Nombre de jours restants", compute="_compute_nb_jour_rest" + ) nb_jour_tot = fields.Float( - "Nombre de jours total", - compute='_compute_nb_jour_tot', - store=True) + "Nombre de jours total", compute="_compute_nb_jour_tot", store=True + ) nb_heure_tot = fields.Float( - "Nombre d'heures' total", - compute='_compute_nb_jour_tot', - store=True) + "Nombre d'heures' total", compute="_compute_nb_jour_tot", store=True + ) total_cout_adefpat = fields.Float( - "Total coûts CFD", - compute='_compute_total_cout_adefpat') + "Total coûts CFD", compute="_compute_total_cout_adefpat" + ) financement_adefpat = fields.Float( - "Financement Adefpat", - compute='_compute_financement_adefpat') + "Financement Adefpat", compute="_compute_financement_adefpat" + ) financement_ids = fields.One2many( - 'adefpat.project.financement', - 'project_id', - string="Financements") - total_financement = fields.Float("Total autres", compute='_compute_total_financment') - cout_ids = fields.One2many( - 'adefpat.project.cout', - 'project_id', - string="Coûts") - total_cout = fields.Float("Total coûts", compute='_compute_total_cout') - honoraire_intervenant = fields.Float( - "Honoraires d'intervenants") + "adefpat.project.financement", "project_id", string="Financements" + ) + total_financement = fields.Float( + "Total autres", compute="_compute_total_financment" + ) + cout_ids = fields.One2many("adefpat.project.cout", "project_id", string="Coûts") + total_cout = fields.Float("Total coûts", compute="_compute_total_cout") + honoraire_intervenant = fields.Float("Honoraires d'intervenants") total_budget_cout = fields.Float( - "Total budget coûts", - compute='_compute_total_budget_cout') - total_budget_financement = fields.Float("Total budget financements", compute='_compute_total_budget_financement') + "Total budget coûts", compute="_compute_total_budget_cout" + ) + total_budget_financement = fields.Float( + "Total budget financements", compute="_compute_total_budget_financement" + ) explication_financement = fields.Text( "Explication financement porteur de projet", - default=_default_explication_financement) + default=_default_explication_financement, + ) # Champs étape "Instruction" => Onglet Consultation consulant_ids = fields.One2many( - 'res.partner.consultants.project', - 'project_id', - string="Consultants") + "res.partner.consultants.project", "project_id", string="Consultants" + ) date_selection = fields.Date("Date de sélection") date_notification = fields.Date("Date de notification") date_cdc = fields.Date("Date d'envoi du CDC") - + # Champs étape "Instruction" => Onglet GAP elu_referent_id = fields.Many2one( - 'res.partner', - string='Élu référent', - domain=[ - ('active', '=', True), - ('is_company', '=', False)], - on_delete='restrict') + "res.partner", + string="Élu référent", + domain=[("active", "=", True), ("is_company", "=", False)], + ondelete="restrict", + ) membre_ids = fields.One2many( - 'res.partner.membres.project', - 'project_id', - string="Membres") - modalite_gap = fields.Text( - "Modalités GAP") + "res.partner.membres.project", "project_id", string="Membres" + ) + modalite_gap = fields.Text("Modalités GAP") reunion_ids = fields.One2many( - 'adefpat.reunion.gap', - 'project_id', - string="Réunions") + "adefpat.reunion.gap", "project_id", string="Réunions" + ) # Champs étape "Prêt pour CA" => Onglet Général num_dossier = fields.Char("Numéro de dossier") - @api.depends('date_ca') + # Champs Résultat + closing_date = fields.Date( + string="Date de clôture", + help="Date de la dernière séance facturée", + ) + on_hold = fields.Boolean( + "Accompagement suspendus ou terminé avec l'abandon du projet" + ) + number_gap_members = fields.Float( + "Nombre moyen de personnes présentes GAP", + compute="_compute_number_gap_members", + store=True, + ) + number_proj_att = fields.Integer( + "Nombre de projets accompagnés dont l'objectif " + "principal est le renforcement de l'attractivité du territoire" + ) + number_proj_inn_idea = fields.Integer( + "Nombre de projets aboutis considérés comme " "innovant dans leurs idées", + help="1 - L’idée n’a jamais été développé\n" + "2 - Un projet qui sort d’un cadre normé et apporte une ou plusieurs " + "dimensions sur les idées qui lui donne un caractère innovant\n" + "3 - L’idée peut répondre à un besoin sur “des signaux faibles", + ) + number_proj_inn_terr = fields.Integer( + "Nombre de projets aboutis considérés comme " + "innovants dans leur existence sur le territoire", + help="1 - Le projet existe mais pas sur le territoire\n" + "2 - Le projet n’existe pas, nouveauté", + ) + number_proj_inn_adap = fields.Integer( + "Nombre de projets aboutis considérés comme " + "innovants dans leur méthode d'adaptation au milieu rural", + help="1 – Création d’un projet en collectif et mutualiser par des entreprises " + "(coopérer, créer pour une stratégie commune au service de tous)\n" + "2 – L’adaptation de services urbains au milieu rural : développer le " + "public / privé, invention de concept de modulation pour créer et pérenniser" + "des services\n" + "3 – L’adaptation des démarches des grandes entreprises au profit de" + "l’entreprenariat local (au profit d’une compétitivité, l’attractivité" + "de l’activité et de l’emploi (RSE, la dynamique managériale et" + "organisationnelle en TPE/PME)", + ) + number_proj_dev = fields.Integer( + "Nombre de projet qui ont eu un changement d’échelle", + help="1 - Diversification : créer une nouvelle activité pour enrichir\n" + "2 - Duplication : répliquer son modèle sur d’autres territoires\n" + "3 - Fertilisation : à plus grande échelle\n" + "4 - Coopération : se rapprocher d’autres structures pour faire mieux et plus\n" + "5 - Fusion : regrouper son patrimoine avec une autre structure", + ) + number_proj_ind_coll = fields.Integer( + "Nombre de projet qui sont passés d’un projet" + "individuel à un projet collectif", + help="1 - Mise en lien avec d’autres porteurs projets ayant des problématiques" + "similaires", + ) + is_service_design = fields.Boolean( + "Utilisation d'éléments de méthode de design de service" + ) + is_social_impact = fields.Boolean("Intégration de notions d'Impact Social") + synthesis_website = fields.Char("Phrase de synthèse site Internet") + number_comp_idea_projet = fields.Integer( + "Nombre d'entreprises accompagnées pour passer de" "l'idée au projet" + ) + number_comp_creation = fields.Integer( + "Nombre d'entreprise en situation de création ou post création" + ) + number_comp_created = fields.Integer("Nombre d'entreprises créées (AI)") + number_comp_consolidated = fields.Integer("Nombre d'entreprises consolidées") + number_emp_created = fields.Integer("Nombre d'emplois créés") + number_emp_consolidated = fields.Integer("Nombre d'emplois consolidés") + number_ac_created = fields.Integer("Nombre d'actions collectives créées") + number_emp_created_coll = fields.Integer( + "Nombre d'emplois crees sur le projet collectif" + ) + number_emp_consolidated_coll = fields.Integer( + "Nombre d'emploi consolidés par l'action collective" + ) + number_comp_created_coll = fields.Integer( + "Nombre d'entreprises concernées par la création" + "d'une nouvelle activité collective" + ) + number_struct_proj = fields.Integer( + "Nombre de projets structurants définis" + ) + number_mean_people_inv = fields.Integer( + "Nombre moyen de personnes sollicitées hors du" + "groupe de travail (enquête, réunion publique)" + ) + number_comp_created_at = fields.Integer("Nombre d'entreprises créées (AT)") + number_day_period = fields.Float( + "Nb jours sur période", compute="_compute_number_day_period", default=0.0) + number_hour_period = fields.Float( + "Nb heures sur période", compute="_compute_number_day_period", default=0.0) + + @api.depends("date_ca") @api.multi def _compute_date_ca_next(self): for project in self: if project.date_ca: project.date_ca_next = project.date_ca + timedelta(days=1) - @api.depends('financement_ids', 'financement_ids.montant') + @api.depends("financement_ids", "financement_ids.montant") @api.multi def _compute_total_financment(self): for project in self: @@ -370,7 +513,7 @@ class Project(models.Model): total_financement += financement.montant project.total_financement = total_financement - @api.depends('cout_ids', 'cout_ids.montant') + @api.depends("cout_ids", "cout_ids.montant") @api.multi def _compute_total_cout(self): for project in self: @@ -379,40 +522,40 @@ class Project(models.Model): total_cout += cout.montant project.total_cout = total_cout - @api.depends('cout_ids', 'cout_ids.nb_jour_theorique') + @api.depends("cout_ids", "cout_ids.nb_jour_theorique") @api.multi def _compute_nb_jour_theorique_tot(self): for project in self: total_jour = 0.0 for cout in project.cout_ids: total_jour += cout.nb_jour_theorique - project.nb_jour_theorique_tot = total_jour\ + project.nb_jour_theorique_tot = total_jour - @api.depends('cout_ids', 'cout_ids.nb_jour_pratiques') + @api.depends("cout_ids", "cout_ids.nb_jour_pratiques") @api.multi def _compute_nb_jour_pratique_tot(self): for project in self: total_jour = 0.0 for cout in project.cout_ids: total_jour += cout.nb_jour_pratiques - project.nb_jour_pratique_tot = total_jour\ + project.nb_jour_pratique_tot = total_jour - @api.depends('task_ids', 'task_ids.duree_hr') + @api.depends("task_ids", "task_ids.duree_hr") @api.multi def _compute_nb_jour_plann(self): for project in self: total_jour = 0.0 for task in project.task_ids: total_jour += task.duree_hr - project.nb_jour_plann = total_jour\ + project.nb_jour_plann = total_jour - @api.depends('nb_jour_plann', 'nb_jour_theorique_tot') + @api.depends("nb_jour_plann", "nb_jour_theorique_tot") @api.multi def _compute_nb_jour_rest(self): for project in self: project.nb_jour_rest = project.nb_jour_theorique_tot - project.nb_jour_plann - @api.depends('cout_ids', 'cout_ids.nb_jour_theorique', 'cout_ids.nb_jour_pratiques') + @api.depends("cout_ids", "cout_ids.nb_jour_theorique", "cout_ids.nb_jour_pratiques") @api.multi def _compute_nb_jour_tot(self): for project in self: @@ -422,51 +565,87 @@ class Project(models.Model): project.nb_jour_tot = total_jour project.nb_heure_tot = total_jour * 7 - @api.depends('nb_jours_adefpat', 'cout_jour_adefpat') + @api.depends("nb_jours_adefpat", "cout_jour_adefpat") @api.multi def _compute_total_cout_adefpat(self): for project in self: - project.total_cout_adefpat = project.nb_jours_adefpat * project.cout_jour_adefpat + project.total_cout_adefpat = ( + project.nb_jours_adefpat * project.cout_jour_adefpat + ) - @api.depends('total_cout_adefpat', 'total_cout') + @api.depends("total_cout_adefpat", "total_cout") @api.multi def _compute_total_budget_cout(self): for project in self: project.total_budget_cout = project.total_cout_adefpat + project.total_cout - @api.depends('total_budget_cout', 'total_financement') + @api.depends("total_budget_cout", "total_financement") @api.multi def _compute_financement_adefpat(self): for project in self: - project.financement_adefpat = project.total_budget_cout - project.total_financement + project.financement_adefpat = ( + project.total_budget_cout - project.total_financement + ) - @api.depends('financement_adefpat', 'total_financement') + @api.depends("financement_adefpat", "total_financement") @api.multi def _compute_total_budget_financement(self): for project in self: - project.total_budget_financement = project.financement_adefpat + project.total_financement + project.total_budget_financement = ( + project.financement_adefpat + project.total_financement + ) - @api.depends('porteurs_projets_ids', - 'porteurs_projets_ids.statut') + @api.depends("porteurs_projets_ids", "porteurs_projets_ids.statut") @api.multi def _compute_type_beneficiaire(self): for project in self: - project.type_beneficiaire = ', '.join(project.porteurs_projets_ids.mapped('statut.name')) + project.type_beneficiaire = ", ".join( + project.porteurs_projets_ids.mapped("statut.name") + ) + @api.depends("reunion_ids", "reunion_ids.number_participants") @api.multi - def validate_ca(self): + def _compute_number_gap_members(self): + for project in self: + project.number_gap_members = sum( + project.reunion_ids.mapped("number_participants")) / len( + project.reunion_ids + ) if project.reunion_ids else 0 + + @api.multi + def _compute_number_day_period(self): + """ + fonction qui calcule le nombre de jours et heures par séance + pour des valeurs passées dans le contexte + """ + period_start = fields.Date.to_date(self.env.context.get("period_start")) + period_end = fields.Date.to_date(self.env.context.get("period_end")) for project in self: - project.num_dossier = project.departement + '/' + datetime.strftime(datetime.today(), '%y') + '/' + self.env['ir.sequence'].next_by_code( - 'increment_num_dossier') + days = project.task_ids.filtered( + lambda t: t.date_deadline and t.date_deadline >= period_start + and t.date_deadline <= period_end).mapped("duree_hr") + project.number_day_period = sum(days) + project.number_hour_period = sum(days) * 7 - def get_workbook(self, filename_, project_id, type): - project = self.env['project.project'].search([('id', '=', project_id)]) + @api.multi + def validate_ca(self): + for project in self: + project.num_dossier = ( + project.departement + + "/" + + datetime.strftime(datetime.today(), "%y") + + "/" + + self.env["ir.sequence"].next_by_code("increment_num_dossier") + ) + + def get_workbook(self, filename_, project_id, partner_type): + project = self.env["project.project"].search([("id", "=", project_id)]) workbook = xlwt.Workbook() worksheet = workbook.add_sheet(filename_) - if type == 'gap': + if partner_type == "gap": gap_ids = project.membre_ids - if type == 'benef': + if partner_type == "benef": gap_ids = project.porteurs_projets_ids header_file = HEADER_MEMBER @@ -474,46 +653,44 @@ class Project(models.Model): worksheet.write(0, i, fieldname) worksheet.col(i).width = 8000 # around 220 pixels - base_style = xlwt.easyxf('align: wrap yes') + base_style = xlwt.easyxf("align: wrap yes") cell_style = base_style row_index = 1 for gap_id in gap_ids: - worksheet.write(row_index, 0, - str(gap_id.lastname), cell_style) - worksheet.write(row_index, 1, - str(gap_id.firstname), cell_style) - if type == "gap": + worksheet.write(row_index, 0, str(gap_id.lastname), cell_style) + worksheet.write(row_index, 1, str(gap_id.firstname), cell_style) + if partner_type == "gap": if gap_id.partner_id.function: - worksheet.write(row_index, 2, - str(gap_id.partner_id.function), cell_style) + worksheet.write( + row_index, 2, str(gap_id.partner_id.function), cell_style + ) else: - worksheet.write(row_index, 2, 'None', cell_style) + worksheet.write(row_index, 2, "None", cell_style) if gap_id.partner_id.parent_id: - worksheet.write(row_index, 3, - str(gap_id.partner_id.parent_id.name), cell_style) + worksheet.write( + row_index, 3, str(gap_id.partner_id.parent_id.name), cell_style + ) else: - worksheet.write(row_index, 3, 'None', cell_style) - if type == "benef": + worksheet.write(row_index, 3, "None", cell_style) + if partner_type == "benef": if gap_id.porteur_id.function: - worksheet.write(row_index, 2, - str(gap_id.porteur_id.function), cell_style) + worksheet.write( + row_index, 2, str(gap_id.porteur_id.function), cell_style + ) else: - worksheet.write(row_index, 2, 'None', cell_style) + worksheet.write(row_index, 2, "None", cell_style) if gap_id.porteur_id.parent_id: - worksheet.write(row_index, 3, - str(gap_id.porteur_id.parent_id.name), cell_style) + worksheet.write( + row_index, 3, str(gap_id.porteur_id.parent_id.name), cell_style + ) else: - worksheet.write(row_index, 3, 'None', cell_style) - worksheet.write(row_index, 4, - str(gap_id.commune), cell_style) - worksheet.write(row_index, 5, - str(gap_id.mobile), cell_style) - worksheet.write(row_index, 6, - str(gap_id.fixe), cell_style) - worksheet.write(row_index, 7, - str(gap_id.email), cell_style) + worksheet.write(row_index, 3, "None", cell_style) + worksheet.write(row_index, 4, str(gap_id.commune), cell_style) + worksheet.write(row_index, 5, str(gap_id.mobile), cell_style) + worksheet.write(row_index, 6, str(gap_id.fixe), cell_style) + worksheet.write(row_index, 7, str(gap_id.email), cell_style) row_index = row_index + 1 return workbook @@ -523,11 +700,10 @@ class Project(models.Model): filename_ = project.name project_id = project.id return { - 'type': 'ir.actions.act_url', - 'url': - '/web/export_gap?filename_=%s&project_id=%s&type=%s' - % (filename_, project_id, 'gap'), - 'target': 'new', + "type": "ir.actions.act_url", + "url": "/web/export_gap?filename_=%s&project_id=%s&type=%s" + % (filename_, project_id, "gap"), + "target": "new", } @api.multi @@ -536,11 +712,10 @@ class Project(models.Model): filename_ = project.name project_id = project.id return { - 'type': 'ir.actions.act_url', - 'url': - '/web/export_gap?filename_=%s&project_id=%s&type=%s' - % (filename_, project_id, 'benef'), - 'target': 'new', + "type": "ir.actions.act_url", + "url": "/web/export_gap?filename_=%s&project_id=%s&type=%s" + % (filename_, project_id, "benef"), + "target": "new", } @api.multi @@ -548,263 +723,360 @@ class Project(models.Model): res = super(Project, self).write(values) for project in self: # Mise à jour des participant sur les séances si ajout - if values.get('porteurs_projets_ids'): + if values.get("porteurs_projets_ids"): participant_ids = [] - for porteur in values.get('porteurs_projets_ids'): + for porteur in values.get("porteurs_projets_ids"): if porteur[2]: - if porteur[2].get('eligible') and porteur[2].get('porteur_id'): + if porteur[2].get("eligible") and porteur[2].get("porteur_id"): vals = { - 'porteur_id': porteur[2]['porteur_id'], - 'eligible': porteur[2]['eligible'], + "porteur_id": porteur[2]["porteur_id"], + "eligible": porteur[2]["eligible"], } participant_ids.append((0, 0, vals)) for task in project.task_ids: - task.update({'participant_ids': participant_ids}) + task.update({"participant_ids": participant_ids}) return res class AdefpatTypeConvention(models.Model): - _name = 'adefpat.type.convention' - _description = 'Liste type de convention' + _name = "adefpat.type.convention" + _description = "Liste type de convention" name = fields.Char( string="Convention", required=True, ) date_end_validity = fields.Date("Date de fin de validité") + is_fd = fields.Boolean("FD") class AdefpatReunionGAP(models.Model): - _name = 'adefpat.reunion.gap' - _description = 'Réunions GAP' - _rec_name = 'date' + _name = "adefpat.reunion.gap" + _description = "Réunions GAP" + _rec_name = "date" + + @api.model + def _default_participant_ids(self): + if "default_project_id" in self.env.context: + project_id = self.env["project.project"].browse( + self.env.context["default_project_id"] + ) + if project_id.exists().membre_ids: + participant_ids = project_id.membre_ids.mapped(lambda p: (0, 0, { + "member_id": p.id, + "present": False + })) + self.update({"participant_ids": participant_ids}) + return participant_ids - # document_ids = fields.One2many('ir.attachment', string="Documents") date = fields.Date( string="Date du GAP", required=True, ) project_id = fields.Many2one( - 'project.project', - string='Projet', - default=lambda self: self.env.context.get('default_project_id')) + "project.project", + string="Projet", + required=True, + ondelete="cascade", + default=lambda self: self.env.context.get("default_project_id"), + ) + participant_ids = fields.One2many( + comodel_name="res.partner.reunion.gap", + inverse_name="gap_id", + string="Participants", + default=_default_participant_ids + ) + number_participants = fields.Integer( + string="Moyenne de participants", + compute="_compute_number_participants", + group_operator="avg", + ) + + @api.depends("participant_ids", "participant_ids.present") + @api.multi + def _compute_number_participants(self): + for gap in self: + gap.mean_participant_number = len(gap.participant_ids.filtered("present")) class AdefpatMembresProjets(models.Model): - _name = 'res.partner.membres.project' - _description = 'Membres' + _name = "res.partner.membres.project" + _description = "Membres" partner_id = fields.Many2one( - 'res.partner', - string='Membre', - domain=[ - ('active', '=', True), - ('is_company', '=', False)], + "res.partner", + string="Membre", + domain=[("active", "=", True), ("is_company", "=", False)], required=True, - on_delete='restrict') - lastname = fields.Char( - related='partner_id.lastname', - string="Nom", - store=False) + ondelete="restrict", + ) + name = fields.Char(related="partner_id.name", store=False) + lastname = fields.Char(related="partner_id.lastname", string="Nom", store=False) firstname = fields.Char( - related='partner_id.firstname', - string="Prénom", - store=False) - commune = fields.Char( - related='partner_id.city', - string="Commune", - store=False) - mobile = fields.Char( - related='partner_id.mobile', - string="Mobile", - store=False) - fixe = fields.Char( - related='partner_id.phone', - string="Fixe", - store=False) - email = fields.Char( - related='partner_id.email', - string="Email", - store=False) + related="partner_id.firstname", string="Prénom", store=False + ) + commune = fields.Char(related="partner_id.city", string="Commune", store=False) + mobile = fields.Char(related="partner_id.mobile", string="Mobile", store=False) + fixe = fields.Char(related="partner_id.phone", string="Fixe", store=False) + email = fields.Char(related="partner_id.email", string="Email", store=False) project_id = fields.Many2one( - 'project.project', - string='Projet', - default=lambda self: self.env.context.get('default_project_id')) + "project.project", + string="Projet", + required=True, + ondelete="cascade", + default=lambda self: self.env.context.get("default_project_id"), + ) class AdefpatConsultantsProjets(models.Model): - _name = 'res.partner.consultants.project' - _description = 'Consultants' - _rec_name = 'name' + _name = "res.partner.consultants.project" + _description = "Consultants" + _rec_name = "name" partner_id = fields.Many2one( - 'res.partner', - string='Consultant', + "res.partner", + string="Consultant", domain=[ - ('active', '=', True), - ('is_company', '=', False), - ('is_consultant_form', '=', True), - '|', ('reference', '=', 'reference'), - ('reference', '=', 'prereference')], + ("active", "=", True), + ("is_company", "=", False), + ("is_consultant_form", "=", True), + "|", + ("reference", "=", "reference"), + ("reference", "=", "prereference"), + ], required=True, - on_delete='restrict') - name = fields.Char(related='partner_id.name') - lastname = fields.Char( - related='partner_id.lastname', - string="Nom", - store=False) + ondelete="restrict", + ) + name = fields.Char(related="partner_id.name") + lastname = fields.Char(related="partner_id.lastname", string="Nom", store=False) firstname = fields.Char( - related='partner_id.firstname', - string="Prénom", - store=False) - mobile = fields.Char( - related='partner_id.mobile', - string="Mobile", - store=False) - email = fields.Char( - related='partner_id.email', - string="Email", - store=False) - reference = fields.Selection([ - ('reference', 'Référencé'), - ('prereference', 'Pré-Référencé'), - ('dereference', 'Dé-Référencé')], - related='partner_id.reference', + related="partner_id.firstname", string="Prénom", store=False + ) + mobile = fields.Char(related="partner_id.mobile", string="Mobile", store=False) + email = fields.Char(related="partner_id.email", string="Email", store=False) + reference = fields.Selection( + [ + ("reference", "Référencé"), + ("prereference", "Pré-Référencé"), + ("dereference", "Dé-Référencé"), + ], + related="partner_id.reference", string="Référencement", - store=False) + store=False, + ) cout_journée = fields.Float("Coût journée") is_selected = fields.Boolean("Est sélectionné") project_id = fields.Many2one( - 'project.project', - string='Projet', - default=lambda self: self.env.context.get('default_project_id')) + "project.project", + string="Projet", + required=True, + ondelete="cascade", + default=lambda self: self.env.context.get("default_project_id"), + ) class AdefpatPorteursProjets(models.Model): - _name = 'res.partner.porteur.project' - _description = 'Porteurs de projets' - _rec_name = 'name' + _name = "res.partner.porteur.project" + _description = "Porteurs de projets" + _rec_name = "name" porteur_id = fields.Many2one( - 'res.partner', - string='Porteur de projet', - domain=[('is_company', '=', False)], + "res.partner", + string="Porteur de projet", + domain=[("is_company", "=", False)], required=True, - on_delete='restrict' + ondelete="restrict", ) - name = fields.Char(related='porteur_id.name') - lastname = fields.Char( - related='porteur_id.lastname', - string="Nom", - store=False) + name = fields.Char(related="porteur_id.name") + lastname = fields.Char(related="porteur_id.lastname", string="Nom", store=False) firstname = fields.Char( - related='porteur_id.firstname', - string="Prénom", - store=False) - commune = fields.Char( - compute='_compute_commune', - string="Commune", - store=False) - mobile = fields.Char( - related='porteur_id.mobile', - string="Mobile", - store=False) - fixe = fields.Char( - related='porteur_id.phone', - string="Fixe", - store=False) - email = fields.Char( - related='porteur_id.email', - string="Email", - store=False) - statut = fields.Many2one( - 'adefpat.project.statut', - string="Statut") - eligible = fields.Boolean( - string="Eligible", - default=True) + related="porteur_id.firstname", string="Prénom", store=False + ) + sexe = fields.Selection(related="porteur_id.sexe", string="Sexe", store=False) + commune = fields.Char(compute="_compute_commune", string="Commune", store=False) + est_dans_comcom = fields.Boolean( + string="Commune dans ComCom du lieu de formation", + help="En cas de lieu tournant, si un des lieux de formation est sur la com com " + "du PP cocher la case", + ) + date_naissance = fields.Date( + related="porteur_id.date_naissance", string="Date de naissance" + ) + mobile = fields.Char(related="porteur_id.mobile", string="Mobile", store=False) + fixe = fields.Char(related="porteur_id.phone", string="Fixe", store=False) + email = fields.Char(related="porteur_id.email", string="Email", store=False) + statut = fields.Many2one("adefpat.project.statut", string="Statut") + eligible = fields.Boolean(string="Eligible", default=True) h_pratiques = fields.Boolean("H pratiques") h_theoriques = fields.Boolean("H théoriques") task_id = fields.Many2one( - 'project.task', - string='Séance', - default=lambda self: self.env.context.get('default_task_id')) + "project.task", + string="Séance", + ondelete="cascade", + default=lambda self: self.env.context.get("default_task_id"), + ) project_id = fields.Many2one( - 'project.project', - string='Projet', - default=lambda self: self.env.context.get('default_project_id')) - - @api.depends('porteur_id') + "project.project", + string="Projet", + ondelete="cascade", + default=lambda self: self.env.context.get("default_project_id"), + ) + project_num = fields.Char( + related="project_id.num_dossier", string="Numéro du dossier" + ) + project_secteur = fields.Selection( + related="project_id.secteur_crit", string="Secteur d'activité du dossier" + ) + number_hour_period = fields.Float( + "Nb heures sur période", compute="_compute_hour_period", default=0.0) + + # ------------------------------------------------------ + # Constrains + # ------------------------------------------------------ + @api.constrains("task_id", "project_id") + def _check_task_or_project(self): + if not self.task_id and not self.project_id: + raise ValidationError( + "Un porteur de projet doit être associé à une tâche ou un projet." + ) + + @api.depends("porteur_id") def _compute_commune(self): for r in self: if r.porteur_id.city: r.commune = r.porteur_id.city.capitalize() + @api.multi + def _compute_hour_period(self): + """ + fonction qui calcule le nombre d'heures par bénéficiaire + pour des valeurs passées dans le contexte + """ + period_start = fields.Date.from_string(self.env.context.get("period_start")) + period_end = fields.Date.from_string(self.env.context.get("period_end")) + for line in self: + if line.project_id: + task_ids = line.project_id.task_ids.filtered( + lambda t: t.date_deadline and t.date_deadline >= period_start + and t.date_deadline <= period_end) + days = [] + for task in task_ids: + participant_ids = task.participant_ids.filtered( + lambda p: p.h_theoriques) + if line.porteur_id in participant_ids.mapped("porteur_id"): + days.append(task.duree_hr * 7) + line.number_hour_period = sum(days) + + +class AdefpatPresentsGap(models.Model): + _name = "res.partner.reunion.gap" + _description = "Présents GAP" + + member_id = fields.Many2one( + "res.partner.membres.project", + string="Membre", + required=True, + ondelete="restrict", + ) + gap_id = fields.Many2one( + "adefpat.reunion.gap", + string="Séance GAP", + required=True, + default=lambda self: self.env.context.get("default_gap_id"), + ondelete="cascade", + ) + present = fields.Boolean("Présent") + class AdefpatStatut(models.Model): - _name = 'adefpat.project.statut' - _description = 'Statut' + _name = "adefpat.project.statut" + _description = "Statut" - name = fields.Char("Statut", required=True,) + name = fields.Char( + "Statut", + required=True, + ) class AdefpatFinancement(models.Model): - _name = 'adefpat.project.financement' - _description = 'Financements' + _name = "adefpat.project.financement" + _description = "Financements" partner_id = fields.Many2one( - 'res.partner', - string='Facturation', - domain=[ - ('active', '=', True)], + "res.partner", + string="Facturation", + domain=[("active", "=", True)], required=True, - on_delete='restrict' + ondelete="restrict", ) montant = fields.Float("Montant") project_id = fields.Many2one( - 'project.project', - string='Projet', - default=lambda self: self.env.context.get('default_project_id')) + "project.project", + string="Projet", + required=True, + ondelete="cascade", + default=lambda self: self.env.context.get("default_project_id"), + ) class AdefpatCout(models.Model): - _name = 'adefpat.project.cout' - _description = 'Coûts' + _name = "adefpat.project.cout" + _description = "Coûts" @api.model def _default_partner_id(self): - return self.env['res.partner.consultants.project'].search([ - ('project_id', '=', self.env.context.get('default_project_id')), - ('is_selected', '=', True) - ], limit=1).partner_id\ + return ( + self.env["res.partner.consultants.project"] + .search( + [ + ("project_id", "=", self.env.context.get("default_project_id")), + ("is_selected", "=", True), + ], + limit=1, + ) + .partner_id + ) @api.model def _default_cout_jour(self): - return self.env['res.partner.consultants.project'].search([ - ('project_id', '=', self.env.context.get('default_project_id')), - ('is_selected', '=', True) - ], limit=1).cout_journée + return ( + self.env["res.partner.consultants.project"] + .search( + [ + ("project_id", "=", self.env.context.get("default_project_id")), + ("is_selected", "=", True), + ], + limit=1, + ) + .cout_journée + ) module = fields.Char("Nom du module") partner_id = fields.Many2one( - 'res.partner', - string='Consultant', + "res.partner", + string="Consultant", domain=[ - ('active', '=', True), - ('is_company', '=', False), - ('is_consultant_form', '=', True)], - on_delete='restrict', - default=_default_partner_id) + ("active", "=", True), + ("is_company", "=", False), + ("is_consultant_form", "=", True), + ], + ondelete="restrict", + required=True, + default=_default_partner_id, + ) nb_jour_theorique = fields.Float("Nombre de jours théoriques") nb_jour_pratiques = fields.Float("Nombre de jours pratiques") cout_jour = fields.Float("Coût jour", default=_default_cout_jour) - montant = fields.Float("Montant", compute='_compute_montant', store=True) + montant = fields.Float("Montant", compute="_compute_montant", store=True) project_id = fields.Many2one( - 'project.project', - string='Projet', - default=lambda self: self.env.context.get('default_project_id')) + "project.project", + string="Projet", + required=True, + ondelete="cascade", + default=lambda self: self.env.context.get("default_project_id"), + ) - @api.depends('cout_jour', 'nb_jour_theorique') + @api.depends("cout_jour", "nb_jour_theorique") def _compute_montant(self): for r in self: - r.montant = r.cout_jour * r.nb_jour_theorique\ \ No newline at end of file + r.montant = r.cout_jour * r.nb_jour_theorique diff --git a/models/project_task.py b/models/project_task.py index cd7379b0be6cadf528e7841ded5323536288cae9..0146f2bdee308c4ba4321794e018140cb075dd69 100644 --- a/models/project_task.py +++ b/models/project_task.py @@ -1,90 +1,99 @@ -# Copyright 2020 Le Filament (<http://www.le-filament.com>) +# Copyright 2020-2022 Le Filament (<http://www.le-filament.com>) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo import fields, models, api +from odoo import api, fields, models class ProjectTask(models.Model): - _inherit = 'project.task' + _inherit = "project.task" @api.model def default_get(self, fields): res = super(ProjectTask, self).default_get(fields) - if 'default_project_id' in self.env.context: - default_project_id = self.env['project.project'].browse(self.env.context['default_project_id']) + if "default_project_id" in self.env.context: + default_project_id = self.env["project.project"].browse( + self.env.context["default_project_id"] + ) if default_project_id.exists().porteurs_projets_ids: participant_ids = [] for porteur_project in default_project_id.porteurs_projets_ids: vals = { - 'porteur_id': porteur_project.porteur_id.id, - 'lastname': porteur_project.lastname, - 'firstname': porteur_project.firstname, - 'commune': porteur_project.commune, - 'mobile': porteur_project.mobile, - 'fixe': porteur_project.fixe, - 'email': porteur_project.email, - 'eligible': porteur_project.eligible, + "porteur_id": porteur_project.porteur_id.id, + "lastname": porteur_project.lastname, + "firstname": porteur_project.firstname, + "commune": porteur_project.commune, + "mobile": porteur_project.mobile, + "fixe": porteur_project.fixe, + "email": porteur_project.email, + "eligible": porteur_project.eligible, } participant_ids.append((0, 0, vals)) - res.update({'participant_ids': participant_ids}) - cout_id = self.env['adefpat.project.cout'].search([ - ('project_id', '=', default_project_id.id), - ('partner_id', '!=', False) - ], limit=1) + res.update({"participant_ids": participant_ids}) + cout_id = self.env["adefpat.project.cout"].search( + [ + ("project_id", "=", default_project_id.id), + ("partner_id", "!=", False), + ], + limit=1, + ) if cout_id: - res.update({'formateur_id': cout_id.partner_id.id}) + res.update({"formateur_id": cout_id.partner_id.id}) return res user_id = fields.Many2one( - 'res.users', - string='CFD', + "res.users", + string="CFD", default=lambda self: self.env.uid, index=True, - track_visibility='always') + track_visibility="always", + ) formateur_id = fields.Many2one( - 'res.partner', - string='Formateur', + "res.partner", + string="Formateur", domain=[ - ('active', '=', True), - ('is_company', '=', False), - ('is_consultant_form', '=', True), - '|', ('reference', '=', 'reference'), - ('reference', '=', 'prereference')], - on_delete='restrict') - duree_jr = fields.Selection([ - ('demi_journee', "Demi journée"), - ('journee', "Journée"), + ("active", "=", True), + ("is_company", "=", False), + ("is_consultant_form", "=", True), + "|", + ("reference", "=", "reference"), + ("reference", "=", "prereference"), + ], + ondelete="restrict", + ) + duree_jr = fields.Selection( + [ + ("demi_journee", "Demi journée"), + ("journee", "Journée"), ], - "Durée (en jours)") - duree_hr = fields.Float( - "Durée en jours", - compute='_compute_duree_hr', - store=True + "Durée (en jours)", ) + duree_hr = fields.Float("Durée (int)", compute="_compute_duree_hr", store=True) participant_ids = fields.One2many( - 'res.partner.porteur.project', - 'task_id', - string="Participants") - cout_seance = fields.Float("Coût de la séance", compute='_compute_cout_seance') + "res.partner.porteur.project", "task_id", string="Participants" + ) + cout_seance = fields.Float("Coût de la séance", compute="_compute_cout_seance") date_account = fields.Date("Date de la facture") - @api.depends('duree_jr') + @api.depends("duree_jr") @api.multi def _compute_duree_hr(self): for task in self: - if task.duree_jr == 'journee': + if task.duree_jr == "journee": task.duree_hr = 1 - if task.duree_jr == 'demi_journee': - task.duree_hr = 0.5\ + if task.duree_jr == "demi_journee": + task.duree_hr = 0.5 - @api.depends('formateur_id', 'duree_hr') + @api.depends("formateur_id", "duree_hr") @api.multi def _compute_cout_seance(self): for task in self: if task.formateur_id: - formateur_proj = self.env['adefpat.project.cout'].search([ - ('project_id', '=', task.project_id.id), - ('partner_id', '=', task.formateur_id.id) - ], limit=1) + formateur_proj = self.env["adefpat.project.cout"].search( + [ + ("project_id", "=", task.project_id.id), + ("partner_id", "=", task.formateur_id.id), + ], + limit=1, + ) if formateur_proj: - task.cout_seance = formateur_proj.cout_jour * task.duree_hr \ No newline at end of file + task.cout_seance = formateur_proj.cout_jour * task.duree_hr diff --git a/models/res_company.py b/models/res_company.py index b4b69c85584d42fa350f2f376398941027dc36d8..6058d8ec83a9efe8f79cdba89f3b9ca14fb0d1a4 100644 --- a/models/res_company.py +++ b/models/res_company.py @@ -1,15 +1,16 @@ -# Copyright 2020 Le Filament (<http://www.le-filament.com>) +# Copyright 2020-2022 Le Filament (<http://www.le-filament.com>) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo import api, fields, models, _ +from odoo import fields, models class ResCompany(models.Model): _inherit = "res.company" explication_financement = fields.Text( - string="Explication financement porteur de projet") + string="Explication financement porteur de projet" + ) modalites_intervention = fields.Text("Modalités d'intervention") modalites_facturation = fields.Text("Modalités de facturation") modalites_reponse = fields.Text("Modalités de réponse") - modalites_modif_marche = fields.Text("Modalités de modification du marché") \ No newline at end of file + modalites_modif_marche = fields.Text("Modalités de modification du marché") diff --git a/models/res_config_settings.py b/models/res_config_settings.py index 4492878d41a0c5667ef9fb870747615415bbf3e3..421a07a266f31356e27dbcca5a2f1ff9f435ccd7 100644 --- a/models/res_config_settings.py +++ b/models/res_config_settings.py @@ -1,29 +1,34 @@ -# Copyright 2020 Le Filament (<http://www.le-filament.com>) +# Copyright 2020-2022 Le Filament (<http://www.le-filament.com>) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo import api, fields, models +from odoo import fields, models class ResConfigSettings(models.TransientModel): - _inherit = 'res.config.settings' + _inherit = "res.config.settings" explication_financement = fields.Text( - related='company_id.explication_financement', - string="Explication financement porteur de projet", - readonly=False) + related="company_id.explication_financement", + string="Explication financement porteur de projet", + readonly=False, + ) modalites_intervention = fields.Text( - related='company_id.modalites_intervention', + related="company_id.modalites_intervention", string="Modalités d'intervention", - readonly=False) + readonly=False, + ) modalites_facturation = fields.Text( - related='company_id.modalites_facturation', + related="company_id.modalites_facturation", string="Modalités de facturation", - readonly=False) + readonly=False, + ) modalites_reponse = fields.Text( - related='company_id.modalites_reponse', + related="company_id.modalites_reponse", string="Modalités de réponse", - readonly=False) + readonly=False, + ) modalites_modif_marche = fields.Text( - related='company_id.modalites_modif_marche', + related="company_id.modalites_modif_marche", string="Modalités de modification du marché", - readonly=False) \ No newline at end of file + readonly=False, + ) diff --git a/models/res_partner.py b/models/res_partner.py index 1dd030bf71783cbedd7c8a5629b82e7010733015..b4d392ed8005c431c7131665dc2c3a86e42cddb5 100644 --- a/models/res_partner.py +++ b/models/res_partner.py @@ -1,32 +1,35 @@ -# Copyright 2020 Le Filament (<http://www.le-filament.com>) +# Copyright 2020-2022 Le Filament (<http://www.le-filament.com>) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo import models, fields, api from ast import literal_eval +from odoo import api, fields, models + class ResPartner(models.Model): - _inherit = 'res.partner' + _inherit = "res.partner" project_count = fields.Integer( - compute='_compute_project_count', - string='Nombre de projets', - store=True) + compute="_compute_project_count", string="Nombre de projets", store=True + ) def _compute_project_count(self): for partner in self: - porteurs_projects = self.env['res.partner.porteur.project'].search([ - ('partner_id', '=', partner.id)]) - projects = self.env['project.project'].search_count([ - ('porteurs_projets_ids', 'in', porteurs_projects.ids)]) + porteurs_projects = self.env["res.partner.porteur.project"].search( + [("partner_id", "=", partner.id)] + ) + projects = self.env["project.project"].search_count( + [("porteurs_projets_ids", "in", porteurs_projects.ids)] + ) partner.project_count = projects @api.multi def action_view_partner_projects(self): self.ensure_one() - action = self.env.ref('project.open_view_project_all_config').read()[0] - action['domain'] = literal_eval(action['domain']) - porteurs_projects = self.env['res.partner.porteur.project'].search([ - ('partner_id', '=', self.id)]) - action['domain'].append(('porteurs_projets_ids', 'in', porteurs_projects.ids)) - return action \ No newline at end of file + action = self.env.ref("project.open_view_project_all_config").read()[0] + action["domain"] = literal_eval(action["domain"]) + porteurs_projects = self.env["res.partner.porteur.project"].search( + [("partner_id", "=", self.id)] + ) + action["domain"].append(("porteurs_projets_ids", "in", porteurs_projects.ids)) + return action diff --git a/report/__init__.py b/report/__init__.py new file mode 100755 index 0000000000000000000000000000000000000000..18400369bd13da878c23326c75efa363a88da3d1 --- /dev/null +++ b/report/__init__.py @@ -0,0 +1,4 @@ +# © 2020 Le Filament (<http://www.le-filament.com>) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import formateur_project_report diff --git a/report/formateur_project_report.py b/report/formateur_project_report.py new file mode 100644 index 0000000000000000000000000000000000000000..8d15fb7ba0713d6875a7c21e4cf77493a17ffbc2 --- /dev/null +++ b/report/formateur_project_report.py @@ -0,0 +1,98 @@ +# © 2022 Le Filament (<http://www.le-filament.com>) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import tools +from odoo import models, fields, api + + +class AdefpatFormateurProjectReport(models.Model): + _name = "adefpat.formateur.project.report" + _description = "Rapport formateurs" + _auto = False + _order = 'formateur_id' + + # project fields + project_id = fields.Many2one( + comodel_name="project.project", + string="Nom du dossier", + ) + project_num = fields.Char( + related="project_id.num_dossier", string="Numéro du dossier" + ) + type_convention_id = fields.Many2one( + comodel_name="adefpat.type.convention", + string="Convention de financement", + related="project_id.type_convention_id" + ) + + # formateur fields + formateur_id = fields.Many2one( + comodel_name="res.partner", + string="Formateur", + ) + formateur_name = fields.Char("Nom du formateur", related="formateur_id.name") + number_hour_period = fields.Float("Nb heures sur période") + + # ------------------------------------------------------ + # Query + # ------------------------------------------------------ + def _select(self): + select_str = """ + SELECT + row_number() over (ORDER BY pr.id) AS id, + pr.id AS project_id, + t.formateur_id AS formateur_id, + SUM(t.duree_hr * 7) AS number_hour_period + FROM + project_task t + LEFT JOIN + project_project pr ON t.project_id = pr.id + LEFT JOIN + adefpat_type_convention c ON pr.type_convention_id = c.id + """ + + return select_str + + def _where(self): + period_start = fields.Date.from_string(self.env.context.get("period_start")) + period_end = fields.Date.from_string(self.env.context.get("period_end")) + is_fd = self.env.context.get("is_fd") + is_closed = self.env.context.get("is_closed") + + where = """ + WHERE + t.duree_hr > 0 + """ + + if period_start and period_end: + where += """ + AND t.date_deadline >= '%s' + AND t.date_deadline <= '%s' + """ % (period_start, period_end) + + if is_fd: + where += "AND c.is_fd is true " + + if is_closed: + where += """ + AND pr.closing_date IS NOT NULL + AND pr.closing_date <= '%s' + AND pr.closing_date >= '%s' + """ % (period_end, period_start) + + return where + + def _group_by(self): + group_by = """ + GROUP BY + pr.id, t.formateur_id + """ + + return group_by + + @api.model_cr + def init(self): + tools.drop_view_if_exists(self.env.cr, self._table) + self.env.cr.execute("""CREATE or REPLACE VIEW %s as ( + %s %s %s + )""" % (self._table, self._select(), self._where(), self._group_by())) diff --git a/report/formateur_project_report.xml b/report/formateur_project_report.xml new file mode 100644 index 0000000000000000000000000000000000000000..cb542674d64632c64641614a7344a524850e8265 --- /dev/null +++ b/report/formateur_project_report.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <data> + <!-- TREE VIEW --> + <!-- Rapport Porteurs de Projets --> + <record id="view_adefpat_formateur_reporting_tree" model="ir.ui.view"> + <field name="name">view.adefpat.formateur.reporting.tree</field> + <field name="model">adefpat.formateur.project.report</field> + <field name="arch" type="xml"> + <tree string="Formateurs" create="0" edit="0" delete="0"> + <field name="project_num" /> + <field name="project_id" string="Nom du dossier" /> + <field name="type_convention_id" /> + <field name="formateur_name" /> + <field name="number_hour_period" /> + </tree> + </field> + </record> + + </data> +</odoo> diff --git a/report/porteur_project_report.py b/report/porteur_project_report.py new file mode 100644 index 0000000000000000000000000000000000000000..bb8cdbd4d9c2a1d399936465370e219e3b949f1a --- /dev/null +++ b/report/porteur_project_report.py @@ -0,0 +1,236 @@ +# © 2022 Le Filament (<http://www.le-filament.com>) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import tools +from odoo import models, fields, api + + +class ScopContributionReport(models.Model): + _name = "scop.contribution.report" + _description = "Vue cotisations" + _auto = False + _order = 'year desc, partner_id' + + name = fields.Char(compute='_compute_name') + year = fields.Char('Année de cotisation') + source = fields.Selection( + string='Source', + selection=[('odoo', 'Odoo')], + required=True,) + + type_contribution_id = fields.Many2one( + comodel_name="scop.contribution.type", + string="Type de cotisation", + readonly=True) + partner_id = fields.Many2one('res.partner', string='Partner', readonly=True) + amount_called = fields.Float('Montant Appelé') + amount_paid = fields.Float('Montant Payé') + amount_due = fields.Float('Montant Restant') + is_loss = fields.Boolean('Exonération/Perte', compute="_compute_is_loss") + payments = fields.Html('Paiements', compute="_compute_payments") + + _depends = { + 'account.invoice': [ + 'year', 'type_contribution_id', 'partner_id', + 'amount_total_signed', 'residual_company_signed', + 'state', 'type', 'is_contribution', 'refund_invoice_id' + ], + } + + # ------------------------------------------------------ + # Sub Query + # ------------------------------------------------------ + def _select_invoice(self): + select_str = """ + SELECT + 'odoo' as source, + CAST(i.year AS VARCHAR), + i.type_contribution_id, + i.partner_id, + SUM(i.amount_total_signed) AS amount_called, + SUM(i.amount_total_signed - i.residual_company_signed) AS amount_paid, + SUM(i.residual_company_signed) AS amount_due + """ + return select_str + + def _from_invoice(self): + from_str = """ + FROM + account_invoice i + """ + return from_str + + def _where_invoice(self): + where_str = """ + WHERE + i.state in ('open', 'paid', 'in_payment') AND + i.is_contribution = true + """ + return where_str + + def _groupby_invoice(self): + groupby_str = """ + GROUP BY + i.year, + i.type_contribution_id, + i.partner_id + """ + return groupby_str + + def _query_invoice(self): + query = "(%s %s %s %s)" % ( + self._select_invoice(), self._from_invoice(), + self._where_invoice(), self._groupby_invoice()) + return query + + def _subquery(self): + return self._query_invoice() + + # ------------------------------------------------------ + # Main Query + # ------------------------------------------------------ + def _select(self): + select_str = """ + SELECT + ROW_NUMBER() OVER(ORDER BY c.year, c.partner_id) AS id, + c.source, + c.year, + c.type_contribution_id, + c.partner_id, + SUM(c.amount_called) AS amount_called, + SUM(c.amount_paid) AS amount_paid, + SUM(c.amount_due) AS amount_due + FROM ( + """ + return select_str + + def _query_groupby(self): + return """ + GROUP BY + c.year, + c.type_contribution_id, + c.partner_id, + c.source + """ + + def _query_order(self): + return "ORDER BY c.year DESC" + + def _query(self): + query = ( + self._select() + self._subquery() + ') c ' + + self._query_groupby() + self._query_order() + ) + return query + + @api.model_cr + def init(self): + tools.drop_view_if_exists(self.env.cr, self._table) + self.env.cr.execute("""CREATE or REPLACE VIEW %s as ( + %s + )""" % ( + self._table, self._query())) + + # ------------------------------------------------------ + # Computed fields + # ------------------------------------------------------ + @api.multi + def _compute_is_loss(self): + for contribution in self: + contribution.is_loss = contribution._get_is_loss() + + @api.multi + def _compute_name(self): + for contribution in self: + contribution.name = (contribution.year + ' - ' + + contribution.type_contribution_id.name + + ' - ' + contribution.partner_id.name) + + @api.multi + def _compute_payments(self): + for contribution in self: + contribution.payments = contribution._get_payment() + + # ------------------------------------------------------ + # Business functions + # ------------------------------------------------------ + def _get_is_loss(self): + invoice_ids = self.env['account.invoice'].sudo().search([ + ('year', '=', int(self.year)), + ('partner_id', '=', self.partner_id.id), + ('type_contribution_id', '=', self.type_contribution_id.id), + ]) + refund_ids = invoice_ids.filtered(lambda i: i.type == 'out_refund') + if refund_ids: + return True + else: + return False + + def _get_payment(self): + self.ensure_one() + payments_html = False + if self.source == 'odoo': + invoice_ids = self.get_invoice_contribution() + payment_ids = invoice_ids.mapped('payment_move_line_ids') + if payment_ids: + payments = payment_ids.mapped(lambda p: { + 'date': p.date, + 'name': p.name, + 'ref': p.ref, + 'credit': p.credit, + 'class': '' + } if not p.invoice_id else { + 'date': p.date, + 'name': p.name, + 'ref': '', + 'credit': p.credit, + 'class': 'text-danger' + }) + payments_html = self._get_html_table(payments) + if not payments_html: + payments_html = "<p>Il n'y a pas de paiements associés à cette cotisation</p>" + return payments_html + + def get_invoice_contribution(self): + invoice_ids = self.env['account.invoice'].sudo().search([ + ('year', '=', int(self.year)), + ('partner_id', '=', self.partner_id.id), + ('type_contribution_id', '=', self.type_contribution_id.id), + ('type', '=', 'out_invoice'), + '|', + ('bordereau_id.state', 'not in', ('cancel',)), + ('bordereau_id', '=', False) + ]) + return invoice_ids + + def _get_html_table(self, payments): + """ + :param payments: list of dict {date, name, ref, amount} + @return: HTML table with payments + """ + start_html = """ + <table class='table table-sm table-striped table-hover table-bordered'> + <thead><tr> + <th>Date</th> + <th>Libellé</th> + <th>Référence</th> + <th>Montant</th> + </tr></thead> + <tbody> + """ + content_html = "" + for payment in payments: + content_html += """ + <tr class='%s'> + <td>%s</td> + <td>%s</td> + <td>%s</td> + <td class='text-right'>%.2f €</td> + </tr> + """ % (payment.get('class', ''), + payment.get('date', '').strftime('%d/%m/%Y'), + payment.get('name', ''), payment.get('ref', ''), + payment.get('credit', 0.0),) + + end_html = "</tbody></table>" + return start_html + content_html + end_html diff --git a/report/porteur_project_report.xml b/report/porteur_project_report.xml new file mode 100644 index 0000000000000000000000000000000000000000..6b7fbd72581220dcf5dd99fc7dbb5dce9d511d09 --- /dev/null +++ b/report/porteur_project_report.xml @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <data> + <!-- SEARCH VIEW --> + <record model="ir.ui.view" id="scop_contribution_report_search"> + <field name="name">scop.contribution.report.search</field> + <field name="model">scop.contribution.report</field> + <field name="arch" type="xml"> + <search string="Cotisations"> + <field name='partner_id'/> + <field name="year"/> + <field name="type_contribution_id"/> + <filter name="filter_paid" string="À régler" domain="[('amount_due', '!=', 0)]"/> + <filter name="filter_paid" string="Réglé" domain="[('amount_due', '=', 0)]"/> + <separator></separator> + <filter name="filter_cg" string="Cotisations CG" domain="[('type_contribution_id', '=', 1)]"/> + <filter name="filter_ur" string="Cotisations UR" domain="[('type_contribution_id', '=', 3)]"/> + <filter name="filter_fede" string="Cotisations Fédé" domain="[('type_contribution_id', '=', 2)]"/> + <group expand="0" string="Group By"> + <filter name="group_by_type_contribution_id" string="Type de cotisation" context="{'group_by':'type_contribution_id'}"/> + <filter name="group_by_year" string="Année" context="{'group_by':'year'}"/> + </group> + </search> + </field> + </record> + + <!-- TREE VIEW --> + <record model="ir.ui.view" id="scop_contribution_report_tree"> + <field name="name">scop.contribution.report.tree</field> + <field name="model">scop.contribution.report</field> + <field name="arch" type="xml"> + <tree string="Cotisations"> + <field name="year"/> + <field name="type_contribution_id"/> + <field name='partner_id'/> + <field name='amount_called'/> + <field name='amount_paid'/> + <field name='amount_due'/> + </tree> + </field> + </record> + + <!-- FORM VIEW --> + <record model="ir.ui.view" id="scop_contribution_report_form"> + <field name="name">scop.contribution.report.form</field> + <field name="model">scop.contribution.report</field> + <field name="arch" type="xml"> + <form string="Cotisations"> + <sheet> + <h2><field name="name"/></h2> + <separator></separator> + <table class="table table-striped table-hover"> + <thead> + <tr> + <th>Montant Appelé</th> + <th>Montant payé</th> + <th>Montant restant</th> + </tr> + </thead> + <tbody> + <tr> + <td><field name='amount_called'/> €</td> + <td><field name='amount_paid'/> €</td> + <td><field name='amount_due'/> €</td> + </tr> + </tbody> + </table> + <separator></separator> + <h4>Détail des Paiements</h4> + <field name='payments' widget="html"/> + </sheet> + </form> + </field> + </record> + + <!-- PIVOT VIEW --> + <record model="ir.ui.view" id="scop_contribution_report_pivot"> + <field name="name">scop.contribution.report.pivot</field> + <field name="model">scop.contribution.report</field> + <field name="arch" type="xml"> + <pivot string="Cotisations"> + <field name="year" type="row"/> + <field name="type_contribution_id" type="row"/> + <field name='amount_called' type="measure"/> + <field name='amount_paid' type="measure"/> + <field name='amount_due' type="measure"/> + </pivot> + </field> + </record> + + <!-- GRAPH VIEW --> + <record model="ir.ui.view" id="scop_contribution_report_graph"> + <field name="name">scop.contribution.report.graph</field> + <field name="model">scop.contribution.report</field> + <field name="arch" type="xml"> + <graph string="Cotisations"> + <field name="year"/> + <field name='amount_called' type="measure"/> + <field name='amount_paid' type="measure"/> + <field name='amount_due' type="measure"/> + </graph> + </field> + </record> + + <!-- ACTION --> + <record id="scop_contribution_report_action" model="ir.actions.act_window"> + <field name="name">Vue cotisations</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">scop.contribution.report</field> + <field name="view_mode">tree,pivot,graph,form</field> + </record> + + <!-- Menu --> + <menuitem + id="scop_contribution_report_menu" + name="Cotisations" + parent="account.account_reports_management_menu" + action="scop_contribution_report_action" + sequence="80" + /> + </data> +</odoo> diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv index 77e3ee0da689f5aa59175c1c2a995db1e36a4958..cde1010f2d374910b6579bb59979cec402db8a73 100644 --- a/security/ir.model.access.csv +++ b/security/ir.model.access.csv @@ -1,9 +1,11 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_adefpat_type_convention_group_user,access_adefpat_type_convention_group_user,model_adefpat_type_convention,base.group_user,1,1,1,1 access_adefpat_reunion_gap_group_user,access_adefpat_reunion_gap_group_user,model_adefpat_reunion_gap,base.group_user,1,1,1,1 +access_res_partner_reunion_gap_group_user,access_res_partner_reunion_gap_group_user,model_res_partner_reunion_gap,base.group_user,1,1,1,1 access_adefpat_project_financement_group_user,access_adefpat_project_financement_group_user,model_adefpat_project_financement,base.group_user,1,1,1,1 access_adefpat_project_cout_group_user,access_adefpat_project_cout_group_user,model_adefpat_project_cout,base.group_user,1,1,1,1 access_res_partner_membres_project_group_user,access_res_partner_membres_project_group_user,model_res_partner_membres_project,base.group_user,1,1,1,1 access_res_partner_consultants_project_group_user,access_res_partner_consultants_project_group_user,model_res_partner_consultants_project,base.group_user,1,1,1,1 access_res_partner_porteur_project_group_user,access_res_partner_porteur_project_group_user,model_res_partner_porteur_project,base.group_user,1,1,1,1 -access_adefpat_project_statut_group_user,access_adefpat_project_statut_group_user,model_adefpat_project_statut,base.group_user,1,1,1,1 \ No newline at end of file +access_adefpat_project_statut_group_user,access_adefpat_project_statut_group_user,model_adefpat_project_statut,base.group_user,1,1,1,1 +access_adefpat_formateur_project_report_group_user,access_adefpat_formateur_project_report_group_user,model_adefpat_formateur_project_report,base.group_user,1,1,1,1 diff --git a/static/src/js/project.js b/static/src/js/project.js deleted file mode 100644 index 0c951da763a7417a984e6cd867eb8b1f67394cc7..0000000000000000000000000000000000000000 --- a/static/src/js/project.js +++ /dev/null @@ -1,15 +0,0 @@ -odoo.define('adefpat_project.update_kanban', function (require) { -'use strict'; - - var KanbanRecord = require('web_kanban.Record'); - - KanbanRecord.include({ - _openRecord: function () { - if (this.modelName === 'project.project') { - this.$('.o_kanban_card_manage_section a').first().click(); - } else { - this._super.apply(this, arguments); - } - }, - }); -}); \ No newline at end of file diff --git a/views/project_assets.xml b/views/project_assets.xml deleted file mode 100644 index 305cc18029b9d54c98ff205f4c7af0828bdf0520..0000000000000000000000000000000000000000 --- a/views/project_assets.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<odoo> - <template id="assets_backend" name="project assets" inherit_id="web.assets_backend"> - <xpath expr="." position="inside"> - <script type="text/javascript" src="/adefpat_project/static/src/js/project.js"></script> - </xpath> - </template> -</odoo> \ No newline at end of file diff --git a/views/project_task_views.xml b/views/project_task_views.xml index b0c678aab5bf182bcf7392693bf52d9ca7b75410..ae500b6e64ba376a3e39f3f963a5070db9666854 100644 --- a/views/project_task_views.xml +++ b/views/project_task_views.xml @@ -1,14 +1,16 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8" ?> +<!-- Copyright 2022 Le Filament + License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> <odoo> <data> <record id="view_task_tree2" model="ir.ui.view"> <field name="name">project.task.tree.inherit</field> <field name="model">project.task</field> - <field name="inherit_id" ref="project.view_task_tree2"/> + <field name="inherit_id" ref="project.view_task_tree2" /> <field name="arch" type="xml"> <field name="date_deadline" position="after"> - <field name="duree_hr" sum="Durée"/> + <field name="duree_hr" sum="Durée" /> </field> </field> </record> @@ -16,38 +18,45 @@ <record id="view_task_form2" model="ir.ui.view"> <field name="name">view.task.form2.form.inherit</field> <field name="model">project.task</field> - <field name="inherit_id" ref="project.view_task_form2"/> + <field name="inherit_id" ref="project.view_task_form2" /> <field name="arch" type="xml"> <xpath expr="//notebook" position="before"> <group> <field name="participant_ids" widget="one2many"> - <tree string="Participants" editable="top" default_order='porteur_id'> - <field name="porteur_id"/> - <field name="lastname"/> - <field name="firstname"/> - <field name="commune"/> - <field name="mobile"/> - <field name="fixe"/> - <field name="email" widget="email"/> - <field name="h_theoriques"/> - <field name="h_pratiques"/> - <field name="eligible"/> + <tree + string="Participants" + editable="top" + default_order='porteur_id' + > + <field name="porteur_id" /> + <field name="lastname" /> + <field name="firstname" /> + <field name="commune" /> + <field name="mobile" /> + <field name="fixe" /> + <field name="email" widget="email" /> + <field name="h_theoriques" /> + <field name="h_pratiques" /> + <field name="eligible" /> </tree> </field> </group> </xpath> <xpath expr="//field[@name='user_id']" position="after"> - <field name="formateur_id"/> + <field name="formateur_id" /> </xpath> <xpath expr="//field[@name='date_deadline']" position="after"> - <field name="date_account"/> - <field name="duree_jr"/> - <field name="cout_seance"/> + <field name="date_account" /> + <field name="duree_jr" /> + <field name="cout_seance" /> <field name="subtask_project_id" invisible="1" /> <field name="parent_id" invisible="1" /> <field name="partner_id" invisible="1" /> </xpath> - <xpath expr="//notebook/page[@name='description_page']" position="attributes"> + <xpath + expr="//notebook/page[@name='description_page']" + position="attributes" + > <attribute name="invisible">1</attribute> </xpath> <xpath expr="//notebook/page[@name='extra_info']" position="attributes"> @@ -59,10 +68,10 @@ <record id="quick_create_task_form" model="ir.ui.view"> <field name="name">project.task.form.quick_create.inherit</field> <field name="model">project.task</field> - <field name="inherit_id" ref="project.quick_create_task_form"/> + <field name="inherit_id" ref="project.quick_create_task_form" /> <field name="arch" type="xml"> <field name="user_id" position="replace"> - <field name="date_deadline"/> + <field name="date_deadline" /> </field> </field> </record> @@ -70,18 +79,21 @@ <record model="ir.ui.view" id="view_task_kanban"> <field name="name">project.task.kanban</field> <field name="model">project.task</field> - <field name="inherit_id" ref="project.view_task_kanban"/> + <field name="inherit_id" ref="project.view_task_kanban" /> <field name="arch" type="xml"> <kanban position="attributes"> <attribute name="default_order">date_deadline asc</attribute> </kanban> <field name="user_id" position="after"> - <field name="cout_seance"/> + <field name="cout_seance" /> </field> - <xpath expr="//templates//div[@class='o_kanban_record_headings']//small" position="inside"> - <br/> + <xpath + expr="//templates//div[@class='o_kanban_record_headings']//small" + position="inside" + > + <br /> <t t-if="record.cout_seance.raw_value"> - <span>Coût de la séance: </span><field name="cout_seance"/> € + <span>Coût de la séance: </span><field name="cout_seance" /> € </t> </xpath> @@ -89,4 +101,4 @@ </record> </data> -</odoo> \ No newline at end of file +</odoo> diff --git a/views/project_views.xml b/views/project_views.xml index 1b5d1a51454814f10290839b3c13e245fa0c9906..66b014632833d667c0b3a6faac0a238f46fe11b3 100644 --- a/views/project_views.xml +++ b/views/project_views.xml @@ -1,46 +1,56 @@ -<?xml version="1.0"?> -<!-- Copyright 2020 Le Filament +<?xml version="1.0" ?> +<!-- Copyright 2020-2022 Le Filament License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> - <odoo> <data> <record model="ir.ui.view" id="view_project_kanban"> <field name="name">project.project.kanban</field> <field name="model">project.project</field> - <field name="inherit_id" ref="project.view_project_kanban"/> + <field name="inherit_id" ref="project.view_project_kanban" /> <field name="priority">30</field> <field name="arch" type="xml"> <kanban position="attributes"> <attribute name="default_order">num_dossier</attribute> <attribute name="quick_create">false</attribute> - <attribute name="on_create"></attribute> + <attribute name="on_create" /> </kanban> <field name="name" position="after"> - <field name="num_dossier"/> - <field name="user_id"/> + <field name="num_dossier" /> + <field name="user_id" /> </field> <xpath expr="//div[hasclass('o_primary')]" position="after"> <div> <span t-if="record.num_dossier.value"> - <strong>Numéro de dossier : <t t-esc="record.num_dossier.value"/></strong><br/> + <strong>Numéro de dossier : <t + t-esc="record.num_dossier.value" + /></strong><br /> </span> <span> - CFD : <field name="user_id"/> + CFD : <field name="user_id" /> </span> </div> </xpath> <div class="o_project_kanban_boxes" position="replace"> - <a class="o_project_kanban_box" name="%(project.act_project_project_2_project_task_all)d" type="action"> + <a + class="o_project_kanban_box" + name="%(project.act_project_project_2_project_task_all)d" + type="action" + > <div> - <span class="o_value"><t t-esc="record.task_count.value"/></span> + <span class="o_value"><t + t-esc="record.task_count.value" + /></span> <span class="o_label">Séances</span> </div> </a> - <a t-if="record.allow_timesheets.raw_value" - class="o_project_kanban_box o_project_timesheet_box" - name="%(hr_timesheet.act_hr_timesheet_line_by_project)d" - type="action" groups="hr_timesheet.group_hr_timesheet_user"> + <a + t-if="record.allow_timesheets.raw_value" + class="o_project_kanban_box o_project_timesheet_box" + name="%(hr_timesheet.act_hr_timesheet_line_by_project)d" + type="action" + groups="hr_timesheet.group_hr_timesheet_user" + > <div> <span class="o_label">Feuilles de temps</span> </div> @@ -52,29 +62,47 @@ <record id="increment_num_dossier_sequence" model="ir.sequence"> <field name="name">Champ incrémentation</field> <field name="code">increment_num_dossier</field> - <field name="prefix"></field> + <field name="prefix" /> <field name="padding">4</field> - <field name="company_id" eval="False"/> + <field name="company_id" eval="False" /> </record> <record id="edit_project" model="ir.ui.view"> <field name="name">project.project.form</field> <field name="model">project.project</field> <field name="priority">30</field> - <field name="inherit_id" ref="project.edit_project"/> + <field name="inherit_id" ref="project.edit_project" /> <field name="arch" type="xml"> <header position="inside"> - <button string="Valider pour CA" type="object" class="btn-primary" name="validate_ca" attrs="{'invisible': ['|', ('num_dossier', '!=', False), ('project_status', '!=', %(adefpat_project.project_status_pret_pour_ca)d)]}"/> - <button name="export_gap" string="Exporter les membres du GAP" type="object"/> - <button name="export_benef" string="Exporter les bénéficiaires" type="object"/> + <button + string="Valider pour CA" + type="object" + class="btn-primary" + name="validate_ca" + attrs="{'invisible': ['|', ('num_dossier', '!=', False), ('project_status', '!=', %(adefpat_project.project_status_pret_pour_ca)d)]}" + /> + <button + name="export_gap" + string="Exporter les membres du GAP" + type="object" + /> + <button + name="export_benef" + string="Exporter les bénéficiaires" + type="object" + /> </header> - <button name="attachment_tree_view" position="replace"/> + <button name="attachment_tree_view" position="replace" /> <h1 position="after"> <h3> - <field name="name_subtitle" placeholder="Sous-titre"/> + <field name="name_subtitle" placeholder="Sous-titre" /> </h3> <h4> - <field name="num_dossier" attrs="{'invisible': [('num_dossier', '=', False)]}" readonly="1"/> + <field + name="num_dossier" + attrs="{'invisible': [('num_dossier', '=', False)]}" + readonly="1" + /> </h4> </h1> @@ -83,7 +111,7 @@ </xpath> <field name="task_count" position="replace"> - <field string="Séances" name="task_count" widget="statinfo"/> + <field string="Séances" name="task_count" widget="statinfo" /> </field> <page name="settings" position="attributes"> @@ -92,49 +120,56 @@ <xpath expr="//notebook" position="before"> <group> - <group attrs="{'invisible': [('project_status', '!=', %(adefpat_project.project_status_demande)d), ('project_status', '!=', %(adefpat_project.project_status_instruction)d)]}"> - <field name="first_contact_id"/> - <field name="type_contact"/> - <field name="date_first_contact"/> - <field name="prescripteur_id"/> + <group + attrs="{'invisible': [('project_status', '!=', %(adefpat_project.project_status_demande)d), ('project_status', '!=', %(adefpat_project.project_status_instruction)d)]}" + > + <field name="first_contact_id" /> + <field name="type_contact" /> + <field name="date_first_contact" /> + <field name="prescripteur_id" /> </group> <group> - <field name="territoire_id" options="{'no_create': True}"/> - <field name="departement_ids" widget="many2many_tags"/> - <field name="region_ids" widget="many2many_tags"/> - <field name="petr_ids" widget="many2many_tags"/> - <field name="departement"/> - <field name="user_id"/> - <field name="description"/> + <field name="territoire_id" options="{'no_create': True}" /> + <field name="departement_ids" widget="many2many_tags" /> + <field name="region_ids" widget="many2many_tags" /> + <field name="petr_ids" widget="many2many_tags" /> + <field name="departement" /> + <field name="user_id" /> + <field name="description" /> </group> </group> <field name="porteurs_projets_ids" widget="one2many"> - <tree string="Porteurs de projet" editable="top" default_order='porteur_id'> - <field name="porteur_id"/> - <field name="lastname"/> - <field name="firstname"/> - <field name="commune"/> - <field name="mobile"/> - <field name="fixe"/> - <field name="email" widget="email"/> - <field name="statut" options="{'no_create': True}"/> - <field name="eligible"/> + <tree + string="Porteurs de projet" + editable="top" + default_order='porteur_id' + > + <field name="porteur_id" /> + <field name="lastname" /> + <field name="firstname" /> + <field name="commune" /> + <field name="mobile" /> + <field name="fixe" /> + <field name="email" widget="email" /> + <field name="statut" options="{'no_create': True}" /> + <field name="est_dans_comcom" /> + <field name="eligible" /> </tree> <form string="Porteurs de projets"> <sheet> <group> <group> - <field name="porteur_id"/> - <field name="lastname"/> - <field name="firstname"/> - <field name="commune"/> + <field name="porteur_id" /> + <field name="lastname" /> + <field name="firstname" /> + <field name="commune" /> </group> <group> - <field name="mobile"/> - <field name="fixe"/> - <field name="email" widget="email"/> - <field name="statut"/> - <field name="eligible"/> + <field name="mobile" /> + <field name="fixe" /> + <field name="email" widget="email" /> + <field name="statut" /> + <field name="eligible" /> </group> </group> </sheet> @@ -142,99 +177,271 @@ </field> <group> <group> - <field name="od_ids" options="{'no_create': True}" widget="many2many_tags"/> - <field name="animateur_ids" options="{'no_create': True}" widget="many2many_tags"/> - <field name="type_convention_id" options="{'limit': 10, 'no_create': True, 'no_open': True}" /> + <field + name="od_ids" + options="{'no_create': True}" + widget="many2many_tags" + /> + <field + name="animateur_ids" + options="{'no_create': True}" + widget="many2many_tags" + /> + <field + name="type_convention_id" + options="{'limit': 10, 'no_create': True, 'no_open': True}" + /> </group> <group> - <field name="date_ca"/> - <field name="date_demarrage"/> + <field name="date_ca" /> + <field name="date_demarrage" /> <field name="elu_id" options="{'no_create': True}" /> </group> </group> </xpath> <xpath expr="//notebook" position="inside"> - <page name="contact_first" string="1er contact" attrs="{'invisible': ['|', ('project_status', '=', %(adefpat_project.project_status_demande)d), ('project_status', '=', %(adefpat_project.project_status_instruction)d)]}"> - <group > - <field name="first_contact_id"/> - <field name="type_contact"/> - <field name="date_first_contact"/> - <field name="prescripteur_id"/> + <page + name="contact_first" + string="1er contact" + attrs="{'invisible': ['|', ('project_status', '=', %(adefpat_project.project_status_demande)d), ('project_status', '=', %(adefpat_project.project_status_instruction)d)]}" + > + <group> + <field name="first_contact_id" /> + <field name="type_contact" /> + <field name="date_first_contact" /> + <field name="prescripteur_id" /> </group> </page> <page name="dossier" string="Dossier"> <group string="Dossier Consultation"> <group> - <field name="contexte" placeholder="Les impacts du projet sur le territoire, la correspondance aux priorités, les bénéfices attendus de sa mise en œuvre"/> - <field name="caract_beneficiaire" placeholder="Nombre / Type : chef d’entreprise, créateur… / Age, niveau et type de formation, expérience professionnelle / Situation, commune nombre habitants / Etat civil, statut, résidence – en lien avec les critères du financeur"/> - <field name="historique" placeholder="La description de l’entreprise avec commentaires non diffusables en rouge si nécessaire. Les raisons amenant à mettre en œuvre le projet. Place du projet dans le projet de vie du PP"/> - <field name="besoins_beneficiaires" placeholder="Les apports de la formation développement dans la mise en œuvre du projet / Les dispositifs complémentaires à mobiliser et pour quels besoins"/> - <field name="objectifs_accompagnement" placeholder="Les activités que les bénéficiaires doivent être capables de réaliser à l’issue du dispositif de formation développement."/> + <field + name="contexte" + placeholder="Les impacts du projet sur le territoire, la correspondance aux priorités, les bénéfices attendus de sa mise en œuvre" + /> + <field + name="caract_beneficiaire" + placeholder="Nombre / Type : chef d’entreprise, créateur… / Age, niveau et type de formation, expérience professionnelle / Situation, commune nombre habitants / Etat civil, statut, résidence – en lien avec les critères du financeur" + /> + <field + name="historique" + placeholder="La description de l’entreprise avec commentaires non diffusables en rouge si nécessaire. Les raisons amenant à mettre en œuvre le projet. Place du projet dans le projet de vie du PP" + /> + <field + name="besoins_beneficiaires" + placeholder="Les apports de la formation développement dans la mise en œuvre du projet / Les dispositifs complémentaires à mobiliser et pour quels besoins" + /> + <field + name="objectifs_accompagnement" + placeholder="Les activités que les bénéficiaires doivent être capables de réaliser à l’issue du dispositif de formation développement." + /> </group> <group> - <field name="competences_requises"/> - <field name="secteurs_requis"/> - <field name="modalites_intervention"/> - <field name="modalites_facturation"/> - <field name="modalites_reponse"/> - <field name="modalites_modif_marche"/> + <field name="competences_requises" /> + <field name="secteurs_requis" /> + <field name="modalites_intervention" /> + <field name="modalites_facturation" /> + <field name="modalites_reponse" /> + <field name="modalites_modif_marche" /> </group> </group> - <group > - <group string="Critères CA"> - <field name="type_beneficiaire"/> - <field name="objectif_projet"/> - <field name="secteur_crit"/> - <field name="taille"/> - <field name="objectif_formation"/> - <field name="type_formation"/> - <field name="encadrement"/> - <field name="nb_activité"/> - <field name="nb_emplois"/> - </group> + <group> + <!-- Critères CA --> + <div class="row"> + <div class="col-12 col-xs-6"> + <separator string="Critères CA"/> + <div class="row"> + <div class="col-7 border-right"> + <label for="type_beneficiaire" style="font-weight: bold;"/> + </div> + <div class="col-5" style="padding-right: 48px;"><field + name="type_beneficiaire" + /></div> + </div> + <div class="row"> + <div class="col-7 border-right"> + <label for="objectif_projet" style="font-weight: bold;"/> + </div> + <div class="col-5" style="padding-right: 48px;"><field + name="objectif_projet" + /></div> + </div> + <div class="row"> + <div class="col-7 border-right"> + <label for="secteur_crit" style="font-weight: bold;"/> + </div> + <div class="col-5" style="padding-right: 48px;"><field + name="secteur_crit" + /></div> + </div> + <div class="row"> + <div class="col-7 border-right"> + <label for="taille" style="font-weight: bold;"/> + </div> + <div class="col-5" style="padding-right: 48px;"><field + name="taille" + /></div> + </div> + <div class="row"> + <div class="col-7 border-right"> + <label for="objectif_formation" style="font-weight: bold;"/> + </div> + <div class="col-5" style="padding-right: 48px;"><field + name="objectif_formation" + /></div> + </div> + <div class="row"> + <div class="col-7 border-right"> + <label for="type_formation" style="font-weight: bold;"/> + </div> + <div class="col-5" style="padding-right: 48px;"> + <field name="type_formation" /> + </div> + </div> + <div class="row"> + <div class="col-7 border-right"> + <label for="encadrement" style="font-weight: bold;"/> + </div> + <div class="col-5" style="padding-right: 48px;"> + <field name="encadrement" /></div> + </div> + <div class="row"> + <div class="col-7 border-right"> + <label for="bpi" style="font-weight: bold;"/> + </div> + <div class="col-5" style="padding-right: 48px;"> + <field name="bpi" /> + </div> + </div> + <div class="row"> + <div class="col-7 border-right"> + <label for="nb_emplois" style="font-weight: bold;"/> + </div> + <div class="col-5" style="padding-right: 48px;"> + <field name="nb_emplois" /> + </div> + </div> + <div class="row"> + <div class="col-7 border-right"> + <label for="type_accompagnement" style="font-weight: bold;" /> + </div> + <div class="col-5" style="padding-right: 48px;"> + <field name="type_accompagnement" /> + </div> + </div> + <div + class="row" + attrs="{'invisible': [('type_accompagnement', '!=', 'individualise')], 'required': [('type_accompagnement', '!=', 'individualise')]}" + > + <div class="col-7 border-right"> + <label for="nb_activité" style="font-weight: bold;" /> + </div> + <div class="col-5" style="padding-right: 48px;"> + <field name="nb_activité" /> + </div> + </div> + <div + class="row" + attrs="{'invisible': [('type_accompagnement', '!=', 'territoriaux')], 'required': [('type_accompagnement', '!=', 'territoriaux')]}" + > + <div class="col-7 border-right"> + <label for="nb_coll" style="font-weight: bold;" /> + </div> + <div class="col-5" style="padding-right: 48px;"> + <field name="nb_coll" /> + </div> + </div> + <div + class="row" + attrs="{'invisible': [('type_accompagnement', '!=', 'territoriaux')], 'required': [('type_accompagnement', '!=', 'territoriaux')]}" + > + <div class="col-7 border-right"> + <label for="nb_citoyens" style="font-weight: bold;" /> + </div> + <div class="col-5" style="padding-right: 48px;"> + <field name="nb_citoyens" /> + </div> + </div> + <div + class="row" + attrs="{'invisible': [('type_accompagnement', 'not in', ('interentreprises','territoriaux'))], 'required': [('type_accompagnement', 'not in', ('interentreprises','territoriaux'))]}" + > + <div class="col-7 border-right"> + <label for="nb_entreprises" style="font-weight: bold;" /> + </div> + <div class="col-5" style="padding-right: 48px;"> + <field name="nb_entreprises" /> + </div> + </div> + <div class="row"> + <div class="col-7 border-right"> + <label for="est_suite" style="font-weight: bold;" /> + </div> + <div class="col-5" style="padding-right: 48px;"> + <field name="est_suite" /> + </div> + </div> + </div> + </div> <group string="Infos Dossier CA"> - <field name="lieu"/> - <field name="periode_realisation"/> - <field name="contenu_formation" placeholder="Contenu des séances / Evaluation - capitalisation / La dernière séance permettra de dégager les acquis pédagogiques en s’appuyant sur les objectifs initiaux d’accompagnement : retour d’expérience / pointage des actions restant à conduire"/> - <field name="methode_savoir" placeholder="Méthodes et savoir transmis / Evaluation - capitalisation / La dernière séance permettra de dégager les acquis pédagogiques en s’appuyant sur les objectifs initiaux d’accompagnement : retour d’expérience / pointage des actions restant à conduire"/> - <field name="travaux_intersessions" placeholder="Travaux intersessions / Evaluation - capitalisation / La dernière séance permettra de dégager les acquis pédagogiques en s’appuyant sur les objectifs initiaux d’accompagnement : retour d’expérience / pointage des actions restant à conduire"/> + <field name="lieu" /> + <field name="periode_realisation" /> + <field + name="contenu_formation" + placeholder="Contenu des séances / Evaluation - capitalisation / La dernière séance permettra de dégager les acquis pédagogiques en s’appuyant sur les objectifs initiaux d’accompagnement : retour d’expérience / pointage des actions restant à conduire" + /> + <field + name="methode_savoir" + placeholder="Méthodes et savoir transmis / Evaluation - capitalisation / La dernière séance permettra de dégager les acquis pédagogiques en s’appuyant sur les objectifs initiaux d’accompagnement : retour d’expérience / pointage des actions restant à conduire" + /> + <field + name="travaux_intersessions" + placeholder="Travaux intersessions / Evaluation - capitalisation / La dernière séance permettra de dégager les acquis pédagogiques en s’appuyant sur les objectifs initiaux d’accompagnement : retour d’expérience / pointage des actions restant à conduire" + /> </group> </group> </page> <page name="consultation" string="Consultation"> <group> <group> - <field name="date_cdc"/> - <field name="date_selection"/> - <field name="date_notification"/> + <field name="date_cdc" /> + <field name="date_selection" /> + <field name="date_notification" /> </group> </group> - <field name="consulant_ids" widget="one2many" options="{'no_create': True}"> + <field + name="consulant_ids" + widget="one2many" + options="{'no_create': True}" + > <tree string="Consultants" editable="top"> - <field name="partner_id" options="{'no_create': True}"/> - <field name="lastname"/> - <field name="firstname"/> - <field name="reference"/> - <field name="mobile"/> - <field name="email" widget="email"/> - <field name="cout_journée"/> - <field name="is_selected"/> + <field + name="partner_id" + options="{'no_create': True}" + /> + <field name="lastname" /> + <field name="firstname" /> + <field name="reference" /> + <field name="mobile" /> + <field name="email" widget="email" /> + <field name="cout_journée" /> + <field name="is_selected" /> </tree> <form string="Consultants"> <sheet> <group> <group> - <field name="partner_id"/> - <field name="lastname"/> - <field name="firstname"/> - <field name="mobile"/> - <field name="email" widget="email"/> + <field name="partner_id" /> + <field name="lastname" /> + <field name="firstname" /> + <field name="mobile" /> + <field name="email" widget="email" /> </group> <group> - <field name="reference"/> - <field name="cout_journée"/> - <field name="is_selected"/> + <field name="reference" /> + <field name="cout_journée" /> + <field name="is_selected" /> </group> </group> </sheet> @@ -244,33 +451,37 @@ <page string="Budget" name="budget"> <group> <group string="Adefpat"> - <field name="nb_jours_adefpat"/> - <field name="cout_jour_adefpat"/> + <field name="nb_jours_adefpat" /> + <field name="cout_jour_adefpat" /> </group> <group string="Global (Avenants compris)"> - <field name="nb_jour_plann"/> - <field name="nb_jour_rest"/> + <field name="nb_jour_plann" /> + <field name="nb_jour_rest" /> </group> </group> - <group > - <field name="nb_jour_tot" invisible="1"/> - <field name="nb_heure_tot" invisible="1"/> - <field name="nb_jour_theorique_tot"/> + <group> + <field name="nb_jour_tot" invisible="1" /> + <field name="nb_heure_tot" invisible="1" /> + <field name="nb_jour_theorique_tot" /> </group> - <field name="cout_ids" widget="one2many" context="{'default_project_id': active_id}"> + <field + name="cout_ids" + widget="one2many" + context="{'default_project_id': active_id}" + > <tree string="Coûts" editable="top"> - <field name="module"/> - <field name="partner_id"/> - <field name="nb_jour_theorique"/> - <field name="nb_jour_pratiques"/> - <field name="cout_jour"/> - <field name="montant" sum="Montant"/> + <field name="module" /> + <field name="partner_id" /> + <field name="nb_jour_theorique" /> + <field name="nb_jour_pratiques" /> + <field name="cout_jour" /> + <field name="montant" sum="Montant" /> </tree> </field> <field name="financement_ids" widget="one2many"> <tree string="Financements" editable="top"> - <field name="partner_id"/> - <field name="montant" sum="Montant"/> + <field name="partner_id" /> + <field name="montant" sum="Montant" /> </tree> </field> <group> @@ -283,29 +494,50 @@ </thead> <tbody> <tr> - <td > + <td> <tr> <td> Honoraire d'intervenant : - <field name="cout_ids" widget="one2many" context="{'default_project_id': active_id}"> - <tree string="Coûts" editable="top"> - <field name="module"/> - <field name="partner_id"/> - <field name="nb_jour_theorique"/> - <field name="nb_jour_pratiques"/> - <field name="cout_jour"/> - <field name="montant" sum="Montant"/> + <field + name="cout_ids" + widget="one2many" + context="{'default_project_id': active_id}" + > + <tree + string="Coûts" + editable="top" + > + <field name="module" /> + <field name="partner_id" /> + <field + name="nb_jour_theorique" + /> + <field + name="nb_jour_pratiques" + /> + <field name="cout_jour" /> + <field + name="montant" + sum="Montant" + /> </tree> </field> </td> - <td><field name="total_cout" widget="monetary" readonly="True"/> €</td> + <td><field + name="total_cout" + widget="monetary" + readonly="True" + /> €</td> </tr> <tr> <td> - Temps Adefpat: <br/> + Temps Adefpat: <br /> </td> <td> - <field name="total_cout_adefpat" readonly="True"/> € + <field + name="total_cout_adefpat" + readonly="True" + /> € </td> </tr> </td> @@ -316,7 +548,11 @@ Adefpat: </td> <td> - <field name="financement_adefpat" widget="monetary" readonly="True"/> € + <field + name="financement_adefpat" + widget="monetary" + readonly="True" + /> € </td> </tr> <tr> @@ -324,7 +560,11 @@ Autres: </td> <td> - <field name="total_financement" widget="monetary" readonly="True"/> € + <field + name="total_financement" + widget="monetary" + readonly="True" + /> € </td> </tr> </td> @@ -333,7 +573,11 @@ <td> <tr> <td>Budget Total: </td> - <td><field name="total_budget_cout" widget="monetary" readonly="True"/> € + <td><field + name="total_budget_cout" + widget="monetary" + readonly="True" + /> € </td> </tr> </td> @@ -341,7 +585,11 @@ <td> <tr> <td>Budget Total:</td> - <td><field name="total_budget_financement" widget="monetary" readonly="True"/> €</td> + <td><field + name="total_budget_financement" + widget="monetary" + readonly="True" + /> €</td> </tr> </td> </tr> @@ -349,68 +597,442 @@ </table> </group> <group> - <field name="explication_financement"/> + <field name="explication_financement" /> </group> </page> <page name="gap" string="GAP"> - <group > + <group> <group> - <field name="elu_referent_id"/> + <field name="elu_referent_id" /> </group> <group> - <field name="modalite_gap"/> + <field name="modalite_gap" /> </group> </group> <field name="membre_ids" widget="one2many"> <tree string="Membres" editable="top"> - <field name="partner_id"/> - <field name="lastname"/> - <field name="firstname"/> - <field name="commune"/> - <field name="mobile"/> - <field name="fixe"/> - <field name="email" widget="email"/> + <field name="partner_id" /> + <field name="lastname" /> + <field name="firstname" /> + <field name="commune" /> + <field name="mobile" /> + <field name="fixe" /> + <field name="email" widget="email" /> </tree> <form string="Membres"> <sheet> <group> <group> - <field name="partner_id"/> - <field name="lastname"/> - <field name="firstname"/> + <field name="partner_id" /> + <field name="lastname" /> + <field name="firstname" /> </group> <group> - <field name="commune"/> - <field name="mobile"/> - <field name="fixe"/> - <field name="email" widget="email"/> + <field name="commune" /> + <field name="mobile" /> + <field name="fixe" /> + <field name="email" widget="email" /> </group> </group> </sheet> </form> </field> - <field name="reunion_ids" widget="one2many"> - <tree string="Réunions GAP" editable="top"> - <field name="date"/> + <field name="reunion_ids" widget="one2many" context="{'default_project_id': active_id}"> + <tree string="Réunions GAP"> + <field name="date" /> + <field name="number_participants" readonly="True" /> </tree> + <form string="Réunion GAP"> + <sheet> + <group class="row"> + <field name="date"/> + </group> + <label for="participant_ids" /> + <field name="participant_ids" widget="one2many"> + <tree string="Participants" editable="top"> + <field name="member_id" /> + <field name="present" /> + </tree> + </field> + </sheet> + </form> </field> </page> - + <page name="results" string="Résultats"> + <div class="row"> + <!-- Left group --> + <div class="col-12 col-md-6" name="results_common"> + <div class="row"> + <div class="col-md-8 border-right"><label + for="closing_date" + /></div> + <div class="col-md-4"><field + name="closing_date" + /></div> + </div> + <div class="row"> + <div class="col-md-8 border-right"><label + for="on_hold" + /></div> + <div class="col-md-4"><field name="on_hold" /></div> + </div> + <div class="row"> + <div class="col-md-8 border-right"><label + for="number_gap_members" + /></div> + <div class="col-md-4"><field + name="number_gap_members" + readonly="1" + /></div> + </div> + <div class="row"> + <div class="col-md-8 border-right"><label + for="number_proj_att" + /></div> + <div class="col-md-4"><field + name="number_proj_att" + /></div> + </div> + <div class="row"> + <div class="col-md-8 border-right"><label + for="number_proj_inn_idea" + /></div> + <div class="col-md-4"><field + name="number_proj_inn_idea" + /></div> + </div> + <div class="row"> + <div class="col-md-8 border-right"><label + for="number_proj_inn_terr" + /></div> + <div class="col-md-4"><field + name="number_proj_inn_terr" + /></div> + </div> + <div class="row"> + <div class="col-md-8 border-right"><label + for="number_proj_inn_adap" + /></div> + <div class="col-md-4"><field + name="number_proj_inn_adap" + /></div> + </div> + <div class="row"> + <div class="col-md-8 border-right"><label + for="number_proj_dev" + /></div> + <div class="col-md-4"><field + name="number_proj_dev" + /></div> + </div> + <div class="row"> + <div class="col-md-8 border-right"><label + for="number_proj_ind_coll" + /></div> + <div class="col-md-4"><field + name="number_proj_ind_coll" + /></div> + </div> + <div class="row"> + <div class="col-md-8 border-right"><label + for="is_service_design" + /></div> + <div class="col-md-4"><field + name="is_service_design" + /></div> + </div> + <div class="row"> + <div class="col-md-8 border-right"><label + for="is_social_impact" + /></div> + <div class="col-md-4"><field + name="is_social_impact" + /></div> + </div> + <div class="row"> + <div class="col-md-8 border-right"><label + for="synthesis_website" + /></div> + <div class="col-md-4"><field + name="synthesis_website" + /></div> + </div> + </div> + <!-- Right group --> + <div + class="col-12 col-md-6" + name="results_indiv" + attrs="{'invisible': [('type_accompagnement', '!=', 'individualise')]}" + > + <div class="row"> + <div class="col-md-8 border-right"><label + for="number_comp_idea_projet" + /></div> + <div class="col-md-4"><field + name="number_comp_idea_projet" + /></div> + </div> + <div class="row"> + <div class="col-md-8 border-right"><label + for="number_comp_creation" + /></div> + <div class="col-md-4"><field + name="number_comp_creation" + readonly="1" + /></div> + </div> + <div class="row"> + <div class="col-md-8 border-right"><label + for="number_comp_created" + /></div> + <div class="col-md-4"><field + name="number_comp_created" + /></div> + </div> + <div class="row"> + <div class="col-md-8 border-right"><label + for="number_comp_consolidated" + /></div> + <div class="col-md-4"><field + name="number_comp_consolidated" + /></div> + </div> + <div class="row"> + <div class="col-md-8 border-right"><label + for="number_emp_created" + /></div> + <div class="col-md-4"><field + name="number_emp_created" + /></div> + </div> + <div class="row"> + <div class="col-md-8 border-right"><label + for="number_emp_consolidated" + /></div> + <div class="col-md-4"><field + name="number_emp_consolidated" + /></div> + </div> + </div> + <div + class="col-12 col-md-6" + name="results_inter" + attrs="{'invisible': [('type_accompagnement', '!=', 'interentreprises')]}" + > + <div class="row"> + <div class="col-md-8 border-right"><label + for="number_ac_created" + /></div> + <div class="col-md-4"><field + name="number_ac_created" + /></div> + </div> + <div class="row"> + <div class="col-md-8 border-right"><label + for="number_emp_created_coll" + /></div> + <div class="col-md-4"><field + name="number_emp_created_coll" + /></div> + </div> + <div class="row"> + <div class="col-md-8 border-right"><label + for="number_emp_consolidated_coll" + /></div> + <div class="col-md-4"><field + name="number_emp_consolidated_coll" + /></div> + </div> + <div class="row"> + <div class="col-md-8 border-right"><label + for="number_comp_created_coll" + /></div> + <div class="col-md-4"><field + name="number_comp_created_coll" + /></div> + </div> + </div> + <div + class="col-12 col-md-6" + name="results_terr" + attrs="{'invisible': [('type_accompagnement', '!=', 'territoriaux')]}" + > + <div class="row"> + <div class="col-md-8 border-right"><label + for="number_struct_proj" + /></div> + <div class="col-md-4"><field + name="number_struct_proj" + /></div> + </div> + <div class="row"> + <div class="col-md-8 border-right"><label + for="number_mean_people_inv" + /></div> + <div class="col-md-4"><field + name="number_mean_people_inv" + /></div> + </div> + <div class="row"> + <div class="col-md-8 border-right"><label + for="number_comp_created_at" + /></div> + <div class="col-md-4"><field + name="number_comp_created_at" + /></div> + </div> + </div> + </div> + </page> </xpath> <div class="oe_chatter" position="inside"> - <field name="message_ids" widget="mail_thread"/> + <field name="message_ids" widget="mail_thread" /> </div> </field> </record> + <!-- Rapport Activité - Projets --> + <record id="view_adefpat_project_reporting_tree" model="ir.ui.view"> + <field name="name">view.adefpat.project.reporting.tree</field> + <field name="model">project.project</field> + <field name="arch" type="xml"> + <tree + string="Rapport d'activité projets" + create="0" + edit="0" + delete="0" + > + <field name="num_dossier" string="Numéro du dossier" /> + <field name="name" string="Nom du dossier" /> + <field name="od_ids" widget="many2many_tags" /> + <field name="departement" /> + <field name="type_convention_id" /> + <field + name="financement_adefpat" + string="budget - facturation" + /> + <field name="objectif_formation" /> + <field name="bpi" /> + <field name="type_accompagnement" /> + <field + name="nb_activité" + string="Nombre d'entreprises accompagnées" + /> + <field name="nb_coll" /> + <field name="nb_citoyens" /> + <field name="nb_entreprises" /> + <field name="est_suite" /> + <field name="number_day_period" /> + </tree> + </field> + </record> + + <record id="view_adefpat_new_project_reporting_tree" model="ir.ui.view"> + <field name="name">view.adefpat.new.project.reporting.tree</field> + <field name="model">project.project</field> + <field name="arch" type="xml"> + <tree + string="Nouveaux projets accompagnés" + create="0" + edit="0" + delete="0" + > + <field name="name" string="Nom du dossier" /> + </tree> + </field> + </record> + + <record id="action_view_adefpat_new_project" model="ir.actions.act_window"> + <field name="name">Nouveaux Projets par année</field> + <field name="res_model">project.project</field> + <field name="view_mode">tree</field> + <field name="view_ids" eval="[(5, 0, 0), + (0, 0, {'view_mode': 'tree', 'view_id': ref('view_adefpat_new_project_reporting_tree')})]"/> + <field name="context">{'group_by': 'date_ca:year'}</field> + </record> + + <!-- Rapport Porteurs de Projets --> + <record id="view_adefpat_participant_reporting_tree" model="ir.ui.view"> + <field name="name">view.adefpat.participant.reporting.tree</field> + <field name="model">res.partner.porteur.project</field> + <field name="arch" type="xml"> + <tree string="Bénéficiaires accompagnés" create="0" edit="0" delete="0"> + <field name="project_num" /> + <field name="project_id" string="Nom du dossier" /> + <field name="project_secteur" /> + <field name="lastname" string="Nom du bénéficiaire" /> + <field name="firstname" string="Prénom du bénéficiaire" /> + <field name="sexe" /> + <field name="commune" string="Commune de résidence" /> + <field name="est_dans_comcom" /> + <field name="date_naissance" /> + <field name="statut" /> + <field name="number_hour_period" /> + </tree> + </field> + </record> + + <!-- Rapport Consultants Formateurs - Projets --> + <record id="view_adefpat_cfd_reporting_tree" model="ir.ui.view"> + <field name="name">view.adefpat.cfd.reporting.tree</field> + <field name="model">project.project</field> + <field name="arch" type="xml"> + <tree string="Projets par CFD" create="0" edit="0" delete="0"> + <field name="num_dossier" string="Numéro du dossier" /> + <field name="name" string="Nom du dossier" /> + <field name="user_id" string="Nom du CF" /> + <field name="number_hour_period" /> + </tree> + </field> + </record> + + <!-- Rapport Résultats - Projets --> + <record id="view_adefpat_project_result_tree" model="ir.ui.view"> + <field name="name">view.adefpat.result.reporting.tree</field> + <field name="model">project.project</field> + <field name="arch" type="xml"> + <tree string="Résultats sur projets" create="0" edit="0" delete="0"> + <field name="num_dossier" string="Numéro du dossier" /> + <field name="name" string="Nom du dossier" /> + <field name="type_convention_id" /> + <field name="type_accompagnement" /> + <field name="closing_date" /> + <field name="on_hold" /> + <field name="number_gap_members" readonly="1" /> + <field name="number_proj_att" /> + <field name="number_proj_inn_idea" /> + <field name="number_proj_inn_terr" /> + <field name="number_proj_inn_adap" /> + <field name="number_proj_dev" /> + <field name="number_proj_ind_coll" /> + <field name="is_service_design" /> + <field name="is_social_impact" /> + <field name="synthesis_website" /> + <field name="number_comp_idea_projet" /> + <field name="number_comp_creation" /> + <field name="number_comp_created" /> + <field name="number_comp_consolidated" /> + <field name="number_emp_created" /> + <field name="number_emp_consolidated" /> + <field name="number_ac_created" /> + <field name="number_emp_created_coll" /> + <field name="number_emp_consolidated_coll" /> + <field name="number_comp_created_coll" /> + <field name="number_struct_proj" /> + <field name="number_mean_people_inv" /> + <field name="number_comp_created_at" /> + </tree> + </field> + </record> <record id="view_adefpat_type_convention_tree" model="ir.ui.view"> <field name="name">view.adefpat.type.convention.tree</field> <field name="model">adefpat.type.convention</field> <field name="arch" type="xml"> <tree string="Types de conventions"> - <field name="name"/> - <field name="date_end_validity"/> + <field name="name" /> + <field name="date_end_validity" /> + <field name="is_fd" /> </tree> </field> </record> @@ -422,11 +1044,20 @@ <field name="view_mode">tree,form</field> </record> - <menuitem - id="adefpat_project.menu_type_convention" + <menuitem + id="adefpat_project.menu_new_project_report" + name="Nouveaux projets par année" + parent="project.menu_project_report" + action="action_view_adefpat_new_project" + sequence="30" + /> + + <menuitem + id="adefpat_project.menu_type_convention" name="Types de convention" - parent="project.menu_project_config" - action="action_view_adefpat_type_convention"/> + parent="project.menu_project_config" + action="action_view_adefpat_type_convention" + /> </data> </odoo> diff --git a/views/res_config_settings_views.xml b/views/res_config_settings_views.xml index 7172bb45a37f7bc12903d868c253ff9fc3a97f47..5a7a7e22cb3e167e436de8add313b37c07f3c161 100644 --- a/views/res_config_settings_views.xml +++ b/views/res_config_settings_views.xml @@ -1,70 +1,87 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8" ?> +<!-- Copyright 2020-2022 Le Filament + License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> <odoo> <record id="res_config_settings_view_form" model="ir.ui.view"> <field name="name">res.config.settings.view.form.inherit.sale.legicoop</field> <field name="model">res.config.settings</field> - <field name="priority" eval="10"/> + <field name="priority" eval="10" /> <field name="inherit_id" ref="project.res_config_settings_view_form" /> <field name="arch" type="xml"> - <xpath expr="//div[@name='project_time']" position="after"> - <h2>Projet Adefpat Spécifique</h2> + <xpath expr="//div[@name='project_time']" position="after"> + <h2>Projet Adefpat Spécifique</h2> <div name="adefpat_project" class="row mt16 o_settings_container"> - <div class="col-12 col-lg-6 o_setting_box" > - <div > - <label for="explication_financement"/> + <div class="col-12 col-lg-6 o_setting_box"> + <div> + <label for="explication_financement" /> <div class="text-muted"> Texte pour le bloc Prestations </div> - <div class="content-group" > + <div class="content-group"> <div class="mt16"> - <field name="explication_financement" placeholder="Explication financement..."/> + <field + name="explication_financement" + placeholder="Explication financement..." + /> </div> </div> </div> </div> - <div class="col-12 col-lg-6 o_setting_box" > - <div > - <label for="modalites_intervention"/> + <div class="col-12 col-lg-6 o_setting_box"> + <div> + <label for="modalites_intervention" /> <div class="text-muted"> Texte pour les modalités d'intervention </div> - <div class="content-group" > + <div class="content-group"> <div class="mt16"> - <field name="modalites_intervention" placeholder="Modalités d'intervention..."/> + <field + name="modalites_intervention" + placeholder="Modalités d'intervention..." + /> </div> </div> </div> - <div > - <label for="modalites_facturation"/> + <div> + <label for="modalites_facturation" /> <div class="text-muted"> Texte pour les modalités de facturation </div> - <div class="content-group" > + <div class="content-group"> <div class="mt16"> - <field name="modalites_facturation" placeholder="Modalités de facturation..."/> + <field + name="modalites_facturation" + placeholder="Modalités de facturation..." + /> </div> </div> </div> - <div > - <label for="modalites_reponse"/> + <div> + <label for="modalites_reponse" /> <div class="text-muted"> Texte pour les modalités de réponse </div> - <div class="content-group" > + <div class="content-group"> <div class="mt16"> - <field name="modalites_reponse" placeholder="Modalités de réponse..."/> + <field + name="modalites_reponse" + placeholder="Modalités de réponse..." + /> </div> </div> </div> - <div > - <label for="modalites_modif_marche"/> + <div> + <label for="modalites_modif_marche" /> <div class="text-muted"> Texte pour les modalités de modification de marché </div> - <div class="content-group" > + <div class="content-group"> <div class="mt16"> - <field name="modalites_modif_marche" placeholder="Modalités de modification de marché..."/> + <field + name="modalites_modif_marche" + placeholder="Modalités de modification de marché..." + /> </div> </div> </div> @@ -74,4 +91,4 @@ </field> </record> -</odoo> \ No newline at end of file +</odoo> diff --git a/views/res_partner_views.xml b/views/res_partner_views.xml index cfcc0e687151a313b3963def6fd61efd4641642e..324c4ccb6f8bda4b7a24f00e46d0317b96e76869 100644 --- a/views/res_partner_views.xml +++ b/views/res_partner_views.xml @@ -1,19 +1,21 @@ -<?xml version="1.0"?> -<!-- Copyright 2020 Le Filament +<?xml version="1.0" ?> +<!-- Copyright 2020-2022 Le Filament License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> - <odoo> <data> <record id="view_res_partner_filter" model="ir.ui.view"> <field name="name">res.partner.search</field> <field name="model">res.partner</field> - <field name="inherit_id" ref="base.view_res_partner_filter"/> + <field name="inherit_id" ref="base.view_res_partner_filter" /> <field name="arch" type="xml"> <xpath expr="//filter[@name='type_company']" position="after"> - <separator/> - <filter string="Porteurs de projets" name="filt_porteurs" - domain="[('project_count', '>', 0)]"/> + <separator /> + <filter + string="Porteurs de projets" + name="filt_porteurs" + domain="[('project_count', '>', 0)]" + /> </xpath> </field> </record> @@ -21,15 +23,24 @@ <record id="view_partner_form" model="ir.ui.view"> <field name="name">view.partner.form.inherit</field> <field name="model">res.partner</field> - <field name="inherit_id" ref="base.view_partner_form"/> + <field name="inherit_id" ref="base.view_partner_form" /> <field name="arch" type="xml"> <button name="toggle_active" position="before"> - <button class="oe_stat_button" type="object" name="action_view_partner_projects" - icon="fa-puzzle-piece" attrs="{'invisible': [('project_count', '=', 0)]}"> - <field string="Projects" name="project_count" widget="statinfo"/> + <button + class="oe_stat_button" + type="object" + name="action_view_partner_projects" + icon="fa-puzzle-piece" + attrs="{'invisible': [('project_count', '=', 0)]}" + > + <field + string="Projects" + name="project_count" + widget="statinfo" + /> </button> </button> </field> </record> </data> -</odoo> \ No newline at end of file +</odoo> diff --git a/wizard/__init__.py b/wizard/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..1d5a937bbcf3564fd2e9732abf8946666de135dd --- /dev/null +++ b/wizard/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2020-2022 Le Filament (<http://www.le-filament.com>) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import adefpat_project_report_wizard diff --git a/wizard/adefpat_project_report_wizard.py b/wizard/adefpat_project_report_wizard.py new file mode 100644 index 0000000000000000000000000000000000000000..444472229e658c0cc4876944c2a5696cff790218 --- /dev/null +++ b/wizard/adefpat_project_report_wizard.py @@ -0,0 +1,100 @@ +# Copyright 2022 Le Filament (<http://www.le-filament.com>) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models + + +class AdefpatProjectReport(models.TransientModel): + _name = "adefpat.project.report.wizard" + _description = "Wizard rapports projets" + + period_start = fields.Date("Début de période", required=True) + period_end = fields.Date( + "Fin de période", required=True, default=fields.Date.today() + ) + export_type = fields.Selection( + [ + ("activity", "Activité"), + ("benef", "Bénéficiaires"), + ("cf", "Consultants Formateurs"), + ("results", "Résultats"), + ], + string="Type de rapport", + required=True, + default="activity", + ) + is_fd = fields.Boolean("Convention FD uniquement", default=True) + is_closed = fields.Boolean("Projet clôturé sur la période uniquement", default=True) + + @api.onchange("export_type") + def onchange_bools(self): + # Default values depending of export_type + if self.export_type in ["benef", "cf"]: + self.is_closed = False + else: + self.is_closed = True + + # function will trigger from the show report button + @api.multi + def show_report(self): + # Get Projects with at least 1 task on period + project_ids = self.env["project.task"].search([ + ("date_deadline", "<=", self.period_end), + ("date_deadline", ">=", self.period_start), + ]).mapped("project_id") + + if self.is_fd: + project_ids = project_ids.filtered(lambda p: p.type_convention_id.is_fd) + if self.is_closed: + project_ids = project_ids.filtered( + lambda p: p.closing_date and p.closing_date <= self.period_end + and p.closing_date >= self.period_start) + + # Select view + res_model = "project.project" + domain = [("id", "in", project_ids.ids)] + limit = len(project_ids) + # Rapport Activité + if self.export_type == "activity": + tree_view = self.env.ref( + "adefpat_project.view_adefpat_project_reporting_tree" + ) + # Rapport Porteurs de projets + elif self.export_type == "benef": + tree_view = self.env.ref( + "adefpat_project.view_adefpat_participant_reporting_tree" + ) + res_model = "res.partner.porteur.project" + domain = [("project_id", "in", project_ids.ids)] + limit = self.env["res.partner.porteur.project"].search_count(domain) + # Rapport Formateurs + elif self.export_type == "cf": + tree_view = self.env.ref("adefpat_project.view_adefpat_formateur_reporting_tree") + res_model = "adefpat.formateur.project.report" + domain = [("project_id", "in", project_ids.ids)] + limit = self.env["adefpat.formateur.project.report"].search_count(domain) + elif self.export_type == "results": + tree_view = self.env.ref("adefpat_project.view_adefpat_project_result_tree") + + action_name = "Rapport %s | Du %s au %s" % ( + dict(self._fields["export_type"].selection).get(self.export_type), + fields.Date.to_string(self.period_start), + fields.Date.to_string(self.period_end), + ) + + return { + "type": "ir.actions.act_window", + "name": action_name, + "res_model": res_model, + "view_mode": "tree", + "views": [(tree_view.id, "tree")], + "target": "main", + "domain": domain, + "context": { + "period_start": self.period_start, + "period_end": self.period_end, + "is_fd": self.is_fd, + "is_closed": self.is_closed + }, + "limit": limit, + } diff --git a/wizard/adefpat_project_report_wizard.xml b/wizard/adefpat_project_report_wizard.xml new file mode 100644 index 0000000000000000000000000000000000000000..e3b23c032bf2591b221e41746b79547fc7349161 --- /dev/null +++ b/wizard/adefpat_project_report_wizard.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!-- Copyright 2022 Le Filament + License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> +<odoo> + <record id="adefpat_project_report_wizard_form" model="ir.ui.view"> + <field name="name">adefpat.project.report.wizard.form</field> + <field name="model">adefpat.project.report.wizard</field> + <field name="arch" type="xml"> + <form string="Génération du Rapport d'Activité"> + <group> + <group> + <field name="export_type" widget="radio" /> + </group> + <group> + <field name="period_start" /> + <field name="period_end" /> + <field name="is_fd" /> + <field name="is_closed" /> + </group> + </group> + <footer> + <button + class="btn btn-sm btn-primary" + name="show_report" + string="Générer le rapport" + type="object" + /> + <button + class="btn btn-sm btn-default" + special="cancel" + string="Fermer" + /> + </footer> + </form> + </field> + </record> + + <record id="adefpat_project_report_action" model="ir.actions.act_window"> + <field name="name">Rapport d'Activité</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">adefpat.project.report.wizard</field> + <field name="view_id" ref="adefpat_project_report_wizard_form" /> + <field name="target">new</field> + </record> + + <menuitem + id="adefpat_project.menu_activity_report" + name="Rapport d'Activité" + parent="project.menu_project_report" + action="adefpat_project_report_action" + sequence="20" + /> +</odoo> diff --git a/wizard/res_partner_membres_wizard.py b/wizard/res_partner_membres_wizard.py deleted file mode 100644 index dc69f41dd37d09fcbc2828f22da4f6a3d60c4d2d..0000000000000000000000000000000000000000 --- a/wizard/res_partner_membres_wizard.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright 2020 Le Filament (<http://www.le-filament.com>) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - -from odoo import models, fields, api - -import base64 - - -class ExportWizard(models.TransientModel): - _name = 'res.partner.membres.wizard' - - csv_data = fields.Binary() - filename = fields.Char() - - # function will trigger from the export button in customer export - @api.multi - def export_customers(self): - # call the function generate csv files - data = self.env['project.project'].export_gap() - self.csv_data = base64.encodestring(data) - self.filename = 'export.csv' - return { - 'type': 'ir.actions.act_url', - 'url': "web/content/?model=res.partner.membres.wizard&id=" + str(self.id) + - "&filename=export.csv&field=csv_data&download=true&filename=" + self.filename, - 'target': 'self', - } \ No newline at end of file