bemade_fsm: tie in with sale_planning to plan SO lines based on task templates.
This commit is contained in:
parent
f6ff61eb39
commit
7cadd52fc6
5 changed files with 73 additions and 5 deletions
|
|
@ -27,7 +27,8 @@
|
|||
'author': 'Bemade Inc.',
|
||||
'website': 'http://www.bemade.org',
|
||||
'license': 'OPL-1',
|
||||
'depends': ['project',
|
||||
'depends': ['project_enterprise',
|
||||
'project_forecast',
|
||||
'stock',
|
||||
'sale',
|
||||
'sale_project',
|
||||
|
|
|
|||
|
|
@ -5,3 +5,6 @@ class ProductTemplate(models.Model):
|
|||
_inherit = 'product.template'
|
||||
|
||||
task_template_id = fields.Many2one("project.task.template", string="Task Template", ondelete='restrict')
|
||||
is_field_service = fields.Boolean("Plan as field service",
|
||||
help="Products planned as field service will have travel time "
|
||||
"considered in planning.")
|
||||
|
|
@ -89,6 +89,14 @@ class SaleOrderLine(models.Model):
|
|||
relation="bemade_fsm_equipment_sale_order_line_rel",
|
||||
column1="sale_order_line_id",
|
||||
column2="equipment_id")
|
||||
is_field_service = fields.Boolean(string="Is Field Service",
|
||||
compute="_compute_is_field_service",
|
||||
store=True)
|
||||
|
||||
@api.depends('product_id')
|
||||
def _compute_is_field_service(self):
|
||||
for rec in self:
|
||||
rec.is_field_service = rec.product_id.is_field_service
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals):
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
from odoo import fields, models, api, Command, _
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.exceptions import ValidationError, UserError
|
||||
from odoo.osv import expression
|
||||
from collections import defaultdict, namedtuple
|
||||
|
||||
|
||||
class Task(models.Model):
|
||||
|
|
@ -10,7 +12,7 @@ class Task(models.Model):
|
|||
column1="task_id",
|
||||
column2="equipment_id",
|
||||
string="Equipment to Service",
|
||||
tracking=True,)
|
||||
tracking=True, )
|
||||
|
||||
work_order_contacts = fields.Many2many(comodel_name="res.partner",
|
||||
relation="task_work_order_contact_rel",
|
||||
|
|
@ -28,13 +30,64 @@ class Task(models.Model):
|
|||
inverse="_inverse_contacts",
|
||||
store=True)
|
||||
|
||||
planned_date_begin = fields.Datetime("Start Date", tracking=True,
|
||||
task_dependency_tracking=True,
|
||||
compute="_compute_planned_dates",
|
||||
inverse="_inverse_planned_dates",
|
||||
store=True)
|
||||
planned_date_end = fields.Datetime("End Date", tracking=True,
|
||||
task_dependency_tracking=True,
|
||||
compute="_compute_planned_dates",
|
||||
inverse="_inverse_planned_dates",
|
||||
store=True)
|
||||
|
||||
# Override related field to make it return false if this is an FSM subtask
|
||||
allow_billable = fields.Boolean(string="Can be billed",
|
||||
related=False,
|
||||
compute="_compute_allow_billable",)
|
||||
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 _get_related_planning_slots(self):
|
||||
domain = expression.AND([
|
||||
self._get_domain_compute_forecast_hours(),
|
||||
[('task_id', 'in', self.ids + self._get_all_subtasks().ids)]
|
||||
])
|
||||
return self.env['planning.slot'].search(domain)
|
||||
|
||||
@api.depends('forecast_hours')
|
||||
def _compute_planned_dates(self):
|
||||
forecast_data = self._get_related_planning_slots()
|
||||
mapped_data = {}
|
||||
TimeSpan = namedtuple('timespan', ['start', 'end'])
|
||||
for d in forecast_data:
|
||||
if d not in mapped_data:
|
||||
mapped_data.update({d: TimeSpan(d.start_datetime, d.end_datetime)})
|
||||
continue
|
||||
if mapped_data[d].start > d.start_datetime:
|
||||
mapped_data[d].start = d.start_datetime
|
||||
if mapped_data[d].end < d.end_datetime:
|
||||
mapped_data[d].end = d.end_datetime
|
||||
for rec in self:
|
||||
if rec not in mapped_data:
|
||||
if not rec.planned_date_end:
|
||||
rec.planned_date_end = False
|
||||
if not rec.planned_date_begin:
|
||||
rec.planned_date_begin = False
|
||||
continue
|
||||
rec.planned_date_begin, rec.planned_date_end = mapped_data[rec]
|
||||
|
||||
def _inverse_planned_dates(self):
|
||||
""" Modifying the planned dates for tasks with existing planning records
|
||||
(planning.slot) has no defined safe behaviour, so we block it."""
|
||||
if self._get_related_planning_slots():
|
||||
raise UserError(_("Modifying the planned start or end time on a task is not "
|
||||
"permitted when that task already has planning records "
|
||||
"associated to it. Please modify or delete the planning "
|
||||
"records instead."))
|
||||
|
||||
@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
|
||||
is related to one, and from the task's customer if there is no sale order related to the task."""
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@
|
|||
<field name="task_template_id"
|
||||
attrs="{'invisible': [('service_tracking', 'not in', ('task_global_project', 'task_in_project'))]}"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='planning_enabled']" position="after">
|
||||
<field name="is_field_service"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
<record id="product_search_form_view_inherit_bemade_fsm" model="ir.ui.view">
|
||||
|
|
|
|||
Loading…
Reference in a new issue