diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..28a0808 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,291 @@ +{ + "globals": { + "$": false, + "_": false, + "fuzzy": false, + "jQuery": false, + "moment": false, + "odoo": false, + "openerp": false, + "self": false + }, + "env": { + "browser": true + }, + "rules": { + "no-alert": "warn", + "no-array-constructor": "warn", + "no-bitwise": "off", + "no-caller": "warn", + "no-case-declarations": "warn", + "no-catch-shadow": "warn", + "no-class-assign": "warn", + "no-cond-assign": "warn", + "no-confusing-arrow": "warn", + "no-console": "off", + "no-const-assign": "warn", + "no-constant-condition": "warn", + "no-continue": "off", + "no-control-regex": "warn", + "no-debugger": "warn", + "no-delete-var": "warn", + "no-div-regex": "warn", + "no-dupe-args": "warn", + "no-dupe-class-members": "warn", + "no-dupe-keys": "warn", + "no-duplicate-case": "warn", + "no-duplicate-imports": "warn", + "no-else-return": "warn", + "no-empty": "warn", + "no-empty-character-class": "warn", + "no-empty-function": "warn", + "no-empty-pattern": "warn", + "no-eq-null": "warn", + "no-eval": "warn", + "no-ex-assign": "warn", + "no-extend-native": "warn", + "no-extra-bind": "warn", + "no-extra-boolean-cast": "warn", + "no-extra-label": "warn", + "no-extra-parens": "warn", + "no-extra-semi": "warn", + "no-fallthrough": "warn", + "no-floating-decimal": "warn", + "no-func-assign": "warn", + "no-implicit-coercion": ["warn", { + "allow": ["~"] + }], + "no-implicit-globals": "warn", + "no-implied-eval": "warn", + "no-inline-comments": "warn", + "no-inner-declarations": "warn", + "no-invalid-regexp": "warn", + "no-invalid-this": "off", + "no-irregular-whitespace": "warn", + "no-iterator": "warn", + "no-label-var": "warn", + "no-labels": "warn", + "no-lone-blocks": "warn", + "no-lonely-if": "warn", + "no-loop-func": "off", + "no-magic-numbers": "off", + "no-mixed-operators": "warn", + "no-mixed-requires": "warn", + "no-mixed-spaces-and-tabs": "warn", + "no-multi-spaces": "warn", + "no-multi-str": "warn", + "no-multiple-empty-lines": "warn", + "no-native-reassign": "warn", + "no-negated-condition": "warn", + "no-negated-in-lhs": "warn", + "no-nested-ternary": "off", + "no-new": "warn", + "no-new-func": "warn", + "no-new-object": "warn", + "no-new-require": "warn", + "no-new-symbol": "warn", + "no-new-wrappers": "warn", + "no-obj-calls": "warn", + "no-octal": "warn", + "no-octal-escape": "warn", + "no-param-reassign": "warn", + "no-path-concat": "warn", + "no-plusplus": "off", + "no-process-env": "warn", + "no-process-exit": "warn", + "no-proto": "warn", + "no-prototype-builtins": "warn", + "no-redeclare": "warn", + "no-regex-spaces": "warn", + "no-restricted-globals": "warn", + "no-restricted-imports": "warn", + "no-restricted-modules": "warn", + "no-restricted-syntax": "warn", + "no-return-assign": "warn", + "no-script-url": "warn", + "no-self-assign": "warn", + "no-self-compare": "warn", + "no-sequences": "warn", + "no-shadow": "warn", + "no-shadow-restricted-names": "warn", + "no-whitespace-before-property": "warn", + "no-spaced-func": "warn", + "no-sparse-arrays": "warn", + "no-sync": "warn", + "no-tabs": "warn", + "no-ternary": "off", + "no-trailing-spaces": "warn", + "no-this-before-super": "warn", + "no-throw-literal": "warn", + "no-undef": "warn", + "no-undef-init": "warn", + "no-undefined": "off", + "no-unexpected-multiline": "warn", + "no-underscore-dangle": "off", + "no-unmodified-loop-condition": "warn", + "no-unneeded-ternary": "warn", + "no-unreachable": "warn", + "no-unsafe-finally": "warn", + "no-unused-expressions": "warn", + "no-unused-labels": "warn", + "no-unused-vars": "warn", + "no-use-before-define": "warn", + "no-useless-call": "warn", + "no-useless-computed-key": "warn", + "no-useless-concat": "warn", + "no-useless-constructor": "warn", + "no-useless-escape": "warn", + "no-useless-rename": "warn", + "no-void": "warn", + "no-var": "off", + "no-warning-comments": "off", + "no-with": "warn", + "array-bracket-spacing": "off", + "array-callback-return": "warn", + "arrow-body-style": "warn", + "arrow-parens": "warn", + "arrow-spacing": "off", + "accessor-pairs": "warn", + "block-scoped-var": "off", + "block-spacing": ["warn", "always"], + "brace-style": "warn", + "callback-return": "warn", + "camelcase": "off", + "capitalized-comments": ["warn", "always", { + "ignoreConsecutiveComments": true, + "ignoreInlineComments": true + }], + "comma-dangle": ["warn", "always-multiline"], + "comma-spacing": ["warn", { + "before": false, + "after": true + }], + "comma-style": "warn", + "complexity": [ + "warn", + 15 + ], + "computed-property-spacing": "off", + "consistent-return": "off", + "consistent-this": "off", + "constructor-super": "warn", + "curly": "warn", + "default-case": "off", + "dot-location": ["warn", "property"], + "dot-notation": "warn", + "eol-last": "warn", + "eqeqeq": "warn", + "func-names": "off", + "func-style": "off", + "generator-star-spacing": "off", + "global-require": "warn", + "guard-for-in": "off", + "handle-callback-err": "warn", + "id-blacklist": "warn", + "id-length": "off", + "id-match": "warn", + "indent": "warn", + "init-declarations": "warn", + "jsx-quotes": "warn", + "key-spacing": "off", + "keyword-spacing": "warn", + "linebreak-style": [ + "warn", + "unix" + ], + "lines-around-comment": "warn", + "max-depth": "warn", + "max-len": ["warn", { + "code": 88, + "ignorePattern": "odoo\\.define\\(", + "tabWidth": 4 + }], + "max-lines": "off", + "max-nested-callbacks": "warn", + "max-params": "off", + "max-statements": "off", + "max-statements-per-line": "warn", + "multiline-ternary": "off", + "new-cap": "off", + "new-parens": "warn", + "newline-after-var": "off", + "newline-before-return": "off", + "newline-per-chained-call": "off", + "object-curly-newline": ["warn", { "consistent": true }], + "object-curly-spacing": ["warn", "never"], + "object-property-newline": ["warn", { + "allowAllPropertiesOnSameLine": true + }], + "object-shorthand": "off", + "one-var": "off", + "one-var-declaration-per-line": "off", + "operator-assignment": "warn", + "operator-linebreak": "warn", + "padded-blocks": "off", + "prefer-arrow-callback": "off", + "prefer-const": "warn", + "prefer-reflect": "off", + "prefer-rest-params": "off", + "prefer-spread": "off", + "prefer-template": "off", + "quote-props": "off", + "quotes": "off", + "radix": "warn", + "require-yield": "warn", + "rest-spread-spacing": "off", + "semi": [ + "warn", + "always" + ], + "semi-spacing": "warn", + "sort-imports": "warn", + "sort-vars": "off", + "space-before-blocks": "warn", + "space-before-function-paren": "warn", + "space-in-parens": "off", + "space-infix-ops": "off", + "space-unary-ops": "off", + "spaced-comment": ["warn", "always"], + "strict": ["warn", "function"], + "template-curly-spacing": "off", + "unicode-bom": "warn", + "use-isnan": "warn", + "valid-jsdoc": ["warn", { + "prefer": { + "arg": "param", + "argument": "param", + "augments": "extends", + "constructor": "class", + "exception": "throws", + "func": "function", + "method": "function", + "prop": "property", + "return": "returns", + "virtual": "abstract", + "yield": "yields" + }, + "preferType": { + "array": "Array", + "bool": "Boolean", + "boolean": "Boolean", + "number": "Number", + "object": "Object", + "str": "String", + "string": "String" + }, + "requireParamDescription": false, + "requireReturn": false, + "requireReturnDescription": false, + "requireReturnType": false + }], + "valid-typeof": "warn", + "vars-on-top": "off", + "wrap-iife": "warn", + "wrap-regex": "warn", + "yield-star-spacing": "off", + "yoda": "warn" + }, + "parserOptions": { + "ecmaVersion": 2017 + } +} diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..2cac7e9 --- /dev/null +++ b/.flake8 @@ -0,0 +1,10 @@ +[flake8] +max-line-length = 79 +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 diff --git a/.gitignore b/.gitignore index 72364f9..894a44c 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,6 @@ __pycache__/ # Distribution / packaging .Python -env/ build/ develop-eggs/ dist/ @@ -20,9 +19,11 @@ lib64/ parts/ sdist/ var/ +wheels/ *.egg-info/ .installed.cfg *.egg +MANIFEST # PyInstaller # Usually these files are written by a python script from a template @@ -42,8 +43,9 @@ htmlcov/ .cache nosetests.xml coverage.xml -*,cover +*.cover .hypothesis/ +.pytest_cache/ # Translations *.mo @@ -52,6 +54,7 @@ coverage.xml # Django stuff: *.log local_settings.py +db.sqlite3 # Flask stuff: instance/ @@ -66,7 +69,7 @@ docs/_build/ # PyBuilder target/ -# IPython Notebook +# Jupyter Notebook .ipynb_checkpoints # pyenv @@ -75,15 +78,27 @@ target/ # celery beat schedule file celerybeat-schedule -# dotenv -.env +# SageMath parsed files +*.sage.py -# virtualenv +# Environments +.env +.venv +env/ venv/ ENV/ +env.bak/ +venv.bak/ # Spyder project settings .spyderproject +.spyproject # Rope project settings .ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 0000000..821e287 --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,12 @@ +[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=79 +known_odoo=odoo +known_odoo_addons=odoo.addons +sections=FUTURE,STDLIB,THIRDPARTY,ODOO,ODOO_ADDONS,FIRSTPARTY,LOCALFOLDER +known_third_party=addons,cStringIO,openerp,requests,werkzeug,xlsxwriter diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..43b7ca3 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,69 @@ +exclude: "^setup/|/static/lib/|/static/src/lib/" +default_language_version: + python: python3 +repos: +- repo: https://github.com/psf/black + rev: 19.3b0 + hooks: + - id: black + args: [-l 79] +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.3.0 + hooks: + - id: trailing-whitespace + # exclude autogenerated files + exclude: /README\.rst$|\.pot?$ + - id: end-of-file-fixer + # exclude autogenerated files + exclude: /README\.rst$|\.pot?$ + - id: debug-statements + - id: flake8 + name: flake8 except __init__.py + exclude: /__init__\.py$ + additional_dependencies: ["flake8-bugbear==19.8.0"] + - id: flake8 + name: flake8 only __init__.py + args: ["--extend-ignore=F401"] # ignore unused imports in __init__.py + files: /__init__\.py$ + additional_dependencies: ["flake8-bugbear==19.8.0"] + - id: fix-encoding-pragma + args: ["--remove"] + - id: check-case-conflict + - id: check-docstring-first + - id: check-executables-have-shebangs + - id: check-merge-conflict + - id: check-symlinks +# - id: check-xml + - id: mixed-line-ending + args: ["--fix=lf"] +- repo: https://github.com/pre-commit/mirrors-pylint + rev: v2.3.1 + hooks: + - id: pylint + name: pylint with optional checks + args: ["--rcfile=.pylintrc", "--exit-zero"] + verbose: true + additional_dependencies: ["pylint-odoo==3.0.3"] + - id: pylint + name: pylint with mandatory checks + args: ["--rcfile=.pylintrc-mandatory"] + additional_dependencies: ["pylint-odoo==3.0.3"] +- repo: https://github.com/asottile/pyupgrade + rev: v1.24.0 + hooks: + - id: pyupgrade +- repo: https://github.com/asottile/seed-isort-config + rev: v1.9.3 + hooks: + - id: seed-isort-config +- repo: https://github.com/pre-commit/mirrors-isort + rev: v4.3.21 + hooks: + - id: isort + name: isort except __init__.py + exclude: /__init__\.py$ +#- repo: https://github.com/pre-commit/mirrors-eslint +# rev: v6.5.1 +# hooks: +# - id: eslint +# verbose: true diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..7e89f0e --- /dev/null +++ b/.pylintrc @@ -0,0 +1,86 @@ +[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= +manifest_required_keys=license +manifest_deprecated_keys=description,active +license_allowed=AGPL-3,GPL-2,GPL-2 or any later version,GPL-3,GPL-3 or any later version,LGPL-3 +valid_odoo_versions=12.0 + +[MESSAGES CONTROL] +disable=all + +# This .pylintrc contains optional AND mandatory checks and is meant to be +# loaded in an IDE to have it check everything, in the hope this will make +# optional checks more visible to contributors who otherwise never look at a +# green travis to see optional checks that failed. +# .pylintrc-mandatory containing only mandatory checks is used the pre-commit +# config as a blocking check. + +enable=anomalous-backslash-in-string, + api-one-deprecated, + api-one-multi-together, + assignment-from-none, + attribute-deprecated, + class-camelcase, + dangerous-default-value, + dangerous-view-replace-wo-priority, + 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-key, + manifest-version-format, + method-compute, + method-inverse, + method-required-super, + method-search, + missing-import-error, + missing-manifest-dependency, + 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-newline-extrafiles, + missing-readme, + 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..a39913f --- /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= +manifest_required_keys=license +manifest_deprecated_keys=description,active +license_allowed=AGPL-3,GPL-2,GPL-2 or any later version,GPL-3,GPL-3 or any later version,LGPL-3 +valid_odoo_versions=12.0 + +[MESSAGES CONTROL] +disable=all + +enable=anomalous-backslash-in-string, + api-one-deprecated, + api-one-multi-together, + assignment-from-none, + attribute-deprecated, + class-camelcase, + dangerous-default-value, + dangerous-view-replace-wo-priority, + 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-key, + manifest-version-format, + method-compute, + method-inverse, + method-required-super, + method-search, + missing-import-error, + missing-manifest-dependency, + 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/.travis.yml b/.travis.yml new file mode 100644 index 0000000..18160a2 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,34 @@ +language: python +sudo: false +cache: pip + +python: + - "3.5" + +addons: + postgresql: "9.6" + apt: + packages: + - expect-dev # provides unbuffer utility + +env: + global: + - VERSION="12.0" TESTS="0" LINT_CHECK="0" MAKEPOT="0" + + matrix: + - LINT_CHECK="1" + - TESTS="1" ODOO_REPO="OCA/OCB" + - TESTS="1" ODOO_REPO="OCA/OCB" UNIT_TEST="1" + + +install: + - git clone --depth=1 https://github.com/coopiteasy/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools + - export PATH=${HOME}/maintainer-quality-tools/travis:${PATH} + - travis_install_nightly + - pip install unidecode + +script: + - travis_run_tests + +after_success: + - travis_after_tests_success diff --git a/README.md b/README.md index e19d749..51bff9e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,44 @@ # vertical-cooperative This repository gather odoo modules for cooperatives + +# MAKE TRAVIS GREEN AGAIN + +pre-commit still issues these messages. They need to be fixed. + +``` +************* Module easy_my_coop.models.partner +easy_my_coop/models/partner.py:56: [E8103(sql-injection), ResPartner._invoice_total] SQL injection risk. Use parameters if you can. - More info https://github.com/OCA/odoo-community.org/blob/master/website/Contribution/CONTRIBUTING.rst#no-sql-injection +************* Module partner_age.models.partner +partner_age/models/partner.py:13: [E8103(sql-injection), ResPartner._search_age] SQL injection risk. Use parameters if you can. - More info https://github.com/OCA/odoo-community.org/blob/master/website/Contribution/CONTRIBUTING.rst#no-sql-injection +************* Module easy_my_coop_taxshelter_report.models.tax_shelter_declaration +easy_my_coop_taxshelter_report/models/tax_shelter_declaration.py:325: [E8102(invalid-commit), TaxShelterCertificate.send_certificates] Use of cr.commit() directly - More info https://github.com/OCA/odoo-community.org/blob/master/website/Contribution/CONTRIBUTING.rst#never-commit-the-transaction +************* Module easy_my_coop.models.account_invoice +easy_my_coop/models/account_invoice.py:11: [C8104(class-camelcase), account_invoice] Use `CamelCase` "AccountInvoice" in class name "account_invoice". You can use oca-autopep8 of https://github.com/OCA/maintainer-tools to auto fix it. +************* Module easy_my_coop.models.operation_request +easy_my_coop/models/operation_request.py:12: [C8104(class-camelcase), operation_request] Use `CamelCase` "OperationRequest" in class name "operation_request". You can use oca-autopep8 of https://github.com/OCA/maintainer-tools to auto fix it. +************* Module easy_my_coop.models.coop +easy_my_coop/models/coop.py:287: [C8108(method-compute), SubscriptionRequest] Name of compute method should start with "_compute_" +************* Module website_recaptcha_reloaded.models.res_config +website_recaptcha_reloaded/models/res_config.py:7: [C8104(class-camelcase), website_config_settings] Use `CamelCase` "WebsiteConfigSettings" in class name "website_config_settings". You can use oca-autopep8 of https://github.com/OCA/maintainer-tools to auto fix it. +************* Module easy_my_coop.models.company +easy_my_coop/models/company.py:61: [C8108(method-compute), ResCompany] Name of compute method should start with "_compute_" + +pylint with mandatory checks.............................................Failed +- hook id: pylint +- exit code: 18 + +************* Module easy_my_coop.models.partner +easy_my_coop/models/partner.py:56: [E8103(sql-injection), ResPartner._invoice_total] SQL injection risk. Use parameters if you can. - More info https://github.com/OCA/odoo-community.org/blob/master/website/Contribution/CONTRIBUTING.rst#no-sql-injection +************* Module partner_age.models.partner +partner_age/models/partner.py:13: [E8103(sql-injection), ResPartner._search_age] SQL injection risk. Use parameters if you can. - More info https://github.com/OCA/odoo-community.org/blob/master/website/Contribution/CONTRIBUTING.rst#no-sql-injection +************* Module easy_my_coop.models.account_invoice +easy_my_coop/models/account_invoice.py:11: [C8104(class-camelcase), account_invoice] Use `CamelCase` "AccountInvoice" in class name "account_invoice". You can use oca-autopep8 of https://github.com/OCA/maintainer-tools to auto fix it. +************* Module easy_my_coop.models.operation_request +easy_my_coop/models/operation_request.py:12: [C8104(class-camelcase), operation_request] Use `CamelCase` "OperationRequest" in class name "operation_request". You can use oca-autopep8 of https://github.com/OCA/maintainer-tools to auto fix it. +************* Module easy_my_coop.models.coop +easy_my_coop/models/coop.py:287: [C8108(method-compute), SubscriptionRequest] Name of compute method should start with "_compute_" +************* Module website_recaptcha_reloaded.models.res_config +website_recaptcha_reloaded/models/res_config.py:7: [C8104(class-camelcase), website_config_settings] Use `CamelCase` "WebsiteConfigSettings" in class name "website_config_settings". You can use oca-autopep8 of https://github.com/OCA/maintainer-tools to auto fix it. +************* Module easy_my_coop.models.company +easy_my_coop/models/company.py:61: [C8108(method-compute), ResCompany] Name of compute method should start with "_compute_" +``` diff --git a/easy_my_coop/README.rst b/easy_my_coop/README.rst new file mode 100644 index 0000000..de5f272 --- /dev/null +++ b/easy_my_coop/README.rst @@ -0,0 +1,58 @@ +============ +Easy My Coop +============ + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-coopiteasy%2Fvertical--cooperative-lightgray.png?logo=github + :target: https://github.com/coopiteasy/vertical-cooperative/tree/12.0/easy_my_coop + :alt: coopiteasy/vertical-cooperative + +|badge1| |badge2| |badge3| + +This module allows to manage the cooperator subscription and all the cooperative business processes. + +**Table of contents** + +.. contents:: + :local: + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Coop IT Easy SCRLfs + +Contributors +~~~~~~~~~~~~ + +* Coop IT Easy SCRLfs + + +Maintainers +~~~~~~~~~~~ + +This module is part of the `coopiteasy/vertical-cooperative `_ project on GitHub. + +You are welcome to contribute. diff --git a/easy_my_coop/__init__.py b/easy_my_coop/__init__.py index 28aa8a4..7660e7b 100644 --- a/easy_my_coop/__init__.py +++ b/easy_my_coop/__init__.py @@ -1,3 +1,3 @@ -from . import models -from . import report -from . import wizard +from . import models +from . import report +from . import wizard diff --git a/easy_my_coop/__manifest__.py b/easy_my_coop/__manifest__.py index 89b3073..8fde8f5 100644 --- a/easy_my_coop/__manifest__.py +++ b/easy_my_coop/__manifest__.py @@ -4,59 +4,54 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). { - 'name': 'Easy My Coop', - 'version': '12.0.3.0.1', - 'depends': [ - 'base', - 'web', - 'sale', - 'account', - 'base_iban', - 'product', - 'partner_firstname', - 'partner_contact_birthdate', - 'partner_contact_address', - 'email_template_config', + "name": "Easy My Coop", + "summary": "Manage your cooperative shares", + "version": "12.0.3.0.1", + "depends": [ + "base", + "web", + "sale", + "account", + "base_iban", + "product", + "partner_firstname", + "partner_contact_birthdate", + "partner_contact_address", + "email_template_config", ], - 'author': 'Coop IT Easy SCRLfs', - 'category': 'Cooperative management', - 'website': 'https://www.coopiteasy.be', - 'license': 'AGPL-3', - 'description': """ - This module allows to manage the cooperator subscription and all the - cooperative business processes. - """, - 'data': [ - 'data/easy_my_coop_data.xml', - 'data/paperformat.xml', - 'security/res_groups.xml', - 'security/ir.model.access.csv', - 'wizard/create_subscription_from_partner.xml', - 'wizard/update_partner_info.xml', - 'wizard/validate_subscription_request.xml', - 'wizard/update_share_line.xml', - 'views/subscription_request_view.xml', - 'views/email_template_view.xml', - 'views/res_partner_view.xml', - 'views/cooperator_register_view.xml', - 'views/operation_request_view.xml', - 'views/account_invoice_view.xml', - 'views/product_view.xml', - 'views/res_company_view.xml', - 'views/account_journal_view.xml', - 'views/menus.xml', - 'report/easy_my_coop_report.xml', - 'report/layout.xml', - 'report/cooperator_invoice_G002.xml', - 'report/cooperator_certificat_G001.xml', - 'report/cooperator_subscription_G001.xml', - 'report/cooperator_register_G001.xml', - 'data/mail_template_data.xml', # Must be loaded after reports + "author": "Coop IT Easy SCRLfs", + "category": "Cooperative management", + "website": "https://www.coopiteasy.be", + "license": "AGPL-3", + "data": [ + "data/easy_my_coop_data.xml", + "data/paperformat.xml", + "security/res_groups.xml", + "security/ir.model.access.csv", + "wizard/create_subscription_from_partner.xml", + "wizard/cooperative_history_wizard.xml", # todo remove? + "wizard/update_partner_info.xml", + "wizard/validate_subscription_request.xml", + "wizard/update_share_line.xml", + "views/subscription_request_view.xml", + "views/email_template_view.xml", + "views/res_partner_view.xml", + "views/cooperator_register_view.xml", + "views/operation_request_view.xml", + "views/account_invoice_view.xml", + "views/product_view.xml", + "views/res_company_view.xml", + "views/account_journal_view.xml", + "views/menus.xml", + "report/easy_my_coop_report.xml", + "report/layout.xml", + "report/cooperator_invoice_G002.xml", + "report/cooperator_certificat_G001.xml", + "report/cooperator_subscription_G001.xml", + "report/cooperator_register_G001.xml", + "data/mail_template_data.xml", # Must be loaded after reports ], - 'demo': [ - 'demo/coop.xml', - 'demo/users.xml', - ], - 'installable': True, - 'application': True, + "demo": ["demo/coop.xml", "demo/users.xml"], + "installable": True, + "application": True, } diff --git a/easy_my_coop/data/easy_my_coop_data.xml b/easy_my_coop/data/easy_my_coop_data.xml index 9cb4333..40c9c7c 100644 --- a/easy_my_coop/data/easy_my_coop_data.xml +++ b/easy_my_coop/data/easy_my_coop_data.xml @@ -1,43 +1,44 @@ - - - - - Cooperative Management - Manage your cooperative. - 3 - - - - - Company Share - - - - Account Default Subscription Journal - - SUBJ/%(year)s/ - True - - - - Subscription Journal - SUBJ - sale - - - - - Subscription Register - subscription.register - - - - - - Register Operation - register.operation - - - - - + + + + + Cooperative Management + Manage your cooperative. + 3 + + + + + Company Share + + + + Account Default Subscription Journal + + SUBJ/%(year)s/ + True + + + + Subscription Journal + SUBJ + sale + + + + + Subscription Register + subscription.register + + + + + + Register Operation + register.operation + + + + + diff --git a/easy_my_coop/data/mail_template_data.xml b/easy_my_coop/data/mail_template_data.xml index 0fe4d8f..c487e1a 100644 --- a/easy_my_coop/data/mail_template_data.xml +++ b/easy_my_coop/data/mail_template_data.xml @@ -1,443 +1,497 @@ - - - - - - - Request to Release Capital - Send by Email - ${(object.company_id.coop_email_contact or object.user_id.email)|safe} - ${object.company_id.name} Request to Release Capital (Ref ${object.number or 'n/a'}) - ${object.partner_id.id} - ${(object.company_id.coop_email_contact or object.user_id.email)|safe} - - - - ${(object.number or '').replace('/','_')}_${object.state == 'draft' and 'draft' or ''} - ${object.partner_id.lang} - - - -

