Skip to Content
Menu
This question has been flagged
1 Reply
8141 Views

I'm trying to develop custom human ressources evaluation dashboards using a custom module.

I took the existing hr_evaluation dashboard as a start, and try to do such developpement.

A- Python side : 

So I found that I have to create a report model, "hr_evaluation_report" that takes fields from models involved on the dashboard (here the hr_evaluation_evaluation and hr_evaluation_interview models), and if needed some fields used to display computed results on the dashboard (here the delay_date and overpass_delay), and this using depends, as the following code demonstrates : 

hr_evaluation_report.py 

In this file I found also a init method.

I need to know more about this init method :

1- Is it mandatory ?

2- Is it serving only for performing a sql request to make data available for the reporting model and then initialize it?

3- I see that it reacts on views displaying using tools.drop_view_if_exists, so what are all the purposes for using this method ?

B- View side :

On the hr_evaluation_view.xml 

We can see that :

1- Initializes the graph view

2- Making Filters and group by availables

3- Adding dashboard menuitem, and action allowing to open the corresponding view.

But I can't see :

How to customize the measure selectio box ?

Image showing the measures selection box 

And a final question about this button sequence :

Image showing the sequence button 

Does this buttons sequence come by default (using inheritance maybe) ? 

Every suggestion, tutorials, additions, hints will be welcome. 


------------------------------------------------- EDIT --------------------------------------------------------------------------------------------------------------------

@Axel Mendoza : 

I developped my custom module for reporting, without the init method, and I set _auto=True, so Odoo will create the sql table for me on the database. Here is the main module files : 

1. hr_evaluation_report_extension.py

On this new created model, I want to get the necessary fields needed to retrieve data, the purpose is to have the goals reaching percentage per employee, and having the ability to group by employee, department, job, ... , So the 'reach' field is the one measurable, because it's the only one with float type.

So I needed to put 3 models on the depends list, but there is no relationnal fields between the three, Is it necessary to have data consistency ? because on the example I followed, when using the init method, they did a LEFT JOIN to have data, what did I need in case I don't use the init method ?

from import openerp.osv fields, osv


class hr_evaluation_report_goals(osv.Model):
_name = "hr.evaluation.report.goals"
"Goals reaching Statistics"
_description = _auto = True

