diff --git a/contract_timesheet_monitoring/README.rst b/contract_timesheet_monitoring/README.rst new file mode 100644 index 000000000..32f52fcbb --- /dev/null +++ b/contract_timesheet_monitoring/README.rst @@ -0,0 +1,67 @@ +============================= +Contract timesheet monitoring +============================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:00fb3cdc565442ffcd3351e97d0516ce6f4ceb55402981b183f7e717a1e23173 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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%2Faddons-lightgray.png?logo=github + :target: https://github.com/coopiteasy/addons/tree/16.0/contract_timesheet_monitoring + :alt: coopiteasy/addons + +|badge1| |badge2| |badge3| + +This module was developped for clients paying a subscription fee for +functional support. We wanted to invoice a fixed amount per period, but +invoice excess time. This module provide a way for the clients and the +service provider to monitor the time spent to compare it with the +quantity bought. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +The time spent for the current period is indicated in the contract line. +Time spent for past periods is indicated in the previous invoices. Note +: this might be confusing if the contract is configured to create +invoices at the begining of the period. + +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 to smash 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 SC + +Maintainers +----------- + +This module is part of the `coopiteasy/addons `_ project on GitHub. + +You are welcome to contribute. diff --git a/contract_timesheet_monitoring/__init__.py b/contract_timesheet_monitoring/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/contract_timesheet_monitoring/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/contract_timesheet_monitoring/__manifest__.py b/contract_timesheet_monitoring/__manifest__.py new file mode 100644 index 000000000..51f557f7d --- /dev/null +++ b/contract_timesheet_monitoring/__manifest__.py @@ -0,0 +1,20 @@ +# Copyright 2017 Tecnativa - Luis M. Ontalba +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +{ + "name": "Contract timesheet monitoring", + "summary": "Compute time spent on service contracts", + "version": "16.0.1.0.0", + "category": "Sales", + "author": "Coop IT Easy SC, Odoo Community Association (OCA)", + "website": "https://github.com/coopiteasy/addons", + "depends": ["contract", "hr_timesheet", "contract_invoice_start_end_dates"], + "data": [ + "views/contract.xml", + "views/account_move.xml", + "views/account_invoice_report.xml", + "views/contract_portal_templates.xml", + ], + "license": "AGPL-3", + "installable": True, +} diff --git a/contract_timesheet_monitoring/models/__init__.py b/contract_timesheet_monitoring/models/__init__.py new file mode 100644 index 000000000..1e0a868ab --- /dev/null +++ b/contract_timesheet_monitoring/models/__init__.py @@ -0,0 +1,3 @@ +from . import contract_line +from . import account_move_line +from . import account_analytic_account diff --git a/contract_timesheet_monitoring/models/account_analytic_account.py b/contract_timesheet_monitoring/models/account_analytic_account.py new file mode 100644 index 000000000..e58ec3a5f --- /dev/null +++ b/contract_timesheet_monitoring/models/account_analytic_account.py @@ -0,0 +1,19 @@ +from odoo import models + + +class AccountAnalyticAccount(models.Model): + _inherit = "account.analytic.account" + + def get_time_spent_for_period(self, start_date, end_date=None): + analytic_account_lines = self.line_ids + timesheets = analytic_account_lines.filtered( + # keep only timesheets + # ensure the uom is the same as the one configure for the project + # timesheets (hours or day) + lambda x: (x.encoding_uom_id == x.project_id.timesheet_encode_uom_id) + ) + if timesheets: + time_spent_on_account = timesheets.filtered( + lambda x: (x.date >= start_date) + ).mapped("unit_amount") + return sum(time_spent_on_account) diff --git a/contract_timesheet_monitoring/models/account_move_line.py b/contract_timesheet_monitoring/models/account_move_line.py new file mode 100644 index 000000000..3e3f2b737 --- /dev/null +++ b/contract_timesheet_monitoring/models/account_move_line.py @@ -0,0 +1,38 @@ +# Copyright 2016 Tecnativa - Carlos Dauden +# Copyright 2018 ACSONE SA/NV. +# Copyright 2020 Tecnativa - Pedro M. Baeza +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class AccountMoveLine(models.Model): + _inherit = "account.move.line" + + time_spent = fields.Float( + string="Time Spent this Period", compute="_compute_time_spent" + ) + + # this method is a duplicate from get_time_spent in contract.line + # but this is due to the fact that contract line tend to mimic account move lines + # it make sense to keep these methods separated. + def get_time_spent(self, analytic_distribution, start_date, end_date=None): + total_time_spent = 0 + for analytic_account, percentage in analytic_distribution.items(): + analytic_account = self.env["account.analytic.account"].browse( + int(analytic_account) + ) + time_spent_on_account = analytic_account.get_time_spent_for_period( + start_date + ) + total_time_spent += time_spent_on_account * percentage / 100 + return total_time_spent + + def _compute_time_spent(self): + for line in self: + if line.analytic_distribution and line.start_date: + line.time_spent = line.get_time_spent( + line.analytic_distribution, line.start_date, line.end_date + ) + else: + line.time_spent = False diff --git a/contract_timesheet_monitoring/models/contract_line.py b/contract_timesheet_monitoring/models/contract_line.py new file mode 100644 index 000000000..d36e39d98 --- /dev/null +++ b/contract_timesheet_monitoring/models/contract_line.py @@ -0,0 +1,34 @@ +from odoo import fields, models + + +class ContractLine(models.Model): + _inherit = "contract.line" + + time_spent = fields.Float( + string="Time Spent this Period", compute="_compute_time_spent" + ) + + # this method is a duplicate from get_time_spent in account.move.line + # but this is due to the fact that contract line tend to mimic account move lines + # it make sense to keep these methods separated. + def get_time_spent(self, analytic_distribution, start_date, end_date=None): + total_time_spent = 0 + for analytic_account, percentage in analytic_distribution.items(): + analytic_account = self.env["account.analytic.account"].browse( + int(analytic_account) + ) + time_spent_on_account = analytic_account.get_time_spent_for_period( + start_date + ) + total_time_spent += time_spent_on_account * percentage / 100 + return total_time_spent + + def _compute_time_spent(self): + for line in self: + if line.analytic_distribution: + period_start_date = line.last_date_invoiced or line.date_start + line.time_spent = line.get_time_spent( + line.analytic_distribution, period_start_date + ) + else: + line.time_spent = False diff --git a/contract_timesheet_monitoring/readme/DESCRIPTION.md b/contract_timesheet_monitoring/readme/DESCRIPTION.md new file mode 100644 index 000000000..7c07c3e31 --- /dev/null +++ b/contract_timesheet_monitoring/readme/DESCRIPTION.md @@ -0,0 +1,6 @@ + +This module was developped for clients paying a subscription fee for functional support. We wanted to invoice a fixed amount per period, but invoice excess time. +This module provide a way for the clients and the service provider to monitor the time spent to compare it with the quantity bought. + + + diff --git a/contract_timesheet_monitoring/readme/USAGE.md b/contract_timesheet_monitoring/readme/USAGE.md new file mode 100644 index 000000000..42dd081ad --- /dev/null +++ b/contract_timesheet_monitoring/readme/USAGE.md @@ -0,0 +1,2 @@ +The time spent for the current period is indicated in the contract line. Time spent for past periods is indicated in the previous invoices. +Note : this might be confusing if the contract is configured to create invoices at the begining of the period. diff --git a/contract_timesheet_monitoring/static/description/index.html b/contract_timesheet_monitoring/static/description/index.html new file mode 100644 index 000000000..821f15548 --- /dev/null +++ b/contract_timesheet_monitoring/static/description/index.html @@ -0,0 +1,420 @@ + + + + + +Contract timesheet monitoring + + + +
+