Hello ${object.partner_id.name},

- -

You will find in attachment all the necessary information for the payment. We kindly remind you that your subscription will be effective only once we received the payment.

- -

Do not forget to add the structured communication to the payment.

- -

Sustainably your,

-

${object.company_id.name}.

- - % if object.company_id.street: - ${object.company_id.street} - % endif - % if object.company_id.street2: - ${object.company_id.street2}
- % endif - % if object.company_id.city or object.company_id.zip: - ${object.company_id.zip} ${object.company_id.city}
- % endif - % if object.company_id.country_id: - ${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}
- % endif - % if object.company_id.phone: - Phone:  ${object.company_id.phone} - % endif - - % if object.company_id.website: - - %endif - -
- -
- - ]]>
-
- - - Confirmation Email - ${(object.company_id.coop_email_contact or object.user_id.email)|safe} - Subscription request confirmation - ${object.email} - ${(object.company_id.coop_email_contact or object.user_id.email)|safe} - - - ${object.lang} - - - -

Hello ${object.name},

- -

Your request will be soon processed by our team. If all the provided info are correct you will soon receive the payment information in another email

- -
-

If you have any question, do not hesitate to contact us.

-
- -

Sustainably your,

-

${object.company_id.name}.

- - % if object.company_id.street: - ${object.company_id.street} - % endif - % if object.company_id.street2: - ${object.company_id.street2}
- % endif - % if object.company_id.city or object.company_id.zip: - ${object.company_id.zip} ${object.company_id.city}
- % endif - % if object.company_id.country_id: - ${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}
- % endif - % if object.company_id.phone: - Phone:  ${object.company_id.phone} - % endif - - % if object.company_id.website: - - %endif - -
- -
- - ]]>
-
- - - Waiting List Email - ${(object.company_id.coop_email_contact or object.user_id.email)|safe} - Subscription request added on waiting list. - ${object.email} - ${(object.company_id.coop_email_contact or object.user_id.email)|safe} - - - ${object.lang} - - - -

Hello ${object.name},

- -

Thank you for your subscription request. There are currently no project to raise funds for, thus we added it on waiting list. - We will contact you as soon as the subscription requests are re-opened. -

- -
-

If you have any question, do not hesitate to contact us.

-
- -

Sustainably yours,

-

${object.company_id.name}.

- - % if object.company_id.street: - ${object.company_id.street} - % endif - % if object.company_id.street2: - ${object.company_id.street2}
- % endif - % if object.company_id.city or object.company_id.zip: - ${object.company_id.zip} ${object.company_id.city}
- % endif - % if object.company_id.country_id: - ${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}
- % endif - % if object.company_id.phone: - Phone:  ${object.company_id.phone} - % endif - - % if object.company_id.website: - - %endif - -
- -
- - ]]>
-
- - - Company Confirmation Email - ${(object.company_id.coop_email_contact or object.user_id.email)|safe} - Subscription request confirmation - ${object.email},${object.company_email} - ${(object.company_id.coop_email_contact or object.user_id.email)|safe} - - - ${object.lang} - - - -

Hello ${object.name},

- -

We have received your subscription request for ${object.company_id.name}. Thank you for your support.

- -

Your request will be soon processed by our team "gestion et participation des membres". If all the provided info are correct you will soon receive the payment information in another email

- -
-

If you have any question, do not hesitate to contact us.

-
- -

Sustainably your,

-

${object.company_id.name}.

- - % if object.company_id.street: - ${object.company_id.street} - % endif - % if object.company_id.street2: - ${object.company_id.street2}
- % endif - % if object.company_id.city or object.company_id.zip: - ${object.company_id.zip} ${object.company_id.city}
- % endif - % if object.company_id.country_id: - ${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}
- % endif - % if object.company_id.phone: - Phone:  ${object.company_id.phone} - % endif - - % if object.company_id.website: - - %endif - -
- -
- - ]]>
-
- - - Payment Received Confirmation - Send By Email - ${(object.company_id.coop_email_contact or object.user_id.email)|safe} - Payment Received Confirmation - ${object.id} - ${(object.company_id.coop_email_contact or object.user_id.email)|safe} - - - - Certificat ${(object.cooperator_register_number or '')} - ${object.lang} - - - -

Hello ${object.name},

- -

We confirm the reception of you payment. You are now shareholder of our cooperative

- -
-

Find in attachment your ${object.company_id.name} certificate.

-

Thank you for choosing ${object.company_id.name or 'us'}!

-
-

Sustainably your,

-

${object.company_id.name}.

- - % if object.company_id.street: - ${object.company_id.street} - % endif - % if object.company_id.street2: - ${object.company_id.street2}
- % endif - % if object.company_id.city or object.company_id.zip: - ${object.company_id.zip} ${object.company_id.city}
- % endif - % if object.company_id.country_id: - ${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}
- % endif - % if object.company_id.phone: - Phone:  ${object.company_id.phone} - % endif - - % if object.company_id.website: - - %endif - -
- -
- - ]]>
-
- - - Share Increase - Payment Received Confirmation - Send By Email - ${(object.company_id.coop_email_contact or object.user_id.email)|safe} - Payment Received Confirmation - ${object.id} - ${(object.company_id.coop_email_contact or object.user_id.email)|safe} - - - - Certificat ${(object.cooperator_register_number or '')} - ${object.lang} - - - -

Hello ${object.name},

- -

We confirm the reception of you payment for the new share(s) you have taken.

- -
-

Find in attachment your ${object.company_id.name} certificate.

-

Thank you for trusting ${object.company_id.name or 'us'}!

-
-

Sustainably your,

-

${object.company_id.name}.

- - % if object.company_id.street: - ${object.company_id.street} - % endif - % if object.company_id.street2: - ${object.company_id.street2}
- % endif - % if object.company_id.city or object.company_id.zip: - ${object.company_id.zip} ${object.company_id.city}
- % endif - % if object.company_id.country_id: - ${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}
- % endif - % if object.company_id.phone: - Phone:  ${object.company_id.phone} - % endif - - % if object.company_id.website: - - %endif - -
- -
- - ]]>
-
- - - Share transfer - Send By Email - ${(object.company_id.coop_email_contact or object.user_id.email)|safe} - Share transfert - ${object.id} - ${(object.company_id.coop_email_contact or object.user_id.email)|safe} - - - - Certificat ${(object.cooperator_register_number or '')} - ${object.lang} - - - -

Hello ${object.name},

- -

We confirm you that the shares have been transfered to you. If you was not already cooperator, you are now shareholder of our cooperative

- -
-

Find in attachment your ${object.company_id.name} certificate.

-

Thank you for choosing ${object.company_id.name or 'us'}!

-
-

Sustainably your,

-

${object.company_id.name}.

- - % if object.company_id.street: - ${object.company_id.street} - % endif - % if object.company_id.street2: - ${object.company_id.street2}
- % endif - % if object.company_id.city or object.company_id.zip: - ${object.company_id.zip} ${object.company_id.city}
- % endif - % if object.company_id.country_id: - ${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}
- % endif - % if object.company_id.phone: - Phone:  ${object.company_id.phone} - % endif - - % if object.company_id.website: - - %endif - -
- -
- - ]]>
-
- - - Share update - Send By Email - ${(object.company_id.coop_email_contact or object.user_id.email)|safe} - Share update - ${object.id} - ${(object.company_id.coop_email_contact or object.user_id.email)|safe} - - - - Certificat ${(object.cooperator_register_number or '')} - ${object.lang} - - - -

Hello ${object.name},

- -

We confirm you that the adaptation on shares portfolio has been succesfully performed. Your cooperator certificate has been adapted accordingly

- -
-

Find in attachment your ${object.company_id.name} certificate.

-

Thank you for choosing ${object.company_id.name or 'us'}!

-
-

Sustainably your,

-

${object.company_id.name}.

- - % if object.company_id.street: - ${object.company_id.street} - % endif - % if object.company_id.street2: - ${object.company_id.street2}
- % endif - % if object.company_id.city or object.company_id.zip: - ${object.company_id.zip} ${object.company_id.city}
- % endif - % if object.company_id.country_id: - ${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}
- % endif - % if object.company_id.phone: - Phone:  ${object.company_id.phone} - % endif - - % if object.company_id.website: - - %endif - -
- -
- - ]]>
-
-
-
+ + + + + + + Request to Release Capital - Send by Email + + + ${(object.company_id.coop_email_contact or object.user_id.email)|safe} + + ${object.company_id.name} Request to Release + Capital (Ref ${object.number or 'n/a'}) + + ${object.partner_id.id} + + ${(object.company_id.coop_email_contact or object.user_id.email)|safe} + + + + + + ${(object.number or '').replace('/','_')}_${object.state == 'draft' and 'draft' or ''} + + ${object.partner_id.lang} + + + +

Hello ${object.partner_id.name},

+ +

You will find in attachment all the necessary information for the payment. We kindly remind you that your subscription will be effective only once we received the payment.

+ +

Do not forget to add the structured communication to the payment.

+ +

Sustainably your,

+

${object.company_id.name}.

+ + % if object.company_id.street: + ${object.company_id.street} + % endif + % if object.company_id.street2: + ${object.company_id.street2}
+ % endif + % if object.company_id.city or object.company_id.zip: + ${object.company_id.zip} ${object.company_id.city}
+ % endif + % if object.company_id.country_id: + ${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}
+ % endif + % if object.company_id.phone: + Phone:  ${object.company_id.phone} + % endif + + % if object.company_id.website: + + %endif + +
+ +
+ + ]]>
+
+ + + Confirmation Email + + ${(object.company_id.coop_email_contact or object.user_id.email)|safe} + + Subscription request confirmation + ${object.email} + + ${(object.company_id.coop_email_contact or object.user_id.email)|safe} + + + + ${object.lang} + + + +

Hello ${object.name},

+ +

Your request will be soon processed by our team. If all the provided info are correct you will soon receive the payment information in another email

+ +
+

If you have any question, do not hesitate to contact us.

+
+ +

Sustainably your,

+

${object.company_id.name}.

+ + % if object.company_id.street: + ${object.company_id.street} + % endif + % if object.company_id.street2: + ${object.company_id.street2}
+ % endif + % if object.company_id.city or object.company_id.zip: + ${object.company_id.zip} ${object.company_id.city}
+ % endif + % if object.company_id.country_id: + ${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}
+ % endif + % if object.company_id.phone: + Phone:  ${object.company_id.phone} + % endif + + % if object.company_id.website: + + %endif + +
+ +
+ + ]]>
+
+ + + Waiting List Email + + ${(object.company_id.coop_email_contact or object.user_id.email)|safe} + + Subscription request added on waiting list. + + ${object.email} + + ${(object.company_id.coop_email_contact or object.user_id.email)|safe} + + + + ${object.lang} + + + +

