Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[16.0][Feature] add website_geoengine_store_locator #376

Open
wants to merge 44 commits into
base: 16.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
464df5c
Init website_geoengine
Feb 6, 2024
8ac78e6
Temporary resolve case issue with python
Feb 6, 2024
a043c89
Init website_geoengine_store_locator module
Feb 6, 2024
2f69f84
first release
Feb 9, 2024
5fec003
Replaced iframe by trivial map created by Stephane
Feb 13, 2024
5868775
Added res partner stores route
Feb 13, 2024
4bbe7c4
Commit OpenLayers js and css
sbrunner Feb 14, 2024
83b0079
First version
sbrunner Feb 14, 2024
c7163eb
Addition from Hadrien
Feb 15, 2024
25684d0
Some more
sbrunner Feb 15, 2024
5cfc37b
Cleanup
sbrunner Feb 15, 2024
cc7b0f3
fix
sbrunner Feb 15, 2024
7c7e62e
fix
sbrunner Feb 15, 2024
2825d76
Added controller and route for res_partner json /geodatas/res_partner…
Feb 15, 2024
7fa14da
temp
sbrunner Feb 15, 2024
1c1310c
Use GeoJSON from service, ...
sbrunner Feb 15, 2024
a3efbf0
Fixed the 504 error while loading datas
Feb 15, 2024
bfd9dfb
Zoom to features git pus
sbrunner Feb 15, 2024
c74c376
Use odoo lib
sbrunner Feb 16, 2024
a379731
Fix search
sbrunner Feb 16, 2024
e6b930b
Fix search
sbrunner Feb 16, 2024
dfc2b4f
Use mapType configuration parameter
sbrunner Feb 16, 2024
7a03d27
rewrite snippet.options
Feb 22, 2024
13ea17e
* Renamed following better convention
Feb 23, 2024
458984c
Map working on dragNdrop
Feb 25, 2024
deec2eb
[FIX] base_geoengine & geospatial_partner
Feb 26, 2024
d558dae
Avoid use of ?. notation
Feb 27, 2024
5f48e76
Use odoo style
sbrunner Feb 20, 2024
7f8653a
Add tag in search
sbrunner Feb 27, 2024
9b55f8a
Add style to search, ...
sbrunner Feb 27, 2024
a1a38e0
Change from rpc.query to session.rpc in order to work on frontend wit…
Feb 28, 2024
67deffa
added sudo to fetch_partner_geoengine to allow retrieve of partner in…
Feb 28, 2024
b1af50b
Add tags and partners filter on type=store
Feb 28, 2024
b4eca36
Corrected issue when searching multiple tag. Only the last one was used
Feb 28, 2024
0a29581
Fix the wrong no resulte, finalyze
sbrunner Feb 28, 2024
2cb1e18
Reapply changes
sbrunner Feb 28, 2024
e7e82f0
No error on no stores
sbrunner Feb 28, 2024
8afd144
Added max result needed functions
Apr 10, 2024
f4b0a3d
Add the messages
sbrunner Apr 17, 2024
611a9af
Ensure to search only in contact with type=shop
Apr 29, 2024
7da416f
Remove unused parameters
Apr 29, 2024
4c91d55
Fix tag removal in search field
sbrunner May 1, 2024
76119c5
prepare-publish-oca
Jul 4, 2024
cd68655
Correction of manifest copyright
Aug 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 17 additions & 17 deletions base_geoengine/expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,23 +97,23 @@ def get_geo_func(current_operator, operator, left, right, params, table):
"""
This method will call the SQL query corresponding to the requested geo operator
"""
match operator:
case "geo_greater":
query = current_operator.get_geo_greater_sql(table, left, right, params)
case "geo_lesser":
query = current_operator.get_geo_lesser_sql(table, left, right, params)
case "geo_equal":
query = current_operator.get_geo_equal_sql(table, left, right, params)
case "geo_touch":
query = current_operator.get_geo_touch_sql(table, left, right, params)
case "geo_within":
query = current_operator.get_geo_within_sql(table, left, right, params)
case "geo_contains":
query = current_operator.get_geo_contains_sql(table, left, right, params)
case "geo_intersect":
query = current_operator.get_geo_intersect_sql(table, left, right, params)
case _:
raise NotImplementedError(f"The operator {operator} is not supported")