_columns = {
'create_date': fields.datetime('Create Date', readonly=True),
        'plan_id': fields.many2one('hr_evaluation.plan','Plan' , readonly=True),
        'job_id': fields.many2one('hr.job','Job Title ),
        'department_id': fields.many2one('hr.department''Department'),
        'statut': fields.char('Statut', size=32),
        'name_related': fields.related('resource_id','name', type='char', string='Name', readonly=True, store=True),
        'reach': fields.float('Atteinte objectif global'),
        'state': fields.selection([
('draft''Draft', ),
('wait''Plan In Progress', ),
('progress''Final Validation', ),
('done''Done', ),
('cancel''Cancelled', ),
], 'Status', readonly=True),
}
_depends = {
'hr.employee': ['name_related','job_id','department_id','statut'],
        'survey.user_input': ['reach'],
        'hr_evaluation.evaluation': [
'create_date','plan_id','state'],
}


The problem is there is no data on the sql table created by Odoo :

Image showing the sql request result


2. hr_evaluation_report_extension_view.xml

On the view side I put on the graph tag the name_related (employee name) field on the rows, and create_date (employee evaluation create date) on the col, and the field reach as measure.

besides, I created the search view for filters and group by.

<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>

<record id="view_goals_reaching_graph" model="ir.ui.view">
<field name="name">hr.goals.reaching</field>
<field name="model">hr.evaluation.report.goals</field>
<field name="arch" type="xml">
<graph string="Goals reaching analysis" type="pivot" stacked="True">
<field name="name_related" type="row"/>
<field name="create_date" type="col"/>
<field name="reach" type="measure"/>
</graph>
</field>
</record>

<record id="view_goals_reaching_search" model="ir.ui.view">
<field name="name">hr.goals.search</field>
<field name="model">hr.evaluation.report.goals</field>
<field name="arch" type="xml">
<search string="Pourcentage atteinte globale des objectifs">
<filter string="En cours" domain="[('state', '=' ,'wait')]" help = "In progress Evaluations"/>
<filter string="Attente appréciation DRH" domain="[('state','=','progress')]" help = "Final Validation Evaluations"/>
<filter string="Terminé" domain="[('state','=','done')]"/>
<field name="name_related"/>
<field name="plan_id"/>
<group expand="0" string="Extended Filters...">
<field name="state"/>
<field name="create_date"/>
<field name="department_id"/>
<field name="job_id"/>
<field name="statut"/>
</group>
<group expand="1" string="Group By">
<filter string="Employé" name="employee" context="{'group_by':'name_related'}"/>
<filter string="Campagne" context="{'group_by':'plan_id'}"/>
<filter string="Direction" context="{'group_by':'department_id'}"/>
<filter string="Fonction" context="{'group_by':'job_id'}"/>
<filter string="statut" context="{'group_by':'statut'}"/>
<separator/>
<filter string="Month" context="{'group_by':'create_date:month'}" help="Mois de création"/>
</group>
</search>
</field>
</record>

<record id="action_evaluation_goals_reaching" model="ir.actions.act_window">
<field name="name">Atteinte des objectifs</field>
<field name="res_model">hr.evaluation.report.goals</field>
<field name="view_type">form</field>
<field name="view_mode">graph</field>
<field name="search_view_id" ref="view_goals_reaching_search"/>
</record>

<menuitem action="action_evaluation_goals_reaching" id="menu_evaluation_goals_reaching" parent="hr.menu_hr_reporting" sequence="3" groups="base.group_hr_manager"/>

</data>
</openerp>

-------------------------------------------------------------------EDIT 2-------------------------------------------------------------------------------------------------

Here after the report model : 

class hr_evaluation_report_goals(osv.Model):
_name = "hr.evaluation.report.goals"
_description = "Goals reaching"
_auto = False

_columns = {
'evaluation_plan_id': fields.many2one('hr_evaluation.plan 'Appraisal Plan' ),
        'job_id': fields.many2one('hr.job''Job Title'),
        'department_id': fields.many2one('hr.department''Department'),
        'statut': fields.char('Statut', size=32),
        'name_related': fields.related('resource_id''name' , type='char', string='Name', readonly=True, store=True),
        'reach': fields.float('Atteinte objectif global'),
    }
_depends = {
'hr.employee': ['id','name_related','job_id','department_id','statut','evaluation_plan_id'],
        'survey.user_input': ['reach']
    }

Here is my init method : 

def init(self, cr):
tools.drop_view_if_exists(cr, 'hr_evaluation_report_goals')
cr.execute("""
create or replace view hr_evaluation_report_goals as (
select
min(e.id) as id,
e.name_related,
e.evaluation_plan_id,
e.department_id,
e.job_id,
e.statut,
s.reach
from
survey_user_input s
INNER JOIN
hr_employee e on (e.id=s.employee_id)
GROUP BY
e.name_related,
e.evaluation_plan_id,
e.department_id,
e.job_id,
e.statut
)
"""
)


I get this error :

ProgrammingError: column "s.reach" must appear in the GROUP BY clause or be used in an aggregate function

LINE 10: s.reach

How to store my goals reaching value in the sql view, if it's not syntaxclty correct ? 


Avatar
Discard

You don't need an SQL view for your report, you could directly use the model survey.user_input that contains all the data you need, just add the necessary related fields and you could delete the model hr.evaluation.report.goals that you are creating. That's the scenario that I describe before when you could access all the data to display in the report by using a low level model that have the many2one relations to fit your needs

Author

Ah okay! So I have to add all relationnal fields from hr.employee model to the survey.user_input model (the one I should base my report on), and override the survey.user_input create method to insert data into relationnal fields columns, and thats 'all I have all necessary data on the survey.user_input to base my report on, correct me if I'm wrong please. Gracias!

Author

I meant related fields not relationnal fields ...

Author

Axel, For testing purposes, I used the sql request, and it worked, I know there is no need to do so, using just the survey.user_input model with relationnal fields from hr.employee would be enough (I'll wait to complete my discussion with you about that), so it worked but I need to do some little adjustments, details here : https://www.odoo.com/forum/help-1/question/odoo-8-reporting-get-data-average-instead-of-data-sum-94558 Thanks a lot Axel for your help.

Looking at your query you just need to have inserted the employee_id to be able to access the data from hr.employee, the rest of the data could be accessed using related fields that don't need to be inserted by you

Best Answer

@Yassine TEIMI

I will try to explain all your questions.

First that's not what it's called dashboards or boards in Odoo, those are a group of several actions/views that are shows in columns for the same menu.

Your case is a graph view of the model hr.evaluation.report. That model will not create an SQL table due to the _auto = False, it get the data from on an SQL view, that's why it need to use the init method that it's called when installing or updating the module to create the tables or views to use for the model. The init method is just called if it's defined for the model, and it's needed if you define _auto = False because that means that Odoo will not create the table for you. The init method only get the database cursor and you could do what you need to do in that method. The statement tools.drop_view_if_exists it's just an utility method that execute:

cr.execute("DROP view IF EXISTS %s CASCADE" % (viewname,))

that could be done in the same view creation using the "create or replace view". The purpose is to update the view always when the module is updated

In the view part, the measure select box it will use all the fields defined as integer or float in the model, I've done some extensions to be able to use others kind of fields to create a kind of cross-table using this type of graph pivot view but it will require to override the read_group method, by default in Odoo only works for integer or float fields.
The buttons sequence are part of the graph widget of Odoo, the first button group in the image is for the types of graph that you can switch to.
Hope this helps

***Update for completeness***

Two more details:

When Odoo create the table for you(_auto = True by default) and not using an SQL view, you need to insert the data to use it using the way you prefers, direclty insert records via UI form, sql or etl operations, python model create calls, XMLRPC calls to model create method, etc

When you set _auto = False and create an SQL view to use as the model table, you need to specify an unique id field value as a return for your SQL view query, it's mandatory since Odoo will performs search for those ids before select the real data.


 

Avatar
Discard
Author

Thanks a lot for your explainations, it's helpfull. Now I get the picture, I'll try to develop my extension, and post the result here.

Author

I have one more question, when Odoo creates the sql table for you, does it require something to be able fill data on the sql table created ? When _auto is set to false, you perform the sql request via the init method, but when it's not set to false what does it requires so as to get data correctly on the sql table created on database by Odoo ? This is my main question, I'll fill all details on y question EDIT. Thanks Axel.

That was something I think to write you but I don't do it, I post an update explaining that and another detail that I just left out

Author

Thanks a lot, I get the picture now, I think it will be too painfull for me to automate data creation on the "real" sql table created by Odoo, So I think I'll use the _auto=False, and use the init method. What do you think is the best solution in my case ?

Normally you should base your report in a model that contains the most low level data, to be able to navigate to the upper levels through many2one fields. For example if you wanna make a report for product sales you need to put your report in the sale.order.line model because it will contains the low level of the sale data with the possibility to use the product_id, sale_id, sale_id.partner_id or reach the data you need though many2one relations fields, if you don't have a low level data model that have the needed relations to access the data you need to display it's a perfect scenario to create an SQL view to retrieve all the data you need to display with the special concern of the id field I referred in the answer

Author

Okay Axel, Thanks for this explainations, So I used the init method, and I've got an sql error, I updated my question (see EDIT 2) for error details. Thanks.

Related Posts Replies Views Activity
1
Nov 24
752
0
Oct 17
2298
1
Apr 19
3020
6
Jun 15
6119
0
May 15
3056