From c9db5cc2a953785c8bff765a37d12bec01d3a79e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Houz=C3=A9fa=20Abbasbhay?= Date: Fri, 12 Jul 2024 14:24:04 +0200 Subject: [PATCH 1/2] [IMP] fieldservice_calendar: Modernize & refactor * Factor out recurse_order_calendar context key checks * Use record sets more * Tests: Factor out order creation & use tests.Form instead of plain create --- fieldservice_calendar/models/calendar.py | 42 ++++++++--------- fieldservice_calendar/models/fsm_order.py | 46 ++++++++----------- fieldservice_calendar/readme/CONTRIBUTORS.rst | 3 ++ .../tests/test_fsm_calendar.py | 44 ++++++------------ 4 files changed, 54 insertions(+), 81 deletions(-) diff --git a/fieldservice_calendar/models/calendar.py b/fieldservice_calendar/models/calendar.py index ec7d2866d9..3728cb8fd9 100644 --- a/fieldservice_calendar/models/calendar.py +++ b/fieldservice_calendar/models/calendar.py @@ -15,38 +15,32 @@ class Meeting(models.Model): def _update_fsm_order_date(self): self.ensure_one() - if self._context.get("recurse_order_calendar"): - # avoid recursion - return to_apply = {} to_apply["scheduled_date_start"] = self.start to_apply["scheduled_duration"] = self.duration - self.fsm_order_id.with_context(recurse_order_calendar=True).write(to_apply) + self.fsm_order_id.write(to_apply) def _update_fsm_assigned(self): # update back fsm_order when an attenndee is member of a team self.ensure_one() - if self._context.get("recurse_order_calendar"): - # avoid recursion - return - person_id = None - for partner in self.partner_ids: - if partner.fsm_person: - person_id = ( - self.env["fsm.person"] - .search([["partner_id", "=", partner.id]], limit=1) - .id - ) - break - self.fsm_order_id.with_context(recurse_order_calendar=True).write( - {"person_id": person_id} - ) + for partner in self.partner_ids.filtered("fsm_person"): + self.fsm_order_id.person_id = self.env["fsm.person"].search( + [("partner_id", "=", partner.id)], limit=1 + ) + break def write(self, values): res = super().write(values) - if self.fsm_order_id: - if "start" in values or "duration" in values: - self._update_fsm_order_date() - if "partner_ids" in values: - self._update_fsm_assigned() + if values.keys() & { + "start", + "duration", + "partner_ids", + } and not self.env.context.get("recurse_order_calendar"): + for event in self.filtered("fsm_order_id").with_context( + recurse_order_calendar=True + ): + if "start" in values or "duration" in values: + event._update_fsm_order_date() + if "partner_ids" in values: + event._update_fsm_assigned() return res diff --git a/fieldservice_calendar/models/fsm_order.py b/fieldservice_calendar/models/fsm_order.py index d374099a78..64e15fd017 100644 --- a/fieldservice_calendar/models/fsm_order.py +++ b/fieldservice_calendar/models/fsm_order.py @@ -53,18 +53,20 @@ def _prepare_calendar_event(self): return vals def write(self, vals): - old_persons = {} - for rec in self: - old_persons[rec.id] = rec.person_id + if "person_id" in vals and not self.env.context.get("recurse_order_calendar"): + old_persons = {order: order.person_id for order in self} res = super().write(vals) to_update = self.create_or_delete_calendar() - with_calendar = to_update.filtered("calendar_event_id") - if "scheduled_date_start" in vals or "scheduled_date_end" in vals: - with_calendar.update_calendar_date(vals) - if "location_id" in vals: - with_calendar.update_calendar_location() - if "person_id" in vals: - with_calendar.update_calendar_person(old_persons) + if not self.env.context.get("recurse_order_calendar"): + with_calendar = to_update.filtered("calendar_event_id").with_context( + recurse_order_calendar=True + ) + if "scheduled_date_start" in vals or "scheduled_date_end" in vals: + with_calendar.update_calendar_date(vals) + if "location_id" in vals: + with_calendar.update_calendar_location() + if "person_id" in vals: + with_calendar.update_calendar_person(old_persons) return res def unlink(self): @@ -84,16 +86,11 @@ def _rm_calendar_event(self): self.calendar_event_id.unlink() def update_calendar_date(self, vals): - if self._context.get("recurse_order_calendar"): - # avoid recursion - return to_apply = {} to_apply["start"] = self.scheduled_date_start to_apply["stop"] = self.scheduled_date_end # always write start and stop in order to calc duration - self.mapped("calendar_event_id").with_context( - recurse_order_calendar=True - ).write(to_apply) + self.calendar_event_id.write(to_apply) def update_calendar_location(self): for rec in self: @@ -104,14 +101,9 @@ def _serialize_location(self): return f"{partner_id.name} {partner_id._display_address()}" def update_calendar_person(self, old_persons): - if self._context.get("recurse_order_calendar"): - # avoid recursion - return - for rec in self: - with_ctx = rec.calendar_event_id.with_context(recurse_order_calendar=True) - if old_persons.get(rec.id): - # remove buddy - with_ctx.partner_ids = [(3, old_persons[rec.id].partner_id.id, False)] - if rec.person_id: - # add the new one - with_ctx.partner_ids = [(4, rec.person_id.partner_id.id, False)] + for order in self: + event = order.calendar_event_id + if person := old_persons.get(order): + event.partner_ids -= person.partner_id # remove buddy + if person := order.person_id: + event.partner_ids += person.partner_id # add new one diff --git a/fieldservice_calendar/readme/CONTRIBUTORS.rst b/fieldservice_calendar/readme/CONTRIBUTORS.rst index 0238b67df8..b9ffec1410 100644 --- a/fieldservice_calendar/readme/CONTRIBUTORS.rst +++ b/fieldservice_calendar/readme/CONTRIBUTORS.rst @@ -1,2 +1,5 @@ * Raphaël Reverdy * Freni Patel +* `XCG Consulting `_: + + * Houzéfa Abbasbhay diff --git a/fieldservice_calendar/tests/test_fsm_calendar.py b/fieldservice_calendar/tests/test_fsm_calendar.py index 40cbeef62f..f58daf0ec7 100644 --- a/fieldservice_calendar/tests/test_fsm_calendar.py +++ b/fieldservice_calendar/tests/test_fsm_calendar.py @@ -2,7 +2,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import fields -from odoo.tests.common import TransactionCase +from odoo.tests.common import Form, TransactionCase class TestFSMOrder(TransactionCase): @@ -18,25 +18,15 @@ def setUpClass(cls): cls.person_id3 = cls.env.ref("fieldservice.person_3") def test_fsm_order_no_duration(self): - new = self.Order.create( - { - "location_id": self.test_location.id, - # no duration = no calendar - } - ) + # not scheduled (no duration) = no calendar + new = self._create_fsm_order(schedule=False) evt = new.calendar_event_id self.assertFalse(evt.exists()) def test_fsm_order_no_calendar_user(self): self.team.calendar_user_id = False # no calendar user_id = no calendar event - new = self.Order.create( - { - "location_id": self.test_location.id, - "scheduled_date_start": fields.Datetime.today(), - "scheduled_duration": 2, - } - ) + new = self._create_fsm_order(schedule=True) evt = new.calendar_event_id self.assertFalse(evt.exists()) self.team.calendar_user_id = self.env.ref("base.partner_root").id @@ -53,14 +43,7 @@ def test_fsm_order_no_calendar_user(self): self.assertFalse(evt.exists()) def test_fsm_order_unlink(self): - # Create an Orders - new = self.Order.create( - { - "location_id": self.test_location.id, - "scheduled_date_start": fields.Datetime.today(), - "scheduled_duration": 2, - } - ) + new = self._create_fsm_order(schedule=True) evt = new.calendar_event_id self.assertTrue(evt.exists()) @@ -72,14 +55,7 @@ def test_fsm_order_unlink(self): self.assertFalse(evt.exists()) def test_fsm_order_ensure_attendee(self): - # Create an Orders - new = self.Order.create( - { - "location_id": self.test_location.id, - "scheduled_date_start": fields.Datetime.today(), - "scheduled_duration": 2, - } - ) + new = self._create_fsm_order(schedule=True) evt = new.calendar_event_id self.assertTrue( len(evt.partner_ids) == 1, @@ -94,3 +70,11 @@ def test_fsm_order_ensure_attendee(self): self.assertTrue( len(evt.partner_ids) == 2, "Not workers should be removed from attendees" ) + + def _create_fsm_order(self, schedule=False): + form = Form(self.Order) + form.location_id = self.test_location + if schedule: + form.scheduled_date_start = fields.Datetime.today() + form.scheduled_duration = 2 + return form.save() From 03d116c8cde0ef9643b1d3155d387fb8faed092e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Houz=C3=A9fa=20Abbasbhay?= Date: Fri, 12 Jul 2024 14:38:55 +0200 Subject: [PATCH 2/2] [IMP] fieldservice_calendar: Sync description MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Propagate description updates between FSM order & calendar event. 1 is an HTML field, the other is not; we handle conversion. Before this change, description was only propagated at calendar event creation, and without text➔html conversion. --- fieldservice_calendar/models/calendar.py | 8 +++++++- fieldservice_calendar/models/fsm_order.py | 11 +++++++++-- fieldservice_calendar/tests/test_fsm_calendar.py | 11 +++++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/fieldservice_calendar/models/calendar.py b/fieldservice_calendar/models/calendar.py index 3728cb8fd9..1862ab7a69 100644 --- a/fieldservice_calendar/models/calendar.py +++ b/fieldservice_calendar/models/calendar.py @@ -1,7 +1,7 @@ # Copyright (C) 2021 Raphaël Reverdy # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import fields, models +from odoo import fields, models, tools class Meeting(models.Model): @@ -29,12 +29,16 @@ def _update_fsm_assigned(self): ) break + def _update_fsm_order_description(self): + self.fsm_order_id.description = tools.html2plaintext(self.description or "") + def write(self, values): res = super().write(values) if values.keys() & { "start", "duration", "partner_ids", + "description", } and not self.env.context.get("recurse_order_calendar"): for event in self.filtered("fsm_order_id").with_context( recurse_order_calendar=True @@ -43,4 +47,6 @@ def write(self, values): event._update_fsm_order_date() if "partner_ids" in values: event._update_fsm_assigned() + if "description" in values: + event._update_fsm_order_description() return res diff --git a/fieldservice_calendar/models/fsm_order.py b/fieldservice_calendar/models/fsm_order.py index 64e15fd017..4e1c05e00b 100644 --- a/fieldservice_calendar/models/fsm_order.py +++ b/fieldservice_calendar/models/fsm_order.py @@ -1,7 +1,7 @@ # Copyright (C) 2021 Raphaël Reverdy # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models +from odoo import api, fields, models, tools class FSMOrder(models.Model): @@ -37,7 +37,7 @@ def _prepare_calendar_event(self): model_id = self.env.ref("fieldservice.model_fsm_order").id vals = { "name": self.name, - "description": self.description, + "description": tools.plaintext2html(self.description or ""), "start": self.scheduled_date_start, "stop": self.scheduled_date_end, "allday": False, @@ -63,6 +63,8 @@ def write(self, vals): ) if "scheduled_date_start" in vals or "scheduled_date_end" in vals: with_calendar.update_calendar_date(vals) + if "description" in vals: + with_calendar.update_calendar_description() if "location_id" in vals: with_calendar.update_calendar_location() if "person_id" in vals: @@ -92,6 +94,11 @@ def update_calendar_date(self, vals): # always write start and stop in order to calc duration self.calendar_event_id.write(to_apply) + def update_calendar_description(self): + for order in self: + html_description = tools.plaintext2html(order.description or "") + order.calendar_event_id.description = html_description + def update_calendar_location(self): for rec in self: rec.calendar_event_id.location = rec._serialize_location() diff --git a/fieldservice_calendar/tests/test_fsm_calendar.py b/fieldservice_calendar/tests/test_fsm_calendar.py index f58daf0ec7..be6c4c30cc 100644 --- a/fieldservice_calendar/tests/test_fsm_calendar.py +++ b/fieldservice_calendar/tests/test_fsm_calendar.py @@ -71,6 +71,17 @@ def test_fsm_order_ensure_attendee(self): len(evt.partner_ids) == 2, "Not workers should be removed from attendees" ) + def test_description_sync(self): + fsm_order = self._create_fsm_order(schedule=True) + event = fsm_order.calendar_event_id + self.assertEqual(event.description, "

") + with Form(fsm_order) as form: + form.description = "line 1\nline 2" + self.assertEqual(event.description, "

line 1
line 2

") + with Form(event) as form: + form.description = "

line 1
line 2
line 3

" + self.assertEqual(fsm_order.description, "line 1\nline 2\nline 3") + def _create_fsm_order(self, schedule=False): form = Form(self.Order) form.location_id = self.test_location