bemade_fsm: Visits on sales orders now group created tasks under a common parent. Fixes #72.
This commit is contained in:
parent
454656159c
commit
37aea5c181
4 changed files with 68 additions and 20 deletions
|
|
@ -24,6 +24,16 @@ class FSMVisit(models.Model):
|
|||
summarized_equipment_ids = fields.Many2many(comodel_name="bemade_fsm.equipment",
|
||||
string="Equipment to Service",
|
||||
compute="_compute_summarized_equipment_ids")
|
||||
task_id = fields.Many2one(comodel_name="project.task",
|
||||
compute="_compute_task_id",
|
||||
string="Service Visit",)
|
||||
task_ids = fields.One2many(comodel_name="project.task",
|
||||
inverse_name="visit_id")
|
||||
|
||||
@api.depends('task_ids')
|
||||
def _compute_task_id(self):
|
||||
for rec in self:
|
||||
rec.task_id = rec.task_ids and rec.task_ids[0]
|
||||
|
||||
@api.depends('so_section_id', 'sale_order_id.summary_equipment_ids')
|
||||
def _compute_summarized_equipment_ids(self):
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ class SaleOrderLine(models.Model):
|
|||
column1="sale_order_line_id",
|
||||
column2="equipment_id")
|
||||
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals):
|
||||
recs = super().create(vals)
|
||||
|
|
@ -162,6 +163,33 @@ class SaleOrderLine(models.Model):
|
|||
task.name = _generate_task_name(tmpl)
|
||||
return task
|
||||
|
||||
def _timesheet_service_generation(self):
|
||||
super()._timesheet_service_generation()
|
||||
visit_lines = self.filtered(lambda l: l.visit_id)
|
||||
for line in visit_lines:
|
||||
task_ids = line.get_section_lines().mapped('task_id')
|
||||
if not task_ids:
|
||||
continue
|
||||
if len(set([task.project_id for task in task_ids])) > 1:
|
||||
# Can't group up the tasks if they're part of different projects
|
||||
return
|
||||
project_id = task_ids[0].project_id
|
||||
line.visit_id.task_id = line._generate_task_for_visit_line(project_id)
|
||||
task_ids.write({'parent_id': line.visit_id.task_id.id})
|
||||
|
||||
def _generate_task_for_visit_line(self, project):
|
||||
self.ensure_one()
|
||||
task = self.env['project.task'].create({
|
||||
'name': self.order_id.name + ": " + self.name,
|
||||
'description': f"Parent task for {self.order_id.name}, visit {self.name}",
|
||||
'project_id': project.id,
|
||||
'equipment_ids': self.get_section_lines().mapped('equipment_ids').ids,
|
||||
'sale_order_id': self.order_id.id,
|
||||
'partner_id': self.order_id.partner_shipping_id.id,
|
||||
'visit_id': self.visit_id.id,
|
||||
})
|
||||
return task
|
||||
|
||||
@api.depends('order_id.order_line', 'display_type', 'qty_to_deliver', 'order_id.order_line.qty_to_deliver',
|
||||
'order_id.order_line.display_type')
|
||||
def _compute_is_fully_delivered(self):
|
||||
|
|
@ -175,22 +203,22 @@ class SaleOrderLine(models.Model):
|
|||
self.is_fully_delivered_and_invoiced = self._iterate_items_compute_bool(lambda l: l.qty_to_invoice == 0)
|
||||
|
||||
def get_section_lines(self):
|
||||
""" Returns a list containing the sale order lines that fall under this section. """
|
||||
""" Returns a RecordSet containing the sale order lines that fall under this section. """
|
||||
self.ensure_one()
|
||||
assert self.display_type == 'line_section', 'Method called incorrectly on non-section order line.'
|
||||
found = False
|
||||
lines = []
|
||||
for line in self.order_id:
|
||||
for line in self.order_id.order_line:
|
||||
if line == self:
|
||||
found = True
|
||||
continue
|
||||
if not found:
|
||||
continue
|
||||
if line.display_type == 'line_section': # Stop when we hit the next section
|
||||
return lines
|
||||
break
|
||||
else:
|
||||
lines.add(line)
|
||||
return lines
|
||||
lines.append(line)
|
||||
return self.env['sale.order.line'].union(*lines)
|
||||
|
||||
def _iterate_items_compute_bool(self, single_line_func):
|
||||
if not self.display_type:
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ class Task(models.Model):
|
|||
allow_billable = fields.Boolean(string="Can be billed",
|
||||
related=False,
|
||||
compute="_compute_allow_billable",)
|
||||
|
||||
visit_id = fields.Many2one(comodel_name='bemade_fsm.visit')
|
||||
@api.depends('sale_line_id.order_id.site_contacts', 'sale_line_id.order_id.work_order_contacts')
|
||||
def _compute_contacts(self):
|
||||
""" The work order contacts and site contacts for a given task are taken from the sale order if the task
|
||||
|
|
@ -55,10 +57,11 @@ class Task(models.Model):
|
|||
'site_contacts': [Command.set(rec.site_contacts.ids)],
|
||||
})
|
||||
|
||||
@api.depends('parent_id', 'project_id')
|
||||
@api.depends('parent_id.visit_id', 'project_id.is_fsm', 'project_id.allow_billable')
|
||||
def _compute_allow_billable(self):
|
||||
for rec in self:
|
||||
if rec.parent_id and rec.project_id and rec.project_id.is_fsm:
|
||||
# If an FSM task has a parent that is linked to an SO line, then the parent is the billable one
|
||||
if rec.parent_id and not rec.parent_id.visit_id and rec.project_id and rec.project_id.is_fsm:
|
||||
rec.allow_billable = False
|
||||
else:
|
||||
rec.allow_billable = rec.project_id.allow_billable
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ class FSMVisitTest(BemadeFSMBaseTest):
|
|||
|
||||
def test_create_visit_sets_name_on_section(self):
|
||||
so = self._generate_sale_order()
|
||||
self._add_service_so_line(so)
|
||||
self._generate_sale_order_line(sale_order=so)
|
||||
|
||||
visit = self._generate_visit(so)
|
||||
|
||||
|
|
@ -35,7 +35,7 @@ class FSMVisitTest(BemadeFSMBaseTest):
|
|||
def test_visit_completes_when_task_completes(self):
|
||||
so = self._generate_sale_order()
|
||||
visit = self._generate_visit(so)
|
||||
self._add_service_so_line(so, task=True)
|
||||
self._generate_sale_order_line(so)
|
||||
so.action_confirm()
|
||||
task = so.order_line.filtered(lambda l: l.task_id).task_id
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ class FSMVisitTest(BemadeFSMBaseTest):
|
|||
def test_visit_shows_invoiced_when_invoiced(self):
|
||||
so = self._generate_sale_order()
|
||||
visit = self._generate_visit(so)
|
||||
self._add_service_so_line(so, task=True)
|
||||
self._generate_sale_order_line(so)
|
||||
so.action_confirm()
|
||||
task = so.order_line.filtered(lambda l: l.task_id).task_id
|
||||
task.action_fsm_validate()
|
||||
|
|
@ -55,6 +55,23 @@ class FSMVisitTest(BemadeFSMBaseTest):
|
|||
|
||||
self.assertTrue(visit.is_invoiced)
|
||||
|
||||
def test_visit_groups_section_tasks_when_confirmed(self):
|
||||
partner = self._generate_partner()
|
||||
so = self._generate_sale_order()
|
||||
visit = self._generate_visit(sale_order=so)
|
||||
sol1 = self._generate_sale_order_line(sale_order=so)
|
||||
sol2 = self._generate_sale_order_line(sale_order=so)
|
||||
visit.so_section_id.sequence = 1
|
||||
sol1.sequence = 2
|
||||
sol2.sequence = 3
|
||||
|
||||
so.action_confirm()
|
||||
|
||||
visit_task = visit.task_id
|
||||
self.assertTrue(visit_task)
|
||||
visit_subtasks = visit_task.child_ids
|
||||
self.assertTrue(visit_subtasks and sol1.task_id in visit_subtasks and sol2.task_id in visit_subtasks)
|
||||
|
||||
def _invoice_sale_order(self, so):
|
||||
wiz = self.env['sale.advance.payment.inv'].with_context({'active_ids': [so.id]}).create({})
|
||||
wiz.create_invoices()
|
||||
|
|
@ -67,13 +84,3 @@ class FSMVisitTest(BemadeFSMBaseTest):
|
|||
'sale_order_id': sale_order.id,
|
||||
'label': label,
|
||||
}])
|
||||
|
||||
def _add_service_so_line(self, sale_order, task: bool = False):
|
||||
""" Generates a sales order line for a service product.
|
||||
|
||||
:param sale_order: The sales order to which the new line is to be added
|
||||
:param task: If true, the created line will be for a product with service_tracking=task_global_project
|
||||
"""
|
||||
service_tracking = 'task_global_project' if task else 'no'
|
||||
product = self._generate_product(service_tracking=service_tracking)
|
||||
return self._generate_sale_order_line(sale_order, product)
|
||||
|
|
|
|||
Loading…
Reference in a new issue