diff --git a/partner_skills/__init__.py b/partner_skills/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/partner_skills/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/partner_skills/__manifest__.py b/partner_skills/__manifest__.py new file mode 100644 index 0000000..b06fc89 --- /dev/null +++ b/partner_skills/__manifest__.py @@ -0,0 +1,22 @@ +{ + 'name': 'partner_skills', + 'version': '18.0.1.0.0', + 'category': 'Contacts', + 'summary': 'Manage skills for contacts/partners', + 'description': 'Adds skill management (skills, skill types, skill levels) to partners.', + 'depends': ['base','contacts'], + 'data': [ + 'security/ir.model.access.csv', + 'views/partner_skill_type_views.xml', + 'views/res_partner_views.xml', + ], + 'assets': { + 'web.assets_backend': [ + 'partner_skills/static/src/views/partner_skills_list_renderer.js', + 'partner_skills/static/src/fields/partner_skills_one2many/*', + ], + }, + 'license': 'LGPL-3', + 'installable': True, + 'auto_install': False, +} diff --git a/partner_skills/i18n/fr.po b/partner_skills/i18n/fr.po new file mode 100644 index 0000000..2f86b74 --- /dev/null +++ b/partner_skills/i18n/fr.po @@ -0,0 +1,297 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * partner_skills +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2026-04-15 13:37+0000\n" +"PO-Revision-Date: 2026-04-15 13:37+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: partner_skills +#. odoo-python +#: code:addons/partner_skills/models/partner_skill_type.py:0 +msgid "%s (copy)" +msgstr "" + +#. module: partner_skills +#. odoo-javascript +#: code:addons/partner_skills/static/src/fields/partner_skills_one2many/partner_skills_one2many.xml:0 +msgid "ADD" +msgstr "" + +#. module: partner_skills +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_type__active +msgid "Active" +msgstr "" + +#. module: partner_skills +#: model_terms:ir.ui.view,arch_db:partner_skills.partner_skill_type_view_form +#: model_terms:ir.ui.view,arch_db:partner_skills.partner_skill_type_view_search +msgid "Archived" +msgstr "" + +#. module: partner_skills +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill__color +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_line__color +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_type__color +msgid "Color" +msgstr "Couleur" + +#. module: partner_skills +#: model:ir.model,name:partner_skills.model_res_partner +msgid "Contact" +msgstr "" + +#. module: partner_skills +#: model_terms:ir.actions.act_window,help:partner_skills.partner_skill_type_action +msgid "Create a new skill type" +msgstr "Créer une nouvelle entrée" + +#. module: partner_skills +#. odoo-javascript +#: code:addons/partner_skills/static/src/fields/partner_skills_one2many/partner_skills_one2many.xml:0 +msgid "Create new Skills" +msgstr "Créer une nouvelle compétence" + +#. module: partner_skills +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill__create_uid +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_level__create_uid +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_line__create_uid +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_type__create_uid +msgid "Created by" +msgstr "" + +#. module: partner_skills +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill__create_date +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_level__create_date +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_line__create_date +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_type__create_date +msgid "Created on" +msgstr "" + +#. module: partner_skills +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_level__default_level +msgid "Default Level" +msgstr "Niveau par défaut" + +#. module: partner_skills +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill__display_name +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_level__display_name +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_line__display_name +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_type__display_name +msgid "Display Name" +msgstr "" + +#. module: partner_skills +#: model_terms:ir.ui.view,arch_db:partner_skills.partner_skill_view_search +msgid "Group By" +msgstr "" + +#. module: partner_skills +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill__id +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_level__id +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_line__id +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_type__id +msgid "ID" +msgstr "" + +#. module: partner_skills +#: model:ir.model.fields,help:partner_skills.field_partner_skill_level__default_level +msgid "" +"If checked, this level will be the default one selected when choosing this " +"skill." +msgstr "" +"Si coché, ce niveau sera sélectionné par défaut lors du choix de cette " +"compétence." + +#. module: partner_skills +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill__write_uid +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_level__write_uid +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_line__write_uid +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_type__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: partner_skills +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill__write_date +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_level__write_date +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_line__write_date +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_type__write_date +msgid "Last Updated on" +msgstr "" + +#. module: partner_skills +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_type__skill_level_ids +#: model_terms:ir.ui.view,arch_db:partner_skills.partner_skill_type_view_form +msgid "Levels" +msgstr "Niveaux" + +#. module: partner_skills +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill__name +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_level__name +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_type__name +msgid "Name" +msgstr "" + +#. module: partner_skills +#. odoo-javascript +#: code:addons/partner_skills/static/src/views/partner_skills_list_renderer.js:0 +msgid "Other" +msgstr "Autre" + +#. module: partner_skills +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_line__partner_id +msgid "Partner" +msgstr "" + +#. module: partner_skills +#: model:ir.model,name:partner_skills.model_partner_skill +msgid "Partner Skill" +msgstr "" + +#. module: partner_skills +#: model:ir.model,name:partner_skills.model_partner_skill_level +msgid "Partner Skill Level" +msgstr "Niveaux" + +#. module: partner_skills +#: model:ir.model,name:partner_skills.model_partner_skill_type +msgid "Partner Skill Type" +msgstr "Type" + +#. module: partner_skills +#: model:ir.ui.menu,name:partner_skills.menu_partner_skills +msgid "Partner Skills" +msgstr "Compétences" + +#. module: partner_skills +#. odoo-javascript +#: code:addons/partner_skills/static/src/fields/partner_skills_one2many/partner_skills_one2many.xml:0 +msgid "Pick a skill from the list" +msgstr "Choisissez une compétence dans la liste" + +#. module: partner_skills +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_level__level_progress +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_line__level_progress +msgid "Progress" +msgstr "" + +#. module: partner_skills +#: model:ir.model.fields,help:partner_skills.field_partner_skill_level__level_progress +#: model:ir.model.fields,help:partner_skills.field_partner_skill_line__level_progress +msgid "Progress from zero knowledge (0%) to fully mastered (100%)." +msgstr "" +"Progression d'une connaissance nulle (0%) à complètement maitrisée (100%)." + +#. module: partner_skills +#: model:ir.model.constraint,message:partner_skills.constraint_partner_skill_level_check_level_progress +msgid "Progress should be a number between 0 and 100." +msgstr "La progression doit être un nombre compris entre 0 et 100." + +#. module: partner_skills +#. odoo-javascript +#: code:addons/partner_skills/static/src/fields/partner_skills_one2many/partner_skills_one2many.js:0 +msgid "Select Skills" +msgstr "Sélectionnez des compétences" + +#. module: partner_skills +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill__sequence +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_level__sequence +msgid "Sequence" +msgstr "" + +#. module: partner_skills +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_line__skill_id +#: model:ir.model.fields,field_description:partner_skills.field_res_partner__skill_ids +#: model:ir.model.fields,field_description:partner_skills.field_res_users__skill_ids +#: model_terms:ir.ui.view,arch_db:partner_skills.res_partner_view_search_inherit_skills +msgid "Skill" +msgstr "Compétence" + +#. module: partner_skills +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_line__skill_level_id +msgid "Skill Level" +msgstr "Niveau" + +#. module: partner_skills +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill__skill_type_id +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_level__skill_type_id +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_line__skill_type_id +#: model_terms:ir.ui.view,arch_db:partner_skills.partner_skill_view_search +msgid "Skill Type" +msgstr "Type" + +#. module: partner_skills +#: model:ir.actions.act_window,name:partner_skills.partner_skill_type_action +#: model:ir.ui.menu,name:partner_skills.menu_action_res_bank_form +msgid "Skill Types" +msgstr "Types" + +#. module: partner_skills +#: model:ir.model,name:partner_skills.model_partner_skill_line +msgid "Skill level for a partner" +msgstr "Niveau" + +#. module: partner_skills +#. odoo-javascript +#: code:addons/partner_skills/static/src/fields/partner_skills_one2many/partner_skills_one2many.xml:0 +#: model:ir.model.fields,field_description:partner_skills.field_partner_skill_type__skill_ids +#: model:ir.model.fields,field_description:partner_skills.field_res_partner__partner_skill_ids +#: model:ir.model.fields,field_description:partner_skills.field_res_users__partner_skill_ids +#: model_terms:ir.ui.view,arch_db:partner_skills.partner_skill_type_view_form +#: model_terms:ir.ui.view,arch_db:partner_skills.res_partner_view_form_inherit_skills +msgid "Skills" +msgstr "Compétences" + +#. module: partner_skills +#. odoo-python +#: code:addons/partner_skills/models/partner_skill_line.py:0 +msgid "The skill %(name)s and skill type %(type)s doesn't match" +msgstr "" +"La compétence %(name)s et le type de compétences %(type)s ne correspondent " +"pas" + +#. module: partner_skills +#. odoo-python +#: code:addons/partner_skills/models/partner_skill_line.py:0 +msgid "The skill level %(level)s is not valid for skill type: %(type)s" +msgstr "" +"Le niveau de compétence %(level)s n'est pas valide pour le type de " +"compétences : %(type)s" + +#. module: partner_skills +#. odoo-javascript +#: code:addons/partner_skills/static/src/fields/partner_skills_one2many/partner_skills_one2many.xml:0 +msgid "There are no skills defined in the library." +msgstr "Aucune compétence n'est définie dans la bibliothèque." + +#. module: partner_skills +#: model:ir.model.constraint,message:partner_skills.constraint_partner_skill_line__unique_skill +msgid "Two levels for the same skill is not allowed" +msgstr "" +"Deux niveaux pour la même compétence le même jour ne sont pas autorisés" + +#. module: partner_skills +#. odoo-javascript +#: code:addons/partner_skills/static/src/fields/partner_skills_one2many/partner_skills_one2many.xml:0 +msgid "Why not try adding some?" +msgstr "Pourquoi ne pas essayer d'en ajouter ?" + +#. module: partner_skills +#. odoo-javascript +#: code:addons/partner_skills/static/src/fields/partner_skills_one2many/partner_skills_one2many.xml:0 +msgid "You can add skills to this contact's profile." +msgstr "" + +#. module: partner_skills +#: model_terms:ir.ui.view,arch_db:partner_skills.partner_skill_type_view_form +msgid "e.g. Languages" +msgstr "" diff --git a/partner_skills/models/__init__.py b/partner_skills/models/__init__.py new file mode 100644 index 0000000..d732aa4 --- /dev/null +++ b/partner_skills/models/__init__.py @@ -0,0 +1,5 @@ +from . import partner_skill_type +from . import partner_skill +from . import partner_skill_level +from . import partner_skill_line +from . import res_partner diff --git a/partner_skills/models/partner_skill.py b/partner_skills/models/partner_skill.py new file mode 100644 index 0000000..82d3306 --- /dev/null +++ b/partner_skills/models/partner_skill.py @@ -0,0 +1,20 @@ +from odoo import api, fields, models + + +class PartnerSkill(models.Model): + _name = 'partner.skill' + _description = "Partner Skill" + _order = "sequence, name" + + name = fields.Char(required=True, translate=True) + sequence = fields.Integer(default=10) + skill_type_id = fields.Many2one('partner.skill.type', required=True, ondelete='cascade') + color = fields.Integer(related='skill_type_id.color') + + @api.depends('skill_type_id') + @api.depends_context('from_skill_dropdown') + def _compute_display_name(self): + if not self._context.get('from_skill_dropdown'): + return super()._compute_display_name() + for record in self: + record.display_name = f"{record.name} ({record.skill_type_id.name})" diff --git a/partner_skills/models/partner_skill_level.py b/partner_skills/models/partner_skill_level.py new file mode 100644 index 0000000..3ade278 --- /dev/null +++ b/partner_skills/models/partner_skill_level.py @@ -0,0 +1,39 @@ +from odoo import api, fields, models + + +class PartnerSkillLevel(models.Model): + _name = 'partner.skill.level' + _description = "Partner Skill Level" + _order = "sequence, level_progress desc" + + sequence = fields.Integer(default=10) + skill_type_id = fields.Many2one('partner.skill.type', ondelete='cascade') + name = fields.Char(required=True) + level_progress = fields.Integer(string="Progress", help="Progress from zero knowledge (0%) to fully mastered (100%).") + default_level = fields.Boolean(help="If checked, this level will be the default one selected when choosing this skill.") + + _sql_constraints = [ + ('check_level_progress', 'CHECK(level_progress BETWEEN 0 AND 100)', "Progress should be a number between 0 and 100."), + ] + + @api.depends('level_progress') + @api.depends_context('from_skill_level_dropdown') + def _compute_display_name(self): + if not self._context.get('from_skill_level_dropdown'): + return super()._compute_display_name() + for record in self: + record.display_name = f"{record.name} ({record.level_progress}%)" + + @api.model_create_multi + def create(self, vals_list): + skill_levels = super().create(vals_list) + for level in skill_levels: + if level.default_level: + level.skill_type_id.skill_level_ids.filtered(lambda r: r.id != level.id).default_level = False + return skill_levels + + def write(self, vals): + res = super().write(vals) + if vals.get('default_level'): + self.skill_type_id.skill_level_ids.filtered(lambda r: r.id != self.id).default_level = False + return res diff --git a/partner_skills/models/partner_skill_line.py b/partner_skills/models/partner_skill_line.py new file mode 100644 index 0000000..e862465 --- /dev/null +++ b/partner_skills/models/partner_skill_line.py @@ -0,0 +1,72 @@ +from odoo import api, fields, models, _ +from odoo.exceptions import ValidationError + + +class PartnerSkillLine(models.Model): + _name = 'partner.skill.line' + _description = "Skill level for a partner" + _order = "skill_type_id, skill_level_id" + _rec_name = "skill_id" + + partner_id = fields.Many2one('res.partner', required=True, ondelete='cascade') + skill_id = fields.Many2one( + 'partner.skill', compute='_compute_skill_id', store=True, + domain="[('skill_type_id', '=', skill_type_id)]", + readonly=False, required=True, ondelete='cascade') + skill_level_id = fields.Many2one( + 'partner.skill.level', compute='_compute_skill_level_id', + domain="[('skill_type_id', '=', skill_type_id)]", + store=True, readonly=False, required=True, ondelete='cascade') + skill_type_id = fields.Many2one( + 'partner.skill.type', + default=lambda self: self.env['partner.skill.type'].search([], limit=1), + required=True, ondelete='cascade') + level_progress = fields.Integer(related='skill_level_id.level_progress') + color = fields.Integer(related='skill_type_id.color') + + _sql_constraints = [ + ('_unique_skill', 'unique (partner_id, skill_id)', + "Two levels for the same skill is not allowed"), + ] + + @api.constrains('skill_id', 'skill_type_id') + def _check_skill_type(self): + for record in self: + if record.skill_id not in record.skill_type_id.skill_ids: + raise ValidationError(_( + "The skill %(name)s and skill type %(type)s doesn't match", + name=record.skill_id.name, + type=record.skill_type_id.name, + )) + + @api.constrains('skill_type_id', 'skill_level_id') + def _check_skill_level(self): + for record in self: + if record.skill_level_id not in record.skill_type_id.skill_level_ids: + raise ValidationError(_( + "The skill level %(level)s is not valid for skill type: %(type)s", + level=record.skill_level_id.name, + type=record.skill_type_id.name, + )) + + @api.depends('skill_type_id') + def _compute_skill_id(self): + for record in self: + if record.skill_type_id: + record.skill_id = record.skill_type_id.skill_ids[0] if record.skill_type_id.skill_ids else False + else: + record.skill_id = False + + @api.depends('skill_id') + def _compute_skill_level_id(self): + for record in self: + if not record.skill_id: + record.skill_level_id = False + else: + skill_levels = record.skill_type_id.skill_level_ids + record.skill_level_id = skill_levels.filtered('default_level') or skill_levels[0] if skill_levels else False + + @api.depends('skill_id', 'skill_level_id') + def _compute_display_name(self): + for record in self: + record.display_name = f"{record.skill_id.name}: {record.skill_level_id.name}" diff --git a/partner_skills/models/partner_skill_type.py b/partner_skills/models/partner_skill_type.py new file mode 100644 index 0000000..39252bb --- /dev/null +++ b/partner_skills/models/partner_skill_type.py @@ -0,0 +1,21 @@ +from random import randint +from odoo import fields, models + + +class PartnerSkillType(models.Model): + _name = 'partner.skill.type' + _description = "Partner Skill Type" + _order = "name" + + def _get_default_color(self): + return randint(1, 11) + + active = fields.Boolean('Active', default=True) + name = fields.Char(required=True, translate=True) + skill_ids = fields.One2many('partner.skill', 'skill_type_id', string="Skills") + skill_level_ids = fields.One2many('partner.skill.level', 'skill_type_id', string="Levels", copy=True) + color = fields.Integer('Color', default=_get_default_color) + + def copy_data(self, default=None): + vals_list = super().copy_data(default=default) + return [dict(vals, name=self.env._("%s (copy)", skill_type.name), color=0) for skill_type, vals in zip(self, vals_list)] diff --git a/partner_skills/models/res_partner.py b/partner_skills/models/res_partner.py new file mode 100644 index 0000000..a0e4a8e --- /dev/null +++ b/partner_skills/models/res_partner.py @@ -0,0 +1,16 @@ +from odoo import api, fields, models + + +class ResPartner(models.Model): + _inherit = 'res.partner' + + partner_skill_ids = fields.One2many( + 'partner.skill.line', 'partner_id', string="Skills", + domain=[('skill_type_id.active', '=', True)]) + skill_ids = fields.Many2many( + 'partner.skill', compute='_compute_skill_ids', store=True) + + @api.depends('partner_skill_ids.skill_id') + def _compute_skill_ids(self): + for partner in self: + partner.skill_ids = partner.partner_skill_ids.skill_id diff --git a/partner_skills/security/ir.model.access.csv b/partner_skills/security/ir.model.access.csv new file mode 100644 index 0000000..1e5b37a --- /dev/null +++ b/partner_skills/security/ir.model.access.csv @@ -0,0 +1,5 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_partner_skill_type,partner.skill.type,model_partner_skill_type,base.group_user,1,1,1,1 +access_partner_skill,partner.skill,model_partner_skill,base.group_user,1,1,1,1 +access_partner_skill_level,partner.skill.level,model_partner_skill_level,base.group_user,1,1,1,1 +access_partner_skill_line,partner.skill.line,model_partner_skill_line,base.group_user,1,1,1,1 diff --git a/partner_skills/static/src/fields/partner_skills_one2many/partner_skills_one2many.js b/partner_skills/static/src/fields/partner_skills_one2many/partner_skills_one2many.js new file mode 100644 index 0000000..37ba320 --- /dev/null +++ b/partner_skills/static/src/fields/partner_skills_one2many/partner_skills_one2many.js @@ -0,0 +1,86 @@ +/** @odoo-module */ + +import { X2ManyField, x2ManyField } from "@web/views/fields/x2many/x2many_field"; +import { + useX2ManyCrud, + useOpenX2ManyRecord, +} from "@web/views/fields/relational_utils"; +import { registry } from "@web/core/registry"; +import { _t } from "@web/core/l10n/translation"; +import { PartnerSkillsListRenderer } from "../../views/partner_skills_list_renderer"; +import { useService } from "@web/core/utils/hooks"; +import { onWillStart } from "@odoo/owl"; + + +export class PartnerSkillsListRendererField extends PartnerSkillsListRenderer { + static template = "partner_skills.SkillsListRenderer"; + setup() { + super.setup(); + this.orm = useService("orm"); + this.actionService = useService("action"); + + onWillStart(async () => { + const res = await this.orm.searchCount("partner.skill.type", []); + this.anySkills = res > 0; + }); + } + + get groupBy() { + return "skill_type_id"; + } + + async skillTypesAction() { + return this.actionService.doAction("partner_skills.partner_skill_type_action"); + } +} + +export class PartnerSkillsX2ManyField extends X2ManyField { + static components = { + ...X2ManyField.components, + ListRenderer: PartnerSkillsListRendererField, + }; + setup() { + super.setup(); + const { saveRecord, updateRecord } = useX2ManyCrud( + () => this.list, + this.isMany2Many + ); + + const openRecord = useOpenX2ManyRecord({ + resModel: this.list.resModel, + activeField: this.activeField, + activeActions: this.activeActions, + getList: () => this.list, + saveRecord: async (record) => { + await saveRecord(record); + await this.props.record.save({ + onError: (e) => { this.list.delete(record); throw e; } + }); + }, + updateRecord: updateRecord, + withParentId: this.props.widget !== "many2many", + }); + + this._openRecord = (params) => { + params.title = _t("Select Skills"); + openRecord({ ...params }); + }; + } + + async onAdd({ context, editable } = {}) { + return super.onAdd({ + editable, + context: { + ...context, + default_partner_id: this.props.record.resId, + }, + }); + } +} + +export const partnerSkillsX2ManyField = { + ...x2ManyField, + component: PartnerSkillsX2ManyField, +}; + +registry.category("fields").add("partner_skills_one2many", partnerSkillsX2ManyField); diff --git a/partner_skills/static/src/fields/partner_skills_one2many/partner_skills_one2many.scss b/partner_skills/static/src/fields/partner_skills_one2many/partner_skills_one2many.scss new file mode 100644 index 0000000..d05d1f8 --- /dev/null +++ b/partner_skills/static/src/fields/partner_skills_one2many/partner_skills_one2many.scss @@ -0,0 +1,27 @@ +.o_field_partner_skills_one2many { + .o_progress { + background: $gray-300; + border: 0; + height: 5px; + } + + .o_progressbar_value { + font-size: $font-size-sm; + font-weight: bold; + } +} + +table.o_partner_skill_table { + .o_partner_skills_dialog_form { + .o_progressbar { + display: flex; + align-items: center; + + .o_progressbar_value input { + width: auto; + } + } + } + + table-layout: fixed; +} diff --git a/partner_skills/static/src/fields/partner_skills_one2many/partner_skills_one2many.xml b/partner_skills/static/src/fields/partner_skills_one2many/partner_skills_one2many.xml new file mode 100644 index 0000000..3d41c62 --- /dev/null +++ b/partner_skills/static/src/fields/partner_skills_one2many/partner_skills_one2many.xml @@ -0,0 +1,65 @@ + + + + + + + + visibility: collapse; + + +
+ Skills +
+
+ + + +
+

