diff --git a/partner_profiles_portal/__manifest__.py b/partner_profiles_portal/__manifest__.py index 57b1583..1842494 100644 --- a/partner_profiles_portal/__manifest__.py +++ b/partner_profiles_portal/__manifest__.py @@ -3,7 +3,7 @@ { "name": "partner_profiles_portal", - "version": "12.0.2.0.0", + "version": "12.0.2.1.0", "author": "Elabore", "website": "https://elabore.coop", "maintainer": "Stéphan Sainléger", @@ -26,8 +26,8 @@ "data": [ "security/members_security.xml", "views/portal_home_template.xml", - "views/portal_my_profiles_template.xml", - "views/portal_partner_profile_template.xml", + "views/portal_my_structures_template.xml", + "views/portal_partner_structure_template.xml", "views/portal_my_account.xml", "views/res_partner_view.xml", ], diff --git a/partner_profiles_portal/controllers/__init__.py b/partner_profiles_portal/controllers/__init__.py index 9866964..dad4392 100644 --- a/partner_profiles_portal/controllers/__init__.py +++ b/partner_profiles_portal/controllers/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from . import portal_my_profiles -from . import portal_partner_profile +from . import portal_my_structures +from . import portal_structure_profile from . import portal_my_account \ No newline at end of file diff --git a/partner_profiles_portal/controllers/portal_my_profiles.py b/partner_profiles_portal/controllers/portal_my_structures.py similarity index 50% rename from partner_profiles_portal/controllers/portal_my_profiles.py rename to partner_profiles_portal/controllers/portal_my_structures.py index 5a7cd65..b6b4897 100644 --- a/partner_profiles_portal/controllers/portal_my_profiles.py +++ b/partner_profiles_portal/controllers/portal_my_structures.py @@ -5,49 +5,38 @@ from odoo.http import request from odoo.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager -class CustomerPortalMyProfiles(CustomerPortal): +class CustomerPortalMyStructures(CustomerPortal): - def _get_domain_my_profiles(self, user): + def _get_domain_my_structures(self, user): if user.partner_id.other_contact_ids: - main_profile_ids = user.partner_id.other_contact_ids.filtered( - "edit_structure_main_profile" - ).mapped("parent_id") - public_profile_ids = user.partner_id.other_contact_ids.filtered( - "edit_structure_public_profile" - ).mapped("parent_id.public_profile_id") - return [ - "|", - "|", - ("contact_id", "=", user.partner_id.id), - ("id", "in", main_profile_ids.ids), - ("id", "in", public_profile_ids.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 [("contact_id", "=", user.partner_id.id)] + return None def _prepare_portal_layout_values(self): - values = super(CustomerPortalMyProfiles, self)._prepare_portal_layout_values() - values["profile_count"] = request.env["res.partner"].search_count( - self._get_domain_my_profiles(request.env.user) - ) + 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/profiles", "/my/profiles/page/"], + ["/my/structures", "/my/structures/page/"], type="http", auth="user", website=True, ) - def portal_my_profiles( + def portal_my_structures( self, page=1, date_begin=None, date_end=None, sortby=None, **kw ): values = self._prepare_portal_layout_values() - profile = request.env["res.partner"] - domain = self._get_domain_my_profiles(request.env.user) + structure = request.env["res.partner"] + domain = self._get_domain_my_structures(request.env.user) searchbar_sortings = { "name": {"label": _("Name"), "order": "name"}, - "partner_profile": {"label": _("Profile Type"), "order": "partner_profile"}, "parent_id": {"label": _("Company"), "order": "parent_id"}, } if not sortby: @@ -57,35 +46,35 @@ class CustomerPortalMyProfiles(CustomerPortal): # archive groups - Default Group By 'create_date' archive_groups = self._get_archive_groups("res.partner", domain) - # profiles count - profile_count = profile.search_count(domain) + # structures count + structure_count = structure.search_count(domain) if domain else 0 # pager pager = portal_pager( - url="/my/profiles", + url="/my/structures", url_args={"sortby": sortby}, - total=profile_count, + total=structure_count, page=page, step=self._items_per_page, ) # content according to pager and archive selected - profiles = profile.search( + structures = structure.search( domain, order=order, limit=self._items_per_page, offset=pager["offset"], - ) - request.session["my_profiles_history"] = profiles.ids[:100] + ) if domain else None + request.session["my_structures_history"] = structures.ids[:100] if structures else None values.update( { - "profiles": profiles, - "page_name": "profile", + "structures": structures, + "page_name": "structure", "archive_groups": archive_groups, - "default_url": "/my/profiles", + "default_url": "/my/structures", "pager": pager, "searchbar_sortings": searchbar_sortings, "sortby": sortby, } ) - return request.render("partner_profiles_portal.portal_my_profiles", values) \ No newline at end of file + return request.render("partner_profiles_portal.portal_my_structures", values) \ No newline at end of file diff --git a/partner_profiles_portal/controllers/portal_partner_profile.py b/partner_profiles_portal/controllers/portal_partner_profile.py deleted file mode 100644 index 74ffa57..0000000 --- a/partner_profiles_portal/controllers/portal_partner_profile.py +++ /dev/null @@ -1,130 +0,0 @@ -# Copyright 2020 Lokavaluto () -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -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 CustomerPortalPartnerProfile(CustomerPortal): - - def _profile_get_page_view_values(self, profile, access_token, **kwargs): - values = { - "page_name": "profile", - "profile": profile, - } - return self._get_page_view_values( - profile, access_token, values, "my_profiles_history", False, **kwargs - ) - - def _details_profile_form_validate(self, data, profile_id): - error = dict() - error_message = [] - # nickname uniqueness - if data.get("nickname") and request.env["res.partner"].sudo().search( - [ - ("name", "=", data.get("nickname")), - ("partner_profile.ref", "=", "partner_profile_public"), - ("id", "!=", profile_id), - ] - ): - error["nickname"] = "error" - error_message.append( - _("This nickname 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_profile_fields(self): - fields = [ - "nickname", - "function", - "phone", - "mobile", - "email", - "website_url", - "street", - "street2", - "city", - "country_id", - "zipcode", - ] - return fields - - def _get_page_saving_values(self, profile, kw): - profile_fields = self._get_profile_fields() - values = {key: kw[key] for key in profile_fields if key in kw} - values.update( - { - "name": values.pop("nickname", profile.name), - "zip": values.pop("zipcode", ""), - "website": values.pop("website_url", ""), - } - ) - 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/profile/", "/my/profile/save"], - type="http", - auth="user", - website=True, - ) - def portal_my_profile( - self, profile_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(profile_id, int): - profile_id = int(profile_id) - - # Check that the user has the right to see this profile - try: - profile_sudo = self._document_check_access( - "res.partner", profile_id, access_token - ) - except (AccessError, MissingError): - return request.redirect("/my/profiles") - - values = self._profile_get_page_view_values(profile_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_profile_form_validate(kw, profile_id) - values.update({"error": error, "error_message": error_message}) - values.update(kw) - if not error: - profile = request.env["res.partner"].browse(profile_id) - values = self._get_page_saving_values(profile, kw) - profile.sudo().write(values) - if redirect: - return request.redirect(redirect) - return request.redirect("/my/profiles") - - # This is just the form page opening. We send all the data needed for the form fields - values.update(self._get_page_opening_values()) - values.update( - { - "profile_id": profile_id, # Sent in order to retrieve it at submit time - "redirect": redirect - } - ) - return request.render("partner_profiles_portal.portal_my_profile", 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..8f5324f --- /dev/null +++ b/partner_profiles_portal/controllers/portal_structure_profile.py @@ -0,0 +1,198 @@ +# 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_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_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 _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 _get_page_saving_main_values(self, kw): + profile_fields = self._get_main_profile_fields() + values = self._transform_in_res_partner_fields(kw, profile_fields, "main_") + if 'logo' in kw: + image = kw.get('logo') + if image: + image = image.read() + image = base64.b64encode(image) + values.update({ + 'image': image + }) + return values + + def _get_page_saving_public_values(self, kw): + profile_fields = self._get_public_profile_fields() + values = self._transform_in_res_partner_fields(kw, profile_fields, "public_") + return values + + def _get_page_saving_position_values(self, kw): + profile_fields = self._get_position_profile_fields() + values = self._transform_in_res_partner_fields(kw, profile_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_values(kw) + main_profile.sudo().write(new_values) + # Update public profile + new_values = self._get_page_saving_public_values(kw) + public_profile.sudo().write(new_values) + # 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/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/models/res_partner.py b/partner_profiles_portal/models/res_partner.py index 3eb9777..7173cc9 100644 --- a/partner_profiles_portal/models/res_partner.py +++ b/partner_profiles_portal/models/res_partner.py @@ -10,44 +10,37 @@ _logger = logging.getLogger(__name__) class res_partner(models.Model): _inherit = "res.partner" - edit_structure_main_profile = fields.Boolean( - string=_("Manage structure's main profile") + edit_structure_profiles = fields.Boolean( + string="Manage structure's profiles" ) - edit_structure_public_profile = fields.Boolean( - string=_("Manage structure's public profile") - ) - can_edit_main_profile_ids = fields.Many2many( + 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_edit", - string="Can edit main profile", + compute="_compute_can_read_edit", + string="Can edit struture profiles", ) - can_edit_public_profile_ids = fields.Many2many( + child_main_contact_ids = fields.Many2many( "res.partner", - relation="res_partner_public_profile_rel", + relation="res_partner_child_contacts_rel", column1="partner_id", column2="profile_id", store=True, - compute="_compute_can_edit", - string="Can edit public profile", + compute="_compute_can_read_edit", + string="Can read structure profiles", ) @api.depends( "other_contact_ids", - "other_contact_ids.edit_structure_main_profile", - "other_contact_ids.edit_structure_public_profile", + "other_contact_ids.edit_structure_profiles", "child_ids", - "child_ids.edit_structure_main_profile", - "child_ids.edit_structure_public_profile", + "child_ids.edit_structure_profiles", ) - def _compute_can_edit(self): + def _compute_can_read_edit(self): for partner in self: - partner.can_edit_main_profile_ids = partner.child_ids.filtered( - "edit_structure_main_profile" + partner.can_edit_structure_profiles_ids = partner.child_ids.filtered( + "edit_structure_profiles" ).mapped("contact_id") - partner.can_edit_public_profile_ids = partner.child_ids.filtered( - "edit_structure_public_profile" - ).mapped("contact_id") \ No newline at end of file + partner.child_main_contact_ids = partner.child_ids.mapped("contact_id") \ No newline at end of file diff --git a/partner_profiles_portal/security/members_security.xml b/partner_profiles_portal/security/members_security.xml index a7267cf..a7a14f3 100644 --- a/partner_profiles_portal/security/members_security.xml +++ b/partner_profiles_portal/security/members_security.xml @@ -1,11 +1,24 @@ - - res_partner: portal: read/write access on my profiles + + res_partner: portal: read access on my structures ['|','|',('contact_id', '=', user.partner_id.id), - ('can_edit_main_profile_ids', 'in', [user.partner_id.id]), - ('contact_id.can_edit_public_profile_ids', 'in', [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])] diff --git a/partner_profiles_portal/views/portal_home_template.xml b/partner_profiles_portal/views/portal_home_template.xml index 84e09bc..a37c3fd 100644 --- a/partner_profiles_portal/views/portal_home_template.xml +++ b/partner_profiles_portal/views/portal_home_template.xml @@ -14,9 +14,10 @@ My information - +
+
diff --git a/partner_profiles_portal/views/portal_my_profiles_template.xml b/partner_profiles_portal/views/portal_my_profiles_template.xml deleted file mode 100644 index cc6f84f..0000000 --- a/partner_profiles_portal/views/portal_my_profiles_template.xml +++ /dev/null @@ -1,97 +0,0 @@ - - - - \ 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_profile_template.xml b/partner_profiles_portal/views/portal_partner_profile_template.xml deleted file mode 100644 index 298136d..0000000 --- a/partner_profiles_portal/views/portal_partner_profile_template.xml +++ /dev/null @@ -1,189 +0,0 @@ - - - - \ 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..9ff5fcc --- /dev/null +++ b/partner_profiles_portal/views/portal_partner_structure_template.xml @@ -0,0 +1,316 @@ + + + + \ 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 index 0904fc1..51c98f0 100644 --- a/partner_profiles_portal/views/res_partner_view.xml +++ b/partner_profiles_portal/views/res_partner_view.xml @@ -9,22 +9,26 @@ - - - + + - - - + + - - - + +