Thursday, October 19, 2023

Remove xml entities from html field value in odoo

In this blog we will explain how to remove xml entities from html field value in odoo and to make it clear let us give an example 

We created new task in the project module , and this task has description as in the image bellow 

 

and we created custom task portal view , but when we try to view the task description it displayed with some tags and we need to remove them .. just as in the image bellow
 

so to fix this bug we can add new function inside the controller and this function take the description field value as an input and return the clean text of the description without tags , so the function can be as bellow 

def clean_text(self,html_field_value):
        clean = re.compile(r'<[^>]+>')
        clean_text =  re.sub(clean, '', html_field_value)
        return clean_text

#Note : we must import re package first as "import re"

website form in odoo

In this blog we will explain how to create website form in odoo website to create new record in internal odoo model , so let us say that we want to create website form to in order to create new products with the major fields {name, price,cost, type,image} from website , so ..

First :  create new template in which we will create the form with all inputs as bellow

<?xml version="1.0" encoding="UTF-8"?>
<odoo>
    <template id="new_products" name="New Products">
        <t t-call="website.layout">
            <form action='/create/product/form'>
                <input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
                <div class="row">
                    <div class="col-sm-4 col-md-3">
                        <table style="width:100%">
                            <tr style="width:100%">
                                <td style="width:20%">
                                    <b>
                                        Name
                                    </b>
                                </td>
                                <td style="width:80%">
                                    <input type="text" name="product_name" id="product_name" required="true" />
                                </td>
                            </tr>
                        </table>
                    </div>
                    <div class="col-sm-4 col-md-3">
                        <table style="width:100%">
                            <tr style="width:100%">
                                <td style="width:20%">
                                    <b>
                                        Price
                                    </b>
                                </td>
                                <td style="width:80%">
                                    <input type="number" name="product_price" id="product_price" required="true"/>
                                </td>
                            </tr>
                        </table>
                    </div>
                    <div class="col-sm-4 col-md-3">
                        <table style="width:100%">
                            <tr style="width:100%">
                                <td style="width:20%">
                                    <b>
                                        Cost
                                    </b>
                                </td>
                                <td style="width:80%">
                                    <input type="number" name="product_cost" id="product_cost" required="true"/>
                                </td>
                            </tr>
                        </table>
                    </div>
                    <div class="col-sm-4 col-md-3">
                        <table style="width:100%">
                            <tr style="width:100%">
                                <td style="width:20%">
                                    <b>
                                        Type
                                    </b>
                                </td>
                                <td style="width:80%">
                                    <select id="product_type" required="True" class="form-control" name="product_type">
                                        <option t-att-value="consu" class="type_option">
                                            Consumable
                                        </option>
                                        <option t-att-value="service" class="type_option">
                                            Service
                                        </option> 
                                        <option t-att-value="product" class="type_option">
                                            Storable Product
                                        </option>      
                                    </select>
                                </td>
                            </tr>
                        </table>
                    </div>
                    <div class="col-sm-4 col-md-3">
                        <table style="width:100%">
                            <tr style="width:100%">
                                <td style="width:20%">
                                    <b>
                                        Image
                                    </b>
                                </td>
                                <td style="width:80%">
                                    <input type="file" class="form-control s_website_form_input" name="image_file" id="image_file" required="true" accept="image/*"/>
                                </td>
                            </tr>
                        </table>
                    </div>
                </div>
                <br></br>
                <div class="control_buttons">
                    <button type='submit' class="submit_bnt" id="s_bnt">
                        Save
                    </button>
                </div>
            </form>
    </template>
</odoo>


Second : create python controller in which you must "import base64" & "from odoo.exceptions import AccessError, MissingError", then add function to create the product with the data posted from the form as bellow 

    @http.route(['/create/product/form/'], type='http', methods=['GET', 'POST'], auth="public", website=True)
    def create_new_product(self, **post):
        product_obj = request.env['product.template'].sudo()
        try:
            product_data = {
                'name':post['product_name'],
                'list_price':float(post['product_price']),
                'standard_price':float(post['product_cost']),
                'type':post['product_type'],
                'datas':base64.b64encode(post['image_file'].read()),
            }
            product_id = product_obj.create(product_data)
            return request.redirect(product_has_been_created_page)
        except (AccessError, MissingError):
            return request.redirect('/my')


