mirror of
https://github.com/bringout/oca-ocb-hr.git
synced 2026-04-24 08:12:02 +02:00
Initial commit: Hr packages
This commit is contained in:
commit
62531cd146
2820 changed files with 1432848 additions and 0 deletions
|
|
@ -0,0 +1,51 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { Many2OneField } from "@web/views/fields/many2one/many2one_field";
|
||||
|
||||
|
||||
class TaskWithHours extends Many2OneField {
|
||||
|
||||
get canCreate() {
|
||||
return Boolean(this.context.default_project_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
get displayName() {
|
||||
const displayName = super.displayName;
|
||||
return displayName ? displayName.split('\u00A0')[0] : displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
get context() {
|
||||
return {...super.context, hr_timesheet_display_remaining_hours: true};
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
get Many2XAutocompleteProps() {
|
||||
const props = super.Many2XAutocompleteProps;
|
||||
if (!this.canCreate) {
|
||||
props.quickCreate = null;
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
computeActiveActions(props) {
|
||||
super.computeActiveActions(props);
|
||||
const activeActions = this.state.activeActions;
|
||||
activeActions.create = activeActions.create && this.canCreate;
|
||||
activeActions.createEdit = activeActions.create;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
registry.category("fields").add("task_with_hours", TaskWithHours);
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/** @odoo-module */
|
||||
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { session } from "@web/session";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { FloatFactorField } from "@web/views/fields/float_factor/float_factor_field";
|
||||
import { FloatToggleField } from "@web/views/fields/float_toggle/float_toggle_field";
|
||||
import { FloatTimeField } from "@web/views/fields/float_time/float_time_field";
|
||||
import { standardFieldProps } from "@web/views/fields/standard_field_props";
|
||||
|
||||
const { Component } = owl;
|
||||
|
||||
|
||||
export class TimesheetUOM extends Component {
|
||||
|
||||
setup() {
|
||||
this.companyService = useService("company");
|
||||
}
|
||||
|
||||
get timesheetUOMId() {
|
||||
return this.companyService.currentCompany.timesheet_uom_id;
|
||||
}
|
||||
|
||||
get timesheetWidget() {
|
||||
let timesheet_widget = "float_factor";
|
||||
if (this.timesheetUOMId in session.uom_ids) {
|
||||
timesheet_widget = session.uom_ids[this.timesheetUOMId].timesheet_widget;
|
||||
}
|
||||
return timesheet_widget;
|
||||
}
|
||||
|
||||
get timesheetComponent() {
|
||||
return registry.category("fields").get(this.timesheetWidget, FloatFactorField);
|
||||
}
|
||||
|
||||
get timesheetComponentProps() {
|
||||
const factorDependantComponents = ["float_toggle", "float_factor"];
|
||||
return factorDependantComponents.includes(this.timesheetWidget) ? this.FactorCompanyDependentProps : this.props;
|
||||
}
|
||||
|
||||
get FactorCompanyDependentProps() {
|
||||
const factor = this.companyService.currentCompany.timesheet_uom_factor || this.props.factor;
|
||||
return { ...this.props, factor };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TimesheetUOM.props = {
|
||||
...standardFieldProps,
|
||||
};
|
||||
|
||||
TimesheetUOM.template = "hr_timesheet.TimesheetUOM";
|
||||
|
||||
TimesheetUOM.components = { FloatFactorField, FloatToggleField, FloatTimeField };
|
||||
|
||||
registry.category("fields").add("timesheet_uom", TimesheetUOM);
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<templates>
|
||||
|
||||
<t t-name="hr_timesheet.TimesheetUOM" owl="1">
|
||||
<t t-component="timesheetComponent" t-props="timesheetComponentProps"/>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
/** @odoo-module */
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
|
||||
import { TimesheetUOM } from "../timesheet_uom/timesheet_uom";
|
||||
|
||||
|
||||
export class TimesheetUOMNoToggle extends TimesheetUOM {
|
||||
|
||||
get timesheetWidget() {
|
||||
const timesheetWidget = super.timesheetWidget;
|
||||
return timesheetWidget !== "float_toggle" ? timesheetWidget : "float_factor";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// As FloatToggleField won't be used by TimesheetUOMNoToggle, we remove it from the components that we get from TimesheetUOM.
|
||||
delete TimesheetUOMNoToggle.components.FloatToggleField;
|
||||
|
||||
registry.category("fields").add("timesheet_uom_no_toggle", TimesheetUOMNoToggle);
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/** @odoo-module alias=hr_timesheet.task_with_hours **/
|
||||
|
||||
import field_registry from 'web.field_registry';
|
||||
import TimesheetFieldMany2One from 'hr_timesheet.TimesheetFieldMany2one';
|
||||
|
||||
const TaskWithHours = TimesheetFieldMany2One.extend({
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this.additionalContext.hr_timesheet_display_remaining_hours = true;
|
||||
// By default, we keep the no_quick_create value or we set to false.
|
||||
this.nodeOptions.no_quick_create = this.nodeOptions.no_quick_create || false;
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_getDisplayNameWithoutHours: function (value) {
|
||||
return value && value.split('\u00A0')[0];
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
* @private
|
||||
*/
|
||||
_onInputClick: function () {
|
||||
const context = Object.assign(
|
||||
this.record.getContext(this.recordParams),
|
||||
this.additionalContext
|
||||
);
|
||||
// We don't want to quick create if no project is set in the timesheet
|
||||
const canCreate = 'default_project_id' in context && context.default_project_id;
|
||||
this.nodeOptions.no_quick_create =
|
||||
this.nodeOptions.no_quick_create || !canCreate;
|
||||
this.can_create = this.can_create && canCreate;
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
* @private
|
||||
*/
|
||||
_renderEdit: function (){
|
||||
this.m2o_value = this._getDisplayNameWithoutHours(this.m2o_value);
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
|
||||
field_registry.add('task_with_hours', TaskWithHours);
|
||||
|
||||
export default TaskWithHours;
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/** @odoo-module alias=hr_timesheet.TimesheetFieldMany2one **/
|
||||
|
||||
import FieldRegistry from 'web.field_registry';
|
||||
import { FieldMany2One } from 'web.relational_fields';
|
||||
|
||||
import { SelectCreateDialog } from "@web/views/view_dialogs/select_create_dialog";
|
||||
import { Component } from "@odoo/owl";
|
||||
|
||||
const TimesheetFieldMany2one = FieldMany2One.extend({
|
||||
/**
|
||||
* @override
|
||||
* @private
|
||||
*/
|
||||
_searchCreatePopup(view, ids, context, dynamicFilters) {
|
||||
const options = this._getSearchCreatePopupOptions(view, ids, context, dynamicFilters);
|
||||
Component.env.services.dialog.add(SelectCreateDialog, {
|
||||
title: options.title,
|
||||
resModel: options.res_model,
|
||||
multiSelect: false,
|
||||
domain: options.domain,
|
||||
context: options.context,
|
||||
noCreate: options.no_create,
|
||||
onSelected: (resId) => {
|
||||
return this.reinitialize(resId);
|
||||
},
|
||||
onClose: () => {
|
||||
this.activate();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
FieldRegistry.add('timesheet_field_many2one', TimesheetFieldMany2one);
|
||||
|
||||
export default TimesheetFieldMany2one;
|
||||
|
|
@ -0,0 +1,196 @@
|
|||
odoo.define('hr_timesheet.timesheet_uom', function (require) {
|
||||
'use strict';
|
||||
|
||||
const { registry } = require("@web/core/registry");
|
||||
const basicFields = require('web.basic_fields');
|
||||
const fieldUtils = require('web.field_utils');
|
||||
|
||||
const fieldRegistry = require('web.field_registry');
|
||||
|
||||
// We need the field registry to be populated, as we bind the
|
||||
// timesheet_uom widget on existing field widgets.
|
||||
require('web._field_registry');
|
||||
|
||||
const session = require('web.session');
|
||||
|
||||
const TimesheetUOMMultiCompanyMixin = {
|
||||
init: function(parent, name, record, options) {
|
||||
this._super(parent, name, record, options);
|
||||
const currentCompanyId = session.user_context.allowed_company_ids[0];
|
||||
const currentCompany = session.user_companies.allowed_companies[currentCompanyId];
|
||||
this.currentCompanyTimesheetUOMFactor = currentCompany.timesheet_uom_factor || 1;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Extend the float factor widget to set default value for timesheet
|
||||
* use case. The 'factor' is forced to be the UoM timesheet
|
||||
* conversion from the session info.
|
||||
**/
|
||||
const FieldTimesheetFactor = basicFields.FieldFloatFactor.extend(TimesheetUOMMultiCompanyMixin).extend({
|
||||
formatType: 'float_factor',
|
||||
/**
|
||||
* Override init to tweak options depending on the session info
|
||||
*
|
||||
* @constructor
|
||||
* @override
|
||||
*/
|
||||
init: function(parent, name, record, options) {
|
||||
this._super(parent, name, record, options);
|
||||
|
||||
// force factor in format and parse options
|
||||
this.nodeOptions.factor = this.currentCompanyTimesheetUOMFactor;
|
||||
this.parseOptions.factor = this.currentCompanyTimesheetUOMFactor;
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Extend the float toggle widget to set default value for timesheet
|
||||
* use case. The 'range' is different from the default one of the
|
||||
* native widget, and the 'factor' is forced to be the UoM timesheet
|
||||
* conversion.
|
||||
**/
|
||||
const FieldTimesheetToggle = basicFields.FieldFloatToggle.extend(TimesheetUOMMultiCompanyMixin).extend({
|
||||
formatType: 'float_factor',
|
||||
/**
|
||||
* Override init to tweak options depending on the session info
|
||||
*
|
||||
* @constructor
|
||||
* @override
|
||||
*/
|
||||
init: function(parent, name, record, options) {
|
||||
options = options || {};
|
||||
var fieldsInfo = record.fieldsInfo[options.viewType || 'default'];
|
||||
var attrs = options.attrs || (fieldsInfo && fieldsInfo[name]) || {};
|
||||
|
||||
var hasRange = _.contains(_.keys(attrs.options || {}), 'range');
|
||||
|
||||
this._super(parent, name, record, options);
|
||||
|
||||
// Set the timesheet widget options: the range can be customized
|
||||
// by setting the option on the field in the view. The factor
|
||||
// is forced to be the UoM conversion factor.
|
||||
if (!hasRange) {
|
||||
this.nodeOptions.range = [0.00, 1.00, 0.50];
|
||||
}
|
||||
this.nodeOptions.factor = this.currentCompanyTimesheetUOMFactor;
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Extend float time widget
|
||||
*/
|
||||
const FieldTimesheetTime = basicFields.FieldFloatTime.extend(TimesheetUOMMultiCompanyMixin).extend({
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this.nodeOptions.factor = this.currentCompanyTimesheetUOMFactor;
|
||||
this.parseOptions.factor = this.currentCompanyTimesheetUOMFactor;
|
||||
}
|
||||
});
|
||||
|
||||
const timesheetUomService = {
|
||||
dependencies: ["legacy_session"],
|
||||
start() {
|
||||
const timesheetUomInfo = {
|
||||
widget: null,
|
||||
factor: 1,
|
||||
};
|
||||
if (session.user_context &&
|
||||
session.user_context.allowed_company_ids &&
|
||||
session.user_context.allowed_company_ids.length) {
|
||||
const currentCompanyId = session.user_context.allowed_company_ids[0];
|
||||
const currentCompany = session.user_companies.allowed_companies[currentCompanyId];
|
||||
const currentCompanyTimesheetUOMId = currentCompany.timesheet_uom_id || false;
|
||||
timesheetUomInfo.factor = currentCompany.timesheet_uom_factor || 1;
|
||||
if (currentCompanyTimesheetUOMId) {
|
||||
timesheetUomInfo.widget = session.uom_ids[currentCompanyTimesheetUOMId].timesheet_widget;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binding depending on Company Preference
|
||||
*
|
||||
* determine wich widget will be the timesheet one.
|
||||
* Simply match the 'timesheet_uom' widget key with the correct
|
||||
* implementation (float_time, float_toggle, ...). The default
|
||||
* value will be 'float_factor'.
|
||||
**/
|
||||
const widgetName = timesheetUomInfo.widget || 'float_factor';
|
||||
|
||||
let FieldTimesheetUom = null;
|
||||
|
||||
if (widgetName === 'float_toggle') {
|
||||
FieldTimesheetUom = FieldTimesheetToggle;
|
||||
} else if (widgetName === 'float_time') {
|
||||
FieldTimesheetUom = FieldTimesheetTime;
|
||||
} else {
|
||||
FieldTimesheetUom = (
|
||||
fieldRegistry.get(widgetName) &&
|
||||
fieldRegistry.get(widgetName).extend({ })
|
||||
) || FieldTimesheetFactor;
|
||||
}
|
||||
fieldRegistry.add('timesheet_uom', FieldTimesheetUom);
|
||||
|
||||
// widget timesheet_uom_no_toggle is the same as timesheet_uom but without toggle.
|
||||
// We can modify easly huge amount of days.
|
||||
let FieldTimesheetUomWithoutToggle = null;
|
||||
if (widgetName === 'float_toggle') {
|
||||
FieldTimesheetUomWithoutToggle = FieldTimesheetFactor;
|
||||
} else {
|
||||
FieldTimesheetUomWithoutToggle = FieldTimesheetTime;
|
||||
}
|
||||
fieldRegistry.add('timesheet_uom_no_toggle', FieldTimesheetUomWithoutToggle);
|
||||
|
||||
|
||||
// bind the formatter and parser method, and tweak the options
|
||||
const _tweak_options = (options) => {
|
||||
if (!_.contains(options, 'factor')) {
|
||||
options.factor = timesheetUomInfo.factor;
|
||||
}
|
||||
return options;
|
||||
};
|
||||
|
||||
fieldUtils.format.timesheet_uom = function(value, field, options) {
|
||||
options = _tweak_options(options || { });
|
||||
const formatter = fieldUtils.format[FieldTimesheetUom.prototype.formatType];
|
||||
return formatter(value, field, options);
|
||||
};
|
||||
|
||||
fieldUtils.parse.timesheet_uom = function(value, field, options) {
|
||||
options = _tweak_options(options || { });
|
||||
const parser = fieldUtils.parse[FieldTimesheetUom.prototype.formatType];
|
||||
return parser(value, field, options);
|
||||
};
|
||||
|
||||
fieldUtils.format.timesheet_uom_no_toggle = function(value, field, options) {
|
||||
options = _tweak_options(options || { });
|
||||
const formatter = fieldUtils.format[FieldTimesheetUom.prototype.formatType];
|
||||
return formatter(value, field, options);
|
||||
};
|
||||
|
||||
fieldUtils.parse.timesheet_uom_no_toggle = function(value, field, options) {
|
||||
options = _tweak_options(options || { });
|
||||
const parser = fieldUtils.parse[FieldTimesheetUom.prototype.formatType];
|
||||
return parser(value, field, options);
|
||||
};
|
||||
if (!registry.category("formatters").contains("timesheet_uom")) {
|
||||
registry.category("formatters").add("timesheet_uom", fieldUtils.format.timesheet_uom);
|
||||
}
|
||||
if (!registry.category("formatters").contains("timesheet_uom_no_toggle")) {
|
||||
registry.category("formatters").add("timesheet_uom_no_toggle", fieldUtils.format.timesheet_uom_no_toggle);
|
||||
}
|
||||
return timesheetUomInfo;
|
||||
},
|
||||
};
|
||||
registry.category("services").add("timesheet_uom", timesheetUomService);
|
||||
|
||||
return {
|
||||
FieldTimesheetFactor,
|
||||
FieldTimesheetTime,
|
||||
FieldTimesheetToggle,
|
||||
timesheetUomService,
|
||||
};
|
||||
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
.o_project_kanban .oe_kanban_align.badge {
|
||||
color: inherit;
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import { GraphModel } from "@web/views/graph/graph_model";
|
||||
|
||||
const FIELDS = [
|
||||
'unit_amount', 'effective_hours', 'planned_hours', 'remaining_hours', 'total_hours_spent', 'subtask_effective_hours',
|
||||
'overtime', 'number_hours', 'difference', 'hours_effective', 'hours_planned', 'timesheet_unit_amount'
|
||||
];
|
||||
|
||||
export class hrTimesheetGraphModel extends GraphModel {
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
setup(params, services) {
|
||||
super.setup(...arguments);
|
||||
this.companyService = services.company;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Override processDataPoints to take into account the analytic line uom.
|
||||
* @override
|
||||
*/
|
||||
_getProcessedDataPoints() {
|
||||
const currentCompany = this.companyService.currentCompany;
|
||||
const factor = currentCompany.timesheet_uom_factor || 1;
|
||||
if (factor !== 1 && FIELDS.includes(this.metaData.measure)) {
|
||||
// recalculate the Duration values according to the timesheet_uom_factor
|
||||
for (const dataPt of this.dataPoints) {
|
||||
dataPt.value *= factor;
|
||||
}
|
||||
}
|
||||
return super._getProcessedDataPoints(...arguments);
|
||||
}
|
||||
}
|
||||
hrTimesheetGraphModel.services = [...GraphModel.services, "company"];
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import { projectGraphView } from "@project/js/project_graph_view";
|
||||
import { hrTimesheetGraphModel } from "./timesheet_graph_model";
|
||||
import { registry } from "@web/core/registry";
|
||||
|
||||
const viewRegistry = registry.category("views");
|
||||
|
||||
export const hrTimesheetGraphView = {
|
||||
...projectGraphView,
|
||||
Model: hrTimesheetGraphModel,
|
||||
};
|
||||
|
||||
viewRegistry.add("hr_timesheet_graphview", hrTimesheetGraphView);
|
||||
Loading…
Add table
Add a link
Reference in a new issue