diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/hr_skills/static | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/hr_skills/static')
| -rw-r--r-- | addons/hr_skills/static/description/icon.png | bin | 0 -> 7646 bytes | |||
| -rw-r--r-- | addons/hr_skills/static/description/icon.svg | 24 | ||||
| -rw-r--r-- | addons/hr_skills/static/src/css/hr_skills.scss | 152 | ||||
| -rw-r--r-- | addons/hr_skills/static/src/js/resume_widget.js | 240 | ||||
| -rw-r--r-- | addons/hr_skills/static/src/xml/resume_templates.xml | 55 | ||||
| -rw-r--r-- | addons/hr_skills/static/src/xml/skills_templates.xml | 24 | ||||
| -rw-r--r-- | addons/hr_skills/static/tests/widget_tests.js | 328 |
7 files changed, 823 insertions, 0 deletions
diff --git a/addons/hr_skills/static/description/icon.png b/addons/hr_skills/static/description/icon.png Binary files differnew file mode 100644 index 00000000..845e53c8 --- /dev/null +++ b/addons/hr_skills/static/description/icon.png diff --git a/addons/hr_skills/static/description/icon.svg b/addons/hr_skills/static/description/icon.svg new file mode 100644 index 00000000..753554b0 --- /dev/null +++ b/addons/hr_skills/static/description/icon.svg @@ -0,0 +1,24 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="70" height="70" viewBox="0 0 70 70"> + <defs> + <path id="icon-a" d="M4,5.35309892e-14 C36.4160122,9.87060235e-15 58.0836068,-3.97961823e-14 65,5.07020818e-14 C69,6.733808e-14 70,1 70,5 C70,43.0488877 70,62.4235458 70,65 C70,69 69,70 65,70 C61,70 9,70 4,70 C1,70 7.10542736e-15,69 7.10542736e-15,65 C7.25721566e-15,62.4676575 3.83358709e-14,41.8005206 3.60818146e-14,5 C-1.13686838e-13,1 1,5.75716207e-14 4,5.35309892e-14 Z"/> + <linearGradient id="icon-c" x1="100%" x2="0%" y1="0%" y2="100%"> + <stop offset="0%" stop-color="#269396"/> + <stop offset="100%" stop-color="#218689"/> + </linearGradient> + <path id="icon-d" d="M35.5,22.75 C39.6529297,22.75 43.0195312,26.1166016 43.0195312,30.2695312 C43.0195312,34.4224609 39.6529297,37.7890625 35.5,37.7890625 C31.3470703,37.7890625 27.9804687,34.4224609 27.9804687,30.2695312 C27.9804687,26.1166016 31.3470703,22.75 35.5,22.75 Z M43.6256055,38.3165755 L40.7623112,37.6007161 C37.2411654,40.1333659 32.9730794,39.5681836 30.2377604,37.6007161 L27.3744661,38.3165755 C25.0790039,38.8904232 23.46875,40.9527799 23.46875,43.3188542 L23.46875,47.671875 C23.46875,49.0957161 24.6230339,50.25 26.046875,50.25 L44.953125,50.25 C46.3769661,50.25 47.53125,49.0957161 47.53125,47.671875 L47.53125,43.3188542 C47.53125,40.9527799 45.9209961,38.8904232 43.6256055,38.3165755 Z M50.3958333,39.6510417 C53.1644531,39.6510417 55.4088542,37.4066406 55.4088542,34.6380208 C55.4088542,31.869401 53.1644531,29.625 50.3958333,29.625 C47.6272135,29.625 45.3828125,31.869401 45.3828125,34.6380208 C45.3828125,37.4066406 47.6272135,39.6510417 50.3958333,39.6510417 Z M20.6041667,39.6510417 C23.3727865,39.6510417 25.6171875,37.4066406 25.6171875,34.6380208 C25.6171875,31.869401 23.3727865,29.625 20.6041667,29.625 C17.8355469,29.625 15.5911458,31.869401 15.5911458,34.6380208 C15.5911458,37.4066406 17.8355469,39.6510417 20.6041667,39.6510417 Z M22.3229167,47.671875 L22.3229167,43.3188542 C22.3229167,42.1335612 22.6518424,41.0125781 23.2326367,40.0533008 C21.0850586,41.1074674 18.6968555,40.6769206 17.0959831,39.5255013 L15.1870964,40.0027409 C13.6568359,40.3852344 12.5833333,41.7602344 12.5833333,43.3375456 L12.5833333,46.2395833 C12.5833333,47.1888346 13.352832,47.9583333 14.3020833,47.9583333 L22.3350195,47.9583333 C22.3273388,47.8630362 22.3233016,47.7674804 22.3229167,47.671875 Z M55.8129036,40.0026693 L53.9040169,39.5254297 C51.9041797,40.9638802 49.5434049,40.902793 47.7604883,40.0423437 C48.3454362,41.004056 48.6770833,42.1289779 48.6770833,43.3188542 L48.6770833,47.671875 C48.6770833,47.7683398 48.6722135,47.8636589 48.6649805,47.9583333 L56.6979167,47.9583333 C57.647168,47.9583333 58.4166667,47.1888346 58.4166667,46.2395833 L58.4166667,43.3375456 C58.4166667,41.7602344 57.3431641,40.3852344 55.8129036,40.0026693 Z"/> + <path id="icon-e" d="M35.5,20.75 C39.6529297,20.75 43.0195312,24.1166016 43.0195312,28.2695312 C43.0195312,32.4224609 39.6529297,35.7890625 35.5,35.7890625 C31.3470703,35.7890625 27.9804687,32.4224609 27.9804687,28.2695312 C27.9804687,24.1166016 31.3470703,20.75 35.5,20.75 Z M43.6256055,36.3165755 L40.7623112,35.6007161 C37.2411654,38.1333659 32.9730794,37.5681836 30.2377604,35.6007161 L27.3744661,36.3165755 C25.0790039,36.8904232 23.46875,38.9527799 23.46875,41.3188542 L23.46875,45.671875 C23.46875,47.0957161 24.6230339,48.25 26.046875,48.25 L44.953125,48.25 C46.3769661,48.25 47.53125,47.0957161 47.53125,45.671875 L47.53125,41.3188542 C47.53125,38.9527799 45.9209961,36.8904232 43.6256055,36.3165755 Z M50.3958333,37.6510417 C53.1644531,37.6510417 55.4088542,35.4066406 55.4088542,32.6380208 C55.4088542,29.869401 53.1644531,27.625 50.3958333,27.625 C47.6272135,27.625 45.3828125,29.869401 45.3828125,32.6380208 C45.3828125,35.4066406 47.6272135,37.6510417 50.3958333,37.6510417 Z M20.6041667,37.6510417 C23.3727865,37.6510417 25.6171875,35.4066406 25.6171875,32.6380208 C25.6171875,29.869401 23.3727865,27.625 20.6041667,27.625 C17.8355469,27.625 15.5911458,29.869401 15.5911458,32.6380208 C15.5911458,35.4066406 17.8355469,37.6510417 20.6041667,37.6510417 Z M22.3229167,45.671875 L22.3229167,41.3188542 C22.3229167,40.1335612 22.6518424,39.0125781 23.2326367,38.0533008 C21.0850586,39.1074674 18.6968555,38.6769206 17.0959831,37.5255013 L15.1870964,38.0027409 C13.6568359,38.3852344 12.5833333,39.7602344 12.5833333,41.3375456 L12.5833333,44.2395833 C12.5833333,45.1888346 13.352832,45.9583333 14.3020833,45.9583333 L22.3350195,45.9583333 C22.3273388,45.8630362 22.3233016,45.7674804 22.3229167,45.671875 Z M55.8129036,38.0026693 L53.9040169,37.5254297 C51.9041797,38.9638802 49.5434049,38.902793 47.7604883,38.0423437 C48.3454362,39.004056 48.6770833,40.1289779 48.6770833,41.3188542 L48.6770833,45.671875 C48.6770833,45.7683398 48.6722135,45.8636589 48.6649805,45.9583333 L56.6979167,45.9583333 C57.647168,45.9583333 58.4166667,45.1888346 58.4166667,44.2395833 L58.4166667,41.3375456 C58.4166667,39.7602344 57.3431641,38.3852344 55.8129036,38.0026693 Z"/> + </defs> + <g fill="none" fill-rule="evenodd"> + <mask id="icon-b" fill="#fff"> + <use xlink:href="#icon-a"/> + </mask> + <g mask="url(#icon-b)"> + <rect width="70" height="70" fill="url(#icon-c)"/> + <path fill="#FFF" fill-opacity=".383" d="M4,1.8 L65,1.8 C67.6666667,1.8 69.3333333,1.13333333 70,-0.2 C70,2.46666667 70,3.46666667 70,2.8 L1.10547097e-14,2.8 C-1.65952376e-14,3.46666667 -2.9161925e-14,2.46666667 -2.66453526e-14,-0.2 C0.666666667,1.13333333 2,1.8 4,1.8 Z" transform="matrix(1 0 0 -1 0 2.8)"/> + <path fill="#393939" d="M44,47 L4,47 C2,47 -7.10542736e-15,46.8509317 0,42.826087 L1.81527147e-16,22.6291049 L17.2090667,6.04664397 L19.583071,9.5209307 L30.2767729,0.11143939 L40.9146315,10.270152 L46.6446282,6.41116033 L55.3045682,10.7749724 L52.3812234,16.1277957 L58.2417324,21.9036543 L44,47 Z" opacity=".324" transform="translate(0 23)"/> + <path fill="#000" fill-opacity=".383" d="M4,4 L65,4 C67.6666667,4 69.3333333,3 70,1 C70,3.66666667 70,5 70,5 L1.77635684e-15,5 C1.77635684e-15,5 1.77635684e-15,3.66666667 1.77635684e-15,1 C0.666666667,3 2,4 4,4 Z" transform="translate(0 65)"/> + <use fill="#000" fill-rule="nonzero" opacity=".3" xlink:href="#icon-d"/> + <use fill="#FFF" fill-rule="nonzero" xlink:href="#icon-e"/> + </g> + </g> +</svg> diff --git a/addons/hr_skills/static/src/css/hr_skills.scss b/addons/hr_skills/static/src/css/hr_skills.scss new file mode 100644 index 00000000..9ff24615 --- /dev/null +++ b/addons/hr_skills/static/src/css/hr_skills.scss @@ -0,0 +1,152 @@ +.o_form_sheet { + $o-hrs-timeline-entry-padding: .5rem; + $o-hrs-timeline-dot-size: .6rem; + + // Overall design + // ========================================= + .o_hr_skills_group { + // Overwrite '.o_list_table' default design + .o_list_view { + .o_list_table { + cursor: auto; + } + + tbody:first-of-type > .o_resume_group_header:first-child { + box-shadow: none; + } + } + + // Use 'cursor:pointer' where is needed only + .o_data_row { + cursor: pointer; + } + + // Deny user interaction to headers but keep access to buttons + .o_group_header, .o_resume_group_header { + &, &:hover { + background: none !important; + cursor: initial; + pointer-events: none; + } + .o_field_x2many_list_row_add, .o_field_x2many_list_row_add:hover { + cursor: pointer; + pointer-events: initial; + } + } + } + + // Resumé design + // ========================================= + .o_group_resume { + .o_data_row td { + padding: $o-hrs-timeline-entry-padding; + + &.o_resume_timeline_cell { + div { + @include size($o-hrs-timeline-dot-size); + } + + &:before { + @include o-position-absolute(0, $left: ($o-hrs-timeline-dot-size * .5 + $o-hrs-timeline-entry-padding)); + @include size(1px, 100%); + margin-left: -.01rem; + background-color: $border-color; + content: ""; + } + } + } + + .o_resume_line_title, .o_resume_line_desc { + white-space: normal; + } + + .o_resume_line_title, .o_resume_line_dates { + line-height: 1; + } + + .o_resume_group_header + .o_data_row .o_resume_timeline_cell:before { + top: $o-hrs-timeline-entry-padding; + } + + .o_data_row.o_data_row_last { + .o_resume_line_desc { + margin-bottom: $headings-margin-bottom; + } + + .o_resume_timeline_cell:before { + height: $o-hrs-timeline-entry-padding; + } + } + } + + // Skills design + // ========================================= + .o_group_skills { + .o_resume_empty_helper { + display: none; + } + + .o_group_header { + > .o_group_name { + padding: .8rem .5rem 0 0; + } + + &:first-child > .o_group_name { + padding-top: 0; + } + } + + .o_skill_cell { + padding-left: 0; + white-space: normal !important; + + .o_progressbar { + display: inline-flex; + align-items: center; + } + + .o_progress { + border: 0; + background: $gray-300; + height: 5px; + } + + .o_progressbar_value { + width: auto; + font-size: $font-size-sm; + font-weight: bold; + } + } + } + + // Editing mode + // ========================================= + .o_form_view.o_form_editable & { + .o_group_name { + background-color: gray('200'); + } + + .o_resume_group_header .btn { + margin-top: .25rem; + margin-right: .4rem; + } + + .o_group_skills .o_group_name { + padding-top: .4em; + padding-bottom: .4rem; + } + + .o_group_name, .o_skill_cell { + padding-left: .5rem; + } + + .o_group_skills .o_group_name > b, .o_hr_skills_group .o_horizontal_separator { + color: color-yiq(gray('200')); + font-style: italic; + } + + .o_list_record_remove > button { + @include o-hover-text-color($text-muted, theme-color('danger')); + } + } +} diff --git a/addons/hr_skills/static/src/js/resume_widget.js b/addons/hr_skills/static/src/js/resume_widget.js new file mode 100644 index 00000000..413942ba --- /dev/null +++ b/addons/hr_skills/static/src/js/resume_widget.js @@ -0,0 +1,240 @@ +odoo.define('web.FieldResume', function (require) { +"use strict"; + +var time = require('web.time'); +var FieldOne2Many = require('web.relational_fields').FieldOne2Many; +var FieldProgressBar = require('web.basic_fields').FieldProgressBar; +var ListRenderer = require('web.ListRenderer'); +var field_registry = require('web.field_registry'); + +var core = require('web.core'); +var qweb = core.qweb; +var _t = core._t; + +var AbstractGroupedOne2ManyRenderer = ListRenderer.extend({ + /** + * This abstract renderer is use to render a one2many field in a form view. + * The records in the one2many field are displayed grouped by a specific field. + * + * A concrete renderer can/should set: + * - groupBy: field to group records + * - dataRowTemplate: template to render a record's data + * - groupTitleTemplate (optional): template to render the header row of a group + * - addLineButtonTemplate (optional): template to render the 'Add a line' button at the end of each group (edit mode only) + **/ + + groupBy: '', // Field: records are grouped based on this field + groupTitleTemplate: 'hr_default_group_row', // Template used to render the title row of a group + dataRowTemplate: '', // Template used to render a record + addLineButtonTemplate: 'group_add_item', + + /** + * Don't freeze the columns because as the header is empty, the algorithm + * won't work. + * + * @override + * @private + */ + _freezeColumnWidths: function () {}, + + /** + * Renders a empty header + * + * @override + * @private + */ + _renderHeader: function () { + return $('<thead/>'); + }, + + /** + * Renders a empty footer + * + * @override + * @private + */ + _renderFooter: function () { + return $('<tfoot/>'); + }, + + /** + * @override + * @private + */ + _renderGroupRow: function (display_name) { + return qweb.render(this.groupTitleTemplate, {display_name: display_name}); + }, + + /** + * This method is meant to be overriten by concrete renderers and + * is called each time a row is rendered. + * It is a hook to format record's data before it's given to the qweb template. + * + * @private + */ + _formatData: function (data) { + return data; + }, + + _renderRow: function (record, isLast) { + return $(qweb.render(this.dataRowTemplate, { + id: record.id, + data: this._formatData(record.data), + is_last: isLast, + })); + }, + + /** + * This method is meant to be overridden by concrete renderers. + * Returns a context used for the 'Add a line' button. + * It's useful to set default values. + * An 'Add a line' button is added after each group of records. + * The group passed as parameters allow to set a different context based on the group. + * If no records exist, group is undefined. + * + * @private + */ + _getCreateLineContext: function (group) { + return {}; + }, + + _renderTrashIcon: function() { + return qweb.render('hr_trash_button'); + }, + + _renderAddItemButton: function (group) { + return qweb.render(this.addLineButtonTemplate, { + context: JSON.stringify(this._getCreateLineContext(group)), + }); + }, + + _renderBody: function () { + var self = this; + + var grouped_by = _.groupBy(this.state.data, function (record) { + return record.data[self.groupBy].res_id; + }); + + var groupTitle; + var $body = $('<tbody>'); + for (var key in grouped_by) { + var group = grouped_by[key]; + if (key === 'undefined') { + groupTitle = _t("Other"); + } else { + groupTitle = group[0].data[self.groupBy].data.display_name; + } + var $title_row = $(self._renderGroupRow(groupTitle)); + $body.append($title_row); + + // Render each rows + group.forEach(function (record, index) { + var isLast = (index + 1 === group.length); + var $row = self._renderRow(record, isLast); + if (self.addTrashIcon) $row.append(self._renderTrashIcon()); + $body.append($row); + }); + + if (self.addCreateLine) { + $title_row.find('.o_group_name').append(self._renderAddItemButton(group)); + } + } + + if ($body.is(':empty') && self.addCreateLine) { + $body.append(this._renderAddItemButton()); + } + return $body; + }, + +}); + +var ResumeLineRenderer = AbstractGroupedOne2ManyRenderer.extend({ + + groupBy: 'line_type_id', + groupTitleTemplate: 'hr_resume_group_row', + dataRowTemplate: 'hr_resume_data_row', + + _formatData: function (data) { + var dateFormat = time.getLangDateFormat(); + var date_start = data.date_start && data.date_start.format(dateFormat) || ""; + var date_end = data.date_end && data.date_end.format(dateFormat) || _t("Current"); + return _.extend(data, { + date_start: date_start, + date_end: date_end, + }); + }, + + _getCreateLineContext: function (group) { + var ctx = this._super(group); + return group ? _.extend({default_line_type_id: group[0].data[this.groupBy] && group[0].data[this.groupBy].data.id || ""}, ctx) : ctx; + }, + + _render: function () { + var self = this; + return this._super().then(function () { + self.$el.find('table').removeClass('table-striped o_list_table_ungrouped'); + self.$el.find('table').addClass('o_resume_table table-borderless'); + }); + }, +}); + + +var SkillsRenderer = AbstractGroupedOne2ManyRenderer.extend({ + + groupBy: 'skill_type_id', + dataRowTemplate: 'hr_skill_data_row', + + _renderRow: function (record) { + var $row = this._super(record); + // Add progress bar widget at the end of rows + var $td = $('<td/>', {class: 'o_data_cell o_skill_cell'}); + var progress = new FieldProgressBar(this, 'level_progress', record, { + current_value: record.data.level_progress, + attrs: this.arch.attrs, + }); + progress.appendTo($td); + return $row.append($td); + }, + + _getCreateLineContext: function (group) { + var ctx = this._super(group); + return group ? _.extend({ default_skill_type_id: group[0].data[this.groupBy].data.id }, ctx) : ctx; + }, + + _render: function () { + var self = this; + return this._super().then(function () { + self.$el.find('table').toggleClass('table-striped'); + }); + }, +}); + + +var FieldResume = FieldOne2Many.extend({ + + /** + * @override + * @private + */ + _getRenderer: function () { + return ResumeLineRenderer; + }, +}); + +var FieldSkills = FieldOne2Many.extend({ + + /** + * @override + * @private + */ + _getRenderer: function () { + return SkillsRenderer; + }, +}); + +field_registry.add('hr_resume', FieldResume); +field_registry.add('hr_skills', FieldSkills); + +return FieldResume; + +}); diff --git a/addons/hr_skills/static/src/xml/resume_templates.xml b/addons/hr_skills/static/src/xml/resume_templates.xml new file mode 100644 index 00000000..5db3b006 --- /dev/null +++ b/addons/hr_skills/static/src/xml/resume_templates.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<templates id="template" xml:space="preserve"> + +<t t-name="hr_resume_data_row"> + <tr class="o_data_row" t-attf-class="o_data_row #{is_last? 'o_data_row_last' : ''}" t-att-data-id="id"> + <t t-if="data.display_type === 'classic'"> + <td class="o_resume_timeline_cell position-relative pr-lg-2"> + <div class="rounded-circle bg-info position-relative"/> + </td> + <td class="o_data_cell pt-0 w-100"> + <div class="o_resume_line" t-att-data-id="id"> + <small class="o_resume_line_dates"> + <b t-esc="data.date_start"/> - <b t-esc="data.date_end"/> + </small> + <h4 class="o_resume_line_title mt-2" t-esc="data.name"/> + <p t-if="data.description" class="o_resume_line_desc" t-esc="data.description"/> + </div> + </td> + </t> + </tr> +</t> + +<t t-name="hr_trash_button"> + <td class="o_list_record_remove pr-3"> + <button name="delete" arial-label="Delete row" class="btn btn-secondary"> + <i class="fa fa-trash"/> + </button> + </td> +</t> + +<t t-name="hr_resume_group_row"> + <tr class="o_resume_group_header"> + <td class="o_group_name" colspan="100%"><span class="o_horizontal_separator my-0" t-esc="display_name"/></td> + </tr> +</t> + +<t t-name="group_add_item"> + <t t-set="empty" t-value="Object.keys(context).length == 2"/> + + <div t-attf-class="o_field_x2many_list_row_add #{empty? 'd-block w-100' : 'd-inline pull-right'}"> + <div t-if="empty" class="o_resume_empty_helper o_horizontal_separator text-muted my-0"> + <em>Resumé empty</em> + </div> + <a href="#" + role="button" + t-attf-class="btn o-kanban-button-new #{empty? 'btn-primary mt-3' : 'btn-secondary btn-sm'}" + t-attf-data-context="{{ context }}"> + <t t-if="empty">CREATE A NEW ENTRY</t> + <t t-else="">ADD</t> + </a> + </div> +</t> + + +</templates> diff --git a/addons/hr_skills/static/src/xml/skills_templates.xml b/addons/hr_skills/static/src/xml/skills_templates.xml new file mode 100644 index 00000000..e078ca3e --- /dev/null +++ b/addons/hr_skills/static/src/xml/skills_templates.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<templates id="template" xml:space="preserve"> + +<t t-name="hr_skill_data_row"> + <tr class="o_data_row" t-att-data-id="id"> + <td class="o_data_cell o_skill_cell w-100"> + <t t-esc="data.skill_id.data.display_name"/> + </td> + <td class="o_data_cell o_skill_cell pr-3"> + <t t-esc="data.skill_level_id.data.display_name"/> + </td> + </tr> +</t> + +<t t-name="hr_default_group_row"> + <tr class="o_group_header o_group_has_content"> + <td class="o_group_name border-0 pr-2" colspan="99"> + <b t-esc="display_name"/> + </td> + </tr> +</t> + + +</templates> diff --git a/addons/hr_skills/static/tests/widget_tests.js b/addons/hr_skills/static/tests/widget_tests.js new file mode 100644 index 00000000..c8d417ec --- /dev/null +++ b/addons/hr_skills/static/tests/widget_tests.js @@ -0,0 +1,328 @@ +odoo.define('hr_skills.field_one_to_many_group_tests', function (require) { + "use strict"; + + var FormView = require('web.FormView'); + var testUtils = require('web.test_utils'); + + var createView = testUtils.createView; + + QUnit.module('skills_widgets', { + beforeEach: function () { + this.data = { + partner: { + fields: { + display_name: { string: "Displayed name", type: "char" }, + line_ids: { string: "one2many field", type: "one2many", relation: 'line', relation_field: 'trululu' }, + skill_ids: { string: "one2many field", type: "one2many", relation: 'partner_skill', relation_field: 'partner_id' }, + }, + records: [{ + id: 1, + display_name: "first record", + line_ids: [37, 38, 39], + skill_ids: [75, 76, 77] + }], + onchanges: {}, + }, + partner_skill: { + fields: { + skill_id: { string: "Name", type: "many2one", relation: 'skill'}, + skill_type_id: { string: "Type", type: "many2one", relation: 'skill_type' }, + skill_level_id: { string: "Level", type: "many2one", relation: 'skill_level' }, + level_progress: { string: "Progress", type: "int" }, + }, + records: [{ + id: 75, + skill_id: 444, + skill_type_id: 221, + skill_level_id: 112, + level_progress: 50, + }, { + id: 76, + skill_id: 445, + skill_type_id: 222, + skill_level_id: 111, + level_progress: 50, + }, { + id: 77, + skill_id: 446, + skill_type_id: 222, + skill_level_id: 111, + level_progress: 70, + }], + }, + skill: { + fields: { + name: { string: "Name", type: "char" }, + }, + records: [ + { id: 444, name: 'Python' }, + { id: 445, name: 'Piano' }, + { id: 446, name: 'Flute' }, + ] + }, + skill_level: { + fields: { + name: { string: "Name", type: "char" }, + }, + records: [ + { id: 111, name: 'L1' }, + { id: 112, name: 'Intermediate' } + ] + }, + skill_type: { + fields: { + name: { string: "Name", type: "char" }, + }, + records: [ + { id: 221, name: 'Dev' }, + { id: 222, name: 'Music' }, + ] + }, + line: { + fields: { + name: { string: "Name", type: "char" }, + line_type_id: { string: "Type", relation: 'line_type', type: "many2one" }, + description: { string: "Description", type: "text" }, + date_start: { string: "Date start", type: "date" }, + date_end: { string: "Date end", type: "date" }, + trululu: { string: "Trululu", type: "many2one", relation: 'partner' }, + display_type: { string: "display type", type: "selection"}, + }, + records: [{ + id: 37, + name: "ULB", + line_type_id: 50, + date_start: "2017-01-25", + date_end: "2019-01-25", + description: 'Hello', + trululu: 1, + display_type: 'classic', + }, { + id: 38, + name: "UCL", + line_type_id: 50, + date_start: "2013-01-25", + date_end: "2014-01-25", + description: 'World', + trululu: 1, + display_type: 'classic', + }, { + id: 39, + name: "KUL", + line_type_id: 51, + date_start: "2008-01-25", + description: 'Hi', + trululu: 1, + display_type: 'classic', + }], + onchanges: {}, + }, + line_type: { + fields: { + name: { string: "Name", type: "char" }, + }, + records: [{ + id: 50, + name: 'AAA', + }, { + id: 51, + name: 'BBB' + }], + } + }; + } + }, function () { + QUnit.test('resumé one2many field group by field render', async function (assert) { + assert.expect(16); + var form = await createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '<form string="Partners">' + + '<field name="line_ids" widget="hr_resume">' + + '<tree>' + + '<field name="name"/>' + + '<field name="line_type_id"/>' + + '<field name="description"/>' + + '<field name="date_start"/>' + + '<field name="date_end"/>' + + '<field name="display_type"/>' + + '</tree>' + + '</field>' + + '</form>', + res_id: 1, + }); + var $headers = form.$('.o_resume_group_header'); + assert.strictEqual($headers.length, 2, 'There should be 2 headers'); + assert.strictEqual($headers.find('td:contains(AAA)').length, 1, "it should have line type AAA"); + assert.strictEqual($headers.find('td:contains(BBB)').length, 1, "it should have line type BBB"); + + var dataRows = form.$('.o_data_row'); + assert.strictEqual(dataRows.length, 3, 'There should be 3 data rows'); + + var $row = $(dataRows[0]); + assert.strictEqual($row.find('td:contains(01/25/2017)').length, 1, "it should have start date 01/25/2017"); + assert.strictEqual($row.find('td:contains(01/25/2019)').length, 1, "it should have end date 01/25/2019"); + assert.strictEqual($row.find('td:contains(ULB)').length, 1, "it should have line name ULB"); + assert.strictEqual($row.find('td:contains(Hello)').length, 1, "it should have line description Hello"); + + $row = $(dataRows[1]); + assert.strictEqual($row.find('td:contains(01/25/2013)').length, 1, "it should have start date 01/25/2013"); + assert.strictEqual($row.find('td:contains(01/25/2014)').length, 1, "it should have end date 01/25/2014"); + assert.strictEqual($row.find('td:contains(UCL)').length, 1, "it should have line name UCL"); + assert.strictEqual($row.find('td:contains(World)').length, 1, "it should have line description World"); + + $row = $(dataRows[2]); + assert.strictEqual($row.find('td:contains(01/25/2008)').length, 1, "it should have start date 01/25/2008"); + assert.strictEqual($row.find('td:contains(Current)').length, 1, "it should have end date Current"); + assert.strictEqual($row.find('td:contains(KUL)').length, 1, "it should have line name KUL"); + assert.strictEqual($row.find('td:contains(Hi)').length, 1, "it should have line description Hi"); + + form.destroy(); + }); + QUnit.test('resumé one2many field group by field create', async function (assert) { + assert.expect(5); + var form = await createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '<form string="Partners">' + + '<field name="line_ids" widget="hr_resume">' + + '<tree>' + + '<field name="name"/>' + + '<field name="line_type_id"/>' + + '<field name="description"/>' + + '<field name="date_start"/>' + + '<field name="date_end"/>' + + '</tree>' + + '</field>' + + '</form>', + archs: { + 'line,false,form': '<form>' + + '<field name="name"/>' + + '<field name="line_type_id"/>' + + '<field name="description"/>' + + '<field name="date_start"/>' + + '<field name="date_end"/>' + + '</form>', + }, + res_id: 1, + mockRPC: function (route, args) { + var result = this._super.apply(this, arguments); + if (args.method === 'write') { + var new_line_data = args.args[1].line_ids[3][2]; + assert.strictEqual(new_line_data.date_end, '2030-01-01'); + assert.strictEqual(new_line_data.date_start, '2025-01-01'); + assert.strictEqual(new_line_data.line_type_id, 50, "it should have the line type from context"); + assert.strictEqual(new_line_data.name, 'new line'); + assert.strictEqual(new_line_data.description, 'new description'); + } + return result; + }, + }); + + await testUtils.form.clickEdit(form); + await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a')[0]); + + // Fill line form (type should be set from the add button context) + await testUtils.fields.editInput($('input[name="name"]'), 'new line'); + await testUtils.fields.editInput($('textarea[name="description"]'), 'new description'); + await testUtils.fields.editSelect($('input[name="date_start"]'), '2025-01-01'); + await testUtils.fields.editSelect($('input[name="date_end"]'), '2030-01-01'); + await testUtils.modal.clickButton('Save & Close'); + + await testUtils.form.clickSave(form); + form.destroy(); + }); + QUnit.test('resumé one2many field group by field delete', async function (assert) { + assert.expect(2); + var form = await createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '<form string="Partners">' + + '<field name="line_ids" widget="hr_resume">' + + '<tree>' + + '<field name="name"/>' + + '<field name="line_type_id"/>' + + '<field name="description"/>' + + '<field name="date_start"/>' + + '<field name="date_end"/>' + + '</tree>' + + '</field>' + + '</form>', + archs: { + 'line,false,form': '<form>' + + '<field name="name"/>' + + '<field name="line_type_id"/>' + + '<field name="description"/>' + + '<field name="date_start"/>' + + '<field name="date_end"/>' + + '</form>', + }, + res_id: 1, + mockRPC: function (route, args) { + var result = this._super.apply(this, arguments); + if (args.method === 'write') { + var orm_cmd = args.args[1].line_ids[2][0]; + var id = args.args[1].line_ids[2][1]; + assert.strictEqual(orm_cmd, 2, "it should delete resume line"); + assert.strictEqual(id, 37, "it should delete resume line #37"); + } + return result; + }, + }); + + await testUtils.form.clickEdit(form); + await testUtils.dom.click(form.$('.o_list_record_remove')[0]); + + await testUtils.form.clickSave(form); + form.destroy(); + }); + + QUnit.test('skills one2many field group by field render', async function (assert) { + assert.expect(13); + var form = await createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '<form string="Partners">' + + '<field name="skill_ids" widget="hr_skills">' + + '<tree>' + + '<field name="skill_id"/>' + + '<field name="skill_type_id"/>' + + '<field name="skill_level_id"/>' + + '<field name="level_progress"/>' + + '</tree>' + + '</field>' + + '</form>', + res_id: 1, + }); + + var $headers = form.$('.o_group_header'); + assert.strictEqual($headers.length, 2, 'There should be 2 headers'); + assert.strictEqual($headers.find('td:contains(Dev)').length, 1, "it should have skill type Dev"); + assert.strictEqual($headers.find('td:contains(Music)').length, 1, "it should have skill type Music"); + + var dataRows = form.$('.o_data_row'); + assert.strictEqual(dataRows.length, 3, 'There should be 3 data rows'); + + var $row = $(dataRows[0]); + assert.strictEqual($row.find('td:contains(Python)').length, 1, "it should have skill name Python"); + assert.strictEqual($row.find('td:contains(Intermediate)').length, 1, "it should have skill name Intermediate"); + assert.strictEqual($row.find('td:contains(50)').length, 1, "it should have skill progress 50"); + + $row = $(dataRows[1]); + assert.strictEqual($row.find('td:contains(Piano)').length, 1, "it should have skill name Piano"); + assert.strictEqual($row.find('td:contains(L1)').length, 1, "it should have skill level L1"); + assert.strictEqual($row.find('td:contains(50)').length, 1, "it should have skill progress 50"); + + $row = $(dataRows[2]); + assert.strictEqual($row.find('td:contains(Flute)').length, 1, "it should have skill name Flute"); + assert.strictEqual($row.find('td:contains(L1)').length, 1, "it should have skill level L1"); + assert.strictEqual($row.find('td:contains(70)').length, 1, "it should have skill progress 70"); + + form.destroy(); + }); + }); +}); |