#Note : You have to create product_has_been_created_page first

Tuesday, October 17, 2023

odoo mail configuration

In this blog we will explain hot to setup odoo mail configuration

Create & use app passwords

Important: To create an app password, you need 2-Step Verification on your Google Account.

If you use 2-Step-Verification and get a "password incorrect" error when you sign in, you can try to use an app password.

  1. Go to your Google Account.
  2. Select Security.
  3. Under "Signing in to Google," select 2-Step Verification.
  4. At the bottom of the page, select App passwords.
  5. Enter a name that helps you remember where you’ll use the app password.
  6. Select Generate.
  7. To enter the app password, follow the instructions on your screen. The app password is the 16-character code that generates on your device.
  8. Select Done.

Thursday, October 12, 2023

Auto Images Slider in Odoo

 

In this blog we will explain how to create auto image slider in odoo website as in the image above , so

First : we have to add the images to the website page , for example let us say we have an odoo controller which call page and pass data to this page , and that data contains "image_ids" which is contains all image data , so we can add the images to the page as bellow 

 

<t t-foreach="image_ids" t-as="image">
    <center>
        <div class="mySlides">
            <img t-att-src="image['image_url']" class="img_01"/>
            <br></br>
            <span t-esc="image['image_name']" class="name_01"/>
        </div>
        <span class="dot"></span>
    </center>
</t>


Second : we have to create the JS script which will add the auto move to the slider and we can add it as bellow

 

<script>
    let slideIndex = 0;
    showSlides();

    function showSlides() {
      let i;
      let slides = document.getElementsByClassName("mySlides");
      let imgs = document.getElementsByClassName("img_01");
      let img_urls = document.getElementsByClassName("image_url");
      let dots = document.getElementsByClassName("dot");
      for (i = 0; i &lt; slides.length; i++) {
        slides[i].style.display = "none";
        slides[i].style.margin = "10px";
      }
      for (i = 0; i &lt; imgs.length; i++) {
        imgs[i].style.width = "80%";
        imgs[i].style.height = "470px";
        imgs[i].style.border = "2px solid black";
        imgs[i].style.borderColor = "#875A7B";
        imgs[i].style.borderRadius = "10px";
        imgs[i].style.boxShadow = "3px 5px #bfbfbf";
        imgs[i].style.marginBottom = "10px";
      }
      slideIndex++;
      if (slideIndex > slides.length) {slideIndex = 1}    
      for (i = 0; i &lt; dots.length; i++) {
        dots[i].className = dots[i].className.replace(" active", "");
      }
      slides[slideIndex-1].style.display = "block";  
      dots[slideIndex-1].className += " active";
      setTimeout(showSlides, 5000); // Change image every 5 seconds
    }
</script>


Third : adding the CSS style as bellow 

 

.img_01{
    width:80%;
    height:420px;
    transition: .1s ease;
  backface-visibility: hidden;
}
.img_01:hover {
    opacity: 0.3;
}

odoo module icons

In this blog we will explain how we can create icon for custom module as odoo standard module , so

First : we have to go to "Icon builder for odoo" website by open this link 


Second : click the internet icon to open the second website which is provides many icons , we choose an icon, then copy it's name and back to the first website 


Third : we put the copied icon class name in the "Icon Class" field, then choose the color and the other style setting for the icon , lastly click on download button to download the icon as png image




website images preview in odoo

 

In This blog we will explain how to create website images preview in odoo as popup zoom out view just as in the image above , so

First : we have to create the the images view for example 

<t t-foreach="image_ids" t-as="image">
    <img t-att-src="image.attachment_url" class="asset_img_01" alt="Asset"/>
    <div id="myModal" class="modal">
        <span class="close">
            <i class="fa fa-window-close" style="background-color:#ffffff;color:#ff4000;"/>
        </span>
        <img class="modal-content" id="img01"/>
        <div id="caption"></div>
    </div>
</t>

