[FIX] web: support properties in calendar

Before this commit, in a calendar view with a properties field, a crash
will be display when you click on an event.

closes odoo/odoo#134476

Related: odoo/enterprise#46990
Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
FrancoisGe
2023-09-07 09:00:27 +00:00
parent 70a4051646
commit 33453faf23
7 changed files with 114 additions and 54 deletions
@@ -3,15 +3,16 @@
import { markRaw, markup, toRaw } from "@odoo/owl";
import { AlertDialog } from "@web/core/confirmation_dialog/confirmation_dialog";
import { Domain } from "@web/core/domain";
import { evaluateBooleanExpr } from "@web/core/py_js/py";
import { serializeDate, serializeDateTime } from "@web/core/l10n/dates";
import { _t } from "@web/core/l10n/translation";
import { x2ManyCommands } from "@web/core/orm_service";
import { evaluateBooleanExpr } from "@web/core/py_js/py";
import { pick } from "@web/core/utils/objects";
import { escape } from "@web/core/utils/strings";
import { DataPoint } from "./datapoint";
import {
FetchRecordError,
applyProperties,
getBasicEvalContext,
getFieldContext,
getFieldsSpec,
@@ -889,7 +890,7 @@ export class Record extends DataPoint {
if (!records.length) {
throw new FetchRecordError(records.map((r) => r.id));
}
this.model._applyProperties(records, this.config);
applyProperties(records, this.config.activeFields, this.config.fields);
if (this.resId) {
this.model._updateSimilarRecords(this, records[0]);
}
@@ -16,12 +16,12 @@ import { Group } from "./group";
import { Record } from "./record";
import { StaticList } from "./static_list";
import {
getFieldsSpec,
createPropertyActiveField,
extractInfoFromGroupData,
isRelational,
FetchRecordError,
applyProperties,
extractInfoFromGroupData,
getBasicEvalContext,
getFieldsSpec,
isRelational,
} from "./utils";
/**
@@ -186,40 +186,6 @@ export class RelationalModel extends Model {
// Protected
// -------------------------------------------------------------------------
_applyProperties(records, config) {
for (const record of records) {
for (const fieldName in record) {
const field = config.fields[fieldName];
if (fieldName !== "id" && field.type === "properties" && record[fieldName]) {
const parent = record[field.definition_record];
const relatedPropertyField = {
fieldName,
};
if (parent) {
relatedPropertyField.id = parent.id;
relatedPropertyField.displayName = parent.display_name;
}
for (const property of record[fieldName]) {
const propertyFieldName = `${fieldName}.${property.name}`;
if (!config.fields[propertyFieldName]) {
config.fields[propertyFieldName] = {
...property,
name: propertyFieldName,
relatedPropertyField,
propertyName: property.name,
relation: property.comodel,
};
}
if (!config.activeFields[propertyFieldName]) {
config.activeFields[propertyFieldName] =
createPropertyActiveField(property);
}
}
}
}
}
}
_askChanges() {
const proms = [];
this.bus.trigger("NEED_LOCAL_CHANGES", { proms });
@@ -533,7 +499,7 @@ export class RelationalModel extends Model {
throw new FetchRecordError(resIds);
}
this._applyProperties(records, config);
applyProperties(records, config.activeFields, config.fields);
return records;
} else {
return resIds.map((resId) => {
@@ -561,7 +527,7 @@ export class RelationalModel extends Model {
};
const response = await this.orm.webSearchRead(config.resModel, config.domain, kwargs);
this._applyProperties(response.records, config);
applyProperties(response.records, config.activeFields, config.fields);
return response;
}
@@ -76,6 +76,39 @@ export function addFieldDependencies(activeFields, fields, fieldDependencies = [
}
}
export function applyProperties(records, activeFields, fields) {
for (const record of records) {
for (const fieldName in record) {
const field = fields[fieldName];
if (fieldName !== "id" && field.type === "properties" && record[fieldName]) {
const parent = record[field.definition_record];
const relatedPropertyField = {
fieldName,
};
if (parent) {
relatedPropertyField.id = parent.id;
relatedPropertyField.displayName = parent.display_name;
}
for (const property of record[fieldName]) {
const propertyFieldName = `${fieldName}.${property.name}`;
if (!fields[propertyFieldName]) {
fields[propertyFieldName] = {
...property,
name: propertyFieldName,
relatedPropertyField,
propertyName: property.name,
relation: property.comodel,
};
}
if (!activeFields[propertyFieldName]) {
activeFields[propertyFieldName] = createPropertyActiveField(property);
}
}
}
}
}
}
function completeActiveField(activeField, extra) {
if (extra.related) {
for (const fieldName in extra.related.activeFields) {
@@ -21,7 +21,7 @@ export class CalendarCommonPopover extends Component {
}
get activeFields() {
return this.props.model.popoverFields;
return this.props.model.activeFields;
}
get isEventEditable() {
return true;
@@ -11,7 +11,7 @@ import { _t } from "@web/core/l10n/translation";
import { registry } from "@web/core/registry";
import { KeepLast } from "@web/core/utils/concurrency";
import { Model } from "@web/model/model";
import { extractFieldsFromArchInfo } from "@web/model/relational_model/utils";
import { applyProperties, extractFieldsFromArchInfo } from "@web/model/relational_model/utils";
export class CalendarModel extends Model {
setup(params, services) {
@@ -27,7 +27,7 @@ export class CalendarModel extends Model {
const { activeFields, fields } = extractFieldsFromArchInfo({ fieldNodes }, params.fields);
this.meta = {
...params,
popoverFields: activeFields,
activeFields,
fields,
firstDayOfWeek: localization.weekStart,
formViewId: params.formViewId || formViewIdFromConfig,
@@ -118,8 +118,8 @@ export class CalendarModel extends Model {
get popoverFieldNodes() {
return this.meta.popoverFieldNodes;
}
get popoverFields() {
return this.meta.popoverFields;
get activeFields() {
return this.meta.activeFields;
}
get rangeEnd() {
return this.data.range.end;
@@ -485,6 +485,7 @@ export class CalendarModel extends Model {
*/
async loadRecords(data) {
const rawRecords = await this.fetchRecords(data);
applyProperties(rawRecords, this.meta.activeFields, this.meta.fields);
const records = {};
for (const rawRecord of rawRecords) {
records[rawRecord.id] = this.normalizeRecord(rawRecord);
@@ -960,12 +960,7 @@ QUnit.module("Views", ({ beforeEach }) => {
assert.containsN(target, ".fc-event-container .fc-event", 10, "should display 10 events");
// move to next month
await navigate(target, "next");
assert.containsN(
target,
".fc-event-container .fc-event",
0,
"should display 0 events"
);
assert.containsN(target, ".fc-event-container .fc-event", 0, "should display 0 events");
await pickDate(target, "2017-01-01");
assert.containsN(
target,
@@ -4814,4 +4809,68 @@ QUnit.module("Views", ({ beforeEach }) => {
"The text in the title should be Test Display"
);
});
QUnit.test(`calendar render properties`, async (assert) => {
serverData.models.event.fields.properties = {
string: "Properties",
type: "properties",
definition_record: "event_type_id",
definition_record_field: "definitions",
};
serverData.models.event_type.fields.definitions = {
string: "Definitions",
type: "properties_definition",
};
serverData.models.event_type.records[0].definitions = [
{ name: "event_prop_1", string: "My Char", type: "char" },
{ name: "event_prop_2", string: "My Selection", type: "selection" },
];
serverData.models.event.records[0].event_type_id = 1;
serverData.models.event.records[0].properties = [
{
name: "property_1",
string: "My Char",
type: "char",
value: "hello",
},
{
name: "property_2",
string: "My Selection",
type: "selection",
selection: [
["a", "A"],
["b", "B"],
["c", "C"],
],
value: "b",
default: "c",
},
];
await makeView({
type: "calendar",
resModel: "event",
serverData,
arch: `
<calendar event_open_popup="1" date_start="start" date_stop="stop" all_day="allday" mode="week" attendee="partner_ids" color="partner_id" date_delay="duration">
<field name="event_type_id" />
<field name="properties" />
</calendar>
`,
async mockRPC(route, args) {
if (args.method === "check_access_rights") {
return true;
}
},
});
await clickEvent(target, 1);
assert.deepEqual(
[...target.querySelectorAll(".o_popover [name='properties'] .o_property_field")].map(
(el) => el.textContent
),
["My Charhello", "My SelectionB"]
);
});
});
@@ -234,7 +234,7 @@ function makeFakeModelState() {
popoverFieldNodes: {
name: Field.parseFieldNode(fakeFieldNode, fakeModels, "event", "calendar"),
},
popoverFields: {
activeFields: {
name: {
context: "{}",
invisible: false,