Hello ${object.name},

+ +

Thank you for your subscription request. There are currently no project to raise funds for, thus we added it on waiting list. + We will contact you as soon as the subscription requests are re-opened. +

+ +
+

If you have any question, do not hesitate to contact us.

+
+ +

Sustainably yours,

+

${object.company_id.name}.

+ + % if object.company_id.street: + ${object.company_id.street} + % endif + % if object.company_id.street2: + ${object.company_id.street2}
+ % endif + % if object.company_id.city or object.company_id.zip: + ${object.company_id.zip} ${object.company_id.city}
+ % endif + % if object.company_id.country_id: + ${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}
+ % endif + % if object.company_id.phone: + Phone:  ${object.company_id.phone} + % endif + + % if object.company_id.website: + + %endif + +
+ +
+ + ]]>
+
+ + + Company Confirmation Email + + ${(object.company_id.coop_email_contact or object.user_id.email)|safe} + + Subscription request confirmation + ${object.email},${object.company_email} + + + ${(object.company_id.coop_email_contact or object.user_id.email)|safe} + + + + ${object.lang} + + + +

Hello ${object.name},

+ +

We have received your subscription request for ${object.company_id.name}. Thank you for your support.

+ +

Your request will be soon processed by our team "gestion et participation des membres". If all the provided info are correct you will soon receive the payment information in another email

+ +
+

If you have any question, do not hesitate to contact us.

+
+ +

Sustainably your,

+

${object.company_id.name}.

+ + % if object.company_id.street: + ${object.company_id.street} + % endif + % if object.company_id.street2: + ${object.company_id.street2}
+ % endif + % if object.company_id.city or object.company_id.zip: + ${object.company_id.zip} ${object.company_id.city}
+ % endif + % if object.company_id.country_id: + ${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}
+ % endif + % if object.company_id.phone: + Phone:  ${object.company_id.phone} + % endif + + % if object.company_id.website: + + %endif + +
+ +
+ + ]]>
+
+ + + Payment Received Confirmation - Send By Email + + + ${(object.company_id.coop_email_contact or object.user_id.email)|safe} + + Payment Received Confirmation + ${object.id} + + ${(object.company_id.coop_email_contact or object.user_id.email)|safe} + + + + + Certificat + ${(object.cooperator_register_number or '')} + + ${object.lang} + + + +

Hello ${object.name},

+ +

We confirm the reception of you payment. You are now shareholder of our cooperative

+ +
+

Find in attachment your ${object.company_id.name} certificate.

+

Thank you for choosing ${object.company_id.name or 'us'}!

+
+

Sustainably your,

+

${object.company_id.name}.

+ + % if object.company_id.street: + ${object.company_id.street} + % endif + % if object.company_id.street2: + ${object.company_id.street2}
+ % endif + % if object.company_id.city or object.company_id.zip: + ${object.company_id.zip} ${object.company_id.city}
+ % endif + % if object.company_id.country_id: + ${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}
+ % endif + % if object.company_id.phone: + Phone:  ${object.company_id.phone} + % endif + + % if object.company_id.website: + + %endif + +
+ +
+ + ]]>
+
+ + + Share Increase - Payment Received Confirmation - + Send By Email + + + ${(object.company_id.coop_email_contact or object.user_id.email)|safe} + + Payment Received Confirmation + ${object.id} + + ${(object.company_id.coop_email_contact or object.user_id.email)|safe} + + + + + Certificat + ${(object.cooperator_register_number or '')} + + ${object.lang} + + + +

Hello ${object.name},

+ +

We confirm the reception of you payment for the new share(s) you have taken.

+ +
+

Find in attachment your ${object.company_id.name} certificate.

+

Thank you for trusting ${object.company_id.name or 'us'}!

+
+

Sustainably your,

+

${object.company_id.name}.

+ + % if object.company_id.street: + ${object.company_id.street} + % endif + % if object.company_id.street2: + ${object.company_id.street2}
+ % endif + % if object.company_id.city or object.company_id.zip: + ${object.company_id.zip} ${object.company_id.city}
+ % endif + % if object.company_id.country_id: + ${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}
+ % endif + % if object.company_id.phone: + Phone:  ${object.company_id.phone} + % endif + + % if object.company_id.website: + + %endif + +
+ +
+ + ]]>
+
+ + + Share transfer - Send By Email + + ${(object.company_id.coop_email_contact or object.user_id.email)|safe} + + Share transfert + ${object.id} + + ${(object.company_id.coop_email_contact or object.user_id.email)|safe} + + + + + Certificat + ${(object.cooperator_register_number or '')} + + ${object.lang} + + + +

Hello ${object.name},

+ +

We confirm you that the shares have been transfered to you. If you was not already cooperator, you are now shareholder of our cooperative

+ +
+

Find in attachment your ${object.company_id.name} certificate.

+

Thank you for choosing ${object.company_id.name or 'us'}!

+
+

Sustainably your,

+

${object.company_id.name}.

+ + % if object.company_id.street: + ${object.company_id.street} + % endif + % if object.company_id.street2: + ${object.company_id.street2}
+ % endif + % if object.company_id.city or object.company_id.zip: + ${object.company_id.zip} ${object.company_id.city}
+ % endif + % if object.company_id.country_id: + ${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}
+ % endif + % if object.company_id.phone: + Phone:  ${object.company_id.phone} + % endif + + % if object.company_id.website: + + %endif + +
+ +
+ + ]]>
+
+ + + Share update - Send By Email + + ${(object.company_id.coop_email_contact or object.user_id.email)|safe} + + Share update + ${object.id} + + ${(object.company_id.coop_email_contact or object.user_id.email)|safe} + + + + + Certificat + ${(object.cooperator_register_number or '')} + + ${object.lang} + + + +

Hello ${object.name},

+ +

We confirm you that the adaptation on shares portfolio has been succesfully performed. Your cooperator certificate has been adapted accordingly

+ +
+

Find in attachment your ${object.company_id.name} certificate.

+

Thank you for choosing ${object.company_id.name or 'us'}!

+
+

Sustainably your,

+

${object.company_id.name}.

