[MERGE] Merge from openerp-web.
bzr revid: jra@tinyerp.com-20110922101617-u1gz39qnnopj016r
This commit is contained in:
@@ -291,7 +291,7 @@ class Session(openerpweb.Controller):
|
||||
@openerpweb.jsonrequest
|
||||
def login(self, req, db, login, password):
|
||||
req.session.login(db, login, password)
|
||||
ctx = req.session.get_context()
|
||||
ctx = req.session.get_context() if req.session._uid else {}
|
||||
|
||||
return {
|
||||
"session_id": req.session_id,
|
||||
|
||||
@@ -892,6 +892,9 @@ label.error {
|
||||
.openerp .oe_forms input.field_datetime {
|
||||
min-width: 11em;
|
||||
}
|
||||
.openerp .oe_forms.oe_frame .oe_datepicker_root {
|
||||
width: 100%;
|
||||
}
|
||||
.openerp .oe_forms .button {
|
||||
color: #4c4c4c;
|
||||
white-space: nowrap;
|
||||
@@ -904,7 +907,14 @@ label.error {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
top: 3px;
|
||||
}
|
||||
.openerp .oe_datepicker_root {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
.openerp .oe_datepicker_root input[type="text"] {
|
||||
min-width: 160px;
|
||||
}
|
||||
.openerp .oe_input_icon_disabled {
|
||||
position: absolute;
|
||||
|
||||
@@ -581,7 +581,6 @@ openerp.web.Header = openerp.web.Widget.extend(/** @lends openerp.web.Header# *
|
||||
this._super(parent);
|
||||
this.qs = "?" + jQuery.param.querystring();
|
||||
this.$content = $();
|
||||
console.debug("initializing header with id", this.element_id);
|
||||
this.update_promise = $.Deferred().resolve();
|
||||
},
|
||||
start: function() {
|
||||
@@ -702,10 +701,11 @@ openerp.web.Header = openerp.web.Widget.extend(/** @lends openerp.web.Header# *
|
||||
},
|
||||
Save: function(){
|
||||
var inner_viewmanager = action_manager.inner_viewmanager;
|
||||
inner_viewmanager.views[inner_viewmanager.active_view].controller.do_save(function(){
|
||||
inner_viewmanager.start();
|
||||
inner_viewmanager.views[inner_viewmanager.active_view].controller.do_save()
|
||||
.then(function() {
|
||||
self.dialog.stop();
|
||||
window.location.reload();
|
||||
});
|
||||
$(this).dialog('destroy')
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -728,21 +728,13 @@ openerp.web.Header = openerp.web.Widget.extend(/** @lends openerp.web.Header# *
|
||||
submitHandler: function (form) {
|
||||
self.rpc("/web/session/change_password",{
|
||||
'fields': $(form).serializeArray()
|
||||
}, function(result) {
|
||||
if (result.error) {
|
||||
self.display_error(result);
|
||||
}, function(result) {
|
||||
if (result.error) {
|
||||
self.display_error(result);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (result.new_password) {
|
||||
self.session.password = result.new_password;
|
||||
var session = new openerp.web.Session(self.session.server, self.session.port);
|
||||
session.start();
|
||||
session.session_login(self.session.db, self.session.login, self.session.password)
|
||||
}
|
||||
}
|
||||
self.notification.notify("Changed Password", "Password has been changed successfully");
|
||||
self.dialog.close();
|
||||
} else {
|
||||
self.session.logout();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -963,7 +955,6 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
|
||||
this.session.start();
|
||||
this.login.start();
|
||||
this.menu.start();
|
||||
console.debug("The openerp client has been initialized.");
|
||||
},
|
||||
on_logged: function() {
|
||||
if(this.action_manager)
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
/*---------------------------------------------------------
|
||||
* OpenERP Web core
|
||||
*--------------------------------------------------------*/
|
||||
|
||||
var console;
|
||||
if (!console) {
|
||||
console = {log: function () {}};
|
||||
}
|
||||
if (!console.debug) {
|
||||
console.debug = console.log;
|
||||
}
|
||||
@@ -483,7 +486,8 @@ openerp.web.Session = openerp.web.CallbackEnabled.extend( /** @lends openerp.web
|
||||
self.user_context = result.context;
|
||||
self.db = result.db;
|
||||
self.session_save();
|
||||
self.on_session_valid();
|
||||
if (self.uid)
|
||||
self.on_session_valid();
|
||||
return true;
|
||||
}).then(success_callback);
|
||||
},
|
||||
@@ -771,7 +775,6 @@ openerp.web.SessionAware = openerp.web.CallbackEnabled.extend(/** @lends openerp
|
||||
* // stuff that you want to init before the rendering
|
||||
* },
|
||||
* start: function() {
|
||||
* this._super();
|
||||
* // stuff you want to make after the rendering, `this.$element` holds a correct value
|
||||
* this.$element.find(".my_button").click(/* an example of event binding * /);
|
||||
*
|
||||
@@ -915,6 +918,8 @@ openerp.web.Widget = openerp.web.SessionAware.extend(/** @lends openerp.web.Widg
|
||||
* @returns {jQuery.Deferred}
|
||||
*/
|
||||
start: function() {
|
||||
/* The default implementation is only useful for retro-compatibility, it is
|
||||
not necessary to call it using _super() when using Widget for new components. */
|
||||
if (!this.$element) {
|
||||
var tmp = document.getElementById(this.element_id);
|
||||
this.$element = tmp ? $(tmp) : undefined;
|
||||
@@ -922,7 +927,7 @@ openerp.web.Widget = openerp.web.SessionAware.extend(/** @lends openerp.web.Widg
|
||||
return $.Deferred().done().promise();
|
||||
},
|
||||
/**
|
||||
* Destroys the current widget, also destory all its children before destroying itself.
|
||||
* Destroys the current widget, also destroy all its children before destroying itself.
|
||||
*/
|
||||
stop: function() {
|
||||
_.each(_.clone(this.widget_children), function(el) {
|
||||
@@ -943,7 +948,6 @@ openerp.web.Widget = openerp.web.SessionAware.extend(/** @lends openerp.web.Widg
|
||||
* If that's not the case this method will simply return `false`.
|
||||
*/
|
||||
do_action: function(action, on_finished) {
|
||||
console.log('Widget.do_action', action, on_finished);
|
||||
if (this.widget_parent) {
|
||||
return this.widget_parent.do_action(action, on_finished);
|
||||
}
|
||||
|
||||
@@ -118,26 +118,26 @@ openerp.web.parse_value = function (value, descriptor, value_if_empty) {
|
||||
var tmp = Date.parseExact(value, _.sprintf("%s %s", Date.CultureInfo.formatPatterns.shortDate,
|
||||
Date.CultureInfo.formatPatterns.longTime));
|
||||
if (tmp !== null)
|
||||
return tmp;
|
||||
return openerp.web.datetime_to_str(tmp);
|
||||
tmp = Date.parse(value);
|
||||
if (tmp !== null)
|
||||
return tmp;
|
||||
return openerp.web.datetime_to_str(tmp);
|
||||
throw value + " is not a valid datetime";
|
||||
case 'date':
|
||||
var tmp = Date.parseExact(value, Date.CultureInfo.formatPatterns.shortDate);
|
||||
if (tmp !== null)
|
||||
return tmp;
|
||||
return openerp.web.date_to_str(tmp);
|
||||
tmp = Date.parse(value);
|
||||
if (tmp !== null)
|
||||
return tmp;
|
||||
return openerp.web.date_to_str(tmp);
|
||||
throw value + " is not a valid date";
|
||||
case 'time':
|
||||
var tmp = Date.parseExact(value, Date.CultureInfo.formatPatterns.longTime);
|
||||
if (tmp !== null)
|
||||
return tmp;
|
||||
return openerp.web.time_to_str(tmp);
|
||||
tmp = Date.parse(value);
|
||||
if (tmp !== null)
|
||||
return tmp;
|
||||
return openerp.web.time_to_str(tmp);
|
||||
throw value + " is not a valid time";
|
||||
}
|
||||
return value;
|
||||
|
||||
@@ -795,20 +795,18 @@ openerp.web.search.BooleanField = openerp.web.search.SelectionField.extend(/** @
|
||||
* @extends openerp.web.search.DateField
|
||||
*/
|
||||
openerp.web.search.DateField = openerp.web.search.Field.extend(/** @lends openerp.web.search.DateField# */{
|
||||
/**
|
||||
* enables date picker on the HTML widgets
|
||||
*/
|
||||
template: "SearchView.date",
|
||||
start: function () {
|
||||
this._super();
|
||||
this.$element.addClass('field_date').datepicker({
|
||||
dateFormat: 'yy-mm-dd'
|
||||
});
|
||||
},
|
||||
stop: function () {
|
||||
this.$element.datepicker('destroy');
|
||||
this.datewidget = new openerp.web.DateWidget(this);
|
||||
this.datewidget.prependTo(this.$element);
|
||||
this.datewidget.$element.find("input").attr("size", 15);
|
||||
this.datewidget.$element.find("input").attr("autofocus",
|
||||
this.attrs.default_focus === '1' ? 'autofocus' : undefined);
|
||||
this.datewidget.set_value(this.defaults[this.attrs.name] || false);
|
||||
},
|
||||
get_value: function () {
|
||||
return this.$element.val();
|
||||
return this.datewidget.get_value() || null;
|
||||
}
|
||||
});
|
||||
/**
|
||||
@@ -1123,7 +1121,7 @@ openerp.web.search.ExtendedSearchProposition.Char = openerp.web.OldWidget.extend
|
||||
}
|
||||
});
|
||||
openerp.web.search.ExtendedSearchProposition.DateTime = openerp.web.OldWidget.extend({
|
||||
template: 'SearchView.extended_search.proposition.datetime',
|
||||
template: 'SearchView.extended_search.proposition.empty',
|
||||
identifier_prefix: 'extended-search-proposition-datetime',
|
||||
operators: [
|
||||
{value: "=", text: "is equal to"},
|
||||
@@ -1134,18 +1132,16 @@ openerp.web.search.ExtendedSearchProposition.DateTime = openerp.web.OldWidget.ex
|
||||
{value: "<=", text: "less or equal than"}
|
||||
],
|
||||
get_value: function() {
|
||||
return this.$element.val();
|
||||
return this.datewidget.get_value();
|
||||
},
|
||||
start: function() {
|
||||
this._super();
|
||||
this.$element.datetimepicker({
|
||||
dateFormat: 'yy-mm-dd',
|
||||
timeFormat: 'hh:mm:ss'
|
||||
});
|
||||
this.datewidget = new openerp.web.DateTimeWidget(this);
|
||||
this.datewidget.prependTo(this.$element);
|
||||
}
|
||||
});
|
||||
openerp.web.search.ExtendedSearchProposition.Date = openerp.web.OldWidget.extend({
|
||||
template: 'SearchView.extended_search.proposition.date',
|
||||
template: 'SearchView.extended_search.proposition.empty',
|
||||
identifier_prefix: 'extended-search-proposition-date',
|
||||
operators: [
|
||||
{value: "=", text: "is equal to"},
|
||||
@@ -1156,14 +1152,12 @@ openerp.web.search.ExtendedSearchProposition.Date = openerp.web.OldWidget.extend
|
||||
{value: "<=", text: "less or equal than"}
|
||||
],
|
||||
get_value: function() {
|
||||
return this.$element.val();
|
||||
return this.datewidget.get_value();
|
||||
},
|
||||
start: function() {
|
||||
this._super();
|
||||
this.$element.datepicker({
|
||||
dateFormat: 'yy-mm-dd',
|
||||
timeFormat: 'hh:mm:ss'
|
||||
});
|
||||
this.datewidget = new openerp.web.DateWidget(this);
|
||||
this.datewidget.prependTo(this.$element);
|
||||
}
|
||||
});
|
||||
openerp.web.search.ExtendedSearchProposition.Integer = openerp.web.OldWidget.extend({
|
||||
|
||||
@@ -1007,19 +1007,18 @@ openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.f
|
||||
* the fields'context with the action's context.
|
||||
*/
|
||||
build_context: function() {
|
||||
// I previously belevied contexts should be herrited, but now I doubt it
|
||||
//var a_context = this.view.dataset.get_context() || {};
|
||||
var f_context = this.field.context || null;
|
||||
// maybe the default_get should only be used when we do a default_get?
|
||||
var v_context1 = this.node.attrs.default_get || {};
|
||||
var v_context2 = this.node.attrs.context || {};
|
||||
var v_context = new openerp.web.CompoundContext(v_context1, v_context2);
|
||||
if (v_context1.__ref || v_context2.__ref || true) { //TODO niv: remove || true
|
||||
var v_contexts = _.compact([this.node.attrs.default_get || null,
|
||||
this.node.attrs.context || null]);
|
||||
var v_context = new openerp.web.CompoundContext();
|
||||
_.each(v_contexts, function(x) {v_context.add(x);});
|
||||
if (_.detect(v_contexts, function(x) {return !!x.__ref;})) {
|
||||
var fields_values = this._build_view_fields_values();
|
||||
v_context.set_eval_context(fields_values);
|
||||
}
|
||||
// if there is a context on the node, overrides the model's context
|
||||
var ctx = f_context || v_context;
|
||||
var ctx = v_contexts.length > 0 ? v_context : f_context;
|
||||
return ctx;
|
||||
},
|
||||
build_domain: function() {
|
||||
@@ -1121,16 +1120,13 @@ openerp.web.form.FieldFloat = openerp.web.form.FieldChar.extend({
|
||||
}
|
||||
});
|
||||
|
||||
openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldDate";
|
||||
this.jqueryui_object = 'datetimepicker';
|
||||
},
|
||||
openerp.web.DateTimeWidget = openerp.web.Widget.extend({
|
||||
template: "web.datetimepicker",
|
||||
jqueryui_object: 'datetimepicker',
|
||||
type_of_date: "datetime",
|
||||
start: function() {
|
||||
var self = this;
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.find('input').change(this.on_ui_change);
|
||||
this.$element.find('input').change(this.on_change);
|
||||
this.picker({
|
||||
onSelect: this.on_picker_select,
|
||||
changeMonth: true,
|
||||
@@ -1148,6 +1144,8 @@ openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({
|
||||
this.$element.find('button.oe_datepicker_close').click(function() {
|
||||
self.$element.find('.oe_datepicker').hide();
|
||||
});
|
||||
this.set_readonly(false);
|
||||
this.value = false;
|
||||
},
|
||||
picker: function() {
|
||||
return $.fn[this.jqueryui_object].apply(this.$element.find('.oe_datepicker_container'), arguments);
|
||||
@@ -1157,60 +1155,93 @@ openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({
|
||||
this.$element.find('input').val(date ? this.format_client(date) : '').change();
|
||||
},
|
||||
set_value: function(value) {
|
||||
value = this.parse(value);
|
||||
this._super(value);
|
||||
this.value = value;
|
||||
this.$element.find('input').val(value ? this.format_client(value) : '');
|
||||
},
|
||||
get_value: function() {
|
||||
return this.format(this.value);
|
||||
return this.value;
|
||||
},
|
||||
set_value_from_ui: function() {
|
||||
var value = this.$element.find('input').val() || false;
|
||||
this.value = this.parse_client(value);
|
||||
this._super();
|
||||
},
|
||||
update_dom: function() {
|
||||
this._super.apply(this, arguments);
|
||||
set_readonly: function(readonly) {
|
||||
this.readonly = readonly;
|
||||
this.$element.find('input').attr('disabled', this.readonly);
|
||||
this.$element.find('img.oe_datepicker_trigger').toggleClass('oe_input_icon_disabled', this.readonly);
|
||||
this.$element.find('img.oe_datepicker_trigger').toggleClass('oe_input_icon_disabled', readonly);
|
||||
},
|
||||
validate: function() {
|
||||
this.invalid = false;
|
||||
is_valid: function(required) {
|
||||
var value = this.$element.find('input').val();
|
||||
if (value === "") {
|
||||
this.invalid = this.required;
|
||||
return !required;
|
||||
} else {
|
||||
try {
|
||||
this.parse_client(value);
|
||||
this.invalid = false;
|
||||
return true;
|
||||
} catch(e) {
|
||||
this.invalid = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
focus: function() {
|
||||
this.$element.find('input').focus();
|
||||
},
|
||||
parse: openerp.web.auto_str_to_date,
|
||||
parse_client: function(v) {
|
||||
return openerp.web.parse_value(v, this.field);
|
||||
},
|
||||
format: function(val) {
|
||||
return openerp.web.auto_date_to_str(val, this.field.type);
|
||||
return openerp.web.parse_value(v, {"widget": this.type_of_date});
|
||||
},
|
||||
format_client: function(v) {
|
||||
return openerp.web.format_value(v, this.field);
|
||||
return openerp.web.format_value(v, {"widget": this.type_of_date});
|
||||
},
|
||||
on_change: function() {
|
||||
if (this.is_valid()) {
|
||||
this.set_value_from_ui();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
openerp.web.DateWidget = openerp.web.DateTimeWidget.extend({
|
||||
jqueryui_object: 'datepicker',
|
||||
type_of_date: "date",
|
||||
on_picker_select: function(text, instance) {
|
||||
this._super(text, instance);
|
||||
this.$element.find('.oe_datepicker').hide();
|
||||
}
|
||||
});
|
||||
|
||||
openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({
|
||||
template: "EmptyComponent",
|
||||
build_widget: function() {
|
||||
return new openerp.web.DateTimeWidget(this);
|
||||
},
|
||||
start: function() {
|
||||
var self = this;
|
||||
this._super.apply(this, arguments);
|
||||
this.datewidget = this.build_widget();
|
||||
this.datewidget.on_change.add(this.on_ui_change);
|
||||
this.datewidget.appendTo(this.$element);
|
||||
},
|
||||
set_value: function(value) {
|
||||
this._super(value);
|
||||
this.datewidget.set_value(value);
|
||||
},
|
||||
get_value: function() {
|
||||
return this.datewidget.get_value();
|
||||
},
|
||||
update_dom: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.datewidget.set_readonly(this.readonly);
|
||||
},
|
||||
validate: function() {
|
||||
this.invalid = !this.datewidget.is_valid(this.required);
|
||||
},
|
||||
focus: function() {
|
||||
this.datewidget.focus();
|
||||
}
|
||||
});
|
||||
|
||||
openerp.web.form.FieldDate = openerp.web.form.FieldDatetime.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.jqueryui_object = 'datepicker';
|
||||
},
|
||||
on_picker_select: function(text, instance) {
|
||||
this._super(text, instance);
|
||||
this.$element.find('.oe_datepicker').hide();
|
||||
build_widget: function() {
|
||||
return new openerp.web.DateWidget(this);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -415,8 +415,8 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
|
||||
*/
|
||||
do_search: function (domains, contexts, groupbys) {
|
||||
return this.rpc('/web/session/eval_domain_and_context', {
|
||||
domains: [this.dataset.get_domain()].concat(domains),
|
||||
contexts: [this.dataset.get_context()].concat(contexts),
|
||||
domains: _([this.dataset.get_domain()].concat(domains)).compact(),
|
||||
contexts: _([this.dataset.get_context()].concat(contexts)).compact(),
|
||||
group_by_seq: groupbys
|
||||
}, $.proxy(this, 'do_actual_search'));
|
||||
},
|
||||
@@ -541,6 +541,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
|
||||
if (_.isEmpty(records)) {
|
||||
records = this.groups.get_records();
|
||||
}
|
||||
records = _(records).compact();
|
||||
|
||||
var count = 0, sums = {};
|
||||
_(columns).each(function (column) {
|
||||
@@ -1235,6 +1236,9 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L
|
||||
},
|
||||
get_records: function () {
|
||||
if (_(this.children).isEmpty()) {
|
||||
if (!this.datagroup.length) {
|
||||
return;
|
||||
}
|
||||
return {
|
||||
count: this.datagroup.length,
|
||||
values: this.datagroup.aggregates
|
||||
|
||||
@@ -800,13 +800,15 @@
|
||||
></textarea>
|
||||
<img class="oe_field_translate" t-if="widget.field.translate" src="/web/static/src/img/icons/terp-translate.png" width="16" height="16" border="0"/>
|
||||
</t>
|
||||
<t t-name="FieldDate">
|
||||
<t t-call="FieldChar"/>
|
||||
<img class="oe_input_icon oe_datepicker_trigger" src="/web/static/src/img/ui/field_calendar.png"
|
||||
title="Select date" width="16" height="16" border="0"/>
|
||||
<div class="oe_datepicker ui-widget-content ui-corner-all" style="display: none; position: absolute; z-index: 1;">
|
||||
<div class="oe_datepicker_container"/>
|
||||
<button type="button" class="oe_datepicker_close ui-state-default ui-priority-primary ui-corner-all" style="float: right;">Done</button>
|
||||
<t t-name="web.datetimepicker">
|
||||
<div class="oe_datepicker_root">
|
||||
<input type="text" size="1" style="width: 100%"/>
|
||||
<img class="oe_input_icon oe_datepicker_trigger" src="/web/static/src/img/ui/field_calendar.png"
|
||||
title="Select date" width="16" height="16" border="0"/>
|
||||
<div class="oe_datepicker ui-widget-content ui-corner-all" style="display: none; position: absolute; z-index: 1;">
|
||||
<div class="oe_datepicker_container"/>
|
||||
<button type="button" class="oe_datepicker_close ui-state-default ui-priority-primary ui-corner-all" style="float: right;">Done</button>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="FieldSelection">
|
||||
@@ -1051,6 +1053,18 @@
|
||||
<t t-if="filters.length" t-raw="filters.render(defaults)"/>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="SearchView.date">
|
||||
<label t-att-class="'oe_label' + (attrs.help ? '_help' : '')"
|
||||
t-att-title="attrs.help"
|
||||
t-att-for="element_id">
|
||||
<t t-esc="attrs.string || attrs.name"/>
|
||||
<span t-if="attrs.help">?</span>
|
||||
</label>
|
||||
<div style="white-space: nowrap;">
|
||||
<span t-att-id="element_id"></span>
|
||||
<t t-if="filters.length" t-raw="filters.render(defaults)"/>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="SearchView.field.selection">
|
||||
<label t-att-title="attrs.help"
|
||||
t-att-class="'oe_label' + (attrs.help ? '_help' : '')"
|
||||
@@ -1142,11 +1156,8 @@
|
||||
<t t-name="SearchView.extended_search.proposition.char">
|
||||
<input t-att-id="element_id" class="field_char"/>
|
||||
</t>
|
||||
<t t-name="SearchView.extended_search.proposition.datetime">
|
||||
<input t-att-id="element_id" class="field_datetime"/>
|
||||
</t>
|
||||
<t t-name="SearchView.extended_search.proposition.date">
|
||||
<input t-att-id="element_id" class="field_date"/>
|
||||
<t t-name="SearchView.extended_search.proposition.empty">
|
||||
<span t-att-id="element_id"></span>
|
||||
</t>
|
||||
<t t-name="SearchView.extended_search.proposition.integer">
|
||||
<input type="number" t-att-id="element_id" class="field_integer" step="1"/>
|
||||
|
||||
Reference in New Issue
Block a user