From 2fc0988c1fb9aa0c736179da0b2f62d6aabfb14f Mon Sep 17 00:00:00 2001 From: benjamin <benjamin@le-filament.com> Date: Mon, 4 Apr 2022 20:27:30 +0200 Subject: [PATCH] [mig] Migration to 14.0 + add pre-commit changes --- .editorconfig | 20 + .eslintrc.yml | 187 ++++ .flake8 | 12 + .gitignore | 78 +- .isort.cfg | 13 + .pre-commit-config.yaml | 127 +++ .prettierrc.yml | 8 + .pylintrc | 87 ++ .pylintrc-mandatory | 64 ++ README.rst | 0 __init__.py | 5 +- __manifest__.py | 12 +- controllers/main.py | 263 +++-- controllers/union_sociale.py | 24 +- .../bordereau_refund_wizard_quarter_data.xml | 30 +- datas/ir_sequence_data.xml | 4 +- datas/mail_data.xml | 27 +- datas/queue_job_data.xml | 29 +- migration/14.0.1.0.0/post-migration.py | 40 + migration/14.0.1.0.0/pre-migration.py | 14 + models/__init__.py | 2 +- models/account_invoice.py | 47 - models/account_move.py | 51 + models/res_company.py | 72 +- models/res_config_settings.py | 113 +- models/scop_bordereau_cg.py | 962 ++++++++++-------- models/scop_bordereau_cg_version.py | 82 +- models/scop_cotisation_cg.py | 730 +++++++------ models/scop_cotisation_simulation.py | 34 +- models/scop_liasse_fiscale.py | 272 ++--- report/__init__.py | 0 report/scop_contribution_report.py | 71 +- report/scop_contribution_report.xml | 25 +- security/ir.model.access.csv | 8 + security/security_rules.xml | 89 +- static/description/icon.png | Bin 9161 -> 15890 bytes templates/report_scop_bordereau.xml | 242 +++-- templates/report_scop_bordereau_payments.xml | 211 +++- templates/report_scop_bordereau_refund.xml | 129 ++- templates/report_union_sociale.xml | 153 ++- views/account_invoice.xml | 95 -- views/account_move.xml | 141 +++ views/menus.xml | 57 +- views/res_config_settings.xml | 162 ++- views/scop_bordereau_cg.xml | 547 +++++++--- views/scop_cotisation_cg.xml | 210 ++-- views/scop_cotisation_simulation.xml | 55 +- views/scop_liasse_fiscale.xml | 41 +- wizard/__init__.py | 2 +- wizard/account_invoice_refund.py | 24 - wizard/account_move_reversal.py | 25 + wizard/export_journal_wizard.py | 67 +- wizard/export_journal_wizard_view.xml | 74 +- wizard/scop_bordereau_payment_mode_wizard.py | 94 +- wizard/scop_bordereau_payment_mode_wizard.xml | 35 +- wizard/scop_bordereau_refund_wizard.py | 240 +++-- wizard/scop_bordereau_refund_wizard.xml | 35 +- wizard/scop_bordereau_update_confirm.py | 19 +- wizard/scop_bordereau_update_confirm_view.xml | 30 +- wizard/scop_bordereau_update_liasse_wizard.py | 160 +-- .../scop_bordereau_update_liasse_wizard.xml | 125 ++- wizard/scop_bordereau_validate_confirm.py | 61 +- .../scop_bordereau_validate_confirm_view.xml | 35 +- wizard/scop_cotisation_cg_regul.py | 332 +++--- wizard/scop_cotisation_cg_regul_wizard.xml | 134 ++- wizard/scop_cotisation_cg_wizard.py | 139 +-- wizard/scop_cotisation_cg_wizard.xml | 35 +- 67 files changed, 4713 insertions(+), 2598 deletions(-) create mode 100644 .editorconfig create mode 100644 .eslintrc.yml create mode 100644 .flake8 create mode 100644 .isort.cfg create mode 100644 .pre-commit-config.yaml create mode 100644 .prettierrc.yml create mode 100644 .pylintrc create mode 100644 .pylintrc-mandatory mode change 100755 => 100644 README.rst mode change 100755 => 100644 __init__.py mode change 100755 => 100644 __manifest__.py create mode 100644 migration/14.0.1.0.0/post-migration.py create mode 100644 migration/14.0.1.0.0/pre-migration.py mode change 100755 => 100644 models/__init__.py delete mode 100644 models/account_invoice.py create mode 100644 models/account_move.py mode change 100755 => 100644 report/__init__.py mode change 100755 => 100644 security/ir.model.access.csv mode change 100755 => 100644 static/description/icon.png delete mode 100644 views/account_invoice.xml create mode 100644 views/account_move.xml delete mode 100644 wizard/account_invoice_refund.py create mode 100644 wizard/account_move_reversal.py mode change 100755 => 100644 wizard/export_journal_wizard.py mode change 100755 => 100644 wizard/export_journal_wizard_view.xml diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..bfd7ac5 --- /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 0000000..d4cc423 --- /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 0000000..e397e8e --- /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 index 75bb204..818770f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,75 @@ -.* -*.pyc -!.gitignore +# 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 0000000..0ec187e --- /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 0000000..8a5999a --- /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 0000000..5b6d4b3 --- /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 0000000..dc6270e --- /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=14.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 0000000..43ea239 --- /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=14.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 old mode 100755 new mode 100644 diff --git a/__init__.py b/__init__.py old mode 100755 new mode 100644 index f88059b..5046b26 --- a/__init__.py +++ b/__init__.py @@ -1,7 +1,4 @@ # © 2022 Le Filament (<http://www.le-filament.com>) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from . import controllers -from . import models -from . import report -from . import wizard +from . import controllers, models, report, wizard diff --git a/__manifest__.py b/__manifest__.py old mode 100755 new mode 100644 index b8d7e6d..64b1d36 --- a/__manifest__.py +++ b/__manifest__.py @@ -3,7 +3,7 @@ { "name": "CG SCOP - Cotisations CG", "summary": "CG SCOP - Cotisations CG Scop", - "version": "12.1.1.0", + "version": "14.0.1.0.0", "author": "Le Filament", "license": "AGPL-3", "application": False, @@ -14,6 +14,7 @@ "lefilament_export_journal_tool", "cgscop_cotisation", "queue_job_batch", + "web_progress", ], "data": [ # Security @@ -30,7 +31,7 @@ "templates/report_scop_bordereau_refund.xml", "templates/report_union_sociale.xml", # Views - "views/account_invoice.xml", + "views/account_move.xml", "views/res_config_settings.xml", "views/scop_cotisation_cg.xml", "views/scop_cotisation_simulation.xml", @@ -50,5 +51,10 @@ "views/scop_bordereau_cg.xml", # Menus "views/menus.xml", - ] + ], + "external_dependencies": { + "python": [ + "unidecode", + ], + }, } diff --git a/controllers/main.py b/controllers/main.py index 4165650..8c6b946 100644 --- a/controllers/main.py +++ b/controllers/main.py @@ -2,27 +2,34 @@ # License AGPL-3 or later (http://www.gnu.org/licenses/agpl.html). import csv -import unidecode from datetime import date, datetime from io import StringIO +import unidecode + from odoo import http from odoo.http import request -from odoo.addons.web.controllers.main import serialize_exception -from odoo.addons.web.controllers.main import content_disposition + +from odoo.addons.web.controllers.main import content_disposition, serialize_exception class ExportJournalCg(http.Controller): # ------------------------------------------------------ # Routes # ------------------------------------------------------ - @http.route('/web/export_journal_cg/', type='http', auth="user") + @http.route("/web/export_journal_cg/", type="http", auth="user") @serialize_exception def export_journal_cg( - self, company_id, - date_start=False, date_end=False, - date_creation_start=False, date_creation_end=False, - partner_ids=False, export_type='empty', **kwargs): + self, + company_id, + date_start=False, + date_end=False, + date_creation_start=False, + date_creation_end=False, + partner_ids=False, + export_type="empty", + **kwargs + ): """ Sélectionne les account.move.line correspondants aux journaux et à la plage de date définis @@ -38,37 +45,50 @@ class ExportJournalCg(http.Controller): :return: file """ # Get accounts variables - company_id = request.env['res.company'].browse(int(company_id)) - default_receivable_account_id = request.env.ref('l10n_fr.1_fr_pcg_recv') + company_id = request.env["res.company"].browse(int(company_id)) + default_receivable_account_id = request.env.ref("l10n_fr.1_fr_pcg_recv") contribution_cg_id = company_id.contribution_cg_id - contribution_ur_or_fede_journal_id = company_id.contribution_ur_or_fede_journal_id + + # Journaux des UR et Fédé + # TODO: vérifier l'export des comptes de banque en sortie pour UR et Fédé + journal_fede_com_id = company_id.company_id.journal_fede_com_id + journal_fede_cae_id = company_id.company_id.journal_fede_cae_id + journal_ur_hdf_id = company_id.company_id.journal_ur_hdf_id + journal_ur_med_id = company_id.company_id.journal_ur_med_id + journal_ids = [ + journal_fede_com_id.id, + journal_fede_cae_id.id, + journal_ur_hdf_id.id, + journal_ur_med_id.id, + ] + product_adhesion_id = company_id.product_adhesion_id journal_adhesion_id = company_id.journal_adhesion_id contribution_journal_id = company_id.contribution_journal_id # Selection des dates + pas d'export du journal UR / FEDE domain = [ - ('partner_id', '!=', False), - ('journal_id', '!=', contribution_ur_or_fede_journal_id.id), + ("partner_id", "!=", False), + ("journal_id", "not in", journal_ids), ] if date_start and date_end: domain += [ - ('date', '>=', date_start), - ('date', '<=', date_end), + ("date", ">=", date_start), + ("date", "<=", date_end), ] if date_creation_start and date_creation_end: domain += [ - ('create_date', '>=', date_creation_start), - ('create_date', '<=', date_creation_end), + ("create_date", ">=", date_creation_start), + ("create_date", "<=", date_creation_end), ] - if export_type == 'empty': - domain += [('date_export', '=', False)] + if export_type == "empty": + domain += [("date_export", "=", False)] # adds partner in domain if partner_ids: - domain += [('partner_id', 'in', list(map(int, partner_ids.split(","))))] + domain += [("partner_id", "in", list(map(int, partner_ids.split(","))))] - export_line_ids = request.env['account.move.line'].search(domain) + export_line_ids = request.env["account.move.line"].search(domain) lines_to_export = [] for line in export_line_ids: @@ -76,76 +96,117 @@ class ExportJournalCg(http.Controller): direction = self._move_direction(line) # Produit cotisation CG + Analytique if line.product_id == contribution_cg_id: - amount_analytic = round(1/3 * amount, 2) + amount_analytic = round(1 / 3 * amount, 2) # Compte général - lines_to_export.append(self._export_row( - line=line, amount=amount, direction=direction, - account=('7060' + line.partner_id.ur_id.code_ur)) + lines_to_export.append( + self._export_row( + line=line, + amount=amount, + direction=direction, + account=("7060" + line.partner_id.ur_id.code_ur), + ) ) # Lignes analytiques - lines_to_export.append(self._export_row( - line=line, amount=amount_analytic, direction=direction, - account=('7060' + line.partner_id.ur_id.code_ur), a_type='A', - analytic='010000') + lines_to_export.append( + self._export_row( + line=line, + amount=amount_analytic, + direction=direction, + account=("7060" + line.partner_id.ur_id.code_ur), + a_type="A", + analytic="010000", + ) ) # Produit Adhésion CG + Analytique + OD elif line.product_id == product_adhesion_id: - amount_analytic = round(1 / 2 * amount, 2) # Compte général - lines_to_export.append(self._export_row( - line=line, amount=amount, direction=direction, - journal='VE', account='756200') + lines_to_export.append( + self._export_row( + line=line, + amount=amount, + direction=direction, + journal="VE", + account="756200", + ) ) # Lignes analytiques - lines_to_export.append(self._export_row( - line=line, amount=amount, direction=direction, - account='756200', journal='VE', a_type='A', - analytic='010000') + lines_to_export.append( + self._export_row( + line=line, + amount=amount, + direction=direction, + account="756200", + journal="VE", + a_type="A", + analytic="010000", + ) ) # OD Adhésion - lines_to_export.append(self._export_row( - line=line, amount=amount/2, direction=direction, - account=('4660' + line.partner_id.ur_id.code_ur), - journal='OD') + lines_to_export.append( + self._export_row( + line=line, + amount=amount / 2, + direction=direction, + account=("4660" + line.partner_id.ur_id.code_ur), + journal="OD", + ) ) - lines_to_export.append(self._export_row( - line=line, amount=amount / 2, direction='D', - account=('6581' + line.partner_id.ur_id.code_ur), - journal='OD',) + lines_to_export.append( + self._export_row( + line=line, + amount=amount / 2, + direction="D", + account=("6581" + line.partner_id.ur_id.code_ur), + journal="OD", + ) ) - lines_to_export.append(self._export_row( - line=line, amount=amount / 2, direction='D', - account=('6581' + line.partner_id.ur_id.code_ur), - journal='OD', a_type='A', analytic='010000') + lines_to_export.append( + self._export_row( + line=line, + amount=amount / 2, + direction="D", + account=("6581" + line.partner_id.ur_id.code_ur), + journal="OD", + a_type="A", + analytic="010000", + ) ) - # Compte client CG Scop - Adhésion & Cotisation + # Compte client CG Scop - Adhésion & Cotisation journaux de vente elif line.account_id == default_receivable_account_id: if line.journal_id == journal_adhesion_id: - journal = 'VE' + journal = "VE" elif line.journal_id == contribution_journal_id: - journal = 'CO' + journal = "CO" else: - journal = 'EF' + journal = "EF" # Compte général - lines_to_export.append(self._export_row( - line=line, amount=amount, direction=direction, - account=('4112' + line.partner_id.ur_id.code_ur), - adh_account=self._get_partner_number(line.partner_id), - journal=journal) + lines_to_export.append( + self._export_row( + line=line, + amount=amount, + direction=direction, + account=("4112" + line.partner_id.ur_id.code_ur), + adh_account=self._get_partner_number(line.partner_id), + journal=journal, + ) ) # Banque else: - lines_to_export.append(self._export_row( - line=line, amount=amount, direction=direction, - account=line.account_id.code, journal='EF') + lines_to_export.append( + self._export_row( + line=line, + amount=amount, + direction=direction, + account=line.account_id.code, + journal="EF", + ) ) - line.write({ - 'date_export': datetime.now() - }) + line.write({"date_export": datetime.now()}) - filename_ = ('Export CG Scop - ' - + datetime.strftime(datetime.now(), "%Y-%m-%d_%Hh%S")) + filename_ = "Export CG Scop - " + datetime.strftime( + datetime.now(), "%Y-%m-%d_%Hh%S" + ) return self.export_cg_csv(lines_to_export, filename_) @@ -153,20 +214,29 @@ class ExportJournalCg(http.Controller): # Common function # ------------------------------------------------------ def _export_row( - self, line, amount, direction, account=None, - adh_account=None, a_type='G', journal=None, - analytic=None,): + self, + line, + amount, + direction, + account=None, + adh_account=None, + a_type="G", + journal=None, + analytic=None, + ): # Sanitize N° Bordereau if line.invoice_id.bordereau_id.name: inv_num = line.invoice_id.bordereau_id.name - elif line.full_reconcile_id.reconciled_line_ids.mapped('invoice_id'): - inv_num = line.full_reconcile_id.reconciled_line_ids.mapped('invoice_id')[0].bordereau_id.name + elif line.full_reconcile_id.reconciled_line_ids.mapped("invoice_id"): + inv_num = line.full_reconcile_id.reconciled_line_ids.mapped("invoice_id")[ + 0 + ].bordereau_id.name else: - inv_num = '' + inv_num = "" # Libellé description = line.partner_id.name.upper() if line.name: - description += ' - ' + line.name.upper() + description += " - " + line.name.upper() description = unidecode.unidecode(description) @@ -176,25 +246,25 @@ class ExportJournalCg(http.Controller): journal_code = line.journal_id.code return [ - line.move_id.name, # N° pièce - line.date, # Date pièce - line.date_maturity, # Date échéance - journal_code, # Journal - a_type, # Type d'écriture (général/analytique) - account, # Compte comptable - adh_account, # Num adh - description, # Libellé - direction, # Sens - amount, # Montant - inv_num, # N° Bordereau - analytic, # Code analytique + line.move_id.name, # N° pièce + line.date, # Date pièce + line.date_maturity, # Date échéance + journal_code, # Journal + a_type, # Type d'écriture (général/analytique) + account, # Compte comptable + adh_account, # Num adh + description, # Libellé + direction, # Sens + amount, # Montant + inv_num, # N° Bordereau + analytic, # Code analytique ] def _move_direction(self, line): if line.credit > 0.0: - return 'C' + return "C" else: - return 'D' + return "D" def _move_amount(self, line): if line.credit > 0: @@ -203,32 +273,33 @@ class ExportJournalCg(http.Controller): return line.debit def _get_partner_number(self, partner): - number = partner.member_number + '00' + number = partner.member_number + "00" prefix = 8 - len(number) - return (prefix * '0') + number + return (prefix * "0") + number def export_cg_csv(self, lines_to_export, filename_): fp = StringIO() export_file = csv.writer( fp, - delimiter=';', - dialect='excel', + delimiter=";", + dialect="excel", ) # Add header line for line in lines_to_export: # Format date value line_values = [ - value if not isinstance(value, date) else value.strftime( - "%d/%m/%Y") for value in line] + value if not isinstance(value, date) else value.strftime("%d/%m/%Y") + for value in line + ] export_file.writerow(line_values) fp.seek(0) data = fp.read() fp.close() - filename = filename_ + '.txt' + filename = filename_ + ".txt" csvhttpheaders = [ - ('Content-Type', 'text/plain;charset=iso-8859-1'), - ('Content-Disposition', content_disposition(filename)), + ("Content-Type", "text/plain;charset=iso-8859-1"), + ("Content-Disposition", content_disposition(filename)), ] return request.make_response(data, headers=csvhttpheaders) diff --git a/controllers/union_sociale.py b/controllers/union_sociale.py index 41968a2..99c9cda 100644 --- a/controllers/union_sociale.py +++ b/controllers/union_sociale.py @@ -12,9 +12,13 @@ class UnionSocialeController(http.Controller): # Routes # ------------------------------------------------------ @http.route( - ['/union-sociale/<adh>', - '/union-sociale/<adh>/<year>'], - type='http', auth="public", method=['GET'], csrf=False, website=False) + ["/union-sociale/<adh>", "/union-sociale/<adh>/<year>"], + type="http", + auth="public", + method=["GET"], + csrf=False, + website=False, + ) def get_union_sociale_pdf(self, adh, year=False): """ URL pour générer le PDF de l'Union Sociale à joindre au @@ -23,15 +27,19 @@ class UnionSocialeController(http.Controller): :params : year -> année du bordereau @return : PDF """ - partner_id = request.env['res.partner'].sudo().search([('member_number', '=', adh)]) + partner_id = ( + request.env["res.partner"].sudo().search([("member_number", "=", adh)]) + ) if partner_id: - ctx = {'year': int(year)} if year else {} + ctx = {"year": int(year)} if year else {} # Get report - report = request.env.ref('cgscop_cotisation_cg.cgscop_union_sociale_report') + report = request.env.ref("cgscop_cotisation_cg.cgscop_union_sociale_report") # Create PDF pdf = report.sudo().with_context(ctx).render_qweb_pdf([partner_id.id])[0] - pdfhttpheaders = [('Content-Type', 'application/pdf'), - ('Content-Length', len(pdf)), ] + pdfhttpheaders = [ + ("Content-Type", "application/pdf"), + ("Content-Length", len(pdf)), + ] # Return PDF return request.make_response(pdf, headers=pdfhttpheaders) else: diff --git a/datas/bordereau_refund_wizard_quarter_data.xml b/datas/bordereau_refund_wizard_quarter_data.xml index 213e9e1..e048f8e 100644 --- a/datas/bordereau_refund_wizard_quarter_data.xml +++ b/datas/bordereau_refund_wizard_quarter_data.xml @@ -1,24 +1,36 @@ -<?xml version="1.0"?> +<?xml version="1.0" ?> <!-- Copyright 2021 Le Filament (<https://www.le-filament.com>) License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). --> <odoo> <data noupdate="1"> - <record id="bordereau_wizard_quarter1" model="scop.bordereau.refund.wizard.quarter"> + <record + id="bordereau_wizard_quarter1" + model="scop.bordereau.refund.wizard.quarter" + > <field name="quarter">1</field> </record> - <record id="bordereau_wizard_quarter2" model="scop.bordereau.refund.wizard.quarter"> + <record + id="bordereau_wizard_quarter2" + model="scop.bordereau.refund.wizard.quarter" + > <field name="quarter">2</field> </record> - - <record id="bordereau_wizard_quarter3" model="scop.bordereau.refund.wizard.quarter"> + + <record + id="bordereau_wizard_quarter3" + model="scop.bordereau.refund.wizard.quarter" + > <field name="quarter">3</field> </record> - - <record id="bordereau_wizard_quarter4" model="scop.bordereau.refund.wizard.quarter"> + + <record + id="bordereau_wizard_quarter4" + model="scop.bordereau.refund.wizard.quarter" + > <field name="quarter">4</field> </record> - + </data> -</odoo> \ No newline at end of file +</odoo> diff --git a/datas/ir_sequence_data.xml b/datas/ir_sequence_data.xml index 6409aac..1d86189 100644 --- a/datas/ir_sequence_data.xml +++ b/datas/ir_sequence_data.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8" ?> <odoo> <data noupdate="1"> @@ -7,7 +7,7 @@ <field name="code">scop.bordereau</field> <field name="padding">4</field> <field name="prefix">CG%(year)s</field> - <field name="company_id" ref="base.main_company"/> + <field name="company_id" ref="base.main_company" /> </record> </data> diff --git a/datas/mail_data.xml b/datas/mail_data.xml index c467205..ad475e8 100644 --- a/datas/mail_data.xml +++ b/datas/mail_data.xml @@ -1,14 +1,18 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8" ?> <odoo> - <data noupdate="0"> + <data> <!-- Template and notification section --> <record id="email_template_cotisation_cg" model="mail.template"> <field name="name">Cotisation CG SCOP : Génération du bordereau</field> - <field name="model_id" ref="cgscop_cotisation_cg.model_scop_bordereau"/> - <field name="email_from">"Confédération Générale des Scop et Scic" <administratif.cg@scop.coop></field> - <field name="reply_to">"Confédération Générale des Scop et Scic" <administratif.cg@scop.coop></field> + <field name="model_id" ref="cgscop_cotisation_cg.model_scop_bordereau" /> + <field + name="email_from" + >"Confédération Générale des Scop et Scic" <administratif.cg@scop.coop></field> + <field + name="reply_to" + >"Confédération Générale des Scop et Scic" <administratif.cg@scop.coop></field> <field name="partner_to">${object.get_recipients()}</field> <field name="subject">CG SCOP : Appel de cotisations ${object.year}</field> <field name="body_html" type="html"> @@ -18,20 +22,23 @@ <p>Cotisation sur votre espace extranet : lien</p> - <p>Vous pouvez également télécharger le lien du bordereau de cotisation à l'Union sociale en cliquant sur le lien suivant : <a href="/union-sociale/${object.partner_id.member_number}/${object.year}">bordereau de l'union sociale</a></p> + <p + >Vous pouvez également télécharger le lien du bordereau de cotisation à l'Union sociale en cliquant sur le lien suivant : <a + href="/union-sociale/${object.partner_id.member_number}/${object.year}" + >bordereau de l'union sociale</a></p> - <p>Nous vous prions d'agréer, Chère Coopératrice, Cher Coopérateur, nos sentiments les meilleurs.</p> + <p + >Nous vous prions d'agréer, Chère Coopératrice, Cher Coopérateur, nos sentiments les meilleurs.</p> <p>Pour la CG Scop,</p> - + <p>JACQUES LANDRIOT</p> <p>Président</p> </p> </div> </field> <field name="lang">fr_FR</field> - <field name="user_signature" eval="False"/> - <field name="auto_delete" eval="False"/> + <field name="auto_delete" eval="False" /> </record> </data> diff --git a/datas/queue_job_data.xml b/datas/queue_job_data.xml index 97e8623..e55a035 100644 --- a/datas/queue_job_data.xml +++ b/datas/queue_job_data.xml @@ -1,24 +1,39 @@ -<?xml version="1.0"?> +<?xml version="1.0" ?> <!-- Copyright 2021 Le Filament (<https://www.le-filament.com>) License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). --> <odoo> <data noupdate="1"> - <record id="job_function_cotisation_cg_create_bordereau" model="queue.job.function"> - <field name="model_id" ref="cgscop_cotisation_cg.model_scop_cotisation_cg" /> + <record + id="job_function_cotisation_cg_create_bordereau" + model="queue.job.function" + > + <field + name="model_id" + ref="cgscop_cotisation_cg.model_scop_cotisation_cg" + /> <field name="method">create_bordereau</field> <field name="channel_id" ref="queue_job.channel_root" /> - <field name="related_action" eval='{"func_name": "related_action_custom", "kwargs": {"model": "scop.bordereau", "record": "result"}}' /> + <field + name="related_action" + eval='{"func_name": "related_action_custom", "kwargs": {"model": "scop.bordereau", "record": "result"}}' + /> <field name="retry_pattern" eval="{1: 10, 5: 30, 10: 300}" /> </record> - <record id="job_function_cotisation_cg_validate_bordereau" model="queue.job.function"> + <record + id="job_function_cotisation_cg_validate_bordereau" + model="queue.job.function" + > <field name="model_id" ref="cgscop_cotisation_cg.model_scop_bordereau" /> <field name="method">validate_bordereau</field> <field name="channel_id" ref="queue_job.channel_root" /> - <field name="related_action" eval='{"func_name": "related_action_custom"}' /> + <field + name="related_action" + eval='{"func_name": "related_action_custom"}' + /> <field name="retry_pattern" eval="{1: 10, 5: 30, 10: 300}" /> </record> </data> -</odoo> \ No newline at end of file +</odoo> diff --git a/migration/14.0.1.0.0/post-migration.py b/migration/14.0.1.0.0/post-migration.py new file mode 100644 index 0000000..36aed22 --- /dev/null +++ b/migration/14.0.1.0.0/post-migration.py @@ -0,0 +1,40 @@ +# © 2022 Le Filament (<http://www.le-filament.com>) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openupgradelib import openupgrade # pylint: disable=W7936 + + +def scop_account_move_cg_map_values(env): + openupgrade.map_values( + env.cr, + openupgrade.get_legacy_name("cotiz_quarter"), + "account_move", + [ + (1, "1"), + (2, "2"), + (3, "3"), + (4, "4"), + ], + table="account_move", + ) + + +def scop_bordereau_map_values(env): + openupgrade.map_values( + env.cr, + openupgrade.get_legacy_name("nb_quarter"), + "scop_bordereau", + [ + (1, "1"), + (2, "2"), + (3, "3"), + (4, "4"), + ], + table="scop_bordereau", + ) + + +@openupgrade.migrate() +def migrate(env, version): + scop_account_move_cg_map_values(env) + scop_bordereau_map_values(env) diff --git a/migration/14.0.1.0.0/pre-migration.py b/migration/14.0.1.0.0/pre-migration.py new file mode 100644 index 0000000..e439454 --- /dev/null +++ b/migration/14.0.1.0.0/pre-migration.py @@ -0,0 +1,14 @@ +# © 2022 Le Filament (<http://www.le-filament.com>) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openupgradelib import openupgrade + +column_renames = { + "account_move": [("cotiz_quarter", None)], + "scop_bordereau": [("nb_quarter", None)], +} + + +@openupgrade.migrate() +def migrate(env, version): + openupgrade.rename_columns(env.cr, column_renames) diff --git a/models/__init__.py b/models/__init__.py old mode 100755 new mode 100644 index c349815..872bf77 --- a/models/__init__.py +++ b/models/__init__.py @@ -1,7 +1,7 @@ # © 2021 Le Filament (<http://www.le-filament.com>) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from . import account_invoice +from . import account_move from . import res_company from . import res_config_settings from . import scop_bordereau_cg diff --git a/models/account_invoice.py b/models/account_invoice.py deleted file mode 100644 index b8c7e82..0000000 --- a/models/account_invoice.py +++ /dev/null @@ -1,47 +0,0 @@ -# © 2021 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 - - -class ScopAccountInvoiceCG(models.Model): - _inherit = "account.invoice" - - bordereau_id = fields.Many2one( - comodel_name='scop.bordereau', - string='Bordereau de rattachement', - ondelete='cascade', - required=False,) - cotisation_cg_id = fields.Many2one( - related='bordereau_id.base_cotisation_cg') - amount_cg_calculated = fields.Monetary( - string="Montant cotisation annuel", - currency_field='company_currency_id', readonly=True) - nb_quarter = fields.Selection(related='bordereau_id.nb_quarter') - cotiz_quarter = fields.Selection( - string='Trimestre', - selection=[(1, 1), (2, 2), (3, 3), (4, 4)], - required=False, ) - - # ------------------------------------------------------ - # Compute fields - # ------------------------------------------------------ - - # ------------------------------------------------------ - # Override parent - # ------------------------------------------------------ - - # ------------------------------------------------------ - # Global functions - # ------------------------------------------------------ - def view_cotiz(self): - form_view = self.env.ref( - 'cgscop_cotisation_cg.invoice_form_scop_cg_inherited').id - return { - 'name': "Cotisation", - 'type': 'ir.actions.act_window', - 'res_model': 'account.invoice', - 'views': [[form_view, 'form']], - 'res_id': self.id, - 'target': 'current', - } diff --git a/models/account_move.py b/models/account_move.py new file mode 100644 index 0000000..b92e499 --- /dev/null +++ b/models/account_move.py @@ -0,0 +1,51 @@ +# © 2021 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 + + +class ScopAccountMove(models.Model): + _inherit = "account.move" + + bordereau_id = fields.Many2one( + comodel_name="scop.bordereau", + string="Bordereau de rattachement", + ondelete="cascade", + required=False, + ) + cotisation_cg_id = fields.Many2one(related="bordereau_id.base_cotisation_cg") + amount_cg_calculated = fields.Monetary( + string="Montant cotisation annuel", + currency_field="company_currency_id", + readonly=True, + ) + nb_quarter = fields.Selection(related="bordereau_id.nb_quarter") + cotiz_quarter = fields.Selection( + string="Trimestre", + selection=[("1", "1"), ("2", "2"), ("3", "3"), ("4", "4")], + required=False, + ) + + # ------------------------------------------------------ + # Compute fields + # ------------------------------------------------------ + + # ------------------------------------------------------ + # Override parent + # ------------------------------------------------------ + + # ------------------------------------------------------ + # Global functions + # ------------------------------------------------------ + def view_cotiz(self): + form_view = self.env.ref( + "cgscop_cotisation_cg.invoice_form_scop_cg_inherited" + ).id + return { + "name": "Cotisation", + "type": "ir.actions.act_window", + "res_model": "account.invoice", + "views": [[form_view, "form"]], + "res_id": self.id, + "target": "current", + } diff --git a/models/res_company.py b/models/res_company.py index 459f8dd..183ab32 100644 --- a/models/res_company.py +++ b/models/res_company.py @@ -5,59 +5,51 @@ from odoo import fields, models class ScopCotisationCGCompany(models.Model): - _inherit = 'res.company' + _inherit = "res.company" - is_contribution_cg = fields.Boolean( - string='Cotisations CG Scop' - ) + is_contribution_cg = fields.Boolean(string="Cotisations CG Scop") contribution_cg_id = fields.Many2one( - comodel_name='product.product', - string='Article de cotisation CG', - domain="[('sale_ok', '=', True), ('company_id', '=', company_id)]" + comodel_name="product.product", + string="Article de cotisation CG", + domain="[('sale_ok', '=', True), ('company_id', '=', company_id)]", ) contribution_fede_com_id = fields.Many2one( - comodel_name='product.product', - string='Article de cotisation Fédé de la Com', - domain="[('sale_ok', '=', True), ('company_id', '=', company_id)]" + comodel_name="product.product", + string="Article de cotisation Fédé de la Com", + domain="[('sale_ok', '=', True), ('company_id', '=', company_id)]", ) - receivable_account_fede_com_id = fields.Many2one( - comodel_name='account.account', - string="Compte de tiers Fédération de la com", - domain="[('internal_type', '=', 'receivable')]", + journal_fede_com_id = fields.Many2one( + comodel_name="account.journal", + string="Journal Fédération de la com", + domain="[('type', '=', 'sale')]", ) contribution_fede_cae_id = fields.Many2one( - comodel_name='product.product', - string='Article de cotisation Fédé CAE', - domain="[('sale_ok', '=', True), ('company_id', '=', company_id)]" + comodel_name="product.product", + string="Article de cotisation Fédé CAE", + domain="[('sale_ok', '=', True), ('company_id', '=', company_id)]", ) - receivable_account_fede_cae_id = fields.Many2one( - comodel_name='account.account', - string="Compte de tiers Fédération CAE", - domain="[('internal_type', '=', 'receivable')]", + journal_fede_cae_id = fields.Many2one( + comodel_name="account.journal", + string="Journal Fédération CAE", + domain="[('type', '=', 'sale')]", ) contribution_hdf_id = fields.Many2one( - comodel_name='product.product', - string='Article de cotisation UR HDF', - domain="[('sale_ok', '=', True), ('company_id', '=', company_id)]" + comodel_name="product.product", + string="Article de cotisation UR HDF", + domain="[('sale_ok', '=', True), ('company_id', '=', company_id)]", ) - receivable_account_ur_hdf_id = fields.Many2one( - comodel_name='account.account', - string="Compte de tiers UR HDF", - domain="[('internal_type', '=', 'receivable')]", + journal_ur_hdf_id = fields.Many2one( + comodel_name="account.journal", + string="Journal UR HDF", + domain="[('type', '=', 'sale')]", ) contribution_med_id = fields.Many2one( - comodel_name='product.product', - string='Article de cotisation UR Méditerranée', - domain="[('sale_ok', '=', True), ('company_id', '=', company_id)]" - ) - receivable_account_ur_med_id = fields.Many2one( - comodel_name='account.account', - string="Compte de tiers UR Med", - domain="[('internal_type', '=', 'receivable')]", + comodel_name="product.product", + string="Article de cotisation UR Méditerranée", + domain="[('sale_ok', '=', True), ('company_id', '=', company_id)]", ) - - contribution_ur_or_fede_journal_id = fields.Many2one( - comodel_name='account.journal', - string='Journal des cotisations UR ou Fédérations', + journal_ur_med_id = fields.Many2one( + comodel_name="account.journal", + string="Journal UR Med", domain="[('type', '=', 'sale')]", ) diff --git a/models/res_config_settings.py b/models/res_config_settings.py index a6b6e01..0fa5f7c 100644 --- a/models/res_config_settings.py +++ b/models/res_config_settings.py @@ -1,113 +1,82 @@ # © 2020 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 CotisationsConfigSettings(models.TransientModel): - _inherit = 'res.config.settings' + _inherit = "res.config.settings" is_contribution_cg = fields.Boolean( - string='Cotisations CG Scop', + string="Cotisations CG Scop", related="company_id.is_contribution_cg", - readonly=False) + readonly=False, + ) contribution_cg_id = fields.Many2one( - comodel_name='product.product', + comodel_name="product.product", related="company_id.contribution_cg_id", readonly=False, - string='Article de cotisation CG', - domain="[('sale_ok', '=', True), ('company_id', '=', company_id)]" + string="Article de cotisation CG", + domain="[('sale_ok', '=', True), ('company_id', '=', company_id)]", ) contribution_fede_com_id = fields.Many2one( - comodel_name='product.product', + comodel_name="product.product", related="company_id.contribution_fede_com_id", readonly=False, - string='Article de cotisation Fédé de la Com', - domain="[('sale_ok', '=', True), ('company_id', '=', company_id)]" + string="Article de cotisation Fédé de la Com", + domain="[('sale_ok', '=', True), ('company_id', '=', company_id)]", ) - receivable_account_fede_com_id = fields.Many2one( - comodel_name='account.account', - related="company_id.receivable_account_fede_com_id", + journal_fede_com_id = fields.Many2one( + comodel_name="account.journal", + related="company_id.journal_fede_com_id", readonly=False, - string="Compte de tiers Fédération de la com", - domain="[('internal_type', '=', 'receivable')]", + string="Journal Fédération de la com", + domain="[('type', '=', 'sale')]", ) contribution_fede_cae_id = fields.Many2one( - comodel_name='product.product', + comodel_name="product.product", related="company_id.contribution_fede_cae_id", readonly=False, - string='Article de cotisation Fédé CAE', - domain="[('sale_ok', '=', True), ('company_id', '=', company_id)]" + string="Article de cotisation Fédé CAE", + domain="[('sale_ok', '=', True), ('company_id', '=', company_id)]", ) - receivable_account_fede_cae_id = fields.Many2one( - comodel_name='account.account', - related="company_id.receivable_account_fede_cae_id", + journal_fede_cae_id = fields.Many2one( + comodel_name="account.journal", + related="company_id.journal_fede_cae_id", readonly=False, - string="Compte de tiers Fédération CAE", - domain="[('internal_type', '=', 'receivable')]", + string="Journal Fédération CAE", + domain="[('type', '=', 'sale')]", ) contribution_hdf_id = fields.Many2one( - comodel_name='product.product', + comodel_name="product.product", related="company_id.contribution_hdf_id", readonly=False, - string='Article de cotisation UR HDF', - domain="[('sale_ok', '=', True), ('company_id', '=', company_id)]" + string="Article de cotisation UR HDF", + domain="[('sale_ok', '=', True), ('company_id', '=', company_id)]", ) - receivable_account_ur_hdf_id = fields.Many2one( - comodel_name='account.account', - related="company_id.receivable_account_ur_hdf_id", + journal_ur_hdf_id = fields.Many2one( + comodel_name="account.journal", + related="company_id.journal_ur_hdf_id", readonly=False, - string="Compte de tiers UR HDF", - domain="[('internal_type', '=', 'receivable')]", + string="Journal UR HDF", + domain="[('type', '=', 'sale')]", ) contribution_med_id = fields.Many2one( - comodel_name='product.product', + comodel_name="product.product", related="company_id.contribution_med_id", readonly=False, - string='Article de cotisation UR Méditerranée', - domain="[('sale_ok', '=', True), ('company_id', '=', company_id)]" + string="Article de cotisation UR Méditerranée", + domain="[('sale_ok', '=', True), ('company_id', '=', company_id)]", ) - receivable_account_ur_med_id = fields.Many2one( - comodel_name='account.account', - related="company_id.receivable_account_ur_med_id", + journal_ur_med_id = fields.Many2one( + comodel_name="account.journal", + related="company_id.journal_ur_med_id", readonly=False, - string="Compte de tiers UR Med", - domain="[('internal_type', '=', 'receivable')]", + string="Journal UR Med", + domain="[('type', '=', 'sale')]", ) - contribution_ur_or_fede_journal_id = fields.Many2one( - comodel_name='account.journal', - related="company_id.contribution_ur_or_fede_journal_id", - readonly=False, - string='Journal des cotisations UR ou Fédération', - domain="[('type', '=', 'sale')]") - - @api.onchange('is_contribution') + @api.onchange("is_contribution") def _onchange_is_contribution_cg(self): if not self.is_contribution: self.is_contribution_cg = False - - def execute(self): - """ - Rewrite execute() function to add current company to the list - of available company in ir_ui_menu - """ - res = super(CotisationsConfigSettings, self).execute() - - menu_appels_cotiz_cg = self.env.ref( - 'cgscop_cotisation_cg.menu_scop_cotisation_cg_appel_cotisation') - menu_cotiz_cg = self.env.ref( - 'cgscop_cotisation_cg.menu_scop_cotisation_cg_calcul') - menu_bordereaux_cg = self.env.ref( - 'cgscop_cotisation_cg.scop_bordereau_menu') - # menu_adhesions_cg = self.env.ref( - # 'cgscop_cotisation_cg.menu_scop_cotisation_cg_adhesions') - - - bool_condition = self.is_contribution_cg - - self.add_company_to_menu(menu_appels_cotiz_cg, bool_condition) - self.add_company_to_menu(menu_cotiz_cg, bool_condition) - self.add_company_to_menu(menu_bordereaux_cg, bool_condition) - # self.add_company_to_menu(menu_adhesions_cg, bool_condition) - return res diff --git a/models/scop_bordereau_cg.py b/models/scop_bordereau_cg.py index 13dd6e2..24773fb 100644 --- a/models/scop_bordereau_cg.py +++ b/models/scop_bordereau_cg.py @@ -1,182 +1,205 @@ # © 2021 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 from odoo.exceptions import UserError class Bordereau(models.Model): - _name = 'scop.bordereau' - _description = 'Bordereau des cotisations CG' - _inherit = 'mail.thread' - _order = 'name desc' + _name = "scop.bordereau" + _description = "Bordereau des cotisations CG" + _inherit = "mail.thread" + _order = "name desc" name = fields.Char( - string='Référence du bordereau', - readonly=True, copy=False, default="Brouillon") - version = fields.Integer('Version', default=1, track_visibility='onchange') + string="Référence du bordereau", + readonly=True, + copy=False, + default="Brouillon", + ) + version = fields.Integer("Version", default=1, tracking=1) base_cotisation_cg = fields.Many2one( - comodel_name='scop.cotisation.cg', - string='Base de cotisation', + comodel_name="scop.cotisation.cg", + string="Base de cotisation", required=False, readonly=True, - ondelete='cascade') + ondelete="cascade", + ) year = fields.Char( - string='Année du bordereau', - compute='_compute_year', + string="Année du bordereau", + compute="_compute_year", store=True, - required=False) + required=False, + ) date_cotisation = fields.Date("Date de cotisation") - is_regul = fields.Boolean("Regularisation", defaut=False) + is_regul = fields.Boolean("Regularisation", default=False) date_regul = fields.Date("Date de régularisation") - comment_regul = fields.Char('Motif de régularisation') + comment_regul = fields.Char("Motif de régularisation") partner_id = fields.Many2one( - comodel_name='res.partner', - string='Adhérent', + comodel_name="res.partner", + string="Adhérent", required=False, readonly=True, - track_visibility='onchange') + tracking=1, + ) member_number = fields.Integer( - string='N° Adhérent', - related='partner_id.member_number_int') + string="N° Adhérent", related="partner_id.member_number_int" + ) liasse_fiscale_id = fields.Many2one( - comodel_name='scop.liasse.fiscale', - string='Liasse fiscale de référence', + comodel_name="scop.liasse.fiscale", + string="Liasse fiscale de référence", readonly=True, - track_visibility='onchange') + tracking=1, + ) liasse_count = fields.Integer( - string='Nombre de liasses', - compute='_compute_liasse_count' + string="Nombre de liasses", compute="_compute_liasse_count" ) type_liasse_fiscale = fields.Selection( - string='Type de liasse', - related='liasse_fiscale_id.type_id') + string="Type de liasse", related="liasse_fiscale_id.type_id" + ) is_liasse_previ = fields.Boolean( - 'Est une liasse prévisionnelle', - compute='_compute_is_liasse_previ') + "Est une liasse prévisionnelle", compute="_compute_is_liasse_previ" + ) dureeExercice = fields.Integer( - string='Durée de l’exercice', - related='liasse_fiscale_id.dureeExercice', - track_visibility='onchange') + string="Durée de l’exercice", + related="liasse_fiscale_id.dureeExercice", + tracking=1, + ) partner_ur_id = fields.Many2one( - comodel_name='union.regionale', - string='UR Adhérent', - related='partner_id.ur_id', - store=True + comodel_name="union.regionale", + string="UR Adhérent", + related="partner_id.ur_id", + store=True, ) payment_mode_id = fields.Many2one( - comodel_name='account.payment.mode', string="Mode de paiment", - ondelete='restrict', - readonly=True, states={'draft': [('readonly', False)]}, - track_visibility='onchange') + comodel_name="account.payment.mode", + string="Mode de paiment", + ondelete="restrict", + readonly=True, + states={"draft": [("readonly", False)]}, + tracking=1, + ) invoice_ids = fields.One2many( - comodel_name='account.invoice', - inverse_name='bordereau_id', - string='Cotisations', - required=False) + comodel_name="account.move", + inverse_name="bordereau_id", + string="Cotisations", + required=False, + ) invoice_count = fields.Integer( - string='Nombre d\'appels', - compute='_compute_invoice_count' + string="Nombre d'appels", compute="_compute_invoice_count" ) - state = fields.Selection([ - ('new', 'Brouillon'), - ('ongoing', 'En cours de modification'), - ('validated', 'Validé'), - ('paid', 'Payé'), - ('cancel', 'Annulé')], + state = fields.Selection( + [ + ("new", "Brouillon"), + ("ongoing", "En cours de modification"), + ("validated", "Validé"), + ("paid", "Payé"), + ("cancel", "Annulé"), + ], string="Statut", - default='new', - compute='_compute_state', store=True, - track_visibility='onchange', ) + default="new", + compute="_compute_state", + store=True, + tracking=1, + ) company_id = fields.Many2one( - comodel_name='res.company', - related='base_cotisation_cg.company_id', - string="Company" + comodel_name="res.company", + related="base_cotisation_cg.company_id", + string="Company", ) company_currency_id = fields.Many2one( - comodel_name='res.currency', - related='company_id.currency_id', - string="Company Currency", readonly=True) + comodel_name="res.currency", + related="company_id.currency_id", + string="Company Currency", + readonly=True, + ) amount_total_cotiz = fields.Monetary( - string='Montant total de(s) cotisation(s)', - currency_field='company_currency_id', - compute='_compute_amount_total_cotiz', - store=True) + string="Montant total de(s) cotisation(s)", + currency_field="company_currency_id", + compute="_compute_amount_total_cotiz", + store=True, + ) amount_residual = fields.Monetary( - string='Montant dû', - currency_field='company_currency_id', - compute='_compute_amount_residual', - store=True) + string="Montant dû", + currency_field="company_currency_id", + compute="_compute_amount_residual", + store=True, + ) nb_quarter = fields.Selection( - string='Nb de trimestres de cotisation', - selection=[(1, '1'), - (2, '2'), - (3, '3'), - (4, '4')], - default='4', required=True, - track_visibility='onchange') - details = fields.Html('Détails', compute='_compute_details') + string="Nb de trimestres de cotisation", + selection=[("1", "1"), ("2", "2"), ("3", "3"), ("4", "4")], + default="4", + required=True, + tracking=1, + ) + details = fields.Html("Détails", compute="_compute_details") # Assiettes for cotiz calcul year_liasse = fields.Integer( - string='Année de la liasse', - related='liasse_fiscale_id.year', - track_visibility='onchange', - oldname='year_liasse_retenue' + string="Année de la liasse", + related="liasse_fiscale_id.year", + tracking=1, ) type_assiette = fields.Selection( - related='liasse_fiscale_id.contribution_base_type', store=True) + related="liasse_fiscale_id.contribution_base_type", store=True + ) montant_assiette = fields.Integer( - related='liasse_fiscale_id.contribution_base_amount', store=True) + related="liasse_fiscale_id.contribution_base_amount", store=True + ) ca = fields.Float( - string='CA', - compute='_compute_values_calculation', store=True, - track_visibility='onchange', - oldname='ca_retenu') + string="CA", + compute="_compute_values_calculation", + store=True, + tracking=1, + ) va = fields.Float( - string='VA', - compute='_compute_values_calculation', store=True, - track_visibility='onchange', - oldname='va_cg_retenu') + string="VA", + compute="_compute_values_calculation", + store=True, + tracking=1, + ) staff_count = fields.Integer( - string='Effectif de la coop', - readonly=True, oldname='staff_count_connu') + string="Effectif de la coop", + readonly=True, + ) staff_shareholder_count = fields.Integer( - string='Effectif sociétaires de la coop', - readonly=True, oldname='staff_shareholder_count_retenu') + string="Effectif sociétaires de la coop", + readonly=True, + ) staff_average = fields.Float( - string='Effectif moyen de la coop', - readonly=True, oldname='staff_average_retenu') + string="Effectif moyen de la coop", + readonly=True, + ) net_results = fields.Float( - string='Résultat net', - compute='_compute_values_calculation', store=True, - oldname='net_results_retenu') + string="Résultat net", + compute="_compute_values_calculation", + store=True, + ) wage_cg = fields.Float( - string='Montant masse salariale', - compute='_compute_values_calculation', store=True, - oldname='wage_cg_retenu') + string="Montant masse salariale", + compute="_compute_values_calculation", + store=True, + ) is_sdd = fields.Boolean( - 'Au prélèvement', - compute='compute_is_sdd', - search='_search_is_sdd') + "Au prélèvement", compute="_compute_is_sdd", search="_search_is_sdd" + ) refund_id = fields.Many2one( - comodel_name='scop.bordereau', - string='Nouveau bordereau', + comodel_name="scop.bordereau", + string="Nouveau bordereau", domain="[('partner_id', '=', partner_id)]", - track_visibility='onchange' + tracking=1, ) related_refund_id = fields.Many2one( - comodel_name='scop.bordereau', - string='Bordereau initial', + comodel_name="scop.bordereau", + string="Bordereau initial", ) # Emails management recipient_ids = fields.One2many( - comodel_name='res.partner', - compute='_compute_recipient' + comodel_name="res.partner", compute="_compute_recipient" ) # TODO : Use when email active @@ -187,186 +210,191 @@ class Bordereau(models.Model): # Historique des versions bordereau_version_ids = fields.One2many( - comodel_name='scop.bordereau.version', - inverse_name='bordereau_id', - string='Historique' + comodel_name="scop.bordereau.version", + inverse_name="bordereau_id", + string="Historique", ) has_outstanding = fields.Boolean( - string='Paiements en circulation', - compute='_compute_has_outstanding', store=True + string="Paiements en circulation", + compute="_compute_has_outstanding", + store=True, ) # ------------------------------------------------------ # Compute # ------------------------------------------------------ - @api.depends('liasse_fiscale_id') - @api.multi + @api.depends("liasse_fiscale_id") def _compute_values_calculation(self): for r in self: if r.liasse_fiscale_id: liasse = r.liasse_fiscale_id r.ca = liasse.revenue_cgsubv r.va = r.base_cotisation_cg.get_va(liasse) - r.net_results = liasse.L2053_HN \ - if liasse.L2053_HN > 0 else liasse.L2051_DI + r.net_results = ( + liasse.L2053_HN if liasse.L2053_HN > 0 else liasse.L2051_DI + ) r.wage_cg = liasse.wage_cg else: r.ca = r.va = r.net_results = r.wage_cg = 0 - @api.depends('invoice_ids.state', - 'bordereau_version_ids', 'bordereau_version_ids.state') - @api.multi + @api.depends( + "invoice_ids.state", + "invoice_ids.payment_state", + "bordereau_version_ids", + "bordereau_version_ids.state", + ) def _compute_state(self): for bordereau in self: draft_cotiz = bordereau.invoice_ids.filtered( - lambda cotiz: cotiz.state == 'draft' + lambda cotiz: cotiz.state == "draft" ) ongoing_version = bordereau.bordereau_version_ids.filtered( - lambda v: v.state == 'new' + lambda v: v.state == "new" ) if ongoing_version: - bordereau.state = 'ongoing' + bordereau.state = "ongoing" elif draft_cotiz: - bordereau.state = 'new' + bordereau.state = "new" else: unpaid_cotiz = bordereau.invoice_ids.filtered( - lambda cotiz: cotiz.state not in ('paid', 'cancel') + lambda cotiz: cotiz.payment_state not in ("paid", "cancel") ) if not unpaid_cotiz: - bordereau.state = 'paid' + bordereau.state = "paid" else: - bordereau.state = 'validated' + bordereau.state = "validated" - @api.depends('base_cotisation_cg') - @api.multi + @api.depends("base_cotisation_cg") def _compute_year(self): for bordereau in self: bordereau.year = bordereau.base_cotisation_cg.year - @api.multi def _compute_invoice_count(self): for r in self: r.invoice_count = len(r.invoice_ids) - @api.multi def _compute_liasse_count(self): for r in self: r.liasse_count = len(r.partner_id.liasse_fiscale_ids) - @api.depends('invoice_count', 'invoice_ids.amount_total_signed') - @api.multi + @api.depends("invoice_count", "invoice_ids.amount_total_signed") def _compute_amount_total_cotiz(self): for r in self: - r.amount_total_cotiz = sum( - r.invoice_ids.mapped('amount_total_signed')) + r.amount_total_cotiz = sum(r.invoice_ids.mapped("amount_total_signed")) - @api.depends('invoice_ids', 'invoice_ids.residual_signed') - @api.multi + @api.depends("invoice_ids", "invoice_ids.amount_residual_signed") def _compute_amount_residual(self): for r in self: - r.amount_total_cotiz = sum( - r.invoice_ids.mapped('residual_signed')) + r.amount_total_cotiz = sum(r.invoice_ids.mapped("amount_residual_signed")) - @api.multi def _compute_recipient(self): tag_cotiz_id = self.env.user.company_id.tag_cotiz_id for bordereau in self: child_ids = bordereau.partner_id.child_ids.filtered( - lambda child: - (tag_cotiz_id in child.category_id) and child.email) + lambda child: (tag_cotiz_id in child.category_id) and child.email + ) if bordereau.partner_id.email: bordereau.recipient_ids = bordereau.partner_id + child_ids else: bordereau.recipient_ids = child_ids - @api.multi def _compute_type_assiette(self): for bordereau in self: - bordereau.type_assiette = \ - bordereau.base_cotisation_cg.get_type_assiette( - bordereau.ca, bordereau.va) + bordereau.type_assiette = bordereau.base_cotisation_cg.get_type_assiette( + bordereau.ca, bordereau.va + ) - @api.multi def _search_type_assiette(self, operator, value): - recs = self.search([]).filtered( - lambda r: r.type_assiette == value) + recs = self.search([]).filtered(lambda r: r.type_assiette == value) if recs: - return [('id', 'in', [x.id for x in recs])] + return [("id", "in", [x.id for x in recs])] - @api.multi - def compute_is_sdd(self): - sdd_id = self.env.ref( - 'account_banking_sepa_direct_debit.sepa_direct_debit').id + def _compute_is_sdd(self): + sdd_id = self.env.ref("account_banking_sepa_direct_debit.sepa_direct_debit").id for bdx in self: - if bdx.payment_mode_id and bdx.payment_mode_id.payment_method_id.id == sdd_id: + if ( + bdx.payment_mode_id + and bdx.payment_mode_id.payment_method_id.id == sdd_id + ): bdx.is_sdd = True else: bdx.is_sdd = False - @api.multi def _search_is_sdd(self, operator, value): recs = self.search([]).filtered(lambda x: x.is_sdd is True) if recs: - return [('id', 'in', [x.id for x in recs])] + return [("id", "in", [x.id for x in recs])] - @api.multi def _compute_is_liasse_previ(self): for bordereau in self: - if bordereau.type_liasse_fiscale == 'forecast': + if bordereau.type_liasse_fiscale == "forecast": bordereau.is_liasse_previ = True else: bordereau.is_liasse_previ = False - @api.depends('invoice_ids.residual') - @api.multi + @api.depends("invoice_ids.amount_residual") def _compute_has_outstanding(self): for r in self: if r.invoice_ids: - if r.invoice_ids.filtered(lambda i: i.has_outstanding is True): + if r.invoice_ids.filtered(lambda i: i.invoice_has_outstanding is True): r.has_outstanding = True else: r.has_outstanding = False else: r.has_outstanding = False - @api.multi def _compute_details(self): for r in self: contribs = r.invoice_ids.read_group( - [('id', 'in', r.invoice_ids.ids)], - ['type_contribution_id', 'amount_total_signed'], - ['type_contribution_id']) + [("id", "in", r.invoice_ids.ids)], + ["type_contribution_id", "amount_total_signed"], + ["type_contribution_id"], + ) detail = "<table class='o_group o_inner_group'>" for contrib in contribs: - detail += ('<tr><td class="o_td_label font-weight-bold">' - + str(contrib.get('type_contribution_id')[1]) - + '</td><td style="width: 100%;">' - + str( - contrib.get('amount_total_signed')) + - ' €</td></tr>') + detail += ( + '<tr><td class="o_td_label font-weight-bold">' + + str(contrib.get("type_contribution_id")[1]) + + '</td><td style="width: 100%;">' + + str(contrib.get("amount_total_signed")) + + " €</td></tr>" + ) detail += "</table><table class='o_group o_inner_group'>" for i in range(1, 5): # Add amount echeance - amount_echeance = round(sum(r.invoice_ids.filtered( - lambda inv: inv.cotiz_quarter == i - ).mapped('amount_total_signed')), 2) - detail += '<tr><td class="o_td_label font-weight-bold">' \ - + 'Trimestre ' + str(i) \ - + '</td><td style="width: 100%;">' \ - + str(amount_echeance) + ' €' + amount_echeance = round( + sum( + r.invoice_ids.filtered( + lambda inv: inv.cotiz_quarter == i + ).mapped("amount_total_signed") + ), + 2, + ) + detail += ( + '<tr><td class="o_td_label font-weight-bold">' + + "Trimestre " + + str(i) + + '</td><td style="width: 100%;">' + + str(amount_echeance) + + " €" + ) # Check if regul has been done after validation - type_contribs = r.invoice_ids.mapped('type_contribution_id') + type_contribs = r.invoice_ids.mapped("type_contribution_id") for type_contrib in type_contribs: - is_exo = len(r.invoice_ids.filtered( - lambda inv: - inv.cotiz_quarter == i and - inv.type_contribution_id == type_contrib - )) > 1 + is_exo = ( + len( + r.invoice_ids.filtered( + lambda inv: inv.cotiz_quarter == i + and inv.type_contribution_id == type_contrib + ) + ) + > 1 + ) if is_exo: - detail += ' ' + '<i class="fa fa-star-o" />' + detail += " " + '<i class="fa fa-star-o" />' break - detail += '</td></tr>' - detail += '</table>' + detail += "</td></tr>" + detail += "</table>" r.details = detail # ------------------------------------------------------ @@ -375,21 +403,27 @@ class Bordereau(models.Model): def validate_cotiz_cg(self): self.ensure_one() for inv in self.invoice_ids: - if inv.state == 'draft': - if not inv.date_invoice: - inv.update({ - 'date_invoice': self.date_cotisation, - }) - inv.update({ - 'payment_mode_id': self.payment_mode_id, - }) + if inv.state == "draft": + if not inv.invoice_date: + inv.update( + { + "invoice_date": self.date_cotisation, + } + ) + inv.update( + { + "payment_mode_id": self.payment_mode_id, + } + ) if self.is_sdd and not inv.mandate_id: raise UserError( - "Vous ne pouvez pas valider une cotisation au " - "prélèvement sans mandat" + _( + "Vous ne pouvez pas valider une cotisation au " + "prélèvement sans mandat" + ) ) else: - inv.action_invoice_open() + inv.action_post() def button_validate_bordereau(self): """ @@ -406,71 +440,69 @@ class Bordereau(models.Model): """ self.ensure_one() # Numéro de séquence - if self.name == 'Brouillon': - self.name = self.env['ir.sequence'].next_by_code('scop.bordereau') + if self.name == "Brouillon": + self.name = self.env["ir.sequence"].next_by_code("scop.bordereau") # Validation des invoices self.validate_cotiz_cg() # Envoi du mail if mail_compose_message_id: - mail_compose_message = self.env['mail.compose.message'].browse( + mail_compose_message = self.env["mail.compose.message"].browse( mail_compose_message_id ) - mail_compose_message.update({ - 'partner_ids': [(6, 0, self.recipient_ids.ids)], - 'res_id': self.id, - }) - mail_compose_message.with_context( - mail_notify_force_send=False - ).send_mail() + mail_compose_message.update( + { + "partner_ids": [(6, 0, self.recipient_ids.ids)], + "res_id": self.id, + } + ) + mail_compose_message.with_context(mail_notify_force_send=False).send_mail() return self.id - @api.multi def validate_bordereau_multi(self, mail_compose_message_id): """ Utilise job queue pour valider les bordereaux """ - batch_name = (fields.Datetime.to_string(fields.Datetime.now()) + - " Validation bordereaux ") - batch = self.env['queue.job.batch'].get_new_batch(batch_name) + batch_name = ( + fields.Datetime.to_string(fields.Datetime.now()) + " Validation bordereaux " + ) + batch = self.env["queue.job.batch"].get_new_batch(batch_name) for bordereau in self: - bordereau.with_context( - job_batch=batch - ).with_delay().validate_bordereau(mail_compose_message_id) + bordereau.with_context(job_batch=batch).with_delay().validate_bordereau( + mail_compose_message_id + ) batch.enqueue() - @api.multi def print_bordereau(self): for bordereau in self: return self.env.ref( - 'cgscop_cotisation_cg.cgscop_bordereau_report'). \ - report_action(bordereau) + "cgscop_cotisation_cg.cgscop_bordereau_report" + ).report_action(bordereau) - @api.multi def action_send_email(self): self.ensure_one() - template_id = self.env.ref( - 'cgscop_cotisation_cg.email_template_cotisation_cg') - ir_model_data = self.env['ir.model.data'] + template_id = self.env.ref("cgscop_cotisation_cg.email_template_cotisation_cg") + ir_model_data = self.env["ir.model.data"] try: compose_form_id = ir_model_data.get_object_reference( - 'mail', 'email_compose_message_wizard_form')[1] + "mail", "email_compose_message_wizard_form" + )[1] except ValueError: compose_form_id = False ctx = { - 'default_model': 'scop.bordereau', - 'default_res_id': self.id, - 'default_use_template': True, - 'default_template_id': template_id.id, + "default_model": "scop.bordereau", + "default_res_id": self.id, + "default_use_template": True, + "default_template_id": template_id.id, } return { - 'type': 'ir.actions.act_window', - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'mail.compose.message', - 'views': [(compose_form_id, 'form')], - 'view_id': compose_form_id, - 'target': 'new', - 'context': ctx, + "type": "ir.actions.act_window", + "view_type": "form", + "view_mode": "form", + "res_model": "mail.compose.message", + "views": [(compose_form_id, "form")], + "view_id": compose_form_id, + "target": "new", + "context": ctx, } def update_bordereau_with_liasse(self): @@ -481,39 +513,34 @@ class Bordereau(models.Model): """ self.create_cotiz_and_lines() - @api.multi def open_payment(self): action_context = { - 'company_ids': [self.company_id.id], - 'partner_ids': [ - self.partner_id.commercial_partner_id.id], - 'mode': 'customers' + "company_ids": [self.company_id.id], + "partner_ids": [self.partner_id.commercial_partner_id.id], + "mode": "customers", } return { - 'type': 'ir.actions.client', - 'tag': 'manual_reconciliation_view', - 'context': action_context, + "type": "ir.actions.client", + "tag": "manual_reconciliation_view", + "context": action_context, } def action_show_cotiz(self): """ :return: Ouvre la vue des cotisations liées au bordereau """ - tree_view_id = self.env.ref( - 'cgscop_cotisation.invoice_tree_scop_inherited').id + tree_view_id = self.env.ref("cgscop_cotisation.invoice_tree_scop_inherited").id form_view_id = self.env.ref( - 'cgscop_cotisation_cg.invoice_form_scop_cg_inherited').id + "cgscop_cotisation_cg.invoice_form_scop_cg_inherited" + ).id return { - 'type': 'ir.actions.act_window', - 'name': 'Appels de cotisations', - 'views': [ - [tree_view_id, "tree"], - [form_view_id, "form"] - ], - 'view_mode': 'form', - 'res_model': 'account.invoice', - 'target': 'current', - 'domain': [('id', 'in', self.invoice_ids.ids)], + "type": "ir.actions.act_window", + "name": "Appels de cotisations", + "views": [[tree_view_id, "tree"], [form_view_id, "form"]], + "view_mode": "form", + "res_model": "account.invoice", + "target": "current", + "domain": [("id", "in", self.invoice_ids.ids)], } def action_show_liasse(self): @@ -521,52 +548,51 @@ class Bordereau(models.Model): :return: Ouvre la vue des liasses liées au partenaire """ return { - 'type': 'ir.actions.act_window', - 'name': 'Liasses fiscales - ' + self.partner_id.name, - 'views': [ - [False, "tree"], - [False, "form"] - ], - 'view_mode': 'form', - 'res_model': 'scop.liasse.fiscale', - 'target': 'current', - 'domain': [('partner_id', '=', self.partner_id.id)], + "type": "ir.actions.act_window", + "name": "Liasses fiscales - " + self.partner_id.name, + "views": [[False, "tree"], [False, "form"]], + "view_mode": "form", + "res_model": "scop.liasse.fiscale", + "target": "current", + "domain": [("partner_id", "=", self.partner_id.id)], } def action_change_liasse(self): """ :return: Ouvre le wizard pour changer la liasse """ - wizard = self.env['scop.bordereau.change.liasse.wizard']. \ - create({ - 'bordereau_id': self.id, - }) + wizard = self.env["scop.bordereau.change.liasse.wizard"].create( + { + "bordereau_id": self.id, + } + ) return { - 'type': 'ir.actions.act_window', - 'name': 'Changer la liasse', - 'views': [[False, "form"]], - 'view_mode': 'form', - 'res_model': 'scop.bordereau.change.liasse.wizard', - 'target': 'new', - 'res_id': wizard.id + "type": "ir.actions.act_window", + "name": "Changer la liasse", + "views": [[False, "form"]], + "view_mode": "form", + "res_model": "scop.bordereau.change.liasse.wizard", + "target": "new", + "res_id": wizard.id, } def action_change_payment_mode(self): """ :return: Ouvre le wizard pour changer le mode de paiement """ - wizard = self.env['scop.bordereau.change.payment.mode.wizard']. \ - create({ - 'bordereau_id': self.id, - }) + wizard = self.env["scop.bordereau.change.payment.mode.wizard"].create( + { + "bordereau_id": self.id, + } + ) return { - 'type': 'ir.actions.act_window', - 'name': 'Changer le mode de paiement', - 'views': [[False, "form"]], - 'view_mode': 'form', - 'res_model': 'scop.bordereau.change.payment.mode.wizard', - 'target': 'new', - 'res_id': wizard.id + "type": "ir.actions.act_window", + "name": "Changer le mode de paiement", + "views": [[False, "form"]], + "view_mode": "form", + "res_model": "scop.bordereau.change.payment.mode.wizard", + "target": "new", + "res_id": wizard.id, } def validate_ongoing_bordereau(self): @@ -577,16 +603,20 @@ class Bordereau(models.Model): """ self.ensure_one() ongoing_version = self.check_ongoing_version() - self.write({ - 'name': self.name[0:10] + '-' + str(self.version + 1), - 'version': self.version + 1, - 'date_regul': ongoing_version.date, - 'comment_regul': ongoing_version.comment, - }) + self.write( + { + "name": self.name[0:10] + "-" + str(self.version + 1), + "version": self.version + 1, + "date_regul": ongoing_version.date, + "comment_regul": ongoing_version.comment, + } + ) self.validate_bordereau() - ongoing_version.write({ - 'state': 'validated', - }) + ongoing_version.write( + { + "state": "validated", + } + ) def cancel_ongoing_bordereau(self): """ @@ -595,29 +625,26 @@ class Bordereau(models.Model): - Supprime le versionnage en cours """ self.ensure_one() - self.invoice_ids.filtered( - lambda i: i.state == 'draft' - ).unlink() + self.invoice_ids.filtered(lambda i: i.state == "draft").unlink() ongoing_version = self.check_ongoing_version() - self.write({ - 'liasse_fiscale_id': ongoing_version.liasse_fiscale_id_old.id - }) - self.invoice_ids.update({ - 'liasse_fiscale_id': ongoing_version.liasse_fiscale_id_old.id - }) + self.write({"liasse_fiscale_id": ongoing_version.liasse_fiscale_id_old.id}) + self.invoice_ids.update( + {"liasse_fiscale_id": ongoing_version.liasse_fiscale_id_old.id} + ) ongoing_version.unlink() # ------------------------------------------------------ # Override ORM # ------------------------------------------------------ - @api.multi def unlink(self): - bordereau_not_new = self.filtered(lambda b: b.state != 'new') + bordereau_not_new = self.filtered(lambda b: b.state != "new") if bordereau_not_new: raise UserError( - 'Vous ne pouvez pas passer supprimer un bordereau qui a ' - 'déjà été validé.\n Vous pouvez par contre créer des ' - 'avoirs sur les appels de cotisations.' + _( + "Vous ne pouvez pas passer supprimer un bordereau qui a " + "déjà été validé.\n Vous pouvez par contre créer des " + "avoirs sur les appels de cotisations." + ) ) else: return super(Bordereau, self).unlink() @@ -630,7 +657,6 @@ class Bordereau(models.Model): - Supprime les factures existantes si besoin - Crée les appels de cotisation par trimestre """ - if self.invoice_ids: self.invoice_ids.unlink() @@ -641,104 +667,119 @@ class Bordereau(models.Model): # Invoice cotiz CG # product_cg_id = self.company_id.contribution_cg_id - type_cotisation_cg = self.env.ref('cgscop_partner.riga_14397').id + type_cotisation_cg = self.env.ref("cgscop_partner.riga_14397").id if liasse: - amount_cg = base_cotiz.round_to_closest_multiple( - liasse.contribution_cg, 4) + amount_cg = base_cotiz.round_to_closest_multiple(liasse.contribution_cg, 4) else: - amount_cg = self.env['scop.liasse.fiscale']. \ - get_values_for_cotiz_cg(partner)['plancher1'] + amount_cg = self.env["scop.liasse.fiscale"].get_values_for_cotiz_cg( + partner + )["plancher1"] self.create_contribution( - product_cg_id, type_cotisation_cg, amount_cg + product=product_cg_id, + type_contribution=type_cotisation_cg, + amount=amount_cg, ) - self.env.cr.commit() # Invoice UR et Fédé - type_cotisation_ur = self.env.ref('cgscop_partner.riga_14399').id - journal_ur_or_fede = self.company_id.contribution_ur_or_fede_journal_id + type_cotisation_ur = self.env.ref("cgscop_partner.riga_14399").id # Invoice cotiz Fede Com # if partner.is_federation_com: product_fede_com_id = self.company_id.contribution_fede_com_id - account_id = self.company_id.receivable_account_fede_com_id - type_cotisation_fede_com = self.env.ref( - 'cgscop_partner.riga_14398').id + journal_fede_com_id = self.company_id.journal_fede_com_id + type_cotisation_fede_com = self.env.ref("cgscop_partner.riga_14398").id if liasse: amount_fede_com = base_cotiz.round_to_closest_multiple( - liasse.contribution_com, 4) + liasse.contribution_com, 4 + ) else: - amount_fede_com = self.env['scop.liasse.fiscale']. \ - get_plancher_cotiz()['fede_com'] + amount_fede_com = self.env["scop.liasse.fiscale"].get_plancher_cotiz()[ + "fede_com" + ] self.create_contribution( - product_fede_com_id, type_cotisation_fede_com, amount_fede_com, - journal_ur_or_fede, account_id + product=product_fede_com_id, + type_contribution=type_cotisation_fede_com, + amount=amount_fede_com, + journal_id=journal_fede_com_id, ) - self.env.cr.commit() # Invoice cotiz Fede CAE # if partner.cae: product_fede_cae_id = self.company_id.contribution_fede_cae_id - account_id = self.company_id.receivable_account_fede_cae_id - type_cotisation_fede_cae = self.env.ref( - 'cgscop_partner.cotiz_fede_cae').id + journal_fede_cae_id = self.company_id.journal_fede_cae_id + type_cotisation_fede_cae = self.env.ref("cgscop_partner.cotiz_fede_cae").id if liasse: amount_fede_cae = base_cotiz.round_to_closest_multiple( - liasse.contribution_cae, 4) + liasse.contribution_cae, 4 + ) else: - amount_fede_cae = self.env['scop.liasse.fiscale']. \ - get_plancher_cotiz()['fede_cae'] + amount_fede_cae = self.env["scop.liasse.fiscale"].get_plancher_cotiz()[ + "fede_cae" + ] self.create_contribution( - product_fede_cae_id, type_cotisation_fede_cae, amount_fede_cae, - journal_ur_or_fede, account_id + product=product_fede_cae_id, + type_contribution=type_cotisation_fede_cae, + amount=amount_fede_cae, + journal_id=journal_fede_cae_id, ) - self.env.cr.commit() # Invoice cotiz UR HDF # - ur_hdf = self.env.ref('cgscop_partner.riga_14232').id + ur_hdf = self.env.ref("cgscop_partner.riga_14232").id if partner.ur_id.id == ur_hdf: product_hdf_id = self.company_id.contribution_hdf_id - account_id = self.company_id.receivable_account_ur_hdf_id + journal_hdf_id = self.company_id.journal_ur_hdf_id if liasse: amount_hdf = base_cotiz.round_to_closest_multiple( - liasse.contribution_hdf, 4) + liasse.contribution_hdf, 4 + ) else: - amount_hdf = self.env['scop.liasse.fiscale']. \ - get_plancher_cotiz()['ur_hdf'] + amount_hdf = self.env["scop.liasse.fiscale"].get_plancher_cotiz()[ + "ur_hdf" + ] self.create_contribution( - product_hdf_id, type_cotisation_ur, amount_hdf, - journal_ur_or_fede, account_id + product=product_hdf_id, + type_contribution=type_cotisation_ur, + amount=amount_hdf, + journal_id=journal_hdf_id, ) - self.env.cr.commit() # Invoice cotiz UR Med # - ur_med = self.env.ref('cgscop_partner.riga_14243').id + ur_med = self.env.ref("cgscop_partner.riga_14243").id if partner.ur_id.id == ur_med: product_med_id = self.company_id.contribution_med_id - account_id = self.company_id.receivable_account_ur_med_id + journal_ur_med_id = self.company_id.journal_ur_med_id if liasse: amount_med = base_cotiz.round_to_closest_multiple( - liasse.contribution_med, 4) + liasse.contribution_med, 4 + ) else: - amount_med = self.env['scop.liasse.fiscale']. \ - get_plancher_cotiz()['ur_med'] + amount_med = self.env["scop.liasse.fiscale"].get_plancher_cotiz()[ + "ur_med" + ] self.create_contribution( - product_med_id, type_cotisation_ur, amount_med, - journal_ur_or_fede, account_id + product=product_med_id, + type_contribution=type_cotisation_ur, + amount=amount_med, + journal_id=journal_ur_med_id, ) - self.env.cr.commit() def create_contribution( - self, product, type_contribution, amount=0, - journal_id=False, account_id=False, type_invoice='out_invoice'): + self, + product, + type_contribution, + amount=0, + journal_id=False, + type_invoice="out_invoice", + ): """ Create invoice from Contribution Base :param product: product_id @@ -749,100 +790,117 @@ class Bordereau(models.Model): :param type_invoice: invoice or refund :return: invoice """ - Invoice = self.env['account.invoice'] - InvoiceLine = self.env['account.invoice.line'] + Invoice = self.env["account.move"] partner = self.partner_id liasse = self.liasse_fiscale_id base_cotiz = self.base_cotisation_cg - quarters = [base_cotiz.trimester_1, base_cotiz.trimester_2, - base_cotiz.trimester_3, base_cotiz.trimester_4] - type_contrib_name = self.env['scop.contribution.type'].browse( - type_contribution).name + quarters = [ + base_cotiz.trimester_1, + base_cotiz.trimester_2, + base_cotiz.trimester_3, + base_cotiz.trimester_4, + ] + type_contrib_name = ( + self.env["scop.contribution.type"].browse(type_contribution).name + ) # TODO: Specific CAE calculation : to be deleted when 2022 is generated - type_contrib_cae = self.env.ref('cgscop_partner.cotiz_fede_cae').id - if type_contribution == type_contrib_cae and self.year == '2022': + type_contrib_cae = self.env.ref("cgscop_partner.cotiz_fede_cae").id + if type_contribution == type_contrib_cae and self.year == "2022": amount = ((amount / 4) * 3) + 25 # ... End specific CAE 2022 - for i in range(1, self.nb_quarter + 1): - cotiz_quarter = (4 - self.nb_quarter) + i - - journal_id = self.company_id.contribution_journal_id \ - if not journal_id else journal_id - account_id = partner.property_account_receivable_id \ - if not account_id else account_id - member_invoice = Invoice.create({ - 'partner_id': partner.id, - 'liasse_fiscale_id': liasse.id, - 'type': type_invoice, - 'year': self.year, - 'is_contribution': True, - 'type_contribution_id': type_contribution, - 'name': 'T' + str(cotiz_quarter) + ' ' + self.year + - ' - ' + type_contrib_name, - 'journal_id': journal_id.id, - 'state': 'draft', - 'account_id': account_id.id, - 'payment_mode_id': partner.customer_payment_mode_id.id, - 'date_invoice': self.date_cotisation, - 'date_due': quarters[cotiz_quarter - 1], - 'bordereau_id': self.id, - 'amount_cg_calculated': amount, - 'cotiz_quarter': cotiz_quarter, - }) - - # Création de la ligne de cotisation - price_unit = amount / 4 + for i in range(1, int(self.nb_quarter) + 1): + cotiz_quarter = (4 - int(self.nb_quarter)) + i + journal_id = ( + self.company_id.contribution_journal_id + if not journal_id + else journal_id + ) + # Calcul prix + price_unit = amount / 4 # TODO: Specific CAE calculation : to be deleted when 2022 is generated - if type_contribution == type_contrib_cae and self.year == '2022': + if type_contribution == type_contrib_cae and self.year == "2022": if cotiz_quarter == 1: price_unit = 25 else: price_unit = (amount - 25) / 3 # ... End specific CAE 2022 - InvoiceLine.create({ - 'invoice_id': member_invoice.id, - 'product_id': product.id, - 'account_id': product.property_account_income_id.id, - 'invoice_line_tax_ids': [(6, 0, product.taxes_id.ids)], - 'name': product.name + " T" + str(cotiz_quarter), - 'price_unit': price_unit - }) + Invoice.create( + { + "partner_id": partner.id, + "liasse_fiscale_id": liasse.id, + "move_type": type_invoice, + "year": self.year, + "is_contribution": True, + "type_contribution_id": type_contribution, + "ref": ( + "T" + + str(cotiz_quarter) + + " " + + self.year + + " - " + + type_contrib_name + ), + "journal_id": journal_id.id, + "state": "draft", + "payment_mode_id": partner.customer_payment_mode_id.id, + "invoice_date": self.date_cotisation, + "invoice_date_due": quarters[cotiz_quarter - 1], + "bordereau_id": self.id, + "amount_cg_calculated": amount, + "cotiz_quarter": str(cotiz_quarter), + "invoice_line_ids": [ + ( + 0, + None, + { + "product_id": product.id, + "account_id": product.property_account_income_id.id, + "tax_ids": [(6, 0, product.taxes_id.ids)], + "name": product.name + " T" + str(cotiz_quarter), + "price_unit": price_unit, + "price_subtotal": price_unit, + }, + ), + ], + } + ) def check_ongoing_version(self): """ :return: le versionnage en cours """ ongoing_version = self.bordereau_version_ids.filtered( - lambda v: v.version == self.version and v.state == 'new' + lambda v: v.version == self.version and v.state == "new" ) if len(ongoing_version) != 1: - raise ValueError('Erreur sur le versionnage de ce bordereau') + raise ValueError(_("Erreur sur le versionnage de ce bordereau")) else: return ongoing_version - @api.multi def get_bordereau_move_line(self): """ Get move line for invoices in bordereau :return: dict with date as key and amount (float) as value """ - MoveLine = self.env['account.move.line'] + # TODO: voir si possible d'optimiser avec le nouveau modèle + MoveLine = self.env["account.move.line"] for bordereau in self: inv_ids = bordereau.invoice_ids if bordereau.is_regul: - inv_ids = inv_ids.filtered( - lambda i: i.type == 'out_invoice') - move_lines = MoveLine.search([ - ('invoice_id', 'in', inv_ids.ids), - ('account_id.internal_type', '=', 'receivable')], - order='date_maturity', + inv_ids = inv_ids.filtered(lambda i: i.move_type == "out_invoice") + move_lines = MoveLine.search( + [ + ("move_id", "in", inv_ids.ids), + ("account_id.internal_type", "=", "receivable"), + ], + order="date_maturity", ) - schedule = list(dict.fromkeys(move_lines.mapped('date_maturity'))) + schedule = list(dict.fromkeys(move_lines.mapped("date_maturity"))) schedule.sort() schedule_plan = [] for date in schedule: @@ -850,39 +908,43 @@ class Bordereau(models.Model): for item in move_lines: if item.date_maturity == date: amount += item.debit - item.credit - schedule_plan.append({ - 'date': date.strftime('%d/%m/%Y'), - 'amount': amount, - }) + schedule_plan.append( + { + "date": date.strftime("%d/%m/%Y"), + "amount": amount, + } + ) return schedule_plan - @api.multi def get_bordereau_move_line_with_payments(self): """ Get payment dates for invoices in bordereau :return: dict with date as key and amount (float) as value """ - MoveLine = self.env['account.move.line'] + # TODO: voir si possible d'optimiser avec le nouveau modèle + MoveLine = self.env["account.move.line"] for bordereau in self: inv_ids = bordereau.invoice_ids.ids schedule_plan = MoveLine.read_group( - [('invoice_id', 'in', inv_ids), - ('account_id.internal_type', '=', 'receivable')], - fields=['date_maturity', 'credit', 'debit', 'amount_residual'], - groupby=['date_maturity:day'], - orderby='date_maturity', - lazy=False + [ + ("move_id", "in", inv_ids), + ("account_id.internal_type", "=", "receivable"), + ], + fields=["date_maturity", "credit", "debit", "amount_residual"], + groupby=["date_maturity:day"], + orderby="date_maturity", + lazy=False, + ) + total_amount = sum(list(map(lambda l: l.get("debit"), schedule_plan))) + total_residual = sum( + list(map(lambda l: l.get("amount_residual"), schedule_plan)) ) - total_amount = sum(list(map( - lambda l: l.get('debit'), schedule_plan))) - total_residual = sum(list(map( - lambda l: l.get('amount_residual'), schedule_plan))) total_paid = total_amount - total_residual return { - 'plan': schedule_plan, - 'total_amount': total_amount, - 'total_paid': total_paid, - 'total_residual': total_residual + "plan": schedule_plan, + "total_amount": total_amount, + "total_paid": total_paid, + "total_residual": total_residual, } def get_contribution_type(self): @@ -890,13 +952,14 @@ class Bordereau(models.Model): Get contribution by type :return: dict type_contribution and amount """ - domain = [('id', 'in', self.invoice_ids.ids)] + domain = [("id", "in", self.invoice_ids.ids)] if self.is_regul: - domain.append(('type', '=', 'out_invoice')) + domain.append(("move_type", "=", "out_invoice")) return self.invoice_ids.read_group( domain, - ['type_contribution_id', 'amount_total_signed'], - ['type_contribution_id']) + ["type_contribution_id", "amount_total_signed"], + ["type_contribution_id"], + ) def get_contribution_type_refund(self): """ @@ -904,11 +967,12 @@ class Bordereau(models.Model): :return: dict type_contribution and amount """ return self.invoice_ids.read_group( - [('id', 'in', self.invoice_ids.ids), ('type', '=', 'out_refund')], - ['type_contribution_id', 'amount_total_signed'], - ['type_contribution_id']) + [("id", "in", self.invoice_ids.ids), ("move_type", "=", "out_refund")], + ["type_contribution_id", "amount_total_signed"], + ["type_contribution_id"], + ) # Email def get_recipients(self): - recipients = ','.join(map(lambda x: str(x), self.recipient_ids.ids)) + recipients = ",".join(map(lambda x: str(x), self.recipient_ids.ids)) return recipients diff --git a/models/scop_bordereau_cg_version.py b/models/scop_bordereau_cg_version.py index ef5e321..1ba4d09 100644 --- a/models/scop_bordereau_cg_version.py +++ b/models/scop_bordereau_cg_version.py @@ -1,58 +1,70 @@ # © 2021 Le Filament (<http://www.le-filament.com>) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from datetime import timedelta -from odoo import fields, models, api +from odoo import _, api, fields, models from odoo.exceptions import ValidationError class BordereauVersion(models.Model): - _name = 'scop.bordereau.version' - _description = 'Historique des versions des bordereaux des cotisations CG' + _name = "scop.bordereau.version" + _description = "Historique des versions des bordereaux des cotisations CG" bordereau_id = fields.Many2one( - comodel_name='scop.bordereau', - string='Bordereau de rattachement', + comodel_name="scop.bordereau", + string="Bordereau de rattachement", required=True, readonly=True, - ondelete='cascade') - version = fields.Integer('Version du bordereau', readonly=True) - comment = fields.Char(string='Motif') + ondelete="cascade", + ) + version = fields.Integer("Version du bordereau", readonly=True) + comment = fields.Char(string="Motif") liasse_fiscale_id_old = fields.Many2one( - comodel_name='scop.liasse.fiscale', - string='Ancienne liasse fiscale', - ondelete='set null', - readonly=True + comodel_name="scop.liasse.fiscale", + string="Ancienne liasse fiscale", + ondelete="set null", + readonly=True, ) type_assiette = fields.Selection( - string='Ancien type d\'assiette', - selection=[('ca', 'CA'), - ('va', 'VA'), ], - readonly=True) - date = fields.Date('Date', readonly=True) - montant_assiette = fields.Integer( - string='Ancien montant assiette', readonly=True) - company_currency_id = fields.Many2one( - related='bordereau_id.company_currency_id') + string="Ancien type d'assiette", + selection=[ + ("ca", "CA"), + ("va", "VA"), + ], + readonly=True, + ) + date = fields.Date("Date", readonly=True) + montant_assiette = fields.Integer(string="Ancien montant assiette", readonly=True) + company_currency_id = fields.Many2one(related="bordereau_id.company_currency_id") amount_total_cotiz = fields.Monetary( - string='Ancien montant cotisation', - currency_field='company_currency_id', readonly=True) - state = fields.Selection([ - ('new', 'Brouillon'), - ('validated', 'Validée')], + string="Ancien montant cotisation", + currency_field="company_currency_id", + readonly=True, + ) + state = fields.Selection( + [("new", "Brouillon"), ("validated", "Validée")], string="Statut", - default='new', readonly=True) + default="new", + readonly=True, + ) # ------------------------------------------------------ # Constrain Functions # ------------------------------------------------------ - @api.constrains('state') + @api.constrains("state") def check_unique_state_new(self): - """ Une seule version en brouillon par bordereau """ - if len(self.bordereau_id.bordereau_version_ids.filtered( - lambda v: v.state == 'new' - )) > 1: + """Une seule version en brouillon par bordereau""" + if ( + len( + self.bordereau_id.bordereau_version_ids.filtered( + lambda v: v.state == "new" + ) + ) + > 1 + ): raise ValidationError( - 'Vous devez valider ou annuler les versions de bordereaux en ' - 'cours de modification avant de créer une nouvelle version !') + _( + "Vous devez valider ou annuler les versions de bordereaux en " + "cours de modification avant de créer une nouvelle version !" + ) + ) diff --git a/models/scop_cotisation_cg.py b/models/scop_cotisation_cg.py index 64b10fb..9c52596 100644 --- a/models/scop_cotisation_cg.py +++ b/models/scop_cotisation_cg.py @@ -4,11 +4,12 @@ import base64 import json import logging -import xlsxwriter -from io import BytesIO from datetime import datetime +from io import BytesIO -from odoo import models, fields, api, exceptions +import xlsxwriter + +from odoo import _, api, exceptions, fields, models _logger = logging.getLogger(__name__) @@ -18,61 +19,62 @@ class ScopCotisation(models.Model): _name = "scop.cotisation.cg" _description = "Base des cotisations CG" - name = fields.Char( - string='Nom', - compute='_compute_name', - store=True) + name = fields.Char(string="Nom", compute="_compute_name", store=True) simul_ids = fields.One2many( - comodel_name='scop.cotisation.cg.simulation', - inverse_name='cotisation_id', - string='Simulations') + comodel_name="scop.cotisation.cg.simulation", + inverse_name="cotisation_id", + string="Simulations", + ) invoice_ids = fields.One2many( - comodel_name='account.invoice', - inverse_name='cotisation_cg_id', - string='Factures', + comodel_name="account.move", + inverse_name="cotisation_cg_id", + string="Factures", domain=[ - ('type', 'in', ('out_invoice', 'out_refund')), - ('is_contribution', '=', True) - ]) + ("move_type", "in", ("out_invoice", "out_refund")), + ("is_contribution", "=", True), + ], + ) bordereau_ids = fields.One2many( - comodel_name='scop.bordereau', - inverse_name='base_cotisation_cg', - string='Bordereau', ) - - state = fields.Selection([ - ('new', 'Brouillon'), - ('ongoing', 'En cours'), - ('end', 'Clôturé')], - string="Statut", default='new', - compute='_compute_state', - store=True, track_visibility='onchange', ) + comodel_name="scop.bordereau", + inverse_name="base_cotisation_cg", + string="Bordereau", + ) + + state = fields.Selection( + [("new", "Brouillon"), ("ongoing", "En cours"), ("end", "Clôturé")], + string="Statut", + default="new", + compute="_compute_state", + store=True, + ) invoice_count = fields.Integer( - string='Appels de cotisations émis', - compute='_compute_real') + string="Appels de cotisations émis", compute="_compute_real" + ) invoice_valid_count = fields.Integer( - string='Appels de cotisations validés', - compute='_compute_real') + string="Appels de cotisations validés", compute="_compute_real" + ) amount_called = fields.Monetary( - 'Montant appelé', - compute='_compute_amount_called', - currency_field='company_currency_id') + "Montant appelé", + compute="_compute_amount_called", + currency_field="company_currency_id", + ) amount_residual = fields.Monetary( - 'Montant restant à réglé', - compute='_compute_amount_residual', - currency_field='company_currency_id') + "Montant restant à réglé", + compute="_compute_amount_residual", + currency_field="company_currency_id", + ) amount_paid = fields.Monetary( - 'Montant payé', - compute='_compute_amount_paid', - currency_field='company_currency_id') + "Montant payé", + compute="_compute_amount_paid", + currency_field="company_currency_id", + ) - percent_cotiz_paid = fields.Float( - "% Payées", - compute="_compute_percent_cotiz_paid") + percent_cotiz_paid = fields.Float("% Payées", compute="_compute_percent_cotiz_paid") graph_values = fields.Text(compute="_compute_graph_values") @@ -81,44 +83,46 @@ class ScopCotisation(models.Model): # ------------------------------------------------------ # Unicité d'année' (company - année) _sql_constraints = [ - ('contribution_unique', - 'unique(year, company_id)', - "La gestion des cotisations pour cette année existe déjà"), + ( + "contribution_unique", + "unique(year, company_id)", + "La gestion des cotisations pour cette année existe déjà", + ), ] # ------------------------------------------------------ # Compute fields # ------------------------------------------------------ - @api.depends('year', 'state') - @api.multi + @api.depends("year", "state") def _compute_name(self): for cotiz in self: - cotiz.name = "Cotisations %d - %s" % ( + cotiz.name = "Cotisations %s - %s" % ( cotiz.year, - dict(self._fields['state'].selection).get(cotiz.state)) + dict(self._fields["state"].selection).get(cotiz.state), + ) - @api.multi def _compute_amount_called(self): for cotiz in self: - cotiz.amount_called = sum(cotiz.invoice_ids.filtered( - lambda i: i.state != 'draft').mapped("amount_total")) + cotiz.amount_called = sum( + cotiz.invoice_ids.filtered(lambda i: i.state == "posted").mapped( + "amount_total_signed" + ) + ) - @api.multi def _compute_amount_residual(self): for cotiz in self: cotiz.amount_residual = sum( - cotiz.invoice_ids.mapped("residual_signed")) + cotiz.invoice_ids.mapped("amount_residual_signed") + ) - @api.multi def _compute_amount_paid(self): """ montant déjà payé = différence entre le reste à payer du total validé, le reste à payer étant égal à 0 pour les factures en brouillon - """ + """ for cotiz in self: cotiz.amount_paid = cotiz.amount_called - cotiz.amount_residual - @api.multi def _compute_percent_cotiz_paid(self): for cotiz in self: if cotiz.amount_called != 0: @@ -133,48 +137,59 @@ class ScopCotisation(models.Model): cotiz.percent_cotiz_paid = 0 @api.depends( - 'invoice_ids', - 'invoice_ids.state', - 'invoice_ids.amount_total', + "invoice_ids", + "invoice_ids.state", + "invoice_ids.amount_total_signed", ) - @api.multi def _compute_real(self): for cotiz in self: cotiz.invoice_count = len(cotiz.invoice_ids) - cotiz.invoice_valid_count = len(cotiz.invoice_ids.filtered( - lambda i: i.state in ('open', 'paid'))) + cotiz.invoice_valid_count = len( + cotiz.invoice_ids.filtered(lambda i: i.state == "posted") + ) - @api.multi def _compute_graph_values(self): for cotiz in self: - draft_count = len(cotiz.invoice_ids.filtered( - lambda i: i.state == 'draft')) - opened_count = len(cotiz.invoice_ids.filtered( - lambda i: i.state == 'open')) - paid_count = len(cotiz.invoice_ids.filtered( - lambda i: i.state == 'paid')) - cotiz.graph_values = json.dumps([{ - 'values': [ - {'label': 'Brouillons', 'value': draft_count}, - {'label': 'Ouverts', 'value': opened_count}, - {'label': 'Payés', 'value': paid_count}, - ], - 'area': True, - 'title': '', - 'key': 'Cotisations', - }]) + draft_count = len(cotiz.invoice_ids.filtered(lambda i: i.state == "draft")) + opened_count = len( + cotiz.invoice_ids.filtered( + lambda i: i.state == "posted" + and i.payment_state not in ("paid", "reversed") + ) + ) + paid_count = len( + cotiz.invoice_ids.filtered( + lambda i: i.state == "posted" and i.payment_state == "paid" + ) + ) + cotiz.graph_values = json.dumps( + [ + { + "values": [ + {"label": "Brouillons", "value": draft_count}, + {"label": "Ouverts", "value": opened_count}, + {"label": "Payés", "value": paid_count}, + ], + "area": True, + "title": "", + "key": "Cotisations", + } + ] + ) - @api.depends('invoice_ids', 'invoice_ids.state') + @api.depends("invoice_ids", "invoice_ids.state") def _compute_state(self): for cotiz in self: - if len(cotiz.invoice_ids) == 0 and cotiz.state != 'new': - cotiz.state = 'new' - elif len(cotiz.invoice_ids) == len(cotiz.invoice_ids.filtered( - lambda i: i.state in ('paid', 'cancel'))) \ - and cotiz.state != 'end': - cotiz.state = 'end' - elif cotiz.state != 'ongoing': - cotiz.state = 'ongoing' + if len(cotiz.invoice_ids) == 0: + cotiz.state = "new" + elif ( + len(cotiz.invoice_ids) + == len(cotiz.invoice_ids.filtered(lambda i: i.state == "posted")) + and cotiz.state != "end" + ): + cotiz.state = "end" + elif cotiz.state != "ongoing": + cotiz.state = "ongoing" # ------------------------------------------------------ # Button functions @@ -183,138 +198,181 @@ class ScopCotisation(models.Model): """ Open wizard to generate cotisations for renew members """ - if not self.env.user.company_id.is_contribution_cg: + if not self.env.company.is_contribution_cg: + raise exceptions.UserError(_("La cotisation CG Scop n'est pas configurée.")) + + if ( + not self.trimester_1 + or not self.trimester_2 + or not self.trimester_3 + or not self.trimester_4 + ): raise exceptions.UserError( - "La cotisation CG Scop n'est pas configurée.") - - if not self.trimester_1 or not self.trimester_2 or not self.trimester_3 or not self.trimester_4: - raise exceptions.UserError( - "Les trimestres doivent être configurés pour lancer les cotisations.") + _("Les trimestres doivent être configurés pour lancer les cotisations.") + ) if not self.date_cotisation: raise exceptions.UserError( - "La date de calcul des cotisations doit être renseignée pour lancer les cotisations.") + _( + "La date de calcul des cotisations doit être renseignée pour " + "lancer les cotisations." + ) + ) # List of members already invoiced - member_invoiced = self.invoice_ids.mapped('partner_id') + member_invoiced = self.invoice_ids.mapped("partner_id") # List of members members = self.get_members() # List of not invoiced members - members_to_invoice = (members - member_invoiced) + members_to_invoice = members - member_invoiced if len(members_to_invoice) > 0: message = ( - "<h3>Appels de cotisation " + str(self.year) + - "</h3> <hr/>" + - "Nombre d'adhérents renouvelés : " + - str(self.member_count) + - "<br/>Bordereaux déjà générées non vides : " + - str(self.invoiced_member_count) + - "<br/>Bordereaux à générer : " + - str(len(members_to_invoice)) + - "<p>Les appels de cotisation vont être générés.</p> " + "<h3>Appels de cotisation " + + self.year + + "</h3> <hr/>" + + "Nombre d'adhérents renouvelés : " + + str(self.member_count) + + "<br/>Bordereaux déjà générées non vides : " + + str(self.invoiced_member_count) + + "<br/>Bordereaux à générer : " + + str(len(members_to_invoice)) + + "<p>Les appels de cotisation vont être générés.</p> " ) - message_id = self.env['message.wizard'].create({ - 'message': message, - 'cancel_button': True, - 'action': 'action_cotiz_generate(' - + str(members_to_invoice.ids) + ')', - }) + return { + "type": "ir.actions.act_window.message", + "title": _("Génération des appels de cotisation annuels"), + "is_html_message": True, + "message": _(message), + "close_button_title": False, + "buttons": [ + { + "type": "method", + "name": "Lancer le calcul", + "model": self._name, + "method": "action_cotiz_generate", + "args": [self.id, members_to_invoice.ids], + "classes": "btn-primary", + }, + { + "type": "ir.actions.act_window_close", + "name": "Fermer", + }, + ], + } else: - message = ("<p class='text-center'>Tous les appels de " + - "cotisations annuels ont déjà été créés !</p>") - message_id = self.env['message.wizard'].create( - {'message': message}) - - return { - 'name': 'Génération des appels de cotisation annuels', - 'type': 'ir.actions.act_window', - 'view_mode': 'form', - 'res_model': 'message.wizard', - 'res_id': message_id.id, - 'target': 'new' - } + message = ( + "<p class='text-center'>Tous les appels de " + + "cotisations annuels ont déjà été créés !</p>" + ) + return { + "type": "ir.actions.act_window.message", + "title": _("Génération des appels de cotisation annuels"), + "is_html_message": True, + "message": _(message), + "close_button_title": False, + "buttons": [ + { + "type": "ir.actions.act_window_close", + "name": "Fermer", + }, + ], + } - def action_cotiz_generate(self, *ids): + def action_cotiz_generate(self, member_ids): """ - Function to be used by message.wizard in "ok" action + Create contribution for members + :params member_ids: list of member ids """ - members_to_invoice = self.env['res.partner'].browse(*ids) - + member_to_invoice = self.env["res.partner"].browse(member_ids) # Job queue - batch_name = (fields.Datetime.to_string(fields.Datetime.now()) + - " Génération des bordereaux " + str(self.year)) - batch = self.env['queue.job.batch'].get_new_batch(batch_name) - for member in members_to_invoice: + batch_name = ( + fields.Datetime.to_string(fields.Datetime.now()) + + " Génération des bordereaux " + + self.year + ) + batch = self.env["queue.job.batch"].get_new_batch(batch_name) + for member in member_to_invoice: liasse_id = self.get_liasse(member) - self.with_context( - job_batch=batch - ).with_delay().create_bordereau( - member=member, liasse=liasse_id, - nb_quarter=4, date=False) + self.with_context(job_batch=batch).with_delay().create_bordereau( + member=member, liasse=liasse_id, nb_quarter="4", date=False + ) batch.enqueue() def cotiz_generate_wizard(self): """ Open wizard to generate cotisations for new members """ - if not self.trimester_1 or not self.trimester_2 or not self.trimester_3 or not self.trimester_4: + if ( + not self.trimester_1 + or not self.trimester_2 + or not self.trimester_3 + or not self.trimester_4 + ): raise exceptions.UserError( - "Les trimestres doivent être configurés pour lancer les cotisations.") + _("Les trimestres doivent être configurés pour lancer les cotisations.") + ) if not self.date_cotisation: raise exceptions.UserError( - "La date de calcul des cotisations doit être renseignée pour lancer les cotisations.") + _( + "La date de calcul des cotisations doit être renseignée pour lancer " + "les cotisations." + ) + ) - wizard = self.env['scop.cotisation.cg.wizard'].create({ - 'year': self.year, - 'cotisation_cg_id': self.id, - }) + wizard = self.env["scop.cotisation.cg.wizard"].create( + { + "year": self.year, + "cotisation_cg_id": self.id, + } + ) return { - 'name': 'Génération des appels de cotisation nouveaux adhérents', - 'type': 'ir.actions.act_window', - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'scop.cotisation.cg.wizard', - 'res_id': wizard.id, - 'context': { - 'id_cotisation_cg': self.id, + "name": "Génération des appels de cotisation nouveaux adhérents", + "type": "ir.actions.act_window", + "view_type": "form", + "view_mode": "form", + "res_model": "scop.cotisation.cg.wizard", + "res_id": wizard.id, + "context": { + "id_cotisation_cg": self.id, }, - 'target': 'new' + "target": "new", } def cotiz_view(self): """ Button to open view for Appels de cotisations """ - tree_id = self.env.ref( - 'cgscop_cotisation.invoice_tree_scop_inherited').id - form_id = self.env.ref( - 'cgscop_cotisation_cg.invoice_form_scop_cg_inherited').id - search_id = (self.env.ref( - 'cgscop_cotisation_cg.invoice_search_scop_cg_inherited').id, - 'view_account_invoice_cotiz_cg_search') + tree_id = self.env.ref("cgscop_cotisation.invoice_tree_scop_inherited").id + form_id = self.env.ref("cgscop_cotisation_cg.invoice_form_scop_cg_inherited").id + search_id = ( + self.env.ref("cgscop_cotisation_cg.invoice_search_scop_cg_inherited").id, + "view_account_invoice_cotiz_cg_search", + ) return { - 'name': "Appels de cotisation " + str(self.year), - 'type': 'ir.actions.act_window', - 'res_model': 'account.invoice', - 'view_mode': 'tree,form', - 'view_type': 'form', - 'search_view_id': search_id, - 'views': [ - [tree_id, 'tree'], [form_id, 'form'], - [False, 'pivot'], [False, 'graph'] + "name": "Appels de cotisation " + self.year, + "type": "ir.actions.act_window", + "res_model": "account.invoice", + "view_mode": "tree,form", + "view_type": "form", + "search_view_id": search_id, + "views": [ + [tree_id, "tree"], + [form_id, "form"], + [False, "pivot"], + [False, "graph"], ], - 'domain': [('cotisation_cg_id', '=', self.id)], - 'context': { - 'create': False, - 'default_year': self.year, - 'default_is_contribution': True, - 'default_cotisation_cg_id': self.id + "domain": [("cotisation_cg_id", "=", self.id)], + "context": { + "create": False, + "default_year": self.year, + "default_is_contribution": True, + "default_cotisation_cg_id": self.id, }, - 'target': 'current', + "target": "current", } def bordereaux_view(self): @@ -322,13 +380,13 @@ class ScopCotisation(models.Model): Button to open view for Bordereaux """ return { - 'name': "Bordereaux de cotisation " + str(self.year), - 'type': 'ir.actions.act_window', - 'res_model': 'scop.bordereau', - 'view_mode': 'tree,form', - 'view_type': 'form', - 'domain': [('base_cotisation_cg', '=', self.id)], - 'target': 'current', + "name": "Bordereaux de cotisation " + self.year, + "type": "ir.actions.act_window", + "res_model": "scop.bordereau", + "view_mode": "tree,form", + "view_type": "form", + "domain": [("base_cotisation_cg", "=", self.id)], + "target": "current", } def create_bordereau(self, member, nb_quarter, liasse=None, date=False): @@ -352,18 +410,20 @@ class ScopCotisation(models.Model): staff_average = 0 date_invoice = date if date else self.date_cotisation - bordereau = self.env['scop.bordereau'].create({ - 'partner_id': member.id, - 'payment_mode_id': member.customer_payment_mode_id.id, - 'base_cotisation_cg': self.id, - 'liasse_fiscale_id': liasse.id if liasse else None, - 'year_liasse': liasse.year if liasse else 0, - 'date_cotisation': date_invoice, - 'nb_quarter': nb_quarter, - 'staff_count': staff_count, - 'staff_shareholder_count': staff_shareholder_count, - 'staff_average': staff_average, - }) + bordereau = self.env["scop.bordereau"].create( + { + "partner_id": member.id, + "payment_mode_id": member.customer_payment_mode_id.id, + "base_cotisation_cg": self.id, + "liasse_fiscale_id": liasse.id if liasse else None, + "year_liasse": liasse.year if liasse else 0, + "date_cotisation": date_invoice, + "nb_quarter": nb_quarter, + "staff_count": staff_count, + "staff_shareholder_count": staff_shareholder_count, + "staff_average": staff_average, + } + ) bordereau.create_cotiz_and_lines() return bordereau.id @@ -371,29 +431,27 @@ class ScopCotisation(models.Model): """ Open wizard to validate all bordereaux from "base de cotisation" """ - bordereau_to_validate = self.bordereau_ids.filtered( - lambda b: b.state == 'new' - ) + bordereau_to_validate = self.bordereau_ids.filtered(lambda b: b.state == "new") bordereau_to_validate.validate_bordereau_multi() if len(bordereau_to_validate) > 0: - message = ( - "%s bordereaux sont en cours de validation" % - len(bordereau_to_validate) + message = "%s bordereaux sont en cours de validation" % len( + bordereau_to_validate ) else: - message = ( - "Tous les bordereaux ont déjà été validés" - ) - message_id = self.env['message.wizard'].create( - {'message': message}) + message = "Tous les bordereaux ont déjà été validés" return { - 'name': 'Validation des bordereaux', - 'type': 'ir.actions.act_window', - 'view_mode': 'form', - 'res_model': 'message.wizard', - 'res_id': message_id.id, - 'target': 'new' + "type": "ir.actions.act_window.message", + "title": _("Validation des bordereaux"), + "is_html_message": True, + "message": _(message), + "close_button_title": False, + "buttons": [ + { + "type": "ir.actions.act_window_close", + "name": "Fermer", + }, + ], } def add_simul(self): @@ -405,13 +463,13 @@ class ScopCotisation(models.Model): @return: object scop.cotisation.cg.simulation """ # Get context - type_simul = self.env.context.get('type_simul') + type_simul = self.env.context.get("type_simul") # Get contribution type - type_cotisation_cg = self.env.ref('cgscop_partner.riga_14397') + type_cotisation_cg = self.env.ref("cgscop_partner.riga_14397") # Get product type - InvoiceLine = self.env['account.invoice.line'] + AccountMoveLine = self.env["account.move.line"] product_cg_id = self.company_id.contribution_cg_id product_com_id = self.company_id.contribution_fede_com_id product_cae_id = self.company_id.contribution_fede_cae_id @@ -422,23 +480,23 @@ class ScopCotisation(models.Model): members = self.get_members() header = [ - 'N° Adhérent', - 'Union Régionale', - 'Forme Coopérative', - 'Organisme', - 'Type de cotisation', - 'Cotisation ' + str(self.year), - 'Cotisation ' + str(self.year - 1), - 'Assiette', - 'Montant Assiette', - 'Année Liasse', - 'Durée Exercice', - 'CA sens CG Scop', - 'VABDF sens CG Scop', - 'Salaires sens CG Scop', - 'Résultat', - 'Dernier effectif connu', - 'Moyen de paiement' + "N° Adhérent", + "Union Régionale", + "Forme Coopérative", + "Organisme", + "Type de cotisation", + "Cotisation " + self.year, + "Cotisation " + str(int(self.year) - 1), + "Assiette", + "Montant Assiette", + "Année Liasse", + "Durée Exercice", + "CA sens CG Scop", + "VABDF sens CG Scop", + "Salaires sens CG Scop", + "Résultat", + "Dernier effectif connu", + "Moyen de paiement", ] # Init variables @@ -448,27 +506,34 @@ class ScopCotisation(models.Model): for m in self.web_progress_iter(members, msg="cotisations calculées"): liasse = self.get_liasse(m) - line_ids = InvoiceLine.search([ - ('partner_id', '=', m.id), - ('invoice_id.state', 'not in', ('draft', 'cancel')), - ('invoice_id.year', '=', self.year - 1) - ]) + line_ids = AccountMoveLine.search( + [ + ("partner_id", "=", m.id), + ("move_id.state", "=", "posted"), + ("move_id.year", "=", int(self.year) - 1), + ("product_id", "!=", False), + ] + ) # Calcul Cotisation CG Scop - net_results = liasse.L2053_HN \ - if liasse.L2053_HN > 0 else liasse.L2051_DI - contrib_cg = self.round_to_closest_multiple( - liasse.contribution_cg, 4) if liasse else 300 + net_results = liasse.L2053_HN if liasse.L2053_HN > 0 else liasse.L2051_DI + contrib_cg = ( + self.round_to_closest_multiple(liasse.contribution_cg, 4) + if liasse + else 300 + ) contribution_amount = contrib_cg contribution_name = type_cotisation_cg.name # Calcul cotisation N-1 - contribution_last_year = sum(line_ids.filtered( - lambda l: l.product_id == product_cg_id).mapped( - 'price_subtotal_signed')) + contribution_last_year = sum( + line_ids.filtered(lambda l: l.product_id == product_cg_id).mapped( + lambda c: c.price_total if c.balance > 0 else -c.price_total + ) + ) # Construction Tableau - liasse.read(['revenue_cg', 'av_cg', 'wage_cg']) + liasse.read(["revenue_cg", "av_cg", "wage_cg"]) datas_contrib = [ m.member_number, m.ur_id.name, @@ -477,7 +542,7 @@ class ScopCotisation(models.Model): contribution_name, contribution_amount, contribution_last_year, - liasse.contribution_base_type.upper() if liasse else 'CA', + liasse.contribution_base_type.upper() if liasse else "CA", liasse.contribution_base_amount, liasse.year, liasse.dureeExercice, @@ -486,42 +551,58 @@ class ScopCotisation(models.Model): liasse.wage_cg, net_results, m.staff_last, - m.customer_payment_mode_id.name if m.customer_payment_mode_id else 'Virement/Chèque', + m.customer_payment_mode_id.name + if m.customer_payment_mode_id + else "Virement/Chèque", ] # Ajout ligne CG Scop datas.append(datas_contrib) - if type_simul == 'all': + if type_simul == "all": # Ajout ligne UR HDF - ur_hdf = self.env.ref('cgscop_partner.riga_14232') + ur_hdf = self.env.ref("cgscop_partner.riga_14232") if m.ur_id == ur_hdf: datas_contrib_hdf = datas_contrib.copy() # Calcul cotisation - contrib_hdf = self.round_to_closest_multiple( - liasse.contribution_hdf, 4) if liasse else 40 + contrib_hdf = ( + self.round_to_closest_multiple(liasse.contribution_hdf, 4) + if liasse + else 40 + ) # Calcul cotisation N-1 - contribution_last_year = sum(line_ids.filtered( - lambda l: l.product_id == product_hdf_id).mapped( - 'price_subtotal_signed')) - datas_contrib_hdf[4] = 'Cotisation UR HDF' + contribution_last_year = sum( + line_ids.filtered( + lambda l: l.product_id == product_hdf_id + ).mapped( + lambda c: c.price_total if c.balance > 0 else -c.price_total + ) + ) + datas_contrib_hdf[4] = "Cotisation UR HDF" datas_contrib_hdf[5] = contrib_hdf datas_contrib_hdf[6] = contribution_last_year datas.append(datas_contrib_hdf) total_hdf += contrib_hdf # Ajout ligne UR Med - ur_med = self.env.ref('cgscop_partner.riga_14243') + ur_med = self.env.ref("cgscop_partner.riga_14243") if m.ur_id == ur_med: datas_contrib_med = datas_contrib.copy() # Calcul cotisation - contrib_med = self.round_to_closest_multiple( - liasse.contribution_med, 4) if liasse else 0 + contrib_med = ( + self.round_to_closest_multiple(liasse.contribution_med, 4) + if liasse + else 0 + ) # Calcul cotisation N-1 - contribution_last_year = sum(line_ids.filtered( - lambda l: l.product_id == product_med_id).mapped( - 'price_subtotal_signed')) - datas_contrib_med[4] = 'Cotisation UR Méditerranée' + contribution_last_year = sum( + line_ids.filtered( + lambda l: l.product_id == product_med_id + ).mapped( + lambda c: c.price_total if c.balance > 0 else -c.price_total + ) + ) + datas_contrib_med[4] = "Cotisation UR Méditerranée" datas_contrib_med[5] = contrib_med datas_contrib_med[6] = contribution_last_year datas.append(datas_contrib_med) @@ -533,7 +614,8 @@ class ScopCotisation(models.Model): # Calcul cotisation if liasse: contrib_fede_com = self.round_to_closest_multiple( - liasse.contribution_com, 4) + liasse.contribution_com, 4 + ) else: staff_id = self.get_last_staff_id(liasse.partner_id) if staff_id.staff_average > 0: @@ -543,10 +625,14 @@ class ScopCotisation(models.Model): if contrib_fede_com < 108: contrib_fede_com = 108 # Calcul cotisation N-1 - contribution_last_year = sum(line_ids.filtered( - lambda l: l.product_id == product_com_id).mapped( - 'price_subtotal_signed')) - datas_contrib_com[4] = 'Cotisation Fédération de la Com' + contribution_last_year = sum( + line_ids.filtered( + lambda l: l.product_id == product_com_id + ).mapped( + lambda c: c.price_total if c.balance > 0 else -c.price_total + ) + ) + datas_contrib_com[4] = "Cotisation Fédération de la Com" datas_contrib_com[5] = contrib_fede_com datas_contrib_com[6] = contribution_last_year datas.append(datas_contrib_com) @@ -557,10 +643,14 @@ class ScopCotisation(models.Model): datas_contrib_cae = datas_contrib.copy() contrib_fede_cae = 100 # Calcul cotisation N-1 - contribution_last_year = sum(line_ids.filtered( - lambda l: l.product_id == product_cae_id).mapped( - 'price_subtotal_signed')) - datas_contrib_cae[4] = 'Cotisation Fédération des CAE' + contribution_last_year = sum( + line_ids.filtered( + lambda l: l.product_id == product_cae_id + ).mapped( + lambda c: c.price_total if c.balance > 0 else -c.price_total + ) + ) + datas_contrib_cae[4] = "Cotisation Fédération des CAE" datas_contrib_cae[5] = contrib_fede_cae datas_contrib_cae[6] = contribution_last_year datas.append(datas_contrib_cae) @@ -570,22 +660,25 @@ class ScopCotisation(models.Model): total_cg += contrib_cg # Génération du ficher Excel - file_name = datetime.strftime(datetime.now(), - '%Y-%m-%d_%Hh%M') + ' Simulation cotisations ' + str( - self.year) + '.xlsx' + file_name = ( + datetime.strftime(datetime.now(), "%Y-%m-%d_%Hh%M") + + " Simulation cotisations " + + self.year + + ".xlsx" + ) output = BytesIO() - workbook = xlsxwriter.Workbook(output, {'in_memory': True}) - worksheet1 = workbook.add_worksheet('Données brutes') + workbook = xlsxwriter.Workbook(output, {"in_memory": True}) + worksheet1 = workbook.add_worksheet("Données brutes") # Intégration Header format_header = workbook.add_format( - {'bg_color': '#eeeeee', 'font_color': '#333333', 'bold': True}) + {"bg_color": "#eeeeee", "font_color": "#333333", "bold": True} + ) for col, h in enumerate(header): worksheet1.write(0, col, h, format_header) # Intégration datas - format_monetary = workbook.add_format( - {'num_format': '#,##0 [$€-407]'}) + format_monetary = workbook.add_format({"num_format": "#,##0 [$€-407]"}) for row, contrib in enumerate(datas): for col, value in enumerate(contrib): @@ -608,44 +701,49 @@ class ScopCotisation(models.Model): mem_bytes = output.read() file_base64 = base64.encodebytes(mem_bytes) - simul_id = self.simul_ids.create({ - 'cotisation_id': self.id, - 'file': file_base64, - 'filename': file_name, - 'type_simul': type_simul, - 'total_member': count_member, - 'total_cg': total_cg, - 'total_hdf': total_hdf, - 'total_com': total_com, - 'total_cae': total_cae, - 'total_med': total_med, - }) + simul_id = self.simul_ids.create( + { + "cotisation_id": self.id, + "file": file_base64, + "filename": file_name, + "type_simul": type_simul, + "total_member": count_member, + "total_cg": total_cg, + "total_hdf": total_hdf, + "total_com": total_com, + "total_cae": total_cae, + "total_med": total_med, + } + ) return { - 'name': 'Simulation cotisation', - 'type': 'ir.actions.act_window', - 'view_mode': 'form', - 'res_model': 'scop.cotisation.cg.simulation', - 'res_id': simul_id.id, - 'target': 'new', - 'flags': {'mode': 'readonly', 'default_buttons': False} + "name": "Simulation cotisation", + "type": "ir.actions.act_window", + "view_mode": "form", + "res_model": "scop.cotisation.cg.simulation", + "res_id": simul_id.id, + "target": "new", + "flags": {"mode": "readonly", "default_buttons": False}, } # ------------------------------------------------------ # Global functions # ------------------------------------------------------ - @api.multi def get_liasse(self, member): self.ensure_one() - Liasse = self.env['scop.liasse.fiscale'] - liasse_ids = Liasse.search([ - ('partner_id', '=', member.id), - ('is_qualified', '=', True), - '|', '|', - ('revenue_cgsubv', '>', 0), - ('av_lf', '>', 0), - ('av_cg', '>', 0), - ], order="year desc") + Liasse = self.env["scop.liasse.fiscale"] + liasse_ids = Liasse.search( + [ + ("partner_id", "=", member.id), + ("is_qualified", "=", True), + "|", + "|", + ("revenue_cgsubv", ">", 0), + ("av_lf", ">", 0), + ("av_cg", ">", 0), + ], + order="year desc", + ) if liasse_ids: for liasse in liasse_ids: if liasse.year > 0: @@ -664,7 +762,7 @@ class ScopCotisation(models.Model): :param liasse: :return: """ - liasse.read(['av_lf', 'av_cg']) + liasse.read(["av_lf", "av_cg"]) va = liasse.av_lf if liasse.av_lf > 0 else liasse.av_cg return va @@ -686,12 +784,12 @@ class ScopCotisation(models.Model): """ if ca > 0 and va > 0: if ca <= va * 7 / 3: - type_cotiz = 'ca' + type_cotiz = "ca" else: - type_cotiz = 'va' + type_cotiz = "va" else: if va > 0: - type_cotiz = 'va' + type_cotiz = "va" else: - type_cotiz = 'ca' + type_cotiz = "ca" return type_cotiz diff --git a/models/scop_cotisation_simulation.py b/models/scop_cotisation_simulation.py index 1e46c08..2e35c4d 100644 --- a/models/scop_cotisation_simulation.py +++ b/models/scop_cotisation_simulation.py @@ -1,11 +1,9 @@ # © 2021 Le Filament (<http://www.le-filament.com>) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -import json import logging -import threading -from odoo import models, fields, api, exceptions, registry +from odoo import fields, models _logger = logging.getLogger(__name__) @@ -15,25 +13,29 @@ class ScopCotisation(models.Model): _description = "Simulation cotisations CG" cotisation_id = fields.Many2one( - comodel_name='scop.cotisation.cg', + comodel_name="scop.cotisation.cg", ) - file = fields.Binary('Fichier') + file = fields.Binary("Fichier") filename = fields.Char() type_simul = fields.Selection( - selection=[('all', 'Toutes les cotisation'), ('cgscop', 'Cotisations CG Scop')], - string='Type de simulation' + selection=[ + ("all", "Toutes les cotisation"), + ("cgscop", "Cotisations CG Scop"), + ], + string="Type de simulation", ) - total_member = fields.Integer('Nombre Adhérents') - total_cg = fields.Float('Total CG Scop') - total_hdf = fields.Float('Total UR HDF') - total_med = fields.Float('Total UR Med') - total_com = fields.Float('Total Fédération Com') - total_cae = fields.Float('Total Fédération CAE') + total_member = fields.Integer("Nombre Adhérents") + total_cg = fields.Float("Total CG Scop") + total_hdf = fields.Float("Total UR HDF") + total_med = fields.Float("Total UR Med") + total_com = fields.Float("Total Fédération Com") + total_cae = fields.Float("Total Fédération CAE") def get_simul_file(self): return { - 'type': 'ir.actions.act_url', - 'name': 'simulation', - 'url': '/web/content/scop.cotisation.cg.simulation/%s/file/%s?download=true' % (self.id, self.filename), + "type": "ir.actions.act_url", + "name": "simulation", + "url": "/web/content/scop.cotisation.cg.simulation/%s/file/%s?download=true" + % (self.id, self.filename), } diff --git a/models/scop_liasse_fiscale.py b/models/scop_liasse_fiscale.py index a736ec9..70afb7c 100644 --- a/models/scop_liasse_fiscale.py +++ b/models/scop_liasse_fiscale.py @@ -1,62 +1,70 @@ # © 2021 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 odoo import api, fields, models class ScopLiasseFiscale(models.Model): _inherit = "scop.liasse.fiscale" contribution_cg = fields.Float( - string='Cotisation CG Scop', - compute='_compute_contribution_cg', - store=True) + string="Cotisation CG Scop", + compute="_compute_contribution_cg", + store=True, + ) contribution_base_type = fields.Selection( - string='Type Assiette de cotisation', - selection=[('ca', 'CA'), - ('va', 'VA'), ], - compute='_compute_contribution_cg', - store=True) + string="Type Assiette de cotisation", + selection=[ + ("ca", "CA"), + ("va", "VA"), + ], + compute="_compute_contribution_cg", + store=True, + ) contribution_base_amount = fields.Integer( - string='Montant Assiette de cotisation', - compute='_compute_contribution_cg', - store=True) + string="Montant Assiette de cotisation", + compute="_compute_contribution_cg", + store=True, + ) contribution_hdf = fields.Float( - string='Cotisation UR HDF', - compute='_compute_contribution_hdf', - store=True) + string="Cotisation UR HDF", + compute="_compute_contribution_hdf", + store=True, + ) contribution_med = fields.Float( - string='Cotisation UR Méditerranée', - compute='_compute_contribution_med', - store=True) + string="Cotisation UR Méditerranée", + compute="_compute_contribution_med", + store=True, + ) contribution_com = fields.Float( - string='Cotisation Fédération Com', - compute='_compute_contribution_com', - store=True) + string="Cotisation Fédération Com", + compute="_compute_contribution_com", + store=True, + ) contribution_cae = fields.Float( - string='Cotisation Fédération CAE', - compute='_compute_contribution_cae', - store=True) + string="Cotisation Fédération CAE", + compute="_compute_contribution_cae", + store=True, + ) # ------------------------------------------------------ # Compute fields # ------------------------------------------------------ - @api.depends('av_lf', 'av_cg', 'revenue_cg', 'dureeExercice') - @api.multi + @api.depends("av_lf", "av_cg", "revenue_cg", "dureeExercice") def _compute_contribution_cg(self): """ - Calcule la cotisation de la CG Scop : - - VA = VA saisie ou VA au sens CGSCOP si pas de VA renseignée - - 0,3% du CA ou 0,7% de la VA - - 300 € pour CA compris entre 0 et 100 K€ - - 600 € pour CA compris entre 100 et 200 K€ - - Max = 375 K€ - - @return float : cotisation + Calcule la cotisation de la CG Scop : + - VA = VA saisie ou VA au sens CGSCOP si pas de VA renseignée + - 0,3% du CA ou 0,7% de la VA + - 300 € pour CA compris entre 0 et 100 K€ + - 600 € pour CA compris entre 100 et 200 K€ + - Max = 375 K€ + + @return float : cotisation """ for liasse in self: # Calcul VA - liasse.read(['av_lf', 'av_cg', 'revenue_cg']) + liasse.read(["av_lf", "av_cg", "revenue_cg"]) va = liasse.av_lf if liasse.av_lf > 0 else liasse.av_cg ca = liasse.revenue_cg if liasse.dureeExercice and liasse.dureeExercice not in (0, 12): @@ -66,17 +74,17 @@ class ScopLiasseFiscale(models.Model): # Calcul Type Assiette if ca > 0 and va > 0: if ca <= va * 7 / 3: - contribution_base_type = 'ca' + contribution_base_type = "ca" contribution_base_amount = ca else: - contribution_base_type = 'va' + contribution_base_type = "va" contribution_base_amount = va else: if va > 0: - contribution_base_type = 'va' + contribution_base_type = "va" contribution_base_amount = va else: - contribution_base_type = 'ca' + contribution_base_type = "ca" contribution_base_amount = ca liasse.contribution_base_type = contribution_base_type @@ -86,47 +94,49 @@ class ScopLiasseFiscale(models.Model): values = self.get_values_for_cotiz_cg(liasse.partner_id) # Calcul Cotisation CG Scop - if contribution_base_type == 'ca': + if contribution_base_type == "ca": rate = self.get_rate_ca(liasse.partner_id) contribution = ca * rate else: rate = self.get_rate_va(liasse.partner_id) contribution = va * rate - if contribution < values['plancher1']: - liasse.contribution_cg = values['plancher1'] - elif contribution < values['plancher2']: - liasse.contribution_cg = values['plancher2'] - elif contribution >= values['plancher3']: - if contribution_base_type == 'ca': + if contribution < values["plancher1"]: + liasse.contribution_cg = values["plancher1"] + elif contribution < values["plancher2"]: + liasse.contribution_cg = values["plancher2"] + elif contribution >= values["plancher3"]: + if contribution_base_type == "ca": contribution = (25000000 * rate) + (ca - 25000000) * (rate / 2) else: contribution = (10714286 * rate) + (va - 10714286) * (rate / 2) - if contribution <= values['plafond']: + if contribution <= values["plafond"]: liasse.contribution_cg = contribution else: - liasse.contribution_cg = values['plafond'] + liasse.contribution_cg = values["plafond"] else: liasse.contribution_cg = contribution - @api.depends('wage_cg') - @api.multi + @api.depends("wage_cg") def _compute_contribution_hdf(self): """ - Calcule la cotisation de l'UR HDF pour 1 partenaire : - - 0,1 % de la masse salariale annuelle brute prévisionnelle - ou figurant sur la liasse fiscale. + Calcule la cotisation de l'UR HDF pour 1 partenaire : + - 0,1 % de la masse salariale annuelle brute prévisionnelle + ou figurant sur la liasse fiscale. - @return float : cotisation + @return float : cotisation """ - ur_hdf = self.env.ref('cgscop_partner.riga_14232') + ur_hdf = self.env.ref("cgscop_partner.riga_14232") for liasse in self: - plancher = liasse.get_plancher_cotiz()['ur_hdf'] - liasse.read(['wage_cg']) + plancher = liasse.get_plancher_cotiz()["ur_hdf"] + liasse.read(["wage_cg"]) if liasse.partner_id.ur_id == ur_hdf: rate = 0.001 - if liasse.dureeExercice and liasse.dureeExercice not in (0, 12): - wage_cg = liasse.wage_cg * (12/liasse.dureeExercice) + if liasse.dureeExercice and liasse.dureeExercice not in ( + 0, + 12, + ): + wage_cg = liasse.wage_cg * (12 / liasse.dureeExercice) else: wage_cg = liasse.wage_cg if wage_cg > 0: @@ -139,39 +149,43 @@ class ScopLiasseFiscale(models.Model): else: liasse.contribution_hdf = plancher - @api.depends('contribution_cg', 'L2053_HN') - @api.multi + @api.depends("contribution_cg", "L2053_HN") def _compute_contribution_med(self): """ - Calcule la cotisation de l'UR Med pour 1 liasse : - - Assiette : Assiette de la cotisation CGSCOP - - Taux d’appel : 1/3 du taux - - Calcul intermédiaire : Assiette * taux - (soit 1/3 de la cotisation CGSCOP sans plancher) - - Complément de cotisation : 1% du résultat net si positif - - Abattement pour sociétariat : - Taux de sociétariat = nombre de salariés sociétaires / - nombre de salariés - Abattement 20 % si >= 50 % - Abattement 30 % si >= 80 % - - Calcul cotisations : - [(Assiette*taux) + (résultat net *0.01)]* (1 ou 0.8 ou 0.7) - - @return float : cotisation + Calcule la cotisation de l'UR Med pour 1 liasse : + - Assiette : Assiette de la cotisation CGSCOP + - Taux d’appel : 1/3 du taux + - Calcul intermédiaire : Assiette * taux + (soit 1/3 de la cotisation CGSCOP sans plancher) + - Complément de cotisation : 1% du résultat net si positif + - Abattement pour sociétariat : + Taux de sociétariat = nombre de salariés sociétaires / + nombre de salariés + Abattement 20 % si >= 50 % + Abattement 30 % si >= 80 % + - Calcul cotisations : + [(Assiette*taux) + (résultat net *0.01)]* (1 ou 0.8 ou 0.7) + + @return float : cotisation """ - ur_med = self.env.ref('cgscop_partner.riga_14243') + ur_med = self.env.ref("cgscop_partner.riga_14243") for liasse in self: - plancher = liasse.get_plancher_cotiz()['ur_med'] + plancher = liasse.get_plancher_cotiz()["ur_med"] if liasse.partner_id.ur_id == ur_med: # Assiette CG - assiette_rate = 1/3 + assiette_rate = 1 / 3 assiette = liasse.contribution_cg # Résultat net net_results_rate = 0.01 - net_results = liasse.L2053_HN if liasse.L2053_HN > 0 else liasse.L2051_DI - if liasse.dureeExercice and liasse.dureeExercice not in (0, 12): - net_results = net_results * (12/liasse.dureeExercice) + net_results = ( + liasse.L2053_HN if liasse.L2053_HN > 0 else liasse.L2051_DI + ) + if liasse.dureeExercice and liasse.dureeExercice not in ( + 0, + 12, + ): + net_results = net_results * (12 / liasse.dureeExercice) # Effectifs staff_id = self.get_last_staff_id(liasse.partner_id) @@ -195,38 +209,43 @@ class ScopLiasseFiscale(models.Model): abatt_rate = 1 - 0 # Calcul Cotisation - contribution_med = ((assiette * assiette_rate) + - (net_results * net_results_rate)) * abatt_rate - final_contribution_med = contribution_med if contribution_med >= 0 else plancher + contribution_med = ( + (assiette * assiette_rate) + (net_results * net_results_rate) + ) * abatt_rate + final_contribution_med = ( + contribution_med if contribution_med >= 0 else plancher + ) liasse.contribution_med = final_contribution_med - @api.depends('av_lf', 'av_cg') - @api.multi + @api.depends("av_lf", "av_cg") def _compute_contribution_com(self): """ - Calcule la cotisation de la fédération de la com pour 1 partenaire - - Assiette annuelle : VA saisie ou VA au sens CGSCOP - - Taux : 0.0032 - - Calcul : Valeur Ajoutée * 0.0032 - - Pour les nouvelles coopératives (pas de comptes annuels) : - 108 € par salarié (effectif moyen s’il est saisi, à défaut - effectif), soit 27 € par salarié et par trimestre. - - Plancher annuel : 108 € - - Plafond annuel : 18 428 € - - @return float : cotisation + Calcule la cotisation de la fédération de la com pour 1 partenaire + - Assiette annuelle : VA saisie ou VA au sens CGSCOP + - Taux : 0.0032 + - Calcul : Valeur Ajoutée * 0.0032 + - Pour les nouvelles coopératives (pas de comptes annuels) : + 108 € par salarié (effectif moyen s’il est saisi, à défaut + effectif), soit 27 € par salarié et par trimestre. + - Plancher annuel : 108 € + - Plafond annuel : 18 428 € + + @return float : cotisation """ plafond = 18428 rate = 0.0032 for liasse in self: - plancher = liasse.get_plancher_cotiz()['fede_com'] - liasse.read(['av_lf', 'av_cg']) + plancher = liasse.get_plancher_cotiz()["fede_com"] + liasse.read(["av_lf", "av_cg"]) if liasse.partner_id.is_federation_com: # Calcul VA va = liasse.av_lf if liasse.av_lf > 0 else liasse.av_cg # Calcul VA proratisée - if liasse.dureeExercice and liasse.dureeExercice not in (0, 12): + if liasse.dureeExercice and liasse.dureeExercice not in ( + 0, + 12, + ): va = va * (12 / liasse.dureeExercice) else: va = va @@ -255,19 +274,18 @@ class ScopLiasseFiscale(models.Model): else: liasse.contribution_com = plafond - @api.depends('L2052_FY', 'partner_id.cae') - @api.multi + @api.depends("L2052_FY", "partner_id.cae") def _compute_contribution_cae(self): """ - Calcule la cotisation CAE 1 partenaire : - - 0,4 % de la masse salariale annuelle brute - - plancher = 300 - - plafond = 8500 - @return float : cotisation + Calcule la cotisation CAE 1 partenaire : + - 0,4 % de la masse salariale annuelle brute + - plancher = 300 + - plafond = 8500 + @return float : cotisation """ for liasse in self: if liasse.partner_id.cae: - plancher = liasse.get_plancher_cotiz()['fede_cae'] + plancher = liasse.get_plancher_cotiz()["fede_cae"] plafond = 8500 rate = 0.004 contribution_cae = 0 @@ -302,15 +320,14 @@ class ScopLiasseFiscale(models.Model): abatt_rate_med = 1 - 0.3 else: abatt_rate_med = 1 - 0 - assiette_rate_med = 1/3 - plancher_cg = self.get_values_for_cotiz_cg( - self.partner_id)['plancher1'] + assiette_rate_med = 1 / 3 + plancher_cg = self.get_values_for_cotiz_cg(self.partner_id)["plancher1"] return { - 'fede_com': 108, - 'fede_cae': 300, - 'ur_med': plancher_cg * assiette_rate_med * abatt_rate_med, - 'ur_hdf': 40, + "fede_com": 108, + "fede_cae": 300, + "ur_med": plancher_cg * assiette_rate_med * abatt_rate_med, + "ur_hdf": 40, } def get_values_for_cotiz_cg(self, partner): @@ -320,25 +337,27 @@ class ScopLiasseFiscale(models.Model): plancher3 = 75000 plafond = 375000 if partner.cooperative_form_id in ( - self.env.ref('cgscop_partner.form_coop47'), - self.env.ref('cgscop_partner.form_lamaneur')): + self.env.ref("cgscop_partner.form_coop47"), + self.env.ref("cgscop_partner.form_lamaneur"), + ): plancher1 = 1 / 3 * plancher1 plancher2 = 1 / 3 * plancher2 plancher3 = 1 / 3 * plancher3 plafond = 1 / 3 * plafond return { - 'plancher1': plancher1, - 'plancher2': plancher2, - 'plancher3': plancher3, - 'plafond': plafond, + "plancher1": plancher1, + "plancher2": plancher2, + "plancher3": plancher3, + "plafond": plafond, } def get_rate_ca(self, partner): # Calcul des Taux SCOP en fonction du type if partner.cooperative_form_id in ( - self.env.ref('cgscop_partner.form_coop47'), - self.env.ref('cgscop_partner.form_lamaneur')): + self.env.ref("cgscop_partner.form_coop47"), + self.env.ref("cgscop_partner.form_lamaneur"), + ): rate_ca = 0.002 else: rate_ca = 0.003 @@ -347,8 +366,9 @@ class ScopLiasseFiscale(models.Model): def get_rate_va(self, partner): # Calcul des Taux SCOP en fonction du type if partner.cooperative_form_id in ( - self.env.ref('cgscop_partner.form_coop47'), - self.env.ref('cgscop_partner.form_lamaneur')): + self.env.ref("cgscop_partner.form_coop47"), + self.env.ref("cgscop_partner.form_lamaneur"), + ): rate_va = 0.0047 else: rate_va = 0.007 diff --git a/report/__init__.py b/report/__init__.py old mode 100755 new mode 100644 diff --git a/report/scop_contribution_report.py b/report/scop_contribution_report.py index f5b1841..27fa8e8 100644 --- a/report/scop_contribution_report.py +++ b/report/scop_contribution_report.py @@ -1,7 +1,7 @@ # © 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 odoo import _, fields, models from odoo.exceptions import UserError @@ -13,17 +13,22 @@ class ScopContributionReport(models.Model): # ------------------------------------------------------ # Compute # ------------------------------------------------------ - @api.multi def _compute_has_bordereau(self): - contrib_cg = self.env.ref('cgscop_partner.riga_14397') + contrib_cg = self.env.ref("cgscop_partner.riga_14397") for contribution in self: # exclude contribution before 2021 from RIGA - if contribution.year > '2020': + if contribution.year > "2020": if contribution.type_contribution_id == contrib_cg: - bordereau = self.env['scop.bordereau'].sudo().search([ - ('partner_id', '=', self.partner_id.id), - ('year', '=', self.year), - ]) + bordereau = ( + self.env["scop.bordereau"] + .sudo() + .search( + [ + ("partner_id", "=", self.partner_id.id), + ("year", "=", self.year), + ] + ) + ) if len(bordereau) == 1: contribution.has_bordereau = True else: @@ -37,16 +42,26 @@ class ScopContributionReport(models.Model): # Button # ------------------------------------------------------ def print_report_with_payment(self): - bordereau = self.env['scop.bordereau'].sudo().search([ - ('partner_id', '=', self.partner_id.id), - ('year', '=', self.year), - ]) + bordereau = ( + self.env["scop.bordereau"] + .sudo() + .search( + [ + ("partner_id", "=", self.partner_id.id), + ("year", "=", self.year), + ] + ) + ) if len(bordereau) == 1: - return self.env.ref( - 'cgscop_cotisation_cg.cgscop_bordereau_report_with_payments' - ).sudo().report_action(bordereau.sudo()) + return ( + self.env.ref( + "cgscop_cotisation_cg.cgscop_bordereau_report_with_payments" + ) + .sudo() + .report_action(bordereau.sudo()) + ) else: - raise UserError('Pas de bordereau rattaché à cette cotisation.') + raise UserError(_("Pas de bordereau rattaché à cette cotisation.")) # ------------------------------------------------------ # Business method @@ -56,11 +71,27 @@ class ScopContributionReport(models.Model): If cotiz is type CG, then we filtered on not canceled bordereaux """ self.ensure_one() - invoice_ids = super( - ScopContributionReport, self).get_invoice_contribution() - contribution_cg = self.env.ref('cgscop_partner.riga_14397') + invoice_ids = super(ScopContributionReport, self).get_invoice_contribution() + contribution_cg = self.env.ref("cgscop_partner.riga_14397") if self.type_contribution_id == contribution_cg: invoice_ids = invoice_ids.filtered( - lambda i: i.bordereau_id.state not in ('cancel',) + lambda i: i.bordereau_id.state not in ("cancel",) ) return invoice_ids + + # ------------------------------------------------------ + # Inherit parent + # ------------------------------------------------------ + def _get_contribution_domain(self): + """ + Hérite la fonction parente pour ajouter la notion de bordereau + dans la vue pour les cotisations CG + """ "" + domain = super()._get_contribution_domain() + domain.append( + "|", + ("bordereau_id.state", "not in", ("cancel", "new")), + ("bordereau_id", "=", False), + ) + print(domain) + return domain diff --git a/report/scop_contribution_report.xml b/report/scop_contribution_report.xml index bdf5c44..b305fe7 100644 --- a/report/scop_contribution_report.xml +++ b/report/scop_contribution_report.xml @@ -1,20 +1,27 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8" ?> <odoo> <data> <!-- FORM VIEW --> <record model="ir.ui.view" id="scop_contribution_report_cg_form"> <field name="name">scop.contribution.report.cg.form</field> <field name="model">scop.contribution.report</field> - <field name="inherit_id" ref="cgscop_cotisation.scop_contribution_report_form"/> + <field + name="inherit_id" + ref="cgscop_cotisation.scop_contribution_report_form" + /> <field name="arch" type="xml"> <xpath expr="//field[@name='payments']" position="before"> - <field name="has_bordereau" invisible="1"/> - <div attrs="{'invisible': [('has_bordereau', '=', False)]}" class="mt16 mb16"> - <button name="print_report_with_payment" - type="object" - string="Imprimer le bordereau" - icon="fa-file-pdf-o" - class="btn btn-sm btn-outline-info" + <field name="has_bordereau" invisible="1" /> + <div + attrs="{'invisible': [('has_bordereau', '=', False)]}" + class="mt16 mb16" + > + <button + name="print_report_with_payment" + type="object" + string="Imprimer le bordereau" + icon="fa-file-pdf-o" + class="btn btn-sm btn-outline-info" /> </div> </xpath> diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv old mode 100755 new mode 100644 index 658ea35..7c925ce --- a/security/ir.model.access.csv +++ b/security/ir.model.access.csv @@ -9,3 +9,11 @@ access_scop_bordereau_version,access_scop_bordereau_version,model_scop_bordereau admin_access_scop_bordereau_version,admin_access_scop_bordereau_version,model_scop_bordereau_version,cgscop_partner.group_cg_administrator,1,1,1,1 access_scop_bordereau_refund_wizard_quarter,access_scop_bordereau_refund_wizard_quarter,model_scop_bordereau_refund_wizard_quarter,cgscop_cotisation_cg.group_cotisation_cg_administrative,1,1,1,1 admin_access_scop_bordereau_refund_wizard_quarter,admin_access_scop_bordereau_refund_wizard_quarter,model_scop_bordereau_refund_wizard_quarter,cgscop_partner.group_cg_administrator,1,1,1,1 +access_scop_bordereau_refund_wizard,access_scop_bordereau_refund_wizard,model_scop_bordereau_refund_wizard,base.group_user,1,1,1,1 +access_scop_bordereau_update,access_scop_bordereau_update,model_scop_bordereau_update,base.group_user,1,1,1,1 +access_scop_bordereau_validate,access_scop_bordereau_validate,model_scop_bordereau_validate,base.group_user,1,1,1,1 +access_scop_cotisation_regul_wizard,access_scop_cotisation_regul_wizard,model_scop_cotisation_regul_wizard,base.group_user,1,1,1,1 +access_scop_cotisation_cg_wizard,access_scop_cotisation_cg_wizard,model_scop_cotisation_cg_wizard,base.group_user,1,1,1,1 +access_export_journal_cg_wizard,access_export_journal_cg_wizard,model_export_journal_cg_wizard,base.group_user,1,1,1,1 +access_scop_bordereau_change_liasse_wizard,access_scop_bordereau_change_liasse_wizard,model_scop_bordereau_change_liasse_wizard,base.group_user,1,1,1,1 +access_scop_bordereau_change_payment_mode_wizard,access_scop_bordereau_change_payment_mode_wizard,model_scop_bordereau_change_payment_mode_wizard,base.group_user,1,1,1,1 diff --git a/security/security_rules.xml b/security/security_rules.xml index a4f7a2f..1fe8d71 100644 --- a/security/security_rules.xml +++ b/security/security_rules.xml @@ -1,22 +1,25 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8" ?> <!-- Copyright 2020 Le Filament License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> - <odoo> - <data noupdate="0"> + <data> <!-- Categories & Groups --> <!-- Add Cotisations group category --> <record model="ir.module.category" id="cgscop_cotisation_cg_module_category"> <field name="name">Cotisation</field> + <field name="parent_id" ref="cgscop_partner.module_cgscop_category" /> </record> <!-- Add cotisations groups --> <record id="group_cotisation_cg_administrative" model="res.groups"> <field name="name">Gestionnaire des cotisations</field> - <field name="category_id" ref="cgscop_cotisation_cg_module_category"/> - <field name="implied_ids" eval="[(6, 0, [ref('account.group_account_manager')])]"/> + <field name="category_id" ref="cgscop_cotisation_cg_module_category" /> + <field + name="implied_ids" + eval="[(6, 0, [ref('account.group_account_manager')])]" + /> </record> <!-- Rules --> @@ -24,46 +27,66 @@ <!-- Cotiz only for own company --> <record id="cg_cotisation_cg_rule" model="ir.rule"> <field name="name">Cotisations consultables que pour sa société</field> - <field name="model_id" ref="cgscop_cotisation_cg.model_scop_cotisation_cg"/> - <field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field> - <field name="perm_read" eval="True"/> - <field name="perm_write" eval="True"/> - <field name="perm_create" eval="True"/> - <field name="perm_unlink" eval="True"/> + <field + name="model_id" + ref="cgscop_cotisation_cg.model_scop_cotisation_cg" + /> + <field + name="domain_force" + >['|',('company_id','=',False),('company_id','in',company_ids)]</field> + <field name="perm_read" eval="True" /> + <field name="perm_write" eval="True" /> + <field name="perm_create" eval="True" /> + <field name="perm_unlink" eval="True" /> </record> <!-- Bordereaux only for own company --> - <record id="cg_cotisation_cg_rule" model="ir.rule"> + <record id="scop_bordereau_rule" model="ir.rule"> <field name="name">Bordereaux consultables que pour sa société</field> - <field name="model_id" ref="cgscop_cotisation_cg.model_scop_bordereau"/> - <field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field> - <field name="perm_read" eval="True"/> - <field name="perm_write" eval="True"/> - <field name="perm_create" eval="True"/> - <field name="perm_unlink" eval="True"/> + <field name="model_id" ref="cgscop_cotisation_cg.model_scop_bordereau" /> + <field + name="domain_force" + >['|',('company_id','=',False),('company_id','in',company_ids)]</field> + <field name="perm_read" eval="True" /> + <field name="perm_write" eval="True" /> + <field name="perm_create" eval="True" /> + <field name="perm_unlink" eval="True" /> </record> <!-- Service Admin grant all access --> <record id="scop_cotisation_cg_admin" model="ir.rule"> - <field name="name">Cotisations - Modification - Service Administratif</field> - <field name="model_id" ref="cgscop_cotisation_cg.model_scop_cotisation_cg"/> + <field + name="name" + >Cotisations - Modification - Service Administratif</field> + <field + name="model_id" + ref="cgscop_cotisation_cg.model_scop_cotisation_cg" + /> <field name="domain_force">[(1,'=',1)]</field> - <field name="groups" eval="[(6, 0, [ref('group_cotisation_cg_administrative')])]"/> - <field name="perm_read" eval="True"/> - <field name="perm_write" eval="True"/> - <field name="perm_create" eval="True"/> - <field name="perm_unlink" eval="True"/> + <field + name="groups" + eval="[(6, 0, [ref('group_cotisation_cg_administrative')])]" + /> + <field name="perm_read" eval="True" /> + <field name="perm_write" eval="True" /> + <field name="perm_create" eval="True" /> + <field name="perm_unlink" eval="True" /> </record> <record id="scop_bordereau_admin" model="ir.rule"> - <field name="name">Bordereaux - Modification - Service Administratif</field> - <field name="model_id" ref="cgscop_cotisation_cg.model_scop_bordereau"/> + <field + name="name" + >Bordereaux - Modification - Service Administratif</field> + <field name="model_id" ref="cgscop_cotisation_cg.model_scop_bordereau" /> <field name="domain_force">[(1,'=',1)]</field> - <field name="groups" eval="[(6, 0, [ref('group_cotisation_cg_administrative')])]"/> - <field name="perm_read" eval="True"/> - <field name="perm_write" eval="True"/> - <field name="perm_create" eval="True"/> - <field name="perm_unlink" eval="True"/> + <field + name="groups" + eval="[(6, 0, [ref('group_cotisation_cg_administrative')])]" + /> + <field name="perm_read" eval="True" /> + <field name="perm_write" eval="True" /> + <field name="perm_create" eval="True" /> + <field name="perm_unlink" eval="True" /> </record> </data> -</odoo> \ No newline at end of file +</odoo> diff --git a/static/description/icon.png b/static/description/icon.png old mode 100755 new mode 100644 index 82ef47760a441cf229b5009f0a18ccf3842fbfa5..499652a2c9c0307f875bafc145b6fbafa133da72 GIT binary patch literal 15890 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 delta 9019 zcmbPKbJD%OGr-TCmrII^fq{Y7)59f*fq~%!2y?J8Ffgo*ST~P>fq^C6(btiIVPik{ zpF~y$1_p&>k04(LhAK4%hK3dfhF=T}3=J<B7)lKo7+xhXFj&oCU=S~uvn$Ysfq{Xu zz$3Dlfq`2Xgc%uT&5>YWV2sQRi71Ki^|4BxJVQ)q@&hqbE>k@dJp&~>E(L|j%Hpm} z3JR0c#8p@n6ciLDhf3&~fh0g21qB70lFYO;EB~U*)Vz|!lFa-(J3||N3^6W{>dAT1 zV)Y6NHu^;=X;wilZgyNY`W03cxdpkYC5d)iK8MsJ85k6AdAc};R4~4cW#1!mb?5(W zXYbW9F6}6p+Nb2Y_{PPE&jB0Ne#Hr8>s0Pr;px*O)%*0OP`1Gf?yN<2N4reB7BsQ4 zhzd+u)Ub$&>)y8V-_L%Z(>-&>(%5?U=Iw9Zl-JeouP%RPo%^i**=)<_HD|X!U2;+K z!4VDvCNGvhURuJEjBXAJ2Y3$gGbtxDGZ=U9TFg1hVbJ`+_yETZg#$cE%zWvFOlK7S z@E9<eaTqk;XgD0%!(hx|&)n0%%XCJ;>_LH;@&TSh=NWKmucuHyrP}Ke&S%N}WTDN? zw6O5EbHNJ@kGgF_LfZFE2F=>sEy$QQ!Nz^<pO()HUQOX<XmUKEA}ml@!{#z?-o*6m zn6<l=4g{~UaZ|fcE)XJ9zAS-n)jRIC1xddw1#XmXw~2bJbypH(g4^-%IM#g=(pJ3W zp5ACyxvKG)`qBD+24fGo<Qcd22nOBW=3#pJn>|-e)J&Ijr;kbpcqSdckvdCSao>y5 zm1zYh3OIUyHoEAWIThTnSYh>AYsJ17Y8SrQZn~kjf5G+BJd@PtY%Gs&^)X3W^Y*jj zj|cJ(7ad7oB7QNl;6%oXzi(p$>aMH|X!{;iee$e;-2?ls7kpgRE9=iFR6d#+^WRX# z?ytux*;~o}A40!Qc;V{kaBQ8$Q>PE#-|T%{R5*RzpA*65bEe-AH1^oXJkQU$<Y0GE zuDxT_oOprUKL-Wo6*ScN&%D$ta*jvIp07{yqv~hHjd@EK*3^8oIbo7#exd8(5pgc( zqn+u@EGZWwzVtrtdMIY~L@L#!-fD?-zS*qI8*E><$1;gtJ|}c}(vek51!|uPHYn9R z&I;~pzHnmi(nhZ}k$f9j&wGCfQZ#7ZA@lOfHQx&r|F`NzzkKvc<Fe-bszo(|CskbL z&ElLnBh%PP?`0&H|3(|@E$8yjgokndle6{|P4d3q^y5;c(B3H=XB3v2+!FgfD|1!- z^lL#gD|nR7-{EhVEx2?}MOWyl#rMnJzZ5aCt>OOh$Bv^{g8yZkRfml^_oQsQ9PPkO z`}gUp_v+oX-7`tH?6vgw>u*|^E~uS2viMr7mqz=31yz$9etD*`|L0ClQVnjquApjs zrDpL@g#$d3__Jb`rwBaI3=`+ssvpDG7~9rRU;q4{z>Ce=|K2hETNU-;`>W)T5|_Ix zO8$PI&}riE>vz85-?!JD&dy8Gf0@7E>8DGvcuZ|(=}DeTo{wcEY=oQ^t2LTcDY3Be zwlkcPt(khY^6R{H_w~wV1;yt#9C_7Wusu_9$-bC&AC=oHo?K-N&vI`l-*{`G#-GOh z>G2o7S;`!$|NU3M$=SVW{i0PL=5E<G<JKn5Nw32fC%pIdSW_h*6kmHSv36?E;WG-d z2DkprEo<DQd|}?B1IdM4SNAhlZOAvee1)NNtG2?ododmQGudC}-oFw4AjX?zhNI5H zleW!o98UGy34fjC$MD~;wm5$QbJmjW`K>=Tl`C3(_x!U-=Uqd++@8BC#vYBw!hatO z45++yRNzi&$73<$P7A$5g|>~C7hl=7c85ehi_!D5CfArVq<&-v+H+k!&%C#aJ2x@O z%Knw_#MKMu2y4k5vy0)Fc+*&6XI|vFtKM6gj5|(G{2m(OeDtKa<59!q%s-gFtz9Tp z^J=!fLSNQL&k$w*b<uoJ80wuo)?VB`E%i`YtGMV|8OZ_(y-(i-_+B?Lm$yGyaQ9$% zpU-21)LF9?J{_`O`19uP)+2Wkw@%VM#`7rY#Yz6KuRFt?Im(y45z4)I%>Gd3x!J`m z!6~y|z3H+(?`&FH-+%9gMpFERZ+G~es(YFp_BZyun!K~%@R@AItq+CWpL%Z<s5kbQ zrz|gH$e5+K>)^LWpT*e=UPu=&__<ZRK<caOJX_24ZdD<UTRQpQ*PCP{t8YIMnzQQi zSJON7%tyrcX9cg?vU!H%CyDyp_s$D`9eQ2Rd%-($_BF94!+Es~%R{86q*~2-@!pnU zOJ}>oO~uO!TW)8Ze&=!HhQ;f!j(y)|q|OYgKbOFEvha&@^7rOh-uc3l@<XpaS@!k9 zm;T#||Ng~<mMy=0;+jp(hmQe;f6gmjC~w$RA9LXP{c_zS&+J#8(@)HL@af{-^_mZV zpMQCcnc3ap?E1V`hx2o%eocCRuC>j&{_ftg&I{L+f8IPU;Ot)h?^T?`xwlJN=0$8N z{OwTm*vg}}{_oByv8Pzl1lkwBe}9!ftG-m4g`Fq#T8GVl{tTzvUwj^aKlNvw+9bR1 znv2;xlEvJg^9UbE{}ku6J7r;@u;RaiOeai4=7&$~J2ZW3<y?hset*$-VXW=Fle`yu zW<6Fece>nc?xZF-`SWhc)@|jl-|u1axRvL}IAwFl++xu@>GJwHUOhX{zpFdP^C+R^ zRKFGTAz}WYX+3*B@MqNhvP+FCS3P#WnzelWy5s9R&s?}Q!&BnaL-D&`FIoTHHN$a} zgWR23{S|ZS)@=&;{QCTZraxk-apmgA#LhG>yLsXUmwen)dmGmM(;n~oty^;Ht6}qv zgFV5kPhGBG8RFev|Hsbc?5_HaEB4lz*jo!PdjH4U=|Ff~W0tYF=3X7IR$IRl&MS{U z?s$>6{!N4Y!*}QQS$UmS^gJg0{{Q3)vnLr@Pi~mFw#oT8$75~5=`7`Ig6`KgTL-?L z%c)lQ*CqPW!n?=04(tA1nqB>t|JIU~4wp9u`JGV^TkIe7@>8b0anz5%$T^ctZ0if< zr8L%VTyfJfCHGOH#%&AktKP@F@_&6P3(0llz4z?-#nSV?Lbr)ex%W%eW!@altIy+e z_k`d1wbtTWkJ#}-jUPH!j|A&<PgH*xRS;h3ZQgc!!Nr~Z(N|u{6dnD-z2KQq&F6j@ zeM9!xM){{EsqFIXTkB_Vrnb+W^L5dpd|U3$dVjkKr&`<IcSi4@tXichIKxq7@muG{ zsGs~#*|jgrG@kf>pLc?nR#qvWX3YhY6*ga)1T9RW`~S_Fx5B4Vb&71=s@lLmP9_Wa z<1!ESZJM;{@Uq+2zgFlOcO*3&p3EPtr5F^l@=3b3i0P)u9-pUQ3Gw)CcHialJ})lY zx!x?=H7Dw|>x(#7o#@~2)@)~y-~{#Z;!DRC@Y=e4)xCOzCrPfdDqF5<&rGeJnM%cS zQ(oNUNU4i1eJ8*0d2hc{zjnGqzqA}{P}v#vR({*Swf?Q$pTCsPo_fgs)$#aH=Bes) z^KYx(T;lTFR(a~j?>8i!9`jFmvD7Jn&9Jv5<HtRLJGnib^;`nAuUS26KdyAyt{mXt zAH_aJzA&geP}nGb!FRoYpUHQu&V@$xT8BM6+3t1s;+FOAww+G+o$ue8JAE!kFaO<j zCG1OlEjZq1`>)(L&ta9u7Uy)oTP$e;#%ik7>mGhNCBr;d<=m?6t7SbF+Ail<8-LYl z{-5}tTG?+_c~>(25n5R<!n^qMd}rC!^$(l(hW<E}HSaTn$-mEhnYN0LcKBPlWQF@H zY+w8__5DlFM;0a}BDZe5SGwrkcpzTjuEt~?$E87&BKGo3nr|In&GL)4?bYXXj&E<u z?YdSjGwEjTI<Cei;oUOlB!%+Cj@+v|>9A<>i`D%L)80jO%1LcBKezo&yG{K*8Ob9m zvCD5L9$a|MOf|?|PU*(trwgS^i?mHxIloNIRZHUjs8}MU&Mbams{PI@m+tc0?&#&e zw`LC8%~<7|^JfRY+O3;$dEJH(m*3xR{Qi9QsG>!I#E%Q>?&_(A?P+GZpq4DSXZeE( z6WyXZ=NLA>V6!sGYI<U*(ktgCr2Qnby?))ShXJ!&`W}6=JmMg%ej;IFdiufd*Z+u2 zTAezxIp(0p$M5g*RUSz0{Ni~%cj^llk9CLj=e_iPz#YD&!YlAvhT^jAxyRP`>#*=H z+Ok(f*x2LtipN)!I}RqF(cGD6I_vB@^T}7A-*EeRart8tHHD|<XaCI($T?G*b@iIS zTbG%^QuWHtk+nBZ+_MqRH4AerceJ}Rb(8N7<!KSWH-@NO;NM=g+2{NcFF6H+<}G}S z*015Z+uS#)&7{$*(@!|_-zue}$ENE$mG)K7HB+8`ShMd`uI9=q5z9iDW~!&2_{Msn zR^aE=Q<FTVpPuvQ>XVq=^Jl*KS)4ZEnj+sK=gj(C|HloF>qS&|hKbFS<bG?x{l2=T zh+XH?mHSKDOWk$N-OCOhtf@5jsjE|ccInW%mN}L2KH}jiY?-nR2N+^lW-oJlxhvPn z_}WE@g+0w%lFoKM``kJ8+;xLzzbfZX-ZJ-JiRNT4&c@|^OKg1dkH3u6NjtGv)<j!h zudPYWI*~0?_QHX6&P(bS`kL4D3%y#ex~t2fX@7&$PQ$*1Q`TPNTvf*9`Z@Ibk(VK@ z8!hjMl|9<h_vX5>iR)$cS<g~$GF}${e#+3;<F&`H>Qh!hTfCoKIcIZ0n)%@j@k>cv zZM!{VC;Zv_%d7hpPu~A!uQwf6ICCYR*<*$T%lm`vuO|d~pHVP*w8PqY*82KqQ*Qm5 zIy0s6o%+wt%r3WcD}p6NS7`?oe2@#;o;hW9N|In_o?6P0M{!&C8T#Ek^L0<fIi9A) zueh_k*e@J9z3Xt`_uvAV%X92ydly_-doYo^QpEVA<rm$Swv&_IKM~i6-CjItZGJ;P zE7KVT7U$XDOD;P<OtHEY=6Xat$2#+`YJF!(TZ(9_N9NC7y|so>zZbr}aiAeFXuTHW zf)EZ)J9%5NRqI=8cvt<3TU2@ciP+ZI+FzP{`(lqeUGD7S*!4U_lYi2(HIG%~v|KZ1 zZ{M?M%dD2%C8Fo<tSOu7^dchq67%8A;H<sIxhe6wT*4O<TP;%yE-$(gbkUe&W=k8t zrJPB<iTzdkCrTE)@1Cn)tL3>EE+<-Ok;8u7#CDVJ>Jv*2m_Ktl)vq$+w2SX9-)Bv% zcGuP9mHfXKoVMEd%-P*`V!)+0l@qUo8Ess#_*a}($Q2%?H@bDT?Lnt}nniBxoZoTR zaij68e<kZmMQ)_Tv_!E5-!Snk$T0pSqIqi0iQ2w|TlGvX^Fu40L>h}foL(6pJ|Sw? zw(pG3ycxULA6?PSVmKT$xoVHyMDZUn%jW#rb+jhtO7?^gQx@yabhg*nvRGJZ_xgD$ zHflc`9=-LrxOk3QVn)iWb3X&klzdC)m}O|Ge_7{a5`VyC?U6qprX;7F|NP@y|K9Jj zR&M*6dAe`T$BQQ(wADLH#d1xJGFlXnQ=Wgxbkp9N*;^glY8;<vPRe?idzJkt<HQuT z6*3YbZ`2~UCc3LXoNM{>)G6Lwp@%{*PFy~-`{9>Q-X>m$__LVqw?X*5R3vwa_x> zYM=NE*Ru*2&F5|2a`+m*->1+17lYpA+McZF$*-F<Yxd-h<qr#TCpj|4)Ms_;m|s+r z(^<=Y=C6fic%J2DC#ljUw}O6tOxJ!Jpz)<qm~&SK`)!eTEw`j{|1OnyllHYTSkQl- z*jFi)&hB%&X6qet{bf?a@i%GuGGEswr6X*=_zy}vj9wrq{&FVU6u#R_wm4~c#0%7) z-`Dr3XY%D{l{^j>_IGJ7T627ys%7grI{D}BKgi3wWnPD-SS@QQ&+7k86FT*OUNKcq z`t#z>&9qHVD+?}oEOi&>vWn_jxk9pqP3Pmu-;d6}eK7HEdxi5WxdRM_k^2w!Nwoax z*PYePce^g7rlYm~e)qyWUcE`NEbl{325VG_#1<zUkb5EdQ7SXh_qvPO>y_*67CXCp z*56KFwPs?^>&-`QrS`q5UK<eTzmdyQLZr2GTN^Lub%8^tE;wwl>wFyU^fbC_$*rmB zF<L7ZMQm4j&cSwa39sanm?oyOYjJh#Ro*Z2Pb?OWsx~=4Q}yCn4yBmeKQx2NUcKi1 zQ5vUu@is?Df1FC=zr#^;tS?;g|Gp?wJG^JV>wngIO_3KO>uoxlOp3Oz^iQl=>^5)a z%QLf;j3S<#-0%1{|HA5yxQtHTY4Ucm_T?`Qewb+gTvgn2GLxe3#xI<uC$#+S{Qnvx z-8rcCC2!KbeUb5pzgd{%)t8)8xIOQ<eT+uF!<KC>7wcBn)v>bt<`cQD_+`fhZXcnU zT|H+P<=ebIGqHZ#+o`3oYW0q{HXL7j??;zwZu;B(jgGsy`#avY=&*m}f9Cx8kL9VL zl)mWCv#VR&%TgJwmF!9~r>5M0<SSeGPqAa~=S8vI$81uzZBq81I-_^rsx2qNRpUeX z=iK@$_=5Z8>NR4~@BV3RvU$UNYsSeJte?aKe&62ReM@Mfq>K5d!un4vP5E)PpB5^t zd$hy&tIF@>n$zxbf4MtMdl#u3)qh|3mHEfDzs$!>)Xi-+{oS=l>c>g7I|~;je!jDO z;meP^L(WEA+R_=UxaWFZP+ihfq0<YNG{&}hBnqG5onS7;tE<~>`ccH&yy#@9ykpW= zcc)+Ll$kvrZLRGuk<o6Rv%~aG{om@Ij%w*+?+&ovp7J6$y6#J6fZ10T!?oV)qC5A` zUr^l9`}VcMq!jjoTTa3A9z1yH^mD?!bK5$Xg`Pj##9_!@CpcH_m(YdVau>8NO_*?V zc6_aq(f(X*=kK1`%JbE3U)nNtVZ7jO7k`aK$-+vmB~N7wS8DJtWLGk&TC}lrPF+a- z-|J5UwhP+MvEZ3JCuYCUtNi^UtMqrW9@gM7+;+X{nX_aYhhak0cMa}z#bw*-czyZr z&%3>4)z-78F4uL|t#|XVJ@WDO#0abSMcV~0MVmWbzWl@VNJfa(a-onu6P_Z$plw_4 z#@5wF`$^?S{VbjC{d(5x1+p`5+22`zXRp_qSjPI&>+_#SEbn}G;C{M7$ox{(4B?c& zE1d2q?sV-<l-PRWjpVtz!Y{4#tpAv-u=~yR^0i}j|1N3$mLo;djM>@NPrgnp+jwHj zjD3A73A_urRWJE>@B5SCxb0!-^gAot&nR&1$V^{${C&Dc?}`(hrHA+KFW)04aF)CO zqq^f!PW$s#>Gk0;eY%>z)Z)&^?%$@HJk@if+p6<BgSN#U=Q_LImb3SJY-*^f^;|Rl zGrRL{W|!_3YCP@!ec!U%v7zGe6AG?0@NsRqIC+Wo!mkYHRuys^v-_)R>|d}$Y2)^I z!BvJ!4tPxZ=X8Q=QhXiX$MX1z>x`urzPa%7N9u3ceJnnGvKx|K)vxB=(RL`})rtiX zuR0}<JmU1I*(K7Xv5-$o=ku0{!WXX9Y+n1p=c>-lR+X^5{2R@mcStlXvD4eq;rUYN z{*Gryb#~e=-O8%_Ai4WF=jDXW#hq7{oiFHmI>+%y)1NQi3%VA4&(gSHytE`)wr*lw z`$D;Z%?-jIl<vM#JfM5^*zfhM_5aV+rq+k=$XNgHW`4`O@T8ketHpF)KCx)5N!|MM zbXMWTU`^kbFL?dhB68n(F;BnSdF$X}o!e(;H3si}^?7~khke0QT{CSj|M))p<Le*m zjP{Fk>acH={91B%`-KnJSF<S3=)Snl_iJVNLj5&&&qi9#7iJFFa`VbDf8P3+y2}hW z>-kdE!o(d`DLnLK*WP?mHQ@38yDjnC)2IAg>XpKF(&*Ed3)lXC&8nGv`twJJ`LPRs zEqZ-1K4iVl+LHbpp{iBaY)kTcjsIVHda-&pyVtSR32cUs7TPF>em&NnCwhCvGo~kb zQ<|Q7IGGh6bef}G-Wqbx{mNX`!q~(<1=n>Auk!ZRvn*O~9o`wf{hhCt{w3Ky?abd! z4u|LJP8JY+-QBuJzoXE_s^DE;{{Kyf{ipFPnxrwiQ-5Ce4(@nWuRETT^3pV>1gjV{ zg{Vo13Vpb-d%fdjW^;$E!|H9nYBn)5#w~e&K<Ra0$~Rj9hvR&y?TZ?xX@>g!mbA-Q z{drQ#>{&tcCe?c@YqCgB^I7O9p?YiXB{f;Lhw?Ivjv*U-a__Zhsega4?q<@&```3? z`Baqr7ie**yK@*cTQsfQDzI;=bo$+npvR6HneML6wNAGuYTdc%Rku^`dB%=kX>0n7 zHM|wMor<{<UGA+aUgzkq7R+O&m?AIqqL{g+{%XBd*@4`=+2`vE%MK_!X=AYT&zMmt zdL^z<--zp$!;4A#pP$<yw^QoXI_ujL`D0?QesiAGdLb{k+C+VhTIU7k7hUd8?IWc3 z*H_1Wc3i-3#F=%+>T5v$y7SM^C{)@qryROv@Y!n1li%G-mI+F26c6#Sm*-uacYbGX z?xTePolG`~vNzJ6JHF<Ksju(2a3$Adxo&doRBPqsS9i+nTQ1S^N5DfTn!~sMuCjj1 z#+{#%>+U4Ip6GmeOK42%D)}X+lx(D@ZRg-SYFMdva;oKq{7cKd)*U~~>Q||JfM=3< z$q$BoUkqFV_n(@R;T2N(>*%Ai`R6uU@qdxeig1pYdfH7rbmEQQ`3`q;r*rw%GZ=k- zI>nx858Fw3>nYt!42s_7I&i0cu5yvy&EKLf-?uwT-}05>-?!mPHnXP2wuUOJFrJ@q z|L%Ev#!a&RYhun+g@h$MbxgPPkX!I_^@T;YcLVM(y1!&T+e1Uw$}ZiCgA+^VU)b4o zyfDj*Z>mt>Bj0TPU(PpMe>nfuFYeNhRj5~a;M;$ncVngh!pt{CC$6OTE2QSNpI<BZ zK)=J(tgrZlrPH4Wj~$G6p5c6W*l(WGBk#EKD`B6z`4@h@8~s3?U-5{{ln>gX_ugx0 z*e6w;-ubTnbGHA6q=kI_eHJNfnk=g{<U7w)3YZj4=>0U|TTA}m4~@sI(gaqytL<gm z9CzhF{n|etRp0t<ljSh|VY>9?nYFyl=YCAxe|zr5)$G4L)>N=RT0PCHd|B@u>BrsQ zC%*9G6uo@Tt@O8H&_|YC8cHiImiM_fNG=KZ@hD31+qcu!%iPw!oz%bL0mtMkcN6D7 zZ0>WPd1_}+<|c9Rdu~f))I_8h(<U$}NyQt<XUx7jp<tt5z104lOAag$3@H6}_0YW* zoB1<>b1vF=tSc4MJry(ke6h2GdE&XXO4FwaDwxkdX{xQluXb?ZwXg;i1s(f8B3okK zv>z<Hpq=Rd=h1>q3$K>tSN+~DQ2B=MrMGH4Z!lLQgRzHVz?H2N9ky0%?v@u!Z&}0W z%UG|`6neJ)KF_Q}ix<|fJNeByQONprTFLv(3+EnVm+fZo-^kUoUh5pkQK2v2U+8`n zkukewS)9;wpl#;$$5!l5wdPpyGX854k$6?Yyw1_1ZK1dn+l6V&TT7NM5%}ksI$^@E zEL#)13hg7)&jqI%uVofYVAC|%AsMsevfkvnLf?B*CyP02Hs8At&2{%@Kz+rI2LU(V zs*C#MMe%QJ)$cU8mN{*L^oG}KgwrN8Dc{v%G~d43Kz4cizJL1D9a)cx-ASDg;JZB5 zfGcA1j|xMh^IqYBE6+?n@O&pI?OUfsW@%V7W<`AH-JrYkd%13+fKT@i9ofP%b%g`n zUznPG6qnxqy5iQYV_!S?uPbiroLPTZY~H$4DQubspOn(vl+rS$UQ^yT>(Pg~sXGen z61xvM*)xmi%Nl5{vCI_abJ-jjsg<WU{ko-=$*=4(ruz4cuCniR)>prO;I)TM)8K$o z+^=J%3l>%@)N0MIQt~?S-9i7ogm2d4D8)D4_1}3XFlTT2b$ZFg{;3^YOXCkeIMjNl z{x8b~yOwP&%~D>sFB;q2n)%~mCo`u4^OtTpxswT}<8O1<#+fdjcP%HQN+kEu$!RT< zwOM93#vEL}PUe4t;;x4inV)g|7T6*v$T?GSvKOOa$gk=zpX40XuV&P=GKs`4)|K<E zIU$nb^7Q@2VC};Xs@V2Rt#V<^(OULb<dOWsQpNf#iQ=O62C3BY+-Ws$C62vTIL@19 z9K&qzqv7`p{_E<I$^24URu%_+8y_67o#80sFm)}5{-w=|Q{3$|b<NJ~U|a4laeGg< z(8(Z+o$d{iM{L_piD?{jyUV-(zyb5mg;J?^t_Fo^2uxg-HBtOur{AjkS^?cl@A4ei zc+>KcpU?SreaV+1p+gh$p1i#f-gc_{MQfl%dcjTm+NGE7=e8Z0^;D^)R7f^(vvJn+ z1+P{aH@`UWqQ^OY2h%Bk8-A@mTjtm<EfI;$F)XfqzO|oO-TMAI{8_s1h^%4z!j+p% z&fer=shGHqVd-LzgNln6ep3H`dC{YF*S~FjIU%Nf*|wZRtlyLB1@9Yd%wr9BqUkrc znZelO`2(JXAK4wxvPyj3uslaRX<rM^tvZ!3eRuZ)?x-nZ=1G6}zD>wzTN!+ArlS?( zvj-Cb1DY4!@|ci5GeBH>W0j=Q(c3#V%;z>KHHub<x~h8m$bkb7cdWJX;E)#9zL(su z9+vRbBc1J};D$Nfi`OiEbfvyqcV=_h+@$wsj|yb?J0`t#&tRE!;-0I=;f(FciY*UU z9F=$5_ciqDw%ZF-F7jsH{U3KEk$;o&%&jcO9$_7KL^;05vfP^C8Kk`Bt914Ac!A1y z@0B*K4RP>yP~-k`pSQJs_hY{sN3Px75UVTx^WZ||z<^3M6Wf~2`$X-|F*Bx3sJC%A zy_D-Hx6162;x649Nh$dqe7#F%@kXXpdVG6*X!ps6#22f}{Zo4G9_Zf{yVK*2YmcDw z>6%IWOlK5+E|}&m8&s>l)N%6P%8FBg7QRxgbz5HDj$WJ_Z+|TQ`&EU>52k3oZ!!D0 z(e>OP#^-n4J3~^~UOs5_dpIpesJZ&m7Wet}9_-ysPeqIZ_k1q-YnCzZw@8(J<-Q5N z({rNFD9GA;S+Y>NGEeN+{#`2{*j}`1GielFJ?+8$pe>2<s!7c$K5Wsf?{~CFrLeth zIoPLLBRA_8f8#0P^V>p#%WEXJ$nN<U{6*rw+Nv%4c~^dYapT~YzQQ|V;%B-TK!L{< zz;K<Ve&X@>hqnK=(y;$k!ohprINeR~_CfFaO*dwr=Qb_-=OvO5BV%%KR|KDfR)*l6 zO}u<^oChC1Zgov4_`q@D*xTfrthKU>xZ~QMiOZkcC!2GVEB#3~L%riCSH1gH8J;u# z<h1@+thP#j&&5qkkDOAy@Z*H+gg+&nsgmbjM6k?ov=Xb2zkLlf`Lc<*f8yx}R?43q zDIe;#Uay~0VKw;>&!w3evwv~<TSn~^%dC3#UZC>J`DK@CZi#Yia7_$f;8(jyxb4D+ zy-V&1XNNm{ep9V#CA^D`F>S)SgRv2!t9X`W{GL|6Vpm3h$X%mm4TqN|ll<H4?{0Da zy5#b@X6sEZ4swd8>le)l^k6YM+j}S|;J#X`ug4>i?%?l^4U$V%A7k2HJzH)U>)TWI zB3#`o&&E4FUK4F_>>+z>sq3BfOpIb8S`IILgnqtO4PP`<Lrdyf?d_Sd#qoRs4|3z! z6-rYCqb4jj%YSo1Z%3{;#|M$qtQ9d=e{&c#SJe0X<cn1}!1IX7W?lWOT9z4(d+d+Z zwCf2ZuoW`Og@3hUI-_t;K6#(BIEO*=4u<~KRq~8!0`L0`?=R#A6@CrJL-+PG7<YU> zKI8ob(7>ABfyb-vfl8m>$!E%6FfpbHh(9O{eXo3g=h0uoXVxW+48|SYA9k$zE||df z@t?$J*<B6|l1ISqsYiDZo!t|H?jX|K^GEv9vV=y3r!9R93=9mOu6{1-oD!M<ed5)w diff --git a/templates/report_scop_bordereau.xml b/templates/report_scop_bordereau.xml index 2457b56..e6db969 100644 --- a/templates/report_scop_bordereau.xml +++ b/templates/report_scop_bordereau.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8" ?> <odoo> <data> @@ -9,116 +9,209 @@ <div class="row"> <div class="col-6 offset-6 mt64 mb64"> - <span t-field="o.partner_id.name" style="text-transform: uppercase; font-weight: 600;"/><br/> - <span t-field="o.partner_id.street" /><br/> - <t t-if="o.partner_id.street2"><span t-field="o.partner_id.street2"/><br/></t> - <t t-if="o.partner_id.street3"><span t-field="o.partner_id.street3"/><br/></t> - <span t-field="o.partner_id.zip" /> <span t-field="o.partner_id.city" style="text-transform: uppercase;"/> - <t t-if="o.partner_id.cedex"> <span t-field="o.partner_id.cedex"/></t> + <span + t-field="o.partner_id.name" + style="text-transform: uppercase; font-weight: 600;" + /><br /> + <span t-field="o.partner_id.street" /><br /> + <t t-if="o.partner_id.street2"><span + t-field="o.partner_id.street2" + /><br /></t> + <t t-if="o.partner_id.street3"><span + t-field="o.partner_id.street3" + /><br /></t> + <span t-field="o.partner_id.zip" /> <span + t-field="o.partner_id.city" + style="text-transform: uppercase;" + /> + <t t-if="o.partner_id.cedex"> <span + t-field="o.partner_id.cedex" + /></t> </div> </div> <div class="row"> <div class="col-12 text-center"> <h2 style="color: #E5074D;"> - Appel de cotisation <span t-esc="str(o.year)"/> + Appel de cotisation <span t-esc="str(o.year)" /> </h2> </div> </div> <div class="row"> <div class="col-12 mb16"> <p> - Paris, le <span t-esc="o.date_cotisation" t-options="{'widget': 'date', 'format': 'd MMMM YYYY'}"/> + Paris, le <span + t-esc="o.date_cotisation" + t-options="{'widget': 'date', 'format': 'd MMMM YYYY'}" + /> </p> <p> - N° adhérent : <t t-esc="str(o.partner_id.member_number_int)"/><br/> - Union régionale : <span t-field="o.partner_ur_id.name"/><br/> - N° Bordereau : <span t-field="o.name"/><t t-if="o.is_regul">-<span t-field="o.version"/></t> + N° adhérent : <t + t-esc="str(o.partner_id.member_number_int)" + /><br /> + Union régionale : <span + t-field="o.partner_ur_id.name" + /><br /> + N° Bordereau : <span t-field="o.name" /><t + t-if="o.is_regul" + >-<span t-field="o.version" /></t> </p> </div> </div> <div class="row mb16"> - <div class="col-12" style="font-style: italic; test-align: justify;"> + <div + class="col-12" + style="font-style: italic; test-align: justify;" + > Les cotisations sont calculées annuellement en début d’année sur la base du dernier exercice connu. Le versement se fait par quart tous les trimestres. Merci de retourner ce bordereau à la CG Scop avec le règlement correspondant </div> </div> <div class="row"> <div class="col-6" style="border: 1px solid #aaa;"> - <div></div> - <h5 class="mt8" style="font-weight: 600;">Cotisations annuelles</h5> + <div /> + <h5 + class="mt8" + style="font-weight: 600;" + >Cotisations annuelles</h5> <p style="font-style: italic; font-size: 13px;"> - <t t-if="o.year_liasse">Calcul basé sur la liasse fiscale + <t + t-if="o.year_liasse" + >Calcul basé sur la liasse fiscale <t t-if="o.is_liasse_previ"> - <span>Prévisionnelle</span><br/> + <span>Prévisionnelle</span><br /> </t> <t t-else=""> - <span t-esc="str(o.year_liasse)" /><br/> + <span t-esc="str(o.year_liasse)" /><br /> </t> </t> - Assiette base <span t-field="o.type_assiette" /> : <span t-field="o.montant_assiette" t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}" />. + Assiette base <span t-field="o.type_assiette" /> : <span + t-field="o.montant_assiette" + t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}" + />. </p> <p> - <t t-set="amount_line" t-value="o.get_contribution_type()" /> + <t + t-set="amount_line" + t-value="o.get_contribution_type()" + /> <table class="table table-sm" style="border: none;"> - <tr t-foreach="amount_line" t-as="line" style="border-bottom: 1px solid #ccc;"> - <td style="border: none; background: inherit; color: inherit;"><t t-esc="line.get('type_contribution_id')[1]"/></td> - <td class="text-right" style="border: none; background: inherit; color: inherit;"><t t-esc="line.get('amount_total_signed')" t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}"/></td> + <tr + t-foreach="amount_line" + t-as="line" + style="border-bottom: 1px solid #ccc;" + > + <td + style="border: none; background: inherit; color: inherit;" + ><t + t-esc="line.get('type_contribution_id')[1]" + /></td> + <td + class="text-right" + style="border: none; background: inherit; color: inherit;" + ><t + t-esc="line.get('amount_total_signed')" + t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}" + /></td> </tr> </table> </p> <p> <table class="table table-sm" style="border: none;"> <tr style="border: none"> - <td style="border: none; background: inherit; color: inherit;"> - <strong>Total cotisation annuelle <t t-esc="str(o.year)"/>*</strong> + <td + style="border: none; background: inherit; color: inherit;" + > + <strong>Total cotisation annuelle <t + t-esc="str(o.year)" + />*</strong> </td> - <td class="text-right" style="border: none; background: inherit; color: inherit;"> + <td + class="text-right" + style="border: none; background: inherit; color: inherit;" + > <t t-if="o.is_regul"> - <strong><span t-esc="sum(o.invoice_ids.filtered(lambda i: i.type == 'out_invoice').mapped('amount_total_signed'))" t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}"/></strong> + <strong><span + t-esc="sum(o.invoice_ids.filtered(lambda i: i.move_type == 'out_invoice').mapped('amount_total_signed'))" + t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}" + /></strong> </t> <t t-else=""> - <strong><span t-field="o.amount_total_cotiz" t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}"/></strong> + <strong><span + t-field="o.amount_total_cotiz" + t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}" + /></strong> </t> </td> </tr> </table> </p> <t t-if="o.is_sdd"> - <h5 style="font-weight: 600;">Échéancier de prélèvement</h5> + <h5 + style="font-weight: 600;" + >Échéancier de prélèvement</h5> </t> <t t-else=""> - <h5 style="font-weight: 600;">Échéancier de paiement</h5> + <h5 + style="font-weight: 600;" + >Échéancier de paiement</h5> </t> <p id="schedule_table"> - <t t-set="schedule_line" t-value="o.get_bordereau_move_line()" /> - <table class="table table-sm table-striped" style="border: none;"> - <tr t-foreach="schedule_line" t-as="line" style="border-bottom: 1px solid #ccc;"> - <td style="border: none; background: inherit; color: inherit;"><t t-esc="line.get('date')"/></td> - <td class="text-right" style="border: none; background: inherit; color: inherit;"><t t-esc="line.get('amount')" t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}"/></td> + <t + t-set="schedule_line" + t-value="o.get_bordereau_move_line()" + /> + <table + class="table table-sm table-striped" + style="border: none;" + > + <tr + t-foreach="schedule_line" + t-as="line" + style="border-bottom: 1px solid #ccc;" + > + <td + style="border: none; background: inherit; color: inherit;" + ><t t-esc="line.get('date')" /></td> + <td + class="text-right" + style="border: none; background: inherit; color: inherit;" + ><t + t-esc="line.get('amount')" + t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}" + /></td> </tr> </table> </p> - <p t-if="o.is_sdd" style="font-size: 13px; font-weight: bold;"> + <p + t-if="o.is_sdd" + style="font-size: 13px; font-weight: bold;" + > Référence Unique du Mandat : - <t t-esc="o.invoice_ids[0].mandate_id.unique_mandate_reference" /> + <t + t-esc="o.invoice_ids[0].mandate_id.unique_mandate_reference" + /> </p> <t t-if="not o.is_sdd"> <p class="small"> - Si vous souhaitez régler vos prochaines cotisations par prélèvement,merci de compléter et de nous retourner le document « Mandat de prélèvements SEPA » (disponible sur notre site) à l'adresse <a href="mailto:les-scop@scop.coop">les-scop@scop.coop</a>, accompagné d’un Relevé d’identité bancaire + Si vous souhaitez régler vos prochaines cotisations par prélèvement,merci de compléter et de nous retourner le document « Mandat de prélèvements SEPA » (disponible sur notre site) à l'adresse <a + href="mailto:les-scop@scop.coop" + >les-scop@scop.coop</a>, accompagné d’un Relevé d’identité bancaire </p> </t> <p class="small"> - <em>* Inclus abonnement annuel à la Revue Participer au tarif préférentiel adhérent de 22 € TTC</em> + <em + >* Inclus abonnement annuel à la Revue Participer au tarif préférentiel adhérent de 22 € TTC</em> </p> <p style="font-size: 13px;"> - <em>IBAN: FR76 4255 9100 0008 0024 8837 710 - BIC : CCOPFRPPXXX</em> + <em + >IBAN: FR76 4255 9100 0008 0024 8837 710 - BIC : CCOPFRPPXXX</em> </p> </div> <div class="col-6" style="margin-top: 220px;"> <div style="border: 1px solid #aaa; padding: 15px;"> - CG Scop <br/> - 30, rue des Epinettes <br/> + CG Scop <br /> + 30, rue des Epinettes <br /> 75017 PARIS </div> </div> @@ -128,33 +221,46 @@ <div class="row"> <div class="col-12"> <p> - <strong>COTISATION A LA CONFÉDÉRATION GÉNÉRALE DES SCOP</strong> + <strong + >COTISATION A LA CONFÉDÉRATION GÉNÉRALE DES SCOP</strong> </p> <p> - La cotisation est calculée selon la méthode la plus favorable entre trois pour mille du chiffre d’affaires et sept pour mille de la valeur ajoutée.<br/> - La valeur ajoutée retenue est celle de la liasse fiscale ou, si elle n’est pas connue, la valeur ajoutée telle que définie par la Banque de France :<br/> - <i>Valeur ajoutée = total des chiffres d’affaires nets(FL) et subventions d’exploitation(FO) + production stockée(FM) + production immobilisée(FN) - achat de marchandises(FS) - variation de stock marchandises(FT) - achat de matières premières et autres approvisionnements(FU) - variation de stock matières premières et approvisionnements(FV) - autres achats et charges externes(FW)(déduction faite du personnel extérieur et des loyers de crédit - bail) - impôts et taxes(FX).</i> + La cotisation est calculée selon la méthode la plus favorable entre trois pour mille du chiffre d’affaires et sept pour mille de la valeur ajoutée.<br + /> + La valeur ajoutée retenue est celle de la liasse fiscale ou, si elle n’est pas connue, la valeur ajoutée telle que définie par la Banque de France :<br + /> + <i + >Valeur ajoutée = total des chiffres d’affaires nets(FL) et subventions d’exploitation(FO) + production stockée(FM) + production immobilisée(FN) - achat de marchandises(FS) - variation de stock marchandises(FT) - achat de matières premières et autres approvisionnements(FU) - variation de stock matières premières et approvisionnements(FV) - autres achats et charges externes(FW)(déduction faite du personnel extérieur et des loyers de crédit - bail) - impôts et taxes(FX).</i> </p> <p> - Le calcul de la cotisation est établi une fois par an en début d’année à partir des comptes de résultat du dernier exercice connu, ou du prévisionnel pour les nouveaux adhérents qui n’ont pas encore de compte de résultat.<br/> - La cotisation est appelée trimestriellement par quart.<br/> + Le calcul de la cotisation est établi une fois par an en début d’année à partir des comptes de résultat du dernier exercice connu, ou du prévisionnel pour les nouveaux adhérents qui n’ont pas encore de compte de résultat.<br + /> + La cotisation est appelée trimestriellement par quart.<br + /> Plancher annuel: 300 € pour les SCOP dont le chiffre d’affaires est inférieur à 100 000 €, 600 € pour les autres. </p> <p class="mt64"> - <strong>COTISATION A LA FEDERATION DES SCOP DE LA COMMUNICATION</strong> + <strong + >COTISATION A LA FEDERATION DES SCOP DE LA COMMUNICATION</strong> </p> <p> - Toutes les coopératives adhérentes de la Confédération Générale des SCOP dont l’objet social se rattache aux secteurs de la communication graphique, audiovisuelle ou multimédia sont concernées.<br/> - L’assiette est la valeur ajoutée de la liasse fiscale ou, si elle n’est pas connue, la valeur ajoutée telle que définie par la Banque de France :<br/> - <i>Valeur ajoutée = total des chiffres d’affaires nets(FL) et subventions d’exploitation(FO) + production stockée(FM) + production immobilisée(FN) - achat de marchandises(FS) - variation de stock marchandises(FT) - achat de matières premières et autres approvisionnements(FU) - variation de stock matières premières et approvisionnements(FV) - autres achats et charges externes(FW)(déduction faite du personnel extérieur et des loyer de crédit - bail) - impôts et taxes(FX)</i> + Toutes les coopératives adhérentes de la Confédération Générale des SCOP dont l’objet social se rattache aux secteurs de la communication graphique, audiovisuelle ou multimédia sont concernées.<br + /> + L’assiette est la valeur ajoutée de la liasse fiscale ou, si elle n’est pas connue, la valeur ajoutée telle que définie par la Banque de France :<br + /> + <i + >Valeur ajoutée = total des chiffres d’affaires nets(FL) et subventions d’exploitation(FO) + production stockée(FM) + production immobilisée(FN) - achat de marchandises(FS) - variation de stock marchandises(FT) - achat de matières premières et autres approvisionnements(FU) - variation de stock matières premières et approvisionnements(FV) - autres achats et charges externes(FW)(déduction faite du personnel extérieur et des loyer de crédit - bail) - impôts et taxes(FX)</i> </p> <p> - Le calcul de la cotisation est établi une fois par an en début d’année à partir des comptes de résultat du dernier exercice connu.<br/> - Taux d’appel de 0,32 %.<br/> - <i>Appel de cotisation trimestriel = (valeur ajoutée x 0,32 %) / 4</i> - Pour les nouvelles coopératives qui n’ont pas encore de compte de résultat: 27 € par trimestre et par salarié.<br/> - Plancher annuel : 108 €<br/> + Le calcul de la cotisation est établi une fois par an en début d’année à partir des comptes de résultat du dernier exercice connu.<br + /> + Taux d’appel de 0,32 %.<br /> + <i + >Appel de cotisation trimestriel = (valeur ajoutée x 0,32 %) / 4</i> + Pour les nouvelles coopératives qui n’ont pas encore de compte de résultat: 27 € par trimestre et par salarié.<br + /> + Plancher annuel : 108 €<br /> Plafond annuel : 18 428 €. </p> </div> @@ -167,22 +273,24 @@ <template id="report_bordereau"> <t t-call="web.html_container"> - <t t-set="docs" t-value="docs.with_context(lang='fr')"/> + <t t-set="docs" t-value="docs.with_context(lang='fr')" /> <t t-foreach="docs" t-as="o"> - <t t-call="cgscop_cotisation_cg.report_bordereau_document" t-lang="fr"/> + <t + t-call="cgscop_cotisation_cg.report_bordereau_document" + t-lang="fr" + /> </t> </t> </template> <!-- QWeb Reports --> - <report - id="cgscop_bordereau_report" - model="scop.bordereau" - string="Bordereau de Cotisation CG" - report_type="qweb-pdf" - name="cgscop_cotisation_cg.report_bordereau" - file="cgscop_cotisation_cg.report_bordereau" - /> + <record id="cgscop_bordereau_report" model="ir.actions.report"> + <field name="name">Bordereau de Cotisation CG</field> + <field name="model">scop.bordereau</field> + <field name="report_type">qweb-pdf</field> + <field name="report_name">cgscop_cotisation_cg.report_bordereau</field> + <field name="report_file">cgscop_cotisation_cg.report_bordereau</field> + </record> </data> </odoo> diff --git a/templates/report_scop_bordereau_payments.xml b/templates/report_scop_bordereau_payments.xml index 15cf0da..f66de07 100644 --- a/templates/report_scop_bordereau_payments.xml +++ b/templates/report_scop_bordereau_payments.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8" ?> <odoo> <data> @@ -8,62 +8,117 @@ <div class="page" style="font-size: 14px;"> <div class="row"> <div class="col-6 offset-6 mt64 mb64"> - <span t-field="o.partner_id.name" style="text-transform: uppercase; font-weight: 600;"/><br/> - <span t-field="o.partner_id.street" /><br/> - <t t-if="o.partner_id.street2"><span t-field="o.partner_id.street2"/><br/></t> - <t t-if="o.partner_id.street3"><span t-field="o.partner_id.street3"/><br/></t> - <span t-field="o.partner_id.zip" /> <span t-field="o.partner_id.city" style="text-transform: uppercase;"/> - <t t-if="o.partner_id.cedex"> <span t-field="o.partner_id.cedex"/></t> + <span + t-field="o.partner_id.name" + style="text-transform: uppercase; font-weight: 600;" + /><br /> + <span t-field="o.partner_id.street" /><br /> + <t t-if="o.partner_id.street2"><span + t-field="o.partner_id.street2" + /><br /></t> + <t t-if="o.partner_id.street3"><span + t-field="o.partner_id.street3" + /><br /></t> + <span t-field="o.partner_id.zip" /> <span + t-field="o.partner_id.city" + style="text-transform: uppercase;" + /> + <t t-if="o.partner_id.cedex"> <span + t-field="o.partner_id.cedex" + /></t> </div> </div> <div class="row"> <div class="col-12 text-center"> <h2 style="color: #E5074D;"> - Etat des paiements de cotisation <span t-esc="str(o.year)"/> + Etat des paiements de cotisation <span + t-esc="str(o.year)" + /> </h2> </div> </div> <div class="row"> <div class="col-12 mb16"> <p> - N° adhérent : <t t-esc="str(o.partner_id.member_number_int)"/><br/> - Union régionale : <span t-field="o.partner_ur_id.name"/><br/> - N° Bordereau : <span t-field="o.name"/><t t-if="o.is_regul">-<span t-field="o.version"/></t> + N° adhérent : <t + t-esc="str(o.partner_id.member_number_int)" + /><br /> + Union régionale : <span + t-field="o.partner_ur_id.name" + /><br /> + N° Bordereau : <span t-field="o.name" /><t + t-if="o.is_regul" + >-<span t-field="o.version" /></t> </p> </div> </div> <div class="row"> <div class="col-6" style="border: 1px solid #aaa;"> - <div></div> - <h5 class="mt8" style="font-weight: 600;">Cotisations annuelles</h5> + <div /> + <h5 + class="mt8" + style="font-weight: 600;" + >Cotisations annuelles</h5> <p style="font-style: italic; font-size: 13px;"> - <t t-if="o.year_liasse">Calcul basé sur la liasse fiscale + <t + t-if="o.year_liasse" + >Calcul basé sur la liasse fiscale <t t-if="o.is_liasse_previ"> - <span>Prévisionnelle</span><br/> + <span>Prévisionnelle</span><br /> </t> <t t-else=""> - <span t-esc="str(o.year_liasse)" /><br/> + <span t-esc="str(o.year_liasse)" /><br /> </t> </t> - Assiette base <span t-field="o.type_assiette" /> : <span t-field="o.montant_assiette" t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}" />. + Assiette base <span t-field="o.type_assiette" /> : <span + t-field="o.montant_assiette" + t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}" + />. </p> <p> - <t t-set="amount_line" t-value="o.get_contribution_type()" /> + <t + t-set="amount_line" + t-value="o.get_contribution_type()" + /> <table class="table table-sm" style="border: none;"> - <tr t-foreach="amount_line" t-as="line" style="border-bottom: 1px solid #ccc;"> - <td style="border: none; background: inherit; color: inherit;"><t t-esc="line.get('type_contribution_id')[1]"/></td> - <td class="text-right" style="border: none; background: inherit; color: inherit;"><t t-esc="line.get('amount_total_signed')" t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}"/></td> + <tr + t-foreach="amount_line" + t-as="line" + style="border-bottom: 1px solid #ccc;" + > + <td + style="border: none; background: inherit; color: inherit;" + ><t + t-esc="line.get('type_contribution_id')[1]" + /></td> + <td + class="text-right" + style="border: none; background: inherit; color: inherit;" + ><t + t-esc="line.get('amount_total_signed')" + t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}" + /></td> </tr> </table> </p> <p> <table class="table table-sm" style="border: none;"> <tr style="border: none"> - <td style="border: none; background: inherit; color: inherit;"> - <strong>Total cotisation annuelle <t t-esc="str(o.year)"/></strong> + <td + style="border: none; background: inherit; color: inherit;" + > + <strong>Total cotisation annuelle <t + t-esc="str(o.year)" + /></strong> </td> - <td class="text-right" style="border: none; background: inherit; color: inherit;"> - <strong></strong><span t-field="o.amount_total_cotiz" t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}"/></td> + <td + class="text-right" + style="border: none; background: inherit; color: inherit;" + > + <strong /><span + t-field="o.amount_total_cotiz" + t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}" + /></td> </tr> </table> </p> @@ -71,49 +126,96 @@ <h5 style="font-weight: 600;">Échéancier</h5> <p id="schedule_table_with_payments"> - <t t-set="schedule_line" t-value="o.get_bordereau_move_line_with_payments()" /> - <table class="table table-sm table-striped" style="border: none;"> + <t + t-set="schedule_line" + t-value="o.get_bordereau_move_line_with_payments()" + /> + <table + class="table table-sm table-striped" + style="border: none;" + > <tr> <th>Date d'échéance</th> <th class="text-right">Montant appelé</th> <th class="text-right">Montant dû</th> </tr> - <tr t-foreach="schedule_line.get('plan')" t-as="line" style="border-bottom: 1px solid #ccc;"> + <tr + t-foreach="schedule_line.get('plan')" + t-as="line" + style="border-bottom: 1px solid #ccc;" + > <t t-if="line.get('debit') > 0"> - <td style="border: none; background: inherit; color: inherit;"><t t-esc="line.get('date_maturity:day')"/></td> - <td class="text-right" style="border: none; background: inherit; color: inherit;"><t t-esc="line.get('debit')" t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}"/></td> - <td class="text-right" style="border: none; background: inherit; color: inherit;"><t t-esc="line.get('amount_residual')" t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}"/></td> + <td + style="border: none; background: inherit; color: inherit;" + ><t + t-esc="line.get('date_maturity:day')" + /></td> + <td + class="text-right" + style="border: none; background: inherit; color: inherit;" + ><t + t-esc="line.get('debit')" + t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}" + /></td> + <td + class="text-right" + style="border: none; background: inherit; color: inherit;" + ><t + t-esc="line.get('amount_residual')" + t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}" + /></td> </t> </tr> </table> - <table class="table table-sm table-striped" style="border: none;"> + <table + class="table table-sm table-striped" + style="border: none;" + > <tr> <th>Montant dû</th> <th class="text-right">Total appelé</th> <th class="text-right">Total réglé</th> </tr> <tr style="border-bottom: 1px solid #ccc;"> - <td style="border: none; background: inherit; color: inherit;"> - <t t-esc="schedule_line.get('total_residual')" t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}"/> + <td + style="border: none; background: inherit; color: inherit;" + > + <t + t-esc="schedule_line.get('total_residual')" + t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}" + /> </td> - <td class="text-right" style="border: none; background: inherit; color: inherit;"> - <t t-esc="schedule_line.get('total_amount')" t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}"/> + <td + class="text-right" + style="border: none; background: inherit; color: inherit;" + > + <t + t-esc="schedule_line.get('total_amount')" + t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}" + /> </td> - <td class="text-right" style="border: none; background: inherit; color: inherit;"> - <t t-esc="schedule_line.get('total_paid')" t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}"/> + <td + class="text-right" + style="border: none; background: inherit; color: inherit;" + > + <t + t-esc="schedule_line.get('total_paid')" + t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}" + /> </td> </tr> </table> </p> <p style="font-size: 13px;"> - <em>IBAN: FR76 4255 9100 0008 0024 8837 710 - BIC : CCOPFRPPXXX</em> + <em + >IBAN: FR76 4255 9100 0008 0024 8837 710 - BIC : CCOPFRPPXXX</em> </p> </div> <div class="col-6" style="margin-top: 220px;"> <div style="border: 1px solid #aaa; padding: 15px;"> - CG Scop <br/> - 30, rue des Epinettes <br/> + CG Scop <br /> + 30, rue des Epinettes <br /> 75017 PARIS </div> </div> @@ -123,25 +225,30 @@ </t> </template> - <template id="report_bordereau_with_payments"> <t t-call="web.html_container"> - <t t-set="docs" t-value="docs.with_context(lang='fr')"/> + <t t-set="docs" t-value="docs.with_context(lang='fr')" /> <t t-foreach="docs" t-as="o"> - <t t-call="cgscop_cotisation_cg.report_bordereau_document_with_payments" t-lang="fr"/> + <t + t-call="cgscop_cotisation_cg.report_bordereau_document_with_payments" + t-lang="fr" + /> </t> </t> </template> <!-- QWeb Reports --> - <report - id="cgscop_bordereau_report_with_payments" - model="scop.bordereau" - string="Etat des paiements" - report_type="qweb-pdf" - name="cgscop_cotisation_cg.report_bordereau_with_payments" - file="cgscop_cotisation_cg.report_bordereau_with_payments" - /> + <record id="cgscop_bordereau_report_with_payments" model="ir.actions.report"> + <field name="name">Etat des paiements</field> + <field name="model">scop.bordereau</field> + <field name="report_type">qweb-pdf</field> + <field + name="report_name" + >cgscop_cotisation_cg.report_bordereau_with_payments</field> + <field + name="report_file" + >cgscop_cotisation_cg.report_bordereau_with_payments</field> + </record> </data> </odoo> diff --git a/templates/report_scop_bordereau_refund.xml b/templates/report_scop_bordereau_refund.xml index 822a22e..a0b6c7f 100644 --- a/templates/report_scop_bordereau_refund.xml +++ b/templates/report_scop_bordereau_refund.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8" ?> <odoo> <data> @@ -8,54 +8,107 @@ <div class="page" style="font-size: 14px;"> <div class="row"> <div class="col-6 offset-6 mt64 mb64"> - <span t-field="o.partner_id.name" style="text-transform: uppercase; font-weight: 600;"/><br/> - <span t-field="o.partner_id.street" /><br/> - <t t-if="o.partner_id.street2"><span t-field="o.partner_id.street2"/><br/></t> - <t t-if="o.partner_id.street3"><span t-field="o.partner_id.street3"/><br/></t> - <span t-field="o.partner_id.zip" /> <span t-field="o.partner_id.city" style="text-transform: uppercase;"/> - <t t-if="o.partner_id.cedex"> <span t-field="o.partner_id.cedex"/></t> + <span + t-field="o.partner_id.name" + style="text-transform: uppercase; font-weight: 600;" + /><br /> + <span t-field="o.partner_id.street" /><br /> + <t t-if="o.partner_id.street2"><span + t-field="o.partner_id.street2" + /><br /></t> + <t t-if="o.partner_id.street3"><span + t-field="o.partner_id.street3" + /><br /></t> + <span t-field="o.partner_id.zip" /> <span + t-field="o.partner_id.city" + style="text-transform: uppercase;" + /> + <t t-if="o.partner_id.cedex"> <span + t-field="o.partner_id.cedex" + /></t> </div> </div> <div class="row"> <div class="col-12 text-center"> <h2 style="color: #E5074D;"> - Avoir sur Appel de cotisation <span t-esc="str(o.year)"/> + Avoir sur Appel de cotisation <span + t-esc="str(o.year)" + /> </h2> </div> </div> <div class="row"> <div class="col-12 mb16"> <p> - Paris, le <span t-esc="o.date_regul" t-options="{'widget': 'date', 'format': 'd MMMM YYYY'}"/> + Paris, le <span + t-esc="o.date_regul" + t-options="{'widget': 'date', 'format': 'd MMMM YYYY'}" + /> </p> <p> - N° adhérent : <t t-esc="str(o.partner_id.member_number_int)"/><br/> - Union régionale : <span t-field="o.partner_ur_id.name"/><br/> - N° Bordereau : <span t-field="o.name"/><t t-if="o.is_regul">-<span t-field="o.version"/></t> + N° adhérent : <t + t-esc="str(o.partner_id.member_number_int)" + /><br /> + Union régionale : <span + t-field="o.partner_ur_id.name" + /><br /> + N° Bordereau : <span t-field="o.name" /><t + t-if="o.is_regul" + >-<span t-field="o.version" /></t> </p> </div> </div> <div class="row"> <div class="col-6" style="border: 1px solid #aaa;"> - <div></div> - <h5 class="mt8" style="font-weight: 600;">Cotisations annuelles</h5> + <div /> + <h5 + class="mt8" + style="font-weight: 600;" + >Cotisations annuelles</h5> <p> - <t t-set="amount_line" t-value="o.get_contribution_type_refund()" /> + <t + t-set="amount_line" + t-value="o.get_contribution_type_refund()" + /> <table class="table table-sm" style="border: none;"> - <tr t-foreach="amount_line" t-as="line" style="border-bottom: 1px solid #ccc;"> - <td style="border: none; background: inherit; color: inherit;"><t t-esc="line.get('type_contribution_id')[1]"/></td> - <td class="text-right" style="border: none; background: inherit; color: inherit;"><t t-esc="line.get('amount_total_signed')" t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}"/></td> + <tr + t-foreach="amount_line" + t-as="line" + style="border-bottom: 1px solid #ccc;" + > + <td + style="border: none; background: inherit; color: inherit;" + ><t + t-esc="line.get('type_contribution_id')[1]" + /></td> + <td + class="text-right" + style="border: none; background: inherit; color: inherit;" + ><t + t-esc="line.get('amount_total_signed')" + t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}" + /></td> </tr> </table> </p> <p> <table class="table table-sm" style="border: none;"> <tr style="border: none"> - <td style="border: none; background: inherit; color: inherit;"> - <strong>Total avoir cotisation annuelle <t t-esc="str(o.year)"/></strong> + <td + style="border: none; background: inherit; color: inherit;" + > + <strong>Total avoir cotisation annuelle <t + t-esc="str(o.year)" + /></strong> </td> - <td class="text-right" style="border: none; background: inherit; color: inherit;"> - <strong></strong><span t-esc="sum(list(map(lambda l: l.get('amount_total_signed'), amount_line)))" t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}"/> + <td + class="text-right" + style="border: none; background: inherit; color: inherit;" + > + <strong /><span + t-esc="sum(list(map(lambda l: l.get('amount_total_signed'), amount_line)))" + t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}" + /> </td> </tr> </table> @@ -66,8 +119,8 @@ <div class="col-6" style="margin-top: 220px;"> <div style="border: 1px solid #aaa; padding: 15px;"> - CG Scop <br/> - 30, rue des Epinettes <br/> + CG Scop <br /> + 30, rue des Epinettes <br /> 75017 PARIS </div> </div> @@ -78,24 +131,28 @@ <template id="report_bordereau_refund"> <t t-call="web.html_container"> - <t t-set="docs" t-value="docs.with_context(lang='fr')"/> + <t t-set="docs" t-value="docs.with_context(lang='fr')" /> <t t-foreach="docs" t-as="o"> - <t t-call="cgscop_cotisation_cg.report_bordereau_document_refund" t-lang="fr"/> + <t + t-call="cgscop_cotisation_cg.report_bordereau_document_refund" + t-lang="fr" + /> </t> </t> </template> - <!-- QWeb Reports --> - <report - id="cgscop_bordereau_report_refund" - model="scop.bordereau" - string="Avoir de Cotisation CG" - report_type="qweb-pdf" - name="cgscop_cotisation_cg.report_bordereau_refund" - file="cgscop_cotisation_cg.report_bordereau_refund" - groups="cgscop_partner.group_cg_administrator" - /> + <record id="cgscop_bordereau_report_refund" model="ir.actions.report"> + <field name="name">Avoir de Cotisation CG</field> + <field name="model">scop.bordereau</field> + <field name="report_type">qweb-pdf</field> + <field + name="report_name" + >cgscop_cotisation_cg.report_bordereau_refund</field> + <field + name="report_file" + >cgscop_cotisation_cg.report_bordereau_refund</field> + </record> </data> </odoo> diff --git a/templates/report_union_sociale.xml b/templates/report_union_sociale.xml index b9ef2d8..1567dee 100644 --- a/templates/report_union_sociale.xml +++ b/templates/report_union_sociale.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8" ?> <odoo> <data> @@ -19,91 +19,151 @@ font-size: 9px; } </style> - <t t-if="not o" t-set="o" t-value="doc"/> + <t t-if="not o" t-set="o" t-value="doc" /> <div class="page" style="font-size: 10px;"> <t t-foreach="range(1,5)" t-as="quarter"> - <div id="union-sociale container" class="mt16 m32" style="page-break-inside: avoid;"> - <hr/> + <div + id="union-sociale container" + class="mt16 m32" + style="page-break-inside: avoid;" + > + <hr /> <div class="row"> <div class="col-8"> - <p style="margin-bottom: 0; color: #E5074D; font-weight: bold; font-size: 13px;"> + <p + style="margin-bottom: 0; color: #E5074D; font-weight: bold; font-size: 13px;" + > COTISATION UNION SOCIALE DES SCOP ET DES SCIC - - <t t-esc="quarter"/> + <t t-esc="quarter" /> <t t-if="quarter == 1">er </t> <t t-else="">ème </t> Trimestre - <t t-esc="env.context.get('year', datetime.datetime.now().year)"/> + <t + t-esc="env.context.get('year', datetime.datetime.now().year)" + /> </p> <p><strong><i> L’Union Sociale perçoit directement sa cotisation, merci de lui envoyer un règlement distinct</i></strong></p> <p class="mt16"> - <strong>N° ADHÉRENT : <t t-esc="o.member_number"/></strong> - <br/> - <strong>IDENTITÉ SOCIÉTÉ : <t t-esc="o.name.upper()"/></strong> + <strong>N° ADHÉRENT : <t + t-esc="o.member_number" + /></strong> + <br /> + <strong>IDENTITÉ SOCIÉTÉ : <t + t-esc="o.name.upper()" + /></strong> </p> </div> <div class="col-4"> <div class="text-right" style="font-size: 9px;"> - <img src="/cgscop_cotisation_cg/static/src/img/logo-usscop.png" class="img-fluid" style="max-height: 60px;"/> - <br/> - Tel. : 01 43 07 18 08<br/> - <a href="mailto:contact@union-sociale.coop">contact@union-sociale.coop</a> - <a href="https://www.union-sociale.coop">www.union-sociale.coop</a> + <img + src="/cgscop_cotisation_cg/static/src/img/logo-usscop.png" + class="img-fluid" + style="max-height: 60px;" + /> + <br /> + Tel. : 01 43 07 18 08<br /> + <a + href="mailto:contact@union-sociale.coop" + >contact@union-sociale.coop</a> - <a + href="https://www.union-sociale.coop" + >www.union-sociale.coop</a> </div> </div> </div> <div class="row mt16"> <div class="col-6"> <div> - <p class="presta"><strong>SALAIRES BRUTS</strong> (non plafonnés du trimestre) :</p> - <input type="text" t-attf-name="salaire-{{quarter}}" class="input-empty presta"/> + <p class="presta"><strong + >SALAIRES BRUTS</strong> (non plafonnés du trimestre) :</p> + <input + type="text" + t-attf-name="salaire-{{quarter}}" + class="input-empty presta" + /> </div> <div> <p class="presta"> - - Pour <strong>PRESTABAT</strong> Scop parisiennes du bâtiment et des travaux publics x <strong>0,40</strong> : + - Pour <strong + >PRESTABAT</strong> Scop parisiennes du bâtiment et des travaux publics x <strong + >0,40</strong> : </p> - <input type="text" t-attf-name="prestabat-{{quarter}}" class="input-empty presta"/> + <input + type="text" + t-attf-name="prestabat-{{quarter}}" + class="input-empty presta" + /> </div> <div> <p class="presta"> - - Pour <strong>PRESTASUP</strong> Scop sur le plan national adhérentes à la super section x <strong>0,40</strong> : + - Pour <strong + >PRESTASUP</strong> Scop sur le plan national adhérentes à la super section x <strong + >0,40</strong> : </p> - <input type="text" t-attf-name="prestasup-{{quarter}}" class="input-empty presta"/> + <input + type="text" + t-attf-name="prestasup-{{quarter}}" + class="input-empty presta" + /> </div> <div> <p class="presta"> - - Pour <strong>PRESTA</strong> les autres Scop x <strong>0,30</strong> : + - Pour <strong + >PRESTA</strong> les autres Scop x <strong + >0,30</strong> : </p> - <input type="text" t-attf-name="presta-{{quarter}}" class="input-empty presta"/> + <input + type="text" + t-attf-name="presta-{{quarter}}" + class="input-empty presta" + /> </div> <div> <p class="presta"> - Nombre de salariés à la fin de la période : </p> - <input type="text" t-attf-name="salaries-{{quarter}}" class="input-empty"/> + <input + type="text" + t-attf-name="salaries-{{quarter}}" + class="input-empty" + /> </div> <div> - <p class="presta" style="margin-top: 10px; margin-bottom: 5px; font-size: 11px;"> + <p + class="presta" + style="margin-top: 10px; margin-bottom: 5px; font-size: 11px;" + > <strong> À retourner à l’Union Sociale avant le : <t t-if="quarter != 4"> <t t-if="quarter != 3"> - 15/0<t t-esc="quarter * 3 + 1"/>/<t t-esc="env.context.get('year', datetime.datetime.now().year)"/> + 15/0<t t-esc="quarter * 3 + 1" />/<t + t-esc="env.context.get('year', datetime.datetime.now().year)" + /> </t> <t t-else=""> - 15/<t t-esc="quarter * 3 + 1"/>/<t t-esc="env.context.get('year', datetime.datetime.now().year)"/> + 15/<t t-esc="quarter * 3 + 1" />/<t + t-esc="env.context.get('year', datetime.datetime.now().year)" + /> </t> </t> <t t-else=""> - 15/0<t t-esc="quarter - 3"/>/<t t-esc="env.context.get('year', datetime.datetime.now().year) + 1"/> + 15/0<t t-esc="quarter - 3" />/<t + t-esc="env.context.get('year', datetime.datetime.now().year) + 1" + /> </t> </strong> </p> <div class="row"> - <div class="col-1"><span class="input-block-empty"/></div> + <div class="col-1"><span + class="input-block-empty" + /></div> <!-- <div class="col-1"><input type="checkbox" t-attf-name="cheque-{{quarter}}" class="input-block-empty"/></div>--> <div class="col-5">Chèque</div> - <div class="col-1"><span class="input-block-empty"/></div> + <div class="col-1"><span + class="input-block-empty" + /></div> <!-- <div class="col-1"><input type="checkbox" t-attf-name="vir-{{quarter}}" class="input-block-empty"/></div>--> <div class="col-5">Virement</div> </div> @@ -115,9 +175,11 @@ </div> <div class="col-6"> <div style="padding-top: 70px; "> - <div style="padding: 20px; border: 1px solid #aaa; font-size: 13px;"> - Union sociale des Scop et des Scic<br/> - 61, boulevard de Picpus<br/> + <div + style="padding: 20px; border: 1px solid #aaa; font-size: 13px;" + > + Union sociale des Scop et des Scic<br /> + 61, boulevard de Picpus<br /> 75012 Paris </div> </div> @@ -130,9 +192,12 @@ <template id="report_union_sociale"> <t t-call="web.basic_layout"> - <t t-set="docs" t-value="docs.with_context(lang='fr')"/> + <t t-set="docs" t-value="docs.with_context(lang='fr')" /> <t t-foreach="docs" t-as="o"> - <t t-call="cgscop_cotisation_cg.report_union_sociale_document" t-lang="fr"/> + <t + t-call="cgscop_cotisation_cg.report_union_sociale_document" + t-lang="fr" + /> </t> </t> </template> @@ -145,22 +210,20 @@ <field name="margin_bottom">10</field> <field name="margin_left">10</field> <field name="margin_right">10</field> - <field name="header_line" eval="False"/> + <field name="header_line" eval="False" /> <field name="header_spacing">0</field> <field name="dpi">90</field> </record> <!-- QWeb Reports --> - <report - id="cgscop_union_sociale_report" - model="res.partner" - string="Bordereau Union Sociale" - report_type="qweb-pdf" - name="cgscop_cotisation_cg.report_union_sociale" - file="cgscop_cotisation_cg.report_union_sociale" - paperformat="paperformat_union_sociale" - attachment_use="False" - /> + <record id="cgscop_union_sociale_report" model="ir.actions.report"> + <field name="name">Bordereau Union Sociale</field> + <field name="model">res.partner</field> + <field name="report_type">qweb-pdf</field> + <field name="report_name">cgscop_cotisation_cg.report_union_sociale</field> + <field name="report_file">cgscop_cotisation_cg.report_union_sociale</field> + <field name="paperformat_id" ref="paperformat_union_sociale" /> + </record> </data> </odoo> diff --git a/views/account_invoice.xml b/views/account_invoice.xml deleted file mode 100644 index 0c01505..0000000 --- a/views/account_invoice.xml +++ /dev/null @@ -1,95 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<odoo> - <data> - - <!-- Form --> - <record id="invoice_form_scop_cg_inherited" model="ir.ui.view"> - <field name="name">account.invoice.form.scop.cg.inherited</field> - <field name="model">account.invoice</field> - <field name="mode">primary</field> - <field name="inherit_id" ref="cgscop_cotisation.invoice_form_scop_inherited"/> - <field name="arch" type="xml"> - - <xpath expr="//header/button[@name='action_invoice_open']" position="attributes"> - <attribute name="states"/> - <attribute name="invisible">1</attribute> - </xpath> - <xpath expr="//header/button[@name='preview_invoice']" position="attributes"> - <attribute name="invisible">True</attribute> - </xpath> - <xpath expr="//header/button[@name='%(account.action_account_invoice_refund)d']" position="attributes"> - <attribute name="invisible">True</attribute> - </xpath> - - <xpath expr="//field[@name='liasse_fiscale_id']" position="replace"> - <group> - <field name="liasse_fiscale_id" readonly="1"/> - <field name="bordereau_id" readonly="1"/> - </group> - <group> - <field name="amount_cg_calculated" readonly="1"/> - <field name="cotiz_quarter" readonly="1"/> - </group> - </xpath> - - <xpath expr="//field[@name='partner_id']" position="attributes"> - <attribute name="readonly">True</attribute> - </xpath> - - <xpath expr="//field[@name='payment_term_id']" position="attributes"> - <attribute name="invisible">1</attribute> - </xpath> - - <xpath expr="//field[@name='date_due']" position="attributes"> - <attribute name="attrs">{'invisible':[('state','=','draft')]}</attribute> - </xpath> - - <xpath expr="//field[@name='payment_mode_id']" position="attributes"> - <attribute name="attrs">{'invisible':[('state','=','draft')]}</attribute> - </xpath> - - </field> - </record> - - <!-- Search --> - <record id="invoice_search_scop_cg_inherited" model="ir.ui.view"> - <field name="name">account.invoice.search.scop.cg.inherited</field> - <field name="model">account.invoice</field> - <field name="priority" eval="25"/> - <field name="mode">primary</field> - <field name="inherit_id" ref="cgscop_cotisation.invoice_search_scop_inherited"/> - <field name="arch" type="xml"> - - <xpath expr="//filter[@name='group_by_partner_id']" position="before"> - <filter name="by_type_cotiz" string="Type de cotisation" context="{'group_by':'type_contribution_id'}"/> - <filter name="by_ur" string="Union régionale" context="{'group_by':'partner_ur_id'}"/> - <separator/> - </xpath> - - </field> - </record> - - <!-- Action --> - <record id="action_scop_cg_appel_cotisation" model="ir.actions.act_window"> - <field name="name">Appels de cotisations</field> - <field name="res_model">account.invoice</field> - <field name="view_mode">tree,form,kanban,calendar,graph,pivot</field> - <field name="domain" eval="[('type','in', ['out_invoice', 'out_refund']), ('is_contribution', '!=', False), ('company_id', '=', ref('base.main_company'))]"/> - <field name="context" eval="{'default_type':'out_invoice', 'type':'out_invoice', 'journal_type': 'sale', 'default_is_contribution': True, 'create': False,}"/> - <field name="search_view_id" ref="invoice_search_scop_cg_inherited"/> - </record> - <record id="action_scop_cg_appel_cotisation_tree" model="ir.actions.act_window.view"> - <field eval="1" name="sequence"/> - <field name="view_mode">tree</field> - <field name="view_id" ref="cgscop_cotisation.invoice_tree_scop_inherited"/> - <field name="act_window_id" ref="action_scop_cg_appel_cotisation"/> - </record> - <record id="action_scop_cg_appel_cotisation_form" model="ir.actions.act_window.view"> - <field eval="2" name="sequence"/> - <field name="view_mode">form</field> - <field name="view_id" ref="invoice_form_scop_cg_inherited"/> - <field name="act_window_id" ref="action_scop_cg_appel_cotisation"/> - </record> - - </data> -</odoo> \ No newline at end of file diff --git a/views/account_move.xml b/views/account_move.xml new file mode 100644 index 0000000..7bda03b --- /dev/null +++ b/views/account_move.xml @@ -0,0 +1,141 @@ +<?xml version="1.0" encoding="utf-8" ?> +<odoo> + <data> + + <!-- Form --> + <record id="invoice_form_scop_cg_inherited" model="ir.ui.view"> + <field name="name">account.move.form.scop.cg.inherited</field> + <field name="model">account.move</field> + <field name="priority">100</field> + <field name="mode">primary</field> + <field + name="inherit_id" + ref="cgscop_cotisation.account_move_form_scop_inherited" + /> + <field name="arch" type="xml"> + + <xpath + expr="//header/button[@name='action_post']" + position="attributes" + > + <attribute name="states" /> + <attribute name="invisible">1</attribute> + </xpath> + <xpath + expr="//header/button[@name='preview_invoice']" + position="attributes" + > + <attribute name="invisible">True</attribute> + </xpath> + <xpath + expr="//header/button[@name='action_reverse']" + position="attributes" + > + <attribute name="invisible">True</attribute> + </xpath> + + <xpath expr="//field[@name='liasse_fiscale_id']" position="replace"> + <group> + <field name="liasse_fiscale_id" readonly="1" /> + <field name="bordereau_id" readonly="1" /> + </group> + <group> + <field name="amount_cg_calculated" readonly="1" /> + <field name="cotiz_quarter" readonly="1" /> + </group> + </xpath> + + <xpath expr="//field[@name='partner_id']" position="attributes"> + <attribute name="readonly">True</attribute> + </xpath> + + <xpath + expr="//field[@name='invoice_payment_term_id']" + position="attributes" + > + <attribute name="invisible">1</attribute> + </xpath> + + <xpath expr="//field[@name='invoice_date_due']" position="attributes"> + <attribute + name="attrs" + >{'invisible':[('state','=','draft')]}</attribute> + </xpath> + + <xpath expr="//field[@name='payment_mode_id']" position="attributes"> + <attribute + name="attrs" + >{'invisible':[('state','=','draft')]}</attribute> + </xpath> + + </field> + </record> + + <!-- Search --> + <record id="invoice_search_scop_cg_inherited" model="ir.ui.view"> + <field name="name">account.move.search.scop.cg.inherited</field> + <field name="model">account.move</field> + <field name="priority" eval="25" /> + <field name="mode">primary</field> + <field + name="inherit_id" + ref="cgscop_cotisation.account_move_search_scop_inherited" + /> + <field name="arch" type="xml"> + + <xpath expr="//filter[@name='group_by_partner_id']" position="before"> + <filter + name="by_type_cotiz" + string="Type de cotisation" + context="{'group_by':'type_contribution_id'}" + /> + <filter + name="by_ur" + string="Union régionale" + context="{'group_by':'partner_ur_id'}" + /> + <separator /> + </xpath> + + </field> + </record> + + <!-- Action --> + <record id="action_scop_cg_appel_cotisation" model="ir.actions.act_window"> + <field name="name">Appels de cotisations</field> + <field name="res_model">account.move</field> + <field name="view_mode">tree,form,kanban,graph,pivot</field> + <field + name="domain" + eval="[('move_type','in', ['out_invoice', 'out_refund']), ('is_contribution', '!=', False), ('company_id', '=', ref('base.main_company'))]" + /> + <field + name="context" + eval="{'default_mov_type':'out_invoice', 'move_type':'out_invoice', 'journal_type': 'sale', 'default_is_contribution': True, 'create': False,}" + /> + <field name="search_view_id" ref="invoice_search_scop_cg_inherited" /> + </record> + <record + id="action_scop_cg_appel_cotisation_tree" + model="ir.actions.act_window.view" + > + <field eval="1" name="sequence" /> + <field name="view_mode">tree</field> + <field + name="view_id" + ref="cgscop_cotisation.account_move_tree_scop_inherited" + /> + <field name="act_window_id" ref="action_scop_cg_appel_cotisation" /> + </record> + <record + id="action_scop_cg_appel_cotisation_form" + model="ir.actions.act_window.view" + > + <field eval="2" name="sequence" /> + <field name="view_mode">form</field> + <field name="view_id" ref="invoice_form_scop_cg_inherited" /> + <field name="act_window_id" ref="action_scop_cg_appel_cotisation" /> + </record> + + </data> +</odoo> diff --git a/views/menus.xml b/views/menus.xml index cf83ae6..2e3a434 100644 --- a/views/menus.xml +++ b/views/menus.xml @@ -1,32 +1,39 @@ -<?xml version="1.0"?> +<?xml version="1.0" ?> <!-- Copyright 2019 Le Filament License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> - <odoo> <data> <!-- MENUS --> - <menuitem id="menu_scop_cotisation_cg_main" - parent="cgscop_cotisation.menu_scop_cotisation" - name="Gestion des cotisations CG Scop" - groups="cgscop_cotisation_cg.group_cotisation_cg_administrative" - sequence="10"/> - <menuitem id="menu_scop_cotisation_cg_calcul" - name="Campagnes de cotisations" - parent="cgscop_cotisation_cg.menu_scop_cotisation_cg_main" - groups="cgscop_cotisation_cg.group_cotisation_cg_administrative" - action="action_scop_cotisation_cg" - sequence="10"/> - <menuitem name="Bordereaux" - id="scop_bordereau_menu" - parent="cgscop_cotisation_cg.menu_scop_cotisation_cg_main" - groups="cgscop_cotisation_cg.group_cotisation_cg_administrative" - action="scop_bordereau_act_window" - sequence="20"/> - <menuitem id="menu_scop_cotisation_cg_appel_cotisation" - name="Appels de cotisations" - parent="cgscop_cotisation_cg.menu_scop_cotisation_cg_main" - groups="cgscop_cotisation_cg.group_cotisation_cg_administrative" - action="action_scop_cg_appel_cotisation" - sequence="30"/> + <menuitem + id="menu_scop_cotisation_cg_main" + parent="cgscop_cotisation.menu_scop_cotisation" + name="Gestion des cotisations CG Scop" + groups="cgscop_cotisation_cg.group_cotisation_cg_administrative" + sequence="10" + /> + <menuitem + id="menu_scop_cotisation_cg_calcul" + name="Campagnes de cotisations" + parent="cgscop_cotisation_cg.menu_scop_cotisation_cg_main" + groups="cgscop_cotisation_cg.group_cotisation_cg_administrative" + action="action_scop_cotisation_cg" + sequence="10" + /> + <menuitem + name="Bordereaux" + id="scop_bordereau_menu" + parent="cgscop_cotisation_cg.menu_scop_cotisation_cg_main" + groups="cgscop_cotisation_cg.group_cotisation_cg_administrative" + action="scop_bordereau_act_window" + sequence="20" + /> + <menuitem + id="menu_scop_cotisation_cg_appel_cotisation" + name="Appels de cotisations" + parent="cgscop_cotisation_cg.menu_scop_cotisation_cg_main" + groups="cgscop_cotisation_cg.group_cotisation_cg_administrative" + action="action_scop_cg_appel_cotisation" + sequence="30" + /> </data> </odoo> diff --git a/views/res_config_settings.xml b/views/res_config_settings.xml index 77a1f0e..ee3ebbe 100644 --- a/views/res_config_settings.xml +++ b/views/res_config_settings.xml @@ -1,70 +1,144 @@ -<?xml version="1.0" encoding="UTF-8"?> +<?xml version="1.0" encoding="UTF-8" ?> <odoo> <data> <record id="res_config_settings_view_form_cotisations_cg" model="ir.ui.view"> <field name="name">res.config.settings.view.form.inherit.cotisations</field> <field name="model">res.config.settings</field> - <field name="priority" eval="25"/> - <field name="inherit_id" ref="cgscop_cotisation.res_config_settings_view_form_cotisations"/> + <field name="priority" eval="25" /> + <field + name="inherit_id" + ref="cgscop_cotisation.res_config_settings_view_form_cotisations" + /> <field name="arch" type="xml"> <xpath expr="//div[@name='contribution']" position="inside"> - <div class="col-xs-12 col-md-6 o_setting_box" attrs="{'invisible': [('is_contribution', '!=', True)]}"> + <div + class="col-xs-12 col-md-6 o_setting_box" + attrs="{'invisible': [('is_contribution', '!=', True)]}" + > <div class="o_setting_left_pane"> <field name="is_contribution_cg" /> </div> <div class="o_setting_right_pane"> - <label for="is_contribution_cg"/> + <label for="is_contribution_cg" /> <div class="text-muted"> Activer la gestion des cotisations de la CG Scop </div> </div> - <hr attrs="{'invisible': [('is_contribution_cg', '=', False)]}"/> - <div class="o_setting_left_pane"/> - <div class="o_setting_right_pane" attrs="{'invisible': [('is_contribution_cg', '=', False)]}"> - <div><label for="contribution_cg_id"/></div> - <field name="contribution_cg_id" options="{'no_open': True, 'no_create': True}" attrs="{'required': [('is_contribution_cg', '=', True)]}"/> + <hr + attrs="{'invisible': [('is_contribution_cg', '=', False)]}" + /> + <div class="o_setting_left_pane" /> + <div + class="o_setting_right_pane" + attrs="{'invisible': [('is_contribution_cg', '=', False)]}" + > + <div><label for="contribution_cg_id" /></div> + <field + name="contribution_cg_id" + options="{'no_open': True, 'no_create': True}" + attrs="{'required': [('is_contribution_cg', '=', True)]}" + /> </div> - <hr attrs="{'invisible': [('is_contribution_cg', '=', False)]}"/> - <div class="o_setting_right_pane" attrs="{'invisible': [('is_contribution_cg', '=', False)]}"> - <div><label for="contribution_ur_or_fede_journal_id"/></div> - <field name="contribution_ur_or_fede_journal_id" options="{'no_open': True, 'no_create': True}" attrs="{'required': [('is_contribution_cg', '=', True)]}"/> + <hr + attrs="{'invisible': [('is_contribution_cg', '=', False)]}" + /> + <div + class="o_setting_right_pane" + attrs="{'invisible': [('is_contribution_cg', '=', False)]}" + > + <div><label for="contribution_fede_com_id" /></div> + <field + name="contribution_fede_com_id" + options="{'no_open': True, 'no_create': True}" + attrs="{'required': [('is_contribution_cg', '=', True)]}" + /> </div> - <hr attrs="{'invisible': [('is_contribution_cg', '=', False)]}"/> - <div class="o_setting_right_pane" attrs="{'invisible': [('is_contribution_cg', '=', False)]}"> - <div><label for="contribution_fede_com_id"/></div> - <field name="contribution_fede_com_id" options="{'no_open': True, 'no_create': True}" attrs="{'required': [('is_contribution_cg', '=', True)]}"/> + <div + class="o_setting_right_pane" + attrs="{'invisible': [('is_contribution_cg', '=', False)]}" + > + <div><label for="journal_fede_com_id" /></div> + <field + name="journal_fede_com_id" + options="{'no_open': True, 'no_create': True}" + attrs="{'required': [('is_contribution_cg', '=', True)]}" + /> </div> - <div class="o_setting_right_pane" attrs="{'invisible': [('is_contribution_cg', '=', False)]}"> - <div><label for="receivable_account_fede_com_id"/></div> - <field name="receivable_account_fede_com_id" options="{'no_open': True, 'no_create': True}" attrs="{'required': [('is_contribution_cg', '=', True)]}"/> + <hr + attrs="{'invisible': [('is_contribution_cg', '=', False)]}" + /> + <div + class="o_setting_right_pane" + attrs="{'invisible': [('is_contribution_cg', '=', False)]}" + > + <div><label for="contribution_fede_cae_id" /></div> + <field + name="contribution_fede_cae_id" + options="{'no_open': True, 'no_create': True}" + attrs="{'required': [('is_contribution_cg', '=', True)]}" + /> </div> - <hr attrs="{'invisible': [('is_contribution_cg', '=', False)]}"/> - <div class="o_setting_right_pane" attrs="{'invisible': [('is_contribution_cg', '=', False)]}"> - <div><label for="contribution_fede_cae_id"/></div> - <field name="contribution_fede_cae_id" options="{'no_open': True, 'no_create': True}" attrs="{'required': [('is_contribution_cg', '=', True)]}"/> + <div + class="o_setting_right_pane" + attrs="{'invisible': [('is_contribution_cg', '=', False)]}" + > + <div><label for="journal_fede_cae_id" /></div> + <field + name="journal_fede_cae_id" + options="{'no_open': True, 'no_create': True}" + attrs="{'required': [('is_contribution_cg', '=', True)]}" + /> </div> - <div class="o_setting_right_pane" attrs="{'invisible': [('is_contribution_cg', '=', False)]}"> - <div><label for="receivable_account_fede_cae_id"/></div> - <field name="receivable_account_fede_cae_id" options="{'no_open': True, 'no_create': True}" attrs="{'required': [('is_contribution_cg', '=', True)]}"/> + <hr + attrs="{'invisible': [('is_contribution_cg', '=', False)]}" + /> + <div + class="o_setting_right_pane" + attrs="{'invisible': [('is_contribution_cg', '=', False)]}" + > + <div><label for="contribution_hdf_id" /></div> + <field + name="contribution_hdf_id" + options="{'no_open': True, 'no_create': True}" + attrs="{'required': [('is_contribution_cg', '=', True)]}" + /> </div> - <hr attrs="{'invisible': [('is_contribution_cg', '=', False)]}"/> - <div class="o_setting_right_pane" attrs="{'invisible': [('is_contribution_cg', '=', False)]}"> - <div><label for="contribution_hdf_id"/></div> - <field name="contribution_hdf_id" options="{'no_open': True, 'no_create': True}" attrs="{'required': [('is_contribution_cg', '=', True)]}"/> + <div + class="o_setting_right_pane" + attrs="{'invisible': [('is_contribution_cg', '=', False)]}" + > + <div><label for="journal_ur_hdf_id" /></div> + <field + name="journal_ur_hdf_id" + options="{'no_open': True, 'no_create': True}" + attrs="{'required': [('is_contribution_cg', '=', True)]}" + /> </div> - <div class="o_setting_right_pane" attrs="{'invisible': [('is_contribution_cg', '=', False)]}"> - <div><label for="receivable_account_ur_hdf_id"/></div> - <field name="receivable_account_ur_hdf_id" options="{'no_open': True, 'no_create': True}" attrs="{'required': [('is_contribution_cg', '=', True)]}"/> + <hr + attrs="{'invisible': [('is_contribution_cg', '=', False)]}" + /> + <div + class="o_setting_right_pane" + attrs="{'invisible': [('is_contribution_cg', '=', False)]}" + > + <div><label for="contribution_med_id" /></div> + <field + name="contribution_med_id" + options="{'no_open': True, 'no_create': True}" + attrs="{'required': [('is_contribution_cg', '=', True)]}" + /> </div> - <hr attrs="{'invisible': [('is_contribution_cg', '=', False)]}"/> - <div class="o_setting_right_pane" attrs="{'invisible': [('is_contribution_cg', '=', False)]}"> - <div><label for="contribution_med_id"/></div> - <field name="contribution_med_id" options="{'no_open': True, 'no_create': True}" attrs="{'required': [('is_contribution_cg', '=', True)]}"/> - </div> - <div class="o_setting_right_pane" attrs="{'invisible': [('is_contribution_cg', '=', False)]}"> - <div><label for="receivable_account_ur_med_id"/></div> - <field name="receivable_account_ur_med_id" options="{'no_open': True, 'no_create': True}" attrs="{'required': [('is_contribution_cg', '=', True)]}"/> + <div + class="o_setting_right_pane" + attrs="{'invisible': [('is_contribution_cg', '=', False)]}" + > + <div><label for="journal_ur_med_id" /></div> + <field + name="journal_ur_med_id" + options="{'no_open': True, 'no_create': True}" + attrs="{'required': [('is_contribution_cg', '=', True)]}" + /> </div> </div> </xpath> @@ -72,4 +146,4 @@ </record> </data> -</odoo> \ No newline at end of file +</odoo> diff --git a/views/scop_bordereau_cg.xml b/views/scop_bordereau_cg.xml index 53c34d8..661365b 100644 --- a/views/scop_bordereau_cg.xml +++ b/views/scop_bordereau_cg.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8" ?> <!-- Copyright 2021 Le Filament License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> <odoo> @@ -11,155 +11,330 @@ <field name="arch" type="xml"> <form create="false" string="Bordereaux"> <header> - <field name="state" widget="statusbar" clickable="False" readonly="True"/> - <button name="button_validate_bordereau" class="oe_highlight" string="Valider le bordereau" type="object" - states="new" confirm="Confirmer la validation du bordereau ?"/> - <button name="print_bordereau" string="Imprimer" type="object" states="validated,paid"/> - <button name="action_send_email" string="Envoyer par mail" type="object" states="validated"/> - <button name="validate_ongoing_bordereau" class="oe_highlight" string="Valider cette nouvelle version" type="object" states="ongoing"/> - <button name="cancel_ongoing_bordereau" class="oe_highlight" string="Annuler cette nouvelle version" type="object" states="ongoing"/> - <button name="action_change_payment_mode" - class="btn-info" string="Changer le mode de paiement" - type="object" states="ongoing,validated"/> - <button name="%(cgscop_cotisation_cg.scop_cotisation_regul_wizard_act_window)d" - class="btn-warning" - string="Effectuer une Régularisation" - type="action" - states="validated,paid"/> - <button name="%(cgscop_cotisation_cg.scop_bordereau_refund_wizard_act_window)d" - class="btn-warning" - string="Ajouter un avoir" - type="action" - states="ongoing,validated,paid"/> - <button name="%(cgscop_bordereau_report_refund)d" - class="btn-info" - string="Imprimer l'avoir" - type="action" - states="cancel"/> + <field + name="state" + widget="statusbar" + clickable="False" + readonly="True" + /> + <button + name="button_validate_bordereau" + class="oe_highlight" + string="Valider le bordereau" + type="object" + states="new" + confirm="Confirmer la validation du bordereau ?" + /> + <button + name="print_bordereau" + string="Imprimer" + type="object" + states="validated,paid" + /> + <button + name="action_send_email" + string="Envoyer par mail" + type="object" + states="validated" + /> + <button + name="validate_ongoing_bordereau" + class="oe_highlight" + string="Valider cette nouvelle version" + type="object" + states="ongoing" + /> + <button + name="cancel_ongoing_bordereau" + class="oe_highlight" + string="Annuler cette nouvelle version" + type="object" + states="ongoing" + /> + <button + name="action_change_payment_mode" + class="btn-info" + string="Changer le mode de paiement" + type="object" + states="ongoing,validated" + /> + <button + name="%(cgscop_cotisation_cg.scop_cotisation_regul_wizard_act_window)d" + class="btn-warning" + string="Effectuer une Régularisation" + type="action" + states="validated,paid" + /> + <button + name="%(cgscop_cotisation_cg.scop_bordereau_refund_wizard_act_window)d" + class="btn-warning" + string="Ajouter un avoir" + type="action" + states="ongoing,validated,paid" + /> + <button + name="%(cgscop_bordereau_report_refund)d" + class="btn-info" + string="Imprimer l'avoir" + type="action" + states="cancel" + /> </header> <sheet> <div class="oe_button_box" name="button_box"> - <button name="action_show_liasse" type="object" - class="oe_stat_button" icon="fa-bar-chart-o" - attrs="{'invisible':[('liasse_count','=',0)]}"> - <field string="Liasses" name="liasse_count" widget="statinfo"/> + <button + name="action_show_liasse" + type="object" + class="oe_stat_button" + icon="fa-bar-chart-o" + attrs="{'invisible':[('liasse_count','=',0)]}" + > + <field + string="Liasses" + name="liasse_count" + widget="statinfo" + /> </button> - <button name="action_show_cotiz" type="object" - class="oe_stat_button" icon="fa-pencil-square-o" - attrs="{'invisible':[('invoice_count','=',0)]}"> - <field string="Appels" name="invoice_count" widget="statinfo"/> + <button + name="action_show_cotiz" + type="object" + class="oe_stat_button" + icon="fa-pencil-square-o" + attrs="{'invisible':[('invoice_count','=',0)]}" + > + <field + string="Appels" + name="invoice_count" + widget="statinfo" + /> </button> - <button class="oe_stat_button" name="open_payment" - string="Paiements en cours" type="object" - attrs="{'invisible':['|', ('has_outstanding','=',False), ('state', '!=', 'validated')]}" icon="fa-university"/> + <button + class="oe_stat_button" + name="open_payment" + string="Paiements en cours" + type="object" + attrs="{'invisible':['|', ('has_outstanding','=',False), ('state', '!=', 'validated')]}" + icon="fa-university" + /> </div> <div class="oe_title"> <h1> - <field name="name"/> + <field name="name" /> </h1> </div> <h4> - Adhérent : <field name="partner_id"/> <br/> - UR : <field name="partner_ur_id" readonly="True" options="{'no_open': True}"/> + Adhérent : <field name="partner_id" /> <br /> + UR : <field + name="partner_ur_id" + readonly="True" + options="{'no_open': True}" + /> </h4> - <field name="has_outstanding" invisible="1"/> - <div class="alert alert-info mt-3" role="alert" - attrs="{'invisible': ['|', ('has_outstanding','=',False), ('state', '!=', 'validated')]}"> + <field name="has_outstanding" invisible="1" /> + <div + class="alert alert-info mt-3" + role="alert" + attrs="{'invisible': ['|', ('has_outstanding','=',False), ('state', '!=', 'validated')]}" + > Vous avez des paiements en circulation pour ce bordereau. </div> - <div class="alert alert-warning" role="alert" attrs="{'invisible': [('state', '!=', 'ongoing')]}"> - <strong>Bordereau en cours de modification </strong>(Détails dans l'onglet "Historique")<br/> + <div + class="alert alert-warning" + role="alert" + attrs="{'invisible': [('state', '!=', 'ongoing')]}" + > + <strong + >Bordereau en cours de modification </strong>(Détails dans l'onglet "Historique")<br + /> </div> - <field name="is_regul" invisible="1"/> - <div class="alert alert-warning" role="alert" attrs="{'invisible': [('is_regul', '!=', True)]}"> - <strong>Bordereau annulé le : </strong><field name="date_regul" readonly="True"/><br/> - <strong>Motif : </strong><field name="comment_regul" readonly="True"/><br/> - <strong>Nouveau bordereau : </strong><field name="refund_id" readonly="True"/> + <field name="is_regul" invisible="1" /> + <div + class="alert alert-warning" + role="alert" + attrs="{'invisible': [('is_regul', '!=', True)]}" + > + <strong>Bordereau annulé le : </strong><field + name="date_regul" + readonly="True" + /><br /> + <strong>Motif : </strong><field + name="comment_regul" + readonly="True" + /><br /> + <strong>Nouveau bordereau : </strong><field + name="refund_id" + readonly="True" + /> </div> - <div class="alert alert-warning" role="alert" attrs="{'invisible': ['|', ('is_regul', '=', True), ('comment_regul', '=', False)]}"> - <strong>Bordereau version <field name="version" readonly="1"/></strong><br/> - <strong>Modifié le : </strong><field name="date_regul" readonly="True"/><br/> - <strong>Motif : </strong><field name="comment_regul" readonly="True"/><br/> + <div + class="alert alert-warning" + role="alert" + attrs="{'invisible': ['|', ('is_regul', '=', True), ('comment_regul', '=', False)]}" + > + <strong>Bordereau version <field + name="version" + readonly="1" + /></strong><br /> + <strong>Modifié le : </strong><field + name="date_regul" + readonly="True" + /><br /> + <strong>Motif : </strong><field + name="comment_regul" + readonly="True" + /><br /> </div> <group> <group> - <field name="base_cotisation_cg"/> - <field name="date_cotisation" attrs="{'readonly':[('state','!=','new')]}"/> - <field name="payment_mode_id" attrs="{'readonly':[('state','!=','new')]}"/> - <hr/> - <field name="nb_quarter" attrs="{'readonly':[('state','!=','new')]}"/> - <button name="update_bordereau_with_liasse" class="btn" string="Recalculer le bordereau" - confirm="Recalculer les valeurs du bordereau en fonction de la liasse et du nombre de trimestres ?" type="object" states="new"/> + <field name="base_cotisation_cg" /> + <field + name="date_cotisation" + attrs="{'readonly':[('state','!=','new')]}" + /> + <field + name="payment_mode_id" + attrs="{'readonly':[('state','!=','new')]}" + /> + <hr /> + <field + name="nb_quarter" + attrs="{'readonly':[('state','!=','new')]}" + /> + <button + name="update_bordereau_with_liasse" + class="btn" + string="Recalculer le bordereau" + confirm="Recalculer les valeurs du bordereau en fonction de la liasse et du nombre de trimestres ?" + type="object" + states="new" + /> </group> <group> - <span class="oe_grey" attrs="{'invisible': [('liasse_fiscale_id', '!=', False)]}">Pas de liasse fiscale pour le calcul</span> - <field name="liasse_fiscale_id" attrs="{'invisible': [('liasse_fiscale_id', '=', False)]}"/> - <field name="year_liasse" attrs="{'invisible': [('liasse_fiscale_id', '=', False)]}"/> - <field name="type_liasse_fiscale" options="{'no_open': True}" attrs="{'invisible': [('liasse_fiscale_id', '=', False)]}"/> - <button name="action_change_liasse" - class="btn-info mb5" - string="Changer de liasse fiscale" - type="object" - states="new"/> - <br/> - <hr/> - <field name="type_assiette" attrs="{'invisible': [('liasse_fiscale_id', '=', False)]}"/> - <field name="montant_assiette" attrs="{'invisible': [('liasse_fiscale_id', '=', False)]}"/> - <field name="amount_total_cotiz"/> - <field name="details" nolabel="1"/> + <span + class="oe_grey" + attrs="{'invisible': [('liasse_fiscale_id', '!=', False)]}" + >Pas de liasse fiscale pour le calcul</span> + <field + name="liasse_fiscale_id" + attrs="{'invisible': [('liasse_fiscale_id', '=', False)]}" + /> + <field + name="year_liasse" + attrs="{'invisible': [('liasse_fiscale_id', '=', False)]}" + /> + <field + name="type_liasse_fiscale" + options="{'no_open': True}" + attrs="{'invisible': [('liasse_fiscale_id', '=', False)]}" + /> + <button + name="action_change_liasse" + class="btn-info mb5" + string="Changer de liasse fiscale" + type="object" + states="new" + /> + <br /> + <hr /> + <field + name="type_assiette" + attrs="{'invisible': [('liasse_fiscale_id', '=', False)]}" + /> + <field + name="montant_assiette" + attrs="{'invisible': [('liasse_fiscale_id', '=', False)]}" + /> + <field name="amount_total_cotiz" /> + <field name="details" nolabel="1" /> </group> </group> <notebook> <page string="Appels de cotisation"> - <field name="invoice_ids" widget="one2many" class="mt-2"> - <tree edit="false" create="false" delete="false" default_order="type_contribution_id, cotiz_quarter" - decoration-info="state == 'draft'" decoration-success="state == 'paid'" - decoration-muted="state == 'cancel'" decoration-danger="type == 'out_refund' and state == 'open'"> - <field name="type" invisible="1"/> - <field name="type_contribution_id"/> - <field name="cotiz_quarter"/> - <field name="date_due"/> - <field name="name"/> - <field name="amount_total_signed" string="Montant total" sum="Total"/> - <field name="residual_signed" string="Montant dû" sum="Total"/> - <field name="state"/> - <button name="view_cotiz" string="Afficher" type="object" icon="fa-pencil-square-o"/> + <field + name="invoice_ids" + widget="one2many" + class="mt-2" + > + <tree + edit="false" + create="false" + delete="false" + default_order="type_contribution_id, cotiz_quarter" + decoration-info="state == 'draft'" + decoration-success="state == 'paid'" + decoration-muted="state == 'cancel'" + decoration-danger="move_type == 'out_refund' and state == 'posted'" + > + <field name="move_type" invisible="1" /> + <field name="type_contribution_id" /> + <field name="cotiz_quarter" /> + <field name="invoice_date_due" /> + <field name="name" /> + <field + name="amount_total_signed" + string="Montant total" + sum="Total" + /> + <field + name="amount_residual_signed" + string="Montant dû" + sum="Total" + /> + <field name="state" /> + <button + name="view_cotiz" + string="Afficher" + type="object" + icon="fa-pencil-square-o" + /> </tree> <form> <group> - <field name="type_contribution_id"/> - <field name="cotiz_quarter"/> - <field name="date_due"/> - <field name="name"/> - <field name="amount_cg_calculated"/> - <field name="amount_total_signed"/> - <field name="residual_signed"/> + <field name="type_contribution_id" /> + <field name="cotiz_quarter" /> + <field name="invoice_date_due" /> + <field name="name" /> + <field name="amount_cg_calculated" /> + <field name="amount_total_signed" /> + <field name="amount_residual_signed" /> <field name="nb_quarter" /> - <field name="state"/> + <field name="state" /> </group> </form> </field> </page> <page string="Historique"> - <field name="bordereau_version_ids" widget="one2many" class="mt-2"> - <tree create="false" delete="false" default_order="version desc" editable="top"> - <field name="date"/> - <field name="version"/> - <field name="comment" attrs="{'readonly':[('state','=','validated')]}"/> - <field name="liasse_fiscale_id_old"/> - <field name="type_assiette"/> - <field name="montant_assiette"/> - <field name="amount_total_cotiz"/> - <field name="state" widget="label_selection"/> + <field + name="bordereau_version_ids" + widget="one2many" + class="mt-2" + > + <tree + create="false" + delete="false" + default_order="version desc" + editable="top" + > + <field name="date" /> + <field name="version" /> + <field + name="comment" + attrs="{'readonly':[('state','=','validated')]}" + /> + <field name="liasse_fiscale_id_old" /> + <field name="type_assiette" /> + <field name="montant_assiette" /> + <field name="amount_total_cotiz" /> + <field name="state" widget="label_selection" /> </tree> </field> </page> </notebook> </sheet> <div class="oe_chatter"> - <field name="message_follower_ids" widget="mail_followers"/> - <field name="message_ids" widget="mail_thread"/> + <field name="message_follower_ids" widget="mail_followers" /> + <field name="message_ids" widget="mail_thread" /> </div> </form> </field> @@ -170,16 +345,20 @@ <field name="name">scop.bordereau.tree</field> <field name="model">scop.bordereau</field> <field name="arch" type="xml"> - <tree decoration-info="state == 'new'" create="false" string="Bordereaux"> - <field name="member_number"/> - <field name="partner_id"/> - <field name="partner_ur_id"/> - <field name="year"/> - <field name="state"/> - <field name="type_assiette"/> - <field name="montant_assiette"/> - <field name="amount_total_cotiz" sum="Total"/> - <field name="amount_residual" sum="Total"/> + <tree + decoration-info="state == 'new'" + create="false" + string="Bordereaux" + > + <field name="member_number" /> + <field name="partner_id" /> + <field name="partner_ur_id" /> + <field name="year" /> + <field name="state" /> + <field name="type_assiette" /> + <field name="montant_assiette" /> + <field name="amount_total_cotiz" sum="Total" /> + <field name="amount_residual" sum="Total" /> </tree> </field> </record> @@ -190,42 +369,100 @@ <field name="model">scop.bordereau</field> <field name="arch" type="xml"> <search string="Bordereaux"> - <field name="partner_id" string="Adhérent"/> - <field name="member_number" string="N° Adhérent"/> - <field name="name" string="Référence du bordereau"/> - <field name="year" string="Année de cotisation"/> - <filter name="state_new" string="Brouillon" - domain="[('state', '=', 'new')]"/> - <filter name="state_ongoing" string="En cours de modification" - domain="[('state', '=', 'ongoing')]"/> - <filter name="state_validated" string="Validé" - domain="[('state', '=', 'validated')]"/> - <filter name="state_paid" string="Payé" - domain="[('state', '=', 'paid')]"/> - <filter name="has_outstanding" string="Paiements en cours" - domain="[('has_outstanding', '=', True)]"/> - <separator/> - <filter name="current_year" string="Campagne année en cours" - domain="[('year', '=', (context_today()).strftime('%Y'))]"/> - <filter name="previous_year" string="Campagne année précédente" - domain="[('year', '=', (context_today()-datetime.timedelta(weeks=52)).strftime('%Y'))]"/> - <filter name="next_year" string="Campagne année suivante" - domain="[('year', '=', (context_today()+datetime.timedelta(weeks=52)).strftime('%Y'))]"/> - <separator/> - <filter name="bordereau_without_liasse" string="Pas de liasse de référence" - domain="[('liasse_fiscale_id', '=', False)]"/> - <filter name="assiette_equals_zero" string="Assiette(s) égale(s) à zéro" - domain="[('montant_assiette', '=', 0)]"/> - <separator/> - <filter name="4_quarter" string="Sur 4 trimestres" domain="[('nb_quarter', '=', '4')]"/> - <filter name="3_quarter" string="Sur 3 trimestres" domain="[('nb_quarter', '=', '3')]"/> - <filter name="2_quarter" string="Sur 2 trimestres" domain="[('nb_quarter', '=', '2')]"/> - <filter name="1_quarter" string="Sur 1 trimestre" domain="[('nb_quarter', '=', '1')]"/> - <separator/> - <filter name="is_ca" string="Assiette CA" domain="[('type_assiette', '=', 'ca')]"/> - <filter name="is_va" string="Assiette VA" domain="[('type_assiette', '=', 'va')]"/> + <field name="partner_id" string="Adhérent" /> + <field name="member_number" string="N° Adhérent" /> + <field name="name" string="Référence du bordereau" /> + <field name="year" string="Année de cotisation" /> + <filter + name="state_new" + string="Brouillon" + domain="[('state', '=', 'new')]" + /> + <filter + name="state_ongoing" + string="En cours de modification" + domain="[('state', '=', 'ongoing')]" + /> + <filter + name="state_validated" + string="Validé" + domain="[('state', '=', 'validated')]" + /> + <filter + name="state_paid" + string="Payé" + domain="[('state', '=', 'paid')]" + /> + <filter + name="has_outstanding" + string="Paiements en cours" + domain="[('has_outstanding', '=', True)]" + /> + <separator /> + <filter + name="current_year" + string="Campagne année en cours" + domain="[('year', '=', (context_today()).strftime('%Y'))]" + /> + <filter + name="previous_year" + string="Campagne année précédente" + domain="[('year', '=', (context_today()-datetime.timedelta(weeks=52)).strftime('%Y'))]" + /> + <filter + name="next_year" + string="Campagne année suivante" + domain="[('year', '=', (context_today()+datetime.timedelta(weeks=52)).strftime('%Y'))]" + /> + <separator /> + <filter + name="bordereau_without_liasse" + string="Pas de liasse de référence" + domain="[('liasse_fiscale_id', '=', False)]" + /> + <filter + name="assiette_equals_zero" + string="Assiette(s) égale(s) à zéro" + domain="[('montant_assiette', '=', 0)]" + /> + <separator /> + <filter + name="4_quarter" + string="Sur 4 trimestres" + domain="[('nb_quarter', '=', '4')]" + /> + <filter + name="3_quarter" + string="Sur 3 trimestres" + domain="[('nb_quarter', '=', '3')]" + /> + <filter + name="2_quarter" + string="Sur 2 trimestres" + domain="[('nb_quarter', '=', '2')]" + /> + <filter + name="1_quarter" + string="Sur 1 trimestre" + domain="[('nb_quarter', '=', '1')]" + /> + <separator /> + <filter + name="is_ca" + string="Assiette CA" + domain="[('type_assiette', '=', 'ca')]" + /> + <filter + name="is_va" + string="Assiette VA" + domain="[('type_assiette', '=', 'va')]" + /> <group string="Group By"> - <filter name="by_year" string="Année de cotisation" context="{'group_by':'year'}"/> + <filter + name="by_year" + string="Année de cotisation" + context="{'group_by':'year'}" + /> </group> </search> </field> @@ -241,4 +478,4 @@ </record> </data> -</odoo> \ No newline at end of file +</odoo> diff --git a/views/scop_cotisation_cg.xml b/views/scop_cotisation_cg.xml index cead779..552baeb 100644 --- a/views/scop_cotisation_cg.xml +++ b/views/scop_cotisation_cg.xml @@ -1,7 +1,6 @@ -<?xml version="1.0"?> +<?xml version="1.0" ?> <!-- Copyright 2019 Le Filament License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> - <odoo> <data> @@ -11,12 +10,12 @@ <field name="model">scop.cotisation.cg</field> <field name="arch" type="xml"> <tree string="Cotisations"> - <field name="year"/> - <field name="name"/> - <field name="create_date"/> - <field name="state"/> - <field name="member_count"/> - <field name="write_date"/> + <field name="year" /> + <field name="name" /> + <field name="create_date" /> + <field name="state" /> + <field name="member_count" /> + <field name="write_date" /> </tree> </field> </record> @@ -28,50 +27,105 @@ <field name="arch" type="xml"> <form string="Cotisations"> <header> - <button name="bordereaux_view" type="object" string="Voir les bordereaux"/> - <button name="cotiz_view" type="object" string="Voir les appels de cotisations"/> - <button name="cotiz_generate" type="object" string="Calculer les cotisatisons de renouvellement" class="oe_highlight" states="new,ongoing"/> - <button name="cotiz_generate_wizard" type="object" string="Calculer les cotisatisons des nouveaux adhérents" class="oe_highlight" states="new,ongoing"/> - <button name="bordereau_validate" type="object" string="Valider les bordereaux" class="oe_highlight" - states="new,ongoing" confirm="Les bordereaux validés ne pourront plus être remis en brouillon"/> - <field name="state" widget="statusbar" clickable="False"/> + <button + name="bordereaux_view" + type="object" + string="Voir les bordereaux" + /> + <button + name="cotiz_view" + type="object" + string="Voir les appels de cotisations" + /> + <button + name="cotiz_generate" + type="object" + string="Calculer les cotisations de renouvellement" + class="oe_highlight" + states="new,ongoing" + /> + <button + name="cotiz_generate_wizard" + type="object" + string="Calculer les cotisatisons des nouveaux adhérents" + class="oe_highlight" + states="new,ongoing" + /> + <button + name="bordereau_validate" + type="object" + string="Valider les bordereaux" + class="oe_highlight" + states="new,ongoing" + confirm="Les bordereaux validés ne pourront plus être remis en brouillon" + /> + <field name="state" widget="statusbar" clickable="False" /> </header> <sheet> - <h1><field name="name"/></h1> + <h1><field name="name" /></h1> <group> <group name="contribution" string="Cotisations"> - <field name="year"/> - <field name="date_cotisation" attrs="{'readonly': [('state', '!=', 'new')], 'required': [('state', '!=', 'new')]}"/> - <field name="create_date"/> - <field name="write_date"/> + <field name="year" /> + <field + name="date_cotisation" + attrs="{'readonly': [('state', '!=', 'new')], 'required': [('state', '!=', 'new')]}" + /> + <field name="create_date" /> + <field name="write_date" /> </group> <group name="count_contribution" string="Adhérents"> - <field name="member_count" readonly="1"/> - <field name="new_member_count" readonly="1"/> - <field name="invoice_count" readonly="1"/> - <field name="invoice_valid_count" readonly="1"/> + <field name="member_count" readonly="1" /> + <field name="new_member_count" readonly="1" /> + <field name="invoice_count" readonly="1" /> + <field name="invoice_valid_count" readonly="1" /> </group> </group> <group> <group name="schedule" string="Échéancier"> - <field name="trimester_1" attrs="{'required': [('state', '!=', 'new')]}"/> - <field name="trimester_2" attrs="{'required': [('state', '!=', 'new')]}"/> - <field name="trimester_3" attrs="{'required': [('state', '!=', 'new')]}"/> - <field name="trimester_4" attrs="{'required': [('state', '!=', 'new')]}"/> + <field + name="trimester_1" + attrs="{'required': [('state', '!=', 'new')]}" + /> + <field + name="trimester_2" + attrs="{'required': [('state', '!=', 'new')]}" + /> + <field + name="trimester_3" + attrs="{'required': [('state', '!=', 'new')]}" + /> + <field + name="trimester_4" + attrs="{'required': [('state', '!=', 'new')]}" + /> </group> </group> <notebook> <page name="simulation" string="Simulations"> - <h5 attrs="{'invisible': [('state', '!=', 'new')]}">Ajouter une simulation :</h5> - <hr attrs="{'invisible': [('state', '!=', 'new')]}"/> - <button name="add_simul" class="btn btn-outline-info" style="margin-right: 20px; width: 250px;" - type="object" string="Globale : CG, UR et Fédération" - attrs="{'invisible': [('state', '!=', 'new')]}" context="{'type_simul': 'all'}"/> + <h5 + attrs="{'invisible': [('state', '!=', 'new')]}" + >Ajouter une simulation :</h5> + <hr attrs="{'invisible': [('state', '!=', 'new')]}" /> + <button + name="add_simul" + class="btn btn-outline-info" + style="margin-right: 20px; width: 250px;" + type="object" + string="Globale : CG, UR et Fédération" + attrs="{'invisible': [('state', '!=', 'new')]}" + context="{'type_simul': 'all'}" + /> - <button name="add_simul" class="btn btn-outline-info" style="width: 250px;" - type="object" string="CG Scop uniquement" - attrs="{'invisible': [('state', '!=', 'new')]}" context="{'type_simul': 'cgscop'}"/> - <field name="simul_ids" mode="tree"/> + <button + name="add_simul" + class="btn btn-outline-info" + style="width: 250px;" + type="object" + string="CG Scop uniquement" + attrs="{'invisible': [('state', '!=', 'new')]}" + context="{'type_simul': 'cgscop'}" + /> + <field name="simul_ids" mode="tree" /> </page> </notebook> </sheet> @@ -84,38 +138,62 @@ <field name="name">scop.cotisation.cg.kanban</field> <field name="model">scop.cotisation.cg</field> <field name="arch" type="xml"> - <kanban class="oe_background_grey o_kanban_dashboard o_account_kanban" default_order="year desc"> - <field name="name"/> - <field name="year"/> - <field name="state"/> - <field name="member_count"/> - <field name="new_member_count"/> - <field name="percent_cotiz_paid"/> - <field name="graph_values"/> - <field name="amount_called"/> - <field name="amount_paid"/> - <field name="amount_residual"/> + <kanban + class="oe_background_grey o_kanban_dashboard o_account_kanban" + default_order="year desc" + > + <field name="name" /> + <field name="year" /> + <field name="state" /> + <field name="member_count" /> + <field name="new_member_count" /> + <field name="percent_cotiz_paid" /> + <field name="graph_values" /> + <field name="amount_called" /> + <field name="amount_paid" /> + <field name="amount_residual" /> <templates> <t t-name="kanban-box"> - <div class="container o_kanban_card_content oe_kanban_global_click"> + <div + class="container o_kanban_card_content oe_kanban_global_click" + > <div class="row"> <div class="col"> - <h2 class="mt16"><field name="name"/></h2> - <hr/> + <h2 class="mt16"><field name="name" /></h2> + <hr /> <t> <p class="text-muted"> - Nombre d'adhérents renouvelés : <field name="member_count"/> <br /> - Nombre de nouveaux adhérents : <field name="new_member_count"/> <br /> + Nombre d'adhérents renouvelés : <field + name="member_count" + /> <br /> + Nombre de nouveaux adhérents : <field + name="new_member_count" + /> <br /> </p> </t> </div> </div> <div class="row"> <div class="col-6 text-center"> - <field name="percent_cotiz_paid" widget="gauge"/> - <div class="btn-group-vertical"> - <button type="object" name="cotiz_view" class="btn btn-info">Voir les appels <br /> de cotisations</button> - <button type="object" name="bordereaux_view" class="btn btn-info">Voir les bordereaux</button> + <field + name="percent_cotiz_paid" + widget="gauge" + /> + <div + class="btn-group btn-group-vertical" + role="group" + > + <button + type="object" + name="cotiz_view" + class="btn btn-info" + >Voir les appels <br + /> de cotisations</button> + <button + type="object" + name="bordereaux_view" + class="btn btn-info" + >Voir les bordereaux</button> </div> </div> <div class="col-6"> @@ -123,21 +201,31 @@ <tbody> <tr> <td>Montant appelé</td> - <td><field name="amount_called"/> €</td> + <td><field + name="amount_called" + /> €</td> </tr> <tr> <td>Montant réglé</td> - <td><field name="amount_paid"/> €</td> + <td><field + name="amount_paid" + /> €</td> </tr> <tr> <td>Montant à percevoir</td> - <td><field name="amount_residual"/> €</td> + <td><field + name="amount_residual" + /> €</td> </tr> </tbody> </table> </div> <div> - <field name="graph_values" widget="dashboard_graph" graph_type="bar"/> + <field + name="graph_values" + widget="dashboard_graph" + graph_type="bar" + /> </div> </div> </div> diff --git a/views/scop_cotisation_simulation.xml b/views/scop_cotisation_simulation.xml index 57e73ed..103d947 100644 --- a/views/scop_cotisation_simulation.xml +++ b/views/scop_cotisation_simulation.xml @@ -1,7 +1,6 @@ -<?xml version="1.0"?> +<?xml version="1.0" ?> <!-- Copyright 2019 Le Filament License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> - <odoo> <data> @@ -11,16 +10,21 @@ <field name="model">scop.cotisation.cg.simulation</field> <field name="arch" type="xml"> <tree string="Simulations" create="0" edit="0"> - <field name="create_date" string="Date de création"/> - <field name="type_simul"/> + <field name="create_date" string="Date de création" /> + <field name="type_simul" /> <field name="file" /> - <field name="total_member"/> - <field name="total_cg"/> - <field name="total_hdf"/> - <field name="total_med"/> - <field name="total_com"/> - <field name="total_cae"/> - <button name="get_simul_file" type="object" string="Télécharger la simulation" class="btn btn-sm btn-outline-info"/> + <field name="total_member" /> + <field name="total_cg" /> + <field name="total_hdf" /> + <field name="total_med" /> + <field name="total_com" /> + <field name="total_cae" /> + <button + name="get_simul_file" + type="object" + string="Télécharger la simulation" + class="btn btn-sm btn-outline-info" + /> </tree> </field> </record> @@ -34,17 +38,23 @@ <sheet> <group> <group string="Fichier simulation"> - <field name="create_date" string="Date de création"/> - <field name="type_simul" readonly="1"/> - <button name="get_simul_file" type="object" icon="fa-download" string="Télécharger la simulation" class="btn btn-sm btn-outline-info"/> + <field name="create_date" string="Date de création" /> + <field name="type_simul" readonly="1" /> + <button + name="get_simul_file" + type="object" + icon="fa-download" + string="Télécharger la simulation" + class="btn btn-sm btn-outline-info" + /> </group> <group string="Données globales"> - <field name="total_member" readonly="1"/> - <field name="total_cg" readonly="1"/> - <field name="total_hdf" readonly="1"/> - <field name="total_med" readonly="1"/> - <field name="total_com" readonly="1"/> - <field name="total_cae" readonly="1"/> + <field name="total_member" readonly="1" /> + <field name="total_cg" readonly="1" /> + <field name="total_hdf" readonly="1" /> + <field name="total_med" readonly="1" /> + <field name="total_com" readonly="1" /> + <field name="total_cae" readonly="1" /> </group> </group> </sheet> @@ -53,7 +63,10 @@ </record> - <record id="view_scop_cotisation_cg_simulation_action" model="ir.actions.act_window"> + <record + id="view_scop_cotisation_cg_simulation_action" + model="ir.actions.act_window" + > <field name="name">Simulation Cotisation</field> <field name="type">ir.actions.act_window</field> <field name="res_model">scop.cotisation.cg.simulation</field> diff --git a/views/scop_liasse_fiscale.xml b/views/scop_liasse_fiscale.xml index c9556bf..4781e34 100644 --- a/views/scop_liasse_fiscale.xml +++ b/views/scop_liasse_fiscale.xml @@ -1,7 +1,6 @@ -<?xml version="1.0"?> +<?xml version="1.0" ?> <!-- Copyright 2021 Le Filament License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> - <odoo> <data> @@ -9,25 +8,43 @@ <record id="view_scop_liasse_fiscale_simulation_form" model="ir.ui.view"> <field name="name">scop.liasse.fiscale.contribution.form</field> <field name="model">scop.liasse.fiscale</field> - <field name="inherit_id" ref="cgscop_liste_ministere.scop_liasse_fiscale_form_view_prio"/> + <field + name="inherit_id" + ref="cgscop_liasse_fiscale.scop_liasse_fiscale_form_view_prio" + /> <field name="arch" type="xml"> <xpath expr="//form" position="inside"> - <hr/> + <hr /> <div class="alert alert-info" role="alert"> - <strong><u>Cotisations théoriques</u></strong><br/> + <strong><u>Cotisations théoriques</u></strong><br /> Cette section donne les montants théoriques des diverses cotisations en fonction des valeurs de la liasse fiscale. </div> <group name="simulation"> <group> - <field name="contribution_base_type" string="Type Assiette"/> - <field name="contribution_base_amount" string="Montant Assiette"/> - <field name="contribution_cg" string="Cotisation CG Scop"/> + <field + name="contribution_base_type" + string="Type Assiette" + /> + <field + name="contribution_base_amount" + string="Montant Assiette" + /> + <field name="contribution_cg" string="Cotisation CG Scop" /> </group> <group> - <field name="contribution_hdf" string="Cotisation HDF"/> - <field name="contribution_med" string="Cotisation Méditerranée"/> - <field name="contribution_com" string="Cotisation Fédération Com"/> - <field name="contribution_cae" string="Cotisation Fédération CAE"/> + <field name="contribution_hdf" string="Cotisation HDF" /> + <field + name="contribution_med" + string="Cotisation Méditerranée" + /> + <field + name="contribution_com" + string="Cotisation Fédération Com" + /> + <field + name="contribution_cae" + string="Cotisation Fédération CAE" + /> </group> </group> </xpath> diff --git a/wizard/__init__.py b/wizard/__init__.py index a23c4c1..be42db4 100644 --- a/wizard/__init__.py +++ b/wizard/__init__.py @@ -1,7 +1,7 @@ # © 2020 Le Filament (<http://www.le-filament.com>) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from . import account_invoice_refund +from . import account_move_reversal from . import export_journal_wizard from . import scop_bordereau_update_liasse_wizard from . import scop_bordereau_payment_mode_wizard diff --git a/wizard/account_invoice_refund.py b/wizard/account_invoice_refund.py deleted file mode 100644 index 489b288..0000000 --- a/wizard/account_invoice_refund.py +++ /dev/null @@ -1,24 +0,0 @@ -# © 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, api - - -class ContributionCgAccountInvoiceRefund(models.TransientModel): - _inherit = 'account.invoice.refund' - - def _get_refund(self, inv, mode): - """ - Surcharge la la fonction initiale pour ajouter l'id - de la base de cotisation cg - :param inv: facture - :param mode: wizard mode - :return: avoir - """ - refund = super( - ContributionCgAccountInvoiceRefund, self)._get_refund(inv, mode) - refund.update({ - 'cotisation_cg_id': inv.cotisation_cg_id.id, - 'bordereau_id': inv.bordereau_id.id, - }) - return refund diff --git a/wizard/account_move_reversal.py b/wizard/account_move_reversal.py new file mode 100644 index 0000000..89eee4f --- /dev/null +++ b/wizard/account_move_reversal.py @@ -0,0 +1,25 @@ +# © 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 + + +class AccountMoveReversal(models.TransientModel): + _inherit = "account.move.reversal" + + def _prepare_default_reversal(self, move): + """ + Surcharge la la fonction initiale pour ajouter l'id + de la base de cotisation cg + :param inv: facture + :param mode: wizard mode + :return: avoir + """ + refund = super(AccountMoveReversal, self)._prepare_default_reversal(move) + refund.update( + { + "cotisation_cg_id": move.cotisation_cg_id.id, + "bordereau_id": move.bordereau_id.id, + } + ) + return refund diff --git a/wizard/export_journal_wizard.py b/wizard/export_journal_wizard.py old mode 100755 new mode 100644 index 6cb84ae..62c1c4e --- a/wizard/export_journal_wizard.py +++ b/wizard/export_journal_wizard.py @@ -3,7 +3,7 @@ from urllib.parse import urlencode -from odoo import models, fields, api +from odoo import api, fields, models class ExportJournalWizard(models.TransientModel): @@ -16,30 +16,33 @@ class ExportJournalWizard(models.TransientModel): @api.model def _get_default_export(self): - export = self.env['export.journal.type'].search([ - ('company_id', '=', self.env.user.company_id.id)], limit=1) + export = self.env["export.journal.type"].search( + [("company_id", "=", self.env.user.company_id.id)], limit=1 + ) return export - date_start = fields.Date('Date de début des écritures') - date_end = fields.Date( - string='Date de fin des écritures') + date_start = fields.Date("Date de début des écritures") + date_end = fields.Date(string="Date de fin des écritures") - date_creation_start = fields.Date('Date de début (création des écritures)') - date_creation_end = fields.Date( - string='Date de fin (création des écritures)') + date_creation_start = fields.Date("Date de début (création des écritures)") + date_creation_end = fields.Date(string="Date de fin (création des écritures)") company_id = fields.Many2one( - comodel_name='res.company', - default=_get_default_company + comodel_name="res.company", default=_get_default_company ) partner_ids = fields.Many2many( - comodel_name='res.partner', - string='Coopératives', - domain=[('is_cooperative', '=', True)] + comodel_name="res.partner", + string="Coopératives", + domain=[("is_cooperative", "=", True)], ) export_type = fields.Selection( - [('empty', 'Ecritures non exportées'), ('all', 'Toutes les écritures')], - string='Ecritures à exporter', default="empty") + [ + ("empty", "Ecritures non exportées"), + ("all", "Toutes les écritures"), + ], + string="Ecritures à exporter", + default="empty", + ) def get_cg_export(self): """ @@ -47,23 +50,27 @@ class ExportJournalWizard(models.TransientModel): :return: ir.actions.act_url """ datas = { - 'export_type': self.export_type, - 'company_id': self.company_id.id, + "export_type": self.export_type, + "company_id": self.company_id.id, } if self.date_start and self.date_end: - datas.update({ - 'date_start': self.date_start, - 'date_end': self.date_end, - }) + datas.update( + { + "date_start": self.date_start, + "date_end": self.date_end, + } + ) if self.date_creation_start and self.date_creation_end: - datas.update({ - 'date_creation_start': self.date_creation_start, - 'date_creation_end': self.date_creation_end, - }) + datas.update( + { + "date_creation_start": self.date_creation_start, + "date_creation_end": self.date_creation_end, + } + ) if self.partner_ids: - datas['partner_ids'] = ','.join(str(x) for x in self.partner_ids.ids) + datas["partner_ids"] = ",".join(str(x) for x in self.partner_ids.ids) return { - 'type': 'ir.actions.act_url', - 'url': '/web/export_journal_cg?' + urlencode(datas), - 'target': 'new', + "type": "ir.actions.act_url", + "url": "/web/export_journal_cg?" + urlencode(datas), + "target": "new", } diff --git a/wizard/export_journal_wizard_view.xml b/wizard/export_journal_wizard_view.xml old mode 100755 new mode 100644 index 4ec1165..e1ec98f --- a/wizard/export_journal_wizard_view.xml +++ b/wizard/export_journal_wizard_view.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="UTF-8"?> +<?xml version="1.0" encoding="UTF-8" ?> <odoo> <record id="data_export_cg_view_form" model="ir.ui.view"> <field name="name">export.journal.cg.wizard.form</field> @@ -7,40 +7,72 @@ <form string="Export des données"> <group> <group> - <field name="company_id" invisible="True"/> + <field name="company_id" invisible="True" /> <field name="export_type" widget="radio" /> </group> </group> - <p class="text-muted">Les dates de début et de fin sont incluses dans l'export des écritures</p> - <hr/> - <p style="background: #f0f0f0; padding: 5px"><u>Sélection sur la plage de <strong>date d'écriture</strong> :</u></p> + <p + class="text-muted" + >Les dates de début et de fin sont incluses dans l'export des écritures</p> + <hr /> + <p style="background: #f0f0f0; padding: 5px"><u + >Sélection sur la plage de <strong + >date d'écriture</strong> :</u></p> <group name="date"> <group> - <field name="date_start" attrs="{'required': [('date_end', '!=', False)]}"/> + <field + name="date_start" + attrs="{'required': [('date_end', '!=', False)]}" + /> </group> <group> - <field name="date_end" attrs="{'required': [('date_start', '!=', False)]}"/> + <field + name="date_end" + attrs="{'required': [('date_start', '!=', False)]}" + /> </group> </group> - <hr/> - <p style="background: #f0f0f0; padding: 5px"><u>Sélection sur la plage de <strong>date de création de l'écriture</strong> :</u></p> + <hr /> + <p style="background: #f0f0f0; padding: 5px"><u + >Sélection sur la plage de <strong + >date de création de l'écriture</strong> :</u></p> <group name="date"> <group> - <field name="date_creation_start" attrs="{'required': [('date_creation_end', '!=', False)]}"/> + <field + name="date_creation_start" + attrs="{'required': [('date_creation_end', '!=', False)]}" + /> </group> <group> - <field name="date_creation_end" attrs="{'required': [('date_creation_start', '!=', False)]}"/> + <field + name="date_creation_end" + attrs="{'required': [('date_creation_start', '!=', False)]}" + /> </group> </group> - <hr/> - <p style="background: #f0f0f0; padding: 5px"><u>Sélection des coopératives (si souhaité) :</u></p> + <hr /> + <p style="background: #f0f0f0; padding: 5px"><u + >Sélection des coopératives (si souhaité) :</u></p> <group> - <field name="partner_ids" widget="many2many_tags" options="{'no_create': True, 'no_edit': True}"/> + <field + name="partner_ids" + widget="many2many_tags" + options="{'no_create': True, 'no_edit': True}" + /> </group> <footer> - <button class="btn btn-sm btn-primary" name="get_cg_export" string="Télécharger" type="object"/> - <button class="btn btn-sm btn-default" special="cancel" string="Fermer"/> + <button + class="btn btn-sm btn-primary" + name="get_cg_export" + string="Télécharger" + type="object" + /> + <button + class="btn btn-sm btn-default" + special="cancel" + string="Fermer" + /> </footer> </form> </field> @@ -51,9 +83,15 @@ <field name="type">ir.actions.act_window</field> <field name="res_model">export.journal.cg.wizard</field> <field name="view_mode">form</field> - <field name="view_id" ref="data_export_cg_view_form"/> + <field name="view_id" ref="data_export_cg_view_form" /> <field name="target">new</field> </record> - <menuitem action="data_export_cg_action" id="menu_data_export_cg" name="Export Journal CG Scop" parent="account.menu_finance_entries" sequence="0" /> + <menuitem + action="data_export_cg_action" + id="menu_data_export_cg" + name="Export Journal CG Scop" + parent="account.menu_finance_entries" + sequence="0" + /> </odoo> diff --git a/wizard/scop_bordereau_payment_mode_wizard.py b/wizard/scop_bordereau_payment_mode_wizard.py index 45efe0a..7f8ee13 100644 --- a/wizard/scop_bordereau_payment_mode_wizard.py +++ b/wizard/scop_bordereau_payment_mode_wizard.py @@ -1,28 +1,34 @@ # Copyright 2021 Le Filament # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo import fields, models, api +from odoo import fields, models class ScopBordereauChangePaymentMode(models.TransientModel): - _name = 'scop.bordereau.change.payment.mode.wizard' - _description = 'Changement de mode de paiement' + _name = "scop.bordereau.change.payment.mode.wizard" + _description = "Changement de mode de paiement" bordereau_id = fields.Many2one( - comodel_name='scop.bordereau', - string='Bordereau', + comodel_name="scop.bordereau", + string="Bordereau", readonly=True, ) payment_mode_id = fields.Many2one( - comodel_name='account.payment.mode', - string="Nouveau mode de paiment",) + comodel_name="account.payment.mode", + string="Nouveau mode de paiment", + ) change_type = fields.Selection( - string='Type d\'affectation', - selection=[('punctual', 'Définir pour les échéances non payées de ce ' - 'bordereau uniquement'), - ('all', 'Définir pour toutes les prochaines échéances')], - default='punctual', - required=False, ) + string="Type d'affectation", + selection=[ + ( + "punctual", + "Définir pour les échéances non payées de ce " "bordereau uniquement", + ), + ("all", "Définir pour toutes les prochaines échéances"), + ], + default="punctual", + required=False, + ) # ------------------------------------------------------ # Action Button @@ -30,29 +36,43 @@ class ScopBordereauChangePaymentMode(models.TransientModel): def action_change_payment_mode(self): bordereau_id = self.bordereau_id # Link new liasse fiscale to bordereau - if self.change_type == 'all': - bordereau_id.partner_id.update({ - 'customer_payment_mode_id': self.payment_mode_id.id, - }) - bordereau_id.update({ - 'payment_mode_id': self.payment_mode_id.id, - }) - bordereau_id.invoice_ids.update({ - 'payment_mode_id': self.payment_mode_id.id, - }) + if self.change_type == "all": + bordereau_id.partner_id.update( + { + "customer_payment_mode_id": self.payment_mode_id.id, + } + ) + bordereau_id.update( + { + "payment_mode_id": self.payment_mode_id.id, + } + ) + bordereau_id.invoice_ids.update( + { + "payment_mode_id": self.payment_mode_id.id, + } + ) contrib_cg_journal = self.env.user.company_id.contribution_journal_id - contrib_ur_fede_journal = \ + contrib_ur_fede_journal = ( self.env.user.company_id.contribution_ur_or_fede_journal_id - move_line_ids = self.env['account.move.line'].search([ - ('partner_id', '=', self.bordereau_id.partner_id.id), - ('journal_id', 'in', ( - contrib_cg_journal.id, contrib_ur_fede_journal.id)), - ('full_reconcile_id', '=', False), - ('balance', '!=', 0), - ('account_id.reconcile', '=', True), - ('account_id.internal_type', '=', 'receivable') - ]) - move_line_ids.update({ - 'payment_mode_id': self.payment_mode_id.id, - }) - return {'type': 'ir.actions.act_window_close'} + ) + move_line_ids = self.env["account.move.line"].search( + [ + ("partner_id", "=", self.bordereau_id.partner_id.id), + ( + "journal_id", + "in", + (contrib_cg_journal.id, contrib_ur_fede_journal.id), + ), + ("full_reconcile_id", "=", False), + ("balance", "!=", 0), + ("account_id.reconcile", "=", True), + ("account_id.internal_type", "=", "receivable"), + ] + ) + move_line_ids.update( + { + "payment_mode_id": self.payment_mode_id.id, + } + ) + return {"type": "ir.actions.act_window_close"} diff --git a/wizard/scop_bordereau_payment_mode_wizard.xml b/wizard/scop_bordereau_payment_mode_wizard.xml index 4af8d77..10f4a82 100644 --- a/wizard/scop_bordereau_payment_mode_wizard.xml +++ b/wizard/scop_bordereau_payment_mode_wizard.xml @@ -1,25 +1,46 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8" ?> <odoo> <!-- Copyright 2021 Le Filament License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> <data> - <record id="scop_bordereau_change_payment_mode_wizard_form_view" model="ir.ui.view"> + <record + id="scop_bordereau_change_payment_mode_wizard_form_view" + model="ir.ui.view" + > <field name="name">scop.bordereau.change.payment.mode.wizard.form</field> <field name="model">scop.bordereau.change.payment.mode.wizard</field> <field name="arch" type="xml"> <form string="Changement de mode de paiement"> <group> - <field name="payment_mode_id" options="{'no_open': True, 'no_create': True}"/> - <field name="change_type" widget="radio" nolabel="1" class="mt-4"/> + <field + name="payment_mode_id" + options="{'no_open': True, 'no_create': True}" + /> + <field + name="change_type" + widget="radio" + nolabel="1" + class="mt-4" + /> </group> <footer> - <button name="action_change_payment_mode" string="Ok" type="object" default_focus="1" class="oe_highlight"/> - <button class="oe_highlight" string="Annuler" special="cancel"/> + <button + name="action_change_payment_mode" + string="Ok" + type="object" + default_focus="1" + class="oe_highlight" + /> + <button + class="oe_highlight" + string="Annuler" + special="cancel" + /> </footer> </form> </field> </record> </data> -</odoo> \ No newline at end of file +</odoo> diff --git a/wizard/scop_bordereau_refund_wizard.py b/wizard/scop_bordereau_refund_wizard.py index 53614d1..0d6a8af 100644 --- a/wizard/scop_bordereau_refund_wizard.py +++ b/wizard/scop_bordereau_refund_wizard.py @@ -1,46 +1,53 @@ # Copyright 2021 Le Filament # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo import fields, models, api -from odoo.exceptions import ValidationError, UserError +from odoo import _, api, fields, models +from odoo.exceptions import UserError, ValidationError class ScopBordereauRefundWizard(models.TransientModel): - _name = 'scop.bordereau.refund.wizard' - _description = 'Avoir sur les bordereaux' + _name = "scop.bordereau.refund.wizard" + _description = "Avoir sur les bordereaux" bordereau_id = fields.Many2one( - comodel_name='scop.bordereau', - string='Bordereau', + comodel_name="scop.bordereau", + string="Bordereau", readonly=True, ) date_refund = fields.Date( string="Date de régularisation", - default=fields.Date.today(), required=1) - cotiz_reminder = fields.Html('Rappel des cotisations', readonly=1) - amount_refund = fields.Float('Montant (par trimestre)', required=1) - comment = fields.Char('Motif de l\'avoir', required=1) + default=fields.Date.today(), + required=1, + ) + cotiz_reminder = fields.Html("Rappel des cotisations", readonly=1) + amount_refund = fields.Float("Montant (par trimestre)", required=1) + comment = fields.Char("Motif de l'avoir", required=1) type_cotiz = fields.Selection( - string='Type de cotisation', + string="Type de cotisation", selection=lambda self: self._selection_type_cotiz(), - required=1) + required=1, + ) quarter_ids = fields.Many2many( - relation='scop_bordereau_refund_wizard_quarter_rel', - comodel_name='scop.bordereau.refund.wizard.quarter', - string='Trimestres') + relation="scop_bordereau_refund_wizard_quarter_rel", + comodel_name="scop.bordereau.refund.wizard.quarter", + string="Trimestres", + ) # ------------------------------------------------------ # Constrains # ------------------------------------------------------ - @api.constrains('date_refund') + @api.constrains("date_refund") def _check_date_refund(self): - last_date = max(self.bordereau_id.invoice_ids.mapped('date_invoice')) - if self.date_refund > fields.Date.today() or \ - self.date_refund < last_date: - raise ValidationError("La date de l'avoir doit être " - "inférieure ou égale à la date du jour et " - "supérieure à la dernière date de " - "facturation liée au bordereau.") + last_date = max(self.bordereau_id.invoice_ids.mapped("date_invoice")) + if self.date_refund > fields.Date.today() or self.date_refund < last_date: + raise ValidationError( + _( + "La date de l'avoir doit être " + "inférieure ou égale à la date du jour et " + "supérieure à la dernière date de " + "facturation liée au bordereau." + ) + ) # ------------------------------------------------------ # Override ORM @@ -48,51 +55,55 @@ class ScopBordereauRefundWizard(models.TransientModel): @api.model def default_get(self, fields): res = super(ScopBordereauRefundWizard, self).default_get(fields) - bordereau_id = self.env['scop.bordereau'].browse( - self.env.context.get('active_id') + bordereau_id = self.env["scop.bordereau"].browse( + self.env.context.get("active_id") + ) + res.update( + { + "bordereau_id": bordereau_id.id, + "cotiz_reminder": bordereau_id.details, + } ) - res.update({ - 'bordereau_id': bordereau_id.id, - 'cotiz_reminder': bordereau_id.details, - }) return res @api.model def _selection_type_cotiz(self): - bordereau_id = self.env['scop.bordereau'].browse( - self.env.context.get('active_id') + bordereau_id = self.env["scop.bordereau"].browse( + self.env.context.get("active_id") ) contribs = bordereau_id.invoice_ids.read_group( - [('id', 'in', bordereau_id.invoice_ids.ids)], - ['type_contribution_id', 'amount_total_signed'], - ['type_contribution_id']) + [("id", "in", bordereau_id.invoice_ids.ids)], + ["type_contribution_id", "amount_total_signed"], + ["type_contribution_id"], + ) - ur_hdf = self.env.ref('cgscop_partner.riga_14232').id + ur_hdf = self.env.ref("cgscop_partner.riga_14232").id if bordereau_id.partner_id.ur_id.id == ur_hdf: product_ur = self.env.user.company_id.contribution_hdf_id else: # ur = ur_med product_ur = self.env.user.company_id.contribution_med_id cotiz_type = { - self.env.ref('cgscop_partner.riga_14397').id: - self.env.user.company_id.contribution_cg_id, - self.env.ref('cgscop_partner.riga_14398').id: - self.env.user.company_id.contribution_fede_com_id, - self.env.ref('cgscop_partner.cotiz_fede_cae').id: - self.env.user.company_id.contribution_fede_cae_id, - self.env.ref('cgscop_partner.riga_14399').id: - product_ur, + self.env.ref( + "cgscop_partner.riga_14397" + ).id: self.env.user.company_id.contribution_cg_id, + self.env.ref( + "cgscop_partner.riga_14398" + ).id: self.env.user.company_id.contribution_fede_com_id, + self.env.ref( + "cgscop_partner.cotiz_fede_cae" + ).id: self.env.user.company_id.contribution_fede_cae_id, + self.env.ref("cgscop_partner.riga_14399").id: product_ur, } type_cotiz_select_i = list() type_cotiz_select_list = list() for contrib in contribs: - type_cotiz = contrib.get('type_contribution_id')[0] + type_cotiz = contrib.get("type_contribution_id")[0] if type_cotiz not in type_cotiz_select_i: type_cotiz_select_i.append(type_cotiz) - type_cotiz_select_list.append( - (type_cotiz, cotiz_type[type_cotiz].name)) + type_cotiz_select_list.append((type_cotiz, cotiz_type[type_cotiz].name)) return type_cotiz_select_list # ------------------------------------------------------ @@ -103,31 +114,33 @@ class ScopBordereauRefundWizard(models.TransientModel): Create refund """ if len(self.quarter_ids) == 0: - raise UserError('Vous devez sélectionner au moins un trimestre.') + raise UserError(_("Vous devez sélectionner au moins un trimestre.")) if self.amount_refund <= 0: - raise UserError('Le montant de l\'avoir doit être supérieur à 0.') + raise UserError(_("Le montant de l'avoir doit être supérieur à 0.")) if not self.type_cotiz: - raise UserError('Vous devez sélectionner un type de cotisation.') + raise UserError(_("Vous devez sélectionner un type de cotisation.")) bordereau_id = self.bordereau_id partner_id = bordereau_id.partner_id - ur_hdf = self.env.ref('cgscop_partner.riga_14232').id + ur_hdf = self.env.ref("cgscop_partner.riga_14232").id # CREATE VERSION - bordereau_id.read(['amount_total_cotiz']) + bordereau_id.read(["amount_total_cotiz"]) ongoing_version = bordereau_id.bordereau_version_ids.filtered( - lambda v: v.version == bordereau_id.version and v.state == 'new' + lambda v: v.version == bordereau_id.version and v.state == "new" ) if not ongoing_version: - self.env['scop.bordereau.version'].create({ - 'bordereau_id': bordereau_id.id, - 'date': self.date_refund, - 'comment': self.comment, - 'version': bordereau_id.version, - 'liasse_fiscale_id_old': bordereau_id.liasse_fiscale_id.id, - 'type_assiette': bordereau_id.type_assiette, - 'montant_assiette': bordereau_id.montant_assiette, - 'amount_total_cotiz': bordereau_id.amount_total_cotiz, - }) + self.env["scop.bordereau.version"].create( + { + "bordereau_id": bordereau_id.id, + "date": self.date_refund, + "comment": self.comment, + "version": bordereau_id.version, + "liasse_fiscale_id_old": bordereau_id.liasse_fiscale_id.id, + "type_assiette": bordereau_id.type_assiette, + "montant_assiette": bordereau_id.montant_assiette, + "amount_total_cotiz": bordereau_id.amount_total_cotiz, + } + ) # CREATE REFUND if partner_id.ur_id.id == ur_hdf: @@ -138,63 +151,70 @@ class ScopBordereauRefundWizard(models.TransientModel): account_ur = self.env.user.company_id.receivable_account_ur_med_id cotiz_type = { - self.env.ref('cgscop_partner.riga_14397').id: - [self.env.user.company_id.contribution_cg_id, - self.env.user.company_id.contribution_journal_id, - partner_id.property_account_receivable_id], - self.env.ref('cgscop_partner.riga_14398').id: - [self.env.user.company_id.contribution_fede_com_id, - self.env.user.company_id.contribution_ur_or_fede_journal_id, - self.env.user.company_id.receivable_account_fede_com_id], - self.env.ref('cgscop_partner.cotiz_fede_cae').id: - [self.env.user.company_id.contribution_fede_cae_id, - self.env.user.company_id.contribution_ur_or_fede_journal_id, - self.env.user.company_id.receivable_account_fede_cae_id], - self.env.ref('cgscop_partner.riga_14399').id: - [product_ur, - self.env.user.company_id.contribution_ur_or_fede_journal_id, - account_ur], + self.env.ref("cgscop_partner.riga_14397").id: [ + self.env.user.company_id.contribution_cg_id, + self.env.user.company_id.contribution_journal_id, + partner_id.property_account_receivable_id, + ], + self.env.ref("cgscop_partner.riga_14398").id: [ + self.env.user.company_id.contribution_fede_com_id, + self.env.user.company_id.contribution_ur_or_fede_journal_id, + self.env.user.company_id.receivable_account_fede_com_id, + ], + self.env.ref("cgscop_partner.cotiz_fede_cae").id: [ + self.env.user.company_id.contribution_fede_cae_id, + self.env.user.company_id.contribution_ur_or_fede_journal_id, + self.env.user.company_id.receivable_account_fede_cae_id, + ], + self.env.ref("cgscop_partner.riga_14399").id: [ + product_ur, + self.env.user.company_id.contribution_ur_or_fede_journal_id, + account_ur, + ], } product = cotiz_type.get(int(self.type_cotiz))[0] for quarter_id in self.quarter_ids: - refund = self.env['account.invoice'].create({ - 'partner_id': partner_id.id, - 'journal_id': cotiz_type.get(int(self.type_cotiz))[1].id, - 'account_id': cotiz_type.get(int(self.type_cotiz))[2].id, - 'type': 'out_refund', - 'date_invoice': self.date_refund, - 'date': self.date_refund, - 'state': 'draft', - 'number': False, - 'origin': bordereau_id.name, - 'name': self.comment, - 'bordereau_id': bordereau_id.id, - 'is_contribution': True, - 'year': bordereau_id.year, - 'cotiz_quarter': quarter_id.quarter, - 'liasse_fiscale_id': bordereau_id.liasse_fiscale_id.id, - 'type_contribution_id': self.type_cotiz, - 'payment_mode_id': bordereau_id.payment_mode_id.id, - 'date_due': self.date_refund, - }) - self.env['account.invoice.line'].create({ - 'name': self.comment, - 'invoice_id': refund.id, - 'product_id': product.id, - 'account_id': product.property_account_income_id.id, - 'price_unit': self.amount_refund - }) + refund = self.env["account.invoice"].create( + { + "partner_id": partner_id.id, + "journal_id": cotiz_type.get(int(self.type_cotiz))[1].id, + "account_id": cotiz_type.get(int(self.type_cotiz))[2].id, + "type": "out_refund", + "date_invoice": self.date_refund, + "date": self.date_refund, + "state": "draft", + "number": False, + "origin": bordereau_id.name, + "name": self.comment, + "bordereau_id": bordereau_id.id, + "is_contribution": True, + "year": bordereau_id.year, + "cotiz_quarter": quarter_id.quarter, + "liasse_fiscale_id": bordereau_id.liasse_fiscale_id.id, + "type_contribution_id": self.type_cotiz, + "payment_mode_id": bordereau_id.payment_mode_id.id, + "date_due": self.date_refund, + } + ) + self.env["account.invoice.line"].create( + { + "name": self.comment, + "invoice_id": refund.id, + "product_id": product.id, + "account_id": product.property_account_income_id.id, + "price_unit": self.amount_refund, + } + ) class ScopBordereauRefundWizardQuarer(models.Model): - _name = 'scop.bordereau.refund.wizard.quarter' - _description = 'Trimestres pour échéance de cotisation' + _name = "scop.bordereau.refund.wizard.quarter" + _description = "Trimestres pour échéance de cotisation" - name = fields.Char('Nom', compute='_compute_name') - quarter = fields.Integer('Trismestre', required=1) + name = fields.Char("Nom", compute="_compute_name") + quarter = fields.Integer("Trimestre", required=1) - @api.multi def _compute_name(self): for r in self: r.name = str(r.quarter) diff --git a/wizard/scop_bordereau_refund_wizard.xml b/wizard/scop_bordereau_refund_wizard.xml index e42f5c0..e20305b 100644 --- a/wizard/scop_bordereau_refund_wizard.xml +++ b/wizard/scop_bordereau_refund_wizard.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8" ?> <odoo> <!-- Copyright 2021 Le Filament License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> @@ -11,32 +11,39 @@ <form string="Générer un avoir"> <sheet> <group> - <field name="cotiz_reminder"/> - <hr/> - <field name="type_cotiz"/> - <field name="date_refund"/> - <separator/> - <field name="amount_refund"/> - <field name="quarter_ids" widget="many2many_checkboxes"/> - <field name="comment"/> + <field name="cotiz_reminder" /> + <hr /> + <field name="type_cotiz" /> + <field name="date_refund" /> + <separator /> + <field name="amount_refund" /> + <field name="quarter_ids" widget="many2many_checkboxes" /> + <field name="comment" /> </group> </sheet> <footer> - <button name="create_refund" type="object" string="Créer l'avoir" class="oe_highlight"/> - <button string="Annuler" special="cancel" class="oe_link"/> + <button + name="create_refund" + type="object" + string="Créer l'avoir" + class="oe_highlight" + /> + <button string="Annuler" special="cancel" class="oe_link" /> </footer> </form> </field> </record> - <record id="scop_bordereau_refund_wizard_act_window" model="ir.actions.act_window"> + <record + id="scop_bordereau_refund_wizard_act_window" + model="ir.actions.act_window" + > <field name="name">Avoir sur le bordereau</field> <field name="type">ir.actions.act_window</field> <field name="res_model">scop.bordereau.refund.wizard</field> - <field name="view_type">form</field> <field name="view_mode">form</field> <field name="target">new</field> </record> </data> -</odoo> \ No newline at end of file +</odoo> diff --git a/wizard/scop_bordereau_update_confirm.py b/wizard/scop_bordereau_update_confirm.py index d14e35b..2a0ed5e 100644 --- a/wizard/scop_bordereau_update_confirm.py +++ b/wizard/scop_bordereau_update_confirm.py @@ -1,7 +1,7 @@ # © 2021 Le Filament (<http://www.le-filament.com>) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) -from odoo import models, api, _ +from odoo import _, models from odoo.exceptions import UserError @@ -13,14 +13,17 @@ class ScopBordereauUpdate(models.TransientModel): _name = "scop.bordereau.update" _description = "Mettre à jour les bordereaux sélectionnés" - @api.multi def bordereau_update(self): context = dict(self._context or {}) - active_ids = context.get('active_ids', []) or [] + active_ids = context.get("active_ids", []) or [] - for record in self.env['scop.bordereau'].browse(active_ids): - if record.state != 'new': - raise UserError(_("Impossible de mettre à jour un bordereau " - "qui n'est pas à l'état de brouillon")) + for record in self.env["scop.bordereau"].browse(active_ids): + if record.state != "new": + raise UserError( + _( + "Impossible de mettre à jour un bordereau " + "qui n'est pas à l'état de brouillon" + ) + ) record.create_cotiz_and_lines() - return {'type': 'ir.actions.act_window_close'} + return {"type": "ir.actions.act_window_close"} diff --git a/wizard/scop_bordereau_update_confirm_view.xml b/wizard/scop_bordereau_update_confirm_view.xml index a68680b..0a53965 100644 --- a/wizard/scop_bordereau_update_confirm_view.xml +++ b/wizard/scop_bordereau_update_confirm_view.xml @@ -1,10 +1,8 @@ -<?xml version="1.0"?> +<?xml version="1.0" ?> <!-- Copyright 2021 Le Filament License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> - <odoo> <data> - <record id="account_invoice_update_view" model="ir.ui.view"> <field name="name">scop.bordereau.update.form</field> <field name="model">scop.bordereau.update</field> @@ -14,18 +12,30 @@ Cette action est susceptible de modifier les montants des cotisations ! </p> <footer> - <button string="Valider" name="bordereau_update" type="object" default_focus="1" class="btn-primary"/> - <button string="Annuler" class="btn-secondary" special="cancel"/> + <button + string="Valider" + name="bordereau_update" + type="object" + default_focus="1" + class="btn-primary" + /> + <button + string="Annuler" + class="btn-secondary" + special="cancel" + /> </footer> </form> </field> </record> - <act_window id="action_scop_bordereau_update" - multi="True" - name="Mettre le(s) cotisation(s) à jour" - res_model="scop.bordereau.update" src_model="scop.bordereau" - view_mode="form" target="new" view_type="form" /> + <record id="action_scop_bordereau_update" model="ir.actions.act_window"> + <field name="name">Mettre le(s) cotisation(s) à jour</field> + <field name="res_model">scop.bordereau.update</field> + <field name="binding_model_id" ref="model_scop_bordereau" /> + <field name="view_mode">form</field> + <field name="target">new</field> + </record> </data> </odoo> diff --git a/wizard/scop_bordereau_update_liasse_wizard.py b/wizard/scop_bordereau_update_liasse_wizard.py index bd0ed6a..4f753f1 100644 --- a/wizard/scop_bordereau_update_liasse_wizard.py +++ b/wizard/scop_bordereau_update_liasse_wizard.py @@ -1,108 +1,103 @@ # Copyright 2021 Le Filament # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo import fields, models, api +from odoo import _, api, fields, models from odoo.exceptions import UserError class ScopBordereauChangeLiasse(models.TransientModel): - _name = 'scop.bordereau.change.liasse.wizard' - _description = 'Changement de liasse sur le bordereau' + _name = "scop.bordereau.change.liasse.wizard" + _description = "Changement de liasse sur le bordereau" bordereau_id = fields.Many2one( - comodel_name='scop.bordereau', - string='Bordereau', + comodel_name="scop.bordereau", + string="Bordereau", readonly=True, ) - partner_id = fields.Many2one( - related='bordereau_id.partner_id' - ) - company_currency_id = fields.Many2one( - related='bordereau_id.company_currency_id') + partner_id = fields.Many2one(related="bordereau_id.partner_id") + company_currency_id = fields.Many2one(related="bordereau_id.company_currency_id") # Ancienne liasse - liasse_fiscale_id = fields.Many2one( - related='bordereau_id.liasse_fiscale_id' - ) + liasse_fiscale_id = fields.Many2one(related="bordereau_id.liasse_fiscale_id") amount_total_cotiz = fields.Monetary( - related='bordereau_id.amount_total_cotiz', - currency_field='company_currency_id' + related="bordereau_id.amount_total_cotiz", + currency_field="company_currency_id", ) amount_cg = fields.Float( - 'Cotisation CG Scop actuelle', compute='_compute_amount_cotiz') + "Cotisation CG Scop actuelle", compute="_compute_amount_cotiz" + ) amount_ur_med = fields.Float( - 'Cotisation UR Med actuelle', compute='_compute_amount_cotiz') + "Cotisation UR Med actuelle", compute="_compute_amount_cotiz" + ) amount_ur_hdf = fields.Float( - 'Cotisation UR HDF actuelle', compute='_compute_amount_cotiz') + "Cotisation UR HDF actuelle", compute="_compute_amount_cotiz" + ) amount_fede_com = fields.Float( - 'Cotisation Fede Com actuelle', compute='_compute_amount_cotiz') + "Cotisation Fede Com actuelle", compute="_compute_amount_cotiz" + ) amount_fede_cae = fields.Float( - 'Cotisation Fede CAE actuelle', compute='_compute_amount_cotiz') + "Cotisation Fede CAE actuelle", compute="_compute_amount_cotiz" + ) type_assiette = fields.Selection( - string='Type d\'assiette', - related='bordereau_id.type_assiette',) + string="Type d'assiette", + related="bordereau_id.type_assiette", + ) montant_assiette = fields.Integer( - string='Montant de l\'assiette', - related='bordereau_id.montant_assiette') - ca = fields.Float( - related='bordereau_id.ca') - va = fields.Float( - related='bordereau_id.va') - net_results = fields.Float( - related='bordereau_id.net_results') - wage_cg = fields.Float( - related='bordereau_id.wage_cg') + string="Montant de l'assiette", related="bordereau_id.montant_assiette" + ) + ca = fields.Float(related="bordereau_id.ca") + va = fields.Float(related="bordereau_id.va") + net_results = fields.Float(related="bordereau_id.net_results") + wage_cg = fields.Float(related="bordereau_id.wage_cg") # Nouvelle liasse liasse_fiscale_new_id = fields.Many2one( - comodel_name='scop.liasse.fiscale', - string='Nouvelle liasse Fiscale', + comodel_name="scop.liasse.fiscale", + string="Nouvelle liasse Fiscale", ) amount_total_cotiz_new = fields.Monetary( - string='Nouveau montant total de(s) cotisation(s)', - currency_field='company_currency_id', - readonly=1) + string="Nouveau montant total de(s) cotisation(s)", + currency_field="company_currency_id", + readonly=1, + ) type_id_new = fields.Selection( - 'Type de liasse', related='liasse_fiscale_new_id.type_id') + string="Type de liasse", related="liasse_fiscale_new_id.type_id" + ) source_new = fields.Selection( - 'Source de la liasse', related='liasse_fiscale_new_id.source') + string="Source de la liasse", related="liasse_fiscale_new_id.source" + ) amount_cg_new = fields.Float( - 'Cotisation CG Scop', - related='liasse_fiscale_new_id.contribution_cg') + "Cotisation CG Scop", related="liasse_fiscale_new_id.contribution_cg" + ) amount_ur_med_new = fields.Float( - 'Cotisation UR Méditerranée', - related='liasse_fiscale_new_id.contribution_med') + "Cotisation UR Méditerranée", + related="liasse_fiscale_new_id.contribution_med", + ) amount_ur_hdf_new = fields.Float( - 'Cotisation UR HDF', - related='liasse_fiscale_new_id.contribution_hdf') + "Cotisation UR HDF", related="liasse_fiscale_new_id.contribution_hdf" + ) amount_fede_com_new = fields.Float( - 'Cotisation Fédé Communication', - related='liasse_fiscale_new_id.contribution_com') + "Cotisation Fédé Communication", + related="liasse_fiscale_new_id.contribution_com", + ) amount_fede_cae_new = fields.Float( - 'Cotisation Fédé CAE', - related='liasse_fiscale_new_id.contribution_cae') + "Cotisation Fédé CAE", related="liasse_fiscale_new_id.contribution_cae" + ) type_assiette_new = fields.Selection( - related='liasse_fiscale_new_id.contribution_base_type') + related="liasse_fiscale_new_id.contribution_base_type" + ) montant_assiette_new = fields.Integer( - related='liasse_fiscale_new_id.contribution_base_amount') - # ca_new = fields.Float( - # related='liasse_fiscale_new_id.av_cg') - # va_new = fields.Float( - # string='VA', compute='_compute_liasse_new_values') - # net_results_new = fields.Float( - # string='Résultat net', compute='_compute_liasse_new_values') - # wage_cg_new = fields.Float( - # string='Montant masse salariale', compute='_compute_liasse_new_values') + related="liasse_fiscale_new_id.contribution_base_amount" + ) # ------------------------------------------------------ # Compute # ------------------------------------------------------ def _compute_amount_cotiz(self): - type_cotisation_cg = self.env.ref('cgscop_partner.riga_14397').id - type_cotisation_fede_com = self.env.ref('cgscop_partner.riga_14398').id - type_cotisation_fede_cae = self.env.ref( - 'cgscop_partner.cotiz_fede_cae').id - type_cotisation_ur = self.env.ref('cgscop_partner.riga_14399').id + type_cotisation_cg = self.env.ref("cgscop_partner.riga_14397").id + type_cotisation_fede_com = self.env.ref("cgscop_partner.riga_14398").id + type_cotisation_fede_cae = self.env.ref("cgscop_partner.cotiz_fede_cae").id + type_cotisation_ur = self.env.ref("cgscop_partner.riga_14399").id for r in self: partner = r.bordereau_id.partner_id cotiz = r.bordereau_id.invoice_ids @@ -110,42 +105,45 @@ class ScopBordereauChangeLiasse(models.TransientModel): cotiz_cg = cotiz.filtered( lambda i: i.type_contribution_id.id == type_cotisation_cg ) - r.amount_cg = sum(cotiz_cg.mapped('amount_total')) + r.amount_cg = sum(cotiz_cg.mapped("amount_total")) # Fede Com cotiz_fede_com = cotiz.filtered( lambda i: i.type_contribution_id.id == type_cotisation_fede_com ) - r.amount_fede_com = sum(cotiz_fede_com.mapped('amount_total')) + r.amount_fede_com = sum(cotiz_fede_com.mapped("amount_total")) # Fede CAE cotiz_fede_cae = cotiz.filtered( lambda i: i.type_contribution_id.id == type_cotisation_fede_cae ) - r.amount_fede_cae = sum(cotiz_fede_cae.mapped('amount_total')) + r.amount_fede_cae = sum(cotiz_fede_cae.mapped("amount_total")) # UR HDF - ur_hdf = self.env.ref('cgscop_partner.riga_14232').id + ur_hdf = self.env.ref("cgscop_partner.riga_14232").id if partner.ur_id.id == ur_hdf: cotiz_ur_hdf = cotiz.filtered( lambda i: i.type_contribution_id.id == type_cotisation_ur ) - r.amount_ur_hdf = sum(cotiz_ur_hdf.mapped('amount_total')) + r.amount_ur_hdf = sum(cotiz_ur_hdf.mapped("amount_total")) # UR Med - ur_med = self.env.ref('cgscop_partner.riga_14243').id + ur_med = self.env.ref("cgscop_partner.riga_14243").id if partner.ur_id.id == ur_med: cotiz_ur_med = cotiz.filtered( lambda i: i.type_contribution_id.id == type_cotisation_ur ) - r.amount_ur_med = sum(cotiz_ur_med.mapped('amount_total')) + r.amount_ur_med = sum(cotiz_ur_med.mapped("amount_total")) # ------------------------------------------------------ # Onchange # ------------------------------------------------------ - @api.onchange('liasse_fiscale_new_id') + @api.onchange("liasse_fiscale_new_id") def _compute_liasse_new_values(self): for r in self: if r.liasse_fiscale_new_id: - r.amount_total_cotiz_new = \ - r.amount_cg_new + r.amount_ur_hdf_new + \ - r.amount_ur_med_new + r.amount_fede_com + r.amount_total_cotiz_new = ( + r.amount_cg_new + + r.amount_ur_hdf_new + + r.amount_ur_med_new + + r.amount_fede_com + ) # ------------------------------------------------------ # Action Button @@ -153,10 +151,12 @@ class ScopBordereauChangeLiasse(models.TransientModel): def update_liasse_fiscale(self): bordereau_id = self.bordereau_id if not self.liasse_fiscale_new_id: - raise UserError('Merci de choisir une nouvelle liasse fiscale.') + raise UserError(_("Merci de choisir une nouvelle liasse fiscale.")) # Link new liasse fiscale to bordereau - bordereau_id.update({ - 'liasse_fiscale_id': self.liasse_fiscale_new_id, - }) + bordereau_id.update( + { + "liasse_fiscale_id": self.liasse_fiscale_new_id, + } + ) bordereau_id.create_cotiz_and_lines() - return {'type': 'ir.actions.act_window_close'} + return {"type": "ir.actions.act_window_close"} diff --git a/wizard/scop_bordereau_update_liasse_wizard.xml b/wizard/scop_bordereau_update_liasse_wizard.xml index 610d83f..125e66c 100644 --- a/wizard/scop_bordereau_update_liasse_wizard.xml +++ b/wizard/scop_bordereau_update_liasse_wizard.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8" ?> <odoo> <!-- Copyright 2021 Le Filament License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> @@ -13,65 +13,112 @@ <div class="row"> <div class="col-6"> <group string="Données initiales" class="text-muted"> - <field name="liasse_fiscale_id" options='{"no_open": True}'/> - <hr/> - <field name="amount_total_cotiz"/> - <field name="amount_cg"/> - <field name="amount_ur_med" attrs="{'invisible':[('amount_ur_med','=',0)]}"/> - <field name="amount_ur_hdf" attrs="{'invisible':[('amount_ur_hdf','=',0)]}"/> - <field name="amount_fede_com" attrs="{'invisible':[('amount_fede_com','=',0)]}"/> - <field name="amount_fede_cae" attrs="{'invisible':[('amount_fede_cae','=',0)]}"/> - <hr/> - <field name="type_assiette"/> - <field name="montant_assiette"/> - <hr/> - <field name="ca"/> - <field name="va"/> - <field name="net_results"/> - <field name="wage_cg"/> + <field + name="liasse_fiscale_id" + options='{"no_open": True}' + /> + <hr /> + <field name="amount_total_cotiz" /> + <field name="amount_cg" /> + <field + name="amount_ur_med" + attrs="{'invisible':[('amount_ur_med','=',0)]}" + /> + <field + name="amount_ur_hdf" + attrs="{'invisible':[('amount_ur_hdf','=',0)]}" + /> + <field + name="amount_fede_com" + attrs="{'invisible':[('amount_fede_com','=',0)]}" + /> + <field + name="amount_fede_cae" + attrs="{'invisible':[('amount_fede_cae','=',0)]}" + /> + <hr /> + <field name="type_assiette" /> + <field name="montant_assiette" /> + <hr /> + <field name="ca" /> + <field name="va" /> + <field name="net_results" /> + <field name="wage_cg" /> </group> </div> <div class="col-6"> <group> - <field name="partner_id" invisible="1"/> - <field name="liasse_fiscale_new_id" options='{"no_open": True, "no_create":True}' - domain="[('partner_id', '=', partner_id), + <field name="partner_id" invisible="1" /> + <field + name="liasse_fiscale_new_id" + options='{"no_open": True, "no_create":True}' + domain="[('partner_id', '=', partner_id), ('is_qualified', '=', True), - ('id', '!=', liasse_fiscale_id)]"/> - <field name="type_id_new" attrs="{'invisible':[('liasse_fiscale_new_id','=',False)]}"/> - <field name="source_new" attrs="{'invisible':[('liasse_fiscale_new_id','=',False)]}"/> + ('id', '!=', liasse_fiscale_id)]" + /> + <field + name="type_id_new" + attrs="{'invisible':[('liasse_fiscale_new_id','=',False)]}" + /> + <field + name="source_new" + attrs="{'invisible':[('liasse_fiscale_new_id','=',False)]}" + /> </group> - <group string="Nouveaux calculs (Sans arrondi)" attrs="{'invisible':[('liasse_fiscale_new_id','=',False)]}"> - <field name="amount_total_cotiz_new"/> - <field name="amount_cg_new"/> - <field name="amount_ur_med_new" attrs="{'invisible':[('amount_ur_med_new','=',0)]}"/> - <field name="amount_ur_hdf_new" attrs="{'invisible':[('amount_ur_hdf_new','=',0)]}"/> - <field name="amount_fede_com_new" attrs="{'invisible':[('amount_fede_com_new','=',0)]}"/> - <field name="amount_fede_cae_new" attrs="{'invisible':[('amount_fede_cae_new','=',0)]}"/> - <hr/> - <field name="type_assiette_new"/> - <field name="montant_assiette_new"/> - <hr/> + <group + string="Nouveaux calculs (Sans arrondi)" + attrs="{'invisible':[('liasse_fiscale_new_id','=',False)]}" + > + <field name="amount_total_cotiz_new" /> + <field name="amount_cg_new" /> + <field + name="amount_ur_med_new" + attrs="{'invisible':[('amount_ur_med_new','=',0)]}" + /> + <field + name="amount_ur_hdf_new" + attrs="{'invisible':[('amount_ur_hdf_new','=',0)]}" + /> + <field + name="amount_fede_com_new" + attrs="{'invisible':[('amount_fede_com_new','=',0)]}" + /> + <field + name="amount_fede_cae_new" + attrs="{'invisible':[('amount_fede_cae_new','=',0)]}" + /> + <hr /> + <field name="type_assiette_new" /> + <field name="montant_assiette_new" /> + <hr /> </group> </div> </div> </sheet> <footer> - <button name="update_liasse_fiscale" type="object" string="Modifier la liasse" class="oe_highlight" confirm="Confirmer le changement de liasse"/> - <button string="Annuler" special="cancel" class="oe_link"/> + <button + name="update_liasse_fiscale" + type="object" + string="Modifier la liasse" + class="oe_highlight" + confirm="Confirmer le changement de liasse" + /> + <button string="Annuler" special="cancel" class="oe_link" /> </footer> </form> </field> </record> - <record id="scop_bordereau_change_liasse_wizard_act_window" model="ir.actions.act_window"> + <record + id="scop_bordereau_change_liasse_wizard_act_window" + model="ir.actions.act_window" + > <field name="name">Changement liasse bordereau</field> <field name="type">ir.actions.act_window</field> <field name="res_model">scop.bordereau.change.liasse.wizard</field> - <field name="view_type">form</field> <field name="view_mode">form</field> <field name="target">new</field> </record> </data> -</odoo> \ No newline at end of file +</odoo> diff --git a/wizard/scop_bordereau_validate_confirm.py b/wizard/scop_bordereau_validate_confirm.py index 023cc3b..8ca1fcb 100644 --- a/wizard/scop_bordereau_validate_confirm.py +++ b/wizard/scop_bordereau_validate_confirm.py @@ -1,7 +1,7 @@ # © 2021 Le Filament (<http://www.le-filament.com>) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) -from odoo import models, api, fields, _ +from odoo import _, api, fields, models from odoo.exceptions import UserError @@ -9,47 +9,54 @@ class ScopBordereauValidate(models.TransientModel): """ This wizard will validate all the selected bordereaux """ + _name = "scop.bordereau.validate" _description = "Valider les bordereaux sélectionnés" @api.model def default_get(self, fields): res = super(ScopBordereauValidate, self).default_get(fields) - template_id = self.env.ref( - 'cgscop_cotisation_cg.email_template_cotisation_cg') - res.update({ - 'subject': template_id.subject, - 'body_html': template_id.body_html, - }) + template_id = self.env.ref("cgscop_cotisation_cg.email_template_cotisation_cg") + res.update( + { + "subject": template_id.subject, + "body_html": template_id.body_html, + } + ) return res - subject = fields.Char('Objet:', translate=True, sanitize=False) - body_html = fields.Html('Corps du mail:', translate=True, sanitize=False) + subject = fields.Char("Objet:", translate=True) + body_html = fields.Html("Corps du mail:", translate=True, sanitize=False) - @api.multi def bordereau_validate(self): context = dict(self._context or {}) - active_ids = context.get('active_ids', []) or [] + active_ids = context.get("active_ids", []) or [] - bordereau_ids = self.env['scop.bordereau'].browse(active_ids) - not_new_bordereau = bordereau_ids.filtered( - lambda b: b.state != 'new') + bordereau_ids = self.env["scop.bordereau"].browse(active_ids) + not_new_bordereau = bordereau_ids.filtered(lambda b: b.state != "new") if not_new_bordereau: - raise UserError(_("Impossible de valider un bordereau qui " - "n'est pas à l'état de brouillon")) + raise UserError( + _( + "Impossible de valider un bordereau qui " + "n'est pas à l'état de brouillon" + ) + ) else: template_id = self.env.ref( - 'cgscop_cotisation_cg.email_template_cotisation_cg') - mail_compose_message_id = self.env['mail.compose.message'].create({ - 'subject': self.subject, - 'body': self.body_html, - 'model': 'scop.bordereau', - 'email_from': template_id.email_from, - 'reply_to': template_id.reply_to, - 'auto_delete': False, - 'composition_mode': 'mass_mail', - }) + "cgscop_cotisation_cg.email_template_cotisation_cg" + ) + mail_compose_message_id = self.env["mail.compose.message"].create( + { + "subject": self.subject, + "body": self.body_html, + "model": "scop.bordereau", + "email_from": template_id.email_from, + "reply_to": template_id.reply_to, + "auto_delete": False, + "composition_mode": "mass_mail", + } + ) bordereau_ids.validate_bordereau_multi(mail_compose_message_id.id) - return {'type': 'ir.actions.act_window_close'} + return {"type": "ir.actions.act_window_close"} diff --git a/wizard/scop_bordereau_validate_confirm_view.xml b/wizard/scop_bordereau_validate_confirm_view.xml index 04e7a80..deec285 100644 --- a/wizard/scop_bordereau_validate_confirm_view.xml +++ b/wizard/scop_bordereau_validate_confirm_view.xml @@ -1,7 +1,6 @@ -<?xml version="1.0"?> +<?xml version="1.0" ?> <!-- Copyright 2021 Le Filament License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> - <odoo> <data> @@ -10,25 +9,37 @@ <field name="model">scop.bordereau.validate</field> <field name="arch" type="xml"> <form string="Valider les bordereaux"> - <field name="subject"/> - <field name="body_html"/> - <hr/> + <field name="subject" /> + <field name="body_html" /> + <hr /> <p class="oe_grey"> Une fois les bordereaux validés, vous ne pourrez plus les remettre en brouillon ! </p> <footer> - <button string="Valider" name="bordereau_validate" type="object" default_focus="1" class="btn-primary"/> - <button string="Annuler" class="btn-secondary" special="cancel"/> + <button + string="Valider" + name="bordereau_validate" + type="object" + default_focus="1" + class="btn-primary" + /> + <button + string="Annuler" + class="btn-secondary" + special="cancel" + /> </footer> </form> </field> </record> - <act_window id="action_scop_bordereau_validate" - multi="True" - name="Valider le(s) bordereau(x)" - res_model="scop.bordereau.validate" src_model="scop.bordereau" - view_mode="form" target="new" view_type="form" /> + <record id="action_scop_bordereau_validate" model="ir.actions.act_window"> + <field name="name">Valider le(s) bordereau(x)</field> + <field name="res_model">scop.bordereau.validate</field> + <field name="binding_model_id" ref="model_scop_bordereau" /> + <field name="view_mode">form</field> + <field name="target">new</field> + </record> </data> </odoo> diff --git a/wizard/scop_cotisation_cg_regul.py b/wizard/scop_cotisation_cg_regul.py index 5f0d6d7..5b6f564 100644 --- a/wizard/scop_cotisation_cg_regul.py +++ b/wizard/scop_cotisation_cg_regul.py @@ -1,85 +1,89 @@ # Copyright 2020 Le Filament # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo import fields, models, api +from odoo import _, api, fields, models from odoo.exceptions import UserError, ValidationError class ScopCotisationRegul(models.TransientModel): - _name = 'scop.cotisation.regul.wizard' - _description = 'Regularisation cotisation CG' + _name = "scop.cotisation.regul.wizard" + _description = "Regularisation cotisation CG" name = fields.Char() date_regul = fields.Date( - string="Date de régularisation", - default=fields.Date.today()) + string="Date de régularisation", default=fields.Date.today() + ) bordereau_id = fields.Many2one( - comodel_name='scop.bordereau', - string='Bordereau', + comodel_name="scop.bordereau", + string="Bordereau", readonly=True, ) partner_id = fields.Many2one( - comodel_name='res.partner', - string='Adhérent', + comodel_name="res.partner", + string="Adhérent", readonly=True, ) # Old liasse liasse_fiscale_id = fields.Many2one( - comodel_name='scop.liasse.fiscale', - string='Liasse Fiscale de référence', + comodel_name="scop.liasse.fiscale", + string="Liasse Fiscale de référence", ) - year = fields.Integer('Année de la liasse') + year = fields.Integer("Année de la liasse") type_assiette_retenu = fields.Selection( - string='Type assiette', - selection=[('ca', 'CA'), - ('va', 'VA'), ], + string="Type assiette", + selection=[ + ("ca", "CA"), + ("va", "VA"), + ], ) montant_assiette = fields.Float( - string='Montant assiette de cotisation',) - amount_total_cotiz = fields.Float( - string='Montant total de(s) cotisation(s)') - detail = fields.Text('Détail cotisation') - comment = fields.Char('Motif de régularisation') + string="Montant assiette de cotisation", + ) + amount_total_cotiz = fields.Float(string="Montant total de(s) cotisation(s)") + detail = fields.Text("Détail cotisation") + comment = fields.Char("Motif de régularisation") # New liasse liasse_fiscale_new_id = fields.Many2one( - comodel_name='scop.liasse.fiscale', - string='Liasse Fiscale', + comodel_name="scop.liasse.fiscale", + string="Liasse Fiscale", ) type_id_new = fields.Selection( - string='Type de liasse', related='liasse_fiscale_new_id.type_id') + string="Type de liasse", related="liasse_fiscale_new_id.type_id" + ) source_new = fields.Selection( - string='Source de la liasse', related='liasse_fiscale_new_id.source') + string="Source de la liasse", related="liasse_fiscale_new_id.source" + ) type_assiette_new = fields.Selection( - related='liasse_fiscale_new_id.contribution_base_type') + related="liasse_fiscale_new_id.contribution_base_type" + ) montant_assiette_new = fields.Integer( - related='liasse_fiscale_new_id.contribution_base_amount') - amount_cg = fields.Float( - related='liasse_fiscale_new_id.contribution_cg') - amount_ur_med = fields.Float( - related='liasse_fiscale_new_id.contribution_med') - amount_ur_hdf = fields.Float( - related='liasse_fiscale_new_id.contribution_hdf') - amount_fede_com = fields.Float( - related='liasse_fiscale_new_id.contribution_com') - amount_fede_cae = fields.Float( - related='liasse_fiscale_new_id.contribution_cae') - - is_payment = fields.Boolean('Paiements liés') + related="liasse_fiscale_new_id.contribution_base_amount" + ) + amount_cg = fields.Float(related="liasse_fiscale_new_id.contribution_cg") + amount_ur_med = fields.Float(related="liasse_fiscale_new_id.contribution_med") + amount_ur_hdf = fields.Float(related="liasse_fiscale_new_id.contribution_hdf") + amount_fede_com = fields.Float(related="liasse_fiscale_new_id.contribution_com") + amount_fede_cae = fields.Float(related="liasse_fiscale_new_id.contribution_cae") + + is_payment = fields.Boolean("Paiements liés") # ------------------------------------------------------ # Constrains # ------------------------------------------------------ - @api.constrains('date_regul') + @api.constrains("date_regul") def _check_date_regul(self): - last_date = max(self.bordereau_id.invoice_ids.mapped('date_invoice')) - if self.date_regul > fields.Date.today() or \ - self.date_regul < last_date: - raise ValidationError("La date de régulation doit être " - "inférieure ou égale à la date du jour et " - "supérieure à la dernière date de " - "facturation liée au bordereau.") + last_date = max(self.bordereau_id.invoice_ids.mapped("date_invoice")) + if self.date_regul > fields.Date.today() or self.date_regul < last_date: + raise ValidationError( + _( + "La date de régulation doit être " + "inférieure ou égale à la date du jour et " + "supérieure à la dernière date de " + "facturation liée au bordereau." + ) + ) # ------------------------------------------------------ # Override ORM @@ -87,38 +91,47 @@ class ScopCotisationRegul(models.TransientModel): @api.model def default_get(self, fields): res = super(ScopCotisationRegul, self).default_get(fields) - bordereau_id = self.env['scop.bordereau'].browse( - self.env.context.get('active_id') + bordereau_id = self.env["scop.bordereau"].browse( + self.env.context.get("active_id") ) contribs = bordereau_id.invoice_ids.read_group( - [('id', 'in', bordereau_id.invoice_ids.ids)], - ['type_contribution_id', 'amount_total_signed'], - ['type_contribution_id']) + [("id", "in", bordereau_id.invoice_ids.ids)], + ["type_contribution_id", "amount_total_signed"], + ["type_contribution_id"], + ) detail = "<table class='o_group o_inner_group'>" for contrib in contribs: - detail += ('<tr><td class="o_td_label font-weight-bold">' - + str(contrib.get('type_contribution_id')[1]) - + '</td><td style="width: 100%;">' - + str(contrib.get('amount_total_signed')) + '€</td>') - detail += '</table>' - payments = list(map( - lambda i: True if i.payment_move_line_ids else False, - bordereau_id.invoice_ids)) + detail += ( + '<tr><td class="o_td_label font-weight-bold">' + + str(contrib.get("type_contribution_id")[1]) + + '</td><td style="width: 100%;">' + + str(contrib.get("amount_total_signed")) + + "€</td>" + ) + detail += "</table>" + payments = list( + map( + lambda i: True if i.payment_move_line_ids else False, + bordereau_id.invoice_ids, + ) + ) if True in payments: is_payment = True else: is_payment = False - res.update({ - 'bordereau_id': bordereau_id.id, - 'partner_id': bordereau_id.partner_id.id, - 'liasse_fiscale_id': bordereau_id.liasse_fiscale_id.id, - 'year': bordereau_id.year_liasse, - 'type_assiette_retenu': bordereau_id.type_assiette, - 'montant_assiette': bordereau_id.montant_assiette, - 'amount_total_cotiz': bordereau_id.amount_total_cotiz, - 'detail': detail, - 'is_payment': is_payment, - }) + res.update( + { + "bordereau_id": bordereau_id.id, + "partner_id": bordereau_id.partner_id.id, + "liasse_fiscale_id": bordereau_id.liasse_fiscale_id.id, + "year": bordereau_id.year_liasse, + "type_assiette_retenu": bordereau_id.type_assiette, + "montant_assiette": bordereau_id.montant_assiette, + "amount_total_cotiz": bordereau_id.amount_total_cotiz, + "detail": detail, + "is_payment": is_payment, + } + ) return res # ------------------------------------------------------ @@ -126,32 +139,36 @@ class ScopCotisationRegul(models.TransientModel): # ------------------------------------------------------ def update_contribution(self): if not self.comment: - raise UserError("Vous devez renseigner un motif de " - "régularisation pour valider.") + raise UserError( + _("Vous devez renseigner un motif de " "régularisation pour valider.") + ) bordereau_id = self.bordereau_id partner_id = bordereau_id.partner_id # CREATE VERSION - bordereau_id.read(['amount_total_cotiz']) - self.env['scop.bordereau.version'].create({ - 'bordereau_id': bordereau_id.id, - 'date': self.date_regul, - 'comment': self.comment, - 'version': bordereau_id.version, - 'liasse_fiscale_id_old': bordereau_id.liasse_fiscale_id.id, - 'type_assiette': bordereau_id.type_assiette, - 'montant_assiette': bordereau_id.montant_assiette, - 'amount_total_cotiz': bordereau_id.amount_total_cotiz, - }) + bordereau_id.read(["amount_total_cotiz"]) + self.env["scop.bordereau.version"].create( + { + "bordereau_id": bordereau_id.id, + "date": self.date_regul, + "comment": self.comment, + "version": bordereau_id.version, + "liasse_fiscale_id_old": bordereau_id.liasse_fiscale_id.id, + "type_assiette": bordereau_id.type_assiette, + "montant_assiette": bordereau_id.montant_assiette, + "amount_total_cotiz": bordereau_id.amount_total_cotiz, + } + ) # CREATE REGUL contribs = bordereau_id.invoice_ids.read_group( - [('id', 'in', bordereau_id.invoice_ids.ids)], - ['type_contribution_id', 'amount_total_signed'], - ['type_contribution_id']) + [("id", "in", bordereau_id.invoice_ids.ids)], + ["type_contribution_id", "amount_total_signed"], + ["type_contribution_id"], + ) - ur_hdf = self.env.ref('cgscop_partner.riga_14232').id + ur_hdf = self.env.ref("cgscop_partner.riga_14232").id if partner_id.ur_id.id == ur_hdf: amount_ur = self.amount_ur_hdf product_ur = self.env.user.company_id.contribution_hdf_id @@ -162,87 +179,100 @@ class ScopCotisationRegul(models.TransientModel): account_ur = self.env.user.company_id.receivable_account_ur_med_id cotiz_type = { - self.env.ref('cgscop_partner.riga_14397').id: - [self.amount_cg, - self.env.user.company_id.contribution_cg_id, - self.env.user.company_id.contribution_journal_id, - partner_id.property_account_receivable_id.id], - self.env.ref('cgscop_partner.riga_14398').id: - [self.amount_fede_com, - self.env.user.company_id.contribution_fede_com_id, - self.env.user.company_id.contribution_ur_or_fede_journal_id, - self.env.user.company_id.receivable_account_fede_com_id], - self.env.ref('cgscop_partner.cotiz_fede_cae').id: - [self.amount_fede_cae, - self.env.user.company_id.contribution_fede_cae_id, - self.env.user.company_id.contribution_ur_or_fede_journal_id, - self.env.user.company_id.receivable_account_fede_cae_id], - self.env.ref('cgscop_partner.riga_14399').id: - [amount_ur, - product_ur, - self.env.user.company_id.contribution_ur_or_fede_journal_id, - account_ur], + self.env.ref("cgscop_partner.riga_14397").id: [ + self.amount_cg, + self.env.user.company_id.contribution_cg_id, + self.env.user.company_id.contribution_journal_id, + partner_id.property_account_receivable_id.id, + ], + self.env.ref("cgscop_partner.riga_14398").id: [ + self.amount_fede_com, + self.env.user.company_id.contribution_fede_com_id, + self.env.user.company_id.contribution_ur_or_fede_journal_id, + self.env.user.company_id.receivable_account_fede_com_id, + ], + self.env.ref("cgscop_partner.cotiz_fede_cae").id: [ + self.amount_fede_cae, + self.env.user.company_id.contribution_fede_cae_id, + self.env.user.company_id.contribution_ur_or_fede_journal_id, + self.env.user.company_id.receivable_account_fede_cae_id, + ], + self.env.ref("cgscop_partner.riga_14399").id: [ + amount_ur, + product_ur, + self.env.user.company_id.contribution_ur_or_fede_journal_id, + account_ur, + ], } - quarters = [bordereau_id.base_cotisation_cg.trimester_1, - bordereau_id.base_cotisation_cg.trimester_2, - bordereau_id.base_cotisation_cg.trimester_3, - bordereau_id.base_cotisation_cg.trimester_4] + quarters = [ + bordereau_id.base_cotisation_cg.trimester_1, + bordereau_id.base_cotisation_cg.trimester_2, + bordereau_id.base_cotisation_cg.trimester_3, + bordereau_id.base_cotisation_cg.trimester_4, + ] for contrib in contribs: - type_cotiz = contrib.get('type_contribution_id')[0] - amount_cotiz_old = contrib.get('amount_total_signed') + type_cotiz = contrib.get("type_contribution_id")[0] + amount_cotiz_old = contrib.get("amount_total_signed") amount_cotiz = cotiz_type.get(type_cotiz)[0] - refund_amount_total = self.env['scop.cotisation'].\ - round_to_closest_multiple(amount_cotiz_old - amount_cotiz, 4) + refund_amount_total = self.env["scop.cotisation"].round_to_closest_multiple( + amount_cotiz_old - amount_cotiz, 4 + ) product = cotiz_type.get(type_cotiz)[1] type_invoice = False if refund_amount_total < 0: - type_invoice = 'out_invoice' + type_invoice = "out_invoice" refund_amount_total *= -1 elif refund_amount_total > 0: - type_invoice = 'out_refund' + type_invoice = "out_refund" refund_amount = refund_amount_total / bordereau_id.nb_quarter if type_invoice: - for i in range(0, bordereau_id.nb_quarter): + for i in range(0, int(bordereau_id.nb_quarter)): if self.date_regul < quarters[i]: date_due = quarters[i] else: date_due = self.date_regul - refund = self.env['account.invoice'].create({ - 'partner_id': partner_id.id, - 'journal_id': cotiz_type.get(type_cotiz)[2].id, - 'account_id': partner_id.property_account_receivable_id.id, - 'type': type_invoice, - 'date_invoice': self.date_regul, - 'date': self.date_regul, - 'state': 'draft', - 'number': False, - 'origin': bordereau_id.name, - 'name': self.comment, - 'bordereau_id': bordereau_id.id, - 'is_contribution': True, - 'year': bordereau_id.year, - 'liasse_fiscale_id': bordereau_id.liasse_fiscale_id.id, - 'type_contribution_id': type_cotiz, - 'payment_mode_id': bordereau_id.payment_mode_id.id, - 'date_due': date_due, - 'cotiz_quarter': i+1, - }) - self.env['account.invoice.line'].create({ - 'name': self.comment + " - " + str(i+1) + "/" + str(bordereau_id.nb_quarter), - 'invoice_id': refund.id, - 'product_id': product.id, - 'account_id': product.property_account_income_id.id, - 'price_unit': refund_amount - }) - bordereau_id.update({ - 'liasse_fiscale_id': self.liasse_fiscale_new_id.id - }) - bordereau_id.invoice_ids.update({ - 'liasse_fiscale_id': self.liasse_fiscale_new_id.id - }) + refund = self.env["account.invoice"].create( + { + "partner_id": partner_id.id, + "journal_id": cotiz_type.get(type_cotiz)[2].id, + "account_id": partner_id.property_account_receivable_id.id, + "type": type_invoice, + "date_invoice": self.date_regul, + "date": self.date_regul, + "state": "draft", + "number": False, + "origin": bordereau_id.name, + "name": self.comment, + "bordereau_id": bordereau_id.id, + "is_contribution": True, + "year": bordereau_id.year, + "liasse_fiscale_id": bordereau_id.liasse_fiscale_id.id, + "type_contribution_id": type_cotiz, + "payment_mode_id": bordereau_id.payment_mode_id.id, + "date_due": date_due, + "cotiz_quarter": i + 1, + } + ) + self.env["account.invoice.line"].create( + { + "name": self.comment + + " - " + + str(i + 1) + + "/" + + bordereau_id.nb_quarter, + "invoice_id": refund.id, + "product_id": product.id, + "account_id": product.property_account_income_id.id, + "price_unit": refund_amount, + } + ) + bordereau_id.update({"liasse_fiscale_id": self.liasse_fiscale_new_id.id}) + bordereau_id.invoice_ids.update( + {"liasse_fiscale_id": self.liasse_fiscale_new_id.id} + ) diff --git a/wizard/scop_cotisation_cg_regul_wizard.xml b/wizard/scop_cotisation_cg_regul_wizard.xml index b4a258d..f1b5638 100644 --- a/wizard/scop_cotisation_cg_regul_wizard.xml +++ b/wizard/scop_cotisation_cg_regul_wizard.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8" ?> <odoo> <!-- Copyright 2020 Le Filament License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> @@ -10,75 +10,145 @@ <field name="arch" type="xml"> <form string="Régularisation Cotisation"> <sheet> - <field name="is_payment" invisible="1"/> + <field name="is_payment" invisible="1" /> <div class="row"> <div class="col-6"> - <label for="bordereau_id"/> + <label for="bordereau_id" /> <h1> - <field name="bordereau_id" options='{"no_open": True}'/> + <field + name="bordereau_id" + options='{"no_open": True}' + /> </h1> </div> <div class="col-6"> - <label for="partner_id"/> + <label for="partner_id" /> <h3> - <field name="partner_id" options='{"no_open": True}'/> + <field + name="partner_id" + options='{"no_open": True}' + /> </h3> </div> </div> <div class="row"> <div class="col-6"> <group string="Données initiales" class="text-muted"> - <field name="liasse_fiscale_id" readonly="1" options='{"no_open": True}'/> - <field name="year" readonly="1"/> - <field name="type_assiette_retenu" readonly="1"/> - <field name="montant_assiette" readonly="1"/> - <field name="amount_total_cotiz" class="font-weight-bold" readonly="1"/> + <field + name="liasse_fiscale_id" + readonly="1" + options='{"no_open": True}' + /> + <field name="year" readonly="1" /> + <field name="type_assiette_retenu" readonly="1" /> + <field name="montant_assiette" readonly="1" /> + <field + name="amount_total_cotiz" + class="font-weight-bold" + readonly="1" + /> </group> - <field name="detail" class="text-muted" readonly="1" widget="html"/> + <field + name="detail" + class="text-muted" + readonly="1" + widget="html" + /> </div> <div class="col-6"> <group string="Nouveaux calculs"> - <field name="date_regul" required="1"/> - <separator/> - <field name="liasse_fiscale_new_id" domain="[('partner_id', '=', partner_id)]" - options='{"no_create": True}' help="Nouvelle liasse fiscale à prendre en compte"/> + <field name="date_regul" required="1" /> + <separator /> + <field + name="liasse_fiscale_new_id" + domain="[('partner_id', '=', partner_id)]" + options='{"no_create": True}' + help="Nouvelle liasse fiscale à prendre en compte" + /> </group> <group> - <field name="type_id_new" attrs="{'invisible': [('liasse_fiscale_new_id', '=', False)]}"/> - <field name="source_new" attrs="{'invisible': [('liasse_fiscale_new_id', '=', False)]}"/> + <field + name="type_id_new" + attrs="{'invisible': [('liasse_fiscale_new_id', '=', False)]}" + /> + <field + name="source_new" + attrs="{'invisible': [('liasse_fiscale_new_id', '=', False)]}" + /> </group> <group> - <field name="type_assiette_new" attrs="{'invisible': [('amount_cg', '=', 0.0)]}" readonly="1"/> - <field name="montant_assiette_new" attrs="{'invisible': [('amount_cg', '=', 0.0)]}" readonly="1"/> + <field + name="type_assiette_new" + attrs="{'invisible': [('amount_cg', '=', 0.0)]}" + readonly="1" + /> + <field + name="montant_assiette_new" + attrs="{'invisible': [('amount_cg', '=', 0.0)]}" + readonly="1" + /> </group> <group> - <field name="amount_cg" attrs="{'invisible': [('amount_cg', '=', 0.0)]}" readonly="1"/> - <field name="amount_fede_com" attrs="{'invisible': [('amount_fede_com', '=', 0.0)]}" readonly="1"/> - <field name="amount_fede_cae" attrs="{'invisible': [('amount_fede_cae', '=', 0.0)]}" readonly="1"/> - <field name="amount_ur_hdf" attrs="{'invisible': [('amount_ur_hdf', '=', 0.0)]}" readonly="1"/> - <field name="amount_ur_med" attrs="{'invisible': [('amount_ur_med', '=', 0.0)]}" readonly="1"/> + <field + name="amount_cg" + attrs="{'invisible': [('amount_cg', '=', 0.0)]}" + readonly="1" + /> + <field + name="amount_fede_com" + attrs="{'invisible': [('amount_fede_com', '=', 0.0)]}" + readonly="1" + /> + <field + name="amount_fede_cae" + attrs="{'invisible': [('amount_fede_cae', '=', 0.0)]}" + readonly="1" + /> + <field + name="amount_ur_hdf" + attrs="{'invisible': [('amount_ur_hdf', '=', 0.0)]}" + readonly="1" + /> + <field + name="amount_ur_med" + attrs="{'invisible': [('amount_ur_med', '=', 0.0)]}" + readonly="1" + /> </group> - <label for="comment" attrs="{'invisible': [('amount_cg', '=', 0.0)]}"/> - <field name="comment" attrs="{'invisible': [('amount_cg', '=', 0.0)]}"/> + <label + for="comment" + attrs="{'invisible': [('amount_cg', '=', 0.0)]}" + /> + <field + name="comment" + attrs="{'invisible': [('amount_cg', '=', 0.0)]}" + /> </div> </div> </sheet> <footer> - <button name="update_contribution" type="object" string="Régulariser le bordereau" class="oe_highlight"/> - <button string="Annuler" special="cancel" class="oe_link"/> + <button + name="update_contribution" + type="object" + string="Régulariser le bordereau" + class="oe_highlight" + /> + <button string="Annuler" special="cancel" class="oe_link" /> </footer> </form> </field> </record> - <record id="scop_cotisation_regul_wizard_act_window" model="ir.actions.act_window"> + <record + id="scop_cotisation_regul_wizard_act_window" + model="ir.actions.act_window" + > <field name="name">Régularisation Cotisation</field> <field name="type">ir.actions.act_window</field> <field name="res_model">scop.cotisation.regul.wizard</field> - <field name="view_type">form</field> <field name="view_mode">form</field> <field name="target">new</field> </record> </data> -</odoo> \ No newline at end of file +</odoo> diff --git a/wizard/scop_cotisation_cg_wizard.py b/wizard/scop_cotisation_cg_wizard.py index bef19b4..3e3d30f 100644 --- a/wizard/scop_cotisation_cg_wizard.py +++ b/wizard/scop_cotisation_cg_wizard.py @@ -3,116 +3,121 @@ import logging -from odoo import api, fields, models, exceptions +from odoo import _, api, exceptions, fields, models _logger = logging.getLogger(__name__) class ScopCotisationWizard(models.TransientModel): - _name = 'scop.cotisation.cg.wizard' + _name = "scop.cotisation.cg.wizard" _description = "Wizard: Génération des cotisations nouveaux adhérents" - year = fields.Char( - string='Année', - required=True) + year = fields.Char(string="Année", required=True) cotisation_cg_id = fields.Many2one( - comodel_name='scop.cotisation.cg', - string='Base de cotisation') + comodel_name="scop.cotisation.cg", string="Base de cotisation" + ) nb_quarter = fields.Selection( - string='Nombre de trimestres de cotisation', - selection=[(1, '1'), - (2, '2'), - (3, '3'), - (4, '4')], - default='4', - required=True) - type = fields.Selection([ - ('all', 'Toutes les coop nouvelles adhérentes'), - ('selected', 'Sélectionner des coop')], - string='Type de création', - default='all' + string="Nombre de trimestres de cotisation", + selection=[("1", "1"), ("2", "2"), ("3", "3"), ("4", "4")], + default="4", + required=True, ) - partner_ids = fields.Many2many( - comodel_name='res.partner', - string='Coopératives' + type = fields.Selection( + [ + ("all", "Toutes les coop nouvelles adhérentes"), + ("selected", "Sélectionner des coop"), + ], + string="Type de création", + default="all", ) - date = fields.Date('Date de cotisation', default=fields.Date.today()) + partner_ids = fields.Many2many(comodel_name="res.partner", string="Coopératives") + date = fields.Date("Date de cotisation", default=fields.Date.today()) # ------------------------------------------------------ # Button function # ------------------------------------------------------ - @api.onchange('type') + @api.onchange("type") def onchange_domain_partner_ids(self): - if self.type == 'selected': - id_cotisation_cg = self.env.context['id_cotisation_cg'] + if self.type == "selected": + id_cotisation_cg = self.env.context["id_cotisation_cg"] invoiced_members = self.cotisation_cg_id.browse( - id_cotisation_cg).invoice_ids.mapped('partner_id') - - res = {'domain': { - 'partner_ids': [ - ('is_company', '=', True), - ('type', '!=', 'facility'), - ('id', 'not in', invoiced_members.mapped('id'))] - }} + id_cotisation_cg + ).invoice_ids.mapped("partner_id") + + res = { + "domain": { + "partner_ids": [ + ("is_company", "=", True), + ("type", "!=", "facility"), + ("id", "not in", invoiced_members.mapped("id")), + ] + } + } return res # ------------------------------------------------------ # Button function # ------------------------------------------------------ - @api.multi def cotiz_generate_new_adherents(self): - if not self.env.user.company_id.is_contribution_cg: + if not self.env.company.is_contribution_cg: raise exceptions.UserError( - "La gestion des cotisations CGScop n'est pas configurée.") - - if self.type == 'selected': + _("La gestion des cotisations CGScop n'est pas configurée.") + ) + if self.type == "selected": members = self.partner_ids else: members = self.cotisation_cg_id.get_new_members(self.date) - invoiced_members = self.cotisation_cg_id. \ - invoice_ids.mapped('partner_id') - + invoiced_members = self.cotisation_cg_id.invoice_ids.mapped("partner_id") members_to_invoice = members - invoiced_members if len(members_to_invoice) > 0: message = ( - "<h3>Bordereaux " + str(self.year) + - "</h3> <hr/>" + - "<br/>Bordereaux nouveaux adhérents à générer : " + - str(len(members_to_invoice)) + - "<p>Les appels de cotisation sont en cours de " - "création...</p> " + "<h3>Bordereaux " + + str(self.year) + + "</h3> <hr/>" + + "<br/>Bordereaux nouveaux adhérents à générer : " + + str(len(members_to_invoice)) + + "<p>Les appels de cotisation sont en cours de " + "création...</p> " ) - message_id = self.env['message.wizard'].create( - {'message': message}) # Job queue - batch_name = (fields.Datetime.to_string(fields.Datetime.now()) + - " Génération des bordereaux " + - str(self.cotisation_cg_id.year)) - batch = self.env['queue.job.batch'].get_new_batch(batch_name) + batch_name = ( + fields.Datetime.to_string(fields.Datetime.now()) + + " Génération des bordereaux " + + str(self.cotisation_cg_id.year) + ) + batch = self.env["queue.job.batch"].get_new_batch(batch_name) for member in members_to_invoice: liasse_id = self.cotisation_cg_id.get_liasse(member) self.cotisation_cg_id.with_context( job_batch=batch ).with_delay().create_bordereau( - member=member, liasse=liasse_id, - nb_quarter=self.nb_quarter, date=self.date) + member=member, + liasse=liasse_id, + nb_quarter=self.nb_quarter, + date=self.date, + ) batch.enqueue() else: - message = ("<p class='text-center'>Tous les bordereaux pour les " - "coops qui ont adhéré cette année avant le %s ont " - "déjà été créés !</p>" % self.date.strftime("%d/%m")) - message_id = self.env['message.wizard'].create( - {'message': message}) + message = ( + "<p class='text-center'>Tous les bordereaux pour les " + "coops qui ont adhéré cette année avant le %s ont " + "déjà été créés !</p>" % self.date.strftime("%d/%m") + ) return { - 'name': 'Génération des appels de cotisation nouveaux adhérents', - 'type': 'ir.actions.act_window', - 'view_mode': 'form', - 'res_model': 'message.wizard', - 'res_id': message_id.id, - 'target': 'new' + "type": "ir.actions.act_window.message", + "title": _("Génération des appels de cotisation nouveaux adhérents"), + "is_html_message": True, + "message": _(message), + "close_button_title": False, + "buttons": [ + { + "type": "ir.actions.act_window_close", + "name": "Fermer", + }, + ], } diff --git a/wizard/scop_cotisation_cg_wizard.xml b/wizard/scop_cotisation_cg_wizard.xml index 5429634..33c52e8 100644 --- a/wizard/scop_cotisation_cg_wizard.xml +++ b/wizard/scop_cotisation_cg_wizard.xml @@ -1,7 +1,6 @@ -<?xml version="1.0"?> +<?xml version="1.0" ?> <!-- Copyright 2021 Le Filament License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> - <odoo> <data> @@ -11,26 +10,38 @@ <field name="arch" type="xml"> <form string="Générer les cotisation pour les nouveaux adhérents"> <group> - <field name="year" required="1" invisible="1"/> - <field name="date" required="1"/> - <field name="nb_quarter" required="1"/> - <field name="type" widget="radio"/> - <field name="partner_ids" attrs="{'invisible': [('type', '=', 'all')]}" widget="many2many_tags" options="{'no_create': True, 'no_open': True}"/> + <field name="year" required="1" invisible="1" /> + <field name="date" required="1" /> + <field name="nb_quarter" required="1" /> + <field name="type" widget="radio" /> + <field + name="partner_ids" + attrs="{'invisible': [('type', '=', 'all')]}" + widget="many2many_tags" + options="{'no_create': True, 'no_open': True}" + /> </group> <footer> - <button name="cotiz_generate_new_adherents" type="object" - string="Générer les cotisations" class="oe_highlight"/> - <button special="cancel" string="Cancel"/> + <button + name="cotiz_generate_new_adherents" + type="object" + string="Générer les cotisations" + class="oe_highlight" + /> + <button special="cancel" string="Cancel" /> </footer> </form> </field> </record> - <record model="ir.actions.act_window" id="action_genere_cotisation_cg_new_adherents"> + <record + model="ir.actions.act_window" + id="action_genere_cotisation_cg_new_adherents" + > <field name="name">Générer les cotisations nouveaux adhérents</field> <field name="res_model">scop.cotisation.cg.wizard</field> <field name="view_mode">form</field> - <field name="view_id" ref="scop_cotisation_cg_wizard_form_view"/> + <field name="view_id" ref="scop_cotisation_cg_wizard_form_view" /> <field name="target">new</field> </record> -- GitLab