[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:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user