if operator == "geo_greater":
query = current_operator.get_geo_greater_sql(table, left, right, params)
elif operator == "geo_lesser":
query = current_operator.get_geo_lesser_sql(table, left, right, params)
elif operator == "geo_equal":
query = current_operator.get_geo_equal_sql(table, left, right, params)
elif operator == "geo_touch":
query = current_operator.get_geo_touch_sql(table, left, right, params)
elif operator == "geo_within":
query = current_operator.get_geo_within_sql(table, left, right, params)
elif operator == "geo_contains":
query = current_operator.get_geo_contains_sql(table, left, right, params)
elif operator == "geo_intersect":
query = current_operator.get_geo_intersect_sql(table, left, right, params)
else:
raise NotImplementedError(f"The operator {operator} is not supported")
return query


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,7 @@ export class GeoengineRenderer extends Component {
.getSource()
.getExtent();
var infinite_extent = [Infinity, Infinity, -Infinity, -Infinity];
if (extent !== infinite_extent) {
if (JSON.stringify(extent) !== JSON.stringify(infinite_extent)) {
var map_view = this.map.getView();
if (map_view) {
map_view.fit(extent, {maxZoom: 15});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,10 @@ export class RecordsPanel extends Component {
*/
filterItems(value, items) {
const lowerValue = value.toLowerCase();
return items.filter(
(item) => item.data.display_name.toLowerCase().indexOf(lowerValue) >= 0
return items.filter((item) =>
item.data && item.data.display_name
? item.data.display_name.toLowerCase().indexOf(lowerValue) >= 0
: false
);
}
}
Expand Down
2 changes: 1 addition & 1 deletion geoengine_partner/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"author": "Camptocamp, Odoo Community Association (OCA)",
"license": "AGPL-3",
"website": "https://github.com/OCA/geospatial",
"depends": ["base", "base_geoengine"],
"depends": ["base", "base_geoengine", "contacts"],
"data": ["views/partner.xml"],
"installable": True,
"application": True,
Expand Down
13 changes: 13 additions & 0 deletions geoengine_partner/views/partner.xml
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,17 @@
<field eval="0" name="overlay" />
</record>

<record id="contacts.action_contacts" model="ir.actions.act_window">
<field name="view_mode">kanban,tree,form,activity,geoengine</field>
</record>



<record id="action_contacts_view_map" model="ir.actions.act_window.view">
<field name="sequence" eval="3" />
<field name="view_mode">geoengine</field>
<field name="view_id" ref="ir_ui_view_geo_partner" />
<field name="act_window_id" ref="contacts.action_contacts" />
</record>

</odoo>
1 change: 1 addition & 0 deletions setup/website_geoengine/odoo/addons/website_geoengine
6 changes: 6 additions & 0 deletions setup/website_geoengine/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import setuptools

setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)
6 changes: 6 additions & 0 deletions setup/website_geoengine_store_locator/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import setuptools

setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)
2 changes: 2 additions & 0 deletions website_geoengine/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import controllers
from . import models
15 changes: 15 additions & 0 deletions website_geoengine/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright 2011-2017 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
{
"name": "Geospatial Website",
"version": "16.0.1.0.0",
"description": "Create a method to search records getting only the ones that are 'is_published'",
"category": "GeoBI",
"author": "Camptocamp, Odoo Community Association (OCA)",
"license": "AGPL-3",
"website": "https://github.com/OCA/geospatial",
"depends": ["base_geoengine", "website", "partner_store"],
"data": [],
"installable": True,
"application": True,
}
1 change: 1 addition & 0 deletions website_geoengine/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import res_partner
19 changes: 19 additions & 0 deletions website_geoengine/controllers/res_partner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import http


class ResPartner(http.Controller):
@http.route("/website-geoengine/tags", type="json", auth="public", cors="*")
def tags(self, **kw):
tags = kw.get("tags", {})
lang = kw.get("lang", "en_US")
return http.request.env["res.partner"].get_search_tags(tags, lang)

@http.route("/website-geoengine/partners", type="json", auth="public", cors="*")
def partners(self, **kw):
tags = kw.get("tags", {})
lang = kw.get("lang", "en_US")
maxResults = kw.get("maxResults", "200")
return http.request.env["res.partner"].fetch_partner_geoengine(
tags, lang, maxResults
)
1 change: 1 addition & 0 deletions website_geoengine/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import res_partner
94 changes: 94 additions & 0 deletions website_geoengine/models/res_partner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import logging

from odoo import _, api, fields, models
from odoo.exceptions import ValidationError

_logger = logging.getLogger(__name__)


class ResPartner(models.Model):
_inherit = "res.partner"

opening_hours = fields.Char(string="Opening hours")

AUTHORIZED_FIELDS = ["name", "city", "zip", "street", "street2", "tag"]

@api.model
def get_search_tags(self, search, lang):
_logger.info(f"get_search_tags: {search}")
_logger.info(f"get_search_tags: {lang}")
# TODO FILTER res_partner on is_store = True AND is_published = True AND website_published = True. And filter category_ids on partners as well
sql = f"""
WITH
names as (SELECT DISTINCT 'name' as column, name as value FROM res_partner WHERE type='store'),
cities as (SELECT DISTINCT 'city' as column, city as value FROM res_partner WHERE type='store'),
zips as (SELECT DISTINCT 'zip' as column, zip as value FROM res_partner WHERE type='store'),
streets as (SELECT DISTINCT 'street' as column, concat(street, street2) as value FROM res_partner WHERE type='store'),
tags as (
SELECT DISTINCT
'tag' as column,
res_partner_category.name->>'{lang}' as value
FROM
res_partner_category,
res_partner_res_partner_category_rel,
res_partner
WHERE
res_partner_res_partner_category_rel.partner_id = res_partner.id
AND
res_partner_res_partner_category_rel.category_id = res_partner_category.id
AND res_partner.type='store'
),
all_tags as (SELECT * FROM names UNION SELECT * FROM cities UNION SELECT * FROM zips UNION SELECT * FROM streets UNION SELECT * FROM tags )


SELECT * FROM all_tags WHERE value ILIKE '%{search}%';
"""
self._cr.execute(sql)
return self._cr.fetchall()

@api.model
def fetch_partner_geoengine(self, tags, lang, maxResults):
_logger.info(f"fetch_partner_geoengine: {tags}")
_logger.info(f"fetch_partner_geoengine: {lang}")

domain = [("type", "=", "store")]
for tag in tags:
field, value = tag.values()
_logger.info(f"fetch_partner_geoengine: {field}: {value}")
if field not in self.AUTHORIZED_FIELDS:
raise ValidationError(_("Unauthorized field"))
domain.append((field.replace("tag", "category_id.name"), "ilike", value))

partners = self.sudo().search(domain)
features = []

if len(partners) > int(maxResults):
return {
"error": "Too many results",
"message": f"Too many results: {len(partners)}",
}

for partner in partners:
features.append(
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
partner.partner_longitude,
partner.partner_latitude,
],
},
"properties": {
"id": partner.id or None,
"name": partner.name or "",
"zip": partner.zip or "",
"city": partner.city or "",
"street": partner.street or "",
"street2": partner.street2 or "",
"tags": partner.category_id.mapped("name") or "",
"opening_hours": partner.opening_hours or "",
},
}
)
return features
75 changes: 75 additions & 0 deletions website_geoengine_store_locator/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
=============================================
Store map view widget (OpenStreetMap)
=============================================


