[IMP] hr{,sale}_timesheet: add employee mapping cost
Prior to this commit, the timesheet amount was computed based on the employee timesheet cost. The sale order line related to the employee was also required This commit allows the project owner to configure a employee mapping cost that will override the employee timesheet cost if defined. This commit removes the fact that the sale order line is no more required, thus the mapping will have no effect. PR : #70527 task-2458135
This commit is contained in:
committed by
Laurent Stukkens (LTU)
parent
dd706ea024
commit
dbbfce18a4
@@ -237,7 +237,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_id.timesheet_cost or 0.0
|
||||
cost = timesheet._employee_timesheet_cost()
|
||||
amount = -timesheet.unit_amount * cost
|
||||
amount_converted = timesheet.employee_id.currency_id._convert(
|
||||
amount, timesheet.account_id.currency_id, self.env.company, timesheet.date)
|
||||
@@ -258,3 +258,7 @@ class AccountAnalyticLine(models.Model):
|
||||
|
||||
def _get_timesheet_time_day(self):
|
||||
return self._convert_hours_to_days(self.unit_amount)
|
||||
|
||||
def _employee_timesheet_cost(self):
|
||||
self.ensure_one()
|
||||
return self.employee_id.timesheet_cost or 0.0
|
||||
|
||||
@@ -98,7 +98,7 @@ class AccountAnalyticLine(models.Model):
|
||||
|
||||
if not self.task_id:
|
||||
if self.project_id.pricing_type == 'employee_rate':
|
||||
map_entry = self.env['project.sale.line.employee.map'].search([('project_id', '=', self.project_id.id), ('employee_id', '=', self.employee_id.id)])
|
||||
map_entry = self._get_employee_mapping_entry()
|
||||
if map_entry:
|
||||
return map_entry.sale_line_id
|
||||
if self.project_id.sale_line_id:
|
||||
@@ -149,3 +149,14 @@ class AccountAnalyticLine(models.Model):
|
||||
def _unlink_except_invoiced(self):
|
||||
if any(line.timesheet_invoice_id and line.timesheet_invoice_id.state == 'posted' for line in self):
|
||||
raise UserError(_('You cannot remove a timesheet that has already been invoiced.'))
|
||||
|
||||
def _get_employee_mapping_entry(self):
|
||||
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):
|
||||
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()
|
||||
|
||||
@@ -10,7 +10,7 @@ class ProjectProductEmployeeMap(models.Model):
|
||||
|
||||
project_id = fields.Many2one('project.project', "Project", required=True)
|
||||
employee_id = fields.Many2one('hr.employee', "Employee", required=True)
|
||||
sale_line_id = fields.Many2one('sale.order.line', "Sale Order Item", compute="_compute_sale_line_id", store=True, readonly=False, required=True,
|
||||
sale_line_id = fields.Many2one('sale.order.line', "Sale Order Item", compute="_compute_sale_line_id", store=True, readonly=False,
|
||||
domain="""[
|
||||
('is_service', '=', True),
|
||||
('is_expense', '=', False),
|
||||
@@ -21,6 +21,10 @@ class ProjectProductEmployeeMap(models.Model):
|
||||
partner_id = fields.Many2one(related='project_id.partner_id')
|
||||
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")
|
||||
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)
|
||||
|
||||
_sql_constraints = [
|
||||
('uniqueness_employee', 'UNIQUE(project_id,employee_id)', 'An employee cannot be selected more than once in the mapping. Please remove duplicate(s) and try again.'),
|
||||
@@ -48,6 +52,17 @@ 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')
|
||||
def _compute_cost(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
|
||||
|
||||
@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
|
||||
|
||||
@api.model
|
||||
def create(self, values):
|
||||
res = super(ProjectProductEmployeeMap, self).create(values)
|
||||
|
||||
@@ -33,7 +33,9 @@
|
||||
<field name="employee_id" options="{'no_create': True}"/>
|
||||
<field name="sale_line_id" attrs="{'required': True}" options="{'no_create': True}"/>
|
||||
<field name="price_unit" widget="monetary" force_save="1" options="{'currency_field': 'currency_id'}"/>
|
||||
<field name="cost"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="cost_currency_id" invisible="1"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
|
||||
Reference in New Issue
Block a user