Skip to content

Commit

Permalink
[ADD] pos_partner_location_google_map: Module added.
Browse files Browse the repository at this point in the history
  • Loading branch information
geomer198 committed Nov 28, 2023
1 parent 34570ca commit 1a44739
Show file tree
Hide file tree
Showing 19 changed files with 563 additions and 0 deletions.
Empty file.
1 change: 1 addition & 0 deletions pos_partner_location_google_map/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
18 changes: 18 additions & 0 deletions pos_partner_location_google_map/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "POS Partner Location Google Map",
"version": "16.0.1.0.0",
"category": "Point Of Sale",
"summary": "POS Partner Location Google Map",
"author": "Cetmix, Odoo Community Association (OCA)",
"maintainers": ["geomer198", "CetmixGitDrone"],
"website": "https://github.com/OCA/pos",
"license": "AGPL-3",
"depends": ["pos_partner_location_abstract"],
"data": [],
"assets": {
"point_of_sale.assets": [
"pos_partner_location_google_map/static/src/js/*.js",
],
},
"installable": True,
}
3 changes: 3 additions & 0 deletions pos_partner_location_google_map/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import address_google_struct
from . import base_geocoder
from . import pos_config
137 changes: 137 additions & 0 deletions pos_partner_location_google_map/models/address_google_struct.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
from odoo.osv.expression import AND, OR

from odoo.addons.pos_partner_location_abstract.models.address_struct import (
AddressStruct,
)


class AddressGoogleStruct(AddressStruct):
ADDR_FIELDS = {
"number": ["street_number"],
"street": ["street_address", "route", "plus_code"],
"city": [
"locality",
"sublocality",
"sublocality_level_1",
"sublocality_level_2",
"sublocality_level_3",
"sublocality_level_4",
],
"state_id": [
"administrative_area_level_1",
"administrative_area_level_2",
"administrative_area_level_3",
"administrative_area_level_4",
"administrative_area_level_5",
],
"country_id": ["country"],
"zip": ["postal_code"],
}
SERVICE_URL = "https://maps.googleapis.com/maps/api/place/details/json"

def __init__(self, odoo_env):
super(AddressGoogleStruct, self).__init__(odoo_env)
self.api_key = (
self.env["ir.config_parameter"]
.sudo()
.get_param("base_geolocalize.google_map_api_key", False)
)

def has_token(self):
"""Checking exists Google API key in settings"""
return bool(self.api_key)

def _get_fields_value(self, addr_key):
"""
Get fields value for preparing values address
:param str addr_key: field name
:return list: list of fields values
"""
return [self._result.get(key, False) for key in self.ADDR_FIELDS[addr_key]]

@property
def street(self):
street_number = self._result.get("street_number")
result = []
if street_number:
result.append(street_number)
for address in self._get_fields_value("street"):
if address:
result.append(address)
return " ".join(result)
return ""

@property
def city(self):
for city in self._get_fields_value("city"):
if city:
return city
return False

@property
def state_id(self):
domain = []
codes = []
for state in self._get_fields_value("state_id"):
if state:
domain = OR([domain, [("name", "like", state.get("name"))]])
codes.append(state.get("code"))
if codes:
domain = OR([domain, [("code", "in", codes)]])
if len(domain) == 0:
return False
country_id = self.country_id
if country_id:
domain = AND([domain, [("country_id", "=", country_id[0])]])
state = self.env["res.country.state"].search(domain, limit=1)
return state.name_get()[0] if state else False

@property
def country_id(self):
country_item = self._result.get("country")
if not country_item:
return False
country = self.env["res.country"].search(
[
"|",
("name", "like", country_item.get("name")),
("code", "=", country_item.get("code")),
]
)
return country.name_get()[0] if country else False

@property
def zip(self):
return self._result.get("postal_code")

def query_addr(self, params, timeout=5):
params.update(key=self.api_key)
response = super(AddressGoogleStruct, self).query_addr(params, timeout=timeout)
if response:
if response.get("status") == "OK":
self._prepare_components_data(response["result"]["address_components"])
return True
return False

def _prepare_components_data(self, components):
"""
Preparing component values for class result
:param dict components: list of components
:return: None
:rtype: NoneType
"""
state_country_fields = [
*self.ADDR_FIELDS.get("state_id", []),
*self.ADDR_FIELDS.get("country_id", []),
]
for component in components:
for type_ in component["types"]:
if type_ in state_country_fields:
self._result[type_] = {
"code": component["short_name"],
"name": component["long_name"],
}
elif type_ in self.ADDR_FIELDS.get("street", []):
self._result[type_] = component["short_name"]
else:
self._result[type_] = component["long_name"]
26 changes: 26 additions & 0 deletions pos_partner_location_google_map/models/base_geocoder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from odoo import _, api, models

from .address_google_struct import AddressGoogleStruct


class GeoProvider(models.AbstractModel):
_inherit = "base.geocoder"

@api.model
def prepare_geo_address_googlemap(self, place_id):
"""
Prepare Address values by place id
:param str place_id: Google map place id
:return dict: address fields values
"""
google = AddressGoogleStruct(self.env)
if not google.has_token():
raise models.UserError(
_(
"API key for GeoCoding (Places) required.\n"
"Visit https://developers.google.com/maps/documentation/geocoding/get-api-key " # noqa
"for more information."
)
)
status = google.query_addr({"place_id": place_id})
return google.get_result() if status else {}
23 changes: 23 additions & 0 deletions pos_partner_location_google_map/models/pos_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from odoo import api, fields, models