Second : add JS script to add the popup preview for each image which is work when we click on the image , as bellow 

<script>
    var modal = document.getElementById("myModal");

    var imgs = document.getElementsByClassName("asset_img_01");
    var modalImg = document.getElementById("img01");
    var captionText = document.getElementById("caption");
    for (i = 0; i &lt; imgs.length; i++) {
        imgs[i].onclick = function(){
            modal.style.display = "block";
            modalImg.src = this.src;
            captionText.innerHTML = this.alt;
        }
    }

    var span = document.getElementsByClassName("close")[0];

    span.onclick = function() {
      modal.style.display = "none";
    }
</script>


# note : the script must be inside the for loop body after the "myModal div"

Thursday, September 28, 2023

Table rows scroll in odoo

 

In this blog we will explain how to create table rows scroll in odoo web view so 

First : create the table as bellow
    <table class="tb_00">
        <tr class="tr_00">
            <th class="td_001">
                  <b class="th_01">
                       Date
                  </b>
            </th>
            <th class="td_002">
                  <b class="th_01">
                       Name
                  </b>
            </th>
            <th class="td_003">
                  <b class="th_01">
                        Manager
                  </b>
            </th>
            <th class="td_004">
                   <b class="th_01">
                         Description
                   </b>
            </th>
         </tr>
   </table>
   <br></br>
        <div class="scroll">
        <table class="tb_01">
            <t t-foreach="all_model_main_data" t-as="model">
                <tr class="tr_02">
                    <td class="td_01">
                        <span t-esc="model['model_date']" class="model_date_01"/>
                    </td>
                    <td class="td_02">
                        <t t-set="allowed" t-value="0"/>
                        <t t-if="request.env.user.has_group('canv_models_website.model_details_puser_r_per')">
                            <t t-set="allowed" t-value="1"/>
                        </t>
                        <t t-if="request.env.user.has_group('canv_models_website.model_details_puser_rw_per')">
                            <t t-set="allowed" t-value="1"/>
                        </t>
                        <t t-if="allowed == 1">
                            <a t-att-href="model['model_id'].get_portal_url()">
                                <span t-esc="model['model_name']" class="model_name_02"/>
                            </a>
                        </t>
                        <t t-if="allowed == 0">
                            <span t-esc="model['model_name']" class="model_name_02"/>
                        </t>
                    </td>
                    <td class="td_03">
                        <span t-esc="model['supervisor_name']" class="auct_visor_name_02"/>
                    </td>
                    <td class="td_04">
                        <span t-esc="model['description']" class="model_desc_02"/>
                    </td>
                </tr>
            </t>
        </table>
    </div>

Second : ass the css styles as bellow 

    .tb_00{
      width:100%;
    }
    .tb_01{
      width:100%;
    }
    .scroll{
      max-height: 500px;
      overflow: auto;
      margin-bottom:30px;
    }
    .tr_00{
      width: 100%;
      font-size: 17px;
    }
    .td_001{
      width: 15%;
      font-family: Amiri-Regular;
      font-size: 17px;
    }
    .td_002{
      width: 20%;
      font-family: Amiri-Regular;
      font-size: 17px;
    }
    .td_003{
      width: 20%;
      font-family: Amiri-Regular;
      font-size: 17px;
    }
    .td_004{
      width: 45%;
      font-family: Amiri-Regular;
      font-size: 17px;
    }
    .tr_01{
      width: 100%;
      font-size: 17px;
    }
    .td_01{
      width: 15%;
      font-family: Amiri-Regular;
      font-size: 17px;
    }
    .td_02{
      width: 20%;
      font-family: Amiri-Regular;
      font-size: 17px;
    }
    .td_03{
      width: 20%;
      font-family: Amiri-Regular;
      font-size: 17px;
    }
    .td_04{
      width: 45%;
      font-family: Amiri-Regular;
      font-size: 17px;
    }
    .th_01{
      font-family: Amiri-Regular;
      font-size: 20px;
    }
    .model_desc_02{
      font-family: Amiri-Regular;
      font-size: 17px;
    }
    .tr_02{
      margin-top: 5px;
      width: 100%;
    }

 


