diff --git a/addons/hr_hourly_cost/__init__.py b/addons/hr_hourly_cost/__init__.py new file mode 100644 index 00000000000..d6210b1285d --- /dev/null +++ b/addons/hr_hourly_cost/__init__.py @@ -0,0 +1,3 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import models diff --git a/addons/hr_hourly_cost/__manifest__.py b/addons/hr_hourly_cost/__manifest__.py new file mode 100644 index 00000000000..1c6fda8db0c --- /dev/null +++ b/addons/hr_hourly_cost/__manifest__.py @@ -0,0 +1,22 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +{ + 'name': 'Employee Hourly Wage', + 'version': '1.0', + 'category': 'Services/Employee Hourly Cost', + 'summary': 'Employee Hourly Wage', + 'description': """ +This module assigns an hourly wage to employees to be used by other modules. +============================================================================ + + """, + 'depends': ['hr'], + 'data': [ + 'security/hr_hourly_cost_security.xml', + 'views/hr_employee_views.xml', + ], + 'demo': [ + 'data/hr_hourly_cost_demo.xml', + ], + 'license': 'LGPL-3', +} diff --git a/addons/hr_hourly_cost/data/hr_hourly_cost_demo.xml b/addons/hr_hourly_cost/data/hr_hourly_cost_demo.xml new file mode 100644 index 00000000000..b5acd41536d --- /dev/null +++ b/addons/hr_hourly_cost/data/hr_hourly_cost_demo.xml @@ -0,0 +1,84 @@ + + + + + 100 + + + + 35 + + + + 25 + + + + 45 + + + + 55 + + + + 15 + + + + 45 + + + + 20 + + + + 40 + + + + 45 + + + + 35 + + + + 25 + + + + 40 + + + + 50 + + + + 15 + + + + 10 + + + + 75 + + + + + 65 + + + + 50 + + + + 35 + + diff --git a/addons/hr_hourly_cost/models/__init__.py b/addons/hr_hourly_cost/models/__init__.py new file mode 100644 index 00000000000..5ce9975a708 --- /dev/null +++ b/addons/hr_hourly_cost/models/__init__.py @@ -0,0 +1,3 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import hr_employee diff --git a/addons/hr_timesheet/models/hr_employee.py b/addons/hr_hourly_cost/models/hr_employee.py similarity index 76% rename from addons/hr_timesheet/models/hr_employee.py rename to addons/hr_hourly_cost/models/hr_employee.py index f5a179fa87c..c23c87a7f60 100644 --- a/addons/hr_timesheet/models/hr_employee.py +++ b/addons/hr_hourly_cost/models/hr_employee.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo import fields, models @@ -7,6 +6,6 @@ from odoo import fields, models class HrEmployee(models.Model): _inherit = 'hr.employee' - timesheet_cost = fields.Monetary('Cost', currency_field='currency_id', + hourly_cost = fields.Monetary('Hourly Cost', currency_field='currency_id', groups="hr.group_hr_user", default=0.0) currency_id = fields.Many2one('res.currency', related='company_id.currency_id', readonly=True) diff --git a/addons/hr_hourly_cost/security/hr_hourly_cost_security.xml b/addons/hr_hourly_cost/security/hr_hourly_cost_security.xml new file mode 100644 index 00000000000..30318334958 --- /dev/null +++ b/addons/hr_hourly_cost/security/hr_hourly_cost_security.xml @@ -0,0 +1,11 @@ + + + + + Administrator + + + + + + diff --git a/addons/hr_hourly_cost/views/hr_employee_views.xml b/addons/hr_hourly_cost/views/hr_employee_views.xml new file mode 100644 index 00000000000..c9358efef6e --- /dev/null +++ b/addons/hr_hourly_cost/views/hr_employee_views.xml @@ -0,0 +1,21 @@ + + + + view.employee.form.inherit.hr.employee.hourly.wage + hr.employee + + + + + Application Settings + + + + + per hour + + + + + + diff --git a/addons/hr_timesheet/__manifest__.py b/addons/hr_timesheet/__manifest__.py index 741c15e59ca..6ef99c523c1 100644 --- a/addons/hr_timesheet/__manifest__.py +++ b/addons/hr_timesheet/__manifest__.py @@ -20,7 +20,7 @@ It is completely integrated with the cost accounting module. It allows you to se up a management by affair. """, 'website': 'https://www.odoo.com/app/timesheet', - 'depends': ['hr', 'analytic', 'project', 'uom'], + 'depends': ['hr', 'hr_hourly_cost', 'analytic', 'project', 'uom'], 'data': [ 'security/hr_timesheet_security.xml', 'security/ir.model.access.csv', diff --git a/addons/hr_timesheet/data/hr_timesheet_demo.xml b/addons/hr_timesheet/data/hr_timesheet_demo.xml index d1bbf0c3f19..021f1e170a8 100644 --- a/addons/hr_timesheet/data/hr_timesheet_demo.xml +++ b/addons/hr_timesheet/data/hr_timesheet_demo.xml @@ -6,88 +6,6 @@ - - - 100 - - - - 35 - - - - 25 - - - - 45 - - - - 55 - - - - 15 - - - - 45 - - - - 20 - - - - 40 - - - - 45 - - - - 35 - - - - 25 - - - - 40 - - - - 50 - - - - 15 - - - - 10 - - - - 75 - - - - - 65 - - - - 50 - - - - 35 - - diff --git a/addons/hr_timesheet/models/__init__.py b/addons/hr_timesheet/models/__init__.py index dd55d6e5b98..7cf0e88d408 100644 --- a/addons/hr_timesheet/models/__init__.py +++ b/addons/hr_timesheet/models/__init__.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from . import hr_employee from . import hr_timesheet from . import ir_http from . import ir_ui_menu diff --git a/addons/hr_timesheet/models/hr_timesheet.py b/addons/hr_timesheet/models/hr_timesheet.py index 250a3a82565..83a6e23327d 100644 --- a/addons/hr_timesheet/models/hr_timesheet.py +++ b/addons/hr_timesheet/models/hr_timesheet.py @@ -284,7 +284,7 @@ class AccountAnalyticLine(models.Model): # (re)compute the amount (depending on unit_amount, employee_id for the cost, and account_id for currency) if any(field_name in values for field_name in ['unit_amount', 'employee_id', 'account_id']): for timesheet in sudo_self: - cost = timesheet._employee_timesheet_cost() + cost = timesheet._hourly_cost() amount = -timesheet.unit_amount * cost amount_converted = timesheet.employee_id.currency_id._convert( amount, timesheet.account_id.currency_id or timesheet.currency_id, self.env.company, timesheet.date) @@ -306,9 +306,9 @@ class AccountAnalyticLine(models.Model): def _get_timesheet_time_day(self): return self._convert_hours_to_days(self.unit_amount) - def _employee_timesheet_cost(self): + def _hourly_cost(self): self.ensure_one() - return self.employee_id.timesheet_cost or 0.0 + return self.employee_id.hourly_cost or 0.0 def _get_report_base_filename(self): task_ids = self.task_id diff --git a/addons/hr_timesheet/security/hr_timesheet_security.xml b/addons/hr_timesheet/security/hr_timesheet_security.xml index 9f105179167..63135d39cb7 100644 --- a/addons/hr_timesheet/security/hr_timesheet_security.xml +++ b/addons/hr_timesheet/security/hr_timesheet_security.xml @@ -22,7 +22,8 @@ Administrator - + diff --git a/addons/hr_timesheet/tests/test_timesheet.py b/addons/hr_timesheet/tests/test_timesheet.py index 8de9c1a40fc..4df8cf990bf 100644 --- a/addons/hr_timesheet/tests/test_timesheet.py +++ b/addons/hr_timesheet/tests/test_timesheet.py @@ -253,8 +253,8 @@ class TestTimesheet(TestCommonTimesheet): def test_recompute_amount_for_multiple_timesheets(self): """ Check that amount is recomputed correctly when setting unit_amount for multiple timesheets at once. """ Timesheet = self.env['account.analytic.line'] - self.empl_employee.timesheet_cost = 5.0 - self.empl_employee2.timesheet_cost = 6.0 + self.empl_employee.hourly_cost = 5.0 + self.empl_employee2.hourly_cost = 6.0 # create a timesheet for each employee timesheet_1 = Timesheet.with_user(self.user_employee).create({ 'project_id': self.project_customer.id, diff --git a/addons/hr_timesheet/views/hr_views.xml b/addons/hr_timesheet/views/hr_views.xml index 7b650c588e2..9ea0e8cdb2e 100644 --- a/addons/hr_timesheet/views/hr_views.xml +++ b/addons/hr_timesheet/views/hr_views.xml @@ -30,19 +30,9 @@ hr.employee.form.timesheet hr.employee - + - - Application Settings - - - - - per hour - - - diff --git a/addons/hr_timesheet_attendance/report/hr_timesheet_attendance_report.py b/addons/hr_timesheet_attendance/report/hr_timesheet_attendance_report.py index e38f663deb2..6e4e067c73e 100644 --- a/addons/hr_timesheet_attendance/report/hr_timesheet_attendance_report.py +++ b/addons/hr_timesheet_attendance/report/hr_timesheet_attendance_report.py @@ -36,7 +36,7 @@ class TimesheetAttendance(models.Model): FROM ( SELECT -hr_attendance.id AS id, - hr_employee.timesheet_cost AS emp_cost, + hr_employee.hourly_cost AS emp_cost, resource_resource.user_id AS user_id, hr_attendance.worked_hours AS attendance, NULL AS timesheet, @@ -48,7 +48,7 @@ class TimesheetAttendance(models.Model): UNION ALL SELECT ts.id AS id, - hr_employee.timesheet_cost AS emp_cost, + hr_employee.hourly_cost AS emp_cost, ts.user_id AS user_id, NULL AS attendance, ts.unit_amount AS timesheet, diff --git a/addons/sale_timesheet/models/account.py b/addons/sale_timesheet/models/account.py index b204126d6f6..6f9755a5232 100644 --- a/addons/sale_timesheet/models/account.py +++ b/addons/sale_timesheet/models/account.py @@ -156,9 +156,9 @@ class AccountAnalyticLine(models.Model): self.ensure_one() return self.env['project.sale.line.employee.map'].search([('project_id', '=', self.project_id.id), ('employee_id', '=', self.employee_id.id)]) - def _employee_timesheet_cost(self): + def _hourly_cost(self): if self.project_id.pricing_type == 'employee_rate': mapping_entry = self._get_employee_mapping_entry() if mapping_entry: return mapping_entry.cost - return super()._employee_timesheet_cost() + return super()._hourly_cost() diff --git a/addons/sale_timesheet/models/project_sale_line_employee_map.py b/addons/sale_timesheet/models/project_sale_line_employee_map.py index f2fa4b8fafe..039978ece89 100644 --- a/addons/sale_timesheet/models/project_sale_line_employee_map.py +++ b/addons/sale_timesheet/models/project_sale_line_employee_map.py @@ -24,7 +24,7 @@ class ProjectProductEmployeeMap(models.Model): price_unit = fields.Float("Unit Price", compute='_compute_price_unit', store=True, readonly=True) currency_id = fields.Many2one('res.currency', string="Currency", compute='_compute_currency_id', store=True, readonly=False) cost = fields.Monetary(currency_field='cost_currency_id', compute='_compute_cost', store=True, readonly=False, - help="This cost overrides the employee's default timesheet cost in employee's HR Settings") + help="This cost overrides the employee's default employee hourly wage in employee's HR Settings") display_cost = fields.Monetary(currency_field='cost_currency_id', compute="_compute_display_cost", inverse="_inverse_display_cost", string="Hourly Cost") cost_currency_id = fields.Many2one('res.currency', string="Cost Currency", related='employee_id.currency_id', readonly=True) is_cost_changed = fields.Boolean('Is Cost Manually Changed', compute='_compute_is_cost_changed', store=True) @@ -55,12 +55,12 @@ class ProjectProductEmployeeMap(models.Model): for line in self: line.currency_id = line.sale_line_id.currency_id if line.sale_line_id else False - @api.depends('employee_id.timesheet_cost') + @api.depends('employee_id.hourly_cost') def _compute_cost(self): self.env.remove_to_compute(self._fields['is_cost_changed'], self) for map_entry in self: if not map_entry.is_cost_changed: - map_entry.cost = map_entry.employee_id.timesheet_cost or 0.0 + map_entry.cost = map_entry.employee_id.hourly_cost or 0.0 def _get_working_hours_per_calendar(self, is_uom_day=False): resource_calendar_per_hours = {} @@ -104,7 +104,7 @@ class ProjectProductEmployeeMap(models.Model): @api.depends('cost') def _compute_is_cost_changed(self): for map_entry in self: - map_entry.is_cost_changed = map_entry.employee_id and map_entry.cost != map_entry.employee_id.timesheet_cost + map_entry.is_cost_changed = map_entry.employee_id and map_entry.cost != map_entry.employee_id.hourly_cost @api.model_create_multi def create(self, vals_list): diff --git a/addons/sale_timesheet/tests/common.py b/addons/sale_timesheet/tests/common.py index d8c1971d8fc..3bee18fbce5 100644 --- a/addons/sale_timesheet/tests/common.py +++ b/addons/sale_timesheet/tests/common.py @@ -34,23 +34,23 @@ class TestCommonSaleTimesheet(TestSaleProjectCommon): cls.employee_user = cls.env['hr.employee'].create({ 'name': 'Employee User', - 'timesheet_cost': 15, + 'hourly_cost': 15, }) cls.employee_manager = cls.env['hr.employee'].create({ 'name': 'Employee Manager', - 'timesheet_cost': 45, + 'hourly_cost': 45, }) cls.employee_company_B = cls.env['hr.employee'].create({ 'name': 'Gregor Clegane', 'user_id': cls.user_employee_company_B.id, - 'timesheet_cost': 15, + 'hourly_cost': 15, }) cls.manager_company_B = cls.env['hr.employee'].create({ 'name': 'Cersei Lannister', 'user_id': cls.user_manager_company_B.id, - 'timesheet_cost': 45, + 'hourly_cost': 45, }) # Account and project diff --git a/addons/sale_timesheet/tests/test_project.py b/addons/sale_timesheet/tests/test_project.py index 6819e85dd24..1a69b1574d5 100644 --- a/addons/sale_timesheet/tests/test_project.py +++ b/addons/sale_timesheet/tests/test_project.py @@ -112,13 +112,13 @@ class TestProject(TestCommonSaleTimesheet): 'sale_line_id': self.project_global.sale_line_id.id, }) self.assertFalse(employee_mapping.is_cost_changed) - self.assertEqual(employee_mapping.cost, self.employee_manager.timesheet_cost) + self.assertEqual(employee_mapping.cost, self.employee_manager.hourly_cost) employee_mapping.cost = 5 self.assertTrue(employee_mapping.is_cost_changed) self.assertEqual(employee_mapping.cost, 5) - self.employee_manager.timesheet_cost = 80 + self.employee_manager.hourly_cost = 80 self.assertTrue(employee_mapping.is_cost_changed) self.assertEqual(employee_mapping.cost, 5) @@ -126,9 +126,9 @@ class TestProject(TestCommonSaleTimesheet): self.assertTrue(employee_mapping.is_cost_changed) self.assertEqual(employee_mapping.cost, 5) - employee_mapping.cost = self.employee_user.timesheet_cost + employee_mapping.cost = self.employee_user.hourly_cost employee_mapping.employee_id = self.employee_company_B - self.assertEqual(employee_mapping.cost, self.employee_company_B.timesheet_cost) + self.assertEqual(employee_mapping.cost, self.employee_company_B.hourly_cost) def test_analytic_account_balance(self): """ @@ -150,7 +150,7 @@ class TestProject(TestCommonSaleTimesheet): }) unit_amount = 6 - expected_analytic_account_balance = - self.employee_user.timesheet_cost * unit_amount + expected_analytic_account_balance = - self.employee_user.hourly_cost * unit_amount self.project_global.write({ 'sale_line_id': sale_line.id, 'sale_line_employee_ids': [ diff --git a/addons/sale_timesheet/tests/test_project_billing.py b/addons/sale_timesheet/tests/test_project_billing.py index 7765541c674..84c15199405 100644 --- a/addons/sale_timesheet/tests/test_project_billing.py +++ b/addons/sale_timesheet/tests/test_project_billing.py @@ -16,7 +16,7 @@ class TestProjectBilling(TestCommonSaleTimesheet): # set up cls.employee_tde = cls.env['hr.employee'].create({ 'name': 'Employee TDE', - 'timesheet_cost': 42, + 'hourly_cost': 42, }) cls.partner_2 = cls.env['res.partner'].create({ diff --git a/addons/sale_timesheet/tests/test_project_profitability.py b/addons/sale_timesheet/tests/test_project_profitability.py index 7ec867e8319..1d98465dbc3 100644 --- a/addons/sale_timesheet/tests/test_project_profitability.py +++ b/addons/sale_timesheet/tests/test_project_profitability.py @@ -95,13 +95,13 @@ class TestSaleTimesheetProjectProfitability(TestCommonSaleTimesheet): { 'id': 'billable_manual', 'sequence': sequence_per_invoice_type['billable_manual'], - 'billed': (timesheet1.unit_amount + timesheet2.unit_amount) * -self.employee_user.timesheet_cost, + 'billed': (timesheet1.unit_amount + timesheet2.unit_amount) * -self.employee_user.hourly_cost, 'to_bill': 0.0, }, ], 'total': { 'to_bill': 0.0, - 'billed': (timesheet1.unit_amount + timesheet2.unit_amount) * -self.employee_user.timesheet_cost + 'billed': (timesheet1.unit_amount + timesheet2.unit_amount) * -self.employee_user.hourly_cost }, }, } @@ -129,21 +129,21 @@ class TestSaleTimesheetProjectProfitability(TestCommonSaleTimesheet): { 'id': 'billable_manual', 'sequence': sequence_per_invoice_type['billable_manual'], - 'billed': (timesheet1.unit_amount + timesheet2.unit_amount) * -self.employee_user.timesheet_cost, + 'billed': (timesheet1.unit_amount + timesheet2.unit_amount) * -self.employee_user.hourly_cost, 'to_bill': 0.0, }, { 'id': 'non_billable', 'sequence': sequence_per_invoice_type['non_billable'], - 'billed': timesheet3.unit_amount * -self.employee_manager.timesheet_cost, + 'billed': timesheet3.unit_amount * -self.employee_manager.hourly_cost, 'to_bill': 0.0, }, ], 'total': { 'to_bill': 0.0, 'billed': - (timesheet1.unit_amount + timesheet2.unit_amount) * -self.employee_user.timesheet_cost - + timesheet3.unit_amount * -self.employee_manager.timesheet_cost, + (timesheet1.unit_amount + timesheet2.unit_amount) * -self.employee_user.hourly_cost + + timesheet3.unit_amount * -self.employee_manager.hourly_cost, }, }, }, @@ -172,21 +172,21 @@ class TestSaleTimesheetProjectProfitability(TestCommonSaleTimesheet): { 'id': 'billable_time', 'sequence': sequence_per_invoice_type['billable_time'], - 'billed': (timesheet1.unit_amount + timesheet2.unit_amount) * -self.employee_user.timesheet_cost, + 'billed': (timesheet1.unit_amount + timesheet2.unit_amount) * -self.employee_user.hourly_cost, 'to_bill': 0.0, }, { 'id': 'non_billable', 'sequence': sequence_per_invoice_type['non_billable'], - 'billed': timesheet3.unit_amount * -self.employee_manager.timesheet_cost, + 'billed': timesheet3.unit_amount * -self.employee_manager.hourly_cost, 'to_bill': 0.0, }, ], 'total': { 'to_bill': 0.0, 'billed': - (timesheet1.unit_amount + timesheet2.unit_amount) * -self.employee_user.timesheet_cost - + timesheet3.unit_amount * -self.employee_manager.timesheet_cost, + (timesheet1.unit_amount + timesheet2.unit_amount) * -self.employee_user.hourly_cost + + timesheet3.unit_amount * -self.employee_manager.hourly_cost, }, }, },