diff --git a/bemade_geo_routing/__init__.py b/bemade_geo_routing/__init__.py deleted file mode 100644 index 0650744..0000000 --- a/bemade_geo_routing/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import models diff --git a/bemade_geo_routing/__manifest__.py b/bemade_geo_routing/__manifest__.py deleted file mode 100644 index 5d080f1..0000000 --- a/bemade_geo_routing/__manifest__.py +++ /dev/null @@ -1,35 +0,0 @@ -# -# Bemade Inc. -# -# Copyright (C) July 2023 Bemade Inc. (). -# Author: Marc Durepos (Contact : marc@bemade.org) -# -# This program is under the terms of the Odoo Proprietary License v1.0 (OPL-1) -# It is forbidden to publish, distribute, sublicense, or sell copies of the Software -# or modified copies of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. -# -{ - 'name': 'Bemade Geo Distance', - 'version': '17.0.1.0.0', - 'summary': 'Utility module for calculating the distance driving between addresses.', - 'description': """Module for calculating the driving distance between addresses using the Google Route API. - **IMPORTANT NOTE:** You must have an active Google Route API key configured in the Geo Localization - settings (under General Settings).""", - 'category': 'Generic Modules/Others', - 'author': 'Bemade Inc.', - 'website': 'https://www.bemade.org', - 'license': 'OPL-1', - 'depends': ['base_geolocalize'], - 'data': [], - 'demo': [], - 'installable': True, - 'auto_install': False, -} diff --git a/bemade_geo_routing/models/__init__.py b/bemade_geo_routing/models/__init__.py deleted file mode 100644 index ecf0446..0000000 --- a/bemade_geo_routing/models/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import bemade_geo_router diff --git a/bemade_geo_routing/models/bemade_geo_router.py b/bemade_geo_routing/models/bemade_geo_router.py deleted file mode 100644 index 4a54392..0000000 --- a/bemade_geo_routing/models/bemade_geo_router.py +++ /dev/null @@ -1,95 +0,0 @@ -from odoo import models, fields, tools, _, api -from odoo.exceptions import UserError -import requests -import logging -import datetime -from typing import Literal - -_logger = logging.getLogger(__name__) - - -class GeoRouter(models.AbstractModel): - """ Abstract class used to call Google Route API and convert responses - into useful distance and route data. - """ - _name = 'base.geo_router' - _description = "Geographic Router" - - units = Literal['metric', 'imperial'] - - @api.model - def _get_api_key(self): - apikey = self.env['ir.config_parameter'].sudo().get_param('base_geolocalize.google_map_api_key') - if not apikey: - raise UserError(_( - "API key for GeoCoding (Places) required.\n" - "Visit https://developers.google.com/maps/documentation/geocoding/get-api-key for more information." - )) - return apikey - - @api.model - def get_driving_distance_time(self, origin, destination, departure_time: datetime.datetime = None, - arrival_time: datetime.datetime = None, units: units = 'metric', - avoid_tolls=False, avoid_highways=False, avoid_ferries=False) -> (float, float): - """ Calculates the route between two addresses and returns the distance it either in Kilometers or Miles and - the time in minutes. - - :param origin: The origin address (res.partner) from which to drive (origin). - :param destination: The destination address (res.partner). - :param departure_time: The departure time to use when planning the route (traffic aware). - Cannot be set simultaneously with arrival_time. - :param arrival_time: The arrival time to use when planning the route (traffic aware). - Cannot be set simultaneously with departure_time. - :param units: Type of units to return: 'imperial' for Miles, 'metric' for Kilometers. - :param avoid_tolls: Whether Google should calculate a route avoiding tolls. - :param avoid_highways: Whether Google should calculate a route avoiding highways. - :param avoid_ferries: Whether Google should calculate a route avoiding ferries. - :returns: tuple(distance: float, time: float) - """ - if departure_time and arrival_time: - raise ValueError("Route cannot be calculated with both an arrival time and departure time.") - apikey = self._get_api_key() - origin_addr = self.env['base.geocoder'].geo_query_address(street=origin.street, zip=origin.zip, - city=origin.city, state=origin.state_id.name, - country=origin.country_id.name) - destination_addr = self.env['base.geocoder'].geo_query_address(street=destination.street, zip=destination.zip, - city=destination.city, - state=destination.state_id.name, - country=destination.country_id.name) - params = { - 'origin': { - 'address': origin_addr, - }, - 'destination': { - 'address': destination_addr, - }, - 'travelMode': 'DRIVE', - 'computeAlternativeRoutes': False, - 'routeModifiers': { - 'avoidTolls': avoid_tolls, - 'avoidHighways': avoid_highways, - 'avoidFerries': avoid_ferries, - }, - 'languageCode': 'en-US', - 'units': units, - } - if departure_time or arrival_time: - params.update({ - 'routingPreference': 'TRAFFIC_AWARE', - 'departureTime' if departure_time else 'arrivalTime': departure_time or arrival_time - }) - - url = "https://routes.googleapis.com/directions/v2:computeRoutes" - distance_field = 'distanceMeters' if units == 'metric' else 'distanceMiles' - result = requests.post(url, - json=params, - headers={'X-Goog-Api-Key': apikey, - 'Content-Type': 'application/json', - 'X-Goog-FieldMask': f"routes.duration,routes.{distance_field}"}).json() - if 'routes' not in result: - _logger.error(f"Failed to retrieve route between {origin_addr} and {destination_addr}. " - f"Google response: {result}.") - route = result['routes'][0] - distance = (route[distance_field] / 1000) if units == 'metric' else route[distance_field] - time = int(route['duration'].strip('s')) / 60 - return distance, time diff --git a/bemade_geo_routing/tests/__init__.py b/bemade_geo_routing/tests/__init__.py deleted file mode 100644 index 51717cf..0000000 --- a/bemade_geo_routing/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import test_geo_router diff --git a/bemade_geo_routing/tests/test_geo_router.py b/bemade_geo_routing/tests/test_geo_router.py deleted file mode 100644 index 11de053..0000000 --- a/bemade_geo_routing/tests/test_geo_router.py +++ /dev/null @@ -1,53 +0,0 @@ -from odoo.tests import TransactionCase, tagged -import datetime -import pytz - -@tagged('-at_install', 'post_install') -class TestGeoRouter(TransactionCase): - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.apikey = cls.env['base.geo_router']._get_api_key() - - def test_distance_time_basic(self): - bemade, montreal = self._generate_partners() - - distance, time = self.env['base.geo_router'].get_driving_distance_time(bemade, montreal) - - self.assertTrue(20 < distance < 40) - self.assertTrue(12 < time < 60) - - def test_time_traffic(self): - bemade, montreal = self._generate_partners() - est = pytz.timezone('US/Eastern') - today_naive = datetime.date.today() - today = datetime.datetime(today_naive.year, today_naive.month, today_naive.day, 8, 30, tzinfo=est) - arrival_time_local = today + datetime.timedelta(days=(2 - today.weekday()) % 7) - arrival_time_utc = arrival_time_local.astimezone(pytz.utc) - arrival_time = arrival_time_utc.strftime('%Y-%m-%dT%H:%M:%S.%fZ') - print(arrival_time) - - distance, time = self.env['base.geo_router'].get_driving_distance_time(bemade, montreal, - arrival_time=arrival_time) - - self.assertTrue(25 < time < 90) - - @classmethod - def _generate_partners(self): - bemade = self.env['res.partner'].create({ - 'name': 'Bemade Inc.', - 'street': '255 North Montcalm Blvd.', - 'city': 'Candiac', - 'zip': 'J5R 3L6', - 'state_id': self.env.ref('base.state_ca_qc').id, - 'country_id': self.env.ref('base.ca').id, - }) - montreal = self.env['res.partner'].create({ - 'name': 'City of Montreal', - 'street': '220 East René-Lévesque Blvd.', - 'city': 'Montreal', - 'zip': 'H2X 3A7', - 'state_id': self.env.ref('base.state_ca_qc').id, - 'country_id': self.env.ref('base.ca').id, - }) - return bemade, montreal diff --git a/bemade_planning_travel/__init__.py b/bemade_planning_travel/__init__.py deleted file mode 100644 index 0650744..0000000 --- a/bemade_planning_travel/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import models diff --git a/bemade_planning_travel/__manifest__.py b/bemade_planning_travel/__manifest__.py deleted file mode 100644 index b0fb88d..0000000 --- a/bemade_planning_travel/__manifest__.py +++ /dev/null @@ -1,40 +0,0 @@ -# -# Bemade Inc. -# -# Copyright (C) July 2023 Bemade Inc. (). -# Author: Marc Durepos (Contact : marc@bemade.org) -# -# This program is under the terms of the Odoo Proprietary License v1.0 (OPL-1) -# It is forbidden to publish, distribute, sublicense, or sell copies of the Software -# or modified copies of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. -# -{ - 'name': 'Planning Travel Time', - 'version': '15.0.1.0.0', - 'summary': "Plan travel time for services planned via Odoo's Planning module.", - 'description': """ - Adds the ability to add travel planning records to existing planning records - linked to sale order lines. Travel planning records book time in the scheduled - resource's calendar. Travel distance is based on the resource's work address, if - available. Otherwise, travel is assumed to be between the client location and - the company's address.""", - 'category': 'Services/Field Service', - 'author': 'Bemade Inc.', - 'website': 'https://www.bemade.org', - 'license': 'OPL-1', - 'depends': ['sale_planning', - 'industry_fsm', - 'bemade_geo_routing'], - 'data': [''], - 'demo': [''], - 'installable': True, - 'auto_install': False, -} diff --git a/bemade_planning_travel/models/__init__.py b/bemade_planning_travel/models/__init__.py deleted file mode 100644 index a3bec19..0000000 --- a/bemade_planning_travel/models/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import planning_slot diff --git a/bemade_planning_travel/models/planning_slot.py b/bemade_planning_travel/models/planning_slot.py deleted file mode 100644 index e9ab27c..0000000 --- a/bemade_planning_travel/models/planning_slot.py +++ /dev/null @@ -1,42 +0,0 @@ -from odoo import models, fields, api - - -class PlanningSlot(models.Model): - _inherit = "planning.slot" - - inbound_travel_slot = fields.Many2one(comodel_name="planning.slot", - string="Inbound Travel Plan", ) - outbound_travel_slot = fields.Many2one(comodel_name="planning.slot", - string="Outbound Travel Plan") - - # Fields for travel slots - travel_origin_slot = fields.One2many(comodel_name="planning.slot", - inverse_name="outbound_travel_slot") - travel_destination_slot = fields.One2many(comodel_name="planning.slot", - inverse_name="inbound_travel_slot") - is_travel_slot = fields.Boolean() - - def action_plan_travel(self): - # Get previous and next planning records for the same day, same resource - for rec in self: - prev_slot = rec._get_previous_same_day_same_resource_slot() - next_slot = rec._get_next_same_day_same_resource_slot() - - - def _get_previous_same_day_same_resource_slot(self): - start = self.start_datetime.replace(hour=0, minute=0, second=0, - microsecond=0) - domain = [('is_travel_slot', '=', False), - ('start_datetime', '>=', start), - ('end_datetime', '<=', self.start_datetime), - ('resource_id', '=', self.resource_id.id)] - return self.env['planning.slot'].search(domain) - - def _get_next_same_day_same_resource_slot(self): - end = self.end_datetime.replace(hour=23, minute=59, second=59, - microsecond=999999) - domain = [('is_travel_slot', '=', False), - ('start_datetime', '>=', self.end_datetime), - ('end_datetime', '<=', end), - ('resource_id', '=', self.resource_id.id)] - return self.env['planning.slot'].search(domain)