-
-
Notifications
You must be signed in to change notification settings - Fork 799
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[FIX] project_consumable: take care timesheet attendance report
- Loading branch information
Showing
8 changed files
with
186 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
from . import hr_timesheet_attendance_report | ||
from . import timesheets_analysis_report |
64 changes: 64 additions & 0 deletions
64
project_consumable/report/hr_timesheet_attendance_report.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
# Copyright 2025 - Pierre Verkest | ||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). | ||
|
||
from odoo import models, tools | ||
|
||
|
||
class TimesheetAttendance(models.Model): | ||
_inherit = "hr.timesheet.attendance.report" | ||
|
||
def init(self): | ||
tools.drop_view_if_exists(self.env.cr, self._table) | ||
self._cr.execute( | ||
"""CREATE OR REPLACE VIEW %s AS ( | ||
SELECT | ||
max(id) AS id, | ||
t.employee_id, | ||
t.date, | ||
t.company_id, | ||
coalesce(sum(t.attendance), 0) AS total_attendance, | ||
coalesce(sum(t.timesheet), 0) AS total_timesheet, | ||
coalesce( | ||
sum(t.attendance), 0) - coalesce(sum(t.timesheet), | ||
0 | ||
) as total_difference, | ||
NULLIF(sum(t.timesheet) * t.emp_cost, 0) as timesheets_cost, | ||
NULLIF(sum(t.attendance) * t.emp_cost, 0) as attendance_cost, | ||
NULLIF( | ||
(coalesce(sum(t.attendance), 0) - coalesce(sum(t.timesheet), 0)) | ||
* t.emp_cost, | ||
0 | ||
) as cost_difference | ||
FROM ( | ||
SELECT | ||
-hr_attendance.id AS id, | ||
hr_employee.hourly_cost AS emp_cost, | ||
hr_attendance.employee_id AS employee_id, | ||
hr_attendance.worked_hours AS attendance, | ||
NULL AS timesheet, | ||
hr_attendance.check_in::date AS date, | ||
hr_employee.company_id as company_id | ||
FROM hr_attendance | ||
LEFT JOIN hr_employee ON hr_employee.id = hr_attendance.employee_id | ||
UNION ALL | ||
SELECT | ||
ts.id AS id, | ||
hr_employee.hourly_cost AS emp_cost, | ||
ts.employee_id AS employee_id, | ||
NULL AS attendance, | ||
ts.unit_amount AS timesheet, | ||
ts.date AS date, | ||
ts.company_id AS company_id | ||
FROM account_analytic_line AS ts | ||
LEFT JOIN hr_employee ON hr_employee.id = ts.employee_id | ||
WHERE ts.project_id IS NOT NULL | ||
-- change start | ||
AND ts.product_id IS NULL | ||
-- change end | ||
) AS t | ||
GROUP BY t.employee_id, t.date, t.company_id, t.emp_cost | ||
ORDER BY t.date | ||
) | ||
""" | ||
% self._table | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
# Copyright 2021 - Pierre Verkest | ||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). | ||
|
||
from . import test_project_consumable_report | ||
from . import test_project_consumable |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
117 changes: 117 additions & 0 deletions
117
project_consumable/tests/test_project_consumable_report.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
# Copyright 2021 - Pierre Verkest | ||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). | ||
from datetime import datetime | ||
|
||
from odoo.tests import TransactionCase | ||
|
||
|
||
class TestProjectConsumableReporting(TransactionCase): | ||
@classmethod | ||
def setUpClass(cls): | ||
super().setUpClass() | ||
cls.product = cls.env.ref("project_consumable.product_coffee_capsule") | ||
default_plan_id = cls.env["account.analytic.plan"].search([], limit=1) | ||
cls.analytic_account = cls.env["account.analytic.account"].create( | ||
{ | ||
"name": "Test", | ||
"plan_id": default_plan_id.id, | ||
"company_id": cls.env.company.id, | ||
} | ||
) | ||
cls.project = cls.env["project.project"].create( | ||
{ | ||
"name": "Test", | ||
"analytic_account_id": cls.analytic_account.id, | ||
"company_id": cls.env.company.id, | ||
} | ||
) | ||
cls.user_demo = cls.env.ref("base.user_demo") | ||
cls.employee = cls.user_demo.employee_id | ||
|
||
cls.env["hr.attendance"].create( | ||
{ | ||
"employee_id": cls.employee.id, | ||
"check_in": datetime(2022, 2, 9, 8, 0), # Wednesday | ||
"check_out": datetime(2022, 2, 9, 16, 0), | ||
} | ||
) | ||
|
||
def _prepare_consumable_line_data(self, **kwargs): | ||
data = { | ||
"name": "collect test material", | ||
"project_id": self.project.id, | ||
"account_id": None, | ||
"product_id": self.product.id, | ||
"unit_amount": 6, | ||
"employee_id": self.employee.id, | ||
"product_uom_id": self.product.uom_id.id, | ||
"task_id": None, | ||
"amount": None, | ||
"date": None, | ||
"partner_id": None, | ||
} | ||
data.update(**kwargs) | ||
return {k: v for k, v in data.items() if v is not None} | ||
|
||
def test_timesheet_analysis_report_exclude_consumable(self): | ||
self.env["account.analytic.line"].create( | ||
{ | ||
"name": "test timesheet", | ||
"project_id": self.project.id, | ||
"unit_amount": 3, | ||
"employee_id": self.employee.id, | ||
} | ||
) | ||
self.env["account.analytic.line"].create( | ||
self._prepare_consumable_line_data( | ||
unit_amount=7, | ||
product_uom_id=self.env.ref( | ||
"project_consumable.uom_cat_coffee_capsule_box_10" | ||
).id, | ||
) | ||
) | ||
analysis = self.env["timesheets.analysis.report"].search( | ||
[ | ||
("project_id", "=", self.project.id), | ||
("employee_id", "=", self.employee.id), | ||
] | ||
) | ||
self.assertEqual(len(analysis), 1) | ||
self.assertEqual(analysis.unit_amount, 3) | ||
|
||
def test_timesheet_attendance_report_with_consumable(self): | ||
self.env["account.analytic.line"].with_user(self.user_demo).create( | ||
{ | ||
"name": "Test timesheet 1", | ||
"project_id": self.project.id, | ||
"unit_amount": 6.0, | ||
"date": datetime(2022, 2, 9), | ||
} | ||
) | ||
self.env["account.analytic.line"].create( | ||
self._prepare_consumable_line_data( | ||
unit_amount=7, | ||
product_uom_id=self.env.ref( | ||
"project_consumable.uom_cat_coffee_capsule_box_10" | ||
).id, | ||
employee_id=self.employee.id, | ||
date=datetime(2022, 2, 9), | ||
) | ||
) | ||
total_timesheet, total_attendance = self.env[ | ||
"hr.timesheet.attendance.report" | ||
]._read_group( | ||
[ | ||
("employee_id", "=", self.employee.id), | ||
("date", ">=", datetime(2022, 2, 9, 8, 0)), | ||
("date", "<=", datetime(2022, 2, 9, 16, 0)), | ||
], | ||
aggregates=["total_timesheet:sum", "total_attendance:sum"], | ||
)[0] | ||
self.assertEqual( | ||
total_timesheet, 6.0, "Total timesheet in report should be 4.0" | ||
) | ||
self.assertEqual( | ||
total_attendance, 7.0, "Total attendance in report should be 8.0" | ||
) | ||
self.assertEqual(total_attendance - total_timesheet, 1) |