class PosConfig(models.Model):
_inherit = "pos.config"

googlemap_api_key = fields.Char(compute="_compute_geolocalize")

@api.model
def _set_extended_data(self):
data = super(PosConfig, self)._set_extended_data()
ICPSudo = self.env["ir.config_parameter"].sudo()
data.update(
api_key=ICPSudo.get_param("base_geolocalize.google_map_api_key", False)
)
return data

def _set_pos_config_parameter(self, tech_name, ext_vals=None):
super(PosConfig, self)._set_pos_config_parameter(tech_name, ext_vals)
key = ext_vals.get("api_key", "") if tech_name == "googlemap" else ""
for config in self:
config.googlemap_api_key = key
return
2 changes: 2 additions & 0 deletions pos_partner_location_google_map/readme/CONFIGURATION.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
In General Settings -> Integrations enable the Geo Localisation checkbox.
Select provided and add API key. NB: only google maps are currently supported.
1 change: 1 addition & 0 deletions pos_partner_location_google_map/readme/CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Cetmix <https://cetmix.com/>
1 change: 1 addition & 0 deletions pos_partner_location_google_map/readme/DESCRIPTION.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This module allows to select partner address directly on map.
3 changes: 3 additions & 0 deletions pos_partner_location_google_map/readme/USAGE.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
In POS open customer list, select a customer and click "Details".
On the customer form click on the "globe" icon and select a location on map.
Click "Save" to save the location.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/** @odoo-module **/

import PartnerDetailsEdit from "point_of_sale.PartnerDetailsEdit";
import Registries from "point_of_sale.Registries";

const PartnerDetailsMapGoogleEdit = (PartnerDetailsEdit) =>
class PartnerDetailsMapGoogleEdit extends PartnerDetailsEdit {
get accessToMap() {
this.config = this.env.pos.config;
if (
this.config.geolocalize_tech_name === "googlemap" &&
this.config.googlemap_api_key
) {
return true;
}
return super.accessToMap;
}
};

Registries.Component.extend(PartnerDetailsEdit, PartnerDetailsMapGoogleEdit);

export default PartnerDetailsMapGoogleEdit;
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/** @odoo-module **/

import PartnerMapEdit from "pos_partner_location_abstract.PartnerMapEdit";
import Registries from "point_of_sale.Registries";
import {loadJS} from "@web/core/assets";
import {onMounted, onWillStart} from "@odoo/owl";

/* eslint no-undef: "warn"*/
const PartnerMapGoogleEdit = (PartnerMapEdit) =>
class PartnerMapGoogleEdit extends PartnerMapEdit {
onHandleMap() {
if (
this.config.geolocalize_tech_name === "googlemap" &&
this.config.googlemap_api_key
) {
this.provider = "googlemap";
onWillStart(async () =>
loadJS(
`https://maps.googleapis.com/maps/api/js?key=${this.config.googlemap_api_key}&libraries=places`
)
);
onMounted(() => this.googleMapConfigure());
} else {
super.onHandleMap();
}
}

googleMapConfigure() {
// Default latLng
// Config
this.geocoder = new google.maps.Geocoder();
const latLng = new google.maps.LatLng(this.lat, this.lng);
const mapOptions = {
zoom: 12,
center: latLng,
};
// Show Map
this.map = new google.maps.Map(this.mapContainerRef.el, mapOptions);

if (this.lat && this.lng) {
this.setAddressByLatLng(this.lat, this.lng);
} else {
this.setAddressByLocation(this.partner.contact_address);
}

this.marker = new google.maps.Marker({
position: latLng,
map: this.map,
draggable: true,
});

this.addrInput.el.value = this.partner.contact_address;

this.map.addListener("click", (event) => {
const lat = event.latLng.lat();
const lng = event.latLng.lng();
this.update_marker(lat, lng);
this.setAddressByLatLng(lat, lng);
});
}

setAddressByLatLng(lat, lng) {
if (lat && lng) {
const latLng = new google.maps.LatLng(lat, lng);
this.geocoder.geocode({location: latLng}, (results, status) => {
if (status === google.maps.GeocoderStatus.OK) {
this.getFormattedAddress(results[0].place_id);
this.addrInput.el.value = results[0].formatted_address;
}
});
}
}

update_marker(lat, lng) {
super.update_marker(lat, lng);
if (this.provider === "googlemap") {
const latLng = new google.maps.LatLng(lat, lng);
this.map.setCenter(latLng);
this.marker.setPosition(latLng);
google.maps.event.trigger(this.map, "resize");
}
}

setAddressByLocation(address) {
if (address && this.provider === "googlemap") {
this.geocoder.geocode({address: address}, (results, status) => {
if (status === google.maps.GeocoderStatus.OK) {
this.lat = results[0].geometry.location.lat();
this.lng = results[0].geometry.location.lng();
this.getFormattedAddress(results[0].place_id);
this.addrInput.el.value = results[0].formatted_address;
this.update_marker(this.lat, this.lng);
}
});
} else {
super.setAddressByLocation(address);
}
}

getFormattedAddress(place_id) {
this.rpc({
model: "base.geocoder",
method: "prepare_geo_address_googlemap",
args: [place_id],
}).then((resp) => {
this.address = resp;
});
}
};

Registries.Component.extend(PartnerMapEdit, PartnerMapGoogleEdit);

export default PartnerMapGoogleEdit;
3 changes: 3 additions & 0 deletions pos_partner_location_google_map/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import common
from . import test_address_google_struct
from . import test_base_geocoder
Loading

0 comments on commit 1a44739

Please sign in to comment.