Thursday, September 14, 2023

portal view for model in odoo

In this blog we will define the steps to create portal view for model in odoo

So

Step 1 : inherit "portal.mixin" model inside your model as bellow 

    class ClassName(models.Model):
        _name = "model.name"
        _inherit = ['model.name','portal.mixin']
        //for old models

    class ClassName(models.Model):
        _name = "model.name"
        _inherit = ['model.name','portal.mixin']
        //for new models

Step 2 : set value for access_url field inside your model by inheriting the get_portal_url() function as bellow 

    def _compute_access_url(self):
        super(productTemplate, self)._compute_access_url()
        for rec in self:
            rec.access_url = '/model/name/%s' % rec.id

Step 3 : add another function to define the portal url for each record from your model as bellow

    def get_portal_url(self, suffix=None, report_type=None, download=None, query_string=None, anchor=None, view_name=None):
        self.ensure_one()
        url = self.access_url + '%s?access_token=%s%s%s%s%s' % (
            suffix if suffix else '',
            self._portal_ensure_token(),
            '&report_type=%s' % report_type if report_type else '',
            '&download=true' if download else '',
            query_string if query_string else '',
            '#%s' % anchor if anchor else ''
        )
        return url

Step 4 : create your controller which must inherit the "CustomerPortal" controller as bellow 

    # -*- coding: utf-8 -*-

    from odoo import http, _
    from odoo.exceptions import AccessError, MissingError
    from odoo.http import request
    from odoo.addons.portal.controllers.portal import CustomerPortal, pager as               portal_pager
    from odoo.tools import groupby as groupbyelem
    from odoo.osv.expression import OR

    class CustomerPortal(CustomerPortal):

        @http.route(['/model/name/<int:record_id>'], type='http', auth="public", website=True)
        def my_model_portal(self, record_id=None, access_token=None, **kw):
            try:
                model_name_sudo = self._document_check_access('model.name', record_id, access_token)
            except (AccessError, MissingError):
               return request.redirect('/my')
            values = {}
            return request.render("module_name.template_id", values)

Step 5 : create the template view as bellow

    <?xml version="1.0" encoding="UTF-8"?>
    <odoo>
        <template id="template_id" name="Template Name">
            <t t-call="website.layout">
                <form action='/form/name'>
                </form>
            </t>
        </template>
    </odoo>

Step 6 : call the portal view for specific model record as bellow

    <a t-att-href="record_id.get_portal_url()"></a>

python function to return multi views in odoo

In this blog we will explain how to create python function to return multi views in odoo for one model

For an example created custom tree view , custom form view and custom kanban view for the "res.partner" model with the bellow ids

tree view id = "res_partner_custom_tree_view"
form view id = "res_partner_custom_form_view"
kanban view id = "res_partner_custom_kanban_view"

and we created smart button on another model view which must call python function to open "res.partner" model with the previous views , so this function can be just like bellow  

def display_partners(self):
        tree_view_id = self.env.ref('module_name.res_partner_custom_tree_view')
        form_view_id = self.env.ref('module_name.res_partner_custom_form_view')
        kanban_view_id = self.env.ref('module_name.res_partner_custom_kanban_view')

        return {
            'type': 'ir.actions.act_window',
            'name': "string",
            'res_model': 'res.partner',
            'domain': [('id', 'in',self.get_partner_ids().ids)],
            'view_mode': 'tree,form',
            'views':[(tree_view_id.id,'tree'),(form_view_id.id,'form'),(,(form_view_id.id,'form').id,'kanban')],
            'target': 'current',
        }

Thursday, August 31, 2023

specific view for many2one field in odoo

 


In this blog we will explain how to add specific view for many2one field in odoo 

so to explain that let us say we created custom form view for the product unit of measure and we want to open it from uom_id field in product view just like at the image , so we must inherit the product from view , then add context attribute to uom_id field , just like bellow 

<xpath expr="//field[@name='uom_id']" position="attributes">
     <attribute name="context">
          {'form_view_ref':'custom_module_name.custom_form_view_id'}
     </attribute>
</xpath>


default view filter in odoo

 

 

