bemade_fsm: manually merge in 15.0 differences and fix 2 failing tests
This commit is contained in:
parent
2a0ca65382
commit
149a30f6f3
12 changed files with 246 additions and 66 deletions
|
|
@ -8,4 +8,5 @@ from . import equipment_tag
|
|||
from . import task
|
||||
from . import res_partner
|
||||
from . import fsm_visit
|
||||
from . import res_company
|
||||
from . import res_company
|
||||
from . import project
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ class FSMVisit(models.Model):
|
|||
_name = "bemade_fsm.visit"
|
||||
_description = 'Represents a single visit by assigned service personnel.'
|
||||
|
||||
label = fields.Text(string="Label", required=True, related='so_section_id.name', readonly=False)
|
||||
label = fields.Text(string="Label", required=True, related='so_section_id.name', readonly=False, copy=True)
|
||||
|
||||
approx_date = fields.Date(string='Approximate Date')
|
||||
approx_date = fields.Date(string='Approximate Date', copy=False)
|
||||
|
||||
so_section_id = fields.Many2one(
|
||||
comodel_name="sale.order.line",
|
||||
|
|
@ -19,7 +19,7 @@ class FSMVisit(models.Model):
|
|||
sale_order_id = fields.Many2one(
|
||||
comodel_name="sale.order",
|
||||
string="Sales Order",
|
||||
required=True
|
||||
readonly=True,
|
||||
)
|
||||
|
||||
is_completed = fields.Boolean(string="Completed", related="so_section_id.is_fully_delivered")
|
||||
|
|
@ -61,10 +61,10 @@ class FSMVisit(models.Model):
|
|||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
recs = super().create(vals_list)
|
||||
for i, rec in enumerate(recs):
|
||||
for i, rec in enumerate(recs.filtered(lambda visit: not visit.so_section_id)):
|
||||
rec.so_section_id = rec.env['sale.order.line'].create({
|
||||
'order_id': rec.sale_order_id.id,
|
||||
'display_type': 'line_section',
|
||||
'name': vals_list[i]['label'],
|
||||
'name': vals_list[i].get('label', False),
|
||||
})
|
||||
return recs
|
||||
|
|
|
|||
11
bemade_fsm/models/project.py
Normal file
11
bemade_fsm/models/project.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
from odoo import models
|
||||
|
||||
|
||||
class Project(models.Model):
|
||||
_inherit = "project.project"
|
||||
|
||||
def _fetch_sale_order_item_ids(self, domain_per_model=None, limit=None, offset=None):
|
||||
# Override to flush the ORM cache to the database prior to running the query
|
||||
# Temporary fix until Odoo fixes this method (PR #160067 submitted for this)
|
||||
self.env.flush_all()
|
||||
return super()._fetch_sale_order_item_ids(domain_per_model, limit, offset)
|
||||
|
|
@ -50,11 +50,17 @@ class SaleOrder(models.Model):
|
|||
readonly=False
|
||||
)
|
||||
|
||||
is_fsm = fields.Boolean(
|
||||
compute='_compute_is_fsm',
|
||||
string='Is FSM',
|
||||
store=True,
|
||||
)
|
||||
|
||||
@api.depends('order_line.task_id')
|
||||
def get_relevant_order_lines(self, task_id):
|
||||
self.ensure_one()
|
||||
linked_lines = self.order_line.filtered(lambda l: l.task_id == task_id
|
||||
or l == task_id.visit_id.so_section_id)
|
||||
or l == task_id.visit_id.so_section_id)
|
||||
visit_lines = linked_lines.filtered(lambda l: l.visit_id)
|
||||
for line in visit_lines:
|
||||
linked_lines |= line.get_section_line_ids()
|
||||
|
|
@ -102,3 +108,41 @@ class SaleOrder(models.Model):
|
|||
rec.visit_ids = [Command.set(rec.order_line.visit_ids.ids)]
|
||||
return rec
|
||||
|
||||
def _create_default_visit(self):
|
||||
""" Called when an order is confirmed with lines that will create an FSM task, in order to make sure there is
|
||||
a visit line grouping all the service being done."""
|
||||
self.ensure_one()
|
||||
visit = self.env['bemade_fsm.visit'].create({
|
||||
'label': _('Service Visit'),
|
||||
'sale_order_id': self.id,
|
||||
})
|
||||
# Make sure it goes to the top of the list
|
||||
visit.so_section_id.sequence = 0
|
||||
|
||||
def _create_or_organize_visits_if_needed(self):
|
||||
""" Adds a visit line to the top of the order if there are not already visit lines for an order with lines that
|
||||
will create an FSM task."""
|
||||
for order in self.filtered("company_id.create_default_fsm_visit"):
|
||||
if not order.visit_ids and order.is_fsm:
|
||||
order._create_default_visit()
|
||||
if order.is_fsm:
|
||||
# Make sure that all the lines producing FSM tasks are under a visit
|
||||
visit_line_ids = order.mapped('visit_ids').mapped('so_section_id').mapped('section_line_ids')
|
||||
if any([
|
||||
True for line in
|
||||
order.order_line.filtered(lambda line: not line.display_type)
|
||||
if line not in visit_line_ids
|
||||
]):
|
||||
# If not, promote the first visit to the top of the order items list
|
||||
for line in order.order_line:
|
||||
line.sequence += 1
|
||||
order.mapped('visit_ids').mapped('so_section_id')[0].sequence = 0
|
||||
|
||||
@api.depends('order_line.is_fsm')
|
||||
def _compute_is_fsm(self):
|
||||
for rec in self:
|
||||
rec.is_fsm = any([line.is_fsm for line in rec.order_line])
|
||||
|
||||
def action_confirm(self):
|
||||
self._create_or_organize_visits_if_needed()
|
||||
return super().action_confirm()
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ class SaleOrderLine(models.Model):
|
|||
visit_ids = fields.One2many(
|
||||
comodel_name="bemade_fsm.visit",
|
||||
inverse_name="so_section_id",
|
||||
string="Visits"
|
||||
string="Visits",
|
||||
copy=True,
|
||||
)
|
||||
|
||||
visit_id = fields.Many2one(
|
||||
|
|
@ -22,7 +23,7 @@ class SaleOrderLine(models.Model):
|
|||
compute="_compute_visit_id",
|
||||
string="Visit",
|
||||
ondelete='cascade',
|
||||
store=True
|
||||
store=True,
|
||||
)
|
||||
|
||||
is_fully_delivered = fields.Boolean(
|
||||
|
|
@ -56,6 +57,17 @@ class SaleOrderLine(models.Model):
|
|||
compute="_compute_task_duration"
|
||||
)
|
||||
|
||||
is_fsm = fields.Boolean(
|
||||
string='Is FSM',
|
||||
compute='_compute_is_fsm',
|
||||
store=True,
|
||||
)
|
||||
|
||||
section_line_ids = fields.One2many(
|
||||
comodel_name='sale.order.line',
|
||||
compute='_compute_section_line_ids',
|
||||
)
|
||||
|
||||
@api.depends('visit_ids')
|
||||
def _compute_visit_id(self):
|
||||
for rec in self:
|
||||
|
|
@ -74,6 +86,13 @@ class SaleOrderLine(models.Model):
|
|||
rec.equipment_ids = rec.order_id.default_equipment_ids
|
||||
return recs
|
||||
|
||||
def copy_data(self, default=None):
|
||||
if default is None:
|
||||
default = {}
|
||||
if 'visit_ids' not in default:
|
||||
default['visit_ids'] = [(0, 0, visit.copy_data()[0]) for visit in self.visit_ids]
|
||||
return super().copy_data(default)
|
||||
|
||||
def _timesheet_create_task(self, project):
|
||||
""" Generate task for the given so line, and link it.
|
||||
:param project: record of project.project in which the task should be created
|
||||
|
|
@ -140,7 +159,7 @@ class SaleOrderLine(models.Model):
|
|||
def _timesheet_service_generation(self):
|
||||
super()._timesheet_service_generation()
|
||||
visit_lines = self.filtered(lambda l: l.visit_id)
|
||||
for line in visit_lines:
|
||||
for index, line in enumerate(visit_lines):
|
||||
task_ids = line.get_section_line_ids().mapped('task_id')
|
||||
if not task_ids:
|
||||
continue
|
||||
|
|
@ -148,16 +167,15 @@ class SaleOrderLine(models.Model):
|
|||
# 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)
|
||||
line.visit_id.task_id = line._generate_task_for_visit_line(project_id, index + 1)
|
||||
task_ids.write({'parent_id': line.visit_id.task_id.id})
|
||||
self.task_id.filtered("is_fsm").synchronize_name_fsm()
|
||||
(self.mapped('task_id') | self.visit_ids.task_id).filtered("is_fsm").synchronize_name_fsm()
|
||||
|
||||
def _generate_task_for_visit_line(self, project):
|
||||
def _generate_task_for_visit_line(self, project, visit_no: int):
|
||||
self.ensure_one()
|
||||
|
||||
task = self.env['project.task'].create({
|
||||
'name': 'Temp Name',
|
||||
'description': f"Parent task for {self.order_id.name}, visit {self.name}",
|
||||
'name': f"{self.order_id.name} - " + _("Visit") + f" {visit_no} - {self.name}",
|
||||
'project_id': project.id,
|
||||
'equipment_ids': self.get_section_line_ids().mapped('equipment_ids').ids,
|
||||
'sale_order_id': self.order_id.id,
|
||||
|
|
@ -207,6 +225,14 @@ class SaleOrderLine(models.Model):
|
|||
lines.append(line)
|
||||
return self.env['sale.order.line'].union(*lines)
|
||||
|
||||
@api.depends('display_type', 'order_id.order_line')
|
||||
def _compute_section_line_ids(self):
|
||||
for rec in self:
|
||||
if rec.display_type == 'line_section':
|
||||
rec.section_line_ids = [Command.set(rec.get_section_line_ids().ids)]
|
||||
else:
|
||||
rec.section_line_ids = False
|
||||
|
||||
def _iterate_items_compute_bool(self, single_line_func):
|
||||
if not self.display_type:
|
||||
return single_line_func(self)
|
||||
|
|
@ -244,8 +270,7 @@ class SaleOrderLine(models.Model):
|
|||
uom_hour = self.env.ref('uom.product_uom_hour')
|
||||
uom_unit = self.env.ref('uom.product_uom_unit')
|
||||
templated_lines = self.filtered(lambda l: l.product_id.task_template_id
|
||||
and l.product_id.task_template_id.
|
||||
planned_hours)
|
||||
and l.product_id.task_template_id.planned_hours)
|
||||
visit_lines = self.filtered(lambda l: l.visit_id)
|
||||
regular_lines = self - templated_lines - visit_lines
|
||||
for line in regular_lines:
|
||||
|
|
@ -265,3 +290,9 @@ class SaleOrderLine(models.Model):
|
|||
for line in visit_lines:
|
||||
line.task_duration = sum(line.get_section_line_ids().
|
||||
mapped('task_duration'))
|
||||
|
||||
@api.depends('product_id.detailed_type', 'product_id.service_tracking')
|
||||
def _compute_is_fsm(self):
|
||||
for rec in self:
|
||||
rec.is_fsm = (rec.product_id.detailed_type == 'service'
|
||||
and rec.product_id.service_tracking == 'task_global_project')
|
||||
|
|
|
|||
|
|
@ -101,9 +101,9 @@ class Task(models.Model):
|
|||
prev_seqs = self.sale_order_id.tasks_ids and \
|
||||
self.sale_order_id.tasks_ids.mapped('work_order_number')
|
||||
if prev_seqs:
|
||||
pattern = re.compile(r"\d+$")
|
||||
pattern = re.compile(r"(\d+)$")
|
||||
matches = map(lambda n: pattern.search(n), prev_seqs)
|
||||
seq += max(map(lambda n: int(n.group(1)) if n else 0), matches)
|
||||
seq += max(map(lambda n: int(n.group(1)) if n else 0, matches))
|
||||
rec.work_order_number = rec.sale_order_id.name.replace('SO', 'SVR', 1) \
|
||||
+ f"-{seq}"
|
||||
return res
|
||||
|
|
|
|||
|
|
@ -3,9 +3,8 @@
|
|||
<data>
|
||||
<template id="workorder_page_materials_table">
|
||||
<t t-set="order_lines"
|
||||
t-value="doc.relevant_order_lines"/>
|
||||
<!--.filtered(lambda l:
|
||||
l.product_id.type != 'service' and not l.is_downpayment and not l.visit_id)"/>-->
|
||||
t-value="doc.relevant_order_lines.filtered(lambda l:
|
||||
l.product_id.type != 'service' and not l.is_downpayment and not l.visit_id)"/>
|
||||
<t t-if="order_lines">
|
||||
<t t-set="visit_lines" t-value="order_lines.mapped('visit_id')"/>
|
||||
<t t-set="section_lines"
|
||||
|
|
|
|||
|
|
@ -4,12 +4,6 @@ from odoo import Command
|
|||
|
||||
@tagged("-at_install", "post_install")
|
||||
class BemadeFSMBaseTest(TransactionCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.env.user.groups_id += cls.env.ref("account.group_delivery_invoice_address")
|
||||
cls.env.company.create_default_fsm_visit = True
|
||||
|
||||
|
||||
@classmethod
|
||||
def _generate_project_manager_user(cls, name, login):
|
||||
|
|
@ -43,7 +37,6 @@ class BemadeFSMBaseTest(TransactionCase):
|
|||
user_group_fsm_user = cls.env.ref('industry_fsm.group_fsm_user')
|
||||
user_group_sales_user = cls.env.ref('sales_team.group_sale_salesman')
|
||||
user_group_sales_manager = cls.env.ref('sales_team.group_sale_manager')
|
||||
# TODO: Split this out into a bemade_fsm_customer_product_code module if it's wanted
|
||||
user_product_customer = cls.env.ref(
|
||||
'customer_product_code.group_product_customer_code_user',
|
||||
raise_if_not_found=False
|
||||
|
|
@ -79,11 +72,8 @@ class BemadeFSMBaseTest(TransactionCase):
|
|||
@classmethod
|
||||
def _generate_sale_order(cls, partner=None, client_order_ref='Test Order', equipment=None, shipping_location=None):
|
||||
partner = partner or cls._generate_partner()
|
||||
vals = {
|
||||
'partner_id': partner.id,
|
||||
'client_order_ref': client_order_ref,
|
||||
'payment_term_id': cls.env.ref('account.account_payment_term_immediate').id,
|
||||
}
|
||||
vals = {'partner_id': partner.id,
|
||||
'client_order_ref': client_order_ref}
|
||||
if equipment:
|
||||
vals.update({'default_equipment_ids': [Command.set([equipment.id])]})
|
||||
if shipping_location:
|
||||
|
|
@ -110,22 +100,6 @@ class BemadeFSMBaseTest(TransactionCase):
|
|||
'partner_location_id': partner and partner.id or False,
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def _generate_product_category(cls):
|
||||
company_id = cls.env.company.id
|
||||
return cls.env['product.category'].create({
|
||||
'name': 'Test Category',
|
||||
'parent_id': cls.env.ref('product.product_category_all', False).id or False,
|
||||
'property_account_income_categ_id': cls.env['account.account'].search(
|
||||
[('account_type', '=', 'income'), ('company_id', '=', company_id)],
|
||||
limit=1
|
||||
).id,
|
||||
'property_account_expense_categ_id': cls.env['account.account'].search(
|
||||
[('account_type', '=', 'expense_direct_cost'), ('company_id', '=', company_id)],
|
||||
limit=1
|
||||
).id,
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def _generate_product(cls, name='Test Product', product_type='service', service_tracking='task_global_project',
|
||||
project=None, task_template=None, service_policy='delivered_manual', uom=None):
|
||||
|
|
@ -142,7 +116,6 @@ class BemadeFSMBaseTest(TransactionCase):
|
|||
'service_policy': service_policy,
|
||||
'uom_id': uom_id,
|
||||
'uom_po_id': uom_id,
|
||||
'categ_id': cls._generate_product_category().id,
|
||||
})
|
||||
|
||||
@classmethod
|
||||
|
|
@ -201,7 +174,7 @@ class BemadeFSMBaseTest(TransactionCase):
|
|||
|
||||
def _invoice_sale_order(self, so):
|
||||
wiz = self.env['sale.advance.payment.inv'].with_context(
|
||||
{'active_ids': [so.id]}).create([{}])
|
||||
{'active_ids': [so.id]}).create({})
|
||||
wiz.create_invoices()
|
||||
inv = so.invoice_ids[-1]
|
||||
inv.action_post()
|
||||
|
|
@ -222,3 +195,16 @@ class BemadeFSMBaseTest(TransactionCase):
|
|||
sol1.sequence = 2
|
||||
sol2.sequence = 3
|
||||
return so, visit, sol1, sol2
|
||||
|
||||
def _generate_so_with_one_visit_two_lines_and_descendants(self):
|
||||
so = self._generate_sale_order()
|
||||
visit = self._generate_visit(sale_order=so)
|
||||
task_template = self._generate_task_template(structure=[2, 2, 2],
|
||||
names=['Parent', 'Child', 'Grandchild', 'Great-grandchild'])
|
||||
product = self._generate_product(task_template=task_template)
|
||||
sol1 = self._generate_sale_order_line(sale_order=so, product=product)
|
||||
sol2 = self._generate_sale_order_line(sale_order=so, product=product)
|
||||
visit.so_section_id.sequence = 1
|
||||
sol1.sequence = 2
|
||||
sol2.sequence = 3
|
||||
return so, visit, sol1, sol2
|
||||
|
|
|
|||
|
|
@ -19,3 +19,7 @@ class TestEquipment(BemadeFSMBaseTest):
|
|||
partner_company.write({'equipment_ids': [Command.set([])]})
|
||||
with self.assertRaises(MissingError):
|
||||
equipment.name
|
||||
|
||||
def test_compute_complete_name_when_name_blank(self):
|
||||
equipment = self._generate_equipment(name=False)
|
||||
complete_name = equipment.complete_name
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class FSMVisitTest(BemadeFSMBaseTest):
|
|||
so.action_confirm()
|
||||
task = so.order_line.filtered(lambda l: l.task_id).task_id
|
||||
|
||||
task.action_fsm_validate(stop_running_timers=True)
|
||||
task.action_fsm_validate()
|
||||
|
||||
self.assertTrue(visit.is_completed)
|
||||
|
||||
|
|
@ -50,7 +50,7 @@ class FSMVisitTest(BemadeFSMBaseTest):
|
|||
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(stop_running_timers=True)
|
||||
task.action_fsm_validate()
|
||||
|
||||
self._invoice_sale_order(so)
|
||||
|
||||
|
|
@ -102,23 +102,34 @@ class FSMVisitTest(BemadeFSMBaseTest):
|
|||
self.assertEqual(len(so.order_line), 3)
|
||||
|
||||
def test_marking_visit_task_done_completes_descendants(self):
|
||||
so, visit, sol1, sol2 = self._generate_so_with_one_visit_two_lines()
|
||||
so, visit, sol1, sol2 = self._generate_so_with_one_visit_two_lines_and_descendants()
|
||||
so.action_confirm()
|
||||
parent, child1, child2 = visit.task_id, sol1.task_id, sol2.task_id
|
||||
parent = visit.task_id
|
||||
|
||||
parent.action_fsm_validate(stop_running_timers=True)
|
||||
parent.action_fsm_validate()
|
||||
|
||||
self.assertTrue(parent.is_closed)
|
||||
self.assertTrue(child1.is_closed)
|
||||
self.assertTrue(child2.is_closed)
|
||||
self.assertEqual(sol1.qty_to_deliver, 0)
|
||||
self.assertEqual(sol2.qty_to_deliver, 0)
|
||||
self.assertTrue(visit.is_completed)
|
||||
self._assert_is_done(parent)
|
||||
|
||||
def _assert_is_done(self, task):
|
||||
""" Recursively assert all tasks in a hierarchy are complete """
|
||||
self.assertTrue(task.is_closed)
|
||||
for child in task.child_ids:
|
||||
self._assert_is_done(child)
|
||||
|
||||
def test_marking_visit_task_done_does_not_create_sale_order_line(self):
|
||||
so, visit, sol1, sol2 = self._generate_so_with_one_visit_two_lines()
|
||||
so.action_confirm()
|
||||
|
||||
visit.task_id.action_fsm_validate(stop_running_timers=True)
|
||||
visit.task_id.action_fsm_validate()
|
||||
|
||||
self.assertEqual(len(so.order_line), 3)
|
||||
|
||||
def test_confirming_so_names_visit_properly(self):
|
||||
""" Visits should be named <SO NUMBER> - Visit <visit #> - <visit label>"""
|
||||
so, visit, sol1, sol2 = self._generate_so_with_one_visit_two_lines()
|
||||
|
||||
so.action_confirm()
|
||||
task = visit.task_id
|
||||
|
||||
supposed_name = f"{so.name} - Visit 1 - {visit.label}"
|
||||
self.assertEqual(task.name, supposed_name)
|
||||
|
|
|
|||
|
|
@ -183,11 +183,11 @@ class TestSalesOrder(BemadeFSMBaseTest):
|
|||
subtasks = parent_task._get_all_subtasks()
|
||||
|
||||
# Marking the subtasks done should not increment delivered quantity
|
||||
subtasks.action_fsm_validate()
|
||||
subtasks.action_fsm_validate(True)
|
||||
self.assertEqual(sol.qty_delivered, 0)
|
||||
|
||||
# Marking the top-level tasks done should set the delivered quantity to some non-zero value based on the UOM
|
||||
parent_task.action_fsm_validate()
|
||||
parent_task.action_fsm_validate(True)
|
||||
self.assertTrue(sol.qty_delivered != 0)
|
||||
|
||||
def test_task_contacts_through_sale_order(self):
|
||||
|
|
@ -287,3 +287,49 @@ class TestSalesOrder(BemadeFSMBaseTest):
|
|||
self.assertEqual(task.child_ids[0].description, template.subtasks[0].description)
|
||||
for t in task.child_ids[1:]:
|
||||
self.assertFalse(t.description)
|
||||
|
||||
def test_duplicate_sale_order_duplicates_visits(self):
|
||||
""" Duplicated sales orders should have visits tied to their SO lines as in the original. The copied visits
|
||||
should not have approximate dates set, however."""
|
||||
so, visit, line1, line2 = self._generate_so_with_one_visit_two_lines()
|
||||
|
||||
so2 = so.copy()
|
||||
|
||||
self.assertTrue(so2.visit_ids)
|
||||
visit2 = so2.visit_ids[0]
|
||||
self.assertEqual(so2.order_line[0].visit_id, visit2)
|
||||
self.assertEqual(visit2.label, visit.label)
|
||||
self.assertFalse(visit2.approx_date)
|
||||
|
||||
def test_confirming_sale_order_creates_visit_if_none_created(self):
|
||||
so = self._generate_sale_order()
|
||||
so.company_id.create_default_fsm_visit = True
|
||||
product = self._generate_product()
|
||||
sol = self._generate_sale_order_line(so, product)
|
||||
|
||||
so.action_confirm()
|
||||
|
||||
visit_line = so.order_line.sorted('sequence')[0]
|
||||
self.assertTrue(so.visit_ids)
|
||||
self.assertEqual(visit_line.visit_id, so.visit_ids)
|
||||
|
||||
def test_confirming_sale_order_with_visit_creates_no_new_lines(self):
|
||||
so = self._generate_sale_order()
|
||||
so.company_id.create_default_fsm_visit = True
|
||||
product = self._generate_product()
|
||||
visit = self._generate_visit(so)
|
||||
|
||||
so.action_confirm()
|
||||
|
||||
self.assertEqual(len(so.visit_ids), 1)
|
||||
|
||||
def test_confirming_sale_order_creates_no_visit_if_setting_off(self):
|
||||
so = self._generate_sale_order()
|
||||
so.company_id.create_default_fsm_visit = False
|
||||
product = self._generate_product()
|
||||
sol = self._generate_sale_order_line(so, product)
|
||||
|
||||
so.action_confirm()
|
||||
|
||||
visit_line = so.order_line.sorted('sequence')[0]
|
||||
self.assertFalse(so.visit_ids)
|
||||
|
|
|
|||
|
|
@ -95,6 +95,28 @@
|
|||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
<record id="project_task_view_list_fsm_inherit" model="ir.ui.view">
|
||||
<field name="name">project.task.view.list.fsm.inherit</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="industry_fsm.project_task_view_list_fsm"/>
|
||||
<field name="arch" type="xml">
|
||||
<tree position="attributes">
|
||||
<attribute name="js_class">project_list</attribute>
|
||||
</tree>
|
||||
<field name="partner_id" position="after">
|
||||
<field name="work_order_number" optional="show"/>
|
||||
</field>
|
||||
<field name="company_id" position="attributes">
|
||||
<attribute name="optional">hide</attribute>
|
||||
</field>
|
||||
<field name="worksheet_template_id" position="attributes">
|
||||
<attribute name="optional">hide</attribute>
|
||||
</field>
|
||||
<field name="project_id" position="attributes">
|
||||
<attribute name="optional">hide</attribute>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
<record id="industry_fsm.project_task_action_fsm_map"
|
||||
model="ir.actions.act_window">
|
||||
<field name="domain">[('is_fsm', '=', True),
|
||||
|
|
@ -169,5 +191,30 @@
|
|||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
<record id="project_task_view_search_fsm_inherit" model="ir.ui.view">
|
||||
<field name="name">project.task.view.search.fsm.inherit</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="industry_fsm.project_task_view_search_fsm"/>
|
||||
<field name="arch" type="xml">
|
||||
<filter name="schedule" position="attributes">
|
||||
<attribute name="domain">
|
||||
[
|
||||
'&',
|
||||
('fsm_done', '=', False),
|
||||
'|',
|
||||
('user_ids', '=', False),
|
||||
'&',
|
||||
('planned_date_start', '=', False),
|
||||
('date_deadline', '=', False),
|
||||
]
|
||||
</attribute>
|
||||
</filter>
|
||||
<filter name="my_tasks" position="after">
|
||||
<filter name="is_parent_task"
|
||||
string="Parent Task"
|
||||
domain="[('parent_id', '=', False)]"/>
|
||||
</filter>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
|
|
|||
Loading…
Reference in a new issue