[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.
This commit is contained in:
jem-odoo
2018-04-06 14:30:46 +02:00
parent ca36c22c54
commit 8f3f1827cb
8 changed files with 63 additions and 58 deletions
+1
View File
@@ -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
@@ -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)
+43 -6
View File
@@ -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
+6 -2
View File
@@ -19,8 +19,12 @@
<field name="inherit_id" ref="hr.view_employee_form"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='active_group']" position="before">
<!-- Empty invisible group to be filled by timesheet_grid and sale_timesheet -->
<group string="Timesheets" name="timesheet" invisible="1" groups="hr_timesheet.group_timesheet_manager">
<group string="Timesheets" name="timesheet" groups="hr_timesheet.group_timesheet_manager">
<label for="timesheet_cost"/>
<div>
<field name="timesheet_cost" class="oe_inline"/> per hour
<field name="currency_id" invisible="1"/>
</div>
</group>
</xpath>
<xpath expr="//button[@name='toggle_active']" position="before">
-1
View File
@@ -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',
-1
View File
@@ -3,7 +3,6 @@
from . import account
from . import account_invoice
from . import hr
from . import product
from . import project
from . import sale_order
+13 -27
View File
@@ -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
-20
View File
@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="hr_timesheet_employee_extd_form" model="ir.ui.view">
<field name="name">hr.timesheet.employee.extd_form</field>
<field name="model">hr.employee</field>
<field name="inherit_id" ref="hr_timesheet.hr_employee_view_form_inherit_timesheet"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='timesheet']" position="inside">
<label for="timesheet_cost"/>
<div>
<field name="timesheet_cost" class="oe_inline"/> per hour
<field name="currency_id" invisible="1"/>
</div>
</xpath>
<xpath expr="//group[@name='timesheet']" position="attributes">
<attribute name="invisible">0</attribute>
</xpath>
</field>
</record>
</odoo>