+ + % if object.company_id.street: + ${object.company_id.street} + % endif + % if object.company_id.street2: + ${object.company_id.street2}
+ % endif + % if object.company_id.city or object.company_id.zip: + ${object.company_id.zip} ${object.company_id.city}
+ % endif + % if object.company_id.country_id: + ${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}
+ % endif + % if object.company_id.phone: + Phone:  ${object.company_id.phone} + % endif + + % if object.company_id.website: + + %endif + +
+ +
+ + ]]>
+
+
+
diff --git a/easy_my_coop/data/paperformat.xml b/easy_my_coop/data/paperformat.xml index 126eb1f..e745b23 100644 --- a/easy_my_coop/data/paperformat.xml +++ b/easy_my_coop/data/paperformat.xml @@ -3,7 +3,7 @@ diff --git a/easy_my_coop/demo/coop.xml b/easy_my_coop/demo/coop.xml index 1a164f1..e274b45 100644 --- a/easy_my_coop/demo/coop.xml +++ b/easy_my_coop/demo/coop.xml @@ -18,9 +18,9 @@ - FR7611808009101234567890147 - Bank - + FR7611808009101234567890147 + Bank + @@ -53,9 +53,9 @@ - BE71096123456769 - Bank - + BE71096123456769 + Bank + @@ -68,8 +68,8 @@ 5101 - - + + 416101 Cooperators @@ -77,7 +77,8 @@ - + @@ -116,24 +117,29 @@ manual 3 - + en_US - + 2 50 - + - + 4 50 - + diff --git a/easy_my_coop/demo/users.xml b/easy_my_coop/demo/users.xml index 1177375..98a69da 100644 --- a/easy_my_coop/demo/users.xml +++ b/easy_my_coop/demo/users.xml @@ -20,7 +20,8 @@ user-emc demo - + @@ -38,7 +39,8 @@ manager-emc demo - + diff --git a/easy_my_coop/migrations/8.0.1.0/pre-migration.py b/easy_my_coop/migrations/8.0.1.0/pre-migration.py index fc9f222..c0776dd 100644 --- a/easy_my_coop/migrations/8.0.1.0/pre-migration.py +++ b/easy_my_coop/migrations/8.0.1.0/pre-migration.py @@ -1,30 +1,29 @@ -# -*- coding: utf-8 -*- - -from openerp.openupgrade import openupgrade -import logging - -logger = logging.getLogger('OpenUpgrade') - -column_renames = { - 'job_sync_line': [ - ('adresse', 'address'), - ('ville', 'city'), - ('codepostal', 'zip'), - ('sync_date','date'), - ], - } - -tables_renames = [ - ('job_sync_line','subscription_request'), - ('job_sync',None), - ('external_db',None), -] - -@openupgrade.migrate() -def migrate(cr, version): - if not version: - return - - openupgrade.rename_columns(cr, column_renames) - openupgrade.rename_tables(cr, tables_renames) - +import logging + +from openerp.openupgrade import openupgrade + +logger = logging.getLogger("OpenUpgrade") + +column_renames = { + "job_sync_line": [ + ("adresse", "address"), + ("ville", "city"), + ("codepostal", "zip"), + ("sync_date", "date"), + ] +} + +tables_renames = [ + ("job_sync_line", "subscription_request"), + ("job_sync", None), + ("external_db", None), +] + + +@openupgrade.migrate() +def migrate(cr, version): + if not version: + return + + openupgrade.rename_columns(cr, column_renames) + openupgrade.rename_tables(cr, tables_renames) diff --git a/easy_my_coop/models/account_invoice.py b/easy_my_coop/models/account_invoice.py index 8b2382e..2961085 100644 --- a/easy_my_coop/models/account_invoice.py +++ b/easy_my_coop/models/account_invoice.py @@ -1,175 +1,197 @@ -# Copyright 2019 Coop IT Easy SCRL fs -# Houssine Bakkali -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - - -from datetime import datetime - -from odoo import api, fields, models - - -class account_invoice(models.Model): - _inherit = 'account.invoice' - - subscription_request = fields.Many2one('subscription.request', - string='Subscription request') - release_capital_request = fields.Boolean( - string='Release of capital request') - - @api.model - def _prepare_refund(self, invoice, date_invoice=None, date=None, - description=None, journal_id=None): - values = super(account_invoice, self)._prepare_refund( - invoice, date_invoice, date, - description, journal_id) - values['release_capital_request'] = self.release_capital_request - - return values - - def create_user(self, partner): - user_obj = self.env['res.users'] - email = partner.email - - user = user_obj.search([('login', '=', email)]) - if not user: - user = user_obj.search([('login', '=', email), - ('active', '=', False)]) - if user: - user.sudo().write({'active': True}) - else: - user_values = {'partner_id': partner.id, 'login': email} - user = user_obj.sudo()._signup_create_user(user_values) - user.sudo().with_context({'create_user': True}).action_reset_password() - - return user - - def get_mail_template_certificate(self): - if self.partner_id.member: - mail_template = 'easy_my_coop.email_template_certificat_increase' - else: - mail_template = 'easy_my_coop.email_template_certificat' - return self.env.ref(mail_template) - - def get_sequence_register(self): - return self.env.ref('easy_my_coop.sequence_subscription', False) - - def get_sequence_operation(self): - return self.env.ref('easy_my_coop.sequence_register_operation', False) - - def get_share_line_vals(self, line, effective_date): - return { - 'share_number': line.quantity, - 'share_product_id': line.product_id.id, - 'partner_id': self.partner_id.id, - 'share_unit_price': line.price_unit, - 'effective_date': effective_date - } - - def get_subscription_register_vals(self, line, effective_date): - return { - 'partner_id': self.partner_id.id, - 'quantity': line.quantity, - 'share_product_id': line.product_id.id, - 'share_unit_price': line.price_unit, - 'date': effective_date, - 'type': 'subscription' - } - - def get_membership_vals(self): - # flag the partner as an effective member - # if not yet cooperator we generate a cooperator number - vals = {} - if self.partner_id.member is False \ - and self.partner_id.old_member is False: - sequence_id = self.get_sequence_register() - sub_reg_num = sequence_id.next_by_id() - vals = {'member': True, 'old_member': False, - 'cooperator_register_number': int(sub_reg_num) - } - elif self.partner_id.old_member: - vals = {'member': True, 'old_member': False} - - return vals - - def set_membership(self): - vals = self.get_membership_vals() - self.partner_id.write(vals) - - return True - - def send_certificate_email(self, certificate_email_template, sub_reg_line): - # we send the email with the certificate in attachment - certificate_email_template.sudo().send_mail(self.partner_id.id, False) - - def set_cooperator_effective(self, effective_date): - sub_register_obj = self.env['subscription.register'] - share_line_obj = self.env['share.line'] - - certificate_email_template = self.get_mail_template_certificate() - - self.set_membership() - - sequence_operation = self.get_sequence_operation() - sub_reg_operation = sequence_operation.next_by_id() - - for line in self.invoice_line_ids: - sub_reg_vals = self.get_subscription_register_vals(line, - effective_date) - sub_reg_vals['name'] = sub_reg_operation - sub_reg_vals['register_number_operation'] = int(sub_reg_operation) - - sub_reg_line = sub_register_obj.create(sub_reg_vals) - - share_line_vals = self.get_share_line_vals(line, effective_date) - share_line_obj.create(share_line_vals) - - if line.product_id.mail_template: - certificate_email_template = line.product_id.mail_template - - self.send_certificate_email(certificate_email_template, sub_reg_line) - - if self.company_id.create_user: - self.create_user(self.partner_id) - - return True - - def post_process_confirm_paid(self, effective_date): - self.set_cooperator_effective(effective_date) - - return True - - def get_refund_domain(self, invoice): - return [ - ('type', '=', 'out_refund'), - ('origin', '=', invoice.move_name) - ] - - @api.multi - def action_invoice_paid(self): - super(account_invoice, self).action_invoice_paid() - for invoice in self: - # we check if there is an open refund for this invoice. in this - # case we don't run the process_subscription function as the - # invoice has been reconciled with a refund and not a payment. - domain = self.get_refund_domain(invoice) - refund = self.search(domain) - - if invoice.partner_id.cooperator \ - and invoice.release_capital_request \ - and invoice.type == 'out_invoice' and not refund: - # take the effective date from the payment. - # by default the confirmation date is the payment date - effective_date = datetime.now().strftime("%d/%m/%Y") - - if invoice.payment_move_line_ids: - move_line = invoice.payment_move_line_ids[0] - effective_date = move_line.date - - invoice.subscription_request.state = 'paid' - invoice.post_process_confirm_paid(effective_date) - # if there is a open refund we mark the subscription as cancelled - elif invoice.partner_id.cooperator \ - and invoice.release_capital_request \ - and invoice.type == 'out_invoice' and refund: - invoice.subscription_request.state = 'cancelled' - return True +# Copyright 2019 Coop IT Easy SCRL fs +# Houssine Bakkali +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + + +from datetime import datetime + +from odoo import api, fields, models + + +class AccountInvoice(models.Model): + _inherit = "account.invoice" + + subscription_request = fields.Many2one( + "subscription.request", string="Subscription request" + ) + release_capital_request = fields.Boolean( + string="Release of capital request" + ) + + @api.model + def _prepare_refund( + self, + invoice, + date_invoice=None, + date=None, + description=None, + journal_id=None, + ): + values = super(AccountInvoice, self)._prepare_refund( + invoice, date_invoice, date, description, journal_id + ) + values["release_capital_request"] = self.release_capital_request + + return values + + def create_user(self, partner): + user_obj = self.env["res.users"] + email = partner.email + + user = user_obj.search([("login", "=", email)]) + if not user: + user = user_obj.search( + [("login", "=", email), ("active", "=", False)] + ) + if user: + user.sudo().write({"active": True}) + else: + user_values = {"partner_id": partner.id, "login": email} + user = user_obj.sudo()._signup_create_user(user_values) + user.sudo().with_context( + {"create_user": True} + ).action_reset_password() + + return user + + def get_mail_template_certificate(self): + if self.partner_id.member: + mail_template = "easy_my_coop.email_template_certificat_increase" + else: + mail_template = "easy_my_coop.email_template_certificat" + return self.env.ref(mail_template) + + def get_sequence_register(self): + return self.env.ref("easy_my_coop.sequence_subscription", False) + + def get_sequence_operation(self): + return self.env.ref("easy_my_coop.sequence_register_operation", False) + + def get_share_line_vals(self, line, effective_date): + return { + "share_number": line.quantity, + "share_product_id": line.product_id.id, + "partner_id": self.partner_id.id, + "share_unit_price": line.price_unit, + "effective_date": effective_date, + } + + def get_subscription_register_vals(self, line, effective_date): + return { + "partner_id": self.partner_id.id, + "quantity": line.quantity, + "share_product_id": line.product_id.id, + "share_unit_price": line.price_unit, + "date": effective_date, + "type": "subscription", + } + + def get_membership_vals(self): + # flag the partner as an effective member + # if not yet cooperator we generate a cooperator number + vals = {} + if ( + self.partner_id.member is False + and self.partner_id.old_member is False + ): + sequence_id = self.get_sequence_register() + sub_reg_num = sequence_id.next_by_id() + vals = { + "member": True, + "old_member": False, + "cooperator_register_number": int(sub_reg_num), + } + elif self.partner_id.old_member: + vals = {"member": True, "old_member": False} + + return vals + + def set_membership(self): + vals = self.get_membership_vals() + self.partner_id.write(vals) + + return True + + def send_certificate_email(self, certificate_email_template, sub_reg_line): + # we send the email with the certificate in attachment + certificate_email_template.sudo().send_mail(self.partner_id.id, False) + + def set_cooperator_effective(self, effective_date): + sub_register_obj = self.env["subscription.register"] + share_line_obj = self.env["share.line"] + + certificate_email_template = self.get_mail_template_certificate() + + self.set_membership() + + sequence_operation = self.get_sequence_operation() + sub_reg_operation = sequence_operation.next_by_id() + + for line in self.invoice_line_ids: + sub_reg_vals = self.get_subscription_register_vals( + line, effective_date + ) + sub_reg_vals["name"] = sub_reg_operation + sub_reg_vals["register_number_operation"] = int(sub_reg_operation) + + sub_reg_line = sub_register_obj.create(sub_reg_vals) + + share_line_vals = self.get_share_line_vals(line, effective_date) + share_line_obj.create(share_line_vals) + + if line.product_id.mail_template: + certificate_email_template = line.product_id.mail_template + + self.send_certificate_email(certificate_email_template, sub_reg_line) + + if self.company_id.create_user: + self.create_user(self.partner_id) + + return True + + def post_process_confirm_paid(self, effective_date): + self.set_cooperator_effective(effective_date) + + return True + + def get_refund_domain(self, invoice): + return [ + ("type", "=", "out_refund"), + ("origin", "=", invoice.move_name), + ] + + @api.multi + def action_invoice_paid(self): + super(AccountInvoice, self).action_invoice_paid() + for invoice in self: + # we check if there is an open refund for this invoice. in this + # case we don't run the process_subscription function as the + # invoice has been reconciled with a refund and not a payment. + domain = self.get_refund_domain(invoice) + refund = self.search(domain) + + if ( + invoice.partner_id.cooperator + and invoice.release_capital_request + and invoice.type == "out_invoice" + and not refund + ): + # take the effective date from the payment. + # by default the confirmation date is the payment date + effective_date = datetime.now().strftime("%d/%m/%Y") + + if invoice.payment_move_line_ids: + move_line = invoice.payment_move_line_ids[0] + effective_date = move_line.date + + invoice.subscription_request.state = "paid" + invoice.post_process_confirm_paid(effective_date) + # if there is a open refund we mark the subscription as cancelled + elif ( + invoice.partner_id.cooperator + and invoice.release_capital_request + and invoice.type == "out_invoice" + and refund + ): + invoice.subscription_request.state = "cancelled" + return True diff --git a/easy_my_coop/models/account_journal.py b/easy_my_coop/models/account_journal.py index 9ec7ae1..8c85fa4 100644 --- a/easy_my_coop/models/account_journal.py +++ b/easy_my_coop/models/account_journal.py @@ -1,14 +1,13 @@ -# -*- coding: utf-8 -*- -# Copyright 2019 Coop IT Easy SCRL fs -# Houssine Bakkali -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - - -from odoo import fields, models - - -class AccountJournal(models.Model): - _inherit = "account.journal" - - get_cooperator_payment = fields.Boolean('Get cooperator payments?') - get_general_payment = fields.Boolean(string='Get general payments?') +# Copyright 2019 Coop IT Easy SCRL fs +# Houssine Bakkali +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + + +from odoo import fields, models + + +class AccountJournal(models.Model): + _inherit = "account.journal" + + get_cooperator_payment = fields.Boolean("Get cooperator payments?") + get_general_payment = fields.Boolean(string="Get general payments?") diff --git a/easy_my_coop/models/company.py b/easy_my_coop/models/company.py index 843f5b0..15cc03e 100644 --- a/easy_my_coop/models/company.py +++ b/easy_my_coop/models/company.py @@ -1,83 +1,93 @@ -# -*- coding: utf-8 -*- -# Copyright 2019 Coop IT Easy SCRL fs -# Houssine Bakkali -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - - -from odoo import api, fields, models - - -class ResCompany(models.Model): - _inherit = 'res.company' - - def _get_base_logo(self): - base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') - self.logo_url = base_url + "/logo.png" - - coop_email_contact = fields.Char(string="Contact email address for the" - " cooperator") - subscription_maximum_amount = fields.Float(string="Maximum authorised" - " subscription amount") - default_country_id = fields.Many2one('res.country', - string="Default country", - default=lambda self: self.country_id) - default_lang_id = fields.Many2one('res.lang', - string="Default lang") - allow_id_card_upload = fields.Boolean(string="Allow ID Card upload") - create_user = fields.Boolean(string="Create user for cooperator", - default=False) - board_representative = fields.Char(string="Board representative name") - signature_scan = fields.Binary(string="Board representative signature") - property_cooperator_account = fields.Many2one('account.account', - company_dependent=True, - string="Cooperator Account", - domain=[('internal_type', '=', 'receivable'), - ('deprecated', '=', False)], - help="This account will be" - " the default one as the" - " receivable account for the" - " cooperators", - required=True) - unmix_share_type = fields.Boolean(string="Unmix share type", - default=True, - help="If checked, A cooperator will be" - " authorised to have only one type" - " of share") - display_logo1 = fields.Boolean(string="Display logo 1") - display_logo2 = fields.Boolean(string="Display logo 2") - bottom_logo1 = fields.Binary(string="Bottom logo 1") - bottom_logo2 = fields.Binary(string="Bottom logo 2") - logo_url = fields.Char(string="logo url", - compute="_get_base_logo") - display_data_policy_approval = fields.Boolean( - help="Choose to display a data policy checkbox on the cooperator" - " website form." - ) - data_policy_approval_required = fields.Boolean( - string="Is data policy approval required?" - ) - data_policy_approval_text = fields.Html( - translate=True, - help="Text to display aside the checkbox to approve data policy." - ) - display_internal_rules_approval = fields.Boolean( - help="Choose to display an internal rules checkbox on the" - " cooperator website form." - ) - internal_rules_approval_required = fields.Boolean( - string="Is internal rules approval required?" - ) - internal_rules_approval_text = fields.Html( - translate=True, - help="Text to display aside the checkbox to approve internal rules." - ) - - @api.onchange('data_policy_approval_required') - def onchange_data_policy_approval_required(self): - if self.data_policy_approval_required: - self.display_data_policy_approval = True - - @api.onchange('internal_rules_approval_required') - def onchange_internal_rules_approval_required(self): - if self.internal_rules_approval_required: - self.display_internal_rules_approval = True +# Copyright 2019 Coop IT Easy SCRL fs +# Houssine Bakkali +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + + +from odoo import api, fields, models + + +class ResCompany(models.Model): + _inherit = "res.company" + + def _compute_base_logo(self): + base_url = ( + self.env["ir.config_parameter"].sudo().get_param("web.base.url") + ) + self.logo_url = base_url + "/logo.png" + + coop_email_contact = fields.Char( + string="Contact email address for the" " cooperator" + ) + subscription_maximum_amount = fields.Float( + string="Maximum authorised" " subscription amount" + ) + default_country_id = fields.Many2one( + "res.country", + string="Default country", + default=lambda self: self.country_id, + ) + default_lang_id = fields.Many2one("res.lang", string="Default lang") + allow_id_card_upload = fields.Boolean(string="Allow ID Card upload") + create_user = fields.Boolean( + string="Create user for cooperator", default=False + ) + board_representative = fields.Char(string="Board representative name") + signature_scan = fields.Binary(string="Board representative signature") + property_cooperator_account = fields.Many2one( + "account.account", + company_dependent=True, + string="Cooperator Account", + domain=[ + ("internal_type", "=", "receivable"), + ("deprecated", "=", False), + ], + help="This account will be" + " the default one as the" + " receivable account for the" + " cooperators", + required=True, + ) + unmix_share_type = fields.Boolean( + string="Unmix share type", + default=True, + help="If checked, A cooperator will be" + " authorised to have only one type" + " of share", + ) + display_logo1 = fields.Boolean(string="Display logo 1") + display_logo2 = fields.Boolean(string="Display logo 2") + bottom_logo1 = fields.Binary(string="Bottom logo 1") + bottom_logo2 = fields.Binary(string="Bottom logo 2") + logo_url = fields.Char(string="logo url", compute="_compute_base_logo") + display_data_policy_approval = fields.Boolean( + help="Choose to display a data policy checkbox on the cooperator" + " website form." + ) + data_policy_approval_required = fields.Boolean( + string="Is data policy approval required?" + ) + data_policy_approval_text = fields.Html( + translate=True, + help="Text to display aside the checkbox to approve data policy.", + ) + display_internal_rules_approval = fields.Boolean( + help="Choose to display an internal rules checkbox on the" + " cooperator website form." + ) + internal_rules_approval_required = fields.Boolean( + string="Is internal rules approval required?" + ) + internal_rules_approval_text = fields.Html( + translate=True, + help="Text to display aside the checkbox to approve internal rules.", + ) + + @api.onchange("data_policy_approval_required") + def onchange_data_policy_approval_required(self): + if self.data_policy_approval_required: + self.display_data_policy_approval = True + + @api.onchange("internal_rules_approval_required") + def onchange_internal_rules_approval_required(self): + if self.internal_rules_approval_required: + self.display_internal_rules_approval = True diff --git a/easy_my_coop/models/coop.py b/easy_my_coop/models/coop.py index 3b7376d..ba370d9 100644 --- a/easy_my_coop/models/coop.py +++ b/easy_my_coop/models/coop.py @@ -1,776 +1,938 @@ -# -*- coding: utf-8 -*- -# Copyright 2019 Coop IT Easy SCRL fs -# Houssine Bakkali -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - - -from datetime import datetime - -from odoo import api, fields, models, _ -from addons.base_iban.models.res_partner_bank import validate_iban -from odoo.exceptions import UserError, ValidationError - -_REQUIRED = ['email', - 'firstname', - 'lastname', - 'birthdate', - 'address', - 'share_product_id', - 'ordered_parts', - 'zip_code', - 'city', - 'iban', - 'gender'] - - -@api.model -def _lang_get(self): - languages = self.env['res.lang'].search([]) - return [(language.code, language.name) for language in languages] - - -# todo move to subscription_request.py -class SubscriptionRequest(models.Model): - _name = 'subscription.request' - _description = 'Subscription Request' - - def get_required_field(self): - required_fields = _REQUIRED - company = self.env['res.company']._company_default_get() - if company.data_policy_approval_required: - required_fields.append('data_policy_approved') - if company.internal_rules_approval_required: - required_fields.append('internal_rules_approved') - return required_fields - - def get_mail_template_notif(self, is_company=False): - if is_company: - mail_template = 'easy_my_coop.email_template_confirmation_company' - else: - mail_template = 'easy_my_coop.email_template_confirmation' - return self.env.ref(mail_template, False) - - def is_member(self, vals, cooperator): - if cooperator.member: - vals['type'] = 'increase' - vals['already_cooperator'] = True - return vals - - @api.model - def create(self, vals): - partner_obj = self.env['res.partner'] - - if not vals.get('partner_id'): - cooperator = False - if vals.get('email'): - cooperator = partner_obj.get_cooperator_from_email( - vals.get('email')) - if cooperator: - # TODO remove the following line once it has - # been found a way to avoid double encoding - cooperator = cooperator[0] - vals['type'] = 'subscription' - vals = self.is_member(vals, cooperator) - vals['partner_id'] = cooperator.id - else: - cooperator_id = vals.get('partner_id') - cooperator = partner_obj.browse(cooperator_id) - vals = self.is_member(vals, cooperator) - - if not cooperator.cooperator: - cooperator.write({'cooperator': True}) - subscr_request = super(SubscriptionRequest, self).create(vals) - - mail_template_notif = subscr_request.get_mail_template_notif(False) - mail_template_notif.send_mail(subscr_request.id) - - return subscr_request - - @api.model - def create_comp_sub_req(self, vals): - vals["name"] = vals['company_name'] - if not vals.get('partner_id'): - cooperator = self.env['res.partner'].get_cooperator_from_crn(vals.get('company_register_number')) - if cooperator: - vals['partner_id'] = cooperator.id - vals['type'] = 'increase' - vals['already_cooperator'] = True - subscr_request = super(SubscriptionRequest, self).create(vals) - - confirmation_mail_template = subscr_request.get_mail_template_notif(True) - confirmation_mail_template.send_mail(subscr_request.id) - - return subscr_request - - def check_empty_string(self, value): - if value is None or value is False or value == '': - return False - return True - - def check_iban(self, iban): - try: - if iban: - validate_iban(iban) - return True - else: - return False - except ValidationError: - return False - - @api.multi - @api.depends('iban', 'skip_control_ng') - def _validated_lines(self): - for sub_request in self: - validated = ( - sub_request.skip_control_ng - or self.check_iban(sub_request.iban) - ) - sub_request.validated = validated - - @api.multi - @api.depends('share_product_id', - 'share_product_id.list_price', - 'ordered_parts') - def _compute_subscription_amount(self): - for sub_request in self: - sub_request.subscription_amount = (sub_request.share_product_id. - list_price * - sub_request.ordered_parts) - - already_cooperator = fields.Boolean(string="I'm already cooperator", - readonly=True, - states={'draft': [('readonly', False)]} - ) - name = fields.Char(string='Name', - required=True, - readonly=True, - states={'draft': [('readonly', False)]}) - firstname = fields.Char(string='Firstname', - readonly=True, - states={'draft': [('readonly', False)]}) - lastname = fields.Char(string='Lastname', - readonly=True, - states={'draft': [('readonly', False)]}) - birthdate = fields.Date(string="Birthdate", - readonly=True, - states={'draft': [('readonly', False)]}) - gender = fields.Selection([('male', _('Male')), - ('female', _('Female')), - ('other', _('Other'))], - string='Gender', - readonly=True, - states={'draft': [('readonly', False)]}) - type = fields.Selection([('new', 'New Cooperator'), - ('subscription', 'Subscription'), - ('increase', 'Increase number of share')], - string='Type', default="new", - readonly=True, - states={'draft': [('readonly', False)]}) - state = fields.Selection([('draft', 'Draft'), - ('block', 'Blocked'), - ('done', 'Done'), - ('waiting', 'Waiting'), - ('transfer', 'Transfer'), - ('cancelled', 'Cancelled'), - ('paid', 'paid')], - string='State', required=True, default="draft") - email = fields.Char(string='Email', - required=True, - readonly=True, - states={'draft': [('readonly', False)]}) - iban = fields.Char(string='Account Number', - readonly=True, - states={'draft': [('readonly', False)]}) - partner_id = fields.Many2one('res.partner', - string='Cooperator', - readonly=True, - states={'draft': [('readonly', False)]}) - share_product_id = fields.Many2one('product.product', - string='Share type', - domain=[('is_share', '=', True)], - required=True, - readonly=True, - states={'draft': [('readonly', False)]}) - share_short_name = fields.Char(related='share_product_id.short_name', - string='Share type name', - readonly=True, - states={'draft': [('readonly', False)]}) - share_unit_price = fields.Float(related='share_product_id.list_price', - string='Share price', - readonly=True, - states={'draft': [('readonly', False)]}) - subscription_amount = fields.Monetary( - compute='_compute_subscription_amount', - string='Subscription amount', - currency_field="company_currency_id", - readonly=True, - states={'draft': [('readonly', False)]}, - ) - ordered_parts = fields.Integer(string='Number of Share', - required=True, - readonly=True, - default=1, - states={'draft': [('readonly', False)]}) - address = fields.Char(string='Address', - required=True, - readonly=True, - states={'draft': [('readonly', False)]}) - city = fields.Char(string='City', - required=True, - readonly=True, - states={'draft': [('readonly', False)]}) - zip_code = fields.Char(string='Zip Code', - required=True, - readonly=True, - states={'draft': [('readonly', False)]}) - country_id = fields.Many2one('res.country', - string='Country', - ondelete='restrict', - required=True, - readonly=True, - states={'draft': [('readonly', False)]}) - phone = fields.Char(string='Phone', - readonly=True, - states={'draft': [('readonly', False)]}) - user_id = fields.Many2one('res.users', - string='Responsible', - readonly=True) - # todo rename to valid_subscription_request - validated = fields.Boolean(compute='_validated_lines', - string='Valid Subscription request?', - readonly=True) - skip_control_ng = fields.Boolean(string="Skip control", - help="if this field is checked then no" - " control will be done on the national" - " register number and on the iban bank" - " account. To be done in case of the id" - " card is from abroad or in case of" - " a passport") - lang = fields.Selection(_lang_get, - string='Language', - required=True, - readonly=True, - states={'draft': [('readonly', False)]}, - default=lambda self: self.env['res.company']._company_default_get().default_lang_id.code) - date = fields.Date(string='Subscription date request', - required=True, - readonly=True, - states={'draft': [('readonly', False)]}, - default=lambda self: datetime.strftime(datetime.now(), - '%Y-%m-%d')) - company_id = fields.Many2one('res.company', - string='Company', - required=True, - change_default=True, - readonly=True, - default=lambda self: self.env['res.company']._company_default_get()) - company_currency_id = fields.Many2one( - "res.currency", - related="company_id.currency_id", - string="Company Currency", - readonly=True, - ) - is_company = fields.Boolean(string='Is a company', - readonly=True, - states={'draft': [('readonly', False)]}) - is_operation = fields.Boolean(string='Is an operation', - readonly=True, - states={'draft': [('readonly', False)]}) - company_name = fields.Char(string="Company name", - readonly=True, - states={'draft': [('readonly', False)]}) - company_email = fields.Char(string="Company email", - readonly=True, - states={'draft': [('readonly', False)]}) - company_register_number = fields.Char(string='Company register number', - readonly=True, - states={'draft': [('readonly', False)]}) - company_type = fields.Selection([('', '')], - string="Company type", - readonly=True, - states={'draft': [('readonly', False)]}) - same_address = fields.Boolean(string='Same address', - readonly=True, - states={'draft': [('readonly', False)]}) - activities_address = fields.Char(string='Activities address', - readonly=True, - states={'draft': [('readonly', False)]}) - activities_city = fields.Char(string='Activities city', - readonly=True, - states={'draft': [('readonly', False)]}) - activities_zip_code = fields.Char(string='Activities zip Code', - readonly=True, - states={'draft': [('readonly', False)]}) - activities_country_id = fields.Many2one('res.country', - string='Activities country', - ondelete='restrict', - readonly=True, - states={'draft': [('readonly', False)]}) - contact_person_function = fields.Char(string='Function', - readonly=True, - states={'draft': [('readonly', False)]}) - operation_request_id = fields.Many2one('operation.request', - string="Operation Request", - readonly=True, - states={'draft': [('readonly', False)]}) - capital_release_request = fields.One2many('account.invoice', - 'subscription_request', - string='Capital release request', - readonly=True, - states={'draft': [('readonly', False)]}) - capital_release_request_date = fields.Date(string="Force the capital " - "release request date", - help="Keep empty to use the " - "current date", - copy=False, - readonly=True, - states={'draft': [('readonly', False)]}) - source = fields.Selection([('website', 'Website'), - ('crm', 'CRM'), - ('manual', 'Manual'), - ('operation', 'Operation')], - string="Source", - default="website", - readonly=True, - states={'draft': [('readonly', False)]}) - data_policy_approved = fields.Boolean( - string='Data Policy Approved', - default=False, - ) - internal_rules_approved = fields.Boolean( - string='Approved Internal Rules', - default=False, - ) - _order = "id desc" - - def get_person_info(self, partner): - self.firstname = partner.firstname - self.name = partner.name - self.lastname = partner.lastname - self.email = partner.email - self.birthdate = partner.birthdate_date - self.gender = partner.gender - self.address = partner.street - self.city = partner.city - self.zip_code = partner.zip - self.country_id = partner.country_id - self.phone = partner.phone - self.lang = partner.lang - - @api.onchange('partner_id') - def onchange_partner(self): - partner = self.partner_id - if partner: - self.is_company = partner.is_company - self.already_cooperator = partner.member - if partner.bank_ids: - self.iban = partner.bank_ids[0].acc_number - if partner.member: - self.type = 'increase' - if partner.is_company: - self.company_name = partner.name - self.company_email = partner.email - self.company_register_number = partner.company_register_number - representative = partner.get_representative() - self.get_person_info(representative) - self.contact_person_function = representative.function - else: - self.get_person_info(partner) - - # declare this function in order to be overriden - def get_eater_vals(self, partner, share_product_id): #noqa - return {} - - def _prepare_invoice_line(self, product, partner, qty): - self.ensure_one() - account = product.property_account_income_id \ - or product.categ_id.property_account_income_categ_id - if not account: - raise UserError(_('Please define income account for this product:' - ' "%s" (id:%d) - or for its category: "%s".') % - (product.name, product.id, product.categ_id.name)) - - fpos = partner.property_account_position_id - if fpos: - account = fpos.map_account(account) - - res = { - 'name': product.name, - 'account_id': account.id, - 'price_unit': product.lst_price, - 'quantity': qty, - 'uom_id': product.uom_id.id, - 'product_id': product.id or False, - } - return res - - def get_capital_release_mail_template(self): - template = 'easy_my_coop.email_template_release_capital' - return self.env.ref(template, False) - - def send_capital_release_request(self, invoice): - email_template = self.get_capital_release_mail_template() - - # we send the email with the capital release request in attachment - # TODO remove sudo() and give necessary access right - email_template.sudo().send_mail(invoice.id, True) - invoice.sent = True - - def get_journal(self): - return self.env['account.journal'].search([('code', '=', 'SUBJ')])[0] - - def get_accounting_account(self): - account_obj = self.env['account.account'] - if self.company_id.property_cooperator_account: - account = self.company_id.property_cooperator_account - else: - accounts = account_obj.search([('code', '=', '416000')]) - if accounts: - account = accounts[0] - else: - raise UserError(_( - 'You must set a cooperator account on you company.' - )) - return account - - def get_invoice_vals(self, partner): - return { - 'partner_id': partner.id, - 'journal_id': self.get_journal().id, - 'account_id': self.get_accounting_account().id, - 'type': 'out_invoice', - 'release_capital_request': True, - 'subscription_request': self.id - } - - def create_invoice(self, partner): - # creating invoice and invoice lines - invoice_vals = self.get_invoice_vals(partner) - if self.capital_release_request_date: - invoice_vals['date_invoice'] = self.capital_release_request_date - invoice = self.env['account.invoice'].create(invoice_vals) - vals = self._prepare_invoice_line(self.share_product_id, partner, - self.ordered_parts) - vals['invoice_id'] = invoice.id - self.env['account.invoice.line'].create(vals) - - # validate the capital release request - invoice.action_invoice_open() - - self.send_capital_release_request(invoice) - - return invoice - - def get_partner_company_vals(self): - partner_vals = {'name': self.company_name, - 'last_name': self.company_name, - 'is_company': self.is_company, - 'company_register_number': self.company_register_number, # noqa - 'customer': False, 'cooperator': True, - 'street': self.address, 'zip': self.zip_code, - 'city': self.city, 'email': self.company_email, - 'out_inv_comm_type': 'bba', - 'customer': self.share_product_id.customer, - 'country_id': self.country_id.id, - 'lang': self.lang, - 'data_policy_approved': self.data_policy_approved, - 'internal_rules_approved': self.internal_rules_approved - } - return partner_vals - - def get_partner_vals(self): - partner_vals = {'name': self.name, 'firstname': self.firstname, - 'lastname': self.lastname, 'street': self.address, - 'zip': self.zip_code, 'email': self.email, - 'gender': self.gender, 'cooperator': True, - 'city': self.city, 'phone': self.phone, - 'country_id': self.country_id.id, 'lang': self.lang, - 'birthdate_date': self.birthdate, - 'customer': self.share_product_id.customer, - 'data_policy_approved': self.data_policy_approved, - 'internal_rules_approved': self.internal_rules_approved - } - return partner_vals - - def get_representative_vals(self): - contact_vals = { - 'name': self.name, - 'firstname': self.firstname, - 'lastname': self.lastname, 'customer': False, - 'is_company': False, 'cooperator': True, - 'street': self.address, 'gender': self.gender, - 'zip': self.zip_code, 'city': self.city, - 'phone': self.phone, 'email': self.email, - 'country_id': self.country_id.id, - 'out_inv_comm_type': 'bba', - 'out_inv_comm_algorithm': 'random', - 'lang': self.lang, - 'birthdate_date': self.birthdate, - 'parent_id': self.partner_id.id, - 'representative': True, - 'function': self.contact_person_function, - 'type': 'representative', - 'data_policy_approved': self.data_policy_approved, - 'internal_rules_approved': self.internal_rules_approved - } - return contact_vals - - def create_coop_partner(self): - partner_obj = self.env['res.partner'] - - if self.is_company: - partner_vals = self.get_partner_company_vals() - else: - partner_vals = self.get_partner_vals() - - partner = partner_obj.create(partner_vals) - if self.iban: - self.env['res.partner.bank'].create({ - 'partner_id': partner.id, - 'acc_number': self.iban - }) - return partner - - def set_membership(self): - # To be overridden - return True - - @api.one # todo remove api.one - def validate_subscription_request(self): - # todo rename to validate (careful with iwp dependencies) - partner_obj = self.env['res.partner'] - - if self.ordered_parts <= 0: - raise UserError(_('Number of share must be greater than 0.')) - if self.partner_id: - partner = self.partner_id - else: - partner = None - domain = [] - if self.already_cooperator: - raise UserError(_('The checkbox already cooperator is' - ' checked please select a cooperator.')) - elif self.is_company and self.company_register_number: - domain = [('company_register_number', '=', self.company_register_number)] # noqa - elif not self.is_company and self.email: - domain = [('email', '=', self.email)] - - if domain: - partner = partner_obj.search(domain) - - if not partner: - partner = self.create_coop_partner() - self.partner_id = partner - else: - partner = partner[0] - - partner.cooperator = True - - if self.is_company and not partner.has_representative(): - contact = False - if self.email: - domain = [('email', '=', self.email)] - contact = partner_obj.search(domain) - if contact: - contact.type = 'representative' - if not contact: - contact_vals = self.get_representative_vals() - partner_obj.create(contact_vals) - else: - if len(contact) > 1: - raise UserError(_('There is two different persons with the' - ' same national register number. Please' - ' proceed to a merge before to continue') - ) - if contact.parent_id and contact.parent_id.id != partner.id: - raise UserError(_('This contact person is already defined' - ' for another company. Please select' - ' another contact')) - else: - contact.write({'parent_id': partner.id, - 'representative': True}) - - invoice = self.create_invoice(partner) - self.write({'state': 'done'}) - self.set_membership() - - return invoice - - @api.one # todo remove api.one - def block_subscription_request(self): - self.write({'state': 'block'}) - - @api.one # todo remove api.one - def unblock_subscription_request(self): - self.write({'state': 'draft'}) - - @api.one # todo remove api.one - def cancel_subscription_request(self): - self.write({'state': 'cancelled'}) - - @api.one # todo remove api.one - def put_on_waiting_list(self): - waiting_list_mail_template = self.env.ref('easy_my_coop.email_template_waiting_list', False) - waiting_list_mail_template.send_mail(self.id, True) - self.write({'state': 'waiting'}) - - -# todo move to share_line.py -class ShareLine(models.Model): - _name = 'share.line' - _description = "Share line" - - @api.multi - def _compute_total_line(self): - res = {} - for line in self: - line.total_amount_line = line.share_unit_price * line.share_number - return res - - share_product_id = fields.Many2one('product.product', - string='Share type', - required=True, - readonly=True) - share_number = fields.Integer(string='Number of Share', - required=True, - readonly=True) - share_short_name = fields.Char(related='share_product_id.short_name', - string='Share type name', - readonly=True) - share_unit_price = fields.Monetary( - string='Share price', - currency_field="company_currency_id", - readonly=True, - ) - effective_date = fields.Date(string='Effective Date', - readonly=True) - partner_id = fields.Many2one('res.partner', - string='Cooperator', - required=True, - ondelete='cascade', - readonly=True) - total_amount_line = fields.Monetary( - string='Total amount line', - currency_field="company_currency_id", - compute='_compute_total_line', - ) - company_id = fields.Many2one( - "res.company", - string='Company', - required=True, - change_default=True, readonly=True, - default=lambda self: self.env['res.company']._company_default_get(), - ) - company_currency_id = fields.Many2one( - "res.currency", - string="Company Currency", - related="company_id.currency_id", - readonly=True, - ) - - -# todo move to subscription_register.py -class SubscriptionRegister(models.Model): - _name = 'subscription.register' - _description = "Subscription register" - - @api.multi - def _compute_total_line(self): - for line in self: - line.total_amount_line = line.share_unit_price * line.quantity - - name = fields.Char(string='Number Operation', - required=True, - readonly=True) - register_number_operation = fields.Integer(string='Register Number Operation', - required=True, - readonly=True) - partner_id = fields.Many2one('res.partner', - string='Cooperator', - required=True, - readonly=True) - partner_id_to = fields.Many2one('res.partner', - string='Transfered to', - readonly=True) - date = fields.Date(string='Subscription Date', - required=True, - readonly=True) - quantity = fields.Integer(string='Number of share', - readonly=True) - share_unit_price = fields.Monetary( - string='Share price', - currency_field="company_currency_id", - readonly=True, - ) - total_amount_line = fields.Monetary( - string='Total amount line', - currency_field="company_currency_id", - compute='_compute_total_line', - ) - share_product_id = fields.Many2one('product.product', - string='Share type', - required=True, - readonly=True, - domain=[('is_share', '=', True)]) - share_short_name = fields.Char(related='share_product_id.short_name', - string='Share type name', - readonly=True) - share_to_product_id = fields.Many2one('product.product', - string='Share to type', - readonly=True, - domain=[('is_share', '=', True)]) - share_to_short_name = fields.Char(related='share_to_product_id.short_name', - string='Share to type name', - readonly=True) - quantity_to = fields.Integer(string='Number of share to', - readonly=True) - share_to_unit_price = fields.Monetary( - string='Share to price', - currency_field="company_currency_id", - readonly=True, - ) - type = fields.Selection([('subscription', 'Subscription'), - ('transfer', 'Transfer'), - ('sell_back', 'Sell Back'), - ('convert', 'Conversion')], - string='Operation Type', readonly=True) - company_id = fields.Many2one('res.company', string='Company', - required=True, - change_default=True, readonly=True, - default=lambda self: self.env['res.company']._company_default_get()) - company_currency_id = fields.Many2one( - "res.currency", - related="company_id.currency_id", - string="Company Currency", - readonly=True, - ) - user_id = fields.Many2one('res.users', - string='Responsible', - readonly=True, - default=lambda self: self.env.user) - - _order = "register_number_operation asc" - - @api.model - def read_group(self, domain, fields, groupby, offset=0, limit=None, - orderby=False, - lazy=True): - if 'share_unit_price' in fields: - fields.remove('share_unit_price') - if 'register_number_operation' in fields: - fields.remove('register_number_operation') - res = super(SubscriptionRegister, self).read_group(domain, fields, - groupby, - offset=offset, - limit=limit, - orderby=orderby, - lazy=lazy) - if 'total_amount_line' in fields: - for line in res: - if '__domain' in line: - lines = self.search(line['__domain']) - inv_value = 0.0 - for line2 in lines: - inv_value += line2.total_amount_line - line['total_amount_line'] = inv_value - return res +# Copyright 2019 Coop IT Easy SCRL fs +# Houssine Bakkali +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + + +from datetime import datetime + +# pylint: disable=missing-manifest-dependency +from addons.base_iban.models.res_partner_bank import validate_iban + +from odoo import _, api, fields, models +from odoo.exceptions import UserError, ValidationError + +_REQUIRED = [ + "email", + "firstname", + "lastname", + "birthdate", + "address", + "share_product_id", + "ordered_parts", + "zip_code", + "city", + "iban", + "gender", +] + + +@api.model +def _lang_get(self): + languages = self.env["res.lang"].search([]) + return [(language.code, language.name) for language in languages] + + +# todo move to subscription_request.py +class SubscriptionRequest(models.Model): + _name = "subscription.request" + _description = "Subscription Request" + + def get_required_field(self): + required_fields = _REQUIRED + company = self.env["res.company"]._company_default_get() + if company.data_policy_approval_required: + required_fields.append("data_policy_approved") + if company.internal_rules_approval_required: + required_fields.append("internal_rules_approved") + return required_fields + + def get_mail_template_notif(self, is_company=False): + if is_company: + mail_template = "easy_my_coop.email_template_confirmation_company" + else: + mail_template = "easy_my_coop.email_template_confirmation" + return self.env.ref(mail_template, False) + + def is_member(self, vals, cooperator): + if cooperator.member: + vals["type"] = "increase" + vals["already_cooperator"] = True + return vals + + @api.model + def create(self, vals): + partner_obj = self.env["res.partner"] + + if not vals.get("partner_id"): + cooperator = False + if vals.get("email"): + cooperator = partner_obj.get_cooperator_from_email( + vals.get("email") + ) + if cooperator: + # TODO remove the following line once it has + # been found a way to avoid double encoding + cooperator = cooperator[0] + vals["type"] = "subscription" + vals = self.is_member(vals, cooperator) + vals["partner_id"] = cooperator.id + else: + cooperator_id = vals.get("partner_id") + cooperator = partner_obj.browse(cooperator_id) + vals = self.is_member(vals, cooperator) + + if not cooperator.cooperator: + cooperator.write({"cooperator": True}) + subscr_request = super(SubscriptionRequest, self).create(vals) + + mail_template_notif = subscr_request.get_mail_template_notif(False) + mail_template_notif.send_mail(subscr_request.id) + + return subscr_request + + @api.model + def create_comp_sub_req(self, vals): + vals["name"] = vals["company_name"] + if not vals.get("partner_id"): + cooperator = self.env["res.partner"].get_cooperator_from_crn( + vals.get("company_register_number") + ) + if cooperator: + vals["partner_id"] = cooperator.id + vals["type"] = "increase" + vals["already_cooperator"] = True + subscr_request = super(SubscriptionRequest, self).create(vals) + + confirmation_mail_template = subscr_request.get_mail_template_notif( + True + ) + confirmation_mail_template.send_mail(subscr_request.id) + + return subscr_request + + def check_empty_string(self, value): + if value is None or value is False or value == "": + return False + return True + + def check_iban(self, iban): + try: + if iban: + validate_iban(iban) + return True + else: + return False + except ValidationError: + return False + + @api.multi + @api.depends("iban", "skip_control_ng") + def _compute_validated_lines(self): + for sub_request in self: + validated = sub_request.skip_control_ng or self.check_iban( + sub_request.iban + ) + sub_request.validated = validated + + @api.multi + @api.depends( + "share_product_id", "share_product_id.list_price", "ordered_parts" + ) + def _compute_subscription_amount(self): + for sub_request in self: + sub_request.subscription_amount = ( + sub_request.share_product_id.list_price + * sub_request.ordered_parts + ) + + already_cooperator = fields.Boolean( + string="I'm already cooperator", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + name = fields.Char( + string="Name", + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, + ) + firstname = fields.Char( + string="Firstname", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + lastname = fields.Char( + string="Lastname", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + birthdate = fields.Date( + string="Birthdate", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + gender = fields.Selection( + [("male", _("Male")), ("female", _("Female")), ("other", _("Other"))], + string="Gender", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + type = fields.Selection( + [ + ("new", "New Cooperator"), + ("subscription", "Subscription"), + ("increase", "Increase number of share"), + ], + string="Type", + default="new", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + state = fields.Selection( + [ + ("draft", "Draft"), + ("block", "Blocked"), + ("done", "Done"), + ("waiting", "Waiting"), + ("transfer", "Transfer"), + ("cancelled", "Cancelled"), + ("paid", "paid"), + ], + string="State", + required=True, + default="draft", + ) + email = fields.Char( + string="Email", + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, + ) + iban = fields.Char( + string="Account Number", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + partner_id = fields.Many2one( + "res.partner", + string="Cooperator", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + share_product_id = fields.Many2one( + "product.product", + string="Share type", + domain=[("is_share", "=", True)], + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, + ) + share_short_name = fields.Char( + related="share_product_id.short_name", + string="Share type name", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + share_unit_price = fields.Float( + related="share_product_id.list_price", + string="Share price", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + subscription_amount = fields.Monetary( + compute="_compute_subscription_amount", + string="Subscription amount", + currency_field="company_currency_id", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + ordered_parts = fields.Integer( + string="Number of Share", + required=True, + readonly=True, + default=1, + states={"draft": [("readonly", False)]}, + ) + address = fields.Char( + string="Address", + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, + ) + city = fields.Char( + string="City", + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, + ) + zip_code = fields.Char( + string="Zip Code", + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, + ) + country_id = fields.Many2one( + "res.country", + string="Country", + ondelete="restrict", + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, + ) + phone = fields.Char( + string="Phone", readonly=True, states={"draft": [("readonly", False)]} + ) + user_id = fields.Many2one("res.users", string="Responsible", readonly=True) + # todo rename to valid_subscription_request + validated = fields.Boolean( + compute="_compute_validated_lines", + string="Valid Subscription request?", + readonly=True, + ) + skip_control_ng = fields.Boolean( + string="Skip control", + help="if this field is checked then no" + " control will be done on the national" + " register number and on the iban bank" + " account. To be done in case of the id" + " card is from abroad or in case of" + " a passport", + ) + lang = fields.Selection( + _lang_get, + string="Language", + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, + default=lambda self: self.env["res.company"] + ._company_default_get() + .default_lang_id.code, + ) + date = fields.Date( + string="Subscription date request", + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, + default=lambda self: datetime.strftime(datetime.now(), "%Y-%m-%d"), + ) + company_id = fields.Many2one( + "res.company", + string="Company", + required=True, + change_default=True, + readonly=True, + default=lambda self: self.env["res.company"]._company_default_get(), + ) + company_currency_id = fields.Many2one( + "res.currency", + related="company_id.currency_id", + string="Company Currency", + readonly=True, + ) + is_company = fields.Boolean( + string="Is a company", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + is_operation = fields.Boolean( + string="Is an operation", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + company_name = fields.Char( + string="Company name", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + company_email = fields.Char( + string="Company email", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + company_register_number = fields.Char( + string="Company register number", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + company_type = fields.Selection( + [("", "")], + string="Company type", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + same_address = fields.Boolean( + string="Same address", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + activities_address = fields.Char( + string="Activities address", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + activities_city = fields.Char( + string="Activities city", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + activities_zip_code = fields.Char( + string="Activities zip Code", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + activities_country_id = fields.Many2one( + "res.country", + string="Activities country", + ondelete="restrict", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + contact_person_function = fields.Char( + string="Function", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + operation_request_id = fields.Many2one( + "operation.request", + string="Operation Request", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + capital_release_request = fields.One2many( + "account.invoice", + "subscription_request", + string="Capital release request", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + capital_release_request_date = fields.Date( + string="Force the capital " "release request date", + help="Keep empty to use the " "current date", + copy=False, + readonly=True, + states={"draft": [("readonly", False)]}, + ) + source = fields.Selection( + [ + ("website", "Website"), + ("crm", "CRM"), + ("manual", "Manual"), + ("operation", "Operation"), + ], + string="Source", + default="website", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + data_policy_approved = fields.Boolean( + string="Data Policy Approved", default=False + ) + internal_rules_approved = fields.Boolean( + string="Approved Internal Rules", default=False + ) + _order = "id desc" + + def get_person_info(self, partner): + self.firstname = partner.firstname + self.name = partner.name + self.lastname = partner.lastname + self.email = partner.email + self.birthdate = partner.birthdate_date + self.gender = partner.gender + self.address = partner.street + self.city = partner.city + self.zip_code = partner.zip + self.country_id = partner.country_id + self.phone = partner.phone + self.lang = partner.lang + + @api.onchange("partner_id") + def onchange_partner(self): + partner = self.partner_id + if partner: + self.is_company = partner.is_company + self.already_cooperator = partner.member + if partner.bank_ids: + self.iban = partner.bank_ids[0].acc_number + if partner.member: + self.type = "increase" + if partner.is_company: + self.company_name = partner.name + self.company_email = partner.email + self.company_register_number = partner.company_register_number + representative = partner.get_representative() + self.get_person_info(representative) + self.contact_person_function = representative.function + else: + self.get_person_info(partner) + + # declare this function in order to be overriden + def get_eater_vals(self, partner, share_product_id): # noqa + return {} + + def _prepare_invoice_line(self, product, partner, qty): + self.ensure_one() + account = ( + product.property_account_income_id + or product.categ_id.property_account_income_categ_id + ) + if not account: + raise UserError( + _( + "Please define income account for this product:" + ' "%s" (id:%d) - or for its category: "%s".' + ) + % (product.name, product.id, product.categ_id.name) + ) + + fpos = partner.property_account_position_id + if fpos: + account = fpos.map_account(account) + + res = { + "name": product.name, + "account_id": account.id, + "price_unit": product.lst_price, + "quantity": qty, + "uom_id": product.uom_id.id, + "product_id": product.id or False, + } + return res + + def get_capital_release_mail_template(self): + template = "easy_my_coop.email_template_release_capital" + return self.env.ref(template, False) + + def send_capital_release_request(self, invoice): + email_template = self.get_capital_release_mail_template() + + # we send the email with the capital release request in attachment + # TODO remove sudo() and give necessary access right + email_template.sudo().send_mail(invoice.id, True) + invoice.sent = True + + def get_journal(self): + return self.env["account.journal"].search([("code", "=", "SUBJ")])[0] + + def get_accounting_account(self): + account_obj = self.env["account.account"] + if self.company_id.property_cooperator_account: + account = self.company_id.property_cooperator_account + else: + accounts = account_obj.search([("code", "=", "416000")]) + if accounts: + account = accounts[0] + else: + raise UserError( + _("You must set a cooperator account on you company.") + ) + return account + + def get_invoice_vals(self, partner): + return { + "partner_id": partner.id, + "journal_id": self.get_journal().id, + "account_id": self.get_accounting_account().id, + "type": "out_invoice", + "release_capital_request": True, + "subscription_request": self.id, + } + + def create_invoice(self, partner): + # creating invoice and invoice lines + invoice_vals = self.get_invoice_vals(partner) + if self.capital_release_request_date: + invoice_vals["date_invoice"] = self.capital_release_request_date + invoice = self.env["account.invoice"].create(invoice_vals) + vals = self._prepare_invoice_line( + self.share_product_id, partner, self.ordered_parts + ) + vals["invoice_id"] = invoice.id + self.env["account.invoice.line"].create(vals) + + # validate the capital release request + invoice.action_invoice_open() + + self.send_capital_release_request(invoice) + + return invoice + + def get_partner_company_vals(self): + partner_vals = { + "name": self.company_name, + "last_name": self.company_name, + "is_company": self.is_company, + "company_register_number": self.company_register_number, # noqa + "cooperator": True, + "street": self.address, + "zip": self.zip_code, + "city": self.city, + "email": self.company_email, + "out_inv_comm_type": "bba", + "customer": self.share_product_id.customer, + "country_id": self.country_id.id, + "lang": self.lang, + "data_policy_approved": self.data_policy_approved, + "internal_rules_approved": self.internal_rules_approved, + } + return partner_vals + + def get_partner_vals(self): + partner_vals = { + "name": self.name, + "firstname": self.firstname, + "lastname": self.lastname, + "street": self.address, + "zip": self.zip_code, + "email": self.email, + "gender": self.gender, + "cooperator": True, + "city": self.city, + "phone": self.phone, + "country_id": self.country_id.id, + "lang": self.lang, + "birthdate_date": self.birthdate, + "customer": self.share_product_id.customer, + "data_policy_approved": self.data_policy_approved, + "internal_rules_approved": self.internal_rules_approved, + } + return partner_vals + + def get_representative_vals(self): + contact_vals = { + "name": self.name, + "firstname": self.firstname, + "lastname": self.lastname, + "customer": False, + "is_company": False, + "cooperator": True, + "street": self.address, + "gender": self.gender, + "zip": self.zip_code, + "city": self.city, + "phone": self.phone, + "email": self.email, + "country_id": self.country_id.id, + "out_inv_comm_type": "bba", + "out_inv_comm_algorithm": "random", + "lang": self.lang, + "birthdate_date": self.birthdate, + "parent_id": self.partner_id.id, + "representative": True, + "function": self.contact_person_function, + "type": "representative", + "data_policy_approved": self.data_policy_approved, + "internal_rules_approved": self.internal_rules_approved, + } + return contact_vals + + def create_coop_partner(self): + partner_obj = self.env["res.partner"] + + if self.is_company: + partner_vals = self.get_partner_company_vals() + else: + partner_vals = self.get_partner_vals() + + partner = partner_obj.create(partner_vals) + if self.iban: + self.env["res.partner.bank"].create( + {"partner_id": partner.id, "acc_number": self.iban} + ) + return partner + + def set_membership(self): + # To be overridden + return True + + @api.multi + def validate_subscription_request(self): + self.ensure_one() + # todo rename to validate (careful with iwp dependencies) + partner_obj = self.env["res.partner"] + + if self.ordered_parts <= 0: + raise UserError(_("Number of share must be greater than 0.")) + if self.partner_id: + partner = self.partner_id + else: + partner = None + domain = [] + if self.already_cooperator: + raise UserError( + _( + "The checkbox already cooperator is" + " checked please select a cooperator." + ) + ) + elif self.is_company and self.company_register_number: + domain = [ + ( + "company_register_number", + "=", + self.company_register_number, + ) + ] # noqa + elif not self.is_company and self.email: + domain = [("email", "=", self.email)] + + if domain: + partner = partner_obj.search(domain) + + if not partner: + partner = self.create_coop_partner() + self.partner_id = partner + else: + partner = partner[0] + + partner.cooperator = True + + if self.is_company and not partner.has_representative(): + contact = False + if self.email: + domain = [("email", "=", self.email)] + contact = partner_obj.search(domain) + if contact: + contact.type = "representative" + if not contact: + contact_vals = self.get_representative_vals() + partner_obj.create(contact_vals) + else: + if len(contact) > 1: + raise UserError( + _( + "There is two different persons with the" + " same national register number. Please" + " proceed to a merge before to continue" + ) + ) + if contact.parent_id and contact.parent_id.id != partner.id: + raise UserError( + _( + "This contact person is already defined" + " for another company. Please select" + " another contact" + ) + ) + else: + contact.write( + {"parent_id": partner.id, "representative": True} + ) + + invoice = self.create_invoice(partner) + self.write({"state": "done"}) + self.set_membership() + + return invoice + + @api.multi + def block_subscription_request(self): + self.ensure_one() + self.write({"state": "block"}) + + @api.multi + def unblock_subscription_request(self): + self.ensure_one() + self.write({"state": "draft"}) + + @api.multi + def cancel_subscription_request(self): + self.ensure_one() + self.write({"state": "cancelled"}) + + @api.multi + def put_on_waiting_list(self): + self.ensure_one() + waiting_list_mail_template = self.env.ref( + "easy_my_coop.email_template_waiting_list", False + ) + waiting_list_mail_template.send_mail(self.id, True) + self.write({"state": "waiting"}) + + +# todo move to share_line.py +class ShareLine(models.Model): + _name = "share.line" + _description = "Share line" + + @api.multi + def _compute_total_line(self): + res = {} + for line in self: + line.total_amount_line = line.share_unit_price * line.share_number + return res + + share_product_id = fields.Many2one( + "product.product", string="Share type", required=True, readonly=True + ) + share_number = fields.Integer( + string="Number of Share", required=True, readonly=True + ) + share_short_name = fields.Char( + related="share_product_id.short_name", + string="Share type name", + readonly=True, + ) + share_unit_price = fields.Monetary( + string="Share price", + currency_field="company_currency_id", + readonly=True, + ) + effective_date = fields.Date(string="Effective Date", readonly=True) + partner_id = fields.Many2one( + "res.partner", + string="Cooperator", + required=True, + ondelete="cascade", + readonly=True, + ) + total_amount_line = fields.Monetary( + string="Total amount line", + currency_field="company_currency_id", + compute="_compute_total_line", + ) + company_id = fields.Many2one( + "res.company", + string="Company", + required=True, + change_default=True, + readonly=True, + default=lambda self: self.env["res.company"]._company_default_get(), + ) + company_currency_id = fields.Many2one( + "res.currency", + string="Company Currency", + related="company_id.currency_id", + readonly=True, + ) + + +# todo move to subscription_register.py +class SubscriptionRegister(models.Model): + _name = "subscription.register" + _description = "Subscription register" + + @api.multi + def _compute_total_line(self): + for line in self: + line.total_amount_line = line.share_unit_price * line.quantity + + name = fields.Char(string="Number Operation", required=True, readonly=True) + register_number_operation = fields.Integer( + string="Register Number Operation", required=True, readonly=True + ) + partner_id = fields.Many2one( + "res.partner", string="Cooperator", required=True, readonly=True + ) + partner_id_to = fields.Many2one( + "res.partner", string="Transfered to", readonly=True + ) + date = fields.Date( + string="Subscription Date", required=True, readonly=True + ) + quantity = fields.Integer(string="Number of share", readonly=True) + share_unit_price = fields.Monetary( + string="Share price", + currency_field="company_currency_id", + readonly=True, + ) + total_amount_line = fields.Monetary( + string="Total amount line", + currency_field="company_currency_id", + compute="_compute_total_line", + ) + share_product_id = fields.Many2one( + "product.product", + string="Share type", + required=True, + readonly=True, + domain=[("is_share", "=", True)], + ) + share_short_name = fields.Char( + related="share_product_id.short_name", + string="Share type name", + readonly=True, + ) + share_to_product_id = fields.Many2one( + "product.product", + string="Share to type", + readonly=True, + domain=[("is_share", "=", True)], + ) + share_to_short_name = fields.Char( + related="share_to_product_id.short_name", + string="Share to type name", + readonly=True, + ) + quantity_to = fields.Integer(string="Number of share to", readonly=True) + share_to_unit_price = fields.Monetary( + string="Share to price", + currency_field="company_currency_id", + readonly=True, + ) + type = fields.Selection( + [ + ("subscription", "Subscription"), + ("transfer", "Transfer"), + ("sell_back", "Sell Back"), + ("convert", "Conversion"), + ], + string="Operation Type", + readonly=True, + ) + company_id = fields.Many2one( + "res.company", + string="Company", + required=True, + change_default=True, + readonly=True, + default=lambda self: self.env["res.company"]._company_default_get(), + ) + company_currency_id = fields.Many2one( + "res.currency", + related="company_id.currency_id", + string="Company Currency", + readonly=True, + ) + user_id = fields.Many2one( + "res.users", + string="Responsible", + readonly=True, + default=lambda self: self.env.user, + ) + + _order = "register_number_operation asc" + + @api.model + def read_group( + self, + domain, + fields, + groupby, + offset=0, + limit=None, + orderby=False, + lazy=True, + ): + if "share_unit_price" in fields: + fields.remove("share_unit_price") + if "register_number_operation" in fields: + fields.remove("register_number_operation") + res = super(SubscriptionRegister, self).read_group( + domain, + fields, + groupby, + offset=offset, + limit=limit, + orderby=orderby, + lazy=lazy, + ) + if "total_amount_line" in fields: + for line in res: + if "__domain" in line: + lines = self.search(line["__domain"]) + inv_value = 0.0 + for line2 in lines: + inv_value += line2.total_amount_line + line["total_amount_line"] = inv_value + return res diff --git a/easy_my_coop/models/mail_template.py b/easy_my_coop/models/mail_template.py index b78c7f7..c120428 100644 --- a/easy_my_coop/models/mail_template.py +++ b/easy_my_coop/models/mail_template.py @@ -1,4 +1,4 @@ -from odoo import models, fields +from odoo import fields, models class MailTemplate(models.Model): diff --git a/easy_my_coop/models/operation_request.py b/easy_my_coop/models/operation_request.py index b31dabe..d608128 100644 --- a/easy_my_coop/models/operation_request.py +++ b/easy_my_coop/models/operation_request.py @@ -1,335 +1,436 @@ -# -*- coding: utf-8 -*- -# Copyright 2019 Coop IT Easy SCRL fs -# Houssine Bakkali -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - - -from datetime import datetime - -from odoo import api, fields, models, _ -from odoo.exceptions import ValidationError - - -class operation_request(models.Model): - _name = 'operation.request' - _description = "Operation request" - - def get_date_now(self): - # fixme odoo 12 uses date types - return datetime.strftime(datetime.now(), '%Y-%m-%d') - - @api.multi - @api.depends('share_product_id', 'share_product_id.list_price', 'quantity') - def _compute_subscription_amount(self): - for operation_request in self: - operation_request.subscription_amount = (operation_request. - share_product_id. - list_price * - operation_request. - quantity) - - request_date = fields.Date(string='Request date', - default=lambda self: self.get_date_now()) - partner_id = fields.Many2one('res.partner', - string='Cooperator', - domain=[('member', '=', True)], - required=True) - partner_id_to = fields.Many2one('res.partner', - string='Transfered to', - domain=[('cooperator', '=', True)]) - operation_type = fields.Selection([('subscription', 'Subscription'), - ('transfer', 'Transfer'), - ('sell_back', 'Sell Back'), - ('convert', 'Conversion')], - string='Operation Type', - required=True) - share_product_id = fields.Many2one('product.product', - string='Share type', - domain=[('is_share', '=', True)], - required=True) - share_to_product_id = fields.Many2one('product.product', - string='Convert to this share type', - domain=[('is_share', '=', True)]) - share_short_name = fields.Char(related='share_product_id.short_name', - string='Share type name') - share_to_short_name = fields.Char(related='share_to_product_id.short_name', - string='Share to type name') - share_unit_price = fields.Float(related='share_product_id.list_price', - string='Share price') - share_to_unit_price = fields.Float(related='share_to_product_id.list_price', - string='Share to price') - subscription_amount = fields.Float(compute='_compute_subscription_amount', - string='Operation amount') - quantity = fields.Integer(string='Number of share', - required=True) - state = fields.Selection([('draft', 'Draft'), - ('waiting', 'Waiting'), - ('approved', 'Approved'), - ('done', 'Done'), - ('cancelled', 'Cancelled'), - ('refused', 'Refused')], - string='State', - required=True, - default='draft') - user_id = fields.Many2one('res.users', - string='Responsible', - readonly=True, - default=lambda self: self.env.user) - subscription_request = fields.One2many('subscription.request', - 'operation_request_id', - string="Share Receiver Info", - help="In case on a transfer of" - " share. If the share receiver" - " isn't a effective member then a" - " subscription form should" - " be filled.") - receiver_not_member = fields.Boolean(string='Receiver is not a member') - company_id = fields.Many2one('res.company', - string='Company', - required=True, - change_default=True, - readonly=True, - default=lambda self: self.env['res.company']._company_default_get()) - - invoice = fields.Many2one('account.invoice', - string="Invoice") - - @api.multi - def approve_operation(self): - for rec in self: - rec.write({'state': 'approved'}) - - @api.multi - def refuse_operation(self): - for rec in self: - rec.write({'state': 'refused'}) - - @api.multi - def submit_operation(self): - for rec in self: - rec.validate() - rec.write({'state': 'waiting'}) - - @api.multi - def cancel_operation(self): - for rec in self: - rec.write({'state': 'cancelled'}) - - @api.multi - def reset_to_draft(self): - for rec in self: - rec.write({'state': 'draft'}) - - def get_total_share_dic(self, partner): - total_share_dic = {} - share_products = self.env['product.product'].search([('is_share', '=', True)]) - - for share_product in share_products: - total_share_dic[share_product.id] = 0 - - for line in partner.share_ids: - total_share_dic[line.share_product_id.id] += line.share_number - - return total_share_dic - - # This function doesn't handle the case of a cooperator can own - # different kinds of share type - def hand_share_over(self, partner, share_product_id, quantity): - if not partner.member: - raise ValidationError(_("This operation can't be executed if the" - " cooperator is not an effective member")) - - share_ind = len(partner.share_ids) - i = 1 - while quantity > 0: - line = self.partner_id.share_ids[share_ind-i] - if line.share_product_id.id == share_product_id.id: - if quantity > line.share_number: - quantity -= line.share_number - line.unlink() - else: - share_left = line.share_number - quantity - quantity = 0 - line.write({'share_number': share_left}) - i += 1 - # if the cooperator sold all his shares he's no more - # an effective member - remaning_share_dict = 0 - for share_quant in self.get_total_share_dic(partner).values(): - remaning_share_dict += share_quant - if remaning_share_dict == 0: - self.partner_id.write({'member': False, 'old_member': True}) - - def has_share_type(self): - for line in self.partner_id.share_ids: - if line.share_product_id.id == self.share_product_id.id: - return True - return False - - def validate(self): - if not self.has_share_type() and \ - self.operation_type in ['sell_back', 'transfer']: - raise ValidationError(_("The cooperator doesn't own this share" - " type. Please choose the appropriate" - " share type.")) - - if self.operation_type in ['sell_back', 'convert', 'transfer']: - total_share_dic = self.get_total_share_dic(self.partner_id) - - if self.quantity > total_share_dic[self.share_product_id.id]: - raise ValidationError(_("The cooperator can't hand over more" - " shares that he/she owns.")) - - if self.operation_type == 'convert': - if self.company_id.unmix_share_type: - if self.share_product_id.code == self.share_to_product_id.code: - raise ValidationError(_("You can't convert the share to" - " the same share type.")) - if self.subscription_amount != self.partner_id.total_value: - raise ValidationError(_("You must convert all the shares" - " to the selected type.")) - else: - if self.subscription_amount != self.partner_id.total_value: - raise ValidationError(_("Converting just part of the" - " shares is not yet implemented")) - elif self.operation_type == 'transfer': - if not self.receiver_not_member and self.company_id.unmix_share_type \ - and (self.partner_id_to.cooperator_type - and self.partner_id.cooperator_type != self.partner_id_to.cooperator_type): - raise ValidationError(_("This share type could not be" - " transfered to " + - self.partner_id_to.name)) - if self.partner_id_to.is_company \ - and not self.share_product_id.by_company: - raise ValidationError(_("This share can not be" - " subscribed by a company")) - if not self.partner_id_to.is_company \ - and not self.share_product_id.by_individual: - raise ValidationError(_("This share can not be" - " subscribed an individual")) - if self.receiver_not_member and self.subscription_request \ - and not self.subscription_request.validated: - raise ValidationError(_("The information of the receiver" - " are not correct. Please correct" - " the information before" - " submitting")) - - def get_share_trans_mail_template(self): - return self.env.ref('easy_my_coop.email_template_share_transfer', - False) - - def get_share_update_mail_template(self): - return self.env.ref('easy_my_coop.email_template_share_update', - False) - - def send_share_trans_mail(self, sub_register_line): # Unused argument is used in synergie project. Do not remove. - cert_email_template = self.get_share_trans_mail_template() - cert_email_template.send_mail(self.partner_id_to.id, False) - - def send_share_update_mail(self, sub_register_line): # Unused argument is used in synergie project. Do not remove. - cert_email_template = self.get_share_update_mail_template() - cert_email_template.send_mail(self.partner_id.id, False) - - def get_subscription_register_vals(self, effective_date): - return { - 'partner_id': self.partner_id.id, 'quantity': self.quantity, - 'share_product_id': self.share_product_id.id, - 'type': self.operation_type, - 'share_unit_price': self.share_unit_price, - 'date': effective_date, - } - - @api.multi - def execute_operation(self): - self.ensure_one() - - effective_date = self.get_date_now() - sub_request = self.env['subscription.request'] - - self.validate() - - if self.state != 'approved': - raise ValidationError(_("This operation must be approved" - " before to be executed")) - - values = self.get_subscription_register_vals(effective_date) - - if self.operation_type == 'sell_back': - self.hand_share_over(self.partner_id, self.share_product_id, - self.quantity) - elif self.operation_type == 'convert': - amount_to_convert = self.share_unit_price * self.quantity - convert_quant = int(amount_to_convert / self.share_to_product_id.list_price) - remainder = amount_to_convert % self.share_to_product_id.list_price - - if convert_quant > 0 and remainder == 0: - share_ids = self.partner_id.share_ids - line = share_ids[0] - if len(share_ids) > 1: - share_ids[1:len(share_ids)].unlink() - line.write({ - 'share_number': convert_quant, - 'share_product_id': self.share_to_product_id.id, - 'share_unit_price': self.share_to_unit_price, - 'share_short_name': self.share_to_short_name - }) - values['share_to_product_id'] = self.share_to_product_id.id - values['quantity_to'] = convert_quant - else: - raise ValidationError(_("Converting just part of the" - " shares is not yet implemented")) - elif self.operation_type == 'transfer': - sequence_id = self.env.ref('easy_my_coop.sequence_subscription', False) - partner_vals = {'member': True} - if self.receiver_not_member: - partner = self.subscription_request.create_coop_partner() - # get cooperator number - sub_reg_num = int(sequence_id.next_by_id()) - partner_vals.update(sub_request.get_eater_vals( - partner, - self.share_product_id)) - partner_vals['cooperator_register_number'] = sub_reg_num - partner.write(partner_vals) - self.partner_id_to = partner - else: - # means an old member or cooperator candidate - if not self.partner_id_to.member: - if self.partner_id_to.cooperator_register_number == 0: - sub_reg_num = int(sequence_id.next_by_id()) - partner_vals['cooperator_register_number'] = sub_reg_num - partner_vals.update(sub_request.get_eater_vals( - self.partner_id_to, - self.share_product_id)) - partner_vals['old_member'] = False - self.partner_id_to.write(partner_vals) - # remove the parts to the giver - self.hand_share_over(self.partner_id, - self.share_product_id, - self.quantity) - # give the share to the receiver - self.env['share.line'].create({ - 'share_number': self.quantity, - 'partner_id': self.partner_id_to.id, - 'share_product_id': self.share_product_id.id, - 'share_unit_price': self.share_unit_price, - 'effective_date': effective_date}) - values['partner_id_to'] = self.partner_id_to.id - else: - raise ValidationError(_("This operation is not yet" - " implemented.")) - - sequence_operation = self.env.ref('easy_my_coop.sequence_register_operation', False) #noqa - sub_reg_operation = sequence_operation.next_by_id() - - values['name'] = sub_reg_operation - values['register_number_operation'] = int(sub_reg_operation) - - self.write({'state': 'done'}) - - sub_register_line = self.env['subscription.register'].create(values) - - # send mail to the receiver - if self.operation_type == 'transfer': - self.send_share_trans_mail(sub_register_line) - - self.send_share_update_mail(sub_register_line) +# Copyright 2019 Coop IT Easy SCRL fs +# Houssine Bakkali +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + + +from datetime import datetime + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + + +class OperationRequest(models.Model): + _name = "operation.request" + _description = "Operation request" + + def get_date_now(self): + # fixme odoo 12 uses date types + return datetime.strftime(datetime.now(), "%Y-%m-%d") + + @api.multi + @api.depends("share_product_id", "share_product_id.list_price", "quantity") + def _compute_subscription_amount(self): + for operation_request in self: + operation_request.subscription_amount = ( + operation_request.share_product_id.list_price + * operation_request.quantity + ) + + request_date = fields.Date( + string="Request date", default=lambda self: self.get_date_now() + ) + partner_id = fields.Many2one( + "res.partner", + string="Cooperator", + domain=[("member", "=", True)], + required=True, + ) + partner_id_to = fields.Many2one( + "res.partner", + string="Transfered to", + domain=[("cooperator", "=", True)], + ) + operation_type = fields.Selection( + [ + ("subscription", "Subscription"), + ("transfer", "Transfer"), + ("sell_back", "Sell Back"), + ("convert", "Conversion"), + ], + string="Operation Type", + required=True, + ) + share_product_id = fields.Many2one( + "product.product", + string="Share type", + domain=[("is_share", "=", True)], + required=True, + ) + share_to_product_id = fields.Many2one( + "product.product", + string="Convert to this share type", + domain=[("is_share", "=", True)], + ) + share_short_name = fields.Char( + related="share_product_id.short_name", string="Share type name" + ) + share_to_short_name = fields.Char( + related="share_to_product_id.short_name", string="Share to type name" + ) + share_unit_price = fields.Float( + related="share_product_id.list_price", string="Share price" + ) + share_to_unit_price = fields.Float( + related="share_to_product_id.list_price", string="Share to price" + ) + subscription_amount = fields.Float( + compute="_compute_subscription_amount", string="Operation amount" + ) + quantity = fields.Integer(string="Number of share", required=True) + state = fields.Selection( + [ + ("draft", "Draft"), + ("waiting", "Waiting"), + ("approved", "Approved"), + ("done", "Done"), + ("cancelled", "Cancelled"), + ("refused", "Refused"), + ], + string="State", + required=True, + default="draft", + ) + user_id = fields.Many2one( + "res.users", + string="Responsible", + readonly=True, + default=lambda self: self.env.user, + ) + subscription_request = fields.One2many( + "subscription.request", + "operation_request_id", + string="Share Receiver Info", + help="In case on a transfer of" + " share. If the share receiver" + " isn't a effective member then a" + " subscription form should" + " be filled.", + ) + receiver_not_member = fields.Boolean(string="Receiver is not a member") + company_id = fields.Many2one( + "res.company", + string="Company", + required=True, + change_default=True, + readonly=True, + default=lambda self: self.env["res.company"]._company_default_get(), + ) + + invoice = fields.Many2one("account.invoice", string="Invoice") + + @api.multi + def approve_operation(self): + for rec in self: + rec.write({"state": "approved"}) + + @api.multi + def refuse_operation(self): + for rec in self: + rec.write({"state": "refused"}) + + @api.multi + def submit_operation(self): + for rec in self: + rec.validate() + rec.write({"state": "waiting"}) + + @api.multi + def cancel_operation(self): + for rec in self: + rec.write({"state": "cancelled"}) + + @api.multi + def reset_to_draft(self): + for rec in self: + rec.write({"state": "draft"}) + + def get_total_share_dic(self, partner): + total_share_dic = {} + share_products = self.env["product.product"].search( + [("is_share", "=", True)] + ) + + for share_product in share_products: + total_share_dic[share_product.id] = 0 + + for line in partner.share_ids: + total_share_dic[line.share_product_id.id] += line.share_number + + return total_share_dic + + # This function doesn't handle the case of a cooperator can own + # different kinds of share type + def hand_share_over(self, partner, share_product_id, quantity): + if not partner.member: + raise ValidationError( + _( + "This operation can't be executed if the" + " cooperator is not an effective member" + ) + ) + + share_ind = len(partner.share_ids) + i = 1 + while quantity > 0: + line = self.partner_id.share_ids[share_ind - i] + if line.share_product_id.id == share_product_id.id: + if quantity > line.share_number: + quantity -= line.share_number + line.unlink() + else: + share_left = line.share_number - quantity + quantity = 0 + line.write({"share_number": share_left}) + i += 1 + # if the cooperator sold all his shares he's no more + # an effective member + remaning_share_dict = 0 + for share_quant in self.get_total_share_dic(partner).values(): + remaning_share_dict += share_quant + if remaning_share_dict == 0: + self.partner_id.write({"member": False, "old_member": True}) + + def has_share_type(self): + for line in self.partner_id.share_ids: + if line.share_product_id.id == self.share_product_id.id: + return True + return False + + def validate(self): + if not self.has_share_type() and self.operation_type in [ + "sell_back", + "transfer", + ]: + raise ValidationError( + _( + "The cooperator doesn't own this share" + " type. Please choose the appropriate" + " share type." + ) + ) + + if self.operation_type in ["sell_back", "convert", "transfer"]: + total_share_dic = self.get_total_share_dic(self.partner_id) + + if self.quantity > total_share_dic[self.share_product_id.id]: + raise ValidationError( + _( + "The cooperator can't hand over more" + " shares that he/she owns." + ) + ) + + if self.operation_type == "convert": + if self.company_id.unmix_share_type: + if self.share_product_id.code == self.share_to_product_id.code: + raise ValidationError( + _( + "You can't convert the share to" + " the same share type." + ) + ) + if self.subscription_amount != self.partner_id.total_value: + raise ValidationError( + _( + "You must convert all the shares" + " to the selected type." + ) + ) + else: + if self.subscription_amount != self.partner_id.total_value: + raise ValidationError( + _( + "Converting just part of the" + " shares is not yet implemented" + ) + ) + elif self.operation_type == "transfer": + if ( + not self.receiver_not_member + and self.company_id.unmix_share_type + and ( + self.partner_id_to.cooperator_type + and self.partner_id.cooperator_type + != self.partner_id_to.cooperator_type + ) + ): + raise ValidationError( + _( + "This share type could not be" + " transfered to " + self.partner_id_to.name + ) + ) + if ( + self.partner_id_to.is_company + and not self.share_product_id.by_company + ): + raise ValidationError( + _("This share can not be" " subscribed by a company") + ) + if ( + not self.partner_id_to.is_company + and not self.share_product_id.by_individual + ): + raise ValidationError( + _("This share can not be" " subscribed an individual") + ) + if ( + self.receiver_not_member + and self.subscription_request + and not self.subscription_request.validated + ): + raise ValidationError( + _( + "The information of the receiver" + " are not correct. Please correct" + " the information before" + " submitting" + ) + ) + + def get_share_trans_mail_template(self): + return self.env.ref( + "easy_my_coop.email_template_share_transfer", False + ) + + def get_share_update_mail_template(self): + return self.env.ref("easy_my_coop.email_template_share_update", False) + + def send_share_trans_mail( + self, sub_register_line + ): # Unused argument is used in synergie project. Do not remove. + cert_email_template = self.get_share_trans_mail_template() + cert_email_template.send_mail(self.partner_id_to.id, False) + + def send_share_update_mail( + self, sub_register_line + ): # Unused argument is used in synergie project. Do not remove. + cert_email_template = self.get_share_update_mail_template() + cert_email_template.send_mail(self.partner_id.id, False) + + def get_subscription_register_vals(self, effective_date): + return { + "partner_id": self.partner_id.id, + "quantity": self.quantity, + "share_product_id": self.share_product_id.id, + "type": self.operation_type, + "share_unit_price": self.share_unit_price, + "date": effective_date, + } + + @api.multi + def execute_operation(self): + self.ensure_one() + + effective_date = self.get_date_now() + sub_request = self.env["subscription.request"] + + self.validate() + + if self.state != "approved": + raise ValidationError( + _("This operation must be approved" " before to be executed") + ) + + values = self.get_subscription_register_vals(effective_date) + + if self.operation_type == "sell_back": + self.hand_share_over( + self.partner_id, self.share_product_id, self.quantity + ) + elif self.operation_type == "convert": + amount_to_convert = self.share_unit_price * self.quantity + convert_quant = int( + amount_to_convert / self.share_to_product_id.list_price + ) + remainder = amount_to_convert % self.share_to_product_id.list_price + + if convert_quant > 0 and remainder == 0: + share_ids = self.partner_id.share_ids + line = share_ids[0] + if len(share_ids) > 1: + share_ids[1 : len(share_ids)].unlink() + line.write( + { + "share_number": convert_quant, + "share_product_id": self.share_to_product_id.id, + "share_unit_price": self.share_to_unit_price, + "share_short_name": self.share_to_short_name, + } + ) + values["share_to_product_id"] = self.share_to_product_id.id + values["quantity_to"] = convert_quant + else: + raise ValidationError( + _( + "Converting just part of the" + " shares is not yet implemented" + ) + ) + elif self.operation_type == "transfer": + sequence_id = self.env.ref( + "easy_my_coop.sequence_subscription", False + ) + partner_vals = {"member": True} + if self.receiver_not_member: + partner = self.subscription_request.create_coop_partner() + # get cooperator number + sub_reg_num = int(sequence_id.next_by_id()) + partner_vals.update( + sub_request.get_eater_vals(partner, self.share_product_id) + ) + partner_vals["cooperator_register_number"] = sub_reg_num + partner.write(partner_vals) + self.partner_id_to = partner + else: + # means an old member or cooperator candidate + if not self.partner_id_to.member: + if self.partner_id_to.cooperator_register_number == 0: + sub_reg_num = int(sequence_id.next_by_id()) + partner_vals[ + "cooperator_register_number" + ] = sub_reg_num + partner_vals.update( + sub_request.get_eater_vals( + self.partner_id_to, self.share_product_id + ) + ) + partner_vals["old_member"] = False + self.partner_id_to.write(partner_vals) + # remove the parts to the giver + self.hand_share_over( + self.partner_id, self.share_product_id, self.quantity + ) + # give the share to the receiver + self.env["share.line"].create( + { + "share_number": self.quantity, + "partner_id": self.partner_id_to.id, + "share_product_id": self.share_product_id.id, + "share_unit_price": self.share_unit_price, + "effective_date": effective_date, + } + ) + values["partner_id_to"] = self.partner_id_to.id + else: + raise ValidationError( + _("This operation is not yet" " implemented.") + ) + + sequence_operation = self.env.ref( + "easy_my_coop.sequence_register_operation", False + ) # noqa + sub_reg_operation = sequence_operation.next_by_id() + + values["name"] = sub_reg_operation + values["register_number_operation"] = int(sub_reg_operation) + + self.write({"state": "done"}) + + sub_register_line = self.env["subscription.register"].create(values) + + # send mail to the receiver + if self.operation_type == "transfer": + self.send_share_trans_mail(sub_register_line) + + self.send_share_update_mail(sub_register_line) diff --git a/easy_my_coop/models/partner.py b/easy_my_coop/models/partner.py index f6d9afc..f579766 100644 --- a/easy_my_coop/models/partner.py +++ b/easy_my_coop/models/partner.py @@ -7,7 +7,7 @@ from odoo import api, fields, models class ResPartner(models.Model): - _inherit = 'res.partner' + _inherit = "res.partner" @api.multi def _get_report_base_filename(self): @@ -15,11 +15,11 @@ class ResPartner(models.Model): if self.member: return "Cooperator Certificate - %s" % self.name else: - return 'unknown' + return "unknown" @api.multi def _invoice_total(self): - account_invoice_report = self.env['account.invoice.report'] + account_invoice_report = self.env["account.invoice.report"] if not self.ids: self.total_invoiced = 0.0 return True @@ -28,7 +28,9 @@ class ResPartner(models.Model): all_partner_ids = [] for partner in self: # price_total is in the company currency - all_partners_and_children[partner] = self.search([('id', 'child_of', partner.id)]).ids + all_partners_and_children[partner] = self.search( + [("id", "child_of", partner.id)] + ).ids all_partner_ids += all_partners_and_children[partner] # searching account.invoice.report via the orm is comparatively @@ -38,30 +40,46 @@ class ResPartner(models.Model): # the user's company access directly these elements # generate where clause to include multicompany rules - where_query = account_invoice_report._where_calc([ - ('partner_id', 'in', all_partner_ids), - ('state', 'not in', ['draft', 'cancel']), - ('company_id', '=', self.env.user.company_id.id), - ('type', 'in', ('out_invoice', 'out_refund')), - ('release_capital_request', '=', False), - ]) - account_invoice_report._apply_ir_rules(where_query, 'read') + where_query = account_invoice_report._where_calc( + [ + ("partner_id", "in", all_partner_ids), + ("state", "not in", ["draft", "cancel"]), + ("company_id", "=", self.env.user.company_id.id), + ("type", "in", ("out_invoice", "out_refund")), + ("release_capital_request", "=", False), + ] + ) + account_invoice_report._apply_ir_rules(where_query, "read") from_clause, where_clause, where_clause_params = where_query.get_sql() - # price_total is in the company currency - query = """ - SELECT SUM(price_total) as total, partner_id - FROM account_invoice_report account_invoice_report - WHERE %s - GROUP BY partner_id - """ % where_clause + # fixme while you're here, please fix the query + # to pass pylint sql-injection + # Note de Houssine: note que c'est la + # surcharge d'une fonction standard de la facturation Odoo. Elle + # date de la 9 voir si la v12 a été adaptée où est équivalente à la 12 + # price_total is in the company currency pylint: + # pylint: disable=sql-injection + query = ( + """ + SELECT SUM(price_total) as total, partner_id + FROM account_invoice_report account_invoice_report + WHERE %s + GROUP BY partner_id + """ + % where_clause + ) + self.env.cr.execute(query, where_clause_params) price_totals = self.env.cr.dictfetchall() for partner, child_ids in all_partners_and_children.items(): - partner.total_invoiced = sum(price['total'] for price in price_totals if price['partner_id'] in child_ids) + partner.total_invoiced = sum( + price["total"] + for price in price_totals + if price["partner_id"] in child_ids + ) @api.multi - @api.depends('share_ids') + @api.depends("share_ids") def _compute_effective_date(self): # TODO change it to compute it from the share register for partner in self: @@ -70,22 +88,20 @@ class ResPartner(models.Model): @api.multi def _get_share_type(self): - shares = ( - self.env['product.product'] - .search([('is_share', '=', True)]) - ) - share_types = [ - (share.default_code, share.short_name) for share in shares - ] - return [('', '')] + share_types + shares = self.env["product.product"].search([("is_share", "=", True)]) + share_types = [(s.default_code, s.short_name) for s in shares] + return [("", "")] + share_types @api.multi - @api.depends('share_ids', 'share_ids.share_product_id', - 'share_ids.share_product_id.default_code', - 'share_ids.share_number') + @api.depends( + "share_ids", + "share_ids.share_product_id", + "share_ids.share_product_id.default_code", + "share_ids.share_number", + ) def _compute_cooperator_type(self): for partner in self: - share_type = '' + share_type = "" for line in partner.share_ids: if line.share_number > 0: share_type = line.share_product_id.default_code @@ -93,7 +109,7 @@ class ResPartner(models.Model): partner.cooperator_type = share_type @api.multi - @api.depends('share_ids') + @api.depends("share_ids") def _compute_share_info(self): for partner in self: number_of_share = 0 @@ -104,62 +120,75 @@ class ResPartner(models.Model): partner.number_of_share = number_of_share partner.total_value = total_value - cooperator = fields.Boolean(string='Cooperator', - help="Check this box if this contact is a" - " cooperator (effective or not).") - member = fields.Boolean(string='Effective cooperator', - help="Check this box if this cooperator" - " is an effective member.") - coop_candidate = fields.Boolean(string="Cooperator candidate", - compute="_compute_coop_candidate", - store=True, - readonly=True) - old_member = fields.Boolean(string='Old cooperator', - help="Check this box if this cooperator is" - " no more an effective member.") + cooperator = fields.Boolean( + string="Cooperator", + help="Check this box if this contact is a" + " cooperator (effective or not).", + ) + member = fields.Boolean( + string="Effective cooperator", + help="Check this box if this cooperator" " is an effective member.", + ) + coop_candidate = fields.Boolean( + string="Cooperator candidate", + compute="_compute_coop_candidate", + store=True, + readonly=True, + ) + old_member = fields.Boolean( + string="Old cooperator", + help="Check this box if this cooperator is" + " no more an effective member.", + ) # todo use oca partner_contact_gender - gender = fields.Selection([('male', 'Male'), - ('female', 'Female'), - ('other', 'Other')], - string='Gender') - share_ids = fields.One2many('share.line', - 'partner_id', - string='Share Lines') - cooperator_register_number = fields.Integer(string='Cooperator Number') - number_of_share = fields.Integer(compute="_compute_share_info", - multi='share', - string='Number of share', - readonly=True) - total_value = fields.Float(compute="_compute_share_info", - multi='share', - string='Total value of shares', - readonly=True) - company_register_number = fields.Char(string='Company Register Number') - cooperator_type = fields.Selection(selection='_get_share_type', - compute=_compute_cooperator_type, - string='Cooperator Type', - store=True) - effective_date = fields.Date(sting="Effective Date", - compute=_compute_effective_date, - store=True) + gender = fields.Selection( + [("male", "Male"), ("female", "Female"), ("other", "Other")], + string="Gender", + ) + share_ids = fields.One2many( + "share.line", "partner_id", string="Share Lines" + ) + cooperator_register_number = fields.Integer(string="Cooperator Number") + number_of_share = fields.Integer( + compute="_compute_share_info", + multi="share", + string="Number of share", + readonly=True, + ) + total_value = fields.Float( + compute="_compute_share_info", + multi="share", + string="Total value of shares", + readonly=True, + ) + company_register_number = fields.Char(string="Company Register Number") + cooperator_type = fields.Selection( + selection="_get_share_type", + compute=_compute_cooperator_type, + string="Cooperator Type", + store=True, + ) + effective_date = fields.Date( + sting="Effective Date", compute=_compute_effective_date, store=True + ) representative = fields.Boolean(string="Legal Representative") - subscription_request_ids = fields.One2many('subscription.request', - 'partner_id', - string="Subscription request") - legal_form = fields.Selection([('', '')], - string="Legal form") + subscription_request_ids = fields.One2many( + "subscription.request", "partner_id", string="Subscription request" + ) + legal_form = fields.Selection([("", "")], string="Legal form") data_policy_approved = fields.Boolean(string="Approved Data Policy") internal_rules_approved = fields.Boolean(string="Approved Internal Rules") @api.multi - @api.depends('subscription_request_ids.state') + @api.depends("subscription_request_ids.state") def _compute_coop_candidate(self): for partner in self: if partner.member: is_candidate = False else: sub_requests = partner.subscription_request_ids.filtered( - lambda record: record.state == 'done') + lambda record: record.state == "done" + ) is_candidate = bool(sub_requests) partner.coop_candidate = is_candidate @@ -167,19 +196,24 @@ class ResPartner(models.Model): @api.multi def has_representative(self): self.ensure_one() - if self.child_ids.filtered('representative'): + if self.child_ids.filtered("representative"): return True return False @api.multi def get_representative(self): self.ensure_one() - return self.child_ids.filtered('representative') + return self.child_ids.filtered("representative") def get_cooperator_from_email(self, email): - return self.env['res.partner'].search([('cooperator', '=', True), - ('email', '=', email)]) + return self.env["res.partner"].search( + [("cooperator", "=", True), ("email", "=", email)] + ) def get_cooperator_from_crn(self, company_register_number): - return self.env['res.partner'].search([('cooperator', '=', True), - ('company_register_number', '=', company_register_number)]) + return self.env["res.partner"].search( + [ + ("cooperator", "=", True), + ("company_register_number", "=", company_register_number), + ] + ) diff --git a/easy_my_coop/models/product.py b/easy_my_coop/models/product.py index 0706a1d..af035e8 100644 --- a/easy_my_coop/models/product.py +++ b/easy_my_coop/models/product.py @@ -1,37 +1,41 @@ -# -*- coding: utf-8 -*- -# Copyright 2019 Coop IT Easy SCRL fs -# Houssine Bakkali -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - - -from odoo import api, fields, models - - -class ProductTemplate(models.Model): - _inherit = 'product.template' - - is_share = fields.Boolean(string="Is share?") - short_name = fields.Char(string="Short name") - display_on_website = fields.Boolean(string="Display on website") - default_share_product = fields.Boolean(string="Default share product") - minimum_quantity = fields.Integer(string="Minimum quantity", default=1) - force_min_qty = fields.Boolean(String="Force minimum quantity?") - by_company = fields.Boolean(string="Can be subscribed by companies?") - by_individual = fields.Boolean(string="Can be subscribed by individuals?") - customer = fields.Boolean(string="Become customer") - mail_template = fields.Many2one('mail.template', - string="Mail template") - - @api.multi - def get_web_share_products(self, is_company): - if is_company is True: - product_templates = self.env['product.template'].search([ - ('is_share', '=', True), - ('display_on_website', '=', True), - ('by_company', '=', True)]) - else: - product_templates = self.env['product.template'].search([ - ('is_share', '=', True), - ('display_on_website', '=', True), - ('by_individual', '=', True)]) - return product_templates +# Copyright 2019 Coop IT Easy SCRL fs +# Houssine Bakkali +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + + +from odoo import api, fields, models + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + is_share = fields.Boolean(string="Is share?") + short_name = fields.Char(string="Short name") + display_on_website = fields.Boolean(string="Display on website") + default_share_product = fields.Boolean(string="Default share product") + minimum_quantity = fields.Integer(string="Minimum quantity", default=1) + force_min_qty = fields.Boolean(String="Force minimum quantity?") + by_company = fields.Boolean(string="Can be subscribed by companies?") + by_individual = fields.Boolean(string="Can be subscribed by individuals?") + customer = fields.Boolean(string="Become customer") + mail_template = fields.Many2one("mail.template", string="Mail template") + + @api.multi + def get_web_share_products(self, is_company): + if is_company is True: + product_templates = self.env["product.template"].search( + [ + ("is_share", "=", True), + ("display_on_website", "=", True), + ("by_company", "=", True), + ] + ) + else: + product_templates = self.env["product.template"].search( + [ + ("is_share", "=", True), + ("display_on_website", "=", True), + ("by_individual", "=", True), + ] + ) + return product_templates diff --git a/easy_my_coop/models/res_partner_bank.py b/easy_my_coop/models/res_partner_bank.py index 53e2bb5..6e845d3 100644 --- a/easy_my_coop/models/res_partner_bank.py +++ b/easy_my_coop/models/res_partner_bank.py @@ -1,15 +1,14 @@ -# -*- coding: utf-8 -*- -# Copyright 2019 Coop IT Easy SCRL fs -# Houssine Bakkali -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - - -from odoo import models - - -class ResPartnerBank(models.Model): - _inherit = 'res.partner.bank' - - _sql_constraints = [ - ('unique_number', 'Check(1=1)', 'Account Number must be unique!'), - ] +# Copyright 2019 Coop IT Easy SCRL fs +# Houssine Bakkali +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + + +from odoo import models + + +class ResPartnerBank(models.Model): + _inherit = "res.partner.bank" + + _sql_constraints = [ + ("unique_number", "Check(1=1)", "Account Number must be unique!") + ] diff --git a/easy_my_coop/readme/CONTRIBUTORS.rst b/easy_my_coop/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000..b2844f3 --- /dev/null +++ b/easy_my_coop/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Coop IT Easy SCRLfs diff --git a/easy_my_coop/readme/DESCRIPTION.rst b/easy_my_coop/readme/DESCRIPTION.rst new file mode 100644 index 0000000..a9ff162 --- /dev/null +++ b/easy_my_coop/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +This module allows to manage the cooperator subscription and all the cooperative business processes. diff --git a/easy_my_coop/report/account_invoice_report.py b/easy_my_coop/report/account_invoice_report.py index bd1fb90..3374ce0 100644 --- a/easy_my_coop/report/account_invoice_report.py +++ b/easy_my_coop/report/account_invoice_report.py @@ -1,13 +1,19 @@ -from odoo import fields, models - - -class AccountInvoiceReport(models.Model): - _inherit = "account.invoice.report" - - release_capital_request = fields.Boolean(string="Release capital request") - - def _select(self): - return super(AccountInvoiceReport, self)._select() + ", sub.release_capital_request as release_capital_request" - - def _sub_select(self): - return super(AccountInvoiceReport, self)._sub_select() + ", ai.release_capital_request as release_capital_request" +from odoo import fields, models + + +class AccountInvoiceReport(models.Model): + _inherit = "account.invoice.report" + + release_capital_request = fields.Boolean(string="Release capital request") + + def _select(self): + return ( + super(AccountInvoiceReport, self)._select() + + ", sub.release_capital_request as release_capital_request" + ) + + def _sub_select(self): + return ( + super(AccountInvoiceReport, self)._sub_select() + + ", ai.release_capital_request as release_capital_request" + ) diff --git a/easy_my_coop/report/cooperator_certificat_G001.xml b/easy_my_coop/report/cooperator_certificat_G001.xml index 26e2442..02661a1 100644 --- a/easy_my_coop/report/cooperator_certificat_G001.xml +++ b/easy_my_coop/report/cooperator_certificat_G001.xml @@ -5,7 +5,8 @@ -
+

