diff --git a/setup/website_contact_lastname/odoo/addons/website_contact_lastname b/setup/website_contact_lastname/odoo/addons/website_contact_lastname new file mode 120000 index 0000000000..c444f62428 --- /dev/null +++ b/setup/website_contact_lastname/odoo/addons/website_contact_lastname @@ -0,0 +1 @@ +../../../../website_contact_lastname \ No newline at end of file diff --git a/setup/website_contact_lastname/setup.py b/setup/website_contact_lastname/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/website_contact_lastname/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/website_contact_lastname/__init__.py b/website_contact_lastname/__init__.py new file mode 100644 index 0000000000..ea6bb1f80b --- /dev/null +++ b/website_contact_lastname/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2023 Manuel Regidor +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import controllers +from . import models diff --git a/website_contact_lastname/__manifest__.py b/website_contact_lastname/__manifest__.py new file mode 100644 index 0000000000..054b7b0825 --- /dev/null +++ b/website_contact_lastname/__manifest__.py @@ -0,0 +1,23 @@ +# Copyright 2023 Manuel Regidor +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + "name": "Website Contact Lastname", + "version": "15.0.1.0.0", + "category": "Website", + "website": "https://github.com/OCA/website", + "author": "Sygel Technology," "Odoo Community Association (OCA)", + "license": "AGPL-3", + "application": False, + "installable": True, + "depends": [ + "auth_signup", + "website_account_fiscal_position_partner_type", + "partner_firstname" + ], + "data": [ + "views/templates.xml", + "views/auth_signup_login_templates.xml", + "views/portal_templates.xml", + ], +} diff --git a/website_contact_lastname/controllers/__init__.py b/website_contact_lastname/controllers/__init__.py new file mode 100644 index 0000000000..294141a404 --- /dev/null +++ b/website_contact_lastname/controllers/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2023 Manuel Regidor +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import main +from . import portal diff --git a/website_contact_lastname/controllers/main.py b/website_contact_lastname/controllers/main.py new file mode 100644 index 0000000000..53538a704d --- /dev/null +++ b/website_contact_lastname/controllers/main.py @@ -0,0 +1,137 @@ +# Copyright 2023 Manuel Regidor +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import _ +from odoo.http import request + +from odoo.addons.website_sale.controllers.main import WebsiteSale +from odoo.addons.auth_signup.controllers.main import AuthSignupHome + + +class WebsiteSale(WebsiteSale): + + def _get_mandatory_fields_shipping(self, country_id=False): + req = super()._get_mandatory_fields_shipping(country_id) + req += ['firstname'] + if 'name' in req: + req.remove('name') + return req + + def _get_mandatory_fields_billing(self, country_id=False): + req = super()._get_mandatory_fields_billing(country_id) + req.append('firstname') + if request.env.context.get('fiscal_position_type') == 'b2c': + req.append('lastname') + # Name is removed as this field cannot be explicitly edited + # It is edited in the backend through the fields first name + # and lastname + if 'name' in req: + req.remove('name') + return req + + def values_postprocess(self, order, mode, values, errors, error_msg): + new_values, errors, error_msg = super( + WebsiteSale, self + ).values_postprocess( + order=order, + mode=mode, + values=values, + errors=errors, + error_msg=error_msg + ) + new_values.update({ + "firstname": values.get("firstname") or '', + "lastname": values.get("lastname") or '', + }) + # Name is removed as this field cannot be explicitly edited + # It is edited in the backend through the fields first name + # and lastname + new_values.pop("name") + return new_values, errors, error_msg + + def checkout_form_validate(self, mode, all_form_values, data): + required_fields = [f for f in ( + all_form_values.get('field_required') or '' + ).split(',') if f] + # Name is removed as this field cannot be explicitly edited + # It is edited in the backend through the fields first name + # and lastname + if 'name' in required_fields: + required_fields.remove('name') + all_form_values['field_required'] = ','.join(required_fields) + error, error_message = super().checkout_form_validate( + mode, all_form_values, data + ) + if data.get('partner_id'): + partner_su = request.env['res.partner'].sudo().browse( + int(data['partner_id']) + ).exists() + can_edit_vat = partner_su.parent_id.can_edit_vat() if partner_su.parent_id else partner_su.can_edit_vat() + firstname_change = partner_su and 'firstname' in data \ + and data['firstname'] != partner_su.firstname + if firstname_change and not can_edit_vat: + error['firstname'] = 'error' + error_message.append(_( + "Changing your name is not allowed once invoices have been" + " issued for your account. Please contact us directly for " + "this operation." + )) + # When lastname field in form is empty, its values is False + # When lastname field in backend is empty, its values is '' + # In that case, if both are compared, they are considered to be + # different so data['lastname'] != partner_su.lastname would + # return True although no real change would have been applied. + lastname_change = partner_su and 'lastname' in data and \ + data['lastname'] != partner_su.lastname and ( + data['lastname'] or partner_su.lastname not in ['', False] + ) + if lastname_change and not can_edit_vat: + error['lastname'] = 'error' + error_message.append(_( + "Changing your last name is not allowed once invoices have" + " been issued for your account. Please contact us directly" + " for this operation." + )) + + # Prevent change the partner name, lastname if + # it is an internal user. + if ( + firstname_change or lastname_change + ) and not all(partner_su.user_ids.mapped('share')): + error.update({ + 'firstname': 'error' if firstname_change else None, + 'lastname': 'error' if lastname_change else None, + }) + error_message.append(_( + "If you are ordering for an external person, please place " + "your order via the backend. If you wish to change your " + "name or last name, please do so in the account settings " + "or contact your administrator." + )) + return error, error_message + + +class AuthSignupHome(AuthSignupHome): + + def get_auth_signup_qcontext(self): + qcontext = super().get_auth_signup_qcontext() + qcontext.update({k: v for (k, v) in request.params.items() if k in { + 'lastname', + }}) + if 'name' in qcontext: + qcontext['firstname'] = qcontext['name'] + if 'error' not in qcontext and \ + request.httprequest.method == 'POST' and \ + qcontext.get( + 'fiscal_position_type' + ) == 'b2c' and not qcontext.get('lastname'): + qcontext["error"] = _("Lastname is required for B2C users.") + return qcontext + + def _prepare_signup_values(self, qcontext): + values = super()._prepare_signup_values(qcontext) + if 'firstname' in qcontext: + values['firstname'] = qcontext['firstname'] + if 'lastname' in qcontext: + values['lastname'] = qcontext['lastname'] + return values diff --git a/website_contact_lastname/controllers/portal.py b/website_contact_lastname/controllers/portal.py new file mode 100644 index 0000000000..325bd3df2b --- /dev/null +++ b/website_contact_lastname/controllers/portal.py @@ -0,0 +1,55 @@ +# Copyright 2023 Manuel Regidor +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import _ +from odoo.http import request +from odoo.addons.portal.controllers.portal import CustomerPortal + + +class CustomerPortal(CustomerPortal): + + CustomerPortal.OPTIONAL_BILLING_FIELDS += [ + 'firstname', + 'lastname', + ] + + def details_form_validate(self, data): + context = dict(request.env.context) + # 'name' field cannont be updated as it would recompute fields + # firstname and lastname. That is why it is set in context + # that changes in field name need to be ignored. + context.update({"name_ignore": True}) + request.env.context = context + error, error_message = super().details_form_validate(data) + partner = request.env.user.partner_id + if partner.can_edit_vat(): + if 'firstname' in data and not data.get('Name'): + error["firstname"] = 'error' + error_message.append(_('Firstname is mandatory.')) + if 'lastname' in data and not data.get('lastname') and(( + data.get('fiscal_position_type') == 'b2c' + ) or ( + partner.fiscal_position_type == 'b2c' and not data.get( + 'fiscal_position_type' + ) == 'b2b' + )): + error["lastname"] = 'error' + error["fiscal_position_type"] = 'error' + error_message.append(_('Lastname is mandatory for B2C users.')) + else: + if 'firstname' in data and \ + data.get('firstname') != partner.firstname: + error["firstname"] = 'error' + error_message.append(_( + 'Changing Name is not allowed once document(s) have been ' + 'issued for your account. Please contact us directly for ' + 'this operation.' + )) + if 'lastname' in data and data.get('lastname') != partner.lastname: + error["lastname"] = 'error' + error_message.append(_( + 'Changing Lastname is not allowed once document(s) have ' + 'been issued for your account. Please contact us directly ' + 'for this operation.' + )) + return error, error_message diff --git a/website_contact_lastname/models/__init__.py b/website_contact_lastname/models/__init__.py new file mode 100644 index 0000000000..a227d7676d --- /dev/null +++ b/website_contact_lastname/models/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2023 Manuel Regidor +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import res_users +from . import res_partner diff --git a/website_contact_lastname/models/res_partner.py b/website_contact_lastname/models/res_partner.py new file mode 100644 index 0000000000..9161e6ec3e --- /dev/null +++ b/website_contact_lastname/models/res_partner.py @@ -0,0 +1,24 @@ +# Copyright 2023 Manuel Regidor +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl-3). + +from odoo import models, api +from odoo.http import request + + +class Partner(models.Model): + _inherit = "res.partner" + + @api.model + def signup_retrieve_info(self, token): + res = super().signup_retrieve_info(token) + partner = self._signup_retrieve_partner(token, raise_exception=True) + res.update({ + 'name': partner.firstname, + 'lastname': partner.lastname, + }) + return res + + def write(self, vals): + if vals.get('name') and request.env.context.get('name_ignore'): + vals.pop('name') + return super().write(vals) diff --git a/website_contact_lastname/models/res_users.py b/website_contact_lastname/models/res_users.py new file mode 100644 index 0000000000..7b6e5f7104 --- /dev/null +++ b/website_contact_lastname/models/res_users.py @@ -0,0 +1,37 @@ +# Copyright 2023 Manuel Regidor +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl-3). + +from odoo import models, api + + +class ResUsers(models.Model): + _inherit = "res.users" + + @api.model + def signup(self, values, token=None): + if token: + partner = self.env['res.partner']._signup_retrieve_partner( + token, + check_validity=True, + raise_exception=True + ) + partner_user = partner.user_ids and partner.user_ids[0] or False + # Don't update firstname and lastname if partner + # related to user exists (i.e. when resetting password) + if partner_user: + values.pop('firstname', None) + values.pop('lastname', None) + return super().signup(values, token) + + def _create_user_from_template(self, values): + user = super()._create_user_from_template(values) + # Because of the way the name is computed, it is important to + # reset the values so the final name is correct. + # It cannot be done before (the way would be setting the value of name + # if param. values to '') as the funcion _create_user_from_template + # checks that values contains a value for 'name' + user.write({ + 'firstname': values.get('firstname'), + 'lastname': values.get('lastname'), + }) + return user diff --git a/website_contact_lastname/readme/CONFIGURE.rst b/website_contact_lastname/readme/CONFIGURE.rst new file mode 100644 index 0000000000..05dcf8709a --- /dev/null +++ b/website_contact_lastname/readme/CONFIGURE.rst @@ -0,0 +1 @@ +No configuration needed. diff --git a/website_contact_lastname/readme/CONTRIBUTORS.rst b/website_contact_lastname/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..16fbee9816 --- /dev/null +++ b/website_contact_lastname/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* `Sygel `_: + + * Manuel Regidor \ No newline at end of file diff --git a/website_contact_lastname/readme/DESCRIPTION.rst b/website_contact_lastname/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..f77181f0df --- /dev/null +++ b/website_contact_lastname/readme/DESCRIPTION.rst @@ -0,0 +1,2 @@ +This module allows users to introduce and edit their lastname +from the portal. diff --git a/website_contact_lastname/readme/USAGE.rst b/website_contact_lastname/readme/USAGE.rst new file mode 100644 index 0000000000..ebb8b7fb10 --- /dev/null +++ b/website_contact_lastname/readme/USAGE.rst @@ -0,0 +1,2 @@ +Lastname field is mandatory for B2C users in portal. +It can be left blank when the user is B2B. diff --git a/website_contact_lastname/static/description/icon.png b/website_contact_lastname/static/description/icon.png new file mode 100644 index 0000000000..207fb7ad55 Binary files /dev/null and b/website_contact_lastname/static/description/icon.png differ diff --git a/website_contact_lastname/views/auth_signup_login_templates.xml b/website_contact_lastname/views/auth_signup_login_templates.xml new file mode 100644 index 0000000000..b18a11461e --- /dev/null +++ b/website_contact_lastname/views/auth_signup_login_templates.xml @@ -0,0 +1,20 @@ + + + + + diff --git a/website_contact_lastname/views/portal_templates.xml b/website_contact_lastname/views/portal_templates.xml new file mode 100644 index 0000000000..32828353a5 --- /dev/null +++ b/website_contact_lastname/views/portal_templates.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/website_contact_lastname/views/templates.xml b/website_contact_lastname/views/templates.xml new file mode 100644 index 0000000000..082c0a597b --- /dev/null +++ b/website_contact_lastname/views/templates.xml @@ -0,0 +1,27 @@ + + + + +