Search code examples
javascriptxmlodoo-16

Portal widget not working for public users


In Odoo v16 Template form, I have a selection field “Activity Type” that contains records from backend (like Many2many-tags widget but now in Portal), will be chosen by the user, and the user can choose more than 1 record.

Depending on the chosen records, new 3 fields for each Activity type (divs) will appear and become mandatory.

This code is functioning flawlessly when logged in as a portal user. However, when a public user accesses this form, the field titled “Activity Type” appears, allowing me to select multiple records. However, the sections (divs) that are required to appear do not appear.

    <div class="col-md-12"
     style="padding-top: 20px !important; padding-bottom: 20px !important;">
    <label class="form-label" for="activity_type_ids">Activity Type</label>
    <span
            style="color: red;"> *
    </span>
    <select class="form-control js-example-basic-multiple col-xl-6 mb-1 new-get_data"
            multiple="multiple" name="activity_type_ids" required="1"
            id="activity_type_ids">
        <t t-foreach="activity_ids" t-as="activity">
            <option t-att-value="activity" t-esc="activity.name"/>
        </t>
    </select>
    <input type="hidden" id="hidden_activity_ids" name="hidden_activity_ids"/>
    <input type="hidden" id="hidden_activity_codes"
           name="hidden_activity_codes"/>
</div>

    <!-- Div for Construction Fields -->

    <div class="col-md-6 activity-section" id="construction_fields"
     style="padding-top: 20px !important; padding-bottom: 20px !important;">
    <label class="form-label">Construction CR Number</label>
    <input type="text" class="form-control" name="construction_cr_number"
           placeholder="Construction CR Number"/>

    <label class="form-label">Construction CR</label>
    <span style="color: red;">
        *
    </span>
    <input type="file" class="form-control" name="construction_cr"
           accept="application/pdf"/>

    <label class="form-label">Construction Activity Register</label>
    <input type="file" class="form-control"
           name="construction_activity_register" accept="application/pdf"/>
</div>

      <!-- Div for Maintenance and Operation Fields -->
      <div class="col-md-6 activity-section" id="maintenance_fields"
         style="padding-top: 20px !important; padding-bottom: 20px !important;">
        <label class="form-label">Maintenance CR Number</label>
        <span
                style="color: red;"> *
        </span>
        <input type="text" class="form-control" name="maintenance_cr_number"
               placeholder="Maintenance CR Number"/>

        <label class="form-label">Maintenance CR</label>
        <span style="color: red;">
            *
        </span>
        <input type="file" class="form-control" name="maintenance_cr"
               accept="application/pdf"/>

        <label class="form-label">Maintenance Activity Register</label>
        <span
                style="color: red;"> *
        </span>
        <input type="file" class="form-control" name="maintenance_activity_register"
               accept="application/pdf"/>
    </div>

    <!-- ETC... -->

The related JS (which is not working for public users) is:

    odoo.define('custom_field.Many2many_tag', function (require) {
    var PublicWidget = require('web.public.widget');
    var rpc = require('web.rpc');
    const { _lt } = require('web.core');

    // Used in new_supplier_registration_request template
    var NewData = PublicWidget.Widget.extend({
        selector: '.new-get_data',
        start: function () {
            this._super.apply(this, arguments);

            // Initialize the select2 widget
            $('.js-example-basic-multiple').select2({
                placeholder: _lt('Select Activities'),
                allowClear: true,
            });

            // Hide all activity sections initially on page load
            $('.activity-section').hide();

            // Add an event listener to capture selected values
            $('#activity_type_ids').on('change', function () {
                // Get the selected values
                var selectedValues = $(this).val();

                // Parse the IDs from the selected values as integers
                var selectedIds = selectedValues.map(function (value) {
                    return parseInt(value.match(/\d+/)[0], 10);
                });

                // Perform an RPC call to get the activity codes based on selected IDs
                console.log("Before RPC");
                rpc.query({
                    model: 'supplier.registration.activity',
                    method: 'read',
                    args: [selectedIds, ['code']],
                }).then(function (records) {
                    console.log("in RPC records >>>", records);
                    var codes = records.map(function (record) {
                        return record.code;
                    });

                    // Clear all activity sections initially
                    $('.activity-section').hide();
                    $('input').removeAttr('required'); // Remove required attribute from all inputs

                    // Iterate over codes to show the relevant divs and set required attributes
                    codes.forEach(function (code) {
                        switch (code) {
                            case 'construction':
                                $('#construction_fields').show(); // Show construction fields
                                $('#construction_fields input').attr('required', true); // Set required attribute for all inputs in the div
                                break;
                            case 'maintenance':
                                $('#maintenance_fields').show();
                                $('#maintenance_fields input').attr('required', true);
                                break;
                            case 'education':
                                $('#education_fields').show();
                                $('#education_fields input').attr('required', true);
                                break;
                            case 'food_supplies':
                                $('#food_supplies_fields').show();
                                $('#food_supplies_fields input').attr('required', true);
                                break;
                            case 'equipment_supplies':
                                $('#equipment_supplies_fields').show();
                                $('#equipment_supplies_fields input').attr('required', true);
                                break;
                            case 'medical_equipment':
                                $('#medical_equipment_fields').show();
                                $('#medical_equipment_fields input').attr('required', true);
                                break;
                            case 'event_organization':
                                $('#event_organization_fields').show();
                                $('#event_organization_fields input').attr('required', true);
                                break;
                            default:
                                console.log("No matching activity found for code:", code);
                        }
                    });

                    // Set the hidden input field with the retrieved activity codes
                    $('#hidden_activity_codes').val(codes.join(','));

                    // Also set the hidden input with the selected IDs
                    $('#hidden_activity_ids').val(selectedIds.join(','));
                });
            });
        },
    });

    PublicWidget.registry.Many2many_tag = NewData;
    return NewData;
    });

