summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorjbm-odoo <jbm@odoo.com>2019-12-03 08:33:10 +0000
committerjbm-odoo <jbm@odoo.com>2020-01-15 15:36:21 +0000
commit79cdcd13c8dacd85ce4f32917f2bc722ede62c78 (patch)
tree9544e0bbfc4b83711528b7c7180b47fe45c525c4 /addons
parent5f65d9c9fcad58165dee8d353e0b045e4386caea (diff)
[IMP] sale_timesheet: Move not invoiced time sheet to new SOL
Description of the issue/feature this PR addresses: There is no easy way to re-assign the time sheets to the correct SOL (changing the task's SOL does not change the non-invoiced time sheets automatically) Current behavior before PR: On a project, if a user put a wrong sale order line, and after that he changes this SOL, the timesheet line will not be moved. Desired behavior after PR is merged: On a project, if a user put a wrong sale order line, and after that he change this SOL, all non invoiced timesheet line will be moved to new line. TaskId: 2144703
Diffstat (limited to 'addons')
-rw-r--r--addons/sale_project/models/project.py16
-rw-r--r--addons/sale_timesheet/models/account_move.py4
-rw-r--r--addons/sale_timesheet/models/project.py26
-rw-r--r--addons/sale_timesheet/tests/test_sale_service.py10
-rw-r--r--addons/sale_timesheet/wizard/project_task_create_sale_order.py8
5 files changed, 48 insertions, 16 deletions
diff --git a/addons/sale_project/models/project.py b/addons/sale_project/models/project.py
index 59aad93a856..523fad6b108 100644
--- a/addons/sale_project/models/project.py
+++ b/addons/sale_project/models/project.py
@@ -71,15 +71,25 @@ class ProjectTask(models.Model):
# Actions
# ---------------------------------------------------
+ def _get_action_view_so_ids(self):
+ return self.sale_order_id.ids
+
def action_view_so(self):
self.ensure_one()
- return {
+ so_ids = self._get_action_view_so_ids()
+ action_window = {
"type": "ir.actions.act_window",
"res_model": "sale.order",
- "views": [[False, "form"]],
- "res_id": self.sale_order_id.id,
+ "name": "Sales Order",
+ "views": [[False, "tree"], [False, "form"]],
"context": {"create": False, "show_sale": True},
+ "domain": [["id", "in", so_ids]],
}
+ if len(so_ids) == 1:
+ action_window["views"] = [[False, "form"]]
+ action_window["res_id"] = so_ids[0]
+
+ return action_window
def rating_get_partner_id(self):
partner = self.partner_id or self.sale_line_id.order_id.partner_id
diff --git a/addons/sale_timesheet/models/account_move.py b/addons/sale_timesheet/models/account_move.py
index e98e479576b..91e5ac99ce0 100644
--- a/addons/sale_timesheet/models/account_move.py
+++ b/addons/sale_timesheet/models/account_move.py
@@ -74,6 +74,6 @@ class AccountMoveLine(models.Model):
"""
return [
('so_line', 'in', sale_line_delivery.ids),
- ('timesheet_invoice_id', '=', False),
- ('project_id', '!=', False)
+ ('project_id', '!=', False),
+ '|', ('timesheet_invoice_id', '=', False), ('timesheet_invoice_id.state', '=', 'cancel')
]
diff --git a/addons/sale_timesheet/models/project.py b/addons/sale_timesheet/models/project.py
index 876e09efe60..31a06694f12 100644
--- a/addons/sale_timesheet/models/project.py
+++ b/addons/sale_timesheet/models/project.py
@@ -126,6 +126,19 @@ class ProjectTask(models.Model):
is_project_map_empty = fields.Boolean("Is Project map empty", compute='_compute_is_project_map_empty')
allow_billable = fields.Boolean(related="project_id.allow_billable")
+ @api.onchange('sale_line_id')
+ def _onchange_sale_line_id(self):
+ if self.timesheet_ids:
+ if self.sale_line_id:
+ message = _("All timesheet hours that are not yet invoiced will be assigned to the selected Sales Order Item on save. Discard to avoid the change.")
+ else:
+ message = _("All timesheet hours that are not yet invoiced will be removed from the selected Sales Order Item on save. Discard to avoid the change.")
+
+ return {'warning': {
+ 'title': _("Warning"),
+ 'message': message
+ }}
+
@api.depends('sale_line_id', 'project_id', 'billable_type')
def _compute_sale_order_id(self):
for task in self:
@@ -166,11 +179,19 @@ class ProjectTask(models.Model):
# set domain on SO: on non billable project, all SOL of customer, otherwise the one from the SO
def write(self, values):
+ old_sale_line_id = dict([(t.id, t.sale_line_id.id) for t in self])
if values.get('project_id'):
project_dest = self.env['project.project'].browse(values['project_id'])
if project_dest.billable_type == 'employee_rate':
values['sale_line_id'] = False
- return super(ProjectTask, self).write(values)
+ res = super(ProjectTask, self).write(values)
+ if 'sale_line_id' in values and self.sudo().timesheet_ids:
+ self.timesheet_ids.filtered(
+ lambda t: (not t.timesheet_invoice_id or t.timesheet_invoice_id.state == 'cancel') and t.so_line.id == old_sale_line_id[t.task_id.id]
+ ).write({
+ 'so_line': values['sale_line_id']
+ })
+ return res
def action_make_billable(self):
return {
@@ -185,3 +206,6 @@ class ProjectTask(models.Model):
'form_view_initial_mode': 'edit',
},
}
+
+ def _get_action_view_so_ids(self):
+ return list(set((self.sale_order_id + self.timesheet_ids.so_line.order_id).ids))
diff --git a/addons/sale_timesheet/tests/test_sale_service.py b/addons/sale_timesheet/tests/test_sale_service.py
index 13413f7c3fa..2f3004920d9 100644
--- a/addons/sale_timesheet/tests/test_sale_service.py
+++ b/addons/sale_timesheet/tests/test_sale_service.py
@@ -162,15 +162,15 @@ class TestSaleService(TestCommonSaleTimesheetNoChart):
self.assertTrue(all([billing_type == 'billable_time' for billing_type in timesheets.mapped('timesheet_invoice_type')]), "All timesheets linked to the task should be on 'billable time'")
self.assertEqual(so_line_deliver_global_project.qty_to_invoice, 5, "Quantity to invoice should have been increased when logging timesheet on delivered quantities task")
- # make task non billable
- task_serv2.write({'sale_line_id': False})
- self.assertTrue(all([billing_type == 'billable_time' for billing_type in timesheets.mapped('timesheet_invoice_type')]), "billable type of timesheet should not change when tranfering task into another project")
- self.assertEqual(task_serv2.timesheet_ids.mapped('so_line'), so_line_deliver_global_project, "Old timesheet are not modified when changing the task SO line")
-
# invoice SO, and validate invoice
invoice = self.sale_order._create_invoices()[0]
invoice.post()
+ # make task non billable
+ task_serv2.write({'sale_line_id': False})
+ self.assertTrue(all([billing_type == 'billable_time' for billing_type in timesheets.mapped('timesheet_invoice_type')]), "billable type of timesheet should not change when tranfering task into another project")
+ self.assertEqual(task_serv2.timesheet_ids.mapped('so_line'), so_line_deliver_global_project, "Old invoiced timesheet are not modified when changing the task SO line")
+
# try to update timesheets, catch error 'You cannot modify invoiced timesheet'
with self.assertRaises(UserError):
timesheets.write({'so_line': False})
diff --git a/addons/sale_timesheet/wizard/project_task_create_sale_order.py b/addons/sale_timesheet/wizard/project_task_create_sale_order.py
index 9b5e6c38f0a..27d9213268d 100644
--- a/addons/sale_timesheet/wizard/project_task_create_sale_order.py
+++ b/addons/sale_timesheet/wizard/project_task_create_sale_order.py
@@ -57,9 +57,7 @@ class ProjectTaskCreateSalesOrder(models.TransientModel):
if self.task_id.sale_line_id:
raise UserError(_("The task is already linked to a sales order item."))
- timesheet_with_so_line = self.env['account.analytic.line'].search_count([('task_id', '=', self.task_id.id), ('so_line', '!=', False), ('project_id', '!=', False)])
- if timesheet_with_so_line:
- raise UserError(_('The sales order cannot be created because some timesheets of this task are already linked to another sales order.'))
+ timesheet_with_so_line = self.env['account.analytic.line'].search([('task_id', '=', self.task_id.id), ('so_line', '!=', False), ('project_id', '!=', False)])
# create SO
sale_order = self.env['sale.order'].create({
@@ -76,7 +74,7 @@ class ProjectTaskCreateSalesOrder(models.TransientModel):
'price_unit': self.price_unit,
'project_id': self.task_id.project_id.id, # prevent to re-create a project on confirmation
'task_id': self.task_id.id,
- 'product_uom_qty': self.task_id.total_hours_spent,
+ 'product_uom_qty': self.task_id.total_hours_spent - round(sum(timesheet_with_so_line.mapped('unit_amount')), 2),
})
# link task to SOL
@@ -87,7 +85,7 @@ class ProjectTaskCreateSalesOrder(models.TransientModel):
})
# assign SOL to timesheets
- self.env['account.analytic.line'].search([('task_id', '=', self.task_id.id), ('so_line', '=', False)]).write({
+ self.env['account.analytic.line'].search([('task_id', '=', self.task_id.id), ('so_line', '=', False), ('timesheet_invoice_id', '=', False)]).write({
'so_line': sale_order_line.id
})