@@ -13,9 +14,9 @@

- Certificate generated on - -

+ Certificate generated on + +

@@ -24,7 +25,8 @@

- +
@@ -51,11 +53,11 @@ @@ -67,14 +69,16 @@
+ t-options='{"widget": "monetary", "display_currency": o.currency_id}'/> + t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
- +
Total + Total + - share(s) + share(s) + t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
@@ -85,7 +89,7 @@

- For the board of . + For the board of.

@@ -93,9 +97,9 @@

+ t-attf-class="mt16 w-50" + t-attf-style="{{ 'min-width: 100px; max-width: 250px' if report_type == 'pdf' else '' }}" + t-att-src="'data:image/png;base64,%s' % o.company_id.signature_scan.decode()"/>
@@ -107,7 +111,8 @@ diff --git a/easy_my_coop/report/cooperator_invoice_G002.xml b/easy_my_coop/report/cooperator_invoice_G002.xml index d845a31..d630031 100644 --- a/easy_my_coop/report/cooperator_invoice_G002.xml +++ b/easy_my_coop/report/cooperator_invoice_G002.xml @@ -5,39 +5,56 @@ -
-
:
+
+
: + +

- Request to Release Capital + Request to Release + Capital + - Invoice - Draft Invoice - Cancelled Invoice + + Invoice + + + Draft Invoice + + + Cancelled Invoice + Credit Note - Vendor Credit Note + Vendor Credit Note + Vendor Bill