In the console I got

Before RPC

Nothing else, means it's not executing the RPC when the user is public!! When I tried to catch the error I got

"Traceback (most recent call last):\n  File \"/home/dzeuf/Odoo/
Wamy/Odoo16/odoo/http.py\", line 1636, in _serve_db\n    return 
service_model.retrying(self._serve_ir_http, self.env)\n  File \"/
home/dzeuf/Odoo/Odoo16/odoo/service/model.py\", line 133, in 
retrying\n    result = func()\n  File \"/home/dzeuf/Odoo/Wamy/
Odoo16/odoo/http.py\", line 1661, in _serve_ir_http\n    
ir_http._authenticate(rule.endpoint)\n  /
Odoo16/odoo/addons/base/models/ir_http.py\", line 127, in 
_authenticate\n    getattr(cls, f'_auth_method_{auth}')()\n  File 
\"/home/dzeuf/Odoo/Odoo16/odoo/addons/base/models/ir_http.py\", 
106, in _auth_method_user\n    raise 
http.SessionExpiredException(\"Session 
expired\")\nodoo.http.SessionExpiredException: Session expired\n"
message
: 
"Session expired"

This is my controller

@route('/my/new/supplier_registration_request', type='http', 
auth='public', csrf=False, save_session=False, website=True)
def new_supplier_registration_request_portal(self):
    country_ids = request.env['res.country'].sudo().search([])
    state_ids =   
  request.env['res.country.state'].sudo().search([])
    job_ids = request.env['hr.job'].sudo().search([])
    bank_id = request.env['res.bank'].sudo().search([])
    enterprise_manager_id = 
request.env['res.partner'].sudo().search([])
    nationality_id = request.env['res.country'].sudo().search([])
    lang_ids = request.env['res.lang'].sudo().search([('active',
 'in', (True, False))])
    activity_ids =     
request.env['supplier.registration.activity'].sudo().search([])
    values = ({
        'country_ids': country_ids,
        'state_ids': state_ids,
        'job_ids': job_ids,
        'bank_id': bank_id,
        'enterprise_manager_id': enterprise_manager_id,
        'nationality_id': nationality_id,
        'lang_ids': lang_ids,
        'activity_ids': activity_ids,
        'page_name': 'new_supplier_registration_request',

    })
    return 
request.render("wamy_supplier_portal.new_supplier_registration_reques
    t", values)

And I have :

def _prepare_portal_layout_values(self):
    values = super(SupplierRegistrationRequestPortal, 
self)._prepare_portal_layout_values()
    if request.env.user:
        user = request.env.user
    else:
        user = request.env['res.users'].browse(1)
    values['user'] = user
    return values

in the Security CSV file I have

id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink

access_supplier_registration_activity_user,supplier.registration.activity,model_supplier_registration_activity,,1,1,1,1

Finally my template

<template id="new_supplier_registration_request">
    <t t-call="portal.portal_layout">
        <t t-set="previous_section">/supplier_portal</t>
        <t t-set="form_add" t-value="1"/>
        <t t-set="no_breadcrumbs" t-value="1"/>
        <t t-set="breadcrumbs_searchbar" t-value="False"/>
        <div class="new_supplier_registration_request text-bold">
            <form data-model_name="supplier.registration.request" role="role"
                  id="supplier_registration_request_form" action="/create/supplier_registration_request"
                  method="POST"
                  enctype="multipart/form-data" data-success-mode="redirect"
                  data-success-page="/contactus-thank-you">
                <input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>

Solution

  • The fact that your code is fine for user=portal but not for user=pubic indicates that the problem is due to access rights on model: 'supplier.registration.activity'...

    Could you check the rpc answer using console.log(records) after your then(function (records) { ?

    In your CSV security file, Try to add access right read on the model 'supplier.registration.activity' for the base.group_public using Settings menu > access rights:

    id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
    
    access_supplier_registration_activity_userpublic,supplier.registration.activity,model_supplier_registration_activity,base.group_public,1,0,0,0
    

    ANOTHER WAY is to use a custom method (def) in your rpc (instead of read):

    rpc.query({
                        model: 'supplier.registration.activity',
                        method: 'read_selected',
                        args: [selectedIds, ['code']],
                    })
    
    
    def read_selected(self, selectedids,code):
    
        self.sudo().search_read(...)