[IMP] hr_skills: improve resume
- Replace "CV" with "Resume" - Allow to choose which information to show on the resume (Contact + Skills) - Use the company colors by default - Minor cosmetic changes on the template (reduced margins, better page break, etc.) task-3198645 closes odoo/odoo#113218 Signed-off-by: Kevin Baptiste <kba@odoo.com>
This commit is contained in:
@@ -23,6 +23,7 @@ This module introduces skills and resume management for employees.
|
||||
'data/hr_resume_data.xml',
|
||||
'data/hr_skill_data.xml',
|
||||
'data/ir_actions_server_data.xml',
|
||||
'data/report_paperformat.xml',
|
||||
'report/hr_employee_skill_report_views.xml',
|
||||
'report/hr_employee_cv_report.xml',
|
||||
'views/hr_department_views.xml',
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
import re
|
||||
|
||||
from odoo import _
|
||||
|
||||
from odoo.http import request, route, Controller, content_disposition
|
||||
|
||||
|
||||
class HrEmployeeCV(Controller):
|
||||
|
||||
@route(["/print/cv"], type='http', auth='user')
|
||||
def print_employee_cv(self, employee_ids='', color='#42a8c0', side_panel_position='right', **post):
|
||||
def print_employee_cv(self, employee_ids='', color_primary='#666666', color_secondary='#666666', **post):
|
||||
if not request.env.user.has_group('base.group_user') or not employee_ids or re.search("[^0-9|,]", employee_ids):
|
||||
return request.not_found()
|
||||
|
||||
@@ -23,18 +24,22 @@ class HrEmployeeCV(Controller):
|
||||
skill_type_language = request.env.ref('hr_skills.hr_skill_type_lang', raise_if_not_found=False)
|
||||
|
||||
report = request.env.ref('hr_skills.action_report_employee_cv', False)
|
||||
|
||||
pdf_content, dummy = request.env['ir.actions.report'].sudo()._render_qweb_pdf(
|
||||
report, employees.ids, data={
|
||||
'color': color,
|
||||
'side_panel_position': side_panel_position,
|
||||
'color_primary': color_primary,
|
||||
'color_secondary': color_secondary,
|
||||
'resume_type_education': resume_type_education,
|
||||
'skill_type_language': skill_type_language,
|
||||
'show_skills': 'show_skills' in post,
|
||||
'show_contact': 'show_contact' in post,
|
||||
'show_others': 'show_others' in post,
|
||||
})
|
||||
|
||||
if len(employees) == 1:
|
||||
report_name = _('CV %s', employees.name)
|
||||
report_name = _('Resume %s', employees.name)
|
||||
else:
|
||||
report_name = _('CV Book')
|
||||
report_name = _('Resumes')
|
||||
|
||||
pdfhttpheaders = [
|
||||
('Content-Type', 'application/pdf'),
|
||||
|
||||
@@ -25,10 +25,11 @@ action['domain'] = [('department_id', '=', record.id)]
|
||||
</record>
|
||||
|
||||
<record id="action_print_employees_cv" model="ir.actions.server">
|
||||
<field name="name">Print CV</field>
|
||||
<field name="name">Print Resume</field>
|
||||
<field name="model_id" ref="hr.model_hr_employee"/>
|
||||
<field name="binding_model_id" ref="hr.model_hr_employee"/>
|
||||
<field name="binding_view_types">list,form</field>
|
||||
<field name="binding_type">report</field>
|
||||
<field name="state">code</field>
|
||||
<field name="code">
|
||||
action = env['ir.actions.act_window']._for_xml_id('hr_skills.action_hr_employee_cv_wizard')
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="paperformat_resume" model="report.paperformat">
|
||||
<field name="name">Resume</field>
|
||||
<field name="default" eval="True"/>
|
||||
<field name="format">A4</field>
|
||||
<field name="orientation">Portrait</field>
|
||||
<field name="margin_top">12</field>
|
||||
<field name="margin_bottom">12</field>
|
||||
<field name="margin_left">5</field>
|
||||
<field name="margin_right">5</field>
|
||||
<field name="header_line" eval="False"/>
|
||||
<field name="header_spacing">20</field>
|
||||
<field name="dpi">90</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -1,3 +1,4 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import hr_employee_cv_report
|
||||
from . import hr_employee_skill_report
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
from odoo import _, models
|
||||
|
||||
class EmployeeResumeReport(models.AbstractModel):
|
||||
_name = 'report.hr_skills.report_employee_cv'
|
||||
_description = 'Employee Resume'
|
||||
|
||||
def _get_report_values(self, docids, data=None):
|
||||
show_others = (data or {}).get('show_others')
|
||||
employees = self.env['hr.employee'].browse(docids)
|
||||
|
||||
resume_lines = {}
|
||||
for employee in employees:
|
||||
resume_lines[employee] = defaultdict(self.env['hr.resume.line'].browse)
|
||||
for line in employee.resume_line_ids:
|
||||
if not show_others and not line.line_type_id:
|
||||
continue
|
||||
resume_lines[employee][line.line_type_id.name or _('Other')] |= line
|
||||
|
||||
return {
|
||||
'doc_ids': docids,
|
||||
'doc_model': 'hr.employee',
|
||||
'docs': employees,
|
||||
'resume_lines': resume_lines,
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="action_report_employee_cv" model="ir.actions.report">
|
||||
<field name="name">Employee CV</field>
|
||||
<field name="name">Employee Resume</field>
|
||||
<field name="model">hr.employee</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">hr_skills.report_employee_cv</field>
|
||||
<field name="report_file">hr_skills.report_employee_cv</field>
|
||||
<field name="paperformat_id" ref="hr_skills.paperformat_resume"/>
|
||||
<field name="print_report_name">'CV - %s' % (object.name)</field>
|
||||
<field name="binding_model_id" eval="False"/> <!-- Don't display it under Print menu -->
|
||||
<field name="binding_type">report</field>
|
||||
|
||||
@@ -23,8 +23,8 @@ div.o_employee_cv {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
&:not(:first-child) .o_new_page {
|
||||
page-break-before: always;
|
||||
&:not(:last-child) .o_new_page {
|
||||
page-break-after: always;
|
||||
}
|
||||
|
||||
.o_sidebar {
|
||||
@@ -42,6 +42,7 @@ div.o_employee_cv {
|
||||
|
||||
.o_profile {
|
||||
padding: 30px;
|
||||
padding-top: 15px;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
@@ -183,10 +184,6 @@ div.o_employee_cv {
|
||||
|
||||
}
|
||||
|
||||
.o_main_panel_section:not(:last-of-type) {
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
|
||||
.o_main_panel_resume_title {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -5,17 +5,10 @@
|
||||
<t t-call="web.basic_layout">
|
||||
<t t-foreach="docs" t-as="o">
|
||||
<div class="o_employee_cv page">
|
||||
<t t-call="hr_skills.report_employee_cv_company"/>
|
||||
<t t-call="hr_skills.report_employee_cv_sidepanel"/>
|
||||
<t t-call="hr_skills.report_employee_cv_main_panel"/>
|
||||
<p class="o_new_page"/>
|
||||
<t t-if="side_panel_position == 'right'">
|
||||
<t t-call="hr_skills.report_employee_cv_company"/>
|
||||
<t t-call="hr_skills.report_employee_cv_sidepanel"/>
|
||||
<t t-call="hr_skills.report_employee_cv_main_panel"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<t t-call="hr_skills.report_employee_cv_sidepanel"/>
|
||||
<t t-call="hr_skills.report_employee_cv_company"/>
|
||||
<t t-call="hr_skills.report_employee_cv_main_panel"/>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
@@ -23,24 +16,26 @@
|
||||
|
||||
<template id="report_employee_cv_company">
|
||||
<div class="o_company row">
|
||||
<div class="col-12 mb-4 mt-5">
|
||||
<div class="col-12 mb-4">
|
||||
<img t-if="o.company_id.logo" t-att-src="image_data_uri(o.company_id.logo)" style="max-height: 45px;" alt="Logo"/>
|
||||
</div>
|
||||
<div class="col-12 mb-4" style="font-weight: bold;">
|
||||
<t t-esc="o.company_id.partner_id.name"/>
|
||||
</div>
|
||||
<div class="col-12" t-field="o.company_id.partner_id" t-options='{"widget": "contact", "fields": ["address"], "no_marker": True}'/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="report_employee_cv_sidepanel">
|
||||
<div class="o_sidebar" t-attf-style="
|
||||
background: #{color};
|
||||
background-color: #{color};">
|
||||
background: #{color_primary};
|
||||
background-color: #{color_primary};">
|
||||
<div class="o_profile">
|
||||
<img class="img img-fluid rounded-circle o_profile_image" t-attf-src="data:image/png;base64,#{o.image_128}" alt=""/>
|
||||
<img class="img img-fluid rounded-circle o_profile_image" t-attf-src="data:image/png;base64,#{o.image_128}" alt="" t-if="o.image_128"/>
|
||||
<h1 class="o_profile_name mt-2" t-field="o.name"/>
|
||||
<h3 class="o_profile_job mb-2" t-field="o.job_id"/>
|
||||
</div>
|
||||
|
||||
<div class="o_sidebar_section">
|
||||
<div class="o_sidebar_section" t-if="show_contact">
|
||||
<ul class="o_social">
|
||||
<li class="email" t-if="o.company_id.email">
|
||||
<i class="fa fa-solid fa-envelope pe-2"/>
|
||||
@@ -60,42 +55,40 @@
|
||||
</template>
|
||||
|
||||
<template id="report_employee_cv_main_panel">
|
||||
<div class="o_main_panel">
|
||||
<div class="o_main_panel_section">
|
||||
<t t-foreach="o.resume_line_ids.line_type_id" t-as="line_type">
|
||||
<h2 class="o_main_panel_title mt-4" t-attf-style="color: #{color};">
|
||||
<span class="o_main_panel_icon" t-attf-style="background: #{color};">
|
||||
<t t-foreach="resume_lines[o]" t-as="line_type">
|
||||
<div class="o_main_panel">
|
||||
<div class="o_main_panel_section">
|
||||
<h2 class="o_main_panel_title mt-4" t-attf-style="color: #{color_secondary};">
|
||||
<span class="o_main_panel_icon" t-attf-style="background: #{color_secondary};">
|
||||
<i class="fa fa-solid fa-briefcase"/>
|
||||
</span>
|
||||
<t t-esc="line_type.name"/>
|
||||
<t t-esc="line_type"/>
|
||||
</h2>
|
||||
<t t-foreach="o.resume_line_ids" t-as="resume_line">
|
||||
<t t-if="resume_line.line_type_id == line_type">
|
||||
<div class="o_main_panel_resume_title ps-5">
|
||||
<div class="o_main_panel_resume_title_job">
|
||||
<h3 class="o_main_panel_resume_job" t-field="resume_line.name"/>
|
||||
</div>
|
||||
<div class="o_main_panel_resume_title_dates">
|
||||
<t t-set="present">Present</t>
|
||||
<div class="o_main_panel_resume_year">
|
||||
<t t-esc="resume_line.date_start.year"/> - <t t-esc="resume_line.date_end.year if resume_line.date_end else present"/>
|
||||
</div>
|
||||
<t t-foreach="resume_lines[o][line_type]" t-as="resume_line">
|
||||
<div class="o_main_panel_resume_title ps-5">
|
||||
<div class="o_main_panel_resume_title_job">
|
||||
<h3 class="o_main_panel_resume_job" t-field="resume_line.name"/>
|
||||
</div>
|
||||
<div class="o_main_panel_resume_title_dates">
|
||||
<t t-set="present">Present</t>
|
||||
<div class="o_main_panel_resume_year">
|
||||
<t t-esc="resume_line.date_start.year"/> - <t t-esc="resume_line.date_end.year if resume_line.date_end else present"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="o_main_panel_resume_description ps-5">
|
||||
<p t-field="resume_line.description"/>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
<div class="o_main_panel_resume_description ps-5">
|
||||
<p t-field="resume_line.description"/>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-set="has_languages" t-value="skill_type_language and any(skill_line.skill_type_id == skill_type_language for skill_line in o.employee_skill_ids)"/>
|
||||
<div class="o_main_panel mt-4" t-if="has_languages">
|
||||
<div class="o_main_panel_section" t-if="skill_type_language">
|
||||
<h2 class="o_main_panel_title" t-attf-style="color: #{color};">
|
||||
<span class="o_main_panel_icon" t-attf-style="background: #{color};">
|
||||
<h2 class="o_main_panel_title" t-attf-style="color: #{color_secondary};">
|
||||
<span class="o_main_panel_icon" t-attf-style="background: #{color_secondary};">
|
||||
<i class="fa fa-solid fa-language"/>
|
||||
</span>
|
||||
Languages
|
||||
@@ -112,11 +105,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<t t-set="has_skills" t-value="any(skill_line.skill_type_id != skill_type_language for skill_line in o.employee_skill_ids)"/>
|
||||
<t t-set="has_skills" t-value="show_skills and any(skill_line.skill_type_id != skill_type_language for skill_line in o.employee_skill_ids)"/>
|
||||
<div class="o_main_panel mt-4" t-if="has_skills">
|
||||
<div class="o_main_panel_section">
|
||||
<h2 class="o_main_panel_title" t-attf-style="color: #{color};">
|
||||
<span class="o_main_panel_icon" t-attf-style="background: #{color};">
|
||||
<h2 class="o_main_panel_title" t-attf-style="color: #{color_secondary};">
|
||||
<span class="o_main_panel_icon" t-attf-style="background: #{color_secondary};">
|
||||
<i class="fa fa-solid fa-rocket"/>
|
||||
</span>Skills
|
||||
</h2>
|
||||
@@ -131,11 +124,11 @@
|
||||
<div
|
||||
class="o_main_panel_skill_progress_bar_color"
|
||||
role="progressbar"
|
||||
t-attf-style="width: #{skill_line.level_progress}%; background: #{color};"
|
||||
t-attf-style="width: #{skill_line.level_progress}%; background: #{color_primary};"
|
||||
t-att-aria-valuenow="skill_line.level_progress"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
|
||||
@@ -3,25 +3,42 @@
|
||||
|
||||
from werkzeug.urls import url_encode
|
||||
|
||||
from odoo import fields, models
|
||||
from odoo import _, api, fields, models
|
||||
|
||||
|
||||
class HrEmployeeCVWizard(models.TransientModel):
|
||||
_name = 'hr.employee.cv.wizard'
|
||||
_description = 'Print CV'
|
||||
_description = 'Print Resume'
|
||||
|
||||
employee_ids = fields.Many2many('hr.employee')
|
||||
color = fields.Char('Color Theme', default="#3a9bc8", required=True)
|
||||
side_panel_position = fields.Selection([('left', 'Left'), ('right', 'Right')], default='right', required=True)
|
||||
|
||||
color_primary = fields.Char('Primary Color', default=lambda self: self.env.company.primary_color or "#666666", required=True)
|
||||
color_secondary = fields.Char('Secondary Color', default=lambda self: self.env.company.secondary_color or "#666666", required=True)
|
||||
|
||||
show_skills = fields.Boolean(string='Skills', default=True)
|
||||
show_contact = fields.Boolean(string='Contact Information', default=True)
|
||||
show_others = fields.Boolean(string='Others', default=True)
|
||||
|
||||
can_show_others = fields.Boolean(compute='_compute_can_show_others')
|
||||
can_show_skills = fields.Boolean(compute='_compute_can_show_others')
|
||||
|
||||
@api.depends('employee_ids')
|
||||
def _compute_can_show_others(self):
|
||||
for wizard in self:
|
||||
wizard.can_show_others = wizard.employee_ids.resume_line_ids.filtered(lambda l: not l.line_type_id)
|
||||
wizard.can_show_skills = wizard.employee_ids.skill_ids
|
||||
|
||||
def action_validate(self):
|
||||
self.ensure_one()
|
||||
return {
|
||||
'name': 'Print CV',
|
||||
'name': _('Print Resume'),
|
||||
'type': 'ir.actions.act_url',
|
||||
'url': '/print/cv?' + url_encode({
|
||||
'employee_ids': ','.join(str(x) for x in self.employee_ids.ids),
|
||||
'color': self.color,
|
||||
'side_panel_position': self.side_panel_position,
|
||||
'color_primary': self.color_primary,
|
||||
'color_secondary': self.color_secondary,
|
||||
'show_skills': 1 if self.show_skills else None,
|
||||
'show_contact': 1 if self.show_contact else None,
|
||||
'show_others': 1 if self.show_others else None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,12 +4,20 @@
|
||||
<field name="name">hr.employee.cv.wizard.view.form</field>
|
||||
<field name="model">hr.employee.cv.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Print CV">
|
||||
<form string="Print Resume">
|
||||
<field name="can_show_others" invisible="1"/>
|
||||
<field name="can_show_skills" invisible="1"/>
|
||||
<group>
|
||||
<group>
|
||||
<field name="employee_ids" widget="many2many_tags" invisible="1"/>
|
||||
<field name="color" widget="color"/>
|
||||
<field name="side_panel_position"/>
|
||||
<label for="color_primary" string="Colors"/>
|
||||
<div class="d-flex flex-row">
|
||||
<field name="color_primary" widget="color" nolabel="1" style="width: 35px;"/>
|
||||
<field name="color_secondary" widget="color" nolabel="1"/>
|
||||
</div>
|
||||
<field name="show_contact"/>
|
||||
<field name="show_others" attrs="{'invisible': [('can_show_others', '=', False)]}"/>
|
||||
<field name="show_skills" attrs="{'invisible': [('can_show_skills', '=', False)]}"/>
|
||||
</group>
|
||||
</group>
|
||||
<footer>
|
||||
@@ -21,7 +29,7 @@
|
||||
</record>
|
||||
|
||||
<record id="action_hr_employee_cv_wizard" model="ir.actions.act_window">
|
||||
<field name="name">Print CV</field>
|
||||
<field name="name">Print Resume</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">hr.employee.cv.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
|
||||
Reference in New Issue
Block a user