This module extends the ``website_geoengine`` odoo module, to add
a map snippet view for the website.
By default the map features are filtered to only display contact of type `store`.

.. IMPORTANT::
This is an alpha version, the data model and design can change at any time without warning.
Only for development or testing purpose, do not use in production.
`More details on development status <https://odoo-community.org/page/development-status>`_

**Table of contents**

.. contents::
:local:

Configuration
=============

You should configure first a web site. Then drag n drop the Store Locator widget from the OCA/Geopspatial section.

.. figure:: ./static/description/snippet.png

You can the configure the maximum number of results you want to display before to force your user to filter results.

.. figure:: ./static/description/snippet_configuration.png

Then go to 'Contact' app and add some store type address to your contacts and add them some tags.

.. figure:: ./static/description/store_type.png

Usage
=====

.. figure:: ./static/description/screencast.gif

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/geospatial/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/geospatial/issues/new?body=module:%20web_view_leaflet_map_partner%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
~~~~~~~

* Stéphane Brunner <[email protected]>
* Hadrien Huvelle <[email protected]>

Maintainers
~~~~~~~~~~~

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.


This module is part of the `OCA/geospatial <https://github.com/OCA/geospatial/tree/16.0/website_geoengine_store_locator>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
Empty file.
34 changes: 34 additions & 0 deletions website_geoengine_store_locator/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright 2024 Camptocamp
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "Geospatial Website store locator",
"version": "16.0.1.0.0",
"description": "Adds a website widget to display a map with the store locations.",
"category": "GeoBI",
"author": "Camptocamp, Odoo Community Association (OCA)",
"license": "AGPL-3",
"website": "https://github.com/OCA/geospatial",
"depends": ["base_geoengine", "website", "partner_store", "website_geoengine"],
"data": [
"templates/snippets/s_openlayer_store_locator.xml",
"views/snippets.xml",
],
"assets": {
"web.assets_frontend": [
"website_geoengine_store_locator/static/lib/node_modules/ol/dist/ol.js",
"website_geoengine_store_locator/static/lib/node_modules/jquery-flexdatalist/jquery.flexdatalist.js",
"website_geoengine_store_locator/static/src/scss/snippets/s_openlayer_store_locator/frontend.scss",
"website_geoengine_store_locator/static/src/js/snippets/s_openlayer_store_locator/frontend.js",
"website_geoengine_store_locator/static/src/js/snippets/s_openlayer_store_locator/popover.js",
"website_geoengine_store_locator/static/src/js/snippets/s_openlayer_store_locator/search.js",
"website_geoengine_store_locator/static/src/js/snippets/s_openlayer_store_locator/map.js",
"/web/static/lib/stacktracejs/stacktrace.js",
],
"website.assets_wysiwyg": [
"website_geoengine_store_locator/static/src/js/snippets/s_openlayer_store_locator/snippet.options.js",
],
},
"maintainers": ["Wouitmil"],
"installable": True,
"application": True,
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading