From 8f3f1827cbb7d7ee0eae132b45394fc0547f940d Mon Sep 17 00:00:00 2001 From: jem-odoo Date: Mon, 19 Feb 2018 10:39:05 +0100 Subject: [PATCH] [IMP] hr_timesheet: ability to track cost without selling time The cost of an employee should be in hr_timesheet for a long time, to allow user to track time on project/task: hours spent and their cost. This commit simply moves fields and related views. It also moves the 'postprocess' method from sale_timesheet to hr_timesheet since this method is responsible to set the amount (cost) on AAL. User can now track their time and cost, without having sale_timesheet installed. --- addons/hr_timesheet/models/__init__.py | 1 + .../models/hr.py | 1 - addons/hr_timesheet/models/hr_timesheet.py | 49 ++++++++++++++++--- addons/hr_timesheet/views/hr_views.xml | 8 ++- addons/sale_timesheet/__manifest__.py | 1 - addons/sale_timesheet/models/__init__.py | 1 - addons/sale_timesheet/models/account.py | 40 +++++---------- addons/sale_timesheet/views/hr_views.xml | 20 -------- 8 files changed, 63 insertions(+), 58 deletions(-) rename addons/{sale_timesheet => hr_timesheet}/models/hr.py (83%) delete mode 100644 addons/sale_timesheet/views/hr_views.xml diff --git a/addons/hr_timesheet/models/__init__.py b/addons/hr_timesheet/models/__init__.py index 1766ea8ddf9..4db761e1e09 100644 --- a/addons/hr_timesheet/models/__init__.py +++ b/addons/hr_timesheet/models/__init__.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from . import hr from . import hr_timesheet from . import res_config_settings from . import project diff --git a/addons/sale_timesheet/models/hr.py b/addons/hr_timesheet/models/hr.py similarity index 83% rename from addons/sale_timesheet/models/hr.py rename to addons/hr_timesheet/models/hr.py index a9f94f57bb9..afedd48561e 100644 --- a/addons/sale_timesheet/models/hr.py +++ b/addons/hr_timesheet/models/hr.py @@ -7,6 +7,5 @@ from odoo import fields, models class HrEmployee(models.Model): _inherit = 'hr.employee' - # FIXME: this field should be in module hr_timesheet, not sale_timesheet 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/hr_timesheet/models/hr_timesheet.py b/addons/hr_timesheet/models/hr_timesheet.py index 30bb3c9dff2..dd4b83dad8b 100644 --- a/addons/hr_timesheet/models/hr_timesheet.py +++ b/addons/hr_timesheet/models/hr_timesheet.py @@ -40,14 +40,20 @@ class AccountAnalyticLine(models.Model): line.department_id = line.employee_id.department_id @api.model - def create(self, vals): - vals = self._timesheet_preprocess(vals) - return super(AccountAnalyticLine, self).create(vals) + def create(self, values): + values = self._timesheet_preprocess(values) + result = super(AccountAnalyticLine, self).create(values) + if result.project_id: # applied only for timesheet + result._timesheet_postprocess(values) + return result @api.multi - def write(self, vals): - vals = self._timesheet_preprocess(vals) - return super(AccountAnalyticLine, self).write(vals) + def write(self, values): + values = self._timesheet_preprocess(values) + result = super(AccountAnalyticLine, self).write(values) + # applied only for timesheet + self.filtered(lambda t: t.project_id)._timesheet_postprocess(values) + return result def _timesheet_preprocess(self, vals): """ Deduce other field values from the one given. @@ -79,3 +85,34 @@ class AccountAnalyticLine(models.Model): if partner_id: vals['partner_id'] = partner_id return vals + + @api.multi + def _timesheet_postprocess(self, values): + """ Hook to update record one by one according to the values of a `write` or a `create`. """ + sudo_self = self.sudo() # this creates only one env for all operation that required sudo() in `_timesheet_postprocess_values`override + values_to_write = self._timesheet_postprocess_values(values) + for timesheet in sudo_self: + if values_to_write[timesheet.id]: + timesheet.write(values_to_write[timesheet.id]) + return values + + @api.multi + def _timesheet_postprocess_values(self, values): + """ Get the addionnal values to write on record + :param dict values: values for the model's fields, as a dictionary:: + {'field_name': field_value, ...} + :return: a dictionary mapping each record id to its corresponding + dictionnary values to write (may be empty). + """ + result = dict.fromkeys(self.ids, dict()) + sudo_self = self.sudo() # this creates only one env for all operation that required sudo() + # (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_id.timesheet_cost or 0.0 + amount = -timesheet.unit_amount * cost + amount_converted = timesheet.employee_id.currency_id.compute(amount, timesheet.account_id.currency_id) + result[timesheet.id].update({ + 'amount': amount_converted, + }) + return result diff --git a/addons/hr_timesheet/views/hr_views.xml b/addons/hr_timesheet/views/hr_views.xml index 21c645d8159..170bd7e861f 100644 --- a/addons/hr_timesheet/views/hr_views.xml +++ b/addons/hr_timesheet/views/hr_views.xml @@ -19,8 +19,12 @@ - - + + diff --git a/addons/sale_timesheet/__manifest__.py b/addons/sale_timesheet/__manifest__.py index f0d059b1e65..b9cfe6c47e0 100644 --- a/addons/sale_timesheet/__manifest__.py +++ b/addons/sale_timesheet/__manifest__.py @@ -21,7 +21,6 @@ have real delivered quantities in sales orders. 'views/account_invoice_views.xml', 'views/sale_order_views.xml', 'views/product_views.xml', - 'views/hr_views.xml', 'views/project_task_views.xml', 'views/hr_timesheet_views.xml', 'views/res_config_settings_views.xml', diff --git a/addons/sale_timesheet/models/__init__.py b/addons/sale_timesheet/models/__init__.py index d22baec4d49..e6421022c33 100644 --- a/addons/sale_timesheet/models/__init__.py +++ b/addons/sale_timesheet/models/__init__.py @@ -3,7 +3,6 @@ from . import account from . import account_invoice -from . import hr from . import product from . import project from . import sale_order diff --git a/addons/sale_timesheet/models/account.py b/addons/sale_timesheet/models/account.py index 23a5b3a5f67..87ec0a10f2c 100644 --- a/addons/sale_timesheet/models/account.py +++ b/addons/sale_timesheet/models/account.py @@ -21,14 +21,6 @@ class AccountAnalyticLine(models.Model): timesheet_invoice_id = fields.Many2one('account.invoice', string="Invoice", readonly=True, copy=False, help="Invoice created from the timesheet") timesheet_revenue = fields.Monetary("Revenue", default=0.0, readonly=True, copy=False) - @api.model - def create(self, values): - result = super(AccountAnalyticLine, self).create(values) - # applied only for timesheet - if result.project_id: - result._timesheet_postprocess(values) - return result - @api.multi def write(self, values): # prevent to update invoiced timesheets if one line is of type delivery @@ -36,8 +28,6 @@ class AccountAnalyticLine(models.Model): if any([field_name in values for field_name in ['unit_amount', 'employee_id', 'task_id', 'timesheet_revenue', 'so_line', 'amount', 'date']]): raise UserError(_('You can not modify already invoiced timesheets (linked to a Sales order items invoiced on Time and material).')) result = super(AccountAnalyticLine, self).write(values) - # applied only for timesheet - self.filtered(lambda t: t.project_id)._timesheet_postprocess(values) return result @api.model @@ -55,30 +45,23 @@ class AccountAnalyticLine(models.Model): return values @api.multi - def _timesheet_postprocess(self, values): + def _timesheet_postprocess_values(self, values): sudo_self = self.sudo() # this creates only one env for all operation that required sudo() - # (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']]): + result = super(AccountAnalyticLine, self)._timesheet_postprocess_values(values) + # (re)compute the UoM from the employee company + if any([field_name in values for field_name in ['employee_id']]): for timesheet in sudo_self: uom = timesheet.employee_id.company_id.project_time_mode_id - cost = timesheet.employee_id.timesheet_cost or 0.0 - amount = -timesheet.unit_amount * cost - amount_converted = timesheet.employee_id.currency_id.compute(amount, timesheet.account_id.currency_id) - timesheet.write({ - 'amount': amount_converted, + result[timesheet.id].update({ 'product_uom_id': uom.id, }) # (re)compute the theorical revenue if any([field_name in values for field_name in ['so_line', 'unit_amount', 'account_id']]): - sudo_self._timesheet_compute_theorical_revenue() - return values - - @api.multi - def _timesheet_compute_theorical_revenue(self): - for timesheet in self: - values = timesheet._timesheet_compute_theorical_revenue_values() - timesheet.write(values) - return True + for timesheet in sudo_self: + values_to_write = timesheet._timesheet_compute_theorical_revenue_values() + if values_to_write: + result[timesheet.id].update(values_to_write) + return result @api.multi def _timesheet_compute_theorical_revenue_values(self): @@ -93,6 +76,9 @@ class AccountAnalyticLine(models.Model): ) else: 0 + + :return: a dictionary mapping each record id to its corresponding + dictionnary values to write (may be empty). """ self.ensure_one() timesheet = self diff --git a/addons/sale_timesheet/views/hr_views.xml b/addons/sale_timesheet/views/hr_views.xml deleted file mode 100644 index ce032cc01e0..00000000000 --- a/addons/sale_timesheet/views/hr_views.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - hr.timesheet.employee.extd_form - hr.employee - - - - - - 0 - - - -