In the blog we will explain how to add default view filter in odoo , just like the apps default filter in odoo apps view

Firstly :  inside the search view you must define your default filter with the right domain , for example let us say we want to add default filter in product view to display products that can be sold only , just as bellow

<filter name="can_be_sold" string="Can Be Sold" domain="[('sale_ok', '=', ture)]"/>

Secondly : pass you filter to view you want using context , just as bellow

<field name="context">{'search_default_can_be_sold':1}</field>

Thursday, August 24, 2023

ModuleNotFoundError: No module named 'psutil'

 
 I got this problem (ModuleNotFoundError: No module named 'psutil') when I was trying to run Odoo so I run the command bellow in-order to fix it
python -m pip install psutil

How to set postgresql password

 

In this blog we will explain How to set postgresql password

In order to set password for postgresql you have to open the terminal and run the comands bellow 

First :

    sudo -u postgres psql

Second

    \password postgres

Third : it will ask you to enter password then to enter it again , just like in         the image bellow 


 

 

multi views for one model in odoo

To explain how to create multi views for one model in Odoo

let us say that we create estate module which is depends on the project standard module and we want to store the estate project data inside the 'project.project' model from project module but it has different fields and view than the standard projects so 

first : we will inherit the "project.project" model and add selection field which we will use as domain inside the actions views 

# -*- coding: utf-8 -*-
from odoo import fields, models, api, _

class projectProject(models.Model):
    _name = 'project.project'
    _inherit = ['project.project','mail.thread']
    _description = 'Project models inherit'

    project_type = fields.Selection([
        ('project','Project'),
        ('estate_project','Estate Project')],
        default='project',
        string="Type"
    )

 

second : we will over write the standard projects action view to add domain in-order to display the records with "project_type = "project"

<record id="project.open_view_project_all" model="ir.actions.act_window">
            <field name="name">Projects</field>
            <field name="res_model">project.project</field>
            <field name="domain">[('project_type','=','project')]</field>
            <field name="context">{'default_project_type':'project'}</field>
            <field name="view_mode">kanban,form</field>
            <field name="view_id" ref="project.view_project_kanban"/>
            <field name="search_view_id" ref="project.view_project_project_filter"/>
            <field name="target">main</field>
            <field name="help" type="html">
                <p class="o_view_nocontent_smiling_face">
                    No projects found. Let's create one!
                </p><p>
                    Projects regroup tasks on the same topic and each have their own dashboard.
                </p>
            </field>
        </record> 


Third : we will create the estate project own views as bellow 

<record id="estate_project_form_view" model="ir.ui.view">
            <field name="name">auction.form</field>
            <field name="model">project.project</field>
            <field name="arch" type="xml">
                <form string="Estate Project">
                </form>
            </field>
        </record>

        <record id="estate_project_tree_view" model="ir.ui.view">
            <field name="name">estate.project.tree</field>
            <field name="model">project.project</field>
            <field name="arch" type="xml">
                <tree string="Estate Projects">
                </tree>
            </field>
        </record>

        <record model="ir.ui.view" id="estate_project_kanban_view">
            <field name="name">project.project.kanban</field>
            <field name="model">project.project</field>
            <field name="arch" type="xml">
                <kanban>
                </kanban>
            </field>
        </record>

        <record id="estate_project_act_window" model="ir.actions.act_window">
            <field name="name">Estate Projects</field>
            <field name="res_model">project.project</field>
            <field name="view_mode">kanban,tree,form</field>
            <field name="view_ids" eval="[(5, 0, 0),
                (0, 0, {'view_mode': 'tree', 'view_id': ref('estate_project_tree_view')}),
                (0, 0, {'view_mode': 'kanban', 'view_id': ref('estate_project_kanban_view')}),
                (0, 0, {'view_mode': 'form', 'view_id': ref('estate_project_form_view')})]"/>
            <field name="help" type="html">
                <p class="oe_view_nocontent_create">
                    There is no examples click here to add new Auction.
                </p>
            </field>
            <field name="domain">[('project_type','=','estate_project')]</field>
            <field name="context">{'default_project_type':'estate_project'}</field>
        </record>

        <menuitem
            name="Estate Projects"
            id="estate_project_menu"
            action="estate_project_act_window"
        />


