oca-ocb-hr/odoo-bringout-oca-ocb-hr/hr/static/src/components/versions_timeline/versions_timeline.js
Ernad Husremovic e1d89e11e3 19.0 vanilla
2026-03-09 09:31:00 +01:00

159 lines
5.6 KiB
JavaScript

import { onWillUpdateProps, useComponent, useState } from "@odoo/owl";
import { useDateTimePicker } from "@web/core/datetime/datetime_picker_hook";
import { Domain } from "@web/core/domain";
import { registry } from "@web/core/registry";
import { useService } from "@web/core/utils/hooks";
import { getFieldDomain, useRecordObserver } from "@web/model/relational_model/utils";
import { statusBarField, StatusBarField } from "@web/views/fields/statusbar/statusbar_field";
import { _t } from "@web/core/l10n/translation";
export class VersionsTimeline extends StatusBarField {
static template = "hr.VersionsTimeline";
/** @override **/
setup() {
super.setup();
this.actionService = useService("action");
this.orm = useService("orm");
if (this.field.type === "many2one") {
this.specialData = useSpecialDataNoCache((orm, props) => {
const { foldField, name: fieldName, record } = props;
const { relation } = record.fields[fieldName];
const fieldNames = [
"display_name",
"contract_type_id",
"contract_date_start",
"contract_date_end",
];
if (foldField) {
fieldNames.push(foldField);
}
const value = record.data[fieldName];
let domain = getFieldDomain(record, fieldName, props.domain);
domain = Domain.and([
[["employee_id", "=", props.record.evalContext.id]],
domain,
]).toList();
if (domain.length && value) {
domain = Domain.or([[["id", "=", value.id]], domain]).toList(
record.evalContext
);
}
return orm.searchRead(
relation,
domain,
fieldNames.filter((fName) => fName in record.fields)
);
});
}
this.dateTimePicker = useDateTimePicker({
target: `datetime-picker-target-version`,
onApply: (date) => {
if (date) {
this.createVersion(date);
}
},
get pickerProps() {
return { type: "date" };
},
});
}
displayContractLines() {
return ["contract_type_id", "contract_date_start", "contract_date_end"].every(
(fieldName) => fieldName in this.props.record.fields
);
}
async createVersion(date) {
await this.props.record.save();
const version_id = await this.orm.call("hr.employee", "create_version", [
this.props.record.evalContext.id,
{ date_version: date },
]);
await this.props.record.model.load({
context: {
...this.props.record.model.env.searchModel.context,
version_id: version_id,
},
});
}
onClickDateTimePickerBtn() {
this.dateTimePicker.open();
}
/** @override **/
async selectItem(item) {
const { record } = this.props;
await record.save();
await this.props.record.model.load({
context: {
...this.props.record.model.env.searchModel.context,
version_id: item.value,
},
});
}
/** @override **/
getAllItems() {
function format(dateString) {
return luxon.DateTime.fromISO(dateString).toFormat("MMM dd, yyyy");
}
const items = super.getAllItems();
if (!this.displayContractLines) {
return items;
}
const dataById = new Map(this.specialData.data.map((d) => [d.id, d]));
const selectedVersion = items.find((item) => item.isSelected)?.value;
const selectedContractDate = dataById.get(selectedVersion)?.contract_date_start;
return items.map((item, index) => {
const itemSpecialData = dataById.get(item.value) || {};
const contractDateStart = itemSpecialData.contract_date_start;
let contractDateEnd = itemSpecialData.contract_date_end;
contractDateEnd = contractDateEnd ? format(contractDateEnd) : _t("Indefinite");
const contractType = itemSpecialData.contract_type_id?.[1] ?? _t("Contract");
const toolTip = contractDateStart
? `${contractType}: ${format(contractDateStart)} - ${contractDateEnd}`
: _t("No contract");
return {
...item,
isCurrentContract: contractDateStart === selectedContractDate,
isInContract: Boolean(contractDateStart),
toolTip,
};
});
}
}
export function useSpecialDataNoCache(loadFn) {
const component = useComponent();
const orm = component.env.services.orm;
/** @type {{ data: Record<string, T> }} */
const result = useState({ data: {} });
useRecordObserver(async (record, props) => {
result.data = await loadFn(orm, { ...props, record });
});
onWillUpdateProps(async (props) => {
// useRecordObserver callback is not called when the record doesn't change
if (props.record.id === component.props.record.id) {
result.data = await loadFn(orm, props);
}
});
return result;
}
export const versionsTimeline = {
...statusBarField,
component: VersionsTimeline,
additionalClasses: ["o_field_statusbar", "d-flex", "gap-1"],
};
registry.category("fields").add("versions_timeline", versionsTimeline);