[REF] web, *: simplify concrete fields API - remove value prop

This commit, is part of a series of commits that aim to simplifie the
concrete fields API.

In this commit we will remove value prop from concrete fields. Now each
field will directly use this.props.record.data[this.props.name] to
access their value, As a consequence of this, the name props need to be
mandatory.

task-id 3179751

closes odoo/odoo#113495

Related: odoo/enterprise#37464
Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
Jorge Pinna Puissant
2023-03-07 09:02:50 +01:00
parent 8eb2893932
commit 688986f888
124 changed files with 392 additions and 273 deletions
@@ -24,7 +24,7 @@ export class AccountPaymentField extends Component {
}
formatData(props) {
const info = props.value || {
const info = props.record.data[props.name] || {
content: [],
outstanding: false,
title: "",
@@ -15,7 +15,7 @@ class ShowResequenceRenderer extends Component {
}
formatData(props) {
this.data = props.value ? JSON.parse(props.value) : { changeLines: [], ordering: "date" };
this.data = props.record.data[props.name] ? JSON.parse(props.record.data[props.name]) : { changeLines: [], ordering: "date" };
}
}
ShowResequenceRenderer.template = "account.ResequenceRenderer";
@@ -20,8 +20,8 @@ class ShowGroupedList extends Component {
}
formatData(props) {
this.data = props.value
? JSON.parse(props.value)
this.data = props.record.data[props.name]
? JSON.parse(props.record.data[props.name])
: { groups_vals: [], options: { discarded_number: "", columns: [] } };
}
}
@@ -13,7 +13,7 @@ export class JournalDashboardActivity extends Component {
}
formatData(props) {
this.info = JSON.parse(this.props.value);
this.info = JSON.parse(this.props.record.data[this.props.name]);
this.info.more_activities = false;
if (this.info.activities.length > this.MAX_ACTIVITY_DISPLAY) {
this.info.more_activities = true;
@@ -5,7 +5,7 @@ import { Many2OneField, many2OneField } from "@web/views/fields/many2one/many2on
class LineOpenMoveWidget extends Many2OneField {
async openAction() {
const action = await this.orm.call("account.move.line", "action_open_business_doc", [this.props.value[0]], {});
const action = await this.orm.call("account.move.line", "action_open_business_doc", [this.props.record.data[this.props.name][0]], {});
await this.action.doAction(action);
}
}
@@ -2,7 +2,7 @@
<templates>
<t t-name="account.OpenMoveWidget" owl="1">
<a href="#" t-out="props.value" t-on-click.prevent.stop="(ev) => this.openMove()"/>
<a href="#" t-out="props.record.data[props.name]" t-on-click.prevent.stop="(ev) => this.openMove()"/>
</t>
</templates>
@@ -107,11 +107,11 @@ TaxGroupComponent.template = "account.TaxGroupComponent";
export class TaxTotalsComponent extends Component {
setup() {
super.setup();
this.totals = this.props.value;
this.totals = this.props.record.data[this.props.name];
this.readonly = this.props.readonly;
onWillUpdateProps((nextProps) => {
// We only reformat tax groups if there are changed
this.totals = nextProps.value;
this.totals = nextProps.record.data[nextProps.name];
this.readonly = nextProps.readonly;
this._computeTotalsFormat();
});
@@ -47,6 +47,8 @@ export class AnalyticDistribution extends Component {
this.focusSelector = false;
this.activeGroup = false;
this.currentValue = this.props.record.data[this.props.name];
onWillStart(this.willStart);
onWillUpdateProps(this.willUpdate);
onPatched(this.patched);
@@ -87,7 +89,9 @@ export class AnalyticDistribution extends Component {
// and thus different applicabilities apply
// or a model applies that contains unavailable plans
// This should only execute when these fields have changed, therefore we use the `_field` props.
const valueChanged = JSON.stringify(this.props.value) !== JSON.stringify(nextProps.value);
const valueChanged =
JSON.stringify(this.currentValue) !==
JSON.stringify(nextProps.record.data[nextProps.name]);
const currentAccount = this.props.account_field && this.props.record.data[this.props.account_field] || false;
const currentProduct = this.props.product_field && this.props.record.data[this.props.product_field] || false;
const accountChanged = !shallowEqual(this.lastAccount, currentAccount);
@@ -100,6 +104,7 @@ export class AnalyticDistribution extends Component {
this.lastProduct = productChanged && currentProduct || this.lastProduct;
await this.formatData(nextProps);
}
this.currentValue = nextProps.record.data[nextProps.name];
}
patched() {
@@ -107,7 +112,7 @@ export class AnalyticDistribution extends Component {
}
async formatData(nextProps) {
const data = nextProps.value;
const data = nextProps.record.data[nextProps.name];
const analytic_account_ids = Object.keys(data).map((id) => parseInt(id));
const records = analytic_account_ids.length ? await this.fetchAnalyticAccounts([["id", "in", analytic_account_ids]]) : [];
if (records.length < data.length) {
@@ -150,7 +155,7 @@ export class AnalyticDistribution extends Component {
if (this.props.force_applicability) {
args['applicability'] = this.props.force_applicability;
}
const existing_account_ids = Object.keys(nextProps.value).map((i) => parseInt(i));
const existing_account_ids = Object.keys(nextProps.record.data[nextProps.name]).map((i) => parseInt(i));
if (existing_account_ids.length) {
args['existing_account_ids'] = existing_account_ids;
}
@@ -9,7 +9,7 @@ import { useInputField } from "@web/views/fields/input_field_hook";
import { recommendations, ConcretePolicy } from "./password_policy";
import { Meter } from "./password_meter";
const { Component, xml, onWillStart, useState } = owl;
const { Component, onWillStart, useState } = owl;
export class PasswordField extends Component {
setup() {
@@ -19,7 +19,7 @@ export class PasswordField extends Component {
});
useInputField({
getValue: () => this.props.value || "",
getValue: () => this.props.record.data[this.props.name] || "",
});
const orm = useService("orm");
@@ -32,17 +32,7 @@ export class PasswordField extends Component {
}
PasswordField.props = standardFieldProps;
PasswordField.components = { Meter };
PasswordField.template = xml`
<span t-if="props.readonly" t-out="props.value and '*'.repeat(props.value.length)"/>
<t t-else="">
<input class="o_input o_field_password" type="password"
t-att-id="props.id" t-ref="input" placeholder=" "
t-on-input="ev => this.state.value = ev.target.value"/>
<Meter password="state.value"
required="state.required"
recommended="recommendations"/>
</t>
`;
PasswordField.template = "auth_password_policy.PasswordField";
export const passwordField = {
component: PasswordField,
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="auth_password_policy.PasswordField" owl="1">
<span t-if="props.readonly" t-out="props.record.data[props.name] and '*'.repeat(props.record.data[props.name].length)"/>
<t t-else="">
<input class="o_input o_field_password" type="password"
t-att-id="props.id" t-ref="input" placeholder=" "
t-on-input="ev => this.state.value = ev.target.value"/>
<Meter password="state.value"
required="state.required"
recommended="recommendations"/>
</t>
</t>
</templates>
@@ -8,10 +8,13 @@ const { Component } = owl;
export class IconSelectionField extends Component {
get icon() {
return this.props.icons[this.props.value];
return this.props.icons[this.props.record.data[this.props.name]];
}
get title() {
return this.props.value.charAt(0).toUpperCase() + this.props.value.slice(1);
return (
this.props.record.data[this.props.name].charAt(0).toUpperCase() +
this.props.record.data[this.props.name].slice(1)
);
}
}
IconSelectionField.template = "event.IconSelectionField";
@@ -16,11 +16,11 @@ export class FilterableSelectionField extends SelectionField {
let options = super.options;
if (this.props.whitelisted_values) {
options = options.filter((option) => {
return option[0] === this.props.value || this.props.whitelisted_values.includes(option[0])
return option[0] === this.props.record.data[this.props.name] || this.props.whitelisted_values.includes(option[0])
});
} else if (this.props.blacklisted_values) {
options = options.filter((option) => {
return option[0] === this.props.value || !this.props.blacklisted_values.includes(option[0]);
return option[0] === this.props.record.data[this.props.name] || !this.props.blacklisted_values.includes(option[0]);
});
}
return options;
@@ -7,9 +7,12 @@ import { Component } from "@odoo/owl";
class ActivityException extends Component {
get textClass() {
if (this.props.value) {
if (this.props.record.data[this.props.name]) {
return (
"text-" + this.props.value + " fa " + this.props.record.data.activity_exception_icon
"text-" +
this.props.record.data[this.props.name] +
" fa " +
this.props.record.data.activity_exception_icon
);
}
return undefined;
@@ -2,7 +2,7 @@
<templates xml:space="preserve">
<t t-name="mail.ActivityException" owl="1">
<div
t-if="props.value"
t-if="props.record.data[props.name]"
class="o_ActivityException float-end mt-1"
t-att-class="textClass"
title="This record has an exception activity."
@@ -58,7 +58,7 @@ export class KanbanFieldActivityViewContainer extends Component {
const kanbanFieldActivityView = messaging.models["KanbanFieldActivityView"].insert({
id: this.kanbanFieldActivityViewId,
thread: {
activities: props.value.records.map((activityData) => {
activities: props.record.data[props.name].records.map((activityData) => {
return {
id: activityData.resId,
};
@@ -58,7 +58,7 @@ export class ListFieldActivityViewContainer extends Component {
const listFieldActivityView = messaging.models["ListFieldActivityView"].insert({
id: this.listFieldActivityViewId,
thread: {
activities: props.value.records.map((activityData) => {
activities: props.record.data[props.name].records.map((activityData) => {
return {
id: activityData.resId,
};
@@ -21,10 +21,10 @@ export function useAssignUserCommand() {
}
const getCurrentIds = () => {
if (type === "many2one" && component.props.value) {
return [component.props.value[0]];
if (type === "many2one" && component.props.record.data[component.props.name]) {
return [component.props.record.data[component.props.name][0]];
} else if (type === "many2many") {
return component.props.value.currentIds;
return component.props.record.data[component.props.name].currentIds;
}
return [];
};
@@ -61,13 +61,14 @@ export class KanbanMany2ManyTagsAvatarUserField extends ListKanbanMany2ManyTagsA
get displayText() {
return (
(this.props.displayText && this.props.value.records.length === 1) ||
(this.props.displayText && this.props.record.data[this.props.name].records.length === 1) ||
!this.props.readonly
);
}
get tags() {
const recordFromId = (id) => this.props.value.records.find((rec) => rec.id === id);
const recordFromId = (id) =>
this.props.record.data[this.props.name].records.find((rec) => rec.id === id);
return super.tags.map((tag) => ({
...tag,
onImageClicked: () => {
@@ -44,7 +44,9 @@ export class FieldMany2ManyTagsEmail extends Many2ManyTagsField {
}
async checkEmails(props) {
const invalidRecords = props.value.records.filter((record) => !record.data.email);
const invalidRecords = props.record.data[props.name].records.filter(
(record) => !record.data.email
);
// Remove records with invalid data, open form view to edit those and readd them if they are updated correctly.
const dialogDefs = [];
for (const record of invalidRecords) {
@@ -59,8 +61,10 @@ export class FieldMany2ManyTagsEmail extends Many2ManyTagsField {
this.openedDialogs += invalidRecords.length;
const invalidRecordIds = invalidRecords.map((rec) => rec.resId);
if (invalidRecordIds.length) {
this.props.value.replaceWith(
props.value.currentIds.filter((id) => !invalidRecordIds.includes(id))
this.props.record.data[this.props.name].replaceWith(
props.record.data[props.name].currentIds.filter(
(id) => !invalidRecordIds.includes(id)
)
);
}
return Promise.all(dialogDefs).then(() => {
@@ -68,7 +72,7 @@ export class FieldMany2ManyTagsEmail extends Many2ManyTagsField {
if (this.openedDialogs || !this.recordsIdsToAdd.length) {
return;
}
props.value.add(this.recordsIdsToAdd, { isM2M: true });
props.record.data[props.name].add(this.recordsIdsToAdd, { isM2M: true });
this.recordsIdsToAdd = [];
});
}
@@ -76,10 +80,13 @@ export class FieldMany2ManyTagsEmail extends Many2ManyTagsField {
get tags() {
// Add email to our tags
const tags = super.tags;
const emailByResId = this.props.value.records.reduce((acc, record) => {
acc[record.resId] = record.data.email;
return acc;
}, {});
const emailByResId = this.props.record.data[this.props.name].records.reduce(
(acc, record) => {
acc[record.resId] = record.data.email;
return acc;
},
{}
);
tags.forEach((tag) => (tag.email = emailByResId[tag.resId]));
return tags;
}
@@ -24,7 +24,7 @@ export class Many2OneAvatarUserField extends Many2OneAvatarField {
}
onClickAvatar() {
this.openChat(this.props.value[0]);
this.openChat(this.props.record.data[this.props.name][0]);
}
}
Many2OneAvatarUserField.template = "mail.Many2OneAvatarUserField";
@@ -18,15 +18,15 @@ export class SlidesViewer extends CharField {
}
get fileName() {
return this.state.fileName || this.props.value || "";
return this.state.fileName || this.props.record.data[this.props.name] || "";
}
get url() {
var src = false;
if (this.props.value) {
if (this.props.record.data[this.props.name]) {
// check given google slide url is valid or not
var googleRegExp = /(^https:\/\/docs.google.com).*(\/d\/e\/|\/d\/)([A-Za-z0-9-_]+)/;
var google = this.props.value.match(googleRegExp);
var google = this.props.record.data[this.props.name].match(googleRegExp);
if (google && google[3]) {
src =
"https://docs.google.com/presentation" +
+8 -5
View File
@@ -29,7 +29,7 @@ export class MrpTimer extends Component {
this.state = useState({
// duration is expected to be given in minutes
duration:
this.props.value !== undefined ? this.props.value : this.props.record.data.duration,
this.props.record.data[this.props.name] !== undefined ? this.props.record.data[this.props.name] : this.props.record.data.duration,
});
this.lastDateTime = Date.now();
useInputField({
@@ -61,7 +61,7 @@ export class MrpTimer extends Component {
const rerun = !this.ongoing && newOngoing;
this.ongoing = newOngoing;
if (rerun) {
this.state.duration = nextProps.value;
this.state.duration = nextProps.record.data[nextProps.name];
this._runTimer();
this._runSleepTimer()
}
@@ -70,8 +70,12 @@ export class MrpTimer extends Component {
}
get durationFormatted() {
if(this.props.value != this.state.duration && this.props.record && this.props.record.isDirty){
this.state.duration = this.props.value;
if (
this.props.record.data[this.props.name] != this.state.duration &&
this.props.record &&
this.props.record.isDirty
) {
this.state.duration = this.props.record.data[this.props.name];
}
return formatMinutes(this.state.duration);
}
@@ -102,7 +106,6 @@ MrpTimer.props = {
...standardFieldProps,
duration: { type: Number, optional: true },
ongoing: { type: Boolean, optional: true },
value: { optional: true },
};
MrpTimer.template = "mrp.MrpTimer";
@@ -18,7 +18,7 @@ export class PartnerAutoCompleteCharField extends CharField {
this.partner_autocomplete = usePartnerAutocomplete();
this.inputRef = useChildRef();
useInputField({ getValue: () => this.props.value || "", parse: (v) => this.parse(v), ref: this.inputRef});
useInputField({ getValue: () => this.props.record.data[this.props.name] || "", parse: (v) => this.parse(v), ref: this.inputRef});
}
sanitizeVAT(request) {
@@ -4,7 +4,7 @@
<xpath expr="//t[@t-else='']" position="before">
<t t-elif="props.record.resModel !== 'res.partner' || props.record.data.company_type === 'company'">
<AutoComplete
value="props.value || ''"
value="props.record.data[props.name] || ''"
sources="sources"
onSelect.bind="onSelect"
input="inputRef"
@@ -2,7 +2,7 @@
<t t-name="project.ProjectPrivateTaskMany2OneField" t-inherit="web.Many2OneField" t-inherit-mode="primary" owl="1">
<xpath expr="//t[@t-if='!props.canOpen']/span" position="attributes">
<attribute name="t-if">props.value</attribute>
<attribute name="t-if">props.record.data[props.name]</attribute>
</xpath>
<xpath expr="//t[@t-if='!props.canOpen']/span" position="after">
<span t-else="" class="text-danger fst-italic text-muted"><i class="fa fa-lock"></i> Private</span>
@@ -13,7 +13,7 @@ export class ProjectStatusWithColorSelectionField extends SelectionField {
}
get currentValue() {
return this.props.value || this.options[0][0];
return this.props.record.data[this.props.name] || this.options[0][0];
}
statusColor(value) {
@@ -17,7 +17,7 @@
style="color: inherit;"
t-esc="subTask.data.display_name"
t-on-click.prevent="() => this.goToSubtask(subTask.resId)"/>
<Field value="subTask.data.user_ids"
<Field
class="`subtask_user_widget_col justify-content-end align-items-center`"
name="'user_ids'"
record="subTask"
@@ -12,7 +12,7 @@ export class PrioritySwitchField extends PriorityField {
{
category: "smart_action",
hotkey: "alt+r",
isAvailable: () => this.props.value !== id,
isAvailable: () => this.props.record.data[this.props.name] !== id,
},
]);
}
@@ -13,12 +13,13 @@ export class PurchaseOrderLineProductField extends Many2OneField {
setup() {
super.setup();
this.dialog = useService("dialog");
this.currentValue = this.value;
onWillUpdateProps(async (nextProps) => {
if (nextProps.record.mode === 'edit' && nextProps.value) {
if (nextProps.record.mode === 'edit' && nextProps.record.data[nextProps.name]) {
if (
!this.props.value ||
this.props.value[0] != nextProps.value[0]
!this.currentValue ||
this.currentValue[0] != nextProps.record.data[nextProps.name][0]
) {
// Field was updated if line was open in edit mode,
// field is not emptied,
@@ -27,6 +28,7 @@ export class PurchaseOrderLineProductField extends Many2OneField {
this._onProductTemplateUpdate();
}
}
this.currentValue = nextProps.record.data[nextProps.name];
});
}
@@ -37,7 +37,7 @@ export class ProductDiscountField extends FloatField {
return {
operation: "UPDATE",
record: line,
data: {["discount"]: this.props.value},
data: {["discount"]: this.props.record.data[this.props.name]},
};
});
@@ -14,7 +14,7 @@ export class SaleOrderLineProductField extends Many2OneField {
// NB: quick creation doesn't go through here, but through quickCreate
// below
if (value) {
if (Array.isArray(value[0]) && this.props.value != value[0]) {
if (Array.isArray(value[0]) && this.props.record.data[this.props.name] != value[0]) {
// product (existing)
newValue = true;
} else {
@@ -61,7 +61,7 @@ export class SaleOrderLineProductField extends Many2OneField {
// Keep external button, even if field is specified as 'no_open' so that the user is not
// redirected to the product when clicking on the field content
const res = super.hasExternalButton;
return res || (!!this.props.value && !this.state.isFloating);
return res || (!!this.props.record.data[this.props.name] && !this.state.isFloating);
}
get hasConfigurationButton() {
return this.isConfigurableLine || this.isConfigurableTemplate;
@@ -3,7 +3,7 @@
<t t-inherit="web.PhoneField" t-inherit-mode="extension">
<xpath expr="//div[hasclass('o_phone_content')]//a" position="after">
<t t-if="props.enableButton and props.value.length > 0">
<t t-if="props.enableButton and props.record.data[props.name].length > 0">
<SendSMSButton t-props="props" />
</t>
</xpath>
@@ -11,7 +11,7 @@
<t t-inherit="web.FormPhoneField" t-inherit-mode="extension">
<xpath expr="//div[hasclass('o_phone_content')]" position="inside">
<t t-if="props.enableButton and props.value.length > 0">
<t t-if="props.enableButton and props.record.data[props.name].length > 0">
<SendSMSButton t-props="props" />
</t>
</xpath>
@@ -20,10 +20,10 @@ export class SmsWidget extends EmojisTextField {
}
get encoding() {
return this._extractEncoding(this.props.value || '');
return this._extractEncoding(this.props.record.data[this.props.name] || '');
}
get nbrChar() {
const content = this.props.value || '';
const content = this.props.record.data[this.props.name] || '';
return content.length + (content.match(/\n/g) || []).length;
}
get nbrSMS() {
@@ -77,7 +77,7 @@ export class SmsWidget extends EmojisTextField {
* @private
*/
async onBlur() {
var content = this.props.value || '';
var content = this.props.record.data[this.props.name] || '';
if( !content.trim().length && content.length > 0) {
this.notification.add(
this.env._t("Your SMS Text Message must include at least one non-whitespace character"),
@@ -41,7 +41,7 @@ export class CountedQuantityWidgetField extends FloatField {
get formattedValue() {
if (
this.props.readonly &&
!this.props.value & !this.props.record.data.inventory_quantity_set
!this.props.record.data[this.props.name] & !this.props.record.data.inventory_quantity_set
) {
return "";
}
@@ -9,9 +9,9 @@ const { Component, onWillStart, onWillUpdateProps } = owl;
export class JsonPopOver extends Component {
setup(){
this.jsonValue = JSON.parse(this.props.value);
this.jsonValue = JSON.parse(this.props.record.data[this.props.name]);
onWillUpdateProps(nextProps => {
this.jsonValue = JSON.parse(nextProps.value);
this.jsonValue = JSON.parse(nextProps.record.data[nextProps.name]);
});
}
}
@@ -69,8 +69,8 @@ export class ProfilingQwebView extends Component {
* @returns {archs, data: {template, xpath, directive, time, duration, query }[]}
*/
get profile() {
if (this.props.value) {
return JSON.parse(this.props.value)[0].results;
if (this.props.record.data[this.props.name]) {
return JSON.parse(this.props.record.data[this.props.name])[0].results;
}
return { archs: {}, data: [] };
}
@@ -75,7 +75,7 @@ export class AceField extends Component {
this.aceEditor.on("blur", this.commitChanges.bind(this));
}
updateAce({ mode, readonly, value }) {
updateAce({ mode, readonly, record }) {
if (!this.aceEditor) {
return;
}
@@ -97,7 +97,7 @@ export class AceField extends Component {
this.aceEditor.renderer.$cursorLayer.element.style.display = readonly ? "none" : "block";
const formattedValue = formatText(value);
const formattedValue = formatText(record.data[this.props.name]);
if (this.aceSession.getValue() !== formattedValue) {
this.aceSession.setValue(formattedValue);
}
@@ -112,7 +112,7 @@ export class AceField extends Component {
commitChanges() {
if (!this.props.readonly) {
const value = this.aceSession.getValue();
if (this.props.value !== value) {
if (this.props.record.data[this.props.name] !== value) {
return this.props.record.update({ [this.props.name]: value });
}
}
@@ -3,7 +3,7 @@
<t t-name="web.AttachmentImageField" owl="1">
<div class="o_attachment_image">
<img t-if="props.value" t-attf-src="/web/image/{{ props.value[0] }}?unique=1" t-att-title="props.value[1]" alt="Image" />
<img t-if="props.record.data[props.name]" t-attf-src="/web/image/{{ props.record.data[props.name][0] }}?unique=1" t-att-title="props.record.data[props.name][1]" alt="Image" />
</div>
</t>
@@ -20,7 +20,7 @@ export class BadgeField extends Component {
get formattedValue() {
const formatter = formatters.get(this.props.record.fields[this.props.name].type);
return formatter(this.props.value, {
return formatter(this.props.record.data[this.props.name], {
selection: this.props.record.fields[this.props.name].selection,
});
}
@@ -2,7 +2,7 @@
<templates id="template" xml:space="preserve">
<t t-name="web.BadgeField" owl="1">
<span t-if="props.value" class="badge rounded-pill" t-att-class="classFromDecoration" t-esc="formattedValue" />
<span t-if="props.record.data[props.name]" class="badge rounded-pill" t-att-class="classFromDecoration" t-esc="formattedValue" />
</t>
</templates>
@@ -29,17 +29,19 @@ export class BadgeSelectionField extends Component {
get string() {
switch (this.props.record.fields[this.props.name].type) {
case "many2one":
return this.props.value ? this.props.value[1] : "";
return this.props.record.data[this.props.name]
? this.props.record.data[this.props.name][1]
: "";
case "selection":
return this.props.value !== false
? this.options.find((o) => o[0] === this.props.value)[1]
return this.props.record.data[this.props.name] !== false
? this.options.find((o) => o[0] === this.props.record.data[this.props.name])[1]
: "";
default:
return "";
}
}
get value() {
const rawValue = this.props.value;
const rawValue = this.props.record.data[this.props.name];
return this.props.record.fields[this.props.name].type === "many2one" && rawValue
? rawValue[0]
: rawValue;
@@ -36,7 +36,7 @@ export class BinaryField extends Component {
}
get fileName() {
return this.state.fileName || this.props.value || "";
return this.state.fileName || this.props.record.data[this.props.name] || "";
}
update({ data, name }) {
@@ -58,7 +58,9 @@ export class BinaryField extends Component {
filename_field: this.fileName,
filename: this.fileName || "",
download: true,
data: isBinarySize(this.props.value) ? null : this.props.value,
data: isBinarySize(this.props.record.data[this.props.name])
? null
: this.props.record.data[this.props.name],
},
url: "/web/content",
});
@@ -3,7 +3,7 @@
<t t-name="web.BinaryField" owl="1">
<t t-if="!props.readonly">
<t t-if="props.value">
<t t-if="props.record.data[props.name]">
<div class="w-100 d-inline-flex">
<FileUploader
acceptedFileExtensions="props.acceptedFileExtensions"
@@ -47,7 +47,7 @@
</label>
</t>
</t>
<t t-elif="props.record.resId and props.value">
<t t-elif="props.record.resId and props.record.data[props.name]">
<a class="o_form_uri" href="#" t-on-click.prevent="onFileDownload">
<span class="fa fa-download me-2" />
<t t-if="state.fileName" t-esc="state.fileName" />
@@ -2,7 +2,7 @@
<templates xml:space="preserve">
<t t-name="web.BooleanField" owl="1">
<CheckBox id="props.id" value="props.value or false" className="'d-inline-block me-2'" disabled="isReadonly" onChange.bind="onChange" />
<CheckBox id="props.id" value="props.record.data[props.name] or false" className="'d-inline-block me-2'" disabled="isReadonly" onChange.bind="onChange" />
</t>
</templates>
@@ -18,7 +18,7 @@ export class BooleanFavoriteField extends Component {
};
update() {
this.props.record.update({ [this.props.name]: !this.props.value });
this.props.record.update({ [this.props.name]: !this.props.record.data[this.props.name] });
}
}
@@ -7,11 +7,11 @@
<i
class="fa"
role="img"
t-att-class="props.value ? 'fa-star' : 'fa-star-o'"
t-att-title="props.value ? 'Remove from Favorites' : 'Add to Favorites'"
t-att-aria-label="props.value ? 'Remove from Favorites' : 'Add to Favorites'"
t-att-class="props.record.data[props.name] ? 'fa-star' : 'fa-star-o'"
t-att-title="props.record.data[props.name] ? 'Remove from Favorites' : 'Add to Favorites'"
t-att-aria-label="props.record.data[props.name] ? 'Remove from Favorites' : 'Add to Favorites'"
/>
<t t-if="!props.noLabel"> <t t-esc="props.value ? 'Remove from Favorites' : 'Add to Favorites'" /></t>
<t t-if="!props.noLabel"> <t t-esc="props.record.data[props.name] ? 'Remove from Favorites' : 'Add to Favorites'" /></t>
</a>
</div>
</t>
@@ -17,7 +17,7 @@ export class BooleanIconField extends Component {
};
update() {
this.props.record.update({ [this.props.name]: !this.props.value });
this.props.record.update({ [this.props.name]: !this.props.record.data[this.props.name] });
}
}
@@ -2,7 +2,7 @@
<templates xml:space="preserve">
<t t-name="web.BooleanIconField" owl="1">
<button t-attf-class="btn btn-sm btn-{{props.value ? 'primary' : 'outline-secondary'}} mx-2 py-1 fa {{props.icon}}" t-att-data-tooltip="props.label" t-on-click.prevent.stop="update" />
<button t-attf-class="btn btn-sm btn-{{props.record.data[props.name] ? 'primary' : 'outline-secondary'}} mx-2 py-1 fa {{props.icon}}" t-att-data-tooltip="props.label" t-on-click.prevent.stop="update" />
</t>
</templates>
@@ -8,7 +8,9 @@ export class ListBooleanToggleField extends BooleanToggleField {
onClick() {
if (!this.props.readonly) {
this.props.record.update({ [this.props.name]: !this.props.value });
this.props.record.update({
[this.props.name]: !this.props.record.data[this.props.name],
});
}
}
}
@@ -35,7 +35,10 @@ export class CharField extends Component {
dynamicPlaceholder.updateModel(this.props.dynamicPlaceholderModelReferenceField)
);
}
useInputField({ getValue: () => this.props.value || "", parse: (v) => this.parse(v) });
useInputField({
getValue: () => this.props.record.data[this.props.name] || "",
parse: (v) => this.parse(v),
});
}
get shouldTrim() {
@@ -48,7 +51,9 @@ export class CharField extends Component {
return this.props.record.fields[this.props.name].translate;
}
get formattedValue() {
return formatChar(this.props.value, { isPassword: this.props.isPassword });
return formatChar(this.props.record.data[this.props.name], {
isPassword: this.props.isPassword,
});
}
parse(value) {
@@ -13,11 +13,11 @@ export class ColorField extends Component {
setup() {
this.state = useState({
color: this.props.value || "",
color: this.props.record.data[this.props.name] || "",
});
onWillUpdateProps((nextProps) => {
this.state.color = nextProps.value || "";
this.state.color = nextProps.record.data[nextProps.name] || "";
});
}
@@ -2,7 +2,7 @@
<templates xml:space="preserve">
<t t-name="web.ColorPickerField" owl="1">
<ColorList canToggle="props.canToggle" colors="constructor.RECORD_COLORS" forceExpanded="isExpanded" onColorSelected.bind="switchColor" selectedColor="props.value || 0"/>
<ColorList canToggle="props.canToggle" colors="constructor.RECORD_COLORS" forceExpanded="isExpanded" onColorSelected.bind="switchColor" selectedColor="props.record.data[props.name] || 0"/>
</t>
</templates>
@@ -4,12 +4,12 @@
<t t-name="web.CopyClipboardField" owl="1">
<div class="d-grid rounded-2 overflow-hidden">
<Field t-props="fieldProps"/>
<CopyButton className="copyButtonClassName" content="props.value" copyText="copyText" successText="successText"/>
<CopyButton className="copyButtonClassName" content="props.record.data[props.name]" copyText="copyText" successText="successText"/>
</div>
</t>
<t t-name="web.CopyClipboardButtonField" owl="1">
<CopyButton t-if="props.value" className="copyButtonClassName" content="props.value" copyText="copyText" disabled="disabled" successText="successText"/>
<CopyButton t-if="props.record.data[props.name]" className="copyButtonClassName" content="props.record.data[props.name]" copyText="copyText" disabled="disabled" successText="successText"/>
</t>
</templates>
@@ -35,13 +35,18 @@ export class DateField extends Component {
return this.props.record.fields[this.props.name].type === "datetime";
}
get date() {
return this.props.value && this.props.value.startOf("day");
return (
this.props.record.data[this.props.name] &&
this.props.record.data[this.props.name].startOf("day")
);
}
get formattedValue() {
return this.isDateTime
? formatDateTime(this.props.value, { format: localization.dateFormat })
: formatDate(this.props.value);
? formatDateTime(this.props.record.data[this.props.name], {
format: localization.dateFormat,
})
: formatDate(this.props.record.data[this.props.name]);
}
onDateTimeChanged(date) {
@@ -59,7 +59,7 @@ export class DateRangeField extends Component {
}
};
},
() => [this.root.el, this.props.value]
() => [this.root.el, this.props.record.data[this.props.name]]
);
}
@@ -70,7 +70,7 @@ export class DateRangeField extends Component {
return this.formatType === "datetime";
}
get formattedValue() {
return this.formatValue(this.formatType, this.props.value);
return this.formatValue(this.formatType, this.props.record.data[this.props.name]);
}
get formattedEndDate() {
return this.formatValue(this.formatType, this.endDate);
@@ -31,11 +31,11 @@ export class DateTimeField extends Component {
}
get formattedValue() {
return formatDateTime(this.props.value);
return formatDateTime(this.props.record.data[this.props.name]);
}
onDateTimeChanged(date) {
if (!areDateEquals(this.props.value || "", date)) {
if (!areDateEquals(this.props.record.data[this.props.name] || "", date)) {
this.props.record.update({ [this.props.name]: date });
}
}
@@ -8,7 +8,7 @@
<t t-else="">
<DateTimePicker
t-props="props.pickerOptions"
date="props.value"
date="props.record.data[props.name]"
inputId="props.id"
placeholder="props.placeholder"
onDateTimeChanged="(datetime) => this.onDateTimeChanged(datetime)"
@@ -37,13 +37,13 @@ export class DomainField extends Component {
this.isDebugEdited = false;
onWillStart(() => {
this.displayedDomain = this.props.value;
this.displayedDomain = this.props.record.data[this.props.name];
this.loadCount(this.props);
});
onWillUpdateProps((nextProps) => {
this.isDebugEdited = this.isDebugEdited && this.props.readonly === nextProps.readonly;
if (!this.isDebugEdited) {
this.displayedDomain = nextProps.value;
this.displayedDomain = nextProps.record.data[nextProps.name];
this.loadCount(nextProps);
}
});
@@ -79,7 +79,10 @@ export class DomainField extends Component {
noCreate: true,
multiSelect: false,
resModel: this.getResModel(this.props),
domain: this.getDomain(this.props.value).toList(this.getContext(this.props)) || [],
domain:
this.getDomain(this.props.record.data[this.props.name]).toList(
this.getContext(this.props)
) || [],
context: this.getContext(this.props) || {},
},
{
@@ -90,7 +93,7 @@ export class DomainField extends Component {
}
get isValidDomain() {
try {
this.getDomain(this.props.value).toList();
this.getDomain(this.props.record.data[this.props.name]).toList();
return true;
} catch {
// WOWL TODO: rethrow error when not the expected type
@@ -108,7 +111,9 @@ export class DomainField extends Component {
let recordCount;
try {
const domain = this.getDomain(props.value).toList(this.getContext(props));
const domain = this.getDomain(props.record.data[props.name]).toList(
this.getContext(props)
);
recordCount = await this.orm.silent.call(
this.getResModel(props),
"search_count",
@@ -131,7 +136,7 @@ export class DomainField extends Component {
onEditDialogBtnClick() {
this.addDialog(DomainSelectorDialog, {
resModel: this.getResModel(this.props),
initialValue: this.props.value || "[]",
initialValue: this.props.record.data[this.props.name] || "[]",
readonly: this.props.readonly,
isDebugMode: !!this.env.debug,
onSelected: (value) => this.props.record.update({ [this.props.name]: value }),
@@ -10,7 +10,7 @@
readonly="props.readonly or props.editInDialog"
update.bind="update"
isDebugMode="!!env.debug"
debugValue="props.value || '[]'"
debugValue="props.record.data[props.name] || '[]'"
className="props.readonly ? 'o_read_mode' : 'o_edit_mode'"
/>
<div class="o_field_domain_panel">
@@ -15,7 +15,7 @@ export class EmailField extends Component {
};
setup() {
useInputField({ getValue: () => this.props.value || "" });
useInputField({ getValue: () => this.props.record.data[this.props.name] || "" });
}
}
@@ -3,7 +3,7 @@
<t t-name="web.EmailField" owl="1">
<t t-if="props.readonly">
<a class="o_form_uri o_text_overflow" t-on-click.stop="" t-att-href="props.value ? 'mailto:'+props.value : undefined" t-esc="props.value || ''"/>
<a class="o_form_uri o_text_overflow" t-on-click.stop="" t-att-href="props.record.data[props.name] ? 'mailto:'+props.record.data[props.name] : undefined" t-esc="props.record.data[props.name] || ''"/>
</t>
<t t-else="">
<div class="d-inline-flex w-100">
@@ -22,8 +22,8 @@
<t t-name="web.FormEmailField" t-inherit="web.EmailField" t-inherit-mode="primary">
<xpath expr="//input" position="after">
<a
t-if="props.value"
t-att-href="'mailto:'+props.value"
t-if="props.record.data[props.name]"
t-att-href="'mailto:'+props.record.data[props.name]"
class="ms-3 d-inline-flex align-items-center"
>
<i class="fa fa-envelope" data-tooltip="Send Email" aria-label="Send Email"></i>
@@ -140,7 +140,6 @@ export class Field extends Component {
delete props.type;
return {
value: this.props.record.data[this.props.name],
readonly: !record.isInEdition || readonlyFromModifiers || false,
...propsFromNode,
...props,
@@ -48,7 +48,7 @@ export class FloatField extends Component {
}
get value() {
return this.props.value;
return this.props.record.data[this.props.name];
}
}
@@ -22,7 +22,7 @@ export class FloatFactorField extends FloatField {
}
get value() {
return this.props.value * this.props.factor;
return this.props.record.data[this.props.name] * this.props.factor;
}
}
@@ -27,7 +27,7 @@ export class FloatTimeField extends Component {
}
get formattedValue() {
return formatFloatTime(this.props.value);
return formatFloatTime(this.props.record.data[this.props.name]);
}
}
@@ -24,7 +24,9 @@ export class FloatToggleField extends Component {
// TODO perf issue (because of update round trip)
// we probably want to have a state and a useEffect or onWillUpateProps
onChange() {
let currentIndex = this.props.range.indexOf(this.props.value * this.factor);
let currentIndex = this.props.range.indexOf(
this.props.record.data[this.props.name] * this.factor
);
currentIndex++;
if (currentIndex > this.props.range.length - 1) {
currentIndex = 0;
@@ -44,7 +46,7 @@ export class FloatToggleField extends Component {
return !this.props.digits && Array.isArray(fieldDigits) ? fieldDigits : this.props.digits;
}
get formattedValue() {
return formatFloat(this.props.value * this.factor, {
return formatFloat(this.props.record.data[this.props.name] * this.factor, {
digits: this.digits,
});
}
@@ -23,7 +23,9 @@ export class FontSelectionField extends Component {
return this.props.record.isRequired(this.props.name);
}
get string() {
return formatSelection(this.props.value, { selection: this.options });
return formatSelection(this.props.record.data[this.props.name], {
selection: this.options,
});
}
stringify(value) {
@@ -3,10 +3,10 @@
<t t-name="web.FontSelectionField" owl="1">
<t t-if="props.readonly">
<span t-esc="string" t-att-raw-value="props.value" t-attf-style="font-family:{{ props.value }};"/>
<span t-esc="string" t-att-raw-value="props.record.data[props.name]" t-attf-style="font-family:{{ props.record.data[props.name] }};"/>
</t>
<t t-else="">
<select class="o_input" t-on-change="onChange" t-attf-style="font-family:{{ props.value }};">
<select class="o_input" t-on-change="onChange" t-attf-style="font-family:{{ props.record.data[props.name] }};">
<option
t-att-selected="false === value"
t-att-value="stringify(false)"
@@ -3,7 +3,7 @@
<t t-name="web.HtmlField" owl="1">
<t t-if="props.readonly">
<span t-out="props.value or ''" />
<span t-out="props.record.data[props.name] or ''" />
</t>
<t t-else="" t-call="web.TextField" />
</t>
@@ -31,7 +31,7 @@ export class IframeWrapperField extends Component {
iframeDoc.write(value);
iframeDoc.close();
},
() => [this.props.value]
() => [this.props.record.data[this.props.name]]
);
}
}
@@ -81,7 +81,9 @@ export class ImageField extends Component {
return style;
}
get hasTooltip() {
return this.props.enableZoom && this.props.readonly && this.props.value;
return (
this.props.enableZoom && this.props.readonly && this.props.record.data[this.props.name]
);
}
get tooltipAttributes() {
return {
@@ -91,8 +93,8 @@ export class ImageField extends Component {
}
getUrl(previewFieldName) {
if (this.state.isValid && this.props.value) {
if (isBinarySize(this.props.value)) {
if (this.state.isValid && this.props.record.data[this.props.name]) {
if (isBinarySize(this.props.record.data[this.props.name])) {
if (!this.rawCacheKey) {
this.rawCacheKey = this.props.record.data.write_date;
}
@@ -104,8 +106,9 @@ export class ImageField extends Component {
});
} else {
// Use magic-word technique for detecting image type
const magic = fileTypeMagicWordMap[this.props.value[0]] || "png";
return `data:image/${magic};base64,${this.props.value}`;
const magic =
fileTypeMagicWordMap[this.props.record.data[this.props.name][0]] || "png";
return `data:image/${magic};base64,${this.props.record.data[this.props.name]}`;
}
}
return placeholder;
@@ -18,7 +18,7 @@
</button>
</t>
<button
t-if="props.value and state.isValid"
t-if="props.record.data[props.name] and state.isValid"
class="o_clear_file_button btn btn-light border-0 rounded-circle m-1 p-1"
data-tooltip="Clear"
aria-label="Clear"
@@ -20,12 +20,12 @@ export class ImageUrlField extends Component {
setup() {
this.notification = useService("notification");
this.state = useState({
src: this.props.value,
src: this.props.record.data[this.props.name],
});
onWillUpdateProps((nextProps) => {
if (this.props.value !== nextProps.value) {
this.state.value = nextProps.value;
if (this.props.record.data[this.props.name] !== nextProps.record.data[nextProps.name]) {
this.state.value = nextProps.record.data[nextProps.name];
}
});
}
@@ -3,7 +3,7 @@
<t t-name="web.ImageUrlField" owl="1">
<img
t-if="props.value"
t-if="props.record.data[props.name]"
class="img img-fluid"
alt="Image"
t-att-src="state.src"
@@ -154,7 +154,7 @@ export function useInputField(params) {
return;
}
if ((val || false) !== (component.props.value || false)) {
if ((val || false) !== (component.props.record.data[component.props.name] || false)) {
await component.props.record.update({ [component.props.name]: val });
lastSetValue = inputRef.el.value;
component.props.record.model.env.bus.trigger(
@@ -33,9 +33,9 @@ export class IntegerField extends Component {
get formattedValue() {
if (!this.props.readonly && this.props.inputType === "number") {
return this.props.value;
return this.props.record.data[this.props.name];
}
return formatInteger(this.props.value);
return formatInteger(this.props.record.data[this.props.name]);
}
}
@@ -19,7 +19,7 @@ export class JournalDashboardGraphField extends Component {
this.chart = null;
this.cookies = useService("cookie");
this.canvasRef = useRef("canvas");
this.data = JSON.parse(this.props.value);
this.data = JSON.parse(this.props.record.data[this.props.name]);
onWillStart(() => loadJS("/web/static/lib/Chart/Chart.js"));
@@ -18,10 +18,10 @@ export class LabelSelectionField extends Component {
};
get className() {
return this.props.classesObj[this.props.value] || "primary";
return this.props.classesObj[this.props.record.data[this.props.name]] || "primary";
}
get string() {
return formatSelection(this.props.value, {
return formatSelection(this.props.record.data[this.props.name], {
selection: Array.from(this.props.record.fields[this.props.name].selection),
});
}
@@ -2,7 +2,7 @@
<templates xml:space="preserve">
<t t-name="web.LabelSelectionField" owl="1">
<span t-attf-class="badge text-bg-{{className}} " t-esc="string" t-att-raw-value="props.value"/>
<span t-attf-class="badge text-bg-{{className}} " t-esc="string" t-att-raw-value="props.record.data[props.name]"/>
</t>
</templates>
@@ -22,14 +22,14 @@ export class Many2ManyBinaryField extends Component {
setup() {
this.orm = useService("orm");
this.notification = useService("notification");
this.operations = useX2ManyCrud(() => this.props.value, true);
this.operations = useX2ManyCrud(() => this.props.record.data[this.props.name], true);
}
get uploadText() {
return this.props.record.fields[this.props.name].string;
}
get files() {
return this.props.value.records.map((record) => record.data);
return this.props.record.data[this.props.name].records.map((record) => record.data);
}
getUrl(id) {
@@ -53,7 +53,9 @@ export class Many2ManyBinaryField extends Component {
}
async onFileRemove(deleteId) {
const record = this.props.value.records.find((record) => record.data.id === deleteId);
const record = this.props.record.data[this.props.name].records.find(
(record) => record.data.id === deleteId
);
this.operations.removeRecord(record);
}
}
@@ -19,19 +19,22 @@ export class Many2ManyCheckboxesField extends Component {
}
isSelected(item) {
return this.props.value.currentIds.includes(item[0]);
return this.props.record.data[this.props.name].currentIds.includes(item[0]);
}
onChange(resId, checked) {
if (checked) {
this.props.value.replaceWith([...this.props.value.currentIds, resId]);
this.props.record.data[this.props.name].replaceWith([
...this.props.record.data[this.props.name].currentIds,
resId,
]);
} else {
const currentIds = [...this.props.value.currentIds];
const currentIds = [...this.props.record.data[this.props.name].currentIds];
const index = currentIds.indexOf(resId);
if (index > -1) {
currentIds.splice(index, 1);
}
this.props.value.replaceWith(currentIds);
this.props.record.data[this.props.name].replaceWith(currentIds);
}
}
}
@@ -62,7 +62,10 @@ export class Many2ManyTagsField extends Component {
this.autoCompleteRef = useRef("autoComplete");
const { saveRecord, removeRecord } = useX2ManyCrud(() => this.props.value, true);
const { saveRecord, removeRecord } = useX2ManyCrud(
() => this.props.record.data[this.props.name],
true
);
this.activeActions = useActiveActions({
fieldType: "many2many",
@@ -128,7 +131,9 @@ export class Many2ManyTagsField extends Component {
}
get tags() {
return this.props.value.records.map((record) => this.getTagProps(record));
return this.props.record.data[this.props.name].records.map((record) =>
this.getTagProps(record)
);
}
get showM2OSelectionField() {
@@ -136,15 +141,19 @@ export class Many2ManyTagsField extends Component {
}
deleteTag(id) {
const tagRecord = this.props.value.records.find((record) => record.id === id);
const ids = this.props.value.currentIds.filter((id) => id !== tagRecord.resId);
this.props.value.replaceWith(ids);
const tagRecord = this.props.record.data[this.props.name].records.find(
(record) => record.id === id
);
const ids = this.props.record.data[this.props.name].currentIds.filter(
(id) => id !== tagRecord.resId
);
this.props.record.data[this.props.name].replaceWith(ids);
}
getDomain() {
return Domain.and([
this.domain,
Domain.not([["id", "in", this.props.value.currentIds]]),
Domain.not([["id", "in", this.props.record.data[this.props.name].currentIds]]),
]).toList(this.context);
}
@@ -339,7 +348,9 @@ export class Many2ManyTagsFieldColorEditable extends Many2ManyTagsField {
}
onTagVisibilityChange(isHidden, tag) {
const tagRecord = this.props.value.records.find((record) => record.id === tag.id);
const tagRecord = this.props.record.data[this.props.name].records.find(
(record) => record.id === tag.id
);
if (tagRecord.data[this.props.colorField] != 0) {
this.previousColorsMap[tagRecord.resId] = tagRecord.data[this.props.colorField];
}
@@ -351,7 +362,9 @@ export class Many2ManyTagsFieldColorEditable extends Many2ManyTagsField {
}
switchTagColor(colorIndex, tag) {
const tagRecord = this.props.value.records.find((record) => record.id === tag.id);
const tagRecord = this.props.record.data[this.props.name].records.find(
(record) => record.id === tag.id
);
tagRecord.update({ [this.props.colorField]: colorIndex });
tagRecord.save();
this.closePopover();
@@ -58,6 +58,7 @@ export class Many2OneField extends Component {
string: { type: String, optional: true },
canScanBarcode: { type: Boolean, optional: true },
update: { type: Function, optional: true },
value: { optional: true },
};
static defaultProps = {
canOpen: true,
@@ -86,7 +87,7 @@ export class Many2OneField extends Component {
};
this.state = useState({
isFloating: !this.props.value,
isFloating: !this.value,
});
this.computeActiveActions(this.props);
@@ -95,7 +96,7 @@ export class Many2OneField extends Component {
activeActions: this.state.activeActions,
isToMany: false,
onRecordSaved: async (record) => {
const resId = this.props.value[0];
const resId = this.value[0];
const fields = ["display_name"];
const context = this.props.record.getFieldContext(this.props.name);
const records = await this.orm.read(this.relation, [resId], fields, { context });
@@ -130,7 +131,8 @@ export class Many2OneField extends Component {
};
onWillUpdateProps(async (nextProps) => {
this.state.isFloating = !nextProps.value;
this.state.isFloating =
"value" in nextProps ? !nextProps.value : !nextProps.record.data[nextProps.name];
this.computeActiveActions(nextProps);
});
}
@@ -156,21 +158,24 @@ export class Many2OneField extends Component {
return this.props.record.getFieldDomain(this.props.name);
}
get hasExternalButton() {
return this.props.canOpen && !!this.props.value && !this.state.isFloating;
return this.props.canOpen && !!this.value && !this.state.isFloating;
}
get displayName() {
return this.props.value ? this.props.value[1].split("\n")[0] : "";
return this.value ? this.value[1].split("\n")[0] : "";
}
get extraLines() {
return this.props.value
? this.props.value[1]
return this.value
? this.value[1]
.split("\n")
.map((line) => line.trim())
.slice(1)
: [];
}
get resId() {
return this.props.value && this.props.value[0];
return this.value && this.value[0];
}
get value() {
return "value" in this.props ? this.props.value : this.props.record.data[this.props.name];
}
get Many2XAutocompleteProps() {
return {
@@ -26,9 +26,9 @@
</t>
<t t-else="">
<a
t-if="props.value"
t-if="value"
class="o_form_uri"
t-att-href="props.value ? `#id=${props.value[0]}&amp;model=${relation}` : '#'"
t-att-href="value ? `#id=${value[0]}&amp;model=${relation}` : '#'"
t-on-click.prevent="onClick"
>
<span t-esc="displayName" />
@@ -4,8 +4,8 @@
<t t-name="web.Many2OneAvatarField" owl="1">
<div class="d-flex">
<span class="o_m2o_avatar">
<span t-if="props.value === false and !props.readonly" class="o_m2o_avatar_empty"></span>
<img t-if="props.value !== false" t-attf-src="/web/image/{{relation}}/{{props.value[0]}}/avatar_128" />
<span t-if="props.record.data[props.name] === false and !props.readonly" class="o_m2o_avatar_empty"></span>
<img t-if="props.record.data[props.name] !== false" t-attf-src="/web/image/{{relation}}/{{props.record.data[props.name][0]}}/avatar_128" />
</span>
<Many2OneField t-props="many2OneProps" canOpen="!props.readonly"/>
</div>
@@ -65,10 +65,14 @@ export class MonetaryField extends Component {
}
get formattedValue() {
if (this.props.inputType === "number" && !this.props.readonly && this.props.value) {
return this.props.value;
if (
this.props.inputType === "number" &&
!this.props.readonly &&
this.props.record.data[this.props.name]
) {
return this.props.record.data[this.props.name];
}
return formatMonetary(this.props.value, {
return formatMonetary(this.props.record.data[this.props.name], {
digits: this.currencyDigits,
currencyId: this.currencyId,
noSymbol: !this.props.readonly || this.props.hideSymbol,
@@ -36,11 +36,11 @@ export class PdfViewerField extends Component {
}
get fileName() {
return this.state.fileName || this.props.value || "";
return this.state.fileName || this.props.record.data[this.props.name] || "";
}
get url() {
if (!this.state.isValid || !this.props.value) {
if (!this.state.isValid || !this.props.record.data[this.props.name]) {
return null;
}
const page = this.props.record.data[`${this.props.name}_page`] || 1;
@@ -4,7 +4,7 @@
<t t-name="web.PdfViewerField" owl="1">
<t t-if="!props.readonly">
<div class="o_form_pdf_controls">
<t t-if="props.value">
<t t-if="props.record.data[props.name]">
<FileUploader
acceptedFileExtensions="'.pdf'"
onUploaded.bind="onFileUploaded"
@@ -38,7 +38,7 @@
</t>
</div>
</t>
<t t-if="props.value">
<t t-if="props.record.data[props.name]">
<iframe class="o_pdfview_iframe"
alt="PDF file"
t-att-src="url"
@@ -14,7 +14,7 @@ export class PercentPieField extends Component {
};
get transform() {
const rotateDeg = (360 * this.props.value) / 100;
const rotateDeg = (360 * this.props.record.data[this.props.name]) / 100;
return {
left: rotateDeg < 180 ? 180 : rotateDeg,
right: rotateDeg < 180 ? rotateDeg : 0,
@@ -6,7 +6,7 @@
<div class="o_mask" t-attf-class="{{transform.value >= 360 ? 'o_full' : ''}}" t-attf-style="transform: rotate({{transform.left}}deg)"/>
<div class="o_mask" t-attf-class="{{transform.value >= 180 ? 'o_full' : ''}}" t-attf-style="transform: rotate({{transform.right}}deg)"/>
<div class="o_pie_value">
<span t-esc="props.value + '%'"/>
<span t-esc="props.record.data[props.name] + '%'"/>
</div>
</div>
<span t-esc="props.string"/>
@@ -21,7 +21,7 @@ export class PercentageField extends Component {
setup() {
useInputField({
getValue: () =>
formatPercentage(this.props.value, {
formatPercentage(this.props.record.data[this.props.name], {
digits: this.digits,
noSymbol: true,
}),
@@ -36,7 +36,7 @@ export class PercentageField extends Component {
return !this.props.digits && Array.isArray(fieldDigits) ? fieldDigits : this.props.digits;
}
get formattedValue() {
return formatPercentage(this.props.value, {
return formatPercentage(this.props.record.data[this.props.name], {
digits: this.digits,
});
}
@@ -15,7 +15,7 @@ export class PhoneField extends Component {
};
setup() {
useInputField({ getValue: () => this.props.value || "" });
useInputField({ getValue: () => this.props.record.data[this.props.name] || "" });
}
}
@@ -4,7 +4,7 @@
<t t-name="web.PhoneField" owl="1">
<div class="o_phone_content d-inline-flex w-100">
<t t-if="props.readonly">
<a t-if="props.value" class="o_form_uri" t-att-href="'tel:'+props.value" t-esc="props.value"/>
<a t-if="props.record.data[props.name]" class="o_form_uri" t-att-href="'tel:'+props.record.data[props.name]" t-esc="props.record.data[props.name]"/>
</t>
<t t-else="">
<input
@@ -21,8 +21,8 @@
<t t-name="web.FormPhoneField" t-inherit="web.PhoneField" t-inherit-mode="primary">
<xpath expr="//input" position="after">
<a
t-if="props.value"
t-att-href="'tel:'+props.value"
t-if="props.record.data[props.name]"
t-att-href="'tel:'+props.record.data[props.name]"
class="o_phone_form_link ms-3 d-inline-flex align-items-center"
>
<i class="fa fa-phone"></i><small class="fw-bold ms-1">Call</small>
@@ -60,7 +60,7 @@ export class PriorityField extends Component {
get index() {
return this.state.index > -1
? this.state.index
: this.options.findIndex((o) => o[0] === this.props.value);
: this.options.findIndex((o) => o[0] === this.props.record.data[this.props.name]);
}
get isReadonly() {
return this.props.record.isReadonly(this.props.name);
@@ -75,7 +75,7 @@ export class PriorityField extends Component {
* @param {string} value
*/
onStarClicked(value) {
if (this.props.value === value) {
if (this.props.record.data[this.props.name] === value) {
this.state.index = -1;
this.updateRecord(this.options[0][0]);
} else {
@@ -92,7 +92,9 @@ export class PropertiesField extends Component {
* @returns {array}
*/
get propertiesList() {
const propertiesValues = JSON.parse(JSON.stringify(this.props.value || []));
const propertiesValues = JSON.parse(
JSON.stringify(this.props.record.data[this.props.name] || [])
);
return propertiesValues.filter((definition) => !definition.definition_deleted);
}
@@ -428,7 +430,7 @@ export class PropertiesField extends Component {
// initial properties values, if the type or the model changed, the
// name will be regenerated in order to reset the value on the children
this.initialValues = {};
for (const propertiesValues of this.props.value || []) {
for (const propertiesValues of this.props.record.data[this.props.name] || []) {
this.initialValues[propertiesValues.name] = {
name: propertiesValues.name,
type: propertiesValues.type,
@@ -312,7 +312,7 @@ export class PropertyTagsField extends Component {
get propertyTagsProps() {
return {
selectedTags: this.props.value || [],
selectedTags: this.props.record.data[this.props.name] || [],
tags: this.props.record.fields[this.props.name].tags || [],
deleteAction: "value",
readonly: this.props.readonly,
@@ -42,9 +42,11 @@ export class RadioField extends Component {
get value() {
switch (this.props.record.fields[this.props.name].type) {
case "selection":
return this.props.value;
return this.props.record.data[this.props.name];
case "many2one":
return Array.isArray(this.props.value) ? this.props.value[0] : this.props.value;
return Array.isArray(this.props.record.data[this.props.name])
? this.props.record.data[this.props.name][0]
: this.props.record.data[this.props.name];
default:
return null;
}
@@ -28,14 +28,17 @@ export class ReferenceField extends Component {
resModel: this.relation,
});
this.currentValue = this.getValue(this.props);
onWillUpdateProps((nextProps) => {
if (
valuesEqual(this.getValue(this.props) || {}, this.getValue(nextProps) || {}) &&
valuesEqual(this.currentValue || {}, this.getValue(nextProps) || {}) &&
this.state.resModel &&
this.getRelation(nextProps) !== this.state.resModel
) {
nextProps.record.update({ [this.props.name]: false });
}
this.currentValue = this.getValue(this.props);
});
}
@@ -54,7 +57,7 @@ export class ReferenceField extends Component {
displayName: pdata.data.display_name,
};
} else {
return p.value;
return p.record.data[p.name];
}
}
get m2oProps() {
@@ -24,17 +24,21 @@ export class RemainingDaysField extends Component {
}
get diffDays() {
if (!this.props.value) {
if (!this.props.record.data[this.props.name]) {
return null;
}
const today = luxon.DateTime.local().startOf("day");
return Math.floor(this.props.value.startOf("day").diff(today, "days").days);
return Math.floor(
this.props.record.data[this.props.name].startOf("day").diff(today, "days").days
);
}
get formattedValue() {
return this.hasTime
? formatDateTime(this.props.value, { format: localization.dateFormat })
: formatDate(this.props.value);
? formatDateTime(this.props.record.data[this.props.name], {
format: localization.dateFormat,
})
: formatDate(this.props.record.data[this.props.name]);
}
onDateTimeChanged(datetime) {
@@ -25,7 +25,7 @@
</t>
<t t-else="">
<div>
<t t-component="pickerComponent" date="props.value or false" onDateTimeChanged.bind="onDateTimeChanged" />
<t t-component="pickerComponent" date="props.record.data[props.name] or false" onDateTimeChanged.bind="onDateTimeChanged" />
</div>
</t>
</t>

Some files were not shown because too many files have changed in this diff Show More