From 4d9f997eacdcc9c1aae43ff35fcf93e8073a9dbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20-=20Le=20Filament?= <remi@le-filament.com> Date: Wed, 22 Jan 2025 16:37:53 +0100 Subject: [PATCH] [FIX] use planned_date and add department & project on planned_hours --- .eslintrc.yml | 1 + .flake8 | 12 ---- .gitignore | 3 +- .isort.cfg | 13 ----- .pre-commit-config.yaml | 76 ++++++++++++------------- .pylintrc | 3 + .pylintrc-mandatory | 1 + .ruff.toml | 28 +++++++++ __manifest__.py | 1 + data/ir_cron_data.xml | 2 +- migrations/14.0.2.0.0/post-migration.py | 2 +- models/__init__.py | 2 + models/project_overview.py | 15 +++++ models/project_task.py | 24 +++++--- models/project_task_planned_hour.py | 4 ++ models/sale_order_line.py | 17 ++++++ views/project_task.xml | 24 ++++---- views/project_task_planned_hour.xml | 28 +++++++++ 18 files changed, 170 insertions(+), 86 deletions(-) delete mode 100644 .flake8 delete mode 100644 .isort.cfg create mode 100644 .ruff.toml create mode 100644 models/project_overview.py create mode 100644 models/sale_order_line.py diff --git a/.eslintrc.yml b/.eslintrc.yml index 9429bc6..fed88d7 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -22,6 +22,7 @@ globals: odoo: readonly openerp: readonly owl: readonly + luxon: readonly # Styling is handled by Prettier, so we only need to enable AST rules; # see https://github.com/OCA/maintainer-quality-tools/pull/618#issuecomment-558576890 diff --git a/.flake8 b/.flake8 deleted file mode 100644 index e397e8e..0000000 --- a/.flake8 +++ /dev/null @@ -1,12 +0,0 @@ -[flake8] -max-line-length = 88 -max-complexity = 16 -# B = bugbear -# B9 = bugbear opinionated (incl line length) -select = C,E,F,W,B,B9 -# E203: whitespace before ':' (black behaviour) -# E501: flake8 line length (covered by bugbear B950) -# W503: line break before binary operator (black behaviour) -ignore = E203,E501,W503 -per-file-ignores= - __init__.py:F401 diff --git a/.gitignore b/.gitignore index 818770f..d99361a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ __pycache__/ *.py[cod] /.venv /.pytest_cache +/.ruff_cache # C extensions *.so @@ -15,7 +16,6 @@ build/ develop-eggs/ dist/ eggs/ -lib/ lib64/ parts/ sdist/ @@ -24,6 +24,7 @@ var/ .installed.cfg *.egg *.eggs +.copier-answers.yml # Installer logs pip-log.txt diff --git a/.isort.cfg b/.isort.cfg deleted file mode 100644 index 0ec187e..0000000 --- a/.isort.cfg +++ /dev/null @@ -1,13 +0,0 @@ -[settings] -; see https://github.com/psf/black -multi_line_output=3 -include_trailing_comma=True -force_grid_wrap=0 -combine_as_imports=True -use_parentheses=True -line_length=88 -known_odoo=odoo -known_odoo_addons=odoo.addons -sections=FUTURE,STDLIB,THIRDPARTY,ODOO,ODOO_ADDONS,FIRSTPARTY,LOCALFOLDER -default_section=THIRDPARTY -ensure_newline_before_comments = True diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 732d0c4..740d10c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,10 +6,16 @@ exclude: | ^setup/|/static/description/index\.html$| # We don't want to mess with tool-generated files .svg$|/tests/([^/]+/)?cassettes/|^.copier-answers.yml$|^.github/| + # Maybe reactivate this when all README files include prettier ignore tags? + ^README\.md$| # Library files can have extraneous formatting (even minimized) /static/(src/)?lib/| # Repos using Sphinx to generate docs don't need prettying ^docs/_templates/.*\.html$| + # Don't bother non-technical authors with formatting issues in docs + readme/.*\.(rst|md)$| + # Ignore build and dist directories in addons + /build/|/dist/| # You don't usually want a bot to modify your legal texts (LICENSE.*|COPYING.*) default_language_version: @@ -25,30 +31,34 @@ repos: entry: found forbidden files; remove them language: fail files: "\\.rej$" + - id: en-po-files + name: en.po files cannot exist + entry: found a en.po file + language: fail + files: '[a-zA-Z0-9_]*/i18n/en\.po$' - repo: https://github.com/oca/maintainer-tools - rev: 7d8a9f9ad73db0976fb03cbee43d953bc29b89e9 + rev: 9a170331575a265c092ee6b24b845ec508e8ef75 hooks: # update the NOT INSTALLABLE ADDONS section above - id: oca-update-pre-commit-excluded-addons - id: oca-fix-manifest-website args: ["https://le-filament.com"] - - repo: https://github.com/myint/autoflake - rev: v1.4 - hooks: - - id: autoflake + - id: oca-gen-addon-readme args: - - --expand-star-imports - - --ignore-init-module-imports - - --in-place - - --remove-all-unused-imports - - --remove-duplicate-keys - - --remove-unused-variables - - repo: https://github.com/psf/black - rev: 22.3.0 + - --addons-dir=. + - --branch=14.0 + - --org-name=lefilament + - --repo-name=template_module + - --if-source-changed + - --keep-source-digest + - --convert-fragments-to-markdown + - repo: https://github.com/OCA/odoo-pre-commit-hooks + rev: v0.0.25 hooks: - - id: black + - id: oca-checks-odoo-module + - id: oca-checks-po - repo: https://github.com/pre-commit/mirrors-prettier - rev: v2.6.2 + rev: v2.1.2 hooks: - id: prettier name: prettier (with plugin-xml) @@ -59,7 +69,7 @@ repos: - --plugin=@prettier/plugin-xml files: \.(css|htm|html|js|json|jsx|less|md|scss|toml|ts|xml|yaml|yml)$ - repo: https://github.com/pre-commit/mirrors-eslint - rev: v8.15.0 + rev: v7.8.1 hooks: - id: eslint verbose: true @@ -67,7 +77,7 @@ repos: - --color - --fix - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.2.0 + rev: v3.2.0 hooks: - id: trailing-whitespace # exclude autogenerated files @@ -89,37 +99,25 @@ repos: - id: mixed-line-ending args: ["--fix=lf"] - repo: https://github.com/asottile/pyupgrade - rev: v2.32.1 + rev: v2.7.2 hooks: - id: pyupgrade args: ["--keep-percent-format"] - - repo: https://github.com/PyCQA/isort - rev: 5.10.1 - hooks: - - id: isort - name: isort except __init__.py - args: - - --settings=. - exclude: /__init__\.py$ - - repo: https://gitlab.com/PyCQA/flake8 - rev: 3.9.2 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.1.3 hooks: - - id: flake8 - name: flake8 - additional_dependencies: ["flake8-bugbear==20.1.4"] - - repo: https://github.com/PyCQA/pylint - rev: v2.11.1 + - id: ruff + args: [--fix, --exit-non-zero-on-fix] + - id: ruff-format + - repo: https://github.com/OCA/pylint-odoo + rev: 7.0.2 hooks: - - id: pylint + - id: pylint_odoo name: pylint with optional checks args: - --rcfile=.pylintrc - --exit-zero verbose: true - additional_dependencies: &pylint_deps - - pylint-odoo==5.0.5 - - id: pylint - name: pylint with mandatory checks + - id: pylint_odoo args: - --rcfile=.pylintrc-mandatory - additional_dependencies: *pylint_deps diff --git a/.pylintrc b/.pylintrc index dc6270e..941ea6b 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,3 +1,5 @@ + + [MASTER] load-plugins=pylint_odoo score=n @@ -73,6 +75,7 @@ enable=anomalous-backslash-in-string, invalid-commit, missing-manifest-dependency, missing-newline-extrafiles, + missing-readme, no-utf8-coding-comment, odoo-addons-relative-import, old-api7-method-defined, diff --git a/.pylintrc-mandatory b/.pylintrc-mandatory index 43ea239..4336f52 100644 --- a/.pylintrc-mandatory +++ b/.pylintrc-mandatory @@ -1,3 +1,4 @@ + [MASTER] load-plugins=pylint_odoo score=n diff --git a/.ruff.toml b/.ruff.toml new file mode 100644 index 0000000..d75c6d9 --- /dev/null +++ b/.ruff.toml @@ -0,0 +1,28 @@ + +fix = true + +[lint] +extend-select = [ + "B", + "C90", + "E501", # line too long (default 88) + "I", # isort +] +exclude = ["setup/*"] + +[format] +exclude = ["setup/*"] + +[per-file-ignores] +"__init__.py" = ["F401", "I001"] # ignore unused and unsorted imports in __init__.py +"__manifest__.py" = ["B018"] # useless expression + +[isort] +section-order = ["future", "standard-library", "third-party", "odoo", "odoo-addons", "first-party", "local-folder"] + +[isort.sections] +"odoo" = ["odoo"] +"odoo-addons" = ["odoo.addons"] + +[mccabe] +max-complexity = 16 diff --git a/__manifest__.py b/__manifest__.py index ac295f4..2fdbe9f 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -13,6 +13,7 @@ "project_timeline", "sale_timesheet", "ecozimut_mixin", + "lefilament_link_sale_project", ], "data": [ "security/ir.model.access.csv", diff --git a/data/ir_cron_data.xml b/data/ir_cron_data.xml index 9e9f744..b47b627 100644 --- a/data/ir_cron_data.xml +++ b/data/ir_cron_data.xml @@ -16,7 +16,7 @@ <field name="code"> env["project.task.planned.hour"].search([]).unlink() env.cr.execute("ALTER SEQUENCE project_task_planned_hour_id_seq RESTART WITH 1") -model.search([("user_id", "!=", False), ("date_end", "!=", False), ("estimated_remaining_hours", ">", 0.0)])._compute_planned_hour_ids() +model.search([("user_id", "!=", False), ("planned_date_end", "!=", False), ("estimated_remaining_hours", ">", 0.0)])._compute_planned_hour_ids() </field> <field name="state">code</field> <field name="priority" eval="5" /> diff --git a/migrations/14.0.2.0.0/post-migration.py b/migrations/14.0.2.0.0/post-migration.py index 2ceca16..ec10fe2 100644 --- a/migrations/14.0.2.0.0/post-migration.py +++ b/migrations/14.0.2.0.0/post-migration.py @@ -9,7 +9,7 @@ def _update_tasks_date_end(env): .with_context(active_test=False) .search([("date_deadline", "!=", False)]) ): - task.date_end = task.date_deadline + task.planned_date_end = task.date_deadline def migrate(cr, version): diff --git a/models/__init__.py b/models/__init__.py index e724b7f..a43ea91 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -1,5 +1,7 @@ from . import account_analytic_line from . import account_move from . import project +from . import project_overview from . import project_task_planned_hour from . import project_task +from . import sale_order_line diff --git a/models/project_overview.py b/models/project_overview.py new file mode 100644 index 0000000..8bf5011 --- /dev/null +++ b/models/project_overview.py @@ -0,0 +1,15 @@ +# Copyright 2021 Le Filament (<http://www.le-filament.com>) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class Project(models.Model): + _inherit = "project.project" + + def _get_late_tasks_domain(self): + return [ + ("project_id", "in", self.ids), + ("date_deadline", "<", fields.Date.to_string(fields.Date.today())), + ("planned_date_end", "=", False), + ] diff --git a/models/project_task.py b/models/project_task.py index 6860d0f..ab940a0 100644 --- a/models/project_task.py +++ b/models/project_task.py @@ -69,11 +69,13 @@ class ProjectTask(models.Model): else: task.estimated_remaining_hours = task.manual_remaining_hours - @api.depends("user_id", "date_start", "date_end", "estimated_remaining_hours") + @api.depends( + "user_id", "planned_date_start", "planned_date_end", "estimated_remaining_hours" + ) def _compute_planned_hour_ids(self): """ - Les heures planifiées par mois sont calculées à partir des heures restantes estimées - entre la date du jour et la date de fin, + Les heures planifiées par mois sont calculées à partir des heures restantes + estimées entre la date du jour et la date de fin, au prorata du nombre de jours dans chaque mois """ date_today = fields.Date.today() @@ -81,16 +83,19 @@ class ProjectTask(models.Model): task.planned_hour_ids.unlink() if ( task.user_id - and task.date_end - and task.date_end.date() >= date_today + and task.planned_date_end + and task.planned_date_end.date() >= date_today and task.estimated_remaining_hours and task.estimated_remaining_hours > 0.0 ): - if task.date_start and task.date_start.date() > date_today: - date_start = task.date_start.date() + if ( + task.planned_date_start + and task.planned_date_start.date() > date_today + ): + date_start = task.planned_date_start.date() else: date_start = date_today - date_end = task.date_end.date() + date_end = task.planned_date_end.date() planned_hours_values = [] # Nombre de jours total entre les 2 dates (intervalle) total_days = (date_end - date_start).days + 1 @@ -127,7 +132,8 @@ class ProjectTask(models.Model): * ((date_end - month_date).days + 1) / total_days ) - # Pour tous les mois intermédiaires, on calcule sur la totalité du mois + # Pour tous les mois intermédiaires, on calcule sur la totalité + # du mois else: planned_hours = ( task.estimated_remaining_hours diff --git a/models/project_task_planned_hour.py b/models/project_task_planned_hour.py index 317491c..d85558e 100644 --- a/models/project_task_planned_hour.py +++ b/models/project_task_planned_hour.py @@ -15,10 +15,14 @@ class ProjectTaskPlannedHour(models.Model): ondelete="cascade", required=1, ) + project_id = fields.Many2one(related="task_id.project_id", store=True) user_id = fields.Many2one( comodel_name="res.users", string="Assigné à", required=1, ) + department_id = fields.Many2one( + related="user_id.employee_id.department_id", store=True + ) planned_hours = fields.Float(string="Heures planifiées") date_month = fields.Date("Mois") diff --git a/models/sale_order_line.py b/models/sale_order_line.py new file mode 100644 index 0000000..eac8141 --- /dev/null +++ b/models/sale_order_line.py @@ -0,0 +1,17 @@ +# © 2018- Le Filament (https://le-filament.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import models + + +class SaleOrderLine(models.Model): + _inherit = "sale.order.line" + + # ------------------------------------------------------ + # Override parent + # ------------------------------------------------------ + def _timesheet_create_task_prepare_values(self, project): + values = super()._timesheet_create_task_prepare_values(project) + values["planned_date_end"] = self.estimated_date_invoice + values["date_deadline"] = self.estimated_date_invoice + return values diff --git a/views/project_task.xml b/views/project_task.xml index cbb7c74..830d125 100644 --- a/views/project_task.xml +++ b/views/project_task.xml @@ -13,7 +13,7 @@ <filter string="A renseigner" name="to_fill" - domain="['|', '|', '|', ('user_id', '=', []), ('date_start', '=', False), ('date_end', '=', False), ('planned_hours', '=', 0.0)]" + domain="['|', '|', '|', ('user_id', '=', []), ('planned_date_start', '=', False), ('planned_date_end', '=', False), ('planned_hours', '=', 0.0)]" /> <separator /> </xpath> @@ -30,8 +30,8 @@ <attribute name="groups">base.group_no_one</attribute> </field> <field name="date_deadline" position="after"> - <field name="date_start" widget="date" /> - <field name="date_end" widget="date" /> + <field name="planned_date_start" widget="date" /> + <field name="planned_date_end" widget="date" /> </field> <xpath expr="//field[@name='planned_hours']" position="after"> @@ -134,8 +134,12 @@ optional="hide" attrs="{'invisible': [('is_closed', '=', True)]}" /> - <field name="date_start" optional="show" widget="date" /> - <field name="date_end" optional="show" widget="date" /> + <field + name="planned_date_start" + optional="show" + widget="date" + /> + <field name="planned_date_end" optional="show" widget="date" /> <field name="tag_ids" widget="many2many_tags" @@ -186,15 +190,15 @@ <xpath expr="//calendar" position="attributes"> <attribute name="mode" /> <attribute name="js_class" /> - <attribute name="date_start">date_start</attribute> - <attribute name="date_stop">date_end</attribute> + <attribute name="date_start">planned_date_start</attribute> + <attribute name="date_stop">planned_date_end</attribute> </xpath> <field name="date_deadline" position="attributes"> <attribute name="groups">base.group_no_one</attribute> </field> <field name="date_deadline" position="after"> - <field name="date_start" widget="date" /> - <field name="date_end" widget="date" /> + <field name="planned_date_start" widget="date" /> + <field name="planned_date_end" widget="date" /> </field> </field> </record> @@ -210,7 +214,7 @@ <attribute name="groups">base.group_no_one</attribute> </field> <field name="date_deadline" position="after"> - <field name="date_end" widget="remaining_days" /> + <field name="planned_date_end" widget="remaining_days" /> </field> </field> </record> diff --git a/views/project_task_planned_hour.xml b/views/project_task_planned_hour.xml index 98d99eb..19a81da 100644 --- a/views/project_task_planned_hour.xml +++ b/views/project_task_planned_hour.xml @@ -13,7 +13,9 @@ <group> <group> <field name="user_id" /> + <field name="department_id" /> <field name="task_id" /> + <field name="project_id" /> </group> <group> <field name="date_month" /> @@ -32,7 +34,9 @@ <tree string="Heures planifiées" create="0" edit="0" delete="0"> <field name="date_month" /> <field name="user_id" /> + <field name="department_id" /> <field name="task_id" /> + <field name="project_id" /> <field name="planned_hours" widget="float_time" /> </tree> </field> @@ -88,6 +92,30 @@ domain="[]" context="{'group_by':'date_month'}" /> + <filter + string="Utilisateur" + name="group_user" + domain="[]" + context="{'group_by':'user_id'}" + /> + <filter + string="Département" + name="group_department" + domain="[]" + context="{'group_by':'department_id'}" + /> + <filter + string="Tâche" + name="group_task" + domain="[]" + context="{'group_by':'task_id'}" + /> + <filter + string="Projet" + name="group_project" + domain="[]" + context="{'group_by':'project_id'}" + /> </group> </search> </field> -- GitLab