diff --git a/addons/hr_timesheet/__manifest__.py b/addons/hr_timesheet/__manifest__.py index efedc7384f0..177797d13a0 100644 --- a/addons/hr_timesheet/__manifest__.py +++ b/addons/hr_timesheet/__manifest__.py @@ -31,6 +31,7 @@ up a management by affair. 'report/report_timesheet_templates.xml', 'hr_timesheet_installer.xml', 'hr_dashboard.xml', + 'views/hr_views.xml', ], 'demo': [ 'demo/hr_timesheet_demo.xml', diff --git a/addons/hr_timesheet/hr_timesheet.py b/addons/hr_timesheet/hr_timesheet.py index c8b7241e5c5..3c3cdecff9e 100644 --- a/addons/hr_timesheet/hr_timesheet.py +++ b/addons/hr_timesheet/hr_timesheet.py @@ -7,24 +7,38 @@ from odoo import api, fields, models class AccountAnalyticLine(models.Model): _inherit = 'account.analytic.line' + def _default_employee_id(self): + return self.env.user.employee_ids[0] + task_id = fields.Many2one('project.task', 'Task') project_id = fields.Many2one('project.project', 'Project', domain=[('allow_timesheets', '=', True)]) - department_id = fields.Many2one('hr.department', "Department", related='user_id.employee_ids.department_id', store=True, readonly=True) + + employee_id = fields.Many2one('hr.employee', "Employee", default=_default_employee_id) + department_id = fields.Many2one('hr.department', "Department", related='employee_id.department_id', store=True, readonly=True) @api.onchange('project_id') def onchange_project_id(self): self.task_id = False + @api.onchange('employee_id') + def _onchange_employee_id(self): + self.user_id = self.employee_id.user_id + @api.model def create(self, vals): - if vals.get('project_id'): - project = self.env['project.project'].browse(vals.get('project_id')) - vals['account_id'] = project.analytic_account_id.id + vals = self._update_timesheet_values(vals) return super(AccountAnalyticLine, self).create(vals) @api.multi def write(self, vals): + vals = self._update_timesheet_values(vals) + return super(AccountAnalyticLine, self).write(vals) + + def _update_timesheet_values(self, vals): if vals.get('project_id'): project = self.env['project.project'].browse(vals.get('project_id')) vals['account_id'] = project.analytic_account_id.id - return super(AccountAnalyticLine, self).write(vals) + if vals.get('employee_id'): + employee = self.env['hr.employee'].browse(vals['employee_id']) + vals['user_id'] = employee.user_id.id + return vals diff --git a/addons/hr_timesheet/hr_timesheet_view.xml b/addons/hr_timesheet/hr_timesheet_view.xml index 95f3d8617d0..d2bac3c2ac0 100644 --- a/addons/hr_timesheet/hr_timesheet_view.xml +++ b/addons/hr_timesheet/hr_timesheet_view.xml @@ -37,7 +37,8 @@ 10 - + + @@ -47,7 +48,7 @@ account.analytic.line - + @@ -89,7 +90,8 @@ 10 - + + @@ -98,7 +100,7 @@ account.analytic.line.calendar account.analytic.line - + @@ -111,7 +113,7 @@ - + @@ -130,7 +132,7 @@ ('date', '<=', (context_today() + relativedelta(months=1, day=1, days=-1)).strftime('%Y-%m-%d')), ]"/> - + @@ -191,7 +193,7 @@ account.analytic.line [('project_id', '!=', False)] - {'search_default_week':1, 'search_default_groupby_user':1} + {'search_default_week':1, 'search_default_groupby_employee':1} diff --git a/addons/hr_timesheet/project_timesheet_view.xml b/addons/hr_timesheet/project_timesheet_view.xml index b7f1b87bab4..f799cc5b76e 100644 --- a/addons/hr_timesheet/project_timesheet_view.xml +++ b/addons/hr_timesheet/project_timesheet_view.xml @@ -102,7 +102,8 @@ - + + diff --git a/addons/hr_timesheet/views/hr_views.xml b/addons/hr_timesheet/views/hr_views.xml new file mode 100644 index 00000000000..6d500f22fa4 --- /dev/null +++ b/addons/hr_timesheet/views/hr_views.xml @@ -0,0 +1,36 @@ + + + + + Timesheets + account.analytic.line + + [('project_id', '!=', False)] + { + 'search_default_month':1, + 'search_default_employee_id': [active_id], + 'default_employee_id': active_id + } + + + + hr.employee.form.timesheet + hr.employee + + + + + + + + + + + + + + diff --git a/addons/sale_timesheet/models/account.py b/addons/sale_timesheet/models/account.py index 812b9b09b1b..7f319ead699 100644 --- a/addons/sale_timesheet/models/account.py +++ b/addons/sale_timesheet/models/account.py @@ -37,16 +37,28 @@ class AccountAnalyticLine(models.Model): if values.get('amount'): return {} unit_amount = values.get('unit_amount', 0.0) or self.unit_amount - user_id = values.get('user_id') or self.user_id.id or self._default_user() - user = self.env['res.users'].browse([user_id]) - emp = self.env['hr.employee'].search([('user_id', '=', user_id)], limit=1) - cost = emp and emp.timesheet_cost or 0.0 - uom = (emp or user).company_id.project_time_mode_id - # Nominal employee cost = 1 * company project UoM (project_time_mode_id) + + # find the employee + employee_id = values.get('employee_id') or self.employee_id.id or self._default_employee_id().id + employee = self.env['hr.employee'].browse(employee_id) if employee_id else self.env['hr.employee'] + if not employee: + user_id = values.get('user_id') or self.user_id.id or self._default_user() + employee = self.env['hr.employee'].search([('user_id', '=', user_id)], limit=1) + + # find the analytic account + account_id = values.get('account_id') or self.account_id.id + analytic_account = self.env['account.analytic.account'].browse(account_id) if account_id else self.env['account.analytic.account'] + + # convert employee cost into timesheet (analytic account) currency + uom = employee.company_id.project_time_mode_id + cost = employee.timesheet_cost or 0.0 + amount = -unit_amount * cost + amount_converted = employee.currency_id.compute(amount, analytic_account.currency_id) + return { - 'amount': -unit_amount * cost, + 'amount': amount_converted, 'product_uom_id': uom.id, - 'account_id': values.get('account_id') or self.account_id.id or emp.account_id.id, + 'account_id': account_id, } return {} diff --git a/addons/sale_timesheet/models/hr.py b/addons/sale_timesheet/models/hr.py index 5964b77ab04..a9f94f57bb9 100644 --- a/addons/sale_timesheet/models/hr.py +++ b/addons/sale_timesheet/models/hr.py @@ -8,5 +8,5 @@ class HrEmployee(models.Model): _inherit = 'hr.employee' # FIXME: this field should be in module hr_timesheet, not sale_timesheet - timesheet_cost = fields.Float('Timesheet Cost', default=0.0) - account_id = fields.Many2one('account.account', string='Account') + timesheet_cost = fields.Monetary('Timesheet Cost', currency_field='currency_id', default=0.0) + currency_id = fields.Many2one('res.currency', related='company_id.currency_id', readonly=True) diff --git a/addons/sale_timesheet/tests/test_sale_timesheet.py b/addons/sale_timesheet/tests/test_sale_timesheet.py index 309650aee2f..d75991411cb 100644 --- a/addons/sale_timesheet/tests/test_sale_timesheet.py +++ b/addons/sale_timesheet/tests/test_sale_timesheet.py @@ -6,6 +6,19 @@ from odoo.addons.sale.tests.test_sale_common import TestSale class TestSaleTimesheet(TestSale): + + def setUp(self): + super(TestSaleTimesheet, self).setUp() + # link hr employee to res.users of sale tests + self.employee_user = self.env['hr.employee'].create({ + 'name': 'Employee User', + 'user_id': self.user.id, + }) + self.employee_manager = self.env['hr.employee'].create({ + 'name': 'Employee Manager', + 'user_id': self.manager.id, + }) + def test_timesheet_order(self): """ Test timesheet invoicing with 'invoice on order' timetracked products """ # intial so @@ -26,7 +39,7 @@ class TestSaleTimesheet(TestSale): 'name': 'Test Line', 'project_id': so.project_project_id.id, 'unit_amount': 10.5, - 'user_id': self.manager.id, + 'employee_id': self.employee_user.id, }) self.assertEqual(so.order_line.qty_delivered, 10.5, 'Sale Timesheet: timesheet does not increase delivered quantity on so line') self.assertEqual(so.invoice_status, 'invoiced', 'Sale Timesheet: "invoice on order" timesheets should not modify the invoice_status of the so') @@ -35,7 +48,7 @@ class TestSaleTimesheet(TestSale): 'name': 'Test Line', 'project_id': so.project_project_id.id, 'unit_amount': 39.5, - 'user_id': self.user.id, + 'employee_id': self.employee_user.id, }) self.assertEqual(so.order_line.qty_delivered, 50, 'Sale Timesheet: timesheet does not increase delivered quantity on so line') self.assertEqual(so.invoice_status, 'invoiced', 'Sale Timesheet: "invoice on order" timesheets should not modify the invoice_status of the so') @@ -44,7 +57,7 @@ class TestSaleTimesheet(TestSale): 'name': 'Test Line', 'project_id': so.project_project_id.id, 'unit_amount': 10, - 'user_id': self.user.id, + 'employee_id': self.employee_user.id, }) self.assertEqual(so.order_line.qty_delivered, 60, 'Sale Timesheet: timesheet does not increase delivered quantity on so line') self.assertEqual(so.invoice_status, 'upselling', 'Sale Timesheet: "invoice on order" timesheets should not modify the invoice_status of the so') @@ -69,7 +82,7 @@ class TestSaleTimesheet(TestSale): 'name': 'Test Line', 'project_id': so.project_project_id.id, 'unit_amount': 10.5, - 'user_id': self.manager.id, + 'employee_id': self.employee_manager.id, }) self.assertEqual(so.invoice_status, 'to invoice', 'Sale Timesheet: "invoice on delivery" timesheets should set the so in "to invoice" status when logged') inv_id = so.action_invoice_create() @@ -80,7 +93,7 @@ class TestSaleTimesheet(TestSale): 'name': 'Test Line', 'project_id': so.project_project_id.id, 'unit_amount': 39.5, - 'user_id': self.user.id, + 'employee_id': self.employee_user.id, }) self.assertEqual(so.invoice_status, 'to invoice', 'Sale Timesheet: "invoice on delivery" timesheets should not modify the invoice_status of the so') so.action_invoice_create() @@ -90,7 +103,7 @@ class TestSaleTimesheet(TestSale): 'name': 'Test Line', 'project_id': so.project_project_id.id, 'unit_amount': 10, - 'user_id': self.user.id, + 'employee_id': self.employee_user.id, }) self.assertEqual(so.invoice_status, 'to invoice', 'Sale Timesheet: supplementary timesheets do not change the status of the SO') @@ -113,7 +126,7 @@ class TestSaleTimesheet(TestSale): 'name': 'Test Line', 'project_id': so.project_project_id.id, 'unit_amount': 16, - 'user_id': self.manager.id, + 'employee_id': self.employee_manager.id, }) self.assertEqual(so.order_line.qty_delivered, 2, 'Sale: uom conversion of timesheets is wrong') @@ -121,7 +134,7 @@ class TestSaleTimesheet(TestSale): 'name': 'Test Line', 'project_id': so.project_project_id.id, 'unit_amount': 24, - 'user_id': self.user.id, + 'employee_id': self.employee_user.id, }) so.action_invoice_create() self.assertEqual(so.invoice_status, 'invoiced', 'Sale Timesheet: "invoice on delivery" timesheets should not modify the invoice_status of the so') diff --git a/addons/sale_timesheet/views/hr_views.xml b/addons/sale_timesheet/views/hr_views.xml index 7b17e2dc67e..ce032cc01e0 100644 --- a/addons/sale_timesheet/views/hr_views.xml +++ b/addons/sale_timesheet/views/hr_views.xml @@ -3,13 +3,17 @@ hr.timesheet.employee.extd_form hr.employee - + - - - - - + + + + 0