-
+
Description:

-
+
Request Date:

-
+
Structured Communication:

Account Number: - +

@@ -53,20 +70,36 @@

- + - +
- - - - - + + + + + @@ -75,26 +108,38 @@ - - + + - - - + + + @@ -116,9 +161,9 @@ @@ -131,7 +176,9 @@
DescriptionSource DocumentPart TypeQuantityUnit Price + Description + + Source Document + - Amount - Total Price + Part Type + + Quantity + + Unit Price + + + Amount + + + Total Price +
+ + + + - + - - + + Subtotal + t-esc="current_subtotal" + t-options='{"widget": "monetary", "display_currency": o.currency_id}' + />
- + @@ -141,7 +188,10 @@

- Please use the following communication for your payment : + Please use the following communication for your payment : + + +

@@ -152,13 +202,22 @@

-
+

- Scan me with your banking app.

- + Scan me with your banking + app. + +
+
+

- The SEPA QR Code informations are not set correctly.
+ The SEPA QR Code + informations are not set correctly. + +

@@ -169,7 +228,8 @@ diff --git a/easy_my_coop/report/cooperator_register_G001.xml b/easy_my_coop/report/cooperator_register_G001.xml index 781230d..845b739 100644 --- a/easy_my_coop/report/cooperator_register_G001.xml +++ b/easy_my_coop/report/cooperator_register_G001.xml @@ -1,121 +1,145 @@ - diff --git a/easy_my_coop/report/cooperator_subscription_G001.xml b/easy_my_coop/report/cooperator_subscription_G001.xml index 29f1313..b3cbdec 100644 --- a/easy_my_coop/report/cooperator_subscription_G001.xml +++ b/easy_my_coop/report/cooperator_subscription_G001.xml @@ -1,106 +1,130 @@ - diff --git a/easy_my_coop/report/easy_my_coop_report.xml b/easy_my_coop/report/easy_my_coop_report.xml index c138c21..84e2d5a 100644 --- a/easy_my_coop/report/easy_my_coop_report.xml +++ b/easy_my_coop/report/easy_my_coop_report.xml @@ -1,49 +1,49 @@ - - - + + - - - + - + diff --git a/easy_my_coop/report/layout.xml b/easy_my_coop/report/layout.xml index fc43853..eea8fa3 100644 --- a/easy_my_coop/report/layout.xml +++ b/easy_my_coop/report/layout.xml @@ -1,38 +1,60 @@ -
Total + Total +