Contract timesheet monitoring

+ + +

Beta License: AGPL-3 coopiteasy/addons

+

This module was developped for clients paying a subscription fee for +functional support. We wanted to invoice a fixed amount per period, but +invoice excess time. This module provide a way for the clients and the +service provider to monitor the time spent to compare it with the +quantity bought.

+

Table of contents

+ +
+

Usage

+

The time spent for the current period is indicated in the contract line. +Time spent for past periods is indicated in the previous invoices. Note +: this might be confusing if the contract is configured to create +invoices at the begining of the period.

+
+
+

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 to smash 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 SC
  • +
+
+
+

Maintainers

+

This module is part of the coopiteasy/addons project on GitHub.

+

You are welcome to contribute.

+
+
+
+ + diff --git a/contract_timesheet_monitoring/tests/__init__.py b/contract_timesheet_monitoring/tests/__init__.py new file mode 100644 index 000000000..3c02082d6 --- /dev/null +++ b/contract_timesheet_monitoring/tests/__init__.py @@ -0,0 +1 @@ +from . import test_contract diff --git a/contract_timesheet_monitoring/tests/test_contract.py b/contract_timesheet_monitoring/tests/test_contract.py new file mode 100644 index 000000000..5b8bd587f --- /dev/null +++ b/contract_timesheet_monitoring/tests/test_contract.py @@ -0,0 +1,58 @@ +from odoo.addons.hr_timesheet.tests.test_timesheet import TestCommonTimesheet + + +class TestContractTimeSpent(TestCommonTimesheet): + @classmethod + def setUpClass(cls): + super(TestContractTimeSpent, cls).setUpClass() + + cls.contract = cls.env["contract.contract"].create( + { + "name": "Test Contract Service", + "partner_id": cls.partner.id, + # "pricelist_id": cls.partner.property_product_pricelist.id, + # "line_recurrence": True, + # "contract_type": "purchase", + "contract_line_ids": [ + ( + 0, + 0, + { + # "product_id": cls.product_1.id, + "name": "Services from #START# to #END#", + "analytic_distribution": {cls.analytic_account.id: 100}, + "quantity": 1, + "uom_id": cls.env.ref("uom.product_uom_hour").id, + "price_unit": 100, + "recurring_rule_type": "monthly", + "recurring_interval": 1, + "date_start": "2024-01-01", + "recurring_next_date": "2024-12-31", + }, + ) + ], + } + ) + cls.env["account.analytic.line"].create( + { + "project_id": cls.project_customer.id, + "task_id": cls.task1.id, + "name": "timesheet1", + "unit_amount": 1, + "date": "2024-02-01", + "employee_id": cls.empl_employee2.id, + } + ) + cls.env["account.analytic.line"].create( + { + "project_id": cls.project_customer.id, + "task_id": cls.task1.id, + "name": "timesheet2", + "unit_amount": 1, + "date": "2023-02-01", + "employee_id": cls.empl_employee2.id, + } + ) + + def test_time_spent_in_period(self): + self.assertEqual(self.contract.contract_line_ids[0].time_spent, 1) diff --git a/contract_timesheet_monitoring/views/account_invoice_report.xml b/contract_timesheet_monitoring/views/account_invoice_report.xml new file mode 100644 index 000000000..93597cacc --- /dev/null +++ b/contract_timesheet_monitoring/views/account_invoice_report.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/contract_timesheet_monitoring/views/account_move.xml b/contract_timesheet_monitoring/views/account_move.xml new file mode 100644 index 000000000..aacba2218 --- /dev/null +++ b/contract_timesheet_monitoring/views/account_move.xml @@ -0,0 +1,13 @@ + + + + account.move.form.view + account.move + + + + + + + + diff --git a/contract_timesheet_monitoring/views/contract.xml b/contract_timesheet_monitoring/views/contract.xml new file mode 100644 index 000000000..969fa2f81 --- /dev/null +++ b/contract_timesheet_monitoring/views/contract.xml @@ -0,0 +1,13 @@ + + + + contract.contract.form.view + contract.contract + + + + + + + + diff --git a/contract_timesheet_monitoring/views/contract_portal_templates.xml b/contract_timesheet_monitoring/views/contract_portal_templates.xml new file mode 100644 index 000000000..fbb742db1 --- /dev/null +++ b/contract_timesheet_monitoring/views/contract_portal_templates.xml @@ -0,0 +1,24 @@ + + + + + diff --git a/setup/contract_timesheet_monitoring/odoo/addons/contract_timesheet_monitoring b/setup/contract_timesheet_monitoring/odoo/addons/contract_timesheet_monitoring new file mode 120000 index 000000000..43b50ce5d --- /dev/null +++ b/setup/contract_timesheet_monitoring/odoo/addons/contract_timesheet_monitoring @@ -0,0 +1 @@ +../../../../contract_timesheet_monitoring \ No newline at end of file diff --git a/setup/contract_timesheet_monitoring/setup.py b/setup/contract_timesheet_monitoring/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/contract_timesheet_monitoring/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)