diff --git a/shipment_advice_planner_toursolver/__manifest__.py b/shipment_advice_planner_toursolver/__manifest__.py index 7a40fef5..226da9f9 100644 --- a/shipment_advice_planner_toursolver/__manifest__.py +++ b/shipment_advice_planner_toursolver/__manifest__.py @@ -8,10 +8,9 @@ "license": "AGPL-3", "author": "ACSONE SA/NV,Odoo Community Association (OCA)", "website": "https://github.com/OCA/stock-logistics-transport", - "depends": ["shipment_advice_planner"], + "depends": ["shipment_advice_planner", "base_time_window"], "data": [ # data - "data/toursolver_delivery_week_day.xml", "data/toursolver_backend.xml", "data/ir_ui_menu.xml", "data/ir_sequence.xml", @@ -20,7 +19,6 @@ "security/toursolver_backend.xml", "security/toursolver_resource.xml", "security/toursolver_task.xml", - "security/toursolver_delivery_week_day.xml", "security/toursolver_delivery_window.xml", # views "views/toursolver_backend.xml", diff --git a/shipment_advice_planner_toursolver/data/toursolver_delivery_week_day.xml b/shipment_advice_planner_toursolver/data/toursolver_delivery_week_day.xml deleted file mode 100644 index aa08bb4d..00000000 --- a/shipment_advice_planner_toursolver/data/toursolver_delivery_week_day.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - 0 - - - 1 - - - 2 - - - 3 - - - 4 - - - 5 - - - 6 - - diff --git a/shipment_advice_planner_toursolver/models/__init__.py b/shipment_advice_planner_toursolver/models/__init__.py index e507edb5..8585920e 100644 --- a/shipment_advice_planner_toursolver/models/__init__.py +++ b/shipment_advice_planner_toursolver/models/__init__.py @@ -5,6 +5,5 @@ from . import toursolver_task from . import stock_picking from . import toursolver_delivery_window -from . import toursolver_delivery_week_day from . import res_partner from . import shipment_advice diff --git a/shipment_advice_planner_toursolver/models/res_partner.py b/shipment_advice_planner_toursolver/models/res_partner.py index 9dea407b..215be39a 100644 --- a/shipment_advice_planner_toursolver/models/res_partner.py +++ b/shipment_advice_planner_toursolver/models/res_partner.py @@ -22,37 +22,14 @@ def _get_delivery_windows(self, day_name): """ Return the list of delivery windows by partner id for the given day. - :param day: The day name (see toursolver.delivery.week.day) + :param day: The day name (see time.weekday) :return: dict partner_id:[delivery_window, ] """ self.ensure_one() - week_day_id = self.env["toursolver.delivery.week.day"]._get_id_by_name(day_name) + week_day_id = self.env["time.weekday"]._get_id_by_name(day_name) return self.env["toursolver.delivery.window"].search( - [("partner_id", "=", self.id), ("week_day_ids", "in", week_day_id)] + [ + ("partner_id", "=", self.id), + ("time_window_weekday_ids", "in", week_day_id), + ] ) - - def _get_delivery_sequence(self, day_name): - """ - Return a sequence position by partner id for the given day. - - The sequence is computed from the delivery_widow start - - :param day: The day name (see toursolver.delivery.week.day) - :return: dict partner_id:sequence - """ - week_day_id = self.env["toursolver.delivery.week.day"]._get_id_by_name(day_name) - res = {} - windows = self.env["toursolver.delivery.window"].search( - [("partner_id", "in", self.ids), ("week_day_ids", "in", week_day_id)], - order="start ASC", - ) - i = 1 - for window in windows.sorted("start"): - if window.partner_id.id not in res: - res[window.partner_id.id] = i - i += 1 - for partner in self.sorted("name"): - if partner.id not in res: - res[partner.id] = i - i += 1 - return res diff --git a/shipment_advice_planner_toursolver/models/toursolver_delivery_week_day.py b/shipment_advice_planner_toursolver/models/toursolver_delivery_week_day.py deleted file mode 100644 index c9c85971..00000000 --- a/shipment_advice_planner_toursolver/models/toursolver_delivery_week_day.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright 2020 ACSONE SA/NV -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import _, api, fields, models, tools - - -class TourSolverDeliveryWeekDay(models.Model): - - _name = "toursolver.delivery.week.day" - _description = "Delivery Week Day" - - name = fields.Selection( - selection=[ - ("0", "Monday"), - ("1", "Tuesday"), - ("2", "Wednesday"), - ("3", "Thursday"), - ("4", "Friday"), - ("5", "Saturday"), - ("6", "Sunday"), - ], - required=True, - ) - _sql_constraints = [("name_uniq", "UNIQUE(name)", _("Name must be unique"))] - - @api.model - @tools.ormcache("name") - def _get_id_by_name(self, name): - return self.search([("name", "=", name)], limit=1).id - - @api.depends("name") - def _compute_display_name(self): - """ - WORKAROUND since Odoo doesn't handle properly records where name is. - - a selection - """ - translated_values = dict(self._fields["name"]._description_selection(self.env)) - for record in self: - record.display_name = translated_values[record.name] - - def name_get(self): - """ - WORKAROUND since Odoo doesn't handle properly records where name is. - - a selection - """ - return [(r.id, r.display_name) for r in self] diff --git a/shipment_advice_planner_toursolver/models/toursolver_delivery_window.py b/shipment_advice_planner_toursolver/models/toursolver_delivery_window.py index fcf7f940..9b8e3edf 100644 --- a/shipment_advice_planner_toursolver/models/toursolver_delivery_window.py +++ b/shipment_advice_planner_toursolver/models/toursolver_delivery_window.py @@ -1,25 +1,18 @@ # Copyright 2020 ACSONE SA/NV # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -import math -from psycopg2.extensions import AsIs - -from odoo import _, api, fields, models -from odoo.exceptions import ValidationError +from odoo import fields, models class ToursolverDeliveryWindow(models.Model): _name = "toursolver.delivery.window" + _inherit = "time.window.mixin" _description = "TourSolver Delivery Window" - _order = "partner_id, start" + _order = "partner_id, time_window_start" + _time_window_overlap_check_field = "partner_id" - start = fields.Float("From", required=True) - end = fields.Float("To", required=True) - week_day_ids = fields.Many2many( - comodel_name="toursolver.delivery.week.day", required=True - ) partner_id = fields.Many2one( comodel_name="res.partner", required=True, index=True, ondelete="cascade" ) @@ -28,69 +21,3 @@ class ToursolverDeliveryWindow(models.Model): required=True, default="preferable", ) - - @api.constrains("start", "end", "week_day_ids") - def _check_window_no_onverlaps(self): - week_days_field = self._fields["week_day_ids"] - for record in self: - if record.start > record.end: - raise ValidationError(_("End time must be higher than start time.")) - # here we use a plain SQL query to benefit of the numrange - # function available in PostgresSQL - # (http://www.postgresql.org/docs/current/static/rangetypes.html) - sql_query = """ - SELECT - id - FROM - %(table)s w - join %(relation)s as d - on d.%(relation_window_fkey)s = w.id - WHERE - NUMRANGE(w.start::numeric, w.end::numeric) && - NUMRANGE(%(start)s::numeric, %(end)s::numeric) - AND w.id != %(window_id)s - AND d.%(relation_week_day_fkey)s in %(week_day_ids)s - AND w.partner_id = %(partner_id)s""" - self.env.cr.execute( - sql_query, - dict( - table=AsIs(self._table), - relation=AsIs(week_days_field.relation), - relation_window_fkey=AsIs(week_days_field.column1), - relation_week_day_fkey=AsIs(week_days_field.column2), - start=record.start, - end=record.end, - window_id=record.id, - week_day_ids=tuple(record.week_day_ids.ids), - partner_id=record.partner_id.id, - ), - ) - res = self.env.cr.fetchall() - if res: - other = self.browse(res[0][0]) - raise ValidationError( - _("%(rec_display_name)s overlaps %(other_display_name)s") - % dict( - rec_display_name=record.display_name, - other_display_name=other.display_name, - ) - ) - - @api.depends("start", "end", "week_day_ids") - def _compute_display_name(self): - for record in self: - days = ", ".join(record.week_day_ids.mapped("display_name")) - start = self.float_to_time_repr(record.start) - end = self.float_to_time_repr(record.end) - record.display_name = f"{days}: From {start} to {end}" - - @api.model - def float_to_time_repr(self, value): - pattern = "%02d:%02d" - hour = math.floor(value) - min_ = round((value % 1) * 60) - if min_ == 60: - min_ = 0 - hour += 1 - - return pattern % (hour, min_) diff --git a/shipment_advice_planner_toursolver/models/toursolver_resource.py b/shipment_advice_planner_toursolver/models/toursolver_resource.py index ffd5e50d..7fc6fea3 100644 --- a/shipment_advice_planner_toursolver/models/toursolver_resource.py +++ b/shipment_advice_planner_toursolver/models/toursolver_resource.py @@ -24,7 +24,7 @@ class ToursolverResource(models.Model): ) use_delivery_person_coordinates_as_end = fields.Boolean( help="If true the computed delivery will end at the delivery person's " - "address. Otherwise it will end at the Alcyon warehouse" + "address. Otherwise it will end at the warehouse address" ) partner_id = fields.Many2one(comodel_name="res.partner", string="Contact") diff --git a/shipment_advice_planner_toursolver/models/toursolver_task.py b/shipment_advice_planner_toursolver/models/toursolver_task.py index e9f2b1c4..9b244a65 100644 --- a/shipment_advice_planner_toursolver/models/toursolver_task.py +++ b/shipment_advice_planner_toursolver/models/toursolver_task.py @@ -291,8 +291,10 @@ def _toursolver_json_request_order_time_window(self, partner): for window in delivery_windows: time_windows.append( { - "beginTime": window.float_to_time_repr(window.start), - "endTime": window.float_to_time_repr(window.end), + "beginTime": window.float_to_time_repr( + window.time_window_start + ), + "endTime": window.float_to_time_repr(window.time_window_end), } ) else: diff --git a/shipment_advice_planner_toursolver/security/toursolver_delivery_week_day.xml b/shipment_advice_planner_toursolver/security/toursolver_delivery_week_day.xml deleted file mode 100644 index 35f3a29a..00000000 --- a/shipment_advice_planner_toursolver/security/toursolver_delivery_week_day.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - toursolver.delivery.window.day access read - - - - - - - - diff --git a/shipment_advice_planner_toursolver/tests/test_toursolver_delivery_window.py b/shipment_advice_planner_toursolver/tests/test_toursolver_delivery_window.py index 0134a3aa..62aba4d0 100644 --- a/shipment_advice_planner_toursolver/tests/test_toursolver_delivery_window.py +++ b/shipment_advice_planner_toursolver/tests/test_toursolver_delivery_window.py @@ -13,12 +13,8 @@ def setUpClass(cls): cls.partner_model = cls.env["res.partner"] cls.partner_1 = cls.partner_model.create({"name": "partner 1"}) cls.partner_2 = cls.partner_model.create({"name": "patner 2"}) - cls.monday = cls.env.ref( - "shipment_advice_planner_toursolver.toursolver_delivery_weed_day_monday" - ) - cls.sunday = cls.env.ref( - "shipment_advice_planner_toursolver.toursolver_delivery_weed_day_sunday" - ) + cls.monday = cls.env.ref("base_time_window.time_weekday_monday") + cls.sunday = cls.env.ref("base_time_window.time_weekday_sunday") def test_00(self): """ @@ -35,16 +31,16 @@ def test_00(self): self.delivery_window_model.create( { "partner_id": self.partner_1.id, - "start": 10.0, - "end": 12.0, - "week_day_ids": [(4, self.monday.id)], + "time_window_start": 10.0, + "time_window_end": 12.0, + "time_window_weekday_ids": [(4, self.monday.id)], } ) self.assertTrue(self.partner_1.toursolver_delivery_window_ids) delivery_window = self.partner_1.toursolver_delivery_window_ids - self.assertEqual(delivery_window.start, 10.0) - self.assertEqual(delivery_window.end, 12.0) - self.assertEqual(delivery_window.week_day_ids, self.monday) + self.assertEqual(delivery_window.time_window_start, 10.0) + self.assertEqual(delivery_window.time_window_end, 12.0) + self.assertEqual(delivery_window.time_window_weekday_ids, self.monday) def test_01(self): """ @@ -63,9 +59,9 @@ def test_01(self): self.delivery_window_model.create( { "partner_id": self.partner_1.id, - "start": 10.0, - "end": 12.0, - "week_day_ids": [(4, self.monday.id)], + "time_window_start": 10.0, + "time_window_end": 12.0, + "time_window_weekday_ids": [(4, self.monday.id)], } ) self.assertTrue(self.partner_1.toursolver_delivery_window_ids) @@ -91,18 +87,21 @@ def test_02(self): self.delivery_window_model.create( { "partner_id": self.partner_1.id, - "start": 10.0, - "end": 12.0, - "week_day_ids": [(4, self.monday.id)], + "time_window_start": 10.0, + "time_window_end": 12.0, + "time_window_weekday_ids": [(4, self.monday.id)], } ) with self.assertRaises(ValidationError): self.delivery_window_model.create( { "partner_id": self.partner_1.id, - "start": 11.0, - "end": 13.0, - "week_day_ids": [(4, self.monday.id), (4, self.sunday.id)], + "time_window_start": 11.0, + "time_window_end": 13.0, + "time_window_weekday_ids": [ + (4, self.monday.id), + (4, self.sunday.id), + ], } ) @@ -122,18 +121,18 @@ def test_03(self): self.delivery_window_model.create( { "partner_id": self.partner_1.id, - "start": 10.0, - "end": 12.0, - "week_day_ids": [(4, self.monday.id)], + "time_window_start": 10.0, + "time_window_end": 12.0, + "time_window_weekday_ids": [(4, self.monday.id)], } ) self.assertTrue(self.partner_1.toursolver_delivery_window_ids) self.delivery_window_model.create( { "partner_id": self.partner_1.id, - "start": 11.0, - "end": 13.0, - "week_day_ids": [(4, self.sunday.id)], + "time_window_start": 11.0, + "time_window_end": 13.0, + "time_window_weekday_ids": [(4, self.sunday.id)], } ) self.assertEqual(len(self.partner_1.toursolver_delivery_window_ids), 2) @@ -155,9 +154,9 @@ def test_04(self): self.delivery_window_model.create( { "partner_id": self.partner_1.id, - "start": 10.0, - "end": 12.0, - "week_day_ids": [(4, self.monday.id)], + "time_window_start": 10.0, + "time_window_end": 12.0, + "time_window_weekday_ids": [(4, self.monday.id)], } ) self.assertTrue(self.partner_1.toursolver_delivery_window_ids) @@ -165,9 +164,9 @@ def test_04(self): self.delivery_window_model.create( { "partner_id": self.partner_2.id, - "start": 10.0, - "end": 12.0, - "week_day_ids": [(4, self.monday.id)], + "time_window_start": 10.0, + "time_window_end": 12.0, + "time_window_weekday_ids": [(4, self.monday.id)], } ) self.assertTrue(self.partner_2.toursolver_delivery_window_ids) @@ -178,7 +177,7 @@ def test_05(self): Partner 1 without delivery window Test Case: - Add a delivery window to partner 1 with end > start + Add a delivery window to partner 1 with time_window_end > time_window_start Expected result: ValidationError is raised """ @@ -186,8 +185,8 @@ def test_05(self): self.delivery_window_model.create( { "partner_id": self.partner_1.id, - "start": 14.0, - "end": 12.0, - "week_day_ids": [(4, self.monday.id)], + "time_window_start": 14.0, + "time_window_end": 12.0, + "time_window_weekday_ids": [(4, self.monday.id)], } ) diff --git a/shipment_advice_planner_toursolver/views/res_partner.xml b/shipment_advice_planner_toursolver/views/res_partner.xml index a54d04cd..89e51418 100644 --- a/shipment_advice_planner_toursolver/views/res_partner.xml +++ b/shipment_advice_planner_toursolver/views/res_partner.xml @@ -9,10 +9,10 @@ - - + + - + diff --git a/shipment_advice_planner_toursolver/views/toursolver_delivery_window.xml b/shipment_advice_planner_toursolver/views/toursolver_delivery_window.xml index e88cceb7..fd875e1a 100644 --- a/shipment_advice_planner_toursolver/views/toursolver_delivery_window.xml +++ b/shipment_advice_planner_toursolver/views/toursolver_delivery_window.xml @@ -7,7 +7,7 @@ - + - - + + - +