+ There are no skills defined in the library.
+ Why not try adding some? +

+ +
+
+ +
+

You can add skills to this contact's profile.

+
+ +
+
+
+
+
+
+ + + + + +
+ + +
+ + + + + + + + +
diff --git a/partner_skills/static/src/views/partner_skills_list_renderer.js b/partner_skills/static/src/views/partner_skills_list_renderer.js new file mode 100644 index 0000000..b7b1659 --- /dev/null +++ b/partner_skills/static/src/views/partner_skills_list_renderer.js @@ -0,0 +1,53 @@ +/** @odoo-module */ + +import { _t } from "@web/core/l10n/translation"; +import { ListRenderer } from "@web/views/list/list_renderer"; + +export class PartnerSkillsListRenderer extends ListRenderer { + get colspan() { + const span = this.allColumns.length; + if (this.isEditable) { + return span + 1; + } + return span; + } + + get groupBy() { + return 'skill_type_id'; + } + + get groupedList() { + const grouped = {}; + for (const record of this.list.records) { + const data = record.data; + const group = data[this.groupBy]; + if (grouped[group[1]] === undefined) { + grouped[group[1]] = { + id: parseInt(group[0]), + name: group[1] || _t('Other'), + list: { + records: [], + }, + }; + } + grouped[group[1]].list.records.push(record); + } + return grouped; + } + + get showTable() { + return this.props.list.records.length; + } + + get isEditable() { + return this.props.editable !== false; + } + + async onCellClicked(record, column, ev) { + if (!this.isEditable) { + return; + } + return await super.onCellClicked(record, column, ev); + } +} +PartnerSkillsListRenderer.rowsTemplate = "partner_skills.SkillsListRenderer.Rows"; diff --git a/partner_skills/views/partner_skill_type_views.xml b/partner_skills/views/partner_skill_type_views.xml new file mode 100644 index 0000000..9deec3a --- /dev/null +++ b/partner_skills/views/partner_skill_type_views.xml @@ -0,0 +1,195 @@ + + + + + + partner.skill.level.view.tree + partner.skill.level + + + + + + + + + + + + + partner.skill.level.view.form + partner.skill.level + +
+ + + + + + +
+
+
+ + + + partner.skill.view.tree + partner.skill + + + + + + + + + + + + partner.skill.view.search + partner.skill + + + + + + + + + + + + + + partner.skill.type.view.search + partner.skill.type + + + + + + + + + + + + + partner.skill.type.view.tree + partner.skill.type + + + + + + + + + + + + + partner.skill.type.view.form + partner.skill.type + +
+ + +
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + + partner.skill.line.view.form + partner.skill.line + +
+ + + + + + + + + + + +
+
+
+ + + + Skill Types + partner.skill.type + list,form + +

+ Create a new skill type +

+
+
+ + + + + +
\ No newline at end of file diff --git a/partner_skills/views/res_partner_views.xml b/partner_skills/views/res_partner_views.xml new file mode 100644 index 0000000..2498138 --- /dev/null +++ b/partner_skills/views/res_partner_views.xml @@ -0,0 +1,37 @@ + + + + + res.partner.view.form.inherit.skills + res.partner + + + + +
+ + + + + + + + +
+
+
+
+
+ + + res.partner.view.search.inherit.skills + res.partner + + + + + + + + +