ImportError: cannot import name 'utils'


 

     if you face this problem (ImportError: cannot import name 'utils') when you try to run odoo , you can fix it by using the commands bellow

pip install checkov

pip3 install checkov

python3.9 -m pip install checkov // for specific python version "3.9 as an example"

send chat message in odoo 16

In this blog we will explain how to send chat message in odoo 16 programmatically

#Sending direct chatting message in odoo mail , means send message in private channel that has "chat" type , so we have to create private channel with "chat"type for the user that we want to send him a message just as bellow

def action_send_chat_message(self,partner_id):
        odoobot = self.env.ref('base.partner_root')
        channel_name = odoobot.name+"/"+partner_id.name[0]+partner_id.name[1]+partner_id.name[2]+partner_id.name[3]+partner_id.name[4]+str(partner_id.id)
        mail_channel_id = self.env['mail.channel'].search([('name','=',channel_name)])
        if not mail_channel_id:
            mail_channel_id = self.env['mail.channel'].create({
                    'name': channel_name,
                    'channel_type':'chat',
                    'channel_partner_ids': [(4,odoobot.id),(4,partner_id.id)]
                })
        self.env['mail.message'].create({
            'email_from': self.env.user.partner_id.email,
            'author_id': self.env.user.partner_id.id,
            'model': 'mail.channel',
            'message_type':'comment',
            'subtype_id': self.env.ref('mail.mt_comment').id,
            'subject': _('Direct Chatting'),
            'body': 'This is a private direct message',
            'res_id':mail_channel_id.id,
            'partner_ids': [(4, partner_id.id)]
        })

#note : we must pass value for "channel_ids" field in odoo 14

 

RTL with odoo 14

To fix the problem of RTL with odoo 14 in Ubuntu , you have to open the terminal then run the commands bellow one by one

  • sudo apt install nodejs
  • sudo apt install npm
  • sudo apt  install curl
  • curl -sL https://deb.nodesource.com/setup_10.x -o nodesource_setup.sh  
  • nano nodesource_setup.sh
  • sudo bash nodesource_setup.sh
  • sudo apt install nodejs
  • sudo apt install build-essential
  • sudo npm install -g rtlcss

Monday, August 21, 2023

how to send email programmatically in odoo

In this blog we will explain how to send email programmatically in odoo

First : we have to create our email template just as bellow  

<?xml version="1.0" ?>
<odoo>
    <data noupdate="1">
        <record id="email_template_id" model="mail.template">
            <field name="name">Custom_Email_Template</field>
            <field name="model_id" ref="module_name.model_your_model_name"/>
            <field name="email_from">iap@odoo.com</field>
            <field name="partner_to">${object.partner_id.id}</field>
            <field name="subject">${object.company_id.name}</field>
            <field name="body_html" type="html">
                <div style="margin: 0px; padding: 0px;">
                    Email body
                </div>
            </field>
        </record>
    </data>
</odoo>

Second : create the python function that will send the email

def action_mail_send(self):
        template_id = self.env['mail.template'].search([('name', '=', 'Custom_Email_Template')])

        ctx = {
            'default_model': 'model_name',
            'default_res_id': record_id,
            'default_use_template': bool(template_id.id),
            'default_template_id': template_id.id,
            'default_composition_mode': 'comment',
            'mark_so_as_sent': True,
            'custom_layout': "mail.mail_notification_paynow",
            'proforma': self.env.context.get('proforma', False),
            'force_email': True,
            'default_check_value': True,
            'default_is_log': True,
        }
        self.env['mail.compose.message'].with_context()._action_send_mail()

 

The above code works for odoo 16 & 15 but in case we are work on odoo 14 we can use the code bellow

template_id = self.env.ref('hr_recruitment.email_template_data_applicant_congratulations')
        template_id.send_mail(self.id, force_send=True)

Odoo Invoice Qr code issues

There are two main issues must of us facing with the QR code in Odoo invoice & these issues are 1/ QR code displayed as broken image w...