new functionality for tzm.at
|  | @ -0,0 +1,3 @@ | ||||||
|  | .DS_Store | ||||||
|  | *.pyc | ||||||
|  | 
 | ||||||
|  | @ -0,0 +1,289 @@ | ||||||
|  | Web Google Maps | ||||||
|  | ===============    | ||||||
|  | 
 | ||||||
|  | [](https://youtu.be/5hvAubXgUnc "Demo")     | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | This module contains three new features: | ||||||
|  |  - New view type and mode `map` | ||||||
|  |  - New widget `gplaces_address_autocomplete` | ||||||
|  |  - New widget `gplaces_autocomplete` | ||||||
|  |   | ||||||
|  | 
 | ||||||
|  | # Map view  `"map"` | ||||||
|  | Basically, this new view `map`  will integrate Google Maps into Odoo.     | ||||||
|  | Enable you to display `res.partner` geolocation on map or any model contains geolocation.    | ||||||
|  | This feature will work seamlessly with Odoo means you can search your partner location using Odoo search feature.      | ||||||
|  | 
 | ||||||
|  | There are five available attributes that you can customize | ||||||
|  |  - `lat` : an attritube to tell the map the latitude field on the object __[mandatory]__ | ||||||
|  |  - `lng` : an attritute to tell the map the longitude field on the object __[mandatory]__ | ||||||
|  |  - `color` : an attribute to modify marker color (optional) any given color will set all markers color __[optional]__. | ||||||
|  |  - `colors` : work like attribute `color` but more configurable (you can set marker color depends on it's value) this attribute works similar to `colors` of tree view on Odoo 9.0 __[optional]__ | ||||||
|  |  - `library` : an attribute to tell map which map that will be loaded __[mandatory]__.     | ||||||
|  |     This options has two values:    | ||||||
|  |     1. `geometry` | ||||||
|  |     2. `drawing` | ||||||
|  |   | ||||||
|  | How to create the view?     | ||||||
|  | Example | ||||||
|  | > | ||||||
|  |     <!-- View --> | ||||||
|  |     <record id="view_res_partner_map" model="ir.ui.view"> | ||||||
|  |         <field name="name">view.res.partner.map</field> | ||||||
|  |         <field name="model">res.partner</field> | ||||||
|  |         <field name="arch" type="xml"> | ||||||
|  |             <map class="o_res_partner_map" library='geometry' string="Map" lat="partner_latitude" lng="partner_longitude" colors="blue:company_type=='person';green:company_type=='company';"> | ||||||
|  |                 <field name="id"/> | ||||||
|  |                 <field name="partner_latitude"/> | ||||||
|  |                 <field name="partner_longitude"/> | ||||||
|  |                 <field name="company_type"/> | ||||||
|  |                 <field name="color"/> | ||||||
|  |                 <field name="display_name"/> | ||||||
|  |                 <field name="title"/> | ||||||
|  |                 <field name="email"/> | ||||||
|  |                 <field name="parent_id"/> | ||||||
|  |                 <field name="is_company"/> | ||||||
|  |                 <field name="function"/> | ||||||
|  |                 <field name="phone"/> | ||||||
|  |                 <field name="street"/> | ||||||
|  |                 <field name="street2"/> | ||||||
|  |                 <field name="zip"/> | ||||||
|  |                 <field name="city"/> | ||||||
|  |                 <field name="country_id"/> | ||||||
|  |                 <field name="mobile"/> | ||||||
|  |                 <field name="state_id"/> | ||||||
|  |                 <field name="category_id"/> | ||||||
|  |                 <field name="image_small"/> | ||||||
|  |                 <field name="type"/> | ||||||
|  |                 <templates> | ||||||
|  |                     <t t-name="kanban-box"> | ||||||
|  |                         <div class="oe_kanban_global_click o_res_partner_kanban"> | ||||||
|  |                             <div class="o_kanban_image"> | ||||||
|  |                                 <t t-if="record.image_small.raw_value"> | ||||||
|  |                                     <img t-att-src="kanban_image('res.partner', 'image_small', record.id.raw_value)"/> | ||||||
|  |                                 </t> | ||||||
|  |                                 <t t-if="!record.image_small.raw_value"> | ||||||
|  |                                     <t t-if="record.type.raw_value === 'delivery'"> | ||||||
|  |                                         <img t-att-src='_s + "/base/static/src/img/truck.png"' class="o_kanban_image oe_kanban_avatar_smallbox"/> | ||||||
|  |                                     </t> | ||||||
|  |                                     <t t-if="record.type.raw_value === 'invoice'"> | ||||||
|  |                                         <img t-att-src='_s + "/base/static/src/img/money.png"' class="o_kanban_image oe_kanban_avatar_smallbox"/> | ||||||
|  |                                     </t> | ||||||
|  |                                     <t t-if="record.type.raw_value != 'invoice' && record.type.raw_value != 'delivery'"> | ||||||
|  |                                         <t t-if="record.is_company.raw_value === true"> | ||||||
|  |                                             <img t-att-src='_s + "/base/static/src/img/company_image.png"'/> | ||||||
|  |                                         </t> | ||||||
|  |                                         <t t-if="record.is_company.raw_value === false"> | ||||||
|  |                                             <img t-att-src='_s + "/base/static/src/img/avatar.png"'/> | ||||||
|  |                                         </t> | ||||||
|  |                                     </t> | ||||||
|  |                                 </t> | ||||||
|  |                             </div> | ||||||
|  |                             <div class="oe_kanban_details"> | ||||||
|  |                                 <strong class="o_kanban_record_title oe_partner_heading"> | ||||||
|  |                                     <field name="display_name"/> | ||||||
|  |                                 </strong> | ||||||
|  |                                 <div class="o_kanban_tags_section oe_kanban_partner_categories"> | ||||||
|  |                                     <span class="oe_kanban_list_many2many"> | ||||||
|  |                                         <field name="category_id" widget="many2many_tags" options="{'color_field': 'color'}"/> | ||||||
|  |                                     </span> | ||||||
|  |                                 </div> | ||||||
|  |                                 <ul> | ||||||
|  |                                     <li t-if="record.parent_id.raw_value and !record.function.raw_value"> | ||||||
|  |                                         <field name="parent_id"/> | ||||||
|  |                                     </li> | ||||||
|  |                                     <li t-if="!record.parent_id.raw_value and record.function.raw_value"> | ||||||
|  |                                         <field name="function"/> | ||||||
|  |                                     </li> | ||||||
|  |                                     <li t-if="record.parent_id.raw_value and record.function.raw_value"> | ||||||
|  |                                         <field name="function"/> at <field name="parent_id"/> | ||||||
|  |                                     </li> | ||||||
|  |                                     <li t-if="record.city.raw_value and !record.country_id.raw_value"> | ||||||
|  |                                         <field name="city"/> | ||||||
|  |                                     </li> | ||||||
|  |                                     <li t-if="!record.city.raw_value and record.country_id.raw_value"> | ||||||
|  |                                         <field name="country_id"/> | ||||||
|  |                                     </li> | ||||||
|  |                                     <li t-if="record.city.raw_value and record.country_id.raw_value"> | ||||||
|  |                                         <field name="city"/> | ||||||
|  |                 ,                        <field name="country_id"/> | ||||||
|  |                                     </li> | ||||||
|  |                                     <li t-if="record.email.raw_value" class="o_text_overflow"> | ||||||
|  |                                         <field name="email"/> | ||||||
|  |                                     </li> | ||||||
|  |                                 </ul> | ||||||
|  |                                 <div class="oe_kanban_partner_links"/> | ||||||
|  |                             </div> | ||||||
|  |                         </div> | ||||||
|  |                     </t> | ||||||
|  |                 </templates> | ||||||
|  |             </map> | ||||||
|  |         </field> | ||||||
|  |     </record> | ||||||
|  |      | ||||||
|  |     <!-- Action --> | ||||||
|  |     <record id="action_partner_map" model="ir.actions.act_window"> | ||||||
|  |         ... | ||||||
|  |         <field name="view_type">form</field> | ||||||
|  |         <field name="view_mode">tree,form,map</field> | ||||||
|  |         ... | ||||||
|  |     </record> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | The view looks familiar?     | ||||||
|  | Yes, you're right.     | ||||||
|  | The marker infowindow will use `kanban-box` kanban card style.     | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ### How to setup color for marker on map? | ||||||
|  | There are two attributes: | ||||||
|  |  - `colors`  | ||||||
|  |  - `color`  | ||||||
|  | 
 | ||||||
|  | Example: | ||||||
|  | >  | ||||||
|  | 	<!-- colors --> | ||||||
|  |     <map string="Map" lat="partner_latitude" lng="partner_longitude" colors="green:company_type=='person';blue:company_type=='company';"> | ||||||
|  |         ... | ||||||
|  |     </map> | ||||||
|  | 
 | ||||||
|  |     <!-- color --> | ||||||
|  |     <map string="Map" lat="partner_latitude" lng="partner_longitude" color="orange"> | ||||||
|  |         ... | ||||||
|  |     </map> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # New widget `"gplaces_address_autocomplete"` | ||||||
|  | 
 | ||||||
|  | New widget to integrate [Place Autocomplete Address Form](https://developers.google.com/maps/documentation/javascript/examples/places-autocomplete-addressform) in Odoo.   | ||||||
|  | The widget has four options that can be modify: | ||||||
|  |  - `component_form` | ||||||
|  |  - `fillfields` | ||||||
|  |  - `lat` | ||||||
|  |  - `lng` | ||||||
|  | 
 | ||||||
|  | ### Component form `component_form` | ||||||
|  | Is an option used to modify which value you want to take from an objects returned by the geocoder.     | ||||||
|  | Full documentation about Google component types can be found [here](https://developers.google.com/maps/documentation/geocoding/intro#Types) | ||||||
|  | By default this option are configured like the following value | ||||||
|  | > | ||||||
|  |     { | ||||||
|  |         'street_number': 'long_name', | ||||||
|  |         'route': 'long_name', | ||||||
|  |         'intersection': 'short_name', | ||||||
|  |         'political': 'short_name', | ||||||
|  |         'country': 'short_name', | ||||||
|  |         'administrative_area_level_1': 'short_name', | ||||||
|  |         'administrative_area_level_2': 'short_name', | ||||||
|  |         'administrative_area_level_3': 'short_name', | ||||||
|  |         'administrative_area_level_4': 'short_name', | ||||||
|  |         'administrative_area_level_5': 'short_name', | ||||||
|  |         'colloquial_area': 'short_name', | ||||||
|  |         'locality': 'short_name', | ||||||
|  |         'ward': 'short_name', | ||||||
|  |         'sublocality_level_1': 'short_name', | ||||||
|  |         'sublocality_level_2': 'short_name', | ||||||
|  |         'sublocality_level_3': 'short_name', | ||||||
|  |         'sublocality_level_5': 'short_name', | ||||||
|  |         'neighborhood': 'short_name', | ||||||
|  |         'premise': 'short_name', | ||||||
|  |         'postal_code': 'short_name', | ||||||
|  |         'natural_feature': 'short_name', | ||||||
|  |         'airport': 'short_name', | ||||||
|  |         'park': 'short_name', | ||||||
|  |         'point_of_interest': 'long_name' | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | This configuration can be modify into view field definition.     | ||||||
|  | Example: | ||||||
|  | >  | ||||||
|  |     <record id="view_res_partner_form" model="ir.ui.view"> | ||||||
|  |        ... | ||||||
|  |        <field name="arch" type="xml"> | ||||||
|  |             ... | ||||||
|  |             <field name="street" widget="gplaces_address_form" options="{'component_form': {'street_number': 'short_name'}}"/> | ||||||
|  |             ... | ||||||
|  |         </field> | ||||||
|  |     </record> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ### Fill fields `fillfields` | ||||||
|  | Is an option that will be influenced by `gplaces_address_autocomplete` widget.     | ||||||
|  | This options should contains known `fields` that you want the widget to fulfill a value for each given field automatically.     | ||||||
|  | A field can contains one or multiple elements of component form     | ||||||
|  | By default this options are configured like the following | ||||||
|  | > | ||||||
|  |     { | ||||||
|  |         'street': ['street_number', 'route'], | ||||||
|  |         'street2': ['administrative_area_level_3', 'administrative_area_level_4', 'administrative_area_level_5'], | ||||||
|  |         'city': ['locality', 'administrative_area_level_2'], | ||||||
|  |         'zip': 'postal_code', | ||||||
|  |         'state_id': 'administrative_area_level_1', | ||||||
|  |         'country_id': 'country', | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |          | ||||||
|  | This configuration can be modify into view field definition as well     | ||||||
|  | Example: | ||||||
|  | > | ||||||
|  |     <record id="view_res_partner_form" model="ir.ui.view"> | ||||||
|  |         ... | ||||||
|  |         <field name="arch" type="xml"> | ||||||
|  |             ... | ||||||
|  |             <field name="street" widget="google_places" options="{'fillfields': {'street2': ['route', 'street_number']}}"/> | ||||||
|  |             ... | ||||||
|  |         </field> | ||||||
|  |     </record> | ||||||
|  | 
 | ||||||
|  | ### Latitude `lat` and Longitude `lng` | ||||||
|  | This options tell the widget the fields geolocation, in order to have this fields filled automatically. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # New widget `"gplaces_autocomplete"` | ||||||
|  | 
 | ||||||
|  | New widget to integrate [Place Autocomplete](https://developers.google.com/maps/documentation/javascript/examples/places-autocomplete) in Odoo. | ||||||
|  | This widget have similar configuration to `gplaces_address_autocomplete`. | ||||||
|  | 
 | ||||||
|  | ### Component form `component_form` ### | ||||||
|  | Same configuration of `gplaces_address_autocomplete` component form | ||||||
|  | 
 | ||||||
|  | ### Fill fields `fillfields` | ||||||
|  | This configuration works similar to `gplaces_address_autocomplete`. | ||||||
|  | By default this options are configured like following value: | ||||||
|  | > | ||||||
|  |     { | ||||||
|  |         general: { | ||||||
|  |             name: 'name', | ||||||
|  |             website: 'website', | ||||||
|  |             phone: ['international_phone_number', 'formatted_phone_number'] | ||||||
|  |         }, | ||||||
|  |         geolocation: { | ||||||
|  |             partner_latitude: 'latitude', | ||||||
|  |             partner_longitude: 'longitude' | ||||||
|  |         }, | ||||||
|  |         address: { | ||||||
|  |             street: ['street_number', 'route'], | ||||||
|  |             street2: ['administrative_area_level_3', 'administrative_area_level_4', 'administrative_area_level_5'], | ||||||
|  |             city: ['locality', 'administrative_area_level_2'], | ||||||
|  |             zip: 'postal_code', | ||||||
|  |             state_id: 'administrative_area_level_1', | ||||||
|  |             country_id: 'country' | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  | # Technical | ||||||
|  | This module will install `base_setup` and `base_geolocalize`.     | ||||||
|  | *I recommend you to setup __Google Maps Key API__ and add it into Odoo `Settings > General` Settings when you installed this module* | ||||||
|  | 
 | ||||||
|  | *__List of Google APIs & services required in order to make all features works__* | ||||||
|  | - Geocoding API | ||||||
|  | - Maps JavaScript API | ||||||
|  | - Places API for Web | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | The goal of this module is to bring the power of Google Maps into Odoo     | ||||||
|  | This module has tested on Odoo Version 11.0c     | ||||||
|  | 
 | ||||||
|  | [](https://ko-fi.com/P5P4FOM0)     | ||||||
|  | *if you want to support me to keep this project maintained. Thanks :)* | ||||||
|  | @ -0,0 +1,5 @@ | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # License AGPL-3 | ||||||
|  | from . import models | ||||||
|  | from . import controllers | ||||||
|  | from .hooks import uninstall_hook | ||||||
|  | @ -0,0 +1,35 @@ | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | { | ||||||
|  |     'name': 'Web Google Maps', | ||||||
|  |     'version': '11.0.1.0.5', | ||||||
|  |     'author': 'Yopi Angi', | ||||||
|  |     'license': 'AGPL-3', | ||||||
|  |     'maintainer': 'Yopi Angi<yopiangi@gmail.com>', | ||||||
|  |     'support': 'yopiangi@gmail.com', | ||||||
|  |     'category': 'Extra Tools', | ||||||
|  |     'description': """ | ||||||
|  | Web Google Map and google places autocomplete address form | ||||||
|  | ========================================================== | ||||||
|  | 
 | ||||||
|  | This module brings three features: | ||||||
|  | 1. Allows user to view all partners addresses on google maps. | ||||||
|  | 2. Enabled google places autocomplete address form into partner | ||||||
|  | form view, it provide autocomplete feature when typing address of partner | ||||||
|  | """, | ||||||
|  |     'depends': [ | ||||||
|  |         'base_setup', | ||||||
|  |         'base_geolocalize', | ||||||
|  |     ], | ||||||
|  |     'website': '', | ||||||
|  |     'data': [ | ||||||
|  |         'data/google_maps_libraries.xml', | ||||||
|  |         'views/google_places_template.xml', | ||||||
|  |         'views/res_partner.xml', | ||||||
|  |         'views/res_config.xml' | ||||||
|  |     ], | ||||||
|  |     'demo': [], | ||||||
|  |     'images': ['static/description/thumbnails.png'], | ||||||
|  |     'qweb': ['static/src/xml/widget_places.xml'], | ||||||
|  |     'installable': True, | ||||||
|  |     'uninstall_hook': 'uninstall_hook', | ||||||
|  | } | ||||||
|  | @ -0,0 +1,2 @@ | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | from . import main | ||||||
|  | @ -0,0 +1,12 @@ | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | from odoo import http | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Main(http.Controller): | ||||||
|  | 
 | ||||||
|  |     @http.route('/web/map_theme', type='json', auth='user') | ||||||
|  |     def map_theme(self): | ||||||
|  |         ICP = http.request.env['ir.config_parameter'].sudo() | ||||||
|  |         theme = ICP.get_param('google.maps_theme', default='default') | ||||||
|  |         res = {'theme': theme} | ||||||
|  |         return res | ||||||
|  | @ -0,0 +1,6 @@ | ||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <odoo> | ||||||
|  |     <data noupdate="1"> | ||||||
|  |         <function model="ir.config_parameter" name="set_param" eval="('google.maps_libraries', 'geometry,places')"/> | ||||||
|  |     </data> | ||||||
|  | </odoo> | ||||||
|  | @ -0,0 +1,12 @@ | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # License AGPL-3 | ||||||
|  | 
 | ||||||
|  | def uninstall_hook(cr, registry): | ||||||
|  |     cr.execute("UPDATE ir_act_window " | ||||||
|  |                "SET view_mode=replace(view_mode, ',map', '')" | ||||||
|  |                "WHERE view_mode LIKE '%,map%';") | ||||||
|  |     cr.execute("UPDATE ir_act_window " | ||||||
|  |                "SET view_mode=replace(view_mode, 'map,', '')" | ||||||
|  |                "WHERE view_mode LIKE '%map,%';") | ||||||
|  |     cr.execute("DELETE FROM ir_act_window " | ||||||
|  |                "WHERE view_mode = 'map';") | ||||||
|  | @ -0,0 +1,466 @@ | ||||||
|  | # Translation of Odoo Server. | ||||||
|  | # This file contains the translation of the following modules: | ||||||
|  | #	* web_google_maps | ||||||
|  | # | ||||||
|  | msgid "" | ||||||
|  | msgstr "" | ||||||
|  | "Project-Id-Version: Odoo Server 11.0\n" | ||||||
|  | "Report-Msgid-Bugs-To: \n" | ||||||
|  | "POT-Creation-Date: 2018-10-09 16:04+0000\n" | ||||||
|  | "PO-Revision-Date: 2018-10-09 16:04+0000\n" | ||||||
|  | "Last-Translator: <>\n" | ||||||
|  | "Language-Team: \n" | ||||||
|  | "MIME-Version: 1.0\n" | ||||||
|  | "Content-Type: text/plain; charset=UTF-8\n" | ||||||
|  | "Content-Transfer-Encoding: \n" | ||||||
|  | "Plural-Forms: \n" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: model:ir.ui.view,arch_db:web_google_maps.view_web_google_maps_config_settings | ||||||
|  | msgid "Api key" | ||||||
|  | msgstr "Api key" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Arabic" | ||||||
|  | msgstr "Arabic" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_theme:0 | ||||||
|  | msgid "Aubergine" | ||||||
|  | msgstr "Aubergine" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Basque" | ||||||
|  | msgstr "Basque" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Bengali" | ||||||
|  | msgstr "Bengali" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Bulgarian" | ||||||
|  | msgstr "Bulgarian" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Catalan" | ||||||
|  | msgstr "Catalan" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Chinese (Simplified)" | ||||||
|  | msgstr "Chinese (Simplified)" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Chinese (Traditional)" | ||||||
|  | msgstr "Chinese (Traditional)" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: model:ir.ui.view,arch_db:web_google_maps.view_web_google_maps_config_settings | ||||||
|  | msgid "Configure your Google Maps View" | ||||||
|  | msgstr "Configure your Google Maps View" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Croatian" | ||||||
|  | msgstr "Croatian" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Czech" | ||||||
|  | msgstr "Czech" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Danish" | ||||||
|  | msgstr "Danish" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_theme:0 | ||||||
|  | msgid "Dark" | ||||||
|  | msgstr "Dark" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_theme:0 | ||||||
|  | msgid "Default" | ||||||
|  | msgstr "Default" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Dutch" | ||||||
|  | msgstr "Dutch" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "English" | ||||||
|  | msgstr "English" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "English (Australian)" | ||||||
|  | msgstr "English (Australian)" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "English (Great Britain)" | ||||||
|  | msgstr "English (Great Britain)" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Farsi" | ||||||
|  | msgstr "Farsi" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Filipino" | ||||||
|  | msgstr "Filipino" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Finnish" | ||||||
|  | msgstr "Finnish" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "French" | ||||||
|  | msgstr "French" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Galician" | ||||||
|  | msgstr "Galician" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: model:ir.model.fields,field_description:web_google_maps.field_res_config_settings_google_maps_geometry | ||||||
|  | msgid "Geometry" | ||||||
|  | msgstr "Geometry" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: model:ir.ui.view,arch_db:web_google_maps.view_web_google_maps_config_settings | ||||||
|  | msgid "Geometry includes utility functions for calculating scalar geometric values (such as distance and area) on the surface of the earth. \n" | ||||||
|  | "                                    Consult the" | ||||||
|  | msgstr "Geometry includes utility functions for calculating scalar geometric values (such as distance and area) on the surface of the earth. \n" | ||||||
|  | "                                    Consult the" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: model:ir.ui.view,arch_db:web_google_maps.view_web_google_maps_config_settings | ||||||
|  | msgid "Geometry library documentation" | ||||||
|  | msgstr "Geometry library documentation" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "German" | ||||||
|  | msgstr "German" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: model:ir.model.fields,field_description:web_google_maps.field_res_config_settings_google_maps_lang_localization | ||||||
|  | msgid "Google Maps Language Localization" | ||||||
|  | msgstr "Google Maps Language Localization" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: model:ir.ui.view,arch_db:web_google_maps.view_web_google_maps_config_settings | ||||||
|  | msgid "Google Maps Libraries" | ||||||
|  | msgstr "Google Maps Libraries" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: model:ir.model.fields,field_description:web_google_maps.field_res_config_settings_google_maps_region_localization | ||||||
|  | msgid "Google Maps Region Localization" | ||||||
|  | msgstr "Google Maps Region Localization" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: model:ir.ui.view,arch_db:web_google_maps.view_web_google_maps_config_settings | ||||||
|  | msgid "Google Maps View" | ||||||
|  | msgstr "Google Maps View" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: model:ir.model.fields,field_description:web_google_maps.field_res_config_settings_google_maps_view_api_key | ||||||
|  | msgid "Google Maps View Api Key" | ||||||
|  | msgstr "Google Maps View Api Key" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Greek" | ||||||
|  | msgstr "Greek" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Gujarati" | ||||||
|  | msgstr "Gujarati" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Hebrew" | ||||||
|  | msgstr "Hebrew" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Hindi" | ||||||
|  | msgstr "Hindi" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Hungarian" | ||||||
|  | msgstr "Hungarian" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: model:ir.ui.view,arch_db:web_google_maps.view_web_google_maps_config_settings | ||||||
|  | msgid "If you set the language of the map, it's important to consider setting the region too. This helps ensure that your application complies with local laws." | ||||||
|  | msgstr "If you set the language of the map, it's important to consider setting the region too. This helps ensure that your application complies with local laws." | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Indonesian" | ||||||
|  | msgstr "Indonesian" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Italian" | ||||||
|  | msgstr "Italian" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Japanese" | ||||||
|  | msgstr "Japanese" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Kannada" | ||||||
|  | msgstr "Kannada" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Korean" | ||||||
|  | msgstr "Korean" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: model:ir.ui.view,arch_db:web_google_maps.view_web_google_maps_config_settings | ||||||
|  | msgid "Language" | ||||||
|  | msgstr "Language" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Latvian" | ||||||
|  | msgstr "Latvian" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Lithuanian" | ||||||
|  | msgstr "Lithuanian" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Malayalam" | ||||||
|  | msgstr "Malayalam" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #. openerp-web | ||||||
|  | #: code:addons/web_google_maps/static/src/js/view/map/map_view.js:15 | ||||||
|  | #: model:ir.ui.view,arch_db:web_google_maps.view_res_partner_map | ||||||
|  | #, python-format | ||||||
|  | msgid "Map" | ||||||
|  | msgstr "Map" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: model:ir.model.fields,field_description:web_google_maps.field_res_config_settings_google_maps_theme | ||||||
|  | msgid "Map theme" | ||||||
|  | msgstr "Map theme" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Marathi" | ||||||
|  | msgstr "Marathi" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_theme:0 | ||||||
|  | msgid "Night" | ||||||
|  | msgstr "Night" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Norwegian" | ||||||
|  | msgstr "Norwegian" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: model:ir.model.fields,field_description:web_google_maps.field_res_config_settings_google_maps_places | ||||||
|  | msgid "Places" | ||||||
|  | msgstr "Places" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: model:ir.ui.view,arch_db:web_google_maps.view_web_google_maps_config_settings | ||||||
|  | msgid "Places enables your application to search for places such as establishments, geographic locations, or prominent points of interest, within a defined area. \n" | ||||||
|  | "                                    Consult the" | ||||||
|  | msgstr "Places enables your application to search for places such as establishments, geographic locations, or prominent points of interest, within a defined area. \n" | ||||||
|  | "                                    Consult the" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: model:ir.ui.view,arch_db:web_google_maps.view_web_google_maps_config_settings | ||||||
|  | msgid "Places library documentation" | ||||||
|  | msgstr "Places library documentation" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Polish" | ||||||
|  | msgstr "Polish" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Portuguese" | ||||||
|  | msgstr "Portuguese" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Portuguese (Brazil)" | ||||||
|  | msgstr "Portuguese (Brazil)" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Portuguese (Portugal)" | ||||||
|  | msgstr "Portuguese (Portugal)" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: model:ir.ui.view,arch_db:web_google_maps.view_web_google_maps_config_settings | ||||||
|  | msgid "Region" | ||||||
|  | msgstr "Region" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_theme:0 | ||||||
|  | msgid "Retro" | ||||||
|  | msgstr "Retro" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Romanian" | ||||||
|  | msgstr "Romanian" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Russian" | ||||||
|  | msgstr "Russian" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Serbian" | ||||||
|  | msgstr "Serbian" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: model:ir.ui.view,arch_db:web_google_maps.view_web_google_maps_config_settings | ||||||
|  | msgid "Set API keys and map localization" | ||||||
|  | msgstr "Set API keys and map localization" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_theme:0 | ||||||
|  | msgid "Silver" | ||||||
|  | msgstr "Silver" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Slovak" | ||||||
|  | msgstr "Slovak" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Slovenian" | ||||||
|  | msgstr "Slovenian" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Spanish" | ||||||
|  | msgstr "Spanish" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Swedish" | ||||||
|  | msgstr "Swedish" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Tagalog" | ||||||
|  | msgstr "Tagalog" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Tamil" | ||||||
|  | msgstr "Tamil" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Telugu" | ||||||
|  | msgstr "Telugu" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Thai" | ||||||
|  | msgstr "Thai" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #. openerp-web | ||||||
|  | #: code:addons/web_google_maps/static/src/js/widgets/gplaces_autocomplete.js:244 | ||||||
|  | #: code:addons/web_google_maps/static/src/js/widgets/gplaces_autocomplete.js:352 | ||||||
|  | #, python-format | ||||||
|  | msgid "The following fields are invalid:" | ||||||
|  | msgstr "The following fields are invalid:" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: model:ir.ui.view,arch_db:web_google_maps.view_web_google_maps_config_settings | ||||||
|  | msgid "Theme" | ||||||
|  | msgstr "Theme" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Turkish" | ||||||
|  | msgstr "Turkish" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Ukrainian" | ||||||
|  | msgstr "Ukrainian" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: selection:res.config.settings,google_maps_lang_localization:0 | ||||||
|  | msgid "Vietnamese" | ||||||
|  | msgstr "Vietnamese" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: model:ir.ui.view,arch_db:web_google_maps.view_web_google_maps_config_settings | ||||||
|  | msgid "Visit the" | ||||||
|  | msgstr "Visit the" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: model:ir.ui.view,arch_db:web_google_maps.view_web_google_maps_config_settings | ||||||
|  | msgid "about Localizing the Map" | ||||||
|  | msgstr "about Localizing the Map" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: model:ir.ui.view,arch_db:web_google_maps.view_res_partner_map | ||||||
|  | msgid "at" | ||||||
|  | msgstr "at" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: model:ir.ui.view,arch_db:web_google_maps.view_web_google_maps_config_settings | ||||||
|  | msgid "for more information." | ||||||
|  | msgstr "for more information." | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: model:ir.model,name:web_google_maps.model_ir_actions_act_window_view | ||||||
|  | msgid "ir.actions.act_window.view" | ||||||
|  | msgstr "ir.actions.act_window.view" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: model:ir.model,name:web_google_maps.model_ir_ui_view | ||||||
|  | msgid "ir.ui.view" | ||||||
|  | msgstr "ir.ui.view" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: model:ir.ui.view,arch_db:web_google_maps.view_web_google_maps_config_settings | ||||||
|  | msgid "page" | ||||||
|  | msgstr "page" | ||||||
|  | 
 | ||||||
|  | #. module: web_google_maps | ||||||
|  | #: model:ir.model,name:web_google_maps.model_res_config_settings | ||||||
|  | msgid "res.config.settings" | ||||||
|  | msgstr "res.config.settings" | ||||||
|  | @ -0,0 +1,5 @@ | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # License AGPL-3 | ||||||
|  | from . import ir_act_window_view | ||||||
|  | from . import ir_ui_view | ||||||
|  | from . import res_config | ||||||
|  | @ -0,0 +1,9 @@ | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # License AGPL-3 | ||||||
|  | from odoo import fields, models | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class IrActionsActWindowView(models.Model): | ||||||
|  |     _inherit = 'ir.actions.act_window.view' | ||||||
|  | 
 | ||||||
|  |     view_mode = fields.Selection(selection_add=[('map', 'Map')]) | ||||||
|  | @ -0,0 +1,9 @@ | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # License AGPL-3 | ||||||
|  | from odoo import api, fields, models | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class IrUiView(models.Model): | ||||||
|  |     _inherit = 'ir.ui.view' | ||||||
|  | 
 | ||||||
|  |     type = fields.Selection(selection_add=[('map', 'Map')]) | ||||||
|  | @ -0,0 +1,199 @@ | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # License AGPL-3 | ||||||
|  | from odoo import api, fields, models | ||||||
|  | 
 | ||||||
|  | GMAPS_LANG_LOCALIZATION = [ | ||||||
|  |     ('ar', 'Arabic'), | ||||||
|  |     ('bg', 'Bulgarian'), | ||||||
|  |     ('bn', 'Bengali'), | ||||||
|  |     ('ca', 'Catalan'), | ||||||
|  |     ('cs', 'Czech'), | ||||||
|  |     ('da', 'Danish'), | ||||||
|  |     ('de', 'German'), | ||||||
|  |     ('el', 'Greek'), | ||||||
|  |     ('en', 'English'), | ||||||
|  |     ('en-AU', 'English (Australian)'), | ||||||
|  |     ('en-GB', 'English (Great Britain)'), | ||||||
|  |     ('es', 'Spanish'), | ||||||
|  |     ('eu', 'Basque'), | ||||||
|  |     ('eu', 'Basque'), | ||||||
|  |     ('fa', 'Farsi'), | ||||||
|  |     ('fi', 'Finnish'), | ||||||
|  |     ('fil', 'Filipino'), | ||||||
|  |     ('fr', 'French'), | ||||||
|  |     ('gl', 'Galician'), | ||||||
|  |     ('gu', 'Gujarati'), | ||||||
|  |     ('hi', 'Hindi'), | ||||||
|  |     ('hr', 'Croatian'), | ||||||
|  |     ('hu', 'Hungarian'), | ||||||
|  |     ('id', 'Indonesian'), | ||||||
|  |     ('it', 'Italian'), | ||||||
|  |     ('iw', 'Hebrew'), | ||||||
|  |     ('ja', 'Japanese'), | ||||||
|  |     ('kn', 'Kannada'), | ||||||
|  |     ('ko', 'Korean'), | ||||||
|  |     ('lt', 'Lithuanian'), | ||||||
|  |     ('lv', 'Latvian'), | ||||||
|  |     ('ml', 'Malayalam'), | ||||||
|  |     ('mr', 'Marathi'), | ||||||
|  |     ('nl', 'Dutch'), | ||||||
|  |     ('no', 'Norwegian'), | ||||||
|  |     ('pl', 'Polish'), | ||||||
|  |     ('pt', 'Portuguese'), | ||||||
|  |     ('pt-BR', 'Portuguese (Brazil)'), | ||||||
|  |     ('pt-PT', 'Portuguese (Portugal)'), | ||||||
|  |     ('ro', 'Romanian'), | ||||||
|  |     ('ru', 'Russian'), | ||||||
|  |     ('sk', 'Slovak'), | ||||||
|  |     ('sl', 'Slovenian'), | ||||||
|  |     ('sr', 'Serbian'), | ||||||
|  |     ('sv', 'Swedish'), | ||||||
|  |     ('ta', 'Tamil'), | ||||||
|  |     ('te', 'Telugu'), | ||||||
|  |     ('th', 'Thai'), | ||||||
|  |     ('tl', 'Tagalog'), | ||||||
|  |     ('tr', 'Turkish'), | ||||||
|  |     ('uk', 'Ukrainian'), | ||||||
|  |     ('vi', 'Vietnamese'), | ||||||
|  |     ('zh-CN', 'Chinese (Simplified)'), | ||||||
|  |     ('zh-TW', 'Chinese (Traditional)'), | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ResConfigSettings(models.TransientModel): | ||||||
|  |     _inherit = 'res.config.settings' | ||||||
|  | 
 | ||||||
|  |     @api.model | ||||||
|  |     def get_region_selection(self): | ||||||
|  |         country_ids = self.env['res.country'].search([]) | ||||||
|  |         values = [(country.code, country.name) for country in country_ids] | ||||||
|  |         return values | ||||||
|  | 
 | ||||||
|  |     google_maps_view_api_key = fields.Char(string='Google Maps View Api Key') | ||||||
|  |     google_maps_lang_localization = fields.Selection( | ||||||
|  |         selection=GMAPS_LANG_LOCALIZATION, | ||||||
|  |         string='Google Maps Language Localization') | ||||||
|  |     google_maps_region_localization = fields.Selection( | ||||||
|  |         selection=get_region_selection, | ||||||
|  |         string='Google Maps Region Localization') | ||||||
|  |     google_maps_theme = fields.Selection( | ||||||
|  |         selection=[('default', 'Default'), | ||||||
|  |                    ('aubergine', 'Aubergine'), | ||||||
|  |                    ('night', 'Night'), | ||||||
|  |                    ('dark', 'Dark'), | ||||||
|  |                    ('retro', 'Retro'), | ||||||
|  |                    ('silver', 'Silver')], | ||||||
|  |         string='Map theme') | ||||||
|  |     google_maps_places = fields.Boolean(string='Places', default=True) | ||||||
|  |     google_maps_geometry = fields.Boolean(string='Geometry', default=True) | ||||||
|  | 
 | ||||||
|  |     @api.onchange('google_maps_lang_localization') | ||||||
|  |     def onchange_lang_localization(self): | ||||||
|  |         if not self.google_maps_lang_localization: | ||||||
|  |             self.google_maps_region_localization = '' | ||||||
|  | 
 | ||||||
|  |     @api.multi | ||||||
|  |     def set_values(self): | ||||||
|  |         super(ResConfigSettings, self).set_values() | ||||||
|  |         ICPSudo = self.env['ir.config_parameter'].sudo() | ||||||
|  |         lang_localization = self._set_google_maps_lang_localization() | ||||||
|  |         region_localization = self._set_google_maps_region_localization() | ||||||
|  | 
 | ||||||
|  |         lib_places = self._set_google_maps_places() | ||||||
|  |         lib_geometry = self._set_google_maps_geometry() | ||||||
|  | 
 | ||||||
|  |         active_libraries = '%s,%s' % (lib_geometry, lib_places) | ||||||
|  | 
 | ||||||
|  |         ICPSudo.set_param('google.api_key_geocode', | ||||||
|  |                           self.google_maps_view_api_key) | ||||||
|  |         ICPSudo.set_param('google.lang_localization', | ||||||
|  |                           lang_localization) | ||||||
|  |         ICPSudo.set_param('google.region_localization', | ||||||
|  |                           region_localization) | ||||||
|  |         ICPSudo.set_param('google.maps_theme', self.google_maps_theme) | ||||||
|  |         ICPSudo.set_param('google.maps_libraries', active_libraries) | ||||||
|  | 
 | ||||||
|  |     @api.model | ||||||
|  |     def get_values(self): | ||||||
|  |         res = super(ResConfigSettings, self).get_values() | ||||||
|  |         ICPSudo = self.env['ir.config_parameter'].sudo() | ||||||
|  | 
 | ||||||
|  |         lang_localization = self._get_google_maps_lang_localization() | ||||||
|  |         region_localization = self._get_google_maps_region_localization() | ||||||
|  |          | ||||||
|  |         lib_places = self._get_google_maps_places() | ||||||
|  |         lib_geometry = self._get_google_maps_geometry() | ||||||
|  | 
 | ||||||
|  |         res.update({ | ||||||
|  |             'google_maps_view_api_key': ICPSudo.get_param( | ||||||
|  |                 'google.api_key_geocode', default=''), | ||||||
|  |             'google_maps_lang_localization': lang_localization, | ||||||
|  |             'google_maps_region_localization': region_localization, | ||||||
|  |             'google_maps_theme': ICPSudo.get_param( | ||||||
|  |                 'google.maps_theme', default='default'), | ||||||
|  |             'google_maps_places': lib_places, | ||||||
|  |             'google_maps_geometry': lib_geometry | ||||||
|  |         }) | ||||||
|  |         return res | ||||||
|  | 
 | ||||||
|  |     @api.multi | ||||||
|  |     def _set_google_maps_lang_localization(self): | ||||||
|  |         if self.google_maps_lang_localization: | ||||||
|  |             lang_localization = '&language=%s' % \ | ||||||
|  |                                 self.google_maps_lang_localization | ||||||
|  |         else: | ||||||
|  |             lang_localization = '' | ||||||
|  | 
 | ||||||
|  |         return lang_localization | ||||||
|  | 
 | ||||||
|  |     @api.model | ||||||
|  |     def _get_google_maps_lang_localization(self): | ||||||
|  |         ICPSudo = self.env['ir.config_parameter'].sudo() | ||||||
|  |         google_maps_lang = ICPSudo.get_param( | ||||||
|  |             'google.lang_localization', default='') | ||||||
|  |         val = google_maps_lang.split('=') | ||||||
|  |         lang = val and val[-1] or '' | ||||||
|  |         return lang | ||||||
|  | 
 | ||||||
|  |     @api.multi | ||||||
|  |     def _set_google_maps_region_localization(self): | ||||||
|  |         if self.google_maps_region_localization: | ||||||
|  |             region_localization = '®ion=%s' % \ | ||||||
|  |                                   self.google_maps_region_localization | ||||||
|  |         else: | ||||||
|  |             region_localization = '' | ||||||
|  | 
 | ||||||
|  |         return region_localization | ||||||
|  | 
 | ||||||
|  |     @api.model | ||||||
|  |     def _get_google_maps_region_localization(self): | ||||||
|  |         ICPSudo = self.env['ir.config_parameter'].sudo() | ||||||
|  |         google_maps_region = ICPSudo.get_param( | ||||||
|  |             'google.region_localization', default='') | ||||||
|  |         val = google_maps_region.split('=') | ||||||
|  |         region = val and val[-1] or '' | ||||||
|  |         return region | ||||||
|  | 
 | ||||||
|  |     @api.model | ||||||
|  |     def _get_google_maps_geometry(self): | ||||||
|  |         ICPSudo = self.env['ir.config_parameter'].sudo() | ||||||
|  |         google_maps_libraries = ICPSudo.get_param( | ||||||
|  |             'google.maps_libraries', default='') | ||||||
|  |         libraries = google_maps_libraries.split(',') | ||||||
|  |         return 'geometry' in libraries | ||||||
|  | 
 | ||||||
|  |     @api.multi | ||||||
|  |     def _set_google_maps_geometry(self): | ||||||
|  |         return 'geometry' if self.google_maps_geometry else '' | ||||||
|  | 
 | ||||||
|  |     @api.model | ||||||
|  |     def _get_google_maps_places(self): | ||||||
|  |         ICPSudo = self.env['ir.config_parameter'].sudo() | ||||||
|  |         google_maps_libraries = ICPSudo.get_param( | ||||||
|  |             'google.maps_libraries', default='') | ||||||
|  |         libraries = google_maps_libraries.split(',') | ||||||
|  |         return 'places' in libraries | ||||||
|  | 
 | ||||||
|  |     @api.multi | ||||||
|  |     def _set_google_maps_places(self): | ||||||
|  |         return 'places' if self.google_maps_places else '' | ||||||
| After Width: | Height: | Size: 108 KiB | 
| After Width: | Height: | Size: 24 KiB | 
|  | @ -0,0 +1,77 @@ | ||||||
|  | <section class="oe_container"> | ||||||
|  |     <div class="oe_row"> | ||||||
|  |         <h2 class="oe_slogan" style="color:#875A7B;">Integrate Google Maps into Odoo</h2> | ||||||
|  |         <h4 class="oe_slogan">Show all your partners location on Google Maps</h4> | ||||||
|  |         <div class="oe_span12"> | ||||||
|  |             <img class="oe_picture oe_screenshot" src="maps.png"> | ||||||
|  |             <div class="text-center"> | ||||||
|  |                 <a href="https://youtu.be/5hvAubXgUnc" class="btn btn-primary" target="_blank">Demo Video</a> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |         <div class="oe_span12"> | ||||||
|  |             <p>This module brings four new features:</p> | ||||||
|  |             <ul> | ||||||
|  |                 <li>New view <em>map</em> allows user to view all partners addresses on google maps.</li> | ||||||
|  |                 <li> | ||||||
|  |                     New widget <em>gplaces_address_autocomplete</em>, enabled <a target="_blank" href="https://developers.google.com/maps/documentation/javascript/examples/places-autocomplete-addressform">Google places autocomplete address form</a> into partner form view,<br/> | ||||||
|  |                     provide autocomplete feature when you typing an address of partner (or any field using this widget) | ||||||
|  |                 </li> | ||||||
|  |                 <li> | ||||||
|  |                     New widget <em>gplaces_autocomplete</em>, enabled <a target="_blank" href="https://developers.google.com/maps/documentation/javascript/examples/places-autocomplete">Google places autocomplete</a> into partner form view,<br/> | ||||||
|  |                     provide autocomplete feature when typing partner name (or any field using this widget) | ||||||
|  |                 </li> | ||||||
|  |                 <li>Map Localization</li> | ||||||
|  |             </ul> | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  | </section> | ||||||
|  | 
 | ||||||
|  | <section class="oe_container"> | ||||||
|  |     <div class="oe_row oe_spaced"> | ||||||
|  |         <h4 class="oe_slogan">New Widget Google Place Autocomplete Address Form</h4> | ||||||
|  |         <div class="oe_span12"> | ||||||
|  |             <img class="oe_picture oe_screenshot" src="widget_gplaces_address_form.gif"> | ||||||
|  |         </div> | ||||||
|  |         <div class="oe_span12 oe_mb32"> | ||||||
|  |             <p>Provide autocomplete feature when typing an address</p> | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  | </section> | ||||||
|  | 
 | ||||||
|  | <section class="oe_container"> | ||||||
|  |     <div class="oe_row oe_spaced"> | ||||||
|  |         <h4 class="oe_slogan">New Widget Google Place Autocomplete</h4> | ||||||
|  |         <div class="oe_span12"> | ||||||
|  |             <img class="oe_picture oe_screenshot" src="widget_gplaces_autocomplete.gif"> | ||||||
|  |         </div> | ||||||
|  |         <div class="oe_span12 oe_mb32"> | ||||||
|  |             <p>Provide autocomplete feature when typing partner's name</p> | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  | </section> | ||||||
|  | 
 | ||||||
|  | <section class="oe_container"> | ||||||
|  |     <div class="oe_row oe_spaced"> | ||||||
|  |         <div class="oe_span12"> | ||||||
|  |             <h2>Bug Tracker</h2> | ||||||
|  |             <p>Bugs are tracked on | ||||||
|  |                 <a class="reference external" target="_blank" href="https://github.com/gityopie/odoo-addons/issues">GitHub Issues</a>. In case of trouble, please check there if your issue has already been reported. If you spotted | ||||||
|  |                 it first, help us smashing it by providing a detailed and welcomed feedback.</p> | ||||||
|  |         </div> | ||||||
|  |         <div class="oe_span12"> | ||||||
|  |             <h3>Contributors</h3> | ||||||
|  |             <ul class="simple"> | ||||||
|  |                 <li>Yopi Angi < | ||||||
|  |                     <a class="reference external" href="mailto:yopiangi@gmail.com">yopiangi@gmail.com</a>></li> | ||||||
|  |             </ul> | ||||||
|  |         </div> | ||||||
|  |         <div class="oe_span12"> | ||||||
|  |             <h3>Maintainer</h3> | ||||||
|  |             <p>This module is maintained by myself, if you are interested to contribute please let me know</p> | ||||||
|  |             <a href="https://ko-fi.com/P5P4FOM0" target="_blank"> | ||||||
|  |                 <img src="https://www.ko-fi.com/img/donate_sm.png" alt="ko-fi"/> | ||||||
|  |             </a> | ||||||
|  |             <p>Thank you</p> | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  | </section> | ||||||
| After Width: | Height: | Size: 334 KiB | 
| After Width: | Height: | Size: 255 KiB | 
| After Width: | Height: | Size: 246 KiB | 
| After Width: | Height: | Size: 457 KiB | 
| After Width: | Height: | Size: 303 KiB | 
| After Width: | Height: | Size: 2.9 KiB | 
| After Width: | Height: | Size: 3.2 KiB | 
| After Width: | Height: | Size: 3.9 KiB | 
| After Width: | Height: | Size: 5.6 KiB | 
| After Width: | Height: | Size: 6.7 KiB | 
|  | @ -0,0 +1,155 @@ | ||||||
|  | /* https://github.com/twbs/bootstrap/issues/4160 */ | ||||||
|  | /* put google places autocomplete dropdown results above the bootstrap modal 1050 zindex. */ | ||||||
|  | .pac-container { | ||||||
|  |     z-index: 1051 !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .o_map { | ||||||
|  |     height: 100%; | ||||||
|  |     width: 100%; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .o_map .o_map_view { | ||||||
|  |     height: 100%; | ||||||
|  |     width: 100%; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .o_map .o_map_sidenav { | ||||||
|  |     left: 0; | ||||||
|  |     top: 0; | ||||||
|  |     bottom: 0; | ||||||
|  |     max-width: 400px; | ||||||
|  |     width: 400px; | ||||||
|  |     position: absolute; | ||||||
|  |     z-index: 1; | ||||||
|  |     background-color: #f5f5f5; | ||||||
|  |     overflow-x: hidden; | ||||||
|  |     transition: 0.5s; | ||||||
|  |     transform: translate3d(0, 0, 0); | ||||||
|  |     -webkit-transform: translate3d(0, 0, 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .o_map .o_map_sidenav.whiteframe { | ||||||
|  |     box-shadow: 0 2px 4px -1px rgba(0, 0, 0, .2), 0 4px 5px 0 rgba(0, 0, 0, .14), 0 1px 10px 0 rgba(0, 0, 0, .12); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .o_map .o_map_sidenav.closed { | ||||||
|  |     transform: translate3d(-100%, 0, 0); | ||||||
|  |     -webkit-transform: translate3d(-100%, 0, 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .o_map .o_map_sidenav .sidenav-body .panel { | ||||||
|  |     border: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .o_act_window .o_map .o_map_view, | ||||||
|  | .o_act_window .o_map .o_map_sidenav { | ||||||
|  |     min-height: 400px; | ||||||
|  |     height: 400px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .o_map .o_map_sidenav .controls, | ||||||
|  | .o_map .o_map_sidenav .layers { | ||||||
|  |     text-align: center; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .o_map .o_map_sidenav .controls span.active, | ||||||
|  | .o_map .o_map_sidenav .layers span.active { | ||||||
|  |     color: #7c7bad; | ||||||
|  |     font-weight: bold; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .o_map .o_map_sidenav .controls span img, | ||||||
|  | .o_map .o_map_sidenav .layers span img { | ||||||
|  |     padding-left: 5px; | ||||||
|  |     padding-right: 10px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .o_map_control .btn_map_control { | ||||||
|  |     margin-top: 10px; | ||||||
|  |     margin-left: 10px; | ||||||
|  |     cursor: pointer; | ||||||
|  |     transition: margin-left .5s; | ||||||
|  |     line-height: 20px; | ||||||
|  |     padding: 4.5px 10px; | ||||||
|  |     background: #ffffff; | ||||||
|  |     border-radius: 2px; | ||||||
|  |     box-shadow: rgba(0, 0, 0, 0.3) 0px 1px 4px -1px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .o_map_control .btn_map_control.opened { | ||||||
|  |     margin-left: 410px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .o_map_routes_window { | ||||||
|  |     min-width: 100px; | ||||||
|  |     border-radius: 2px; | ||||||
|  |     font-size: 14px; | ||||||
|  |     margin-left: 15px; | ||||||
|  |     opacity: 0.8; | ||||||
|  |     background: #ffffff; | ||||||
|  |     padding: 5px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .o_map_redirect_google { | ||||||
|  |     cursor: pointer; | ||||||
|  |     text-align: center; | ||||||
|  |     width: 25px; | ||||||
|  |     height: 25px; | ||||||
|  |     margin-right: 15px; | ||||||
|  |     background-color: #ffffff; | ||||||
|  |     border-radius: 2px; | ||||||
|  |     line-height: 25px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .o_map_places_control .pac-card #pac-container #pac-button { | ||||||
|  |     padding-top: 5px; | ||||||
|  |     text-align: right; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .o_map_places_control .pac-card #pac-container #pac-result { | ||||||
|  |     border-bottom: 1px solid #ccc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .o_map_places_control .pac-card .pac-controls { | ||||||
|  |     padding: 0 0 10px 0; | ||||||
|  |     text-align: center; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .o_map_places_control .pac-card .pac-controls label { | ||||||
|  |     font-size: 13px; | ||||||
|  |     font-weight: 300; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .o_map_places_control .pac-card #pac-input { | ||||||
|  |     font-size: 15px; | ||||||
|  |     font-weight: 300; | ||||||
|  |     text-overflow: ellipsis; | ||||||
|  |     height: 35px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .o_map_places_control .pac-card #pac-input:focus { | ||||||
|  |     border-color: #7c7bad; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .o_map_place_result input#place-input-name { | ||||||
|  |     background-color: #efefef; | ||||||
|  |     font-size: 15px; | ||||||
|  |     font-weight: 300; | ||||||
|  |     text-overflow: ellipsis; | ||||||
|  |     height: 35px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .o_map_place_result ul { | ||||||
|  |     list-style-type: none; | ||||||
|  |     margin-left: -30px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .o_map_place_result ul li i { | ||||||
|  |     padding-right: 5px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .o_control_panel .o_map_buttons_view>button:first-child { | ||||||
|  |     float: left; | ||||||
|  |     margin-right: 4px; | ||||||
|  | } | ||||||
| After Width: | Height: | Size: 313 B | 
| After Width: | Height: | Size: 185 B | 
| After Width: | Height: | Size: 217 B | 
| After Width: | Height: | Size: 1.1 KiB | 
| After Width: | Height: | Size: 1.1 KiB | 
| After Width: | Height: | Size: 1.2 KiB | 
| After Width: | Height: | Size: 1.1 KiB | 
| After Width: | Height: | Size: 1.1 KiB | 
| After Width: | Height: | Size: 1.2 KiB | 
| After Width: | Height: | Size: 1.0 KiB | 
| After Width: | Height: | Size: 1.1 KiB | 
| After Width: | Height: | Size: 1.1 KiB | 
| After Width: | Height: | Size: 1.3 KiB | 
| After Width: | Height: | Size: 1.2 KiB | 
| After Width: | Height: | Size: 1.0 KiB | 
| After Width: | Height: | Size: 1.0 KiB | 
| After Width: | Height: | Size: 1.1 KiB | 
| After Width: | Height: | Size: 313 B | 
| After Width: | Height: | Size: 198 B | 
| After Width: | Height: | Size: 217 B | 
| After Width: | Height: | Size: 231 B | 
|  | @ -0,0 +1,61 @@ | ||||||
|  | odoo.define('web_google_maps.relational_fields', function (require) { | ||||||
|  |      | ||||||
|  |     var core = require('web.core'); | ||||||
|  |     var relational_fields = require('web.relational_fields'); | ||||||
|  |     var MapRenderer = require('web_google_maps.MapRenderer'); | ||||||
|  | 
 | ||||||
|  |     var qweb = core.qweb; | ||||||
|  | 
 | ||||||
|  |     relational_fields.FieldOne2Many.include({ | ||||||
|  |         _render: function () { | ||||||
|  |             if (!this.view || this.renderer) { | ||||||
|  |                 return this._super(); | ||||||
|  |             } | ||||||
|  |             var arch = this.view.arch; | ||||||
|  |             var viewType; | ||||||
|  |             if (arch.tag == 'map') { | ||||||
|  |                 viewType = 'map'; | ||||||
|  |                 var record_options = { | ||||||
|  |                     editable: true, | ||||||
|  |                     deletable: true, | ||||||
|  |                     read_only_mode: this.isReadonly | ||||||
|  |                 } | ||||||
|  |                 this.renderer = new MapRenderer(this, this.value, { | ||||||
|  |                     arch: arch, | ||||||
|  |                     record_options: record_options, | ||||||
|  |                     viewType: viewType, | ||||||
|  |                     fieldLat: arch.attrs.lat, | ||||||
|  |                     fieldLng: arch.attrs.lng, | ||||||
|  |                     markerColor: arch.attrs.color, | ||||||
|  |                     mapLibrary: arch.attrs.library, | ||||||
|  |                     drawingMode: arch.attrs.drawing_mode, | ||||||
|  |                     drawingPath: arch.attrs.drawing_path | ||||||
|  |                 }); | ||||||
|  |                 this.$el.addClass('o_field_x2many o_field_x2many_' + viewType); | ||||||
|  |                 return this.renderer.appendTo(this.$el); | ||||||
|  |             } | ||||||
|  |             return this._super(); | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          * Override | ||||||
|  |          */ | ||||||
|  |         _renderButtons: function () { | ||||||
|  |             this._super.apply(this, arguments); | ||||||
|  |             if (this.view.arch.tag === 'map') { | ||||||
|  |                 var options = {create_text: this.nodeOptions.create_text, widget: this}; | ||||||
|  |                 this.$buttons = $(qweb.render('MapView.buttons', options)); | ||||||
|  |                 this.$buttons.on('click', 'button.o-map-button-new', this._onAddRecord.bind(this)); | ||||||
|  |                 this.$buttons.on('click', 'button.o-map-button-center-map', this._onMapCenter.bind(this)); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         _onMapCenter: function (event) { | ||||||
|  |             event.stopPropagation(); | ||||||
|  |             if (this.renderer.mapLibrary === 'geometry') { | ||||||
|  |                 this.renderer.mapGeometryCentered(); | ||||||
|  |             } else if (this.renderer.mapLibrary === 'drawing') { | ||||||
|  |                 this.renderer.mapShapesCentered(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  | }); | ||||||
|  | @ -0,0 +1,181 @@ | ||||||
|  | odoo.define('web_google_maps.MapController', function (require) { | ||||||
|  |     'use strict'; | ||||||
|  | 
 | ||||||
|  |     var Context = require('web.Context'); | ||||||
|  |     var core = require('web.core'); | ||||||
|  |     var BasicController = require('web.BasicController'); | ||||||
|  |     var Domain = require('web.Domain'); | ||||||
|  | 
 | ||||||
|  |     var _t = core._t; | ||||||
|  |     var qweb = core.qweb; | ||||||
|  | 
 | ||||||
|  |     var MapController = BasicController.extend({ | ||||||
|  |         custom_events: _.extend({}, BasicController.prototype.custom_events, { | ||||||
|  |             button_clicked: '_onButtonClicked', | ||||||
|  |             kanban_record_delete: '_onRecordDelete', | ||||||
|  |             kanban_record_update: '_onUpdateRecord', | ||||||
|  |             kanban_column_archive_records: '_onArchiveRecords', | ||||||
|  |         }), | ||||||
|  |         /** | ||||||
|  |          * @override | ||||||
|  |          * @param {Object} params | ||||||
|  |          */ | ||||||
|  |         init: function (parent, model, renderer, params) { | ||||||
|  |             this._super.apply(this, arguments); | ||||||
|  | 
 | ||||||
|  |             this.on_create = params.on_create; | ||||||
|  |             this.hasButtons = params.hasButtons; | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          * @private | ||||||
|  |          * @param {OdooEvent} event | ||||||
|  |          */ | ||||||
|  |         _onButtonClicked: function (event) { | ||||||
|  |             event.stopPropagation(); | ||||||
|  |             var self = this; | ||||||
|  |             var attrs = event.data.attrs; | ||||||
|  |             var record = event.data.record; | ||||||
|  |             if (attrs.context) { | ||||||
|  |                 attrs.context = new Context(attrs.context) | ||||||
|  |                     .set_eval_context({ | ||||||
|  |                         active_id: record.res_id, | ||||||
|  |                         active_ids: [record.res_id], | ||||||
|  |                         active_model: record.model, | ||||||
|  |                     }); | ||||||
|  |             } | ||||||
|  |             this.trigger_up('execute_action', { | ||||||
|  |                 action_data: attrs, | ||||||
|  |                 env: { | ||||||
|  |                     context: record.getContext(), | ||||||
|  |                     currentID: record.res_id, | ||||||
|  |                     model: record.model, | ||||||
|  |                     resIDs: record.res_ids, | ||||||
|  |                 }, | ||||||
|  |                 on_closed: function () { | ||||||
|  |                     var recordModel = self.model.localData[record.id]; | ||||||
|  |                     var group = self.model.localData[recordModel.parentID]; | ||||||
|  |                     var parent = self.model.localData[group.parentID]; | ||||||
|  | 
 | ||||||
|  |                     self.model.reload(record.id).then(function (db_id) { | ||||||
|  |                         var data = self.model.get(db_id); | ||||||
|  |                         var kanban_record = event.target; | ||||||
|  |                         kanban_record.update(data); | ||||||
|  | 
 | ||||||
|  |                         // Check if we still need to display the record. Some fields of the domain are
 | ||||||
|  |                         // not guaranteed to be in data. This is for example the case if the action
 | ||||||
|  |                         // contains a domain on a field which is not in the Kanban view. Therefore,
 | ||||||
|  |                         // we need to handle multiple cases based on 3 variables:
 | ||||||
|  |                         // domInData: all domain fields are in the data
 | ||||||
|  |                         // activeInDomain: 'active' is already in the domain
 | ||||||
|  |                         // activeInData: 'active' is available in the data
 | ||||||
|  | 
 | ||||||
|  |                         var domain = (parent ? parent.domain : group.domain) || []; | ||||||
|  |                         var domInData = _.every(domain, function (d) { | ||||||
|  |                             return d[0] in data.data; | ||||||
|  |                         }); | ||||||
|  |                         var activeInDomain = _.pluck(domain, 0).indexOf('active') !== -1; | ||||||
|  |                         var activeInData = 'active' in data.data; | ||||||
|  | 
 | ||||||
|  |                         // Case # | domInData | activeInDomain | activeInData
 | ||||||
|  |                         //   1    |   true    |      true      |      true     => no domain change
 | ||||||
|  |                         //   2    |   true    |      true      |      false    => not possible
 | ||||||
|  |                         //   3    |   true    |      false     |      true     => add active in domain
 | ||||||
|  |                         //   4    |   true    |      false     |      false    => no domain change
 | ||||||
|  |                         //   5    |   false   |      true      |      true     => no evaluation
 | ||||||
|  |                         //   6    |   false   |      true      |      false    => no evaluation
 | ||||||
|  |                         //   7    |   false   |      false     |      true     => replace domain
 | ||||||
|  |                         //   8    |   false   |      false     |      false    => no evaluation
 | ||||||
|  | 
 | ||||||
|  |                         // There are 3 cases which cannot be evaluated since we don't have all the
 | ||||||
|  |                         // necessary information. The complete solution would be to perform a RPC in
 | ||||||
|  |                         // these cases, but this is out of scope. A simpler one is to do a try / catch.
 | ||||||
|  | 
 | ||||||
|  |                         if (domInData && !activeInDomain && activeInData) { | ||||||
|  |                             domain = domain.concat([ | ||||||
|  |                                 ['active', '=', true] | ||||||
|  |                             ]); | ||||||
|  |                         } else if (!domInData && !activeInDomain && activeInData) { | ||||||
|  |                             domain = [ | ||||||
|  |                                 ['active', '=', true] | ||||||
|  |                             ]; | ||||||
|  |                         } | ||||||
|  |                         try { | ||||||
|  |                             var visible = new Domain(domain).compute(data.evalContext); | ||||||
|  |                         } catch (e) { | ||||||
|  |                             return; | ||||||
|  |                         } | ||||||
|  |                         if (!visible) { | ||||||
|  |                             kanban_record.destroy(); | ||||||
|  |                         } | ||||||
|  |                     }); | ||||||
|  |                 }, | ||||||
|  |             }); | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          * @private | ||||||
|  |          * @param {OdooEvent} event | ||||||
|  |          */ | ||||||
|  |         _onRecordDelete: function (event) { | ||||||
|  |             this._deleteRecords([event.data.id]); | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          * @todo should simply use field_changed event... | ||||||
|  |          * | ||||||
|  |          * @private | ||||||
|  |          * @param {OdooEvent} ev | ||||||
|  |          */ | ||||||
|  |         _onUpdateRecord: function (ev) { | ||||||
|  |             var changes = _.clone(ev.data); | ||||||
|  |             ev.data.force_save = true; | ||||||
|  |             this._applyChanges(ev.target.db_id, changes, ev); | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          * The interface allows in some case the user to archive a column. This is | ||||||
|  |          * what this handler is for. | ||||||
|  |          * | ||||||
|  |          * @private | ||||||
|  |          * @param {OdooEvent} event | ||||||
|  |          */ | ||||||
|  |         _onArchiveRecords: function (event) { | ||||||
|  |             var self = this; | ||||||
|  |             var active_value = !event.data.archive; | ||||||
|  |             var column = event.target; | ||||||
|  |             var record_ids = _.pluck(column.records, 'db_id'); | ||||||
|  |             if (record_ids.length) { | ||||||
|  |                 this.model | ||||||
|  |                     .toggleActive(record_ids, active_value, column.db_id) | ||||||
|  |                     .then(function (db_id) { | ||||||
|  |                         var data = self.model.get(db_id); | ||||||
|  |                         self._updateEnv(); | ||||||
|  |                     }); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         renderButtons: function ($node) { | ||||||
|  |             if (this.hasButtons) { | ||||||
|  |                 this.$buttons = $(qweb.render('MapView.buttons', { | ||||||
|  |                     widget: this | ||||||
|  |                 })); | ||||||
|  |                 this.$buttons.on('click', 'button.o-map-button-new', this._onButtonNew.bind(this)); | ||||||
|  |                 this.$buttons.on('click', 'button.o-map-button-center-map', this._onButtonMapCenter.bind(this)); | ||||||
|  |                 this.$buttons.appendTo($node); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         _onButtonMapCenter: function (event) { | ||||||
|  |             event.preventDefault(); | ||||||
|  |             if (this.renderer.mapLibrary === 'geometry') { | ||||||
|  |                 this.renderer.mapGeometryCentered(); | ||||||
|  |             } else if (this.renderer.mapLibrary === 'drawing') { | ||||||
|  |                 this.renderer.mapShapesCentered(); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         _onButtonNew: function (event) { | ||||||
|  |             event.preventDefault(); | ||||||
|  |             this.trigger_up('switch_view', { | ||||||
|  |                 view_type: 'form', | ||||||
|  |                 res_id: undefined | ||||||
|  |             }); | ||||||
|  |         }, | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     return MapController; | ||||||
|  | }); | ||||||
|  | @ -0,0 +1,41 @@ | ||||||
|  | odoo.define('web_google_maps.MapModel', function(require) { | ||||||
|  |     'use strict'; | ||||||
|  | 
 | ||||||
|  |     var BasicModel = require('web.BasicModel'); | ||||||
|  | 
 | ||||||
|  |     var MapModel = BasicModel.extend({ | ||||||
|  |         /** | ||||||
|  |          * @override | ||||||
|  |          */ | ||||||
|  |         reload: function (id, options) { | ||||||
|  |             if (options && options.groupBy && !options.groupBy.length) { | ||||||
|  |                 options.groupBy = this.defaultGroupedBy; | ||||||
|  |             } | ||||||
|  |             return this._super.apply(this, arguments); | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          * @override | ||||||
|  |          */ | ||||||
|  |         load: function (params) { | ||||||
|  |             this.defaultGroupedBy = params.groupBy; | ||||||
|  |             params.groupedBy = (params.groupedBy && params.groupedBy.length) ? params.groupedBy : this.defaultGroupedBy; | ||||||
|  |             return this._super(params); | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          * Ensures that there is no nested groups in Map (only the first grouping | ||||||
|  |          * level is taken into account). | ||||||
|  |          * | ||||||
|  |          * @override | ||||||
|  |          */ | ||||||
|  |         _readGroup: function (list) { | ||||||
|  |             var self = this; | ||||||
|  |             if (list.groupedBy.length > 1) { | ||||||
|  |                 list.groupedBy = [list.groupedBy[0]]; | ||||||
|  |             } | ||||||
|  |             return this._super.apply(this, arguments); | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     return MapModel; | ||||||
|  | 
 | ||||||
|  | }); | ||||||
|  | @ -0,0 +1,81 @@ | ||||||
|  | odoo.define('web_google_maps.MapView', function (require) { | ||||||
|  |     'use strict'; | ||||||
|  | 
 | ||||||
|  |     var BasicView = require('web.BasicView'); | ||||||
|  |     var core = require('web.core'); | ||||||
|  | 
 | ||||||
|  |     var MapModel = require('web_google_maps.MapModel'); | ||||||
|  |     var MapRenderer = require('web_google_maps.MapRenderer'); | ||||||
|  |     var MapController = require('web_google_maps.MapController'); | ||||||
|  | 
 | ||||||
|  |     var _lt = core._lt; | ||||||
|  | 
 | ||||||
|  |     var MapView = BasicView.extend({ | ||||||
|  |         accesskey: 'm', | ||||||
|  |         display_name: _lt('Map'), | ||||||
|  |         icon: 'fa-map-o', | ||||||
|  |         jsLibs: [], | ||||||
|  |         config: _.extend({}, BasicView.prototype.config, { | ||||||
|  |             Model: MapModel, | ||||||
|  |             Renderer: MapRenderer, | ||||||
|  |             Controller: MapController | ||||||
|  |         }), | ||||||
|  |         viewType: 'map', | ||||||
|  |         init: function (viewInfo, params) { | ||||||
|  |             this._super.apply(this, arguments); | ||||||
|  | 
 | ||||||
|  |             var arch = viewInfo.arch; | ||||||
|  |             var attrs = arch.attrs; | ||||||
|  | 
 | ||||||
|  |             var activeActions = this.controllerParams.activeActions; | ||||||
|  |             var mode = arch.attrs.editable && !params.readonly ? "edit" : "readonly"; | ||||||
|  | 
 | ||||||
|  |             this.loadParams.limit = this.loadParams.limit || 40; | ||||||
|  |             this.loadParams.openGroupByDefault = true; | ||||||
|  |             this.loadParams.type = 'list'; | ||||||
|  | 
 | ||||||
|  |             this.loadParams.groupBy = arch.attrs.default_group_by ? [arch.attrs.default_group_by] : (params.groupBy || []); | ||||||
|  | 
 | ||||||
|  |             this.rendererParams.arch = arch; | ||||||
|  |             this.rendererParams.mapLibrary = attrs.library; | ||||||
|  | 
 | ||||||
|  |             if (attrs.library === 'drawing') { | ||||||
|  |                 this.rendererParams.drawingMode = attrs.drawing_mode; | ||||||
|  |                 this.rendererParams.drawingPath = attrs.drawing_path; | ||||||
|  |             } else if (attrs.library === 'geometry') { | ||||||
|  |                 var colors = this._setMarkersColor(attrs.colors); | ||||||
|  |                 this.rendererParams.markerColor = attrs.color; | ||||||
|  |                 this.rendererParams.markerColors = colors; | ||||||
|  |                 this.rendererParams.fieldLat = attrs.lat; | ||||||
|  |                 this.rendererParams.fieldLng = attrs.lng; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             this.rendererParams.record_options = { | ||||||
|  |                 editable: activeActions.edit, | ||||||
|  |                 deletable: activeActions.delete, | ||||||
|  |                 read_only_mode: params.readOnlyMode || true, | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             this.controllerParams.mode = mode; | ||||||
|  |             this.controllerParams.hasButtons = true; | ||||||
|  |         }, | ||||||
|  |         _setMarkersColor: function (colors) { | ||||||
|  |             var pair, color, expr; | ||||||
|  |             if (!colors) { | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |             return _(colors.split(';')) | ||||||
|  |                 .chain() | ||||||
|  |                 .compact() | ||||||
|  |                 .map(function (color_pair) { | ||||||
|  |                     pair = color_pair.split(':'); | ||||||
|  |                     color = pair[0]; | ||||||
|  |                     expr = pair[1]; | ||||||
|  |                     return [color, py.parse(py.tokenize(expr)), expr]; | ||||||
|  |                 }).value(); | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     return MapView; | ||||||
|  | 
 | ||||||
|  | }); | ||||||
|  | @ -0,0 +1,9 @@ | ||||||
|  | odoo.define('web_google_maps.view_registry', function (require) { | ||||||
|  |     "use strict"; | ||||||
|  | 
 | ||||||
|  |     var MapView = require('web_google_maps.MapView'); | ||||||
|  |     var view_registry = require('web.view_registry'); | ||||||
|  | 
 | ||||||
|  |     view_registry.add('map', MapView); | ||||||
|  | 
 | ||||||
|  | }); | ||||||
|  | @ -0,0 +1,10 @@ | ||||||
|  | odoo.define('web_google_maps.FieldsRegistry', function(require) { | ||||||
|  |     'use strict'; | ||||||
|  | 
 | ||||||
|  |     var registry = require('web.field_registry'); | ||||||
|  |     var GplacesAutocomplete = require('web_google_maps.GplaceAutocompleteFields'); | ||||||
|  | 
 | ||||||
|  |     registry.add('gplaces_address_autocomplete', GplacesAutocomplete.GplacesAddressAutocompleteField); | ||||||
|  |     registry.add('gplaces_autocomplete', GplacesAutocomplete.GplacesAutocompleteField); | ||||||
|  | 
 | ||||||
|  | }); | ||||||
|  | @ -0,0 +1,496 @@ | ||||||
|  | odoo.define('web_google_maps.GplaceAutocompleteFields', function (require) { | ||||||
|  |     'use strict'; | ||||||
|  | 
 | ||||||
|  |     var BasicFields = require('web.basic_fields'); | ||||||
|  |     var core = require('web.core'); | ||||||
|  |     var Utils = require('web_google_maps.Utils'); | ||||||
|  |     var _t = core._t; | ||||||
|  | 
 | ||||||
|  |     var GplaceAutocomplete = BasicFields.InputField.extend({ | ||||||
|  |         className: 'o_field_char o_field_google_autocomplete', | ||||||
|  |         tagName: 'span', | ||||||
|  |         supportedFieldTypes: ['char'], | ||||||
|  |         events: _.extend({}, BasicFields.InputField.prototype.events, { | ||||||
|  |             'focus': '_geolocate' | ||||||
|  |         }), | ||||||
|  |         /** | ||||||
|  |          * @override | ||||||
|  |          */ | ||||||
|  |         init: function () { | ||||||
|  |             this._super.apply(this, arguments); | ||||||
|  | 
 | ||||||
|  |             this._type_relations = ['one2many', 'many2one', 'many2many']; | ||||||
|  |             this.places_autocomplete = false; | ||||||
|  |             this.component_form = Utils.GOOGLE_PLACES_COMPONENT_FORM; | ||||||
|  |             this.address_form = Utils.ADDRESS_FORM; | ||||||
|  |             this.fillfields_delimiter = { | ||||||
|  |                 street: " ", | ||||||
|  |                 street2: ", ", | ||||||
|  |             }; | ||||||
|  |             this.fillfields = {}; | ||||||
|  |             this.lng = false; | ||||||
|  |             this.lat = false; | ||||||
|  |             this.setDefault(); | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          * @override | ||||||
|  |          */ | ||||||
|  |         start: function () { | ||||||
|  |             return this._super.apply(this, arguments).then(this.prepareWidgetOptions.bind(this)); | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          * Set widget default value | ||||||
|  |          */ | ||||||
|  |         setDefault: function () {}, | ||||||
|  |         /** | ||||||
|  |          * Prepare widget options | ||||||
|  |          */ | ||||||
|  |         prepareWidgetOptions: function () { | ||||||
|  |             if (this.mode === 'edit') { | ||||||
|  |                 // update  'component_form', 'delimiter' if exists
 | ||||||
|  |                 if (this.attrs.options) { | ||||||
|  |                     if (this.attrs.options.hasOwnProperty('component_form')) { | ||||||
|  |                         this.component_form = _.defaults({}, this.attrs.options.component_form, this.component_form); | ||||||
|  |                     } | ||||||
|  |                     if (this.attrs.options.hasOwnProperty('delimiter')) { | ||||||
|  |                         this.fillfields_delimiter = _.defaults({}, this.attrs.options.delimiter, this.fillfields_delimiter); | ||||||
|  |                     } | ||||||
|  |                     if (this.attrs.options.hasOwnProperty('lat')) { | ||||||
|  |                         this.lat = this.attrs.options.lat; | ||||||
|  |                     } | ||||||
|  |                     if (this.attrs.options.hasOwnProperty('lng')) { | ||||||
|  |                         this.lng = this.attrs.options.lng; | ||||||
|  |                     } | ||||||
|  |                     if (this.attrs.options.hasOwnProperty('address_form')) { | ||||||
|  |                         this.address_form = _.defaults({}, this.attrs.options.address_form, this.address_form); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 this.target_fields = this.getFillFieldsType(); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          * To be overriden | ||||||
|  |          */ | ||||||
|  |         getFillFieldsType: function () { | ||||||
|  |             return []; | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          * Geolocate | ||||||
|  |          * @private | ||||||
|  |          */ | ||||||
|  |         _geolocate: function () { | ||||||
|  |             var self = this; | ||||||
|  |             if (navigator.geolocation) { | ||||||
|  |                 navigator.geolocation.getCurrentPosition(function (position) { | ||||||
|  |                     var geolocation = { | ||||||
|  |                         lat: position.coords.latitude, | ||||||
|  |                         lng: position.coords.longitude | ||||||
|  |                     }; | ||||||
|  | 
 | ||||||
|  |                     var circle = new google.maps.Circle({ | ||||||
|  |                         center: geolocation, | ||||||
|  |                         radius: position.coords.accuracy | ||||||
|  |                     }); | ||||||
|  | 
 | ||||||
|  |                     self.places_autocomplete.setBounds(circle.getBounds()); | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          * @private | ||||||
|  |          */ | ||||||
|  |         _prepareValue: function (model, field_name, value) { | ||||||
|  |             var model = model || false; | ||||||
|  |             var field_name = field_name || false; | ||||||
|  |             var value = value || false; | ||||||
|  |             return Utils.fetchValues(model, field_name, value); | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          * @private | ||||||
|  |          */ | ||||||
|  |         _populateAddress: function (place, fillfields, delimiter) { | ||||||
|  |             var place = place || false; | ||||||
|  |             var fillfields = fillfields || this.fillfields; | ||||||
|  |             var delimiter = delimiter || this.fillfields_delimiter; | ||||||
|  |             return Utils.gmaps_populate_address(place, fillfields, delimiter); | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          * @private | ||||||
|  |          */ | ||||||
|  |         _populatePlaces: function (place, fillfields) { | ||||||
|  |             var place = place || false; | ||||||
|  |             var fillfields = fillfields || this.fillfields; | ||||||
|  |             return Utils.gmaps_populate_places(place, fillfields); | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          * @private | ||||||
|  |          */ | ||||||
|  |         _getCountryState: function (model, country, state) { | ||||||
|  |             var model = model || false; | ||||||
|  |             var country = country || false; | ||||||
|  |             var state = state || false; | ||||||
|  |             return Utils.fetchCountryState(model, country, state); | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          * @private | ||||||
|  |          */ | ||||||
|  |         _setGeolocation: function (latitude, longitude) { | ||||||
|  |             var res = {}; | ||||||
|  |             if (_.intersection(_.keys(this.record.fields), [this.lat, this.lng]).length == 2) { | ||||||
|  |                 res[this.lat] = latitude; | ||||||
|  |                 res[this.lng] = longitude; | ||||||
|  |             } | ||||||
|  |             return res; | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          * @private | ||||||
|  |          */ | ||||||
|  |         _onUpdateWidgetFields: function (changes) { | ||||||
|  |             var changes = changes || {}; | ||||||
|  |             this.trigger_up('field_changed', { | ||||||
|  |                 dataPointID: this.dataPointID, | ||||||
|  |                 changes: changes, | ||||||
|  |                 viewType: this.viewType, | ||||||
|  |             }); | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          * @override  | ||||||
|  |          */ | ||||||
|  |         _renderEdit: function () { | ||||||
|  |             this._super.apply(this, arguments); | ||||||
|  |             if (this.isValid()) { | ||||||
|  |                 this._initGplacesAutocomplete(); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          * @override | ||||||
|  |          */ | ||||||
|  |         destroy: function () { | ||||||
|  |             if (this.places_autocomplete) { | ||||||
|  |                 this.places_autocomplete.unbindAll(); | ||||||
|  |             } | ||||||
|  |             // Remove all PAC container in DOM if any
 | ||||||
|  |             $('.pac-container').remove(); | ||||||
|  |             return this._super(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     var GplaceAddressAutocompleteField = GplaceAutocomplete.extend({ | ||||||
|  |         setDefault: function () { | ||||||
|  |             this._super.apply(this, arguments); | ||||||
|  |             this.fillfields = { | ||||||
|  |                 [this.address_form.street]: ['street_number', 'route'], | ||||||
|  |                 [this.address_form.street2]: ['administrative_area_level_3', 'administrative_area_level_4', 'administrative_area_level_5'], | ||||||
|  |                 [this.address_form.city]: ['locality', 'administrative_area_level_2'], | ||||||
|  |                 [this.address_form.zip]: 'postal_code', | ||||||
|  |                 [this.address_form.state_id]: 'administrative_area_level_1', | ||||||
|  |                 [this.address_form.country_id]: 'country' | ||||||
|  |             }; | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          * @override | ||||||
|  |          */ | ||||||
|  |         prepareWidgetOptions: function () { | ||||||
|  |             if (this.mode === 'edit' && this.attrs.options) { | ||||||
|  |                 if (this.attrs.options.hasOwnProperty('fillfields')) { | ||||||
|  |                     this.fillfields = _.defaults({}, this.attrs.options.fillfields, this.fillfields); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             this._super(); | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          * Get fields attributes | ||||||
|  |          * @override | ||||||
|  |          */ | ||||||
|  |         getFillFieldsType: function () { | ||||||
|  |             var self = this; | ||||||
|  |             var field; | ||||||
|  |             var res = this._super(); | ||||||
|  |             if (this._isValid) { | ||||||
|  |                 _.each(this.fillfields, function (val, name) { | ||||||
|  |                     if (_.contains(self._type_relations, self.record.fields[name].type)) { | ||||||
|  |                         field = { | ||||||
|  |                             name: name, | ||||||
|  |                             type: self.record.fields[name].type, | ||||||
|  |                             relation: self.record.fields[name].relation | ||||||
|  |                         }; | ||||||
|  |                         res.push(field); | ||||||
|  |                     } else { | ||||||
|  |                         field = { | ||||||
|  |                             name: name, | ||||||
|  |                             type: self.record.fields[name].type, | ||||||
|  |                             relation: false | ||||||
|  |                         }; | ||||||
|  |                         res.push(field); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |             return res; | ||||||
|  |         }, | ||||||
|  |         _initGplacesAutocomplete: function () { | ||||||
|  |             var self = this; | ||||||
|  |             if (!this.places_autocomplete) { | ||||||
|  |                 this.places_autocomplete = new google.maps.places.Autocomplete(this.$input.get(0), { | ||||||
|  |                     types: ['address'] | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |             // When the user selects an address from the dropdown, populate the address fields in the form.
 | ||||||
|  |             this.place_listener = this.places_autocomplete.addListener('place_changed', function () { | ||||||
|  |                 var requests = []; | ||||||
|  |                 var place = this.getPlace(); | ||||||
|  |                 if (place.hasOwnProperty('address_components')) { | ||||||
|  |                     var google_address = self._populateAddress(place); | ||||||
|  |                     _.each(self.target_fields, function (field) { | ||||||
|  |                         requests.push(self._prepareValue(field.relation, field.name, google_address[field.name])); | ||||||
|  |                     }); | ||||||
|  | 
 | ||||||
|  |                     var partner_geometry = self._setGeolocation(place.geometry.location.lat(), place.geometry.location.lng()); | ||||||
|  |                     _.each(partner_geometry, function (val, field) { | ||||||
|  |                         requests.push(self._prepareValue(false, field, val)); | ||||||
|  |                     }); | ||||||
|  | 
 | ||||||
|  |                     $.when.apply($, requests).done(function () { | ||||||
|  |                         var changes = {}; | ||||||
|  |                         _.each(arguments, function (data, idx) { | ||||||
|  |                             _.each(data, function (val, key) { | ||||||
|  |                                 if (typeof val === 'object') { | ||||||
|  |                                     changes[key] = val; | ||||||
|  |                                 } else { | ||||||
|  |                                     changes[key] = self._parseValue(val); | ||||||
|  |                                 } | ||||||
|  |                             }); | ||||||
|  |                         }); | ||||||
|  |                         if (!changes[self.address_form.state_id] && | ||||||
|  |                             changes[self.address_form.country_id].hasOwnProperty('id') && | ||||||
|  |                             google_address[self.address_form.state_id]) { | ||||||
|  |                                 var state_id = _.find(self.target_fields, function(field) { | ||||||
|  |                                     return field.name === self.address_form.state_id; | ||||||
|  |                                 }) | ||||||
|  |                                 if (!state_id) return; | ||||||
|  |                                 self._getCountryState( | ||||||
|  |                                     state_id.relation, | ||||||
|  |                                     changes[self.address_form.country_id].id, | ||||||
|  |                                     google_address[self.address_form.state_id] | ||||||
|  |                                 ).then(function (result) { | ||||||
|  |                                     var state = { | ||||||
|  |                                         [self.address_form.state_id]: result.length == 1 ? result[0] : false, | ||||||
|  |                                     } | ||||||
|  |                                     _.extend(changes, state); | ||||||
|  |                                     self._onUpdateWidgetFields(changes); | ||||||
|  |                                 }); | ||||||
|  |                         } else { | ||||||
|  |                             self._onUpdateWidgetFields(changes); | ||||||
|  |                         } | ||||||
|  |                     }); | ||||||
|  |                     self.$input.val(google_address[self.name]); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          * @override | ||||||
|  |          */ | ||||||
|  |         isValid: function () { | ||||||
|  |             this._super.apply(this, arguments); | ||||||
|  |             var self = this, | ||||||
|  |                 unknown_fields; | ||||||
|  | 
 | ||||||
|  |             unknown_fields = _.filter(_.keys(self.fillfields), function (field) { | ||||||
|  |                 return !self.record.fields.hasOwnProperty(field); | ||||||
|  |             }); | ||||||
|  |             if (unknown_fields.length > 0) { | ||||||
|  |                 self.do_warn(_t('The following fields are invalid:'), _t('<ul><li>' + unknown_fields.join('</li><li>') + '</li></ul>')); | ||||||
|  |                 this._isValid = false; | ||||||
|  |             } | ||||||
|  |             return this._isValid; | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          * @override | ||||||
|  |          */ | ||||||
|  |         destroy: function () { | ||||||
|  |             if (this.places_autocomplete) { | ||||||
|  |                 google.maps.event.removeListener(this.place_listener); | ||||||
|  |                 google.maps.event.clearInstanceListeners(this.places_autocomplete); | ||||||
|  |             } | ||||||
|  |             return this._super(); | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     var GplacesAutocompleteField = GplaceAutocomplete.extend({ | ||||||
|  |         setDefault: function () { | ||||||
|  |             this._super.apply(this); | ||||||
|  |             this.fillfields = { | ||||||
|  |                 general: { | ||||||
|  |                     name: 'name', | ||||||
|  |                     website: 'website', | ||||||
|  |                     phone: ['international_phone_number', 'formatted_phone_number'] | ||||||
|  |                 }, | ||||||
|  |                 address: { | ||||||
|  |                     [this.address_form.street]: ['street_number', 'route'], | ||||||
|  |                     [this.address_form.street2]: ['administrative_area_level_3', 'administrative_area_level_4', 'administrative_area_level_5'], | ||||||
|  |                     [this.address_form.city]: ['locality', 'administrative_area_level_2'], | ||||||
|  |                     [this.address_form.zip]: 'postal_code', | ||||||
|  |                     [this.address_form.state_id]: 'administrative_area_level_1', | ||||||
|  |                     [this.address_form.country_id]: 'country' | ||||||
|  |                 } | ||||||
|  |             }; | ||||||
|  |         }, | ||||||
|  |         prepareWidgetOptions: function () { | ||||||
|  |             if (this.mode === 'edit' && this.attrs.options) { | ||||||
|  |                 if (this.attrs.options.hasOwnProperty('fillfields')) { | ||||||
|  |                     if (this.attrs.options.fillfields.hasOwnProperty('address')) { | ||||||
|  |                         this.fillfields['address'] = _.defaults({}, this.attrs.options.fillfields.address, this.fillfields.address); | ||||||
|  |                     } | ||||||
|  |                     if (this.attrs.options.fillfields.hasOwnProperty('general')) { | ||||||
|  |                         this.fillfields['general'] = _.defaults({}, this.attrs.options.fillfields.general, this.fillfields.general); | ||||||
|  |                     } | ||||||
|  |                     if (this.attrs.options.fillfields.hasOwnProperty('geolocation')) { | ||||||
|  |                         this.fillfields['geolocation'] = this.attrs.options.fillfields.geolocation; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             this._super(); | ||||||
|  |         }, | ||||||
|  |         getFillFieldsType: function () { | ||||||
|  |             var self = this; | ||||||
|  |             var field; | ||||||
|  |             var res = this._super(); | ||||||
|  |             if (this._isValid) { | ||||||
|  |                 for (var option in this.fillfields) { | ||||||
|  |                     _.each(this.fillfields[option], function (val, name) { | ||||||
|  |                         if (_.contains(self._type_relations, self.record.fields[name].type)) { | ||||||
|  |                             field = { | ||||||
|  |                                 name: name, | ||||||
|  |                                 type: self.record.fields[name].type, | ||||||
|  |                                 relation: self.record.fields[name].relation | ||||||
|  |                             }; | ||||||
|  |                             res.push(field); | ||||||
|  |                         } else { | ||||||
|  |                             field = { | ||||||
|  |                                 name: name, | ||||||
|  |                                 type: self.record.fields[name].type, | ||||||
|  |                                 relation: false | ||||||
|  |                             }; | ||||||
|  |                             res.push(field); | ||||||
|  |                         } | ||||||
|  |                     }); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return res; | ||||||
|  |         }, | ||||||
|  |         _setGeolocation: function (lat, lng) { | ||||||
|  |             var res = {}; | ||||||
|  |             if (this.lat && this.lng) { | ||||||
|  |                 var _res = this._super(lat, lng); | ||||||
|  |                 return _res; | ||||||
|  |             } else if (this.fillfields.geolocation) { | ||||||
|  |                 _.each(this.fillfields.geolocation, function (alias, field) { | ||||||
|  |                     if (alias === 'latitude') { | ||||||
|  |                         res[field] = lat; | ||||||
|  |                     } | ||||||
|  |                     if (alias === 'longitude') { | ||||||
|  |                         res[field] = lng; | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |             return res; | ||||||
|  |         }, | ||||||
|  |         _initGplacesAutocomplete: function () { | ||||||
|  |             var self = this; | ||||||
|  |             this.places_autocomplete = new google.maps.places.Autocomplete(this.$input.get(0), { | ||||||
|  |                 types: ['establishment'] | ||||||
|  |             }); | ||||||
|  |             // When the user selects an address from the dropdown, populate the address fields in the form.
 | ||||||
|  |             this.place_listener = this.places_autocomplete.addListener('place_changed', function () { | ||||||
|  |                 var values = {}, | ||||||
|  |                     requests = [], | ||||||
|  |                     place; | ||||||
|  |                 place = this.getPlace(); | ||||||
|  |                 if (place.hasOwnProperty('address_components')) { | ||||||
|  |                     // Get address
 | ||||||
|  |                     var google_address = self._populateAddress(place, self.fillfields.address, self.fillfields_delimiter); | ||||||
|  |                     _.extend(values, google_address); | ||||||
|  |                     // Get place (name, website, phone)
 | ||||||
|  |                     var google_place = self._populatePlaces(place, self.fillfields.general); | ||||||
|  |                     _.extend(values, google_place); | ||||||
|  |                     // Get place geolocation
 | ||||||
|  |                     var google_geolocation = self._setGeolocation(place.geometry.location.lat(), place.geometry.location.lng()); | ||||||
|  |                     _.extend(values, google_geolocation); | ||||||
|  | 
 | ||||||
|  |                     _.each(self.target_fields, function (field) { | ||||||
|  |                         requests.push(self._prepareValue(field.relation, field.name, values[field.name])); | ||||||
|  |                     }); | ||||||
|  | 
 | ||||||
|  |                     $.when.apply($, requests).done(function () { | ||||||
|  |                         var changes = {} | ||||||
|  |                         _.each(arguments, function (data, idx) { | ||||||
|  |                             _.each(data, function (val, key) { | ||||||
|  |                                 if (typeof val === 'object') { | ||||||
|  |                                     changes[key] = val; | ||||||
|  |                                 } else { | ||||||
|  |                                     changes[key] = self._parseValue(val); | ||||||
|  |                                 } | ||||||
|  |                             }); | ||||||
|  |                         }); | ||||||
|  |                         if (!changes[self.address_form.state_id] && | ||||||
|  |                             changes[self.address_form.country_id].hasOwnProperty('id') && | ||||||
|  |                             google_address[self.address_form.state_id]) { | ||||||
|  |                                 var state_id = _.find(self.target_fields, function(field) { | ||||||
|  |                                     return field.name === self.address_form.state_id; | ||||||
|  |                                 }) | ||||||
|  |                                 if (!state_id) return; | ||||||
|  |                                 self._getCountryState( | ||||||
|  |                                     state_id.relation, | ||||||
|  |                                     changes[self.address_form.country_id].id, | ||||||
|  |                                     google_address[self.address_form.state_id] | ||||||
|  |                                 ).then(function (result) { | ||||||
|  |                                     var state = { | ||||||
|  |                                         [self.address_form.state_id]: result.length == 1 ? result[0] : false, | ||||||
|  |                                     } | ||||||
|  |                                     _.extend(changes, state); | ||||||
|  |                                     self._onUpdateWidgetFields(changes); | ||||||
|  |                                 }); | ||||||
|  |                         } else { | ||||||
|  |                             self._onUpdateWidgetFields(changes); | ||||||
|  |                         } | ||||||
|  |                     }); | ||||||
|  |                     self.$input.val(place.name); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          * @override | ||||||
|  |          */ | ||||||
|  |         isValid: function () { | ||||||
|  |             this._super.apply(this, arguments); | ||||||
|  |             var self = this, | ||||||
|  |                 unknown_fields; | ||||||
|  |             for (var option in this.fillfields) { | ||||||
|  |                 unknown_fields = _.filter(_.keys(this.fillfields[option]), function (field) { | ||||||
|  |                     return !self.record.fields.hasOwnProperty(field); | ||||||
|  |                 }); | ||||||
|  |                 if (unknown_fields.length > 0) { | ||||||
|  |                     self.do_warn(_t('The following fields are invalid:'), _t('<ul><li>' + unknown_fields.join('</li><li>') + '</li></ul>')); | ||||||
|  |                     this._isValid = false; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return this._isValid; | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          * @override | ||||||
|  |          */ | ||||||
|  |         destroy: function () { | ||||||
|  |             if (this.places_autocomplete) { | ||||||
|  |                 google.maps.event.removeListener(this.place_listener); | ||||||
|  |                 google.maps.event.clearInstanceListeners(this.$input.get(0)); | ||||||
|  |             } | ||||||
|  |             return this._super(); | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     return { | ||||||
|  |         GplacesAddressAutocompleteField: GplaceAddressAutocompleteField, | ||||||
|  |         GplacesAutocompleteField: GplacesAutocompleteField | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  | }); | ||||||
|  | @ -0,0 +1,164 @@ | ||||||
|  | odoo.define('web_google_maps.Utils', function (require) { | ||||||
|  |     'use strict'; | ||||||
|  | 
 | ||||||
|  |     var rpc = require("web.rpc"); | ||||||
|  | 
 | ||||||
|  |     var GOOGLE_PLACES_COMPONENT_FORM = { | ||||||
|  |         street_number: 'long_name', | ||||||
|  |         route: 'long_name', | ||||||
|  |         intersection: 'short_name', | ||||||
|  |         political: 'short_name', | ||||||
|  |         country: 'short_name', | ||||||
|  |         administrative_area_level_1: 'short_name', | ||||||
|  |         administrative_area_level_2: 'short_name', | ||||||
|  |         administrative_area_level_3: 'short_name', | ||||||
|  |         administrative_area_level_4: 'short_name', | ||||||
|  |         administrative_area_level_5: 'short_name', | ||||||
|  |         colloquial_area: 'short_name', | ||||||
|  |         locality: 'short_name', | ||||||
|  |         ward: 'short_name', | ||||||
|  |         sublocality_level_1: 'short_name', | ||||||
|  |         sublocality_level_2: 'short_name', | ||||||
|  |         sublocality_level_3: 'short_name', | ||||||
|  |         sublocality_level_5: 'short_name', | ||||||
|  |         neighborhood: 'short_name', | ||||||
|  |         premise: 'short_name', | ||||||
|  |         postal_code: 'short_name', | ||||||
|  |         natural_feature: 'short_name', | ||||||
|  |         airport: 'short_name', | ||||||
|  |         park: 'short_name', | ||||||
|  |         point_of_interest: 'long_name' | ||||||
|  |     }; | ||||||
|  |     /** | ||||||
|  |      * Mapping field with an alias | ||||||
|  |      * key: alias | ||||||
|  |      * value: field | ||||||
|  |      */ | ||||||
|  |     var ADDRESS_FORM = { | ||||||
|  |         street: 'street', | ||||||
|  |         street2: 'street2', | ||||||
|  |         city: 'city', | ||||||
|  |         zip: 'zip', | ||||||
|  |         state_id: 'state_id', | ||||||
|  |         country_id: 'country_id' | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     function fetchValues(model, field_name, value) { | ||||||
|  |         var def = $.Deferred(), | ||||||
|  |             res = {}; | ||||||
|  | 
 | ||||||
|  |         if (model && value) { | ||||||
|  |             rpc.query({ | ||||||
|  |                 'model': model, | ||||||
|  |                 'method': 'search_read', | ||||||
|  |                 'args': [['|', ['name', '=', value], ['code', '=', value]], ['display_name', ]] | ||||||
|  |             }).then(function (record) { | ||||||
|  |                 res[field_name] = record.length == 1 ? record[0] : false; | ||||||
|  |                 def.resolve(res); | ||||||
|  |             }); | ||||||
|  |         } else { | ||||||
|  |             res[field_name] = value; | ||||||
|  |             def.resolve(res); | ||||||
|  |         } | ||||||
|  |         return def; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     function fetchCountryState(model, country, state) { | ||||||
|  |         var def = $.Deferred(); | ||||||
|  | 
 | ||||||
|  |         if (country && state) { | ||||||
|  |             rpc.query({ | ||||||
|  |                 model: model, | ||||||
|  |                 method: 'search_read', | ||||||
|  |                 args: [[['country_id', '=', country], ['code', '=', state]], ['display_name']] | ||||||
|  |             }).then(function(record) { | ||||||
|  |                 def.resolve(record); | ||||||
|  |             }); | ||||||
|  |         } else { | ||||||
|  |             def.resolve([]); | ||||||
|  |         } | ||||||
|  |         return def; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     function gmaps_get_geolocation(place, options) { | ||||||
|  |         if (!place) return {}; | ||||||
|  | 
 | ||||||
|  |         var vals = {}; | ||||||
|  |         _.each(options, function (alias, field) { | ||||||
|  |             if (alias === 'latitude') { | ||||||
|  |                 vals[field] = place.geometry.location.lat(); | ||||||
|  |             } else if (alias === 'longitude') { | ||||||
|  |                 vals[field] = place.geometry.location.lng(); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |         return vals; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     function gmaps_populate_places(place, place_options) { | ||||||
|  |         if (!place) return {}; | ||||||
|  | 
 | ||||||
|  |         var values = {}, | ||||||
|  |             vals; | ||||||
|  |         _.each(place_options, function (option, field) { | ||||||
|  |             if (option instanceof Array && !_.has(values, field)) { | ||||||
|  |                 vals = _.filter(_.map(option, function (opt) { | ||||||
|  |                     return place[opt] || false; | ||||||
|  |                 })); | ||||||
|  |                 values[field] = _.first(vals) || ""; | ||||||
|  |             } else { | ||||||
|  |                 values[field] = place[option] || ""; | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |         return values; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     function gmaps_populate_address(place, address_options, delimiter) { | ||||||
|  |         if (!place) return {}; | ||||||
|  | 
 | ||||||
|  |         var address_options = address_options || {}, | ||||||
|  |             fields_delimiter = delimiter || { | ||||||
|  |                 street: " ", | ||||||
|  |                 street2: ", " | ||||||
|  |             }, | ||||||
|  |             fields_to_fill = {}, | ||||||
|  |             options, temp, dlmter, result = {}; | ||||||
|  | 
 | ||||||
|  |         // initialize object key and value
 | ||||||
|  |         _.each(address_options, function (value, key) { | ||||||
|  |             fields_to_fill[key] = []; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         _.each(address_options, function (options, field) { | ||||||
|  |             // turn all fields options into an Array
 | ||||||
|  |             options = _.flatten([options]); | ||||||
|  |             temp = {}; | ||||||
|  |             _.each(place.address_components, function (component) { | ||||||
|  |                 _.each(_.intersection(options, component.types), function (match) { | ||||||
|  |                     temp[match] = component[GOOGLE_PLACES_COMPONENT_FORM[match]] || false; | ||||||
|  |                 }); | ||||||
|  |             }); | ||||||
|  |             fields_to_fill[field] = _.map(options, function (item) { | ||||||
|  |                 return temp[item]; | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         _.each(fields_to_fill, function (value, key) { | ||||||
|  |             dlmter = fields_delimiter[key] || ' '; | ||||||
|  |             if (key == 'city') { | ||||||
|  |                 result[key] = _.first(_.filter(value)) || ''; | ||||||
|  |             } else { | ||||||
|  |                 result[key] = _.filter(value).join(dlmter); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |         return result; | ||||||
|  |     } | ||||||
|  |     return { | ||||||
|  |         'GOOGLE_PLACES_COMPONENT_FORM': GOOGLE_PLACES_COMPONENT_FORM, | ||||||
|  |         'ADDRESS_FORM': ADDRESS_FORM, | ||||||
|  |         'gmaps_populate_address': gmaps_populate_address, | ||||||
|  |         'gmaps_populate_places': gmaps_populate_places, | ||||||
|  |         'gmaps_get_geolocation': gmaps_get_geolocation, | ||||||
|  |         'fetchValues': fetchValues, | ||||||
|  |         'fetchCountryState': fetchCountryState | ||||||
|  |     } | ||||||
|  | }); | ||||||
|  | @ -0,0 +1,101 @@ | ||||||
|  | /* https://github.com/twbs/bootstrap/issues/4160 */ | ||||||
|  | /* put google places autocomplete dropdown results above the bootstrap modal 1050 zindex. */ | ||||||
|  | .pac-container { | ||||||
|  |     z-index: 1051 !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .o_field_x2many_map { | ||||||
|  |     margin-top: 10px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .o_map_container { | ||||||
|  |     min-height: 400px; | ||||||
|  |     min-width: 100%; | ||||||
|  |     height: 100%; | ||||||
|  |     .o-flex-display(); | ||||||
|  | 
 | ||||||
|  |     .o_map_view { | ||||||
|  |         .o-flex(1, 1, auto); | ||||||
|  |         min-width: 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .o_map_left_sidenav { | ||||||
|  |         left: 0; | ||||||
|  |         top: 0; | ||||||
|  |         bottom: 0; | ||||||
|  |         max-width: 400px; | ||||||
|  |         width: 400px; | ||||||
|  |         position: absolute; | ||||||
|  |         z-index: 1; | ||||||
|  |         background-color: #f5f5f5; | ||||||
|  |         overflow-x: hidden; | ||||||
|  |         transition: 0.5s; | ||||||
|  |         transform: translate3d(0, 0, 0); | ||||||
|  |         -webkit-transform: translate3d(0, 0, 0); | ||||||
|  | 
 | ||||||
|  |         &.closed { | ||||||
|  |             transform: translate3d(-100%, 0, 0); | ||||||
|  |             -webkit-transform: translate3d(-100%, 0, 0); | ||||||
|  | 
 | ||||||
|  |             .sidenav-body .panel { | ||||||
|  |                 border: none; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .o_map_right_sidebar { | ||||||
|  |         right: 0; | ||||||
|  |         bottom: 0; | ||||||
|  |         background-color: #f5f5f5; | ||||||
|  | 
 | ||||||
|  |         &.closed { | ||||||
|  |             width: 0; | ||||||
|  |             transition: 0.5s; | ||||||
|  |             transform: translateX(-100%); | ||||||
|  |             -webkit-transform: translateX(-100%); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         &.open { | ||||||
|  |             width: 250px; | ||||||
|  |             transition: 0.5s; | ||||||
|  |             transform: translateX(0); | ||||||
|  |             -webkit-transform: translateX(0); | ||||||
|  |             overflow-x: auto; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         ul { | ||||||
|  |             margin-top: 10px; | ||||||
|  |             padding-left: 3px; | ||||||
|  |             list-style: none; | ||||||
|  |             font-size: 12px; | ||||||
|  | 
 | ||||||
|  |             li { | ||||||
|  |                 padding-bottom: 8px; | ||||||
|  | 
 | ||||||
|  |                 span { | ||||||
|  |                     padding: 3px 6px; | ||||||
|  | 
 | ||||||
|  |                     &.total { | ||||||
|  |                         font-size: 10px; | ||||||
|  |                         background: #ddd; | ||||||
|  |                         border-radius: 3px; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .o_map_button { | ||||||
|  |         cursor: pointer; | ||||||
|  |         text-align: center; | ||||||
|  |         width: auto; | ||||||
|  |         padding: 0px 6px; | ||||||
|  |         margin: 0px 15px 5px 0px; | ||||||
|  |         background-color: #ffffff; | ||||||
|  |         border-radius: 2px; | ||||||
|  |         line-height: 25px; | ||||||
|  |         font-family: 'Roboto,Arial,sans-serif'; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,30 @@ | ||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <template> | ||||||
|  |     <div t-name="MapView.MapView" class="o_map_container"> | ||||||
|  |         <div class="o_map_view"></div> | ||||||
|  |         <div class="o_map_right_sidebar closed"></div> | ||||||
|  |     </div> | ||||||
|  |     <t t-name="MapView.MapViewGroupInfo"> | ||||||
|  |         <ul t-if="widget.groups"> | ||||||
|  |             <t t-foreach="widget.groups" t-as="group"> | ||||||
|  |                 <li> | ||||||
|  |                     <img t-att-src="group.marker"/> | ||||||
|  |                     <span class="title"><t t-esc="group.title"/></span> | ||||||
|  |                     <span class="total"><t t-esc="group.count"/></span> | ||||||
|  |                 </li> | ||||||
|  |             </t> | ||||||
|  |         </ul> | ||||||
|  |     </t> | ||||||
|  |     <t t-name="MapView.buttons"> | ||||||
|  |         <div class="o_list_buttons"> | ||||||
|  |             <t t-if="(widget.is_action_enabled !== undefined ? widget.is_action_enabled('create') : false) || !widget.isReadonly"> | ||||||
|  |                 <button type="button" class="btn btn-primary btn-sm o-map-button-new" accesskey="c"> | ||||||
|  |                     <t t-esc="create_text || _t('Create')"/> | ||||||
|  |                 </button> | ||||||
|  |             </t> | ||||||
|  |             <button type="button" class="btn btn-default btn-sm o-map-button-center-map"> | ||||||
|  |                 <t t-esc="_t('Center Map')"/> | ||||||
|  |             </button> | ||||||
|  |         </div> | ||||||
|  |     </t> | ||||||
|  | </template> | ||||||
|  | @ -0,0 +1,35 @@ | ||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <odoo> | ||||||
|  |     <template id="web_google_maps.assets_gmaps"> | ||||||
|  |         <t t-set="google_maps_api_key" t-value="request.env['ir.config_parameter'].sudo().get_param('google.api_key_geocode')"/> | ||||||
|  |         <t t-set="google_maps_lang_localization" t-value="request.env['ir.config_parameter'].sudo().get_param('google.lang_localization')"/> | ||||||
|  |         <t t-set="google_maps_region_localization" t-value="request.env['ir.config_parameter'].sudo().get_param('google.region_localization')"/> | ||||||
|  |         <t t-set="google_maps_libraries" t-value="request.env['ir.config_parameter'].sudo().get_param('google.maps_libraries')"/> | ||||||
|  |         <t t-if="google_maps_api_key"> | ||||||
|  |             <script t-att-async="'async'" t-att-defer="'defer'" t-attf-src="https://maps.googleapis.com/maps/api/js?v=quarterly&key=#{google_maps_api_key}&libraries=#{google_maps_libraries}#{google_maps_lang_localization}#{google_maps_region_localization}"></script> | ||||||
|  |         </t> | ||||||
|  |         <t t-if="not google_maps_api_key"> | ||||||
|  |             <script t-att-async="'async'" t-att-defer="'defer'" t-attf-src="https://maps.googleapis.com/maps/api/js?v=quarterly&libraries=#{google_maps_libraries}#{google_maps_lang_localization}#{google_maps_region_localization}"></script> | ||||||
|  |         </t> | ||||||
|  |         <script src="/web_google_maps/static/lib/markercluster/markerclusterer.js"></script> | ||||||
|  |     </template> | ||||||
|  |     <template id="webclient_bootstrap" name="webclient_bootstrap gmaps" inherit_id="web.webclient_bootstrap"> | ||||||
|  |         <xpath expr="//t[@t-call-assets='web.assets_common']" position="before"> | ||||||
|  |             <t t-call="web_google_maps.assets_gmaps"/> | ||||||
|  |         </xpath> | ||||||
|  |     </template> | ||||||
|  |     <template id="assets_backend" name="web_google_maps assets backend" inherit_id="web.assets_backend"> | ||||||
|  |         <xpath expr="." position="inside"> | ||||||
|  |             <link rel="stylesheet" type="text/less" href="/web_google_maps/static/src/less/web_maps.less"/> | ||||||
|  |             <script type="text/javascript" src="/web_google_maps/static/src/js/view/map/map_model.js"></script> | ||||||
|  |             <script type="text/javascript" src="/web_google_maps/static/src/js/view/map/map_controller.js"></script> | ||||||
|  |             <script type="text/javascript" src="/web_google_maps/static/src/js/view/map/map_renderer.js"></script> | ||||||
|  |             <script type="text/javascript" src="/web_google_maps/static/src/js/view/map/map_view.js"></script> | ||||||
|  |             <script type="text/javascript" src="/web_google_maps/static/src/js/view/view_registry.js"></script> | ||||||
|  |             <script type="text/javascript" src="/web_google_maps/static/src/js/fields/relational_fields.js"></script> | ||||||
|  |             <script type="text/javascript" src="/web_google_maps/static/src/js/widgets/utils.js"></script> | ||||||
|  |             <script type="text/javascript" src="/web_google_maps/static/src/js/widgets/gplaces_autocomplete.js"></script> | ||||||
|  |             <script type="text/javascript" src="/web_google_maps/static/src/js/widgets/fields_registry.js"></script> | ||||||
|  |         </xpath> | ||||||
|  |     </template> | ||||||
|  | </odoo> | ||||||
|  | @ -0,0 +1,74 @@ | ||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <odoo> | ||||||
|  |     <record id="view_web_google_maps_config_settings" model="ir.ui.view"> | ||||||
|  |         <field name="name">view.web.google.config.settings</field> | ||||||
|  |         <field name="model">res.config.settings</field> | ||||||
|  |         <field name="inherit_id" ref="base_setup.res_config_settings_view_form" /> | ||||||
|  |         <field name="arch" type="xml"> | ||||||
|  |             <xpath expr="//div[@name='integration']" position="after"> | ||||||
|  |                 <div name="web_google_maps"> | ||||||
|  |                     <h2>Google Maps View</h2> | ||||||
|  |                     <div class="row mt16 o_settings_container"> | ||||||
|  |                         <div class="col-xs-12 col-md-6 o_setting_box"> | ||||||
|  |                             <div class="o_setting_right_pane"> | ||||||
|  |                                 <label string="Configure your Google Maps View"/> | ||||||
|  |                                 <div class="text-muted"> | ||||||
|  |                                     <p>Set API keys and map localization</p> | ||||||
|  |                                     <span>Visit the <a href="https://developers.google.com/maps/documentation/javascript/localization" target="_blank">page</a> about Localizing the Map</span> | ||||||
|  |                                 </div> | ||||||
|  |                                 <div class="content-group"> | ||||||
|  |                                     <div class="mt16"> | ||||||
|  |                                         <label for="google_maps_view_api_key" string="Api key"/> | ||||||
|  |                                         <field name="google_maps_view_api_key"/> | ||||||
|  |                                     </div> | ||||||
|  |                                     <div class="mt16"> | ||||||
|  |                                         <label for="google_maps_theme" string="Theme"/> | ||||||
|  |                                         <field name="google_maps_theme"/> | ||||||
|  |                                     </div> | ||||||
|  |                                     <div class="mt16"> | ||||||
|  |                                         <label for="google_maps_lang_localization" string="Language"/> | ||||||
|  |                                         <field name="google_maps_lang_localization"/> | ||||||
|  |                                     </div> | ||||||
|  |                                     <div class="mt16" attrs="{'invisible': [('google_maps_lang_localization', 'in', [False, ''])]}"> | ||||||
|  |                                         <div class="text-muted"> | ||||||
|  |                                             If you set the language of the map, it's important to consider setting the region too. This helps ensure that your application complies with local laws. | ||||||
|  |                                         </div> | ||||||
|  |                                         <label for="google_maps_region_localization" string="Region"/> | ||||||
|  |                                         <field name="google_maps_region_localization"/> | ||||||
|  |                                     </div> | ||||||
|  |                                 </div> | ||||||
|  |                             </div> | ||||||
|  |                         </div> | ||||||
|  |                     </div> | ||||||
|  |                     <h2> Google Maps Libraries </h2> | ||||||
|  |                     <div class="row mt16 o_settings_container"> | ||||||
|  |                         <div class="col-xs-12 col-md-6 o_setting_box"> | ||||||
|  |                             <div class="o_setting_left_pane"> | ||||||
|  |                                 <field name="google_maps_geometry"/> | ||||||
|  |                             </div> | ||||||
|  |                             <div class="o_setting_right_pane"> | ||||||
|  |                                 <label for="google_maps_geometry"/> | ||||||
|  |                                 <div class="text-muted"> | ||||||
|  |                                     Geometry includes utility functions for calculating scalar geometric values (such as distance and area) on the surface of the earth.  | ||||||
|  |                                     Consult the <a href="https://developers.google.com/maps/documentation/javascript/geometry">Geometry library documentation</a> for more information. | ||||||
|  |                                 </div> | ||||||
|  |                             </div> | ||||||
|  |                         </div> | ||||||
|  |                         <div class="col-xs-12 col-md-6 o_setting_box"> | ||||||
|  |                             <div class="o_setting_left_pane"> | ||||||
|  |                                 <field name="google_maps_places"/> | ||||||
|  |                             </div> | ||||||
|  |                             <div class="o_setting_right_pane"> | ||||||
|  |                                 <label for="google_maps_places"/> | ||||||
|  |                                 <div class="text-muted"> | ||||||
|  |                                     Places enables your application to search for places such as establishments, geographic locations, or prominent points of interest, within a defined area.  | ||||||
|  |                                     Consult the <a href="https://developers.google.com/maps/documentation/javascript/places">Places library documentation</a> for more information. | ||||||
|  |                                 </div> | ||||||
|  |                             </div> | ||||||
|  |                         </div> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |             </xpath> | ||||||
|  |         </field> | ||||||
|  |     </record> | ||||||
|  | </odoo> | ||||||
|  | @ -0,0 +1,104 @@ | ||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <odoo> | ||||||
|  |     <record id="view_res_partner_map" model="ir.ui.view"> | ||||||
|  |         <field name="name">view.res.partner.map</field> | ||||||
|  |         <field name="model">res.partner</field> | ||||||
|  |         <field name="arch" type="xml"> | ||||||
|  |             <map class="o_res_partner_map" library="geometry" string="Map" lat="partner_latitude" lng="partner_longitude" colors="blue:company_type=='person';green:company_type=='company';"> | ||||||
|  |                 <field name="id"/> | ||||||
|  |                 <field name="partner_latitude"/> | ||||||
|  |                 <field name="partner_longitude"/> | ||||||
|  |                 <field name="company_type"/> | ||||||
|  |                 <field name="color"/> | ||||||
|  |                 <field name="display_name"/> | ||||||
|  |                 <field name="title"/> | ||||||
|  |                 <field name="email"/> | ||||||
|  |                 <field name="parent_id"/> | ||||||
|  |                 <field name="is_company"/> | ||||||
|  |                 <field name="function"/> | ||||||
|  |                 <field name="phone"/> | ||||||
|  |                 <field name="street"/> | ||||||
|  |                 <field name="street2"/> | ||||||
|  |                 <field name="zip"/> | ||||||
|  |                 <field name="city"/> | ||||||
|  |                 <field name="country_id"/> | ||||||
|  |                 <field name="mobile"/> | ||||||
|  |                 <field name="state_id"/> | ||||||
|  |                 <field name="category_id"/> | ||||||
|  |                 <field name="image_small"/> | ||||||
|  |                 <field name="type"/> | ||||||
|  |                 <templates> | ||||||
|  |                     <t t-name="kanban-box"> | ||||||
|  |                         <div class="oe_kanban_global_click o_res_partner_kanban"> | ||||||
|  |                             <div class="o_kanban_image"> | ||||||
|  |                                 <t t-if="record.image_small.raw_value"> | ||||||
|  |                                     <img t-att-src="kanban_image('res.partner', 'image_small', record.id.raw_value)"/> | ||||||
|  |                                 </t> | ||||||
|  |                                 <t t-if="!record.image_small.raw_value"> | ||||||
|  |                                     <t t-if="record.type.raw_value === 'delivery'"> | ||||||
|  |                                         <img t-att-src='_s + "/base/static/src/img/truck.png"' class="o_kanban_image oe_kanban_avatar_smallbox"/> | ||||||
|  |                                     </t> | ||||||
|  |                                     <t t-if="record.type.raw_value === 'invoice'"> | ||||||
|  |                                         <img t-att-src='_s + "/base/static/src/img/money.png"' class="o_kanban_image oe_kanban_avatar_smallbox"/> | ||||||
|  |                                     </t> | ||||||
|  |                                     <t t-if="record.type.raw_value != 'invoice' && record.type.raw_value != 'delivery'"> | ||||||
|  |                                         <t t-if="record.is_company.raw_value === true"> | ||||||
|  |                                             <img t-att-src='_s + "/base/static/src/img/company_image.png"'/> | ||||||
|  |                                         </t> | ||||||
|  |                                         <t t-if="record.is_company.raw_value === false"> | ||||||
|  |                                             <img t-att-src='_s + "/base/static/src/img/avatar.png"'/> | ||||||
|  |                                         </t> | ||||||
|  |                                     </t> | ||||||
|  |                                 </t> | ||||||
|  |                             </div> | ||||||
|  |                             <div class="oe_kanban_details"> | ||||||
|  |                                 <strong class="o_kanban_record_title oe_partner_heading"> | ||||||
|  |                                     <field name="display_name"/> | ||||||
|  |                                 </strong> | ||||||
|  |                                 <div class="o_kanban_tags_section oe_kanban_partner_categories"> | ||||||
|  |                                     <span class="oe_kanban_list_many2many"> | ||||||
|  |                                         <field name="category_id" widget="many2many_tags" options="{'color_field': 'color'}"/> | ||||||
|  |                                     </span> | ||||||
|  |                                 </div> | ||||||
|  |                                 <ul> | ||||||
|  |                                     <li t-if="record.parent_id.raw_value and !record.function.raw_value"> | ||||||
|  |                                         <field name="parent_id"/> | ||||||
|  |                                     </li> | ||||||
|  |                                     <li t-if="!record.parent_id.raw_value and record.function.raw_value"> | ||||||
|  |                                         <field name="function"/> | ||||||
|  |                                     </li> | ||||||
|  |                                     <li t-if="record.parent_id.raw_value and record.function.raw_value"> | ||||||
|  |                                         <field name="function"/> at <field name="parent_id"/> | ||||||
|  |                                     </li> | ||||||
|  |                                     <li t-if="record.city.raw_value and !record.country_id.raw_value"> | ||||||
|  |                                         <field name="city"/> | ||||||
|  |                                     </li> | ||||||
|  |                                     <li t-if="!record.city.raw_value and record.country_id.raw_value"> | ||||||
|  |                                         <field name="country_id"/> | ||||||
|  |                                     </li> | ||||||
|  |                                     <li t-if="record.city.raw_value and record.country_id.raw_value"> | ||||||
|  |                                         <field name="city"/> | ||||||
|  |                 ,                        <field name="country_id"/> | ||||||
|  |                                     </li> | ||||||
|  |                                     <li t-if="record.email.raw_value" class="o_text_overflow"> | ||||||
|  |                                         <field name="email"/> | ||||||
|  |                                     </li> | ||||||
|  |                                 </ul> | ||||||
|  |                                 <div class="oe_kanban_partner_links"/> | ||||||
|  |                             </div> | ||||||
|  |                         </div> | ||||||
|  |                     </t> | ||||||
|  |                 </templates> | ||||||
|  |             </map> | ||||||
|  |         </field> | ||||||
|  |     </record> | ||||||
|  |     <record id="base.action_partner_form" model="ir.actions.act_window"> | ||||||
|  |         <field name="view_mode">kanban,tree,form,map</field> | ||||||
|  |     </record> | ||||||
|  |     <record id="action_partner_form_view3" model="ir.actions.act_window.view"> | ||||||
|  |         <field eval="3" name="sequence"/> | ||||||
|  |         <field name="view_mode">map</field> | ||||||
|  |         <field name="view_id" ref="view_res_partner_map"/> | ||||||
|  |         <field name="act_window_id" ref="base.action_partner_form"/> | ||||||
|  |     </record> | ||||||
|  | </odoo> | ||||||