From faf5507f2630a6fcb604bc722043905fae689889 Mon Sep 17 00:00:00 2001 From: Boris Gallet Date: Wed, 13 Mar 2024 11:58:08 +0100 Subject: [PATCH] [MIG] partner_profiles_portal : Migration to 16.0 --- partner_profiles_portal/.gitignore | 2 + partner_profiles_portal/README.rst | 43 ++ partner_profiles_portal/__init__.py | 4 + partner_profiles_portal/__manifest__.py | 46 ++ .../controllers/__init__.py | 6 + .../controllers/portal_my_account.py | 168 +++++++ .../controllers/portal_my_positions.py | 80 +++ .../controllers/portal_my_structures.py | 100 ++++ .../controllers/portal_position_profile.py | 120 +++++ .../controllers/portal_structure_profile.py | 234 +++++++++ partner_profiles_portal/i18n/README | 1 + partner_profiles_portal/i18n/fr.po | 458 +++++++++++++++++ .../i18n/partner_profiles_portal.pot | 462 ++++++++++++++++++ partner_profiles_portal/models/__init__.py | 2 + partner_profiles_portal/models/res_partner.py | 59 +++ .../security/members_security.xml | 28 ++ .../views/portal_home_template.xml | 39 ++ .../views/portal_my_account.xml | 243 +++++++++ .../views/portal_my_positions_template.xml | 44 ++ .../views/portal_my_structures_template.xml | 38 ++ .../portal_partner_position_template.xml | 94 ++++ .../portal_partner_structure_template.xml | 318 ++++++++++++ .../views/res_partner_view.xml | 25 + partner_profiles_portal/wizard/__init__.py | 2 + .../wizard/create_position_profile.py | 20 + .../wizard/create_position_profile.xml | 13 + 26 files changed, 2649 insertions(+) create mode 100644 partner_profiles_portal/.gitignore create mode 100644 partner_profiles_portal/README.rst create mode 100644 partner_profiles_portal/__init__.py create mode 100644 partner_profiles_portal/__manifest__.py create mode 100644 partner_profiles_portal/controllers/__init__.py create mode 100644 partner_profiles_portal/controllers/portal_my_account.py create mode 100644 partner_profiles_portal/controllers/portal_my_positions.py create mode 100644 partner_profiles_portal/controllers/portal_my_structures.py create mode 100644 partner_profiles_portal/controllers/portal_position_profile.py create mode 100644 partner_profiles_portal/controllers/portal_structure_profile.py create mode 100644 partner_profiles_portal/i18n/README create mode 100644 partner_profiles_portal/i18n/fr.po create mode 100644 partner_profiles_portal/i18n/partner_profiles_portal.pot create mode 100644 partner_profiles_portal/models/__init__.py create mode 100644 partner_profiles_portal/models/res_partner.py create mode 100644 partner_profiles_portal/security/members_security.xml create mode 100644 partner_profiles_portal/views/portal_home_template.xml create mode 100644 partner_profiles_portal/views/portal_my_account.xml create mode 100644 partner_profiles_portal/views/portal_my_positions_template.xml create mode 100644 partner_profiles_portal/views/portal_my_structures_template.xml create mode 100644 partner_profiles_portal/views/portal_partner_position_template.xml create mode 100644 partner_profiles_portal/views/portal_partner_structure_template.xml create mode 100644 partner_profiles_portal/views/res_partner_view.xml create mode 100644 partner_profiles_portal/wizard/__init__.py create mode 100644 partner_profiles_portal/wizard/create_position_profile.py create mode 100644 partner_profiles_portal/wizard/create_position_profile.xml diff --git a/partner_profiles_portal/.gitignore b/partner_profiles_portal/.gitignore new file mode 100644 index 0000000..6da5887 --- /dev/null +++ b/partner_profiles_portal/.gitignore @@ -0,0 +1,2 @@ +*.*~ +*pyc diff --git a/partner_profiles_portal/README.rst b/partner_profiles_portal/README.rst new file mode 100644 index 0000000..267d172 --- /dev/null +++ b/partner_profiles_portal/README.rst @@ -0,0 +1,43 @@ +=============== +partner_profiles_portal +=============== + +Provide portal pages and forms to manage partner's profiles from portal home space. + +Installation +============ + +Use Odoo normal module installation procedure to install +``partner_profiles_portal``. + +Known issues / Roadmap +====================== + +None yet. +Bug Tracker +=========== + +Bugs are tracked on `our issues website `_. 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. + +Credits +======= + +Contributors +------------ + +* Stéphan Sainléger + +Funders +------- + +The development of this module has been financially supported by: +* Elabore (https://elabore.coop) + + +Maintainer +---------- + +This module is maintained by Elabore. \ No newline at end of file diff --git a/partner_profiles_portal/__init__.py b/partner_profiles_portal/__init__.py new file mode 100644 index 0000000..7aaf0e4 --- /dev/null +++ b/partner_profiles_portal/__init__.py @@ -0,0 +1,4 @@ + +from . import models +from . import controllers +from . import wizard \ No newline at end of file diff --git a/partner_profiles_portal/__manifest__.py b/partner_profiles_portal/__manifest__.py new file mode 100644 index 0000000..50f03f6 --- /dev/null +++ b/partner_profiles_portal/__manifest__.py @@ -0,0 +1,46 @@ +# Copyright 2022 Stéphan Sainléger (Elabore) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "partner_profiles_portal", + "version": "16.0.1.0.0", + "author": "Elabore", + "website": "https://elabore.coop", + "maintainer": "Stéphan Sainléger", + "license": "AGPL-3", + "category": "Tools", + "summary": "Provide portal pages and forms to manage partner's profiles from portal home space.", + # any module necessary for this one to work correctly + "depends": [ + "base", + "partner_contact_in_several_companies", + "partner_profiles", + "portal", + "website", + ], + "qweb": [], + "external_dependencies": { + "python": [], + }, + # always loaded + "data": [ + "security/members_security.xml", + "views/portal_home_template.xml", + "views/portal_my_structures_template.xml", + "views/portal_my_positions_template.xml", + "views/portal_partner_structure_template.xml", + "views/portal_partner_position_template.xml", + "views/portal_my_account.xml", + "views/res_partner_view.xml", + "wizard/create_position_profile.xml", + ], + # only loaded in demonstration mode + "demo": [], + "js": [], + "css": [], + "installable": True, + # Install this module automatically if all dependency have been previously + # and independently installed. Used for synergetic or glue modules. + "auto_install": False, + "application": False, +} \ No newline at end of file diff --git a/partner_profiles_portal/controllers/__init__.py b/partner_profiles_portal/controllers/__init__.py new file mode 100644 index 0000000..a22edbf --- /dev/null +++ b/partner_profiles_portal/controllers/__init__.py @@ -0,0 +1,6 @@ + +from . import portal_my_structures +from . import portal_structure_profile +from . import portal_my_positions +from . import portal_position_profile +from . import portal_my_account \ No newline at end of file diff --git a/partner_profiles_portal/controllers/portal_my_account.py b/partner_profiles_portal/controllers/portal_my_account.py new file mode 100644 index 0000000..5ec8887 --- /dev/null +++ b/partner_profiles_portal/controllers/portal_my_account.py @@ -0,0 +1,168 @@ +import base64 +from odoo import tools +from odoo.http import request, route +from odoo.addons.portal.controllers.portal import CustomerPortal + +class CustomerPortalMyProfile(CustomerPortal): + + def _get_mandatory_main_fields(self): + return ["main_name", "main_email"] + + def _get_optional_main_fields(self): + return ["main_street", "main_street2", "main_city", "main_country_id", "main_phone", "main_mobile", "main_zip", "main_state_id", "main_website"] + + def _get_mandatory_public_fields(self): + return ["public_name"] + + def _get_optional_public_fields(self): + return ["public_email", "public_street", "public_street2", "public_city", "public_phone", "public_mobile", "public_zip", "public_website"] + + def _get_special_fields(self): + return ["main_logo"] + + def _get_main_boolean_account_fields(self): + '''Provides the fields for which we must check the presence + in form's kw to know the value to save in the partner field. + All of them MUST start with "main_".''' + fields = [] + return fields + + def _get_public_boolean_account_fields(self): + '''Provides the fields for which we must check the presence + in form's kw to know the value to save in the partner field. + All of them MUST start with "public_".''' + fields = [] + return fields + + def _transform_in_partner_fields(self, kw, profile_fields, prefix=""): + '''Transforms kw's values in res_partner fields and values''' + return {key[len(prefix):]: kw[key] for key in profile_fields if key in kw} + + def _add_boolean_values(self, values, kw, boolean_fields, prefix=""): + for key in boolean_fields: + values.update( + { + key[len(prefix):]: kw.get(key, "off") == "on", + } + ) + return values + + def _retrieve_main_values(self, data): + main_fields = self._get_mandatory_main_fields() + self._get_optional_main_fields() + values = self._transform_in_partner_fields(data, main_fields, "main_") + boolean_fields = self._get_main_boolean_account_fields() + values = self._add_boolean_values(values, data, boolean_fields, "main_") + if 'main_logo' in data: + image = data.get('main_logo') + if image: + image = image.read() + image = base64.b64encode(image) + values.update({ + 'image_1920': image + }) + return values + + def _retrieve_public_values(self, data): + public_fields = self._get_mandatory_public_fields() + self._get_optional_public_fields() + values = self._transform_in_partner_fields(data, public_fields, "public_") + boolean_fields = self._get_public_boolean_account_fields() + values = self._add_boolean_values(values, data, boolean_fields, "public_") + return values + + def _get_page_opening_values(self): + # Just retrieve the values to display for Selection fields + countries = request.env["res.country"].sudo().search([]) + states = request.env['res.country.state'].sudo().search([]) + values = { + "countries": countries, + 'states': states, + } + return values + + @route(["/my/account"], type="http", auth="user", website=True) + def account(self, redirect=None, **post): + values = self._prepare_portal_layout_values() + user = request.env.user + partner = user.partner_id + public_partner = partner.public_profile_id + values.update({ + 'error': {}, + 'error_message': [], + }) + + if post and request.httprequest.method == 'POST': + error, error_message = self.details_form_validate(post) + values.update({'error': error, 'error_message': error_message}) + values.update(post) + if not error: + # Save main profile values + values = self._retrieve_main_values(post) + partner.sudo().write(values) + # Save public profile values + public_values = self._retrieve_public_values(post) + if len(public_values) > 0: + public_partner.sudo().write(public_values) + # Email change generates a change of user's login + if post.get("main_email", user.login) != user.login: + user.login = post["main_email"] + return request.redirect("/web/session/logout") + if redirect: + return request.redirect(redirect) + return request.redirect('/my/home') + + values.update(self._get_page_opening_values()) + values.update({ + 'partner': partner, + 'public_partner': public_partner, + 'has_check_vat': hasattr(request.env['res.partner'], 'check_vat'), + 'redirect': "/my/account?success=True", + 'page_name': 'my_details', + }) + + response = request.render("portal.portal_my_details", values) + response.headers['X-Frame-Options'] = 'DENY' + + return response + + + + def details_form_validate(self, data): + error = dict() + error_message = [] + + # Validation + for field_name in self._get_mandatory_main_fields() + self._get_mandatory_public_fields(): + if not data.get(field_name): + error[field_name] = 'missing' + + # email validation + if data.get('main_email') and not tools.single_email_re.match(data.get('main_email')): + error["main_email"] = 'error' + error_message.append(_('Invalid Email! Please enter a valid email address.')) + if data.get('public_email') and not tools.single_email_re.match(data.get('public_email')): + error["public_email"] = 'error' + error_message.append(_('Invalid Public Email! Please enter a valid public email address.')) + + # public name uniqueness + if data.get("public_name") and request.env["res.partner"].sudo().search( + [ + ("name", "=", data.get("public_name")), + ("is_public_profile", "=", True), + ("contact_id", "!=", request.env.user.partner_id.id), + ] + ): + error["public_name"] = "error" + error_message.append( + _("This public name is already used, please find an other idea.") + ) + + # error message for empty required fields + if [err for err in error.values() if err == 'missing']: + error_message.append('Some required fields are empty.') + + unknown = [k for k in data if k not in self._get_mandatory_main_fields() + self._get_optional_main_fields() + self._get_mandatory_public_fields() + self._get_optional_public_fields() + self._get_special_fields()] + if unknown: + error['common'] = 'Unknown field' + error_message.append("Unknown field '%s'" % ','.join(unknown)) + + return error, error_message \ No newline at end of file diff --git a/partner_profiles_portal/controllers/portal_my_positions.py b/partner_profiles_portal/controllers/portal_my_positions.py new file mode 100644 index 0000000..bb00b99 --- /dev/null +++ b/partner_profiles_portal/controllers/portal_my_positions.py @@ -0,0 +1,80 @@ +# Copyright 2020 Lokavaluto () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import http, _ +from odoo.http import request +from odoo.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager + + +class CustomerPortalMyPositions(CustomerPortal): + + def _get_domain_my_positions(self, user): + if user.partner_id.structure_position_ids: + return [("id", "in", user.partner_id.structure_position_ids.ids), + ("is_company", "=", False), + ("is_position_profile", "=", True), + ] + else: + return None + + def _prepare_portal_layout_values(self): + values = super(CustomerPortalMyPositions, self)._prepare_portal_layout_values() + domain = self._get_domain_my_structures(request.env.user) + values["structure_count"] = request.env["res.partner"].search_count(domain) if domain else 0 + return values + + @http.route( + ["/my/positions", "/my/positions/page/"], + type="http", + auth="user", + website=True, + ) + def portal_my_positions( + self, page=1, date_begin=None, date_end=None, sortby=None, **kw + ): + values = self._prepare_portal_layout_values() + position = request.env["res.partner"] + domain = self._get_domain_my_positions(request.env.user) + + searchbar_sortings = { + "name": {"label": _("Name"), "order": "name"}, + "parent_id": {"label": _("Company"), "order": "parent_id"}, + } + if not sortby: + sortby = "name" + order = searchbar_sortings[sortby]["order"] + + # archive groups - Default Group By 'create_date' + archive_groups = self._get_archive_groups("res.partner", domain) + + # structures count + position_count = position.search_count(domain) if domain else 0 + # pager + pager = portal_pager( + url="/my/positions", + url_args={"sortby": sortby}, + total=position_count, + page=page, + step=self._items_per_page, + ) + + # content according to pager and archive selected + positions = position.search( + domain, + order=order, + limit=self._items_per_page, + offset=pager["offset"], + ) if domain else None + request.session["my_positions_history"] = positions.ids[:100] if positions else None + + values.update( + { + "positions": positions, + "page_name": "position", + "archive_groups": archive_groups, + "default_url": "/my/positions", + "pager": pager, + "searchbar_sortings": searchbar_sortings, + "sortby": sortby, + } + ) + return request.render("partner_profiles_portal.portal_my_positions", values) \ No newline at end of file diff --git a/partner_profiles_portal/controllers/portal_my_structures.py b/partner_profiles_portal/controllers/portal_my_structures.py new file mode 100644 index 0000000..ca576a5 --- /dev/null +++ b/partner_profiles_portal/controllers/portal_my_structures.py @@ -0,0 +1,100 @@ +# Copyright 2020 Lokavaluto () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import fields as odoo_fields +from odoo import http, _ +from odoo.http import request +from odoo.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager + + +class CustomerPortalMyStructures(CustomerPortal): + + def _get_domain_my_structures(self, user): + if user.partner_id.other_contact_ids: + main_structure_ids = user.partner_id.other_contact_ids.mapped("parent_id") + return [("id", "in", main_structure_ids.ids), + ("is_company", "=", True) + ] + else: + return None + + def _get_archive_groups(self, model, domain=None, fields=None, groupby="create_date", order="create_date desc"): + if not model: + return [] + if domain is None: + domain = [] + if fields is None: + fields = ['name', 'create_date'] + groups = [] + for group in request.env[model]._read_group_raw(domain, fields=fields, groupby=groupby, orderby=order): + dates, label = group[groupby] + date_begin, date_end = dates.split('/') + groups.append({ + 'date_begin': odoo_fields.Date.to_string(odoo_fields.Date.from_string(date_begin)), + 'date_end': odoo_fields.Date.to_string(odoo_fields.Date.from_string(date_end)), + 'name': label, + 'item_count': group[groupby + '_count'] + }) + return groups + + def _prepare_portal_layout_values(self): + values = super(CustomerPortalMyStructures, self)._prepare_portal_layout_values() + domain = self._get_domain_my_structures(request.env.user) + values["structure_count"] = request.env["res.partner"].search_count(domain) if domain else 0 + return values + + @http.route( + ["/my/structures", "/my/structures/page/"], + type="http", + auth="user", + website=True, + ) + def portal_my_structures( + self, page=1, date_begin=None, date_end=None, sortby=None, **kw + ): + values = self._prepare_portal_layout_values() + structure = request.env["res.partner"] + domain = self._get_domain_my_structures(request.env.user) + + searchbar_sortings = { + "name": {"label": _("Name"), "order": "name"}, + "parent_id": {"label": _("Company"), "order": "parent_id"}, + } + if not sortby: + sortby = "name" + order = searchbar_sortings[sortby]["order"] + + # archive groups - Default Group By 'create_date' + archive_groups = self._get_archive_groups("res.partner", domain) + + # structures count + structure_count = structure.search_count(domain) if domain else 0 + # pager + pager = portal_pager( + url="/my/structures", + url_args={"sortby": sortby}, + total=structure_count, + page=page, + step=self._items_per_page, + ) + + # content according to pager and archive selected + structures = structure.search( + domain, + order=order, + limit=self._items_per_page, + offset=pager["offset"], + ) if domain else None + request.session["my_structures_history"] = structures.ids[:100] if structures else None + + values.update( + { + "structures": structures, + "page_name": "structure", + "archive_groups": archive_groups, + "default_url": "/my/structures", + "pager": pager, + "searchbar_sortings": searchbar_sortings, + "sortby": sortby, + } + ) + return request.render("partner_profiles_portal.portal_my_structures", values) \ No newline at end of file diff --git a/partner_profiles_portal/controllers/portal_position_profile.py b/partner_profiles_portal/controllers/portal_position_profile.py new file mode 100644 index 0000000..c71db7e --- /dev/null +++ b/partner_profiles_portal/controllers/portal_position_profile.py @@ -0,0 +1,120 @@ +# Copyright 2020 Lokavaluto () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import base64 +from odoo import http, tools, _ +from odoo.exceptions import AccessError, MissingError +from odoo.http import request +from odoo.addons.portal.controllers.portal import CustomerPortal + + +class CustomerPortalPositionProfile(CustomerPortal): + + def _position_get_page_view_values(self, position, access_token, **kwargs): + values = { + "page_name": "position", + "position": position, + } + return self._get_page_view_values( + position, access_token, values, "my_positions_history", False, **kwargs + ) + + def _details_position_form_validate(self, data, position_id): + error = dict() + error_message = [] + # email validation + if data.get("email") and not tools.single_email_re.match(data.get("email")): + error["email"] = "error" + error_message.append( + _("Invalid Email! Please enter a valid email address.") + ) + return error, error_message + + def _get_position_profile_fields(self): + '''Provides all the fields that must fill the structure's position profile of the user. + All of them MUST start with "position_".''' + fields = [ + "function", + "phone", + "email", + "edit_structure_profiles", + ] + return fields + + def _get_position_boolean_fields(self): + '''Provides the fields for which we must check the presence + in form's kw to know the value to save in the partner field.''' + fields = ["edit_structure_profiles"] + return fields + + def _transform_fields(self, kw, profile_fields): + '''Transforms kw's values in res_partner fields and values''' + return {key: kw[key] for key in profile_fields if key in kw} + + def _get_page_saving_position_values(self, kw): + profile_fields = self._get_position_profile_fields() + values = self._transform_fields(kw, profile_fields) + # Boolean fields are not returned in "kw" if their value in the form is False. + # Then we have to check their presence to determine which value to save in the partner. + boolean_fields = self._get_position_boolean_fields() + for key in boolean_fields: + values.update( + { + key: kw.get(key, "off") == "on" + } + ) + return values + + @http.route( + ["/my/position/", "/my/position/save"], + type="http", + auth="user", + website=True, + ) + def portal_my_position( + self,position_id=None, access_token=None, redirect=None, **kw + ): + # The following condition is to transform profile_id to an int, as it is sent as a string from the templace "portal_my_profile" + # TODO: find a better way to retrieve the profile_id at form submit step + if not isinstance(position_id, int): + position_id = int(position_id) + + # Check that the user has the right to see this profile + try: + position_sudo = self._document_check_access( + "res.partner", position_id, access_token + ) + except (AccessError, MissingError): + return request.redirect("/my/positions") + + position_profile = request.env["res.partner"].browse(position_id) + + values = self._position_get_page_view_values(position_sudo, access_token, **kw) + values.update( + { + "error": {}, + "error_message": [], + } + ) + if kw and request.httprequest.method == "POST": + # the user has clicked in the Save button to save new data + error, error_message = self._details_position_form_validate(kw, position_id) + values.update({"error": error, "error_message": error_message}) + values.update(kw) + if not error: + # Update position profile + new_values = self._get_page_saving_position_values(kw) + position_profile.sudo().write(new_values) + # End of updates + if redirect: + return request.redirect(redirect) + return request.redirect("/my/positions") + + # This is just the form page opening. We send all the data needed for the form fields + values.update( + { + "position_id": position_id, # Sent in order to retrieve it at submit time + "position": position_profile, + "redirect": "/my/position/" + str(position_id) + "?success=True" + } + ) + return request.render("partner_profiles_portal.portal_position", values) diff --git a/partner_profiles_portal/controllers/portal_structure_profile.py b/partner_profiles_portal/controllers/portal_structure_profile.py new file mode 100644 index 0000000..bd6eded --- /dev/null +++ b/partner_profiles_portal/controllers/portal_structure_profile.py @@ -0,0 +1,234 @@ +# Copyright 2020 Lokavaluto () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import base64 +from odoo import http, tools, _ +from odoo.exceptions import AccessError, MissingError +from odoo.http import request +from odoo.addons.portal.controllers.portal import CustomerPortal + + +class CustomerPortalStructureProfile(CustomerPortal): + + def _structure_get_page_view_values(self, structure, access_token, **kwargs): + values = { + "page_name": "structure", + "structure": structure, + } + return self._get_page_view_values( + structure, access_token, values, "my_structures_history", False, **kwargs + ) + + def _details_structure_form_validate(self, data, structure_id): + error = dict() + error_message = [] + # public name uniqueness + if data.get("public_name") and request.env["res.partner"].sudo().search( + [ + ("name", "=", data.get("public_name")), + ("is_public_profile", "=", True), + ("contact_id", "!=", structure_id), + ] + ): + error["public_name"] = "error" + error_message.append( + _("This public name is already used, please find an other idea.") + ) + + # email validation + if data.get("email") and not tools.single_email_re.match(data.get("email")): + error["email"] = "error" + error_message.append( + _("Invalid Email! Please enter a valid email address.") + ) + return error, error_message + + def _get_main_profile_fields(self): + '''Provides all the fields that must fill the structure's main profile. + All of them MUST start with "main_".''' + fields = [ + "main_name", + "main_street", + "main_street2", + "main_zip", + "main_city", + "main_country_id", + "main_phone", + "main_mobile", + "main_email", + "main_website", + ] + return fields + + def _get_main_boolean_structure_fields(self): + '''Provides the fields for which we must check the presence + in form's kw to know the value to save in the partner field. + All of them MUST start with "main_".''' + fields = [] + return fields + + def _get_public_profile_fields(self): + '''Provides all the fields that must fill the structure's public profile. + All of them MUST start with "public_".''' + fields = [ + "public_name", + "public_street2", + "public_street", + "public_zip", + "public_city", + "public_phone", + "public_mobile", + "public_email", + "public_website", + ] + return fields + + def _get_public_boolean_structure_fields(self): + '''Provides the fields for which we must check the presence + in form's kw to know the value to save in the partner field. + All of them MUST start with "public_".''' + fields = [] + return fields + + def _get_position_profile_fields(self): + '''Provides all the fields that must fill the structure's position profile of the user. + All of them MUST start with "position_".''' + fields = [ + "position_function", + "position_phone", + "position_email", + ] + return fields + + def _get_position_boolean_structure_fields(self): + '''Provides the fields for which we must check the presence + in form's kw to know the value to save in the partner field. + All of them MUST start with "position_".''' + fields = [] + return fields + + def _transform_in_res_partner_fields(self, kw, profile_fields, prefix=""): + '''Transforms kw's values in res_partner fields and values''' + return {key[len(prefix):]: kw[key] for key in profile_fields if key in kw} + + def _add_boolean_values(self, values, kw, boolean_fields, prefix=""): + for key in boolean_fields: + values.update( + { + key[len(prefix):]: kw.get(key, "off") == "on", + } + ) + return values + + def _get_page_saving_main_structure_values(self, kw): + profile_fields = self._get_main_profile_fields() + values = self._transform_in_res_partner_fields(kw, profile_fields, "main_") + boolean_fields = self._get_main_boolean_structure_fields() + values = self._add_boolean_values(values, kw, boolean_fields, "main_") + if 'logo' in kw: + image = kw.get('logo') + if image: + image = image.read() + image = base64.b64encode(image) + values.update({ + 'image_1920': image + }) + return values + + def _get_page_saving_public_structure_values(self, kw): + profile_fields = self._get_public_profile_fields() + values = self._transform_in_res_partner_fields(kw, profile_fields, "public_") + boolean_fields = self._get_public_boolean_structure_fields() + values = self._add_boolean_values(values, kw, boolean_fields, "public_") + return values + + def _get_page_saving_position_structure_values(self, kw): + profile_fields = self._get_position_profile_fields() + values = self._transform_in_res_partner_fields(kw, profile_fields, "position_") + boolean_fields = self._get_position_boolean_structure_fields() + values = self._add_boolean_values(values, kw, boolean_fields, "position_") + return values + + def _get_page_opening_values(self): + # Just retrieve the values to display for Selection fields + countries = request.env["res.country"].sudo().search([]) + values = { + "countries": countries, + } + return values + + @http.route( + ["/my/structure/", "/my/structure/save"], + type="http", + auth="user", + website=True, + ) + def portal_my_structure( + self,structure_id=None, access_token=None, redirect=None, **kw + ): + # The following condition is to transform profile_id to an int, as it is sent as a string from the templace "portal_my_profile" + # TODO: find a better way to retrieve the profile_id at form submit step + if not isinstance(structure_id, int): + structure_id = int(structure_id) + + # Check that the user has the right to see this profile + try: + structure_sudo = self._document_check_access( + "res.partner", structure_id, access_token + ) + except (AccessError, MissingError): + return request.redirect("/my/structures") + + Partner = request.env["res.partner"] + partner_id = request.env.user.partner_id + main_profile = Partner.browse(structure_id) + public_profile = Partner.browse(main_profile.public_profile_id.id) + position_profile = Partner.search( + [ + ("parent_id", "=", structure_id), + ("contact_id", "=", partner_id.id), + ("is_position_profile", "=", True), + ("active", "=", True) + ], + limit=1 + )[0] + + values = self._structure_get_page_view_values(structure_sudo, access_token, **kw) + values.update( + { + "error": {}, + "error_message": [], + } + ) + if kw and request.httprequest.method == "POST": + # the user has clicked in the Save button to save new data + error, error_message = self._details_structure_form_validate(kw, structure_id) + values.update({"error": error, "error_message": error_message}) + values.update(kw) + if not error: + # Update main profile + new_values = self._get_page_saving_main_structure_values(kw) + main_profile.sudo().write(new_values) + # Update public profile + new_values = self._get_page_saving_public_structure_values(kw) + public_profile.sudo().write(new_values) + # Update position profile + new_values = self._get_page_saving_position_structure_values(kw) + position_profile.sudo().write(new_values) + # End of updates + if redirect: + return request.redirect(redirect) + return request.redirect("/my/structures") + + # This is just the form page opening. We send all the data needed for the form fields + can_edit_structure = partner_id in main_profile.can_edit_structure_profiles_ids + values.update(self._get_page_opening_values()) + values.update( + { + "structure_id": structure_id, # Sent in order to retrieve it at submit time + "public_profile": public_profile, + "position_profile": position_profile, + "can_edit_structure": can_edit_structure, + "redirect": "/my/structure/" + str(structure_id) + "?success=True" + } + ) + return request.render("partner_profiles_portal.portal_structure", values) diff --git a/partner_profiles_portal/i18n/README b/partner_profiles_portal/i18n/README new file mode 100644 index 0000000..62197a1 --- /dev/null +++ b/partner_profiles_portal/i18n/README @@ -0,0 +1 @@ +This directory should contain the *.po for Odoo translation. diff --git a/partner_profiles_portal/i18n/fr.po b/partner_profiles_portal/i18n/fr.po new file mode 100644 index 0000000..f381891 --- /dev/null +++ b/partner_profiles_portal/i18n/fr.po @@ -0,0 +1,458 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * partner_profiles_portal +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-06-13 14:02+0000\n" +"PO-Revision-Date: 2023-06-13 14:02+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: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_position +msgid "'s position in the structure" +msgstr " - Fonction dans la structure" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_position +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid " Data saved!" +msgstr " Données enregistrées!" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "Current logo/picture:" +msgstr "Logo/image actuel :" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Current logo:" +msgstr "Logo actuel :" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "New logo/picture:" +msgstr "Nouveau logo/image :" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "New logo:" +msgstr "Nouveau logo :" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "- Modification causes log out. Sign in just after!" +msgstr "- La modification entraîne une déconnexion. Reconnectez-vous ensuite !" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_positions +msgid "Function" +msgstr "Fonction occupée" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_positions +msgid "Person name" +msgstr "Nom" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_structures +msgid "Structure name" +msgstr "Nom de la structure" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_position +msgid " Back to my positions list" +msgstr " Retour à la liste des fonctions" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid " Back to my structures list" +msgstr " Retour à la liste de mes structures" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_position +msgid "Access rights" +msgstr "Droits d'accès" + +#. module: partner_profiles_portal +#: model:ir.model.fields,field_description:partner_profiles_portal.field_res_partner__odoo_user_id +#: model:ir.model.fields,field_description:partner_profiles_portal.field_res_users__odoo_user_id +msgid "Associated Odoo user" +msgstr "Utilisateur Odoo associé" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.partner_profiles_form_view +msgid "Associated User" +msgstr "Utilisateur associé" + +#. module: partner_profiles_portal +#: model:ir.model.fields,field_description:partner_profiles_portal.field_res_partner__can_edit_structure_profiles_ids +#: model:ir.model.fields,field_description:partner_profiles_portal.field_res_users__can_edit_structure_profiles_ids +msgid "Can edit struture profiles" +msgstr "Peut éditer les profils de la structure" + +#. module: partner_profiles_portal +#: model:ir.model.fields,field_description:partner_profiles_portal.field_res_partner__child_main_contact_ids +#: model:ir.model.fields,field_description:partner_profiles_portal.field_res_users__child_main_contact_ids +msgid "Can read structure profiles" +msgstr "Peut lire les profiles de la structure" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "City" +msgstr "Ville" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "City:" +msgstr "Ville :" + +#. module: partner_profiles_portal +#: code:addons/partner_profiles_portal/controllers/portal_my_positions.py:40 +#: code:addons/partner_profiles_portal/controllers/portal_my_profiles.py:51 +#: code:addons/partner_profiles_portal/controllers/portal_my_structures.py:40 +#, python-format +msgid "Company" +msgstr "" + +#. module: partner_profiles_portal +#: model:ir.model,name:partner_profiles_portal.model_res_partner +msgid "Contact" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Contact information" +msgstr "Information de contact" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "Country" +msgstr "Pays" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Country..." +msgstr "Pays..." + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Country:" +msgstr "Pays :" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "Email" +msgstr "Courriel" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_position +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Email Pro:" +msgstr "Courriel pro :" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Email:" +msgstr "Courriel :" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_position +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Function:" +msgstr "Fonction occupée :" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "Image" +msgstr "" + +#. module: partner_profiles_portal +#: code:addons/partner_profiles_portal/controllers/portal_my_account.py:115 +#: code:addons/partner_profiles_portal/controllers/portal_partner_profile.py:40 +#: code:addons/partner_profiles_portal/controllers/portal_position_profile.py:28 +#: code:addons/partner_profiles_portal/controllers/portal_structure_profile.py:41 +#, python-format +msgid "Invalid Email! Please enter a valid email address." +msgstr "L'email n'est pas valide, merci de renseigner un email valide !" + +#. module: partner_profiles_portal +#: code:addons/partner_profiles_portal/controllers/portal_my_account.py:118 +#, python-format +msgid "Invalid Public Email! Please enter a valid public email address." +msgstr "L'email public n'est pas valide, merci de renseigner un email valide !" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Logo:" +msgstr "Logo :" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_position +msgid "Manage\n" +" the structure's profiles" +msgstr "Gère les profils de la structure" + +#. module: partner_profiles_portal +#: model:ir.model.fields,field_description:partner_profiles_portal.field_create_position_profile__edit_structure_profiles +#: model:ir.model.fields,field_description:partner_profiles_portal.field_res_partner__edit_structure_profiles +#: model:ir.model.fields,field_description:partner_profiles_portal.field_res_users__edit_structure_profiles +msgid "Manage structure's profiles" +msgstr "Gère les profiles de la structure" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "Mobile" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Mobile:" +msgstr "Mobile :" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_position +msgid "My Position Details" +msgstr "Détail de la fonction" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_positions +msgid "My Positions" +msgstr "Mon équipe" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "My Structure Details" +msgstr "Informations de ma structure" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_structures +msgid "My Structures" +msgstr "Mes Structures" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_home_profile_menu +msgid "My information" +msgstr "Mes informations" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_home_profile_menu +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_positions +msgid "My positions" +msgstr "Mon équipe" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_home_profile_menu +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_structures +msgid "My structures" +msgstr "Mes structures" + +#. module: partner_profiles_portal +#: code:addons/partner_profiles_portal/controllers/portal_my_positions.py:39 +#: code:addons/partner_profiles_portal/controllers/portal_my_profiles.py:49 +#: code:addons/partner_profiles_portal/controllers/portal_my_structures.py:39 +#, python-format +msgid "Name" +msgstr "Nom" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "Name /\n" +" Nickname" +msgstr "Nom /\n" +" Surnom" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Name:" +msgstr "Nom :" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "Phone" +msgstr "Téléphone" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_position +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Phone Pro:" +msgstr "Téléphone pro :" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Phone:" +msgstr "Téléphone :" + +#. module: partner_profiles_portal +#: code:addons/partner_profiles_portal/controllers/portal_my_profiles.py:50 +#, python-format +msgid "Profile Type" +msgstr "Type de profil" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Public contact information" +msgstr "Information publiques de contact" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "Public information" +msgstr "Informations publiques" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Public name:" +msgstr "Nom public :" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_position +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Save" +msgstr "Sauvegarder" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "State /\n" +" Province" +msgstr "État /\n" +" Province" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Street" +msgstr "Rue" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Street 2" +msgstr "Rue (complément)" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Street:" +msgstr "Rue :" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "The following information are public information that might be\n" +" used in tierce applications (annuary for instance).
You\n" +" can customize them (and be anonymous for instance) to publicly\n" +" show whatever you need or want." +msgstr "Les informations suivantes sont des informations publiques qui peuvent être\n" +" visibles dans des applications tierces (annuaires par exemple).
Vous\n" +" pouvez les personnaliser (et vous rendre anonyme par exemple) pour montrer\n" +" publiquement ce que vous souhaitez ou avez besoin." + +#. module: partner_profiles_portal +#: code:addons/partner_profiles_portal/controllers/portal_partner_profile.py:33 +#, python-format +msgid "This nickname is already used, please find an other idea." +msgstr "The Pseudo est déjà utilisé, merci d'en trouver un autre." + +#. module: partner_profiles_portal +#: code:addons/partner_profiles_portal/controllers/portal_my_account.py:130 +#: code:addons/partner_profiles_portal/controllers/portal_structure_profile.py:34 +#, python-format +msgid "This public name is already used, please find an other idea." +msgstr "The nom public est déjà utilisé, merci d'en trouver un autre." + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "Website" +msgstr "Site Web" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Website:" +msgstr "Site Web :" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_structures +msgid "You are not linked with any structure." +msgstr "Vous n'êtes lié à aucune structure." + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_positions +msgid "You do not have any positions in your structure." +msgstr "Vous n'avez pas de fonctions occupées dans votre structure." + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_home_profile_menu +msgid "Your Details" +msgstr "Vos détails" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "Structure's name" +msgstr "Nom de la structure" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "Firstname and Lastname" +msgstr "Prénom et Nom" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Your position in the structure" +msgstr "Votre fonction dans la structure" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Zip /\n" +" Postal Code:" +msgstr "ZIP /\n" +" Code postal :" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "Zip / Postal\n" +" Code" +msgstr "ZIP / Code\n" +" Postal" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Zip / Postal\n" +" Code:" +msgstr "Zip / Code\n" +" Postal :" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "Zip / Postal Code" +msgstr "Zip / Code postal" + +#. module: partner_profiles_portal +#: model:ir.model,name:partner_profiles_portal.model_create_position_profile +msgid "create Position Profile" +msgstr "créer une fiche fonction" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_position +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "details" +msgstr "détails" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "e.g. https://odoo.com" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "select..." +msgstr "sélectionner..." + diff --git a/partner_profiles_portal/i18n/partner_profiles_portal.pot b/partner_profiles_portal/i18n/partner_profiles_portal.pot new file mode 100644 index 0000000..89b7c2f --- /dev/null +++ b/partner_profiles_portal/i18n/partner_profiles_portal.pot @@ -0,0 +1,462 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * partner_profiles_portal +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-06-13 13:59+0000\n" +"PO-Revision-Date: 2023-06-13 13:59+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: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_position +msgid "'s position in the structure" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_position +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid " Data saved!" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "Current logo/picture:" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Current logo:" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "New logo/picture:" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "New logo:" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "- Modification causes log out. Sign in just after!" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_positions +msgid "Function" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_positions +msgid "Person name" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_structures +msgid "Structure name" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_position +msgid " Back to my positions list" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid " Back to my structures list" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_position +msgid "Access rights" +msgstr "" + +#. module: partner_profiles_portal +#: model:ir.model.fields,field_description:partner_profiles_portal.field_res_partner__odoo_user_id +#: model:ir.model.fields,field_description:partner_profiles_portal.field_res_users__odoo_user_id +msgid "Associated Odoo user" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.partner_profiles_form_view +msgid "Associated User" +msgstr "" + +#. module: partner_profiles_portal +#: model:ir.model.fields,field_description:partner_profiles_portal.field_res_partner__can_edit_structure_profiles_ids +#: model:ir.model.fields,field_description:partner_profiles_portal.field_res_users__can_edit_structure_profiles_ids +msgid "Can edit struture profiles" +msgstr "" + +#. module: partner_profiles_portal +#: model:ir.model.fields,field_description:partner_profiles_portal.field_res_partner__child_main_contact_ids +#: model:ir.model.fields,field_description:partner_profiles_portal.field_res_users__child_main_contact_ids +msgid "Can read structure profiles" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "City" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "City:" +msgstr "" + +#. module: partner_profiles_portal +#: code:addons/partner_profiles_portal/controllers/portal_my_positions.py:40 +#: code:addons/partner_profiles_portal/controllers/portal_my_profiles.py:51 +#: code:addons/partner_profiles_portal/controllers/portal_my_structures.py:40 +#, python-format +msgid "Company" +msgstr "" + +#. module: partner_profiles_portal +#: model:ir.model,name:partner_profiles_portal.model_res_partner +msgid "Contact" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Contact information" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "Country" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Country..." +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Country:" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "Email" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_position +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Email Pro:" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Email:" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_position +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Function:" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "Image" +msgstr "" + +#. module: partner_profiles_portal +#: code:addons/partner_profiles_portal/controllers/portal_my_account.py:115 +#: code:addons/partner_profiles_portal/controllers/portal_partner_profile.py:40 +#: code:addons/partner_profiles_portal/controllers/portal_position_profile.py:28 +#: code:addons/partner_profiles_portal/controllers/portal_structure_profile.py:41 +#, python-format +msgid "Invalid Email! Please enter a valid email address." +msgstr "" + +#. module: partner_profiles_portal +#: code:addons/partner_profiles_portal/controllers/portal_my_account.py:118 +#, python-format +msgid "Invalid Public Email! Please enter a valid public email address." +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Logo:" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_position +msgid "Manage\n" +" the structure's profiles" +msgstr "" + +#. module: partner_profiles_portal +#: code:addons/partner_profiles_portal/models/res_partner.py:14 +#, python-format +msgid "Manage structure's main profile" +msgstr "" + +#. module: partner_profiles_portal +#: model:ir.model.fields,field_description:partner_profiles_portal.field_create_position_profile__edit_structure_profiles +#: model:ir.model.fields,field_description:partner_profiles_portal.field_res_partner__edit_structure_profiles +#: model:ir.model.fields,field_description:partner_profiles_portal.field_res_users__edit_structure_profiles +msgid "Manage structure's profiles" +msgstr "" + +#. module: partner_profiles_portal +#: code:addons/partner_profiles_portal/models/res_partner.py:17 +#, python-format +msgid "Manage structure's public profile" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "Mobile" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Mobile:" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_position +msgid "My Position Details" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_positions +msgid "My Positions" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "My Structure Details" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_structures +msgid "My Structures" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_home_profile_menu +msgid "My information" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_home_profile_menu +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_positions +msgid "My positions" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_home_profile_menu +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_structures +msgid "My structures" +msgstr "" + +#. module: partner_profiles_portal +#: code:addons/partner_profiles_portal/controllers/portal_my_positions.py:39 +#: code:addons/partner_profiles_portal/controllers/portal_my_profiles.py:49 +#: code:addons/partner_profiles_portal/controllers/portal_my_structures.py:39 +#, python-format +msgid "Name" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "Name /\n" +" Nickname" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Name:" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "Phone" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_position +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Phone Pro:" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Phone:" +msgstr "" + +#. module: partner_profiles_portal +#: code:addons/partner_profiles_portal/controllers/portal_my_profiles.py:50 +#, python-format +msgid "Profile Type" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Public contact information" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "Public information" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Public name:" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_position +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Save" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "State /\n" +" Province" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Street" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Street 2" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Street:" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "The following information are public information that might be\n" +" used in tierce applications (annuary for instance).
You\n" +" can customize them (and be anonymous for instance) to publicly\n" +" show whatever you need or want." +msgstr "" + +#. module: partner_profiles_portal +#: code:addons/partner_profiles_portal/controllers/portal_partner_profile.py:33 +#, python-format +msgid "This nickname is already used, please find an other idea." +msgstr "" + +#. module: partner_profiles_portal +#: code:addons/partner_profiles_portal/controllers/portal_my_account.py:130 +#: code:addons/partner_profiles_portal/controllers/portal_structure_profile.py:34 +#, python-format +msgid "This public name is already used, please find an other idea." +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "Website" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Website:" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_structures +msgid "You are not linked with any structure." +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_positions +msgid "You do not have any positions in your structure." +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_home_profile_menu +msgid "Your Details" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "Structure's name" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "Firstname and Lastname" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Your position in the structure" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Zip /\n" +" Postal Code:" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "Zip / Postal\n" +" Code" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "Zip / Postal\n" +" Code:" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "Zip / Postal Code" +msgstr "" + +#. module: partner_profiles_portal +#: model:ir.model,name:partner_profiles_portal.model_create_position_profile +msgid "create Position Profile" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_position +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "details" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_structure +msgid "e.g. https://odoo.com" +msgstr "" + +#. module: partner_profiles_portal +#: model_terms:ir.ui.view,arch_db:partner_profiles_portal.portal_my_details_profiles +msgid "select..." +msgstr "" + diff --git a/partner_profiles_portal/models/__init__.py b/partner_profiles_portal/models/__init__.py new file mode 100644 index 0000000..4255189 --- /dev/null +++ b/partner_profiles_portal/models/__init__.py @@ -0,0 +1,2 @@ + +from . import res_partner \ No newline at end of file diff --git a/partner_profiles_portal/models/res_partner.py b/partner_profiles_portal/models/res_partner.py new file mode 100644 index 0000000..e63b5c9 --- /dev/null +++ b/partner_profiles_portal/models/res_partner.py @@ -0,0 +1,59 @@ +# Copyright 2022 Elabore (https://elabore.coop) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import logging +from odoo import _, api, fields, models + +_logger = logging.getLogger(__name__) + + +class res_partner(models.Model): + _inherit = "res.partner" + + edit_structure_profiles = fields.Boolean( + string="Manage structure's profiles" + ) + can_edit_structure_profiles_ids = fields.Many2many( + "res.partner", + relation="res_partner_main_profile_rel", + column1="partner_id", + column2="profile_id", + store=True, + compute="_compute_can_read_edit", + string="Can edit struture profiles", + ) + child_main_contact_ids = fields.Many2many( + "res.partner", + relation="res_partner_child_contacts_rel", + column1="partner_id", + column2="profile_id", + store=True, + compute="_compute_can_read_edit", + string="Can read structure profiles", + ) + odoo_user_id = fields.Many2one( + "res.users", + compute="_compute_odoo_user_id", + string="Associated Odoo user", + store=True, + ) + + @api.depends( + "other_contact_ids", + "other_contact_ids.edit_structure_profiles", + "structure_position_ids", + "structure_position_ids.edit_structure_profiles", + ) + def _compute_can_read_edit(self): + for partner in self: + partner.can_edit_structure_profiles_ids = partner.structure_position_ids.filtered( + "edit_structure_profiles" + ).mapped("contact_id") + partner.child_main_contact_ids = partner.structure_position_ids.mapped("contact_id") + + @api.depends("user_ids") + def _compute_odoo_user_id(self): + for partner in self: + partner.odoo_user_id = self.env["res.users"].search( + [("partner_id", "=", partner.id)], limit=1 + ) \ No newline at end of file diff --git a/partner_profiles_portal/security/members_security.xml b/partner_profiles_portal/security/members_security.xml new file mode 100644 index 0000000..a7a14f3 --- /dev/null +++ b/partner_profiles_portal/security/members_security.xml @@ -0,0 +1,28 @@ + + + + res_partner: portal: read access on my structures + + ['|','|',('contact_id', '=', user.partner_id.id), + ('child_main_contact_ids', 'in', [user.partner_id.id]), + ('contact_id.child_main_contact_ids', 'in', [user.partner_id.id])] + + + + + + + + + res_partner: portal: write access on my structures + + ['|','|',('contact_id', '=', user.partner_id.id), + ('can_edit_structure_profiles_ids', 'in', [user.partner_id.id]), + ('contact_id.can_edit_structure_profiles_ids', 'in', [user.partner_id.id])] + + + + + + + \ No newline at end of file diff --git a/partner_profiles_portal/views/portal_home_template.xml b/partner_profiles_portal/views/portal_home_template.xml new file mode 100644 index 0000000..15bf0ae --- /dev/null +++ b/partner_profiles_portal/views/portal_home_template.xml @@ -0,0 +1,39 @@ + + + + \ No newline at end of file diff --git a/partner_profiles_portal/views/portal_my_account.xml b/partner_profiles_portal/views/portal_my_account.xml new file mode 100644 index 0000000..92f619f --- /dev/null +++ b/partner_profiles_portal/views/portal_my_account.xml @@ -0,0 +1,243 @@ + + + + \ No newline at end of file diff --git a/partner_profiles_portal/views/portal_my_positions_template.xml b/partner_profiles_portal/views/portal_my_positions_template.xml new file mode 100644 index 0000000..7c145a5 --- /dev/null +++ b/partner_profiles_portal/views/portal_my_positions_template.xml @@ -0,0 +1,44 @@ + + + + \ No newline at end of file diff --git a/partner_profiles_portal/views/portal_my_structures_template.xml b/partner_profiles_portal/views/portal_my_structures_template.xml new file mode 100644 index 0000000..f99c0cd --- /dev/null +++ b/partner_profiles_portal/views/portal_my_structures_template.xml @@ -0,0 +1,38 @@ + + + + \ No newline at end of file diff --git a/partner_profiles_portal/views/portal_partner_position_template.xml b/partner_profiles_portal/views/portal_partner_position_template.xml new file mode 100644 index 0000000..5f4205d --- /dev/null +++ b/partner_profiles_portal/views/portal_partner_position_template.xml @@ -0,0 +1,94 @@ + + + + \ No newline at end of file diff --git a/partner_profiles_portal/views/portal_partner_structure_template.xml b/partner_profiles_portal/views/portal_partner_structure_template.xml new file mode 100644 index 0000000..a89dbcc --- /dev/null +++ b/partner_profiles_portal/views/portal_partner_structure_template.xml @@ -0,0 +1,318 @@ + + + + \ No newline at end of file diff --git a/partner_profiles_portal/views/res_partner_view.xml b/partner_profiles_portal/views/res_partner_view.xml new file mode 100644 index 0000000..b0c3a95 --- /dev/null +++ b/partner_profiles_portal/views/res_partner_view.xml @@ -0,0 +1,25 @@ + + + + + Partner Profiles Form View + res.partner + + 99 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/partner_profiles_portal/wizard/__init__.py b/partner_profiles_portal/wizard/__init__.py new file mode 100644 index 0000000..7f376a3 --- /dev/null +++ b/partner_profiles_portal/wizard/__init__.py @@ -0,0 +1,2 @@ + +from . import create_position_profile \ No newline at end of file diff --git a/partner_profiles_portal/wizard/create_position_profile.py b/partner_profiles_portal/wizard/create_position_profile.py new file mode 100644 index 0000000..447d497 --- /dev/null +++ b/partner_profiles_portal/wizard/create_position_profile.py @@ -0,0 +1,20 @@ +# Copyright 2022 Elabore (https://elabore.coop) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + +class CreatePositionProfile(models.TransientModel): + _inherit = "create.position.profile" + + edit_structure_profiles = fields.Boolean( + string="Manage structure's profiles" + ) + + def _compute_position_profile_values(self): + values = super(CreatePositionProfile, self)._compute_position_profile_values() + values.update( + { + "edit_structure_profiles": self.edit_structure_profiles + } + ) + return values \ No newline at end of file diff --git a/partner_profiles_portal/wizard/create_position_profile.xml b/partner_profiles_portal/wizard/create_position_profile.xml new file mode 100644 index 0000000..a0fa0a1 --- /dev/null +++ b/partner_profiles_portal/wizard/create_position_profile.xml @@ -0,0 +1,13 @@ + + + + create.position.wizard.view.form.inherit + create.position.profile + + + + + + + + \ No newline at end of file