[16.0][MIG]web_widget_dropdown_dynamic: Migrate to version 16.0

pull/2915/head
sonhd91 2023-07-06 10:04:24 +07:00 committed by Carlos Roca
parent ccaa9f55dd
commit 237c7af396
15 changed files with 383 additions and 418 deletions

View File

@ -2,10 +2,13 @@
Dynamic Dropdown Widget Dynamic Dropdown Widget
======================= =======================
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !! !! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !! !! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:e2b8eb236b4dc206f3a4ab9b5932f86c2e46c33bdac31c385725384f3fe970e3
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status :target: https://odoo-community.org/page/development-status
@ -14,14 +17,14 @@ Dynamic Dropdown Widget
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3 :alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github
:target: https://github.com/OCA/web/tree/15.0/web_widget_dropdown_dynamic :target: https://github.com/OCA/web/tree/16.0/web_widget_dropdown_dynamic
:alt: OCA/web :alt: OCA/web
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/web-15-0/web-15-0-web_widget_dropdown_dynamic :target: https://translation.odoo-community.org/projects/web-16-0/web-16-0-web_widget_dropdown_dynamic
:alt: Translate me on Weblate :alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png .. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/162/15.0 :target: https://runboat.odoo-community.org/builds?repo=OCA/web&target_branch=16.0
:alt: Try me on Runbot :alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5| |badge1| |badge2| |badge3| |badge4| |badge5|
@ -64,7 +67,7 @@ Usage
<field <field
name="char_field" name="char_field"
widget="dynamic_dropdown" widget="dynamic_dropdown"
values="method_name" options="{'values':'method_name'}"
context="{'depending_on': other_field}" context="{'depending_on': other_field}"
/> />
@ -73,8 +76,8 @@ Bug Tracker
Bugs are tracked on `GitHub Issues <https://github.com/OCA/web/issues>`_. Bugs are tracked on `GitHub Issues <https://github.com/OCA/web/issues>`_.
In case of trouble, please check there if your issue has already been reported. In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/web/issues/new?body=module:%20web_widget_dropdown_dynamic%0Aversion:%2015.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_. `feedback <https://github.com/OCA/web/issues/new?body=module:%20web_widget_dropdown_dynamic%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues. Do not contact contributors directly about support or help with technical issues.
@ -98,6 +101,14 @@ Contributors
* Ronald Portier <ronald@therp.nl> * Ronald Portier <ronald@therp.nl>
* Thanakrit Pintana <thanakrit.p39@gmail.com> * Thanakrit Pintana <thanakrit.p39@gmail.com>
* `Trobz <https://trobz.com>`_:
* Son Ho <sonho@trobz.com>
Other credits
~~~~~~~~~~~~~
The migration of this module from 15.0 to 16.0 was financially supported by Camptocamp
Maintainers Maintainers
~~~~~~~~~~~ ~~~~~~~~~~~
@ -112,6 +123,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and mission is to support the collaborative development of Odoo features and
promote its widespread use. promote its widespread use.
This module is part of the `OCA/web <https://github.com/OCA/web/tree/15.0/web_widget_dropdown_dynamic>`_ project on GitHub. This module is part of the `OCA/web <https://github.com/OCA/web/tree/16.0/web_widget_dropdown_dynamic>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@ -13,11 +13,7 @@
"installable": True, "installable": True,
"assets": { "assets": {
"web.assets_backend": [ "web.assets_backend": [
"web_widget_dropdown_dynamic/static/src/js/basic_model.js", "web_widget_dropdown_dynamic/**/*",
"web_widget_dropdown_dynamic/static/src/js/field_dynamic_dropdown.js",
],
"web.qunit_suite_tests": [
"web_widget_dropdown_dynamic/static/tests/web_widget_dropdown_dynamic_tests.js"
], ],
}, },
} }

View File

@ -17,8 +17,8 @@ msgstr ""
"X-Generator: Weblate 4.3.2\n" "X-Generator: Weblate 4.3.2\n"
#. module: web_widget_dropdown_dynamic #. module: web_widget_dropdown_dynamic
#. openerp-web #. odoo-javascript
#: code:addons/web_widget_dropdown_dynamic/static/src/js/field_dynamic_dropdown.js:0 #: code:addons/web_widget_dropdown_dynamic/static/src/js/field_dynamic_dropdown.esm.js:0
#, python-format #, python-format
msgid "Dynamic Dropdown" msgid "Dynamic Dropdown"
msgstr "Dynamisches Dropdown" msgstr "Dynamisches Dropdown"

View File

@ -17,8 +17,8 @@ msgstr ""
"X-Generator: Weblate 4.3.2\n" "X-Generator: Weblate 4.3.2\n"
#. module: web_widget_dropdown_dynamic #. module: web_widget_dropdown_dynamic
#. openerp-web #. odoo-javascript
#: code:addons/web_widget_dropdown_dynamic/static/src/js/field_dynamic_dropdown.js:0 #: code:addons/web_widget_dropdown_dynamic/static/src/js/field_dynamic_dropdown.esm.js:0
#, python-format #, python-format
msgid "Dynamic Dropdown" msgid "Dynamic Dropdown"
msgstr "Menú desplegable dinámico" msgstr "Menú desplegable dinámico"

View File

@ -4,7 +4,7 @@
# #
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Odoo Server 15.0\n" "Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: \n" "Language-Team: \n"
@ -14,8 +14,8 @@ msgstr ""
"Plural-Forms: \n" "Plural-Forms: \n"
#. module: web_widget_dropdown_dynamic #. module: web_widget_dropdown_dynamic
#. openerp-web #. odoo-javascript
#: code:addons/web_widget_dropdown_dynamic/static/src/js/field_dynamic_dropdown.js:0 #: code:addons/web_widget_dropdown_dynamic/static/src/js/field_dynamic_dropdown.esm.js:0
#, python-format #, python-format
msgid "Dynamic Dropdown" msgid "Dynamic Dropdown"
msgstr "" msgstr ""

View File

@ -7,3 +7,6 @@
* Ronald Portier <ronald@therp.nl> * Ronald Portier <ronald@therp.nl>
* Thanakrit Pintana <thanakrit.p39@gmail.com> * Thanakrit Pintana <thanakrit.p39@gmail.com>
* `Trobz <https://trobz.com>`_:
* Son Ho <sonho@trobz.com>

View File

@ -0,0 +1 @@
The migration of this module from 15.0 to 16.0 was financially supported by Camptocamp

View File

@ -19,6 +19,6 @@
<field <field
name="char_field" name="char_field"
widget="dynamic_dropdown" widget="dynamic_dropdown"
values="method_name" options="{'values':'method_name'}"
context="{'depending_on': other_field}" context="{'depending_on': other_field}"
/> />

View File

@ -3,7 +3,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.15.1: http://docutils.sourceforge.net/" /> <meta name="generator" content="Docutils: http://docutils.sourceforge.net/" />
<title>Dynamic Dropdown Widget</title> <title>Dynamic Dropdown Widget</title>
<style type="text/css"> <style type="text/css">
@ -366,8 +366,10 @@ ul.auto-toc {
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! <!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !! !! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !! !! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:e2b8eb236b4dc206f3a4ab9b5932f86c2e46c33bdac31c385725384f3fe970e3
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/web/tree/15.0/web_widget_dropdown_dynamic"><img alt="OCA/web" src="https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/web-15-0/web-15-0-web_widget_dropdown_dynamic"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/162/15.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p> <p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/web/tree/16.0/web_widget_dropdown_dynamic"><img alt="OCA/web" src="https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/web-16-0/web-16-0-web_widget_dropdown_dynamic"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runboat.odoo-community.org/builds?repo=OCA/web&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>Dynamic dropdown widget that supports resolving options from backend of:</p> <p>Dynamic dropdown widget that supports resolving options from backend of:</p>
<blockquote> <blockquote>
<ul class="simple"> <ul class="simple">
@ -387,7 +389,8 @@ instead.</p>
<li><a class="reference internal" href="#credits" id="id3">Credits</a><ul> <li><a class="reference internal" href="#credits" id="id3">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id4">Authors</a></li> <li><a class="reference internal" href="#authors" id="id4">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id5">Contributors</a></li> <li><a class="reference internal" href="#contributors" id="id5">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id6">Maintainers</a></li> <li><a class="reference internal" href="#other-credits" id="id6">Other credits</a></li>
<li><a class="reference internal" href="#maintainers" id="id7">Maintainers</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>
@ -413,7 +416,7 @@ instead.</p>
<span class="nt">&lt;field</span> <span class="nt">&lt;field</span>
<span class="na">name=</span><span class="s">&quot;char_field&quot;</span> <span class="na">name=</span><span class="s">&quot;char_field&quot;</span>
<span class="na">widget=</span><span class="s">&quot;dynamic_dropdown&quot;</span> <span class="na">widget=</span><span class="s">&quot;dynamic_dropdown&quot;</span>
<span class="na">values=</span><span class="s">&quot;method_name&quot;</span> <span class="na">options=</span><span class="s">&quot;{'values':'method_name'}&quot;</span>
<span class="na">context=</span><span class="s">&quot;{'depending_on': other_field}&quot;</span> <span class="na">context=</span><span class="s">&quot;{'depending_on': other_field}&quot;</span>
<span class="nt">/&gt;</span> <span class="nt">/&gt;</span>
</pre> </pre>
@ -422,8 +425,8 @@ instead.</p>
<h1><a class="toc-backref" href="#id2">Bug Tracker</a></h1> <h1><a class="toc-backref" href="#id2">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/web/issues">GitHub Issues</a>. <p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/web/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported. In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed If you spotted it first, help us to smash it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/web/issues/new?body=module:%20web_widget_dropdown_dynamic%0Aversion:%2015.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p> <a class="reference external" href="https://github.com/OCA/web/issues/new?body=module:%20web_widget_dropdown_dynamic%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p> <p>Do not contact contributors directly about support or help with technical issues.</p>
</div> </div>
<div class="section" id="credits"> <div class="section" id="credits">
@ -436,26 +439,40 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
</div> </div>
<div class="section" id="contributors"> <div class="section" id="contributors">
<h2><a class="toc-backref" href="#id5">Contributors</a></h2> <h2><a class="toc-backref" href="#id5">Contributors</a></h2>
<ul>
<li><p class="first"><a class="reference external" href="https://corporatehub.eu/">CorporateHub</a></p>
<ul class="simple"> <ul class="simple">
<li><a class="reference external" href="https://corporatehub.eu/">CorporateHub</a><ul>
<li>Alexey Pelykh &lt;<a class="reference external" href="mailto:alexey.pelykh&#64;corphub.eu">alexey.pelykh&#64;corphub.eu</a>&gt;</li> <li>Alexey Pelykh &lt;<a class="reference external" href="mailto:alexey.pelykh&#64;corphub.eu">alexey.pelykh&#64;corphub.eu</a>&gt;</li>
</ul> </ul>
</li> </li>
<li><a class="reference external" href="https://therp.nl/">Therp BV</a><ul> <li><p class="first"><a class="reference external" href="https://therp.nl/">Therp BV</a></p>
<ul class="simple">
<li>Ronald Portier &lt;<a class="reference external" href="mailto:ronald&#64;therp.nl">ronald&#64;therp.nl</a>&gt;</li> <li>Ronald Portier &lt;<a class="reference external" href="mailto:ronald&#64;therp.nl">ronald&#64;therp.nl</a>&gt;</li>
</ul> </ul>
</li> </li>
<li>Thanakrit Pintana &lt;<a class="reference external" href="mailto:thanakrit.p39&#64;gmail.com">thanakrit.p39&#64;gmail.com</a>&gt;</li> <li><p class="first">Thanakrit Pintana &lt;<a class="reference external" href="mailto:thanakrit.p39&#64;gmail.com">thanakrit.p39&#64;gmail.com</a>&gt;</p>
</li>
<li><p class="first"><a class="reference external" href="https://trobz.com">Trobz</a>:</p>
<blockquote>
<ul class="simple">
<li>Son Ho &lt;<a class="reference external" href="mailto:sonho&#64;trobz.com">sonho&#64;trobz.com</a>&gt;</li>
</ul>
</blockquote>
</li>
</ul> </ul>
</div> </div>
<div class="section" id="other-credits">
<h2><a class="toc-backref" href="#id6">Other credits</a></h2>
<p>The migration of this module from 15.0 to 16.0 was financially supported by Camptocamp</p>
</div>
<div class="section" id="maintainers"> <div class="section" id="maintainers">
<h2><a class="toc-backref" href="#id6">Maintainers</a></h2> <h2><a class="toc-backref" href="#id7">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p> <p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a> <a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose <p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and mission is to support the collaborative development of Odoo features and
promote its widespread use.</p> promote its widespread use.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/web/tree/15.0/web_widget_dropdown_dynamic">OCA/web</a> project on GitHub.</p> <p>This module is part of the <a class="reference external" href="https://github.com/OCA/web/tree/16.0/web_widget_dropdown_dynamic">OCA/web</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p> <p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div> </div>
</div> </div>

View File

@ -0,0 +1,50 @@
/** @odoo-module **/
import BasicModel from "web.BasicModel";
BasicModel.include({
/**
* Fetches all the values associated to the given fieldName.
*
* @param {Object} record - an element from the localData
* @param {Object} fieldName - the name of the field
* @param {Object} fieldInfo
* @returns {Promise<any>}
* The promise is resolved with the fetched special values.
* If this data is the same as the previously fetched one
* (for the given parameters), no RPC is done and the promise
* is resolved with the undefined value.
*/
_fetchDynamicDropdownValues: function (record, fieldName, fieldInfo) {
var model = fieldInfo.options.model || record.model;
var method = fieldInfo.values || fieldInfo.options.values;
if (!method) {
return Promise.resolve();
}
var context = record.getContext({fieldName: fieldName});
// Avoid rpc if not necessary
var hasChanged = this._saveSpecialDataCache(record, fieldName, {
context: context,
});
if (!hasChanged) {
return Promise.resolve();
}
return this._rpc({
model: model,
method: method,
context: context,
}).then(function (result) {
var new_result = result.map((val_updated) => {
return val_updated.map((e) => {
if (typeof e !== "string") {
return String(e);
}
return e;
});
});
return new_result;
});
},
});

View File

@ -1,47 +0,0 @@
/*
* Copyright 2019-2020 Brainbean Apps (https://brainbeanapps.com)
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
*/
odoo.define("web_widget_dropdown_dynamic.basic_model", function (require) {
"use strict";
var BasicModel = require("web.BasicModel");
BasicModel.include({
/**
* Fetches all the values associated to the given fieldName.
*
* @param {Object} record - an element from the localData
* @param {Object} fieldName - the name of the field
* @param {Object} fieldInfo
* @returns {Promise<any>}
* The promise is resolved with the fetched special values.
* If this data is the same as the previously fetched one
* (for the given parameters), no RPC is done and the promise
* is resolved with the undefined value.
*/
_fetchDynamicDropdownValues: function (record, fieldName, fieldInfo) {
var model = fieldInfo.options.model || record.model;
var method = fieldInfo.values || fieldInfo.options.values;
if (!method) {
return Promise.resolve();
}
var context = record.getContext({fieldName: fieldName});
// Avoid rpc if not necessary
var hasChanged = this._saveSpecialDataCache(record, fieldName, {
context: context,
});
if (!hasChanged) {
return Promise.resolve();
}
return this._rpc({
model: model,
method: method,
context: context,
});
},
});
});

View File

@ -0,0 +1,88 @@
/** @odoo-module **/
import core from "web.core";
import {registry} from "@web/core/registry";
import {standardFieldProps} from "@web/views/fields/standard_field_props";
import {Component} from "@odoo/owl";
var _lt = core._lt;
export class FieldDynamicDropdown extends Component {
get options() {
var field_type = this.props.record.fields[this.props.name].type || "";
if (["char", "integer", "selection"].includes(field_type)) {
this._setValues();
return this.props.record.fields[this.props.name].selection.filter(
(option) => option[0] !== false && option[1] !== ""
);
}
return [];
}
get value() {
const rawValue = this.props.value;
this.props.setDirty(false);
return this.props.type === "many2one" && rawValue ? rawValue[0] : rawValue;
}
parseInteger(value) {
return Number(value);
}
/**
* @param {Event} ev
*/
onChange(ev) {
let lastSetValue = null;
let isInvalid = false;
var isDirty = ev.target.value !== lastSetValue;
const field = this.props.record.fields[this.props.name];
let value = JSON.parse(ev.target.value);
if (isDirty) {
if (value && field.type === "integer") {
value = Number(value);
if (!value) {
if (this.props.record) {
this.props.record.setInvalidField(this.props.name);
}
isInvalid = true;
}
}
if (!isInvalid) {
Promise.resolve(this.props.update(value));
lastSetValue = ev.target.value;
}
}
if (this.props.setDirty) {
this.props.setDirty(isDirty);
}
}
stringify(value) {
return JSON.stringify(value);
}
_setValues() {
if (this.props.record.preloadedData[this.props.name]) {
var sel_value = this.props.record.preloadedData[this.props.name];
// Convert string element to integer if field is integer
if (this.props.record.fields[this.props.name].type === "integer") {
sel_value = sel_value.map((val_updated) => {
return val_updated.map((e) => {
if (typeof e === "string" && !isNaN(Number(e))) {
return Number(e);
}
return e;
});
});
}
this.props.record.fields[this.props.name].selection = sel_value;
}
}
}
FieldDynamicDropdown.description = _lt("Dynamic Dropdown");
FieldDynamicDropdown.template = "web.SelectionField";
FieldDynamicDropdown.legacySpecialData = "_fetchDynamicDropdownValues";
FieldDynamicDropdown.props = {
...standardFieldProps,
};
FieldDynamicDropdown.supportedTypes = ["char", "integer", "selection"];
registry.category("fields").add("dynamic_dropdown", FieldDynamicDropdown);

View File

@ -1,149 +0,0 @@
/*
* Copyright 2019 Brainbean Apps (https://brainbeanapps.com)
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
*/
odoo.define("web_widget_dropdown_dynamic.field_dynamic_dropdown", function (require) {
"use strict";
var core = require("web.core");
var AbstractField = require("web.AbstractField");
var field_registry = require("web.field_registry");
var _lt = core._lt;
var FieldDynamicDropdown = AbstractField.extend({
description: _lt("Dynamic Dropdown"),
template: "FieldSelection",
specialData: "_fetchDynamicDropdownValues",
supportedFieldTypes: ["selection", "char", "integer"],
events: _.extend({}, AbstractField.prototype.events, {
change: "_onChange",
}),
/**
* @override
*/
init: function () {
this._super.apply(this, arguments);
this._setValues();
},
// --------------------------------------------------------------------------
// Public
// --------------------------------------------------------------------------
/**
* @override
* @returns {jQuery}
*/
getFocusableElement: function () {
return this.$el.is("select") ? this.$el : $();
},
/**
* @override
*/
isSet: function () {
return this.value !== false;
},
/**
* Listen to modifiers updates to hide/show the falsy value in the dropdown
* according to the required modifier.
*
* @override
*/
updateModifiersValue: function () {
this._super.apply(this, arguments);
if (!this.attrs.modifiersValue.invisible && this.mode !== "readonly") {
this._setValues();
this._renderEdit();
}
},
// --------------------------------------------------------------------------
// Private
// --------------------------------------------------------------------------
/**
* @override
* @private
*/
_formatValue: function (value) {
var options = _.extend(
{},
this.nodeOptions,
{data: this.recordData},
this.formatOptions
);
var formattedValue = _.find(this.values, function (option) {
return option[0] === value;
});
if (!formattedValue) {
return value;
}
formattedValue = formattedValue[1];
if (options && options.escape) {
formattedValue = _.escape(formattedValue);
}
return formattedValue;
},
/**
* @override
* @private
*/
_renderEdit: function () {
this.$el.empty();
for (var i = 0; i < this.values.length; i++) {
this.$el.append(
$("<option/>", {
value: JSON.stringify(this.values[i][0]),
text: this.values[i][1],
})
);
}
this.$el.val(JSON.stringify(this.value));
},
/**
* @override
* @private
*/
_renderReadonly: function () {
this.$el.empty().text(this._formatValue(this.value));
},
/**
* @override
*/
_reset: function () {
this._super.apply(this, arguments);
this._setValues();
},
/**
* Sets the possible field values.
*
* @private
*/
_setValues: function () {
this.values = _.reject(this.record.specialData[this.name], function (v) {
return v[0] === false && v[1] === "";
});
if (!this.attrs.modifiersValue || !this.attrs.modifiersValue.required) {
this.values = [[false, this.attrs.placeholder || ""]].concat(
this.values
);
}
},
// --------------------------------------------------------------------------
// Handlers
// --------------------------------------------------------------------------
/**
* @private
*/
_onChange: function () {
var value = JSON.parse(this.$el.val());
this._setValue(value.toString());
},
});
field_registry.add("dynamic_dropdown", FieldDynamicDropdown);
return FieldDynamicDropdown;
});

View File

@ -0,0 +1,182 @@
/** @odoo-module **/
import {editInput, getFixture} from "@web/../tests/helpers/utils";
import {makeView, setupViewRegistries} from "@web/../tests/views/helpers";
const {QUnit} = window;
let serverData = {};
let target = getFixture();
QUnit.module("web_widget_dropdown_dynamic", (hooks) => {
hooks.beforeEach(() => {
target = getFixture();
serverData = {
models: {
"sale.order": {
fields: {
content_string: {string: "Content", type: "char"},
bool_field: {string: "Boolean", type: "boolean"},
content_integer: {string: "Integer", type: "integer"},
change_field: {string: "Change", type: "char"},
content_selection: {
string: "Selection",
type: "selection",
selection: [["default", "Default"]],
},
},
records: [
{id: 1, bool_field: false, change_field: ""},
{id: 2, bool_field: true, change_field: ""},
],
methods: {
method_name() {
return Promise.resolve([["value a", "Value A"]]);
},
},
},
},
};
setupViewRegistries();
});
QUnit.test("values are fetched with changing context", async function (assert) {
assert.expect(13);
await makeView({
type: "form",
resModel: "sale.order",
serverData,
arch: `
<form>
<field name="change_field"/>
<field name="content_string" widget="dynamic_dropdown" options="{'values':'method_name'}" context="{'depending_on': change_field}" />
</form>`,
resId: 1,
mockRPC: function (route, args) {
assert.step(args.method);
if (args.method === "method_name") {
if (args.kwargs.context.depending_on === "step-1") {
return Promise.resolve([["value", "Title"]]);
} else if (args.kwargs.context.depending_on === "step-2") {
return Promise.resolve([
["value", "Title"],
["value_2", "Title 2"],
]);
}
return Promise.resolve([]);
}
},
});
await editInput(target, ".o_field_widget[name='change_field'] input", "step-1");
assert.containsN(target, "option", 2);
assert.containsOnce(target, "option[value='\"value\"']");
await editInput(target, ".o_field_widget[name='change_field'] input", "step-2");
assert.containsN(target, "option", 3);
assert.containsOnce(target, "option[value='\"value\"']");
assert.containsOnce(target, "option[value='\"value_2\"']");
await editInput(
target,
".o_field_widget[name='change_field'] input",
"step-other"
);
assert.containsN(target, "option", 1);
assert.verifySteps([
"get_views",
"read",
"method_name",
"method_name",
"method_name",
"method_name",
]);
});
QUnit.test("values are fetched w/o context (char)", async (assert) => {
assert.expect(6);
console.log("Start assert", serverData);
console.log("Start makeView");
await makeView({
type: "form",
resModel: "sale.order",
serverData,
arch: `
<form>
<field name="bool_field"/>
<field name="content_string" widget="dynamic_dropdown" options="{'values':'method_name'}" context="{'depending_on': bool_field}" />
</form>`,
resId: 2,
mockRPC(route, args) {
assert.step(args.method);
if (args.method === "method_name") {
if (args.kwargs.context.depending_on) {
return Promise.resolve([["value b", "Value B"]]);
}
}
},
});
const field_target = target.querySelector("div[name='content_string']");
assert.verifySteps(["get_views", "read", "method_name"]);
assert.containsN(field_target, "option", 2);
assert.containsOnce(
field_target,
"option[value='\"value b\"']",
"got `value b` "
);
console.log("Ending makeView", target);
});
QUnit.test("values are fetched w/o context (integer)", async (assert) => {
assert.expect(6);
await makeView({
type: "form",
resModel: "sale.order",
serverData,
arch: `
<form>
<field name="bool_field"/>
<field name="content_integer" widget="dynamic_dropdown" options="{'values':'method_name'}" context="{'depending_on': bool_field}" />
</form>`,
resId: 2,
mockRPC(route, args) {
assert.step(args.method);
if (args.method === "method_name") {
if (args.kwargs.context.depending_on) {
return Promise.resolve([["10", "Value B"]]);
}
}
},
});
const field_target = target.querySelector("div[name='content_integer']");
assert.verifySteps(["get_views", "read", "method_name"]);
assert.containsN(field_target, "option", 2);
assert.containsOnce(field_target, 'option[value="10"]');
});
QUnit.test("values are fetched w/o context (selection)", async (assert) => {
assert.expect(6);
await makeView({
type: "form",
resModel: "sale.order",
serverData,
arch: `
<form>
<field name="bool_field"/>
<field name="content_selection" widget="dynamic_dropdown" options="{'values':'method_name'}" context="{'depending_on': bool_field}" />
</form>`,
resId: 2,
mockRPC(route, args) {
assert.step(args.method);
if (args.method === "method_name") {
if (args.kwargs.context.depending_on) {
return Promise.resolve([["choice b", "Choice B"]]);
}
}
},
});
const field_target = target.querySelector("div[name='content_selection']");
assert.verifySteps(["get_views", "read", "method_name"]);
assert.containsN(field_target, "option", 2);
assert.containsOnce(field_target, "option[value='\"choice b\"']");
});
});

View File

@ -1,187 +0,0 @@
odoo.define(
"web_widget_dropdown_dynamic.web_widget_dropdown_dynamic_tests",
function (require) {
"use strict";
/* global QUnit*/
var FormView = require("web.FormView");
var testUtils = require("web.test_utils");
QUnit.module("web_widget_dropdown_dynamic", {}, function () {
QUnit.test(
"values are fetched w/o context (char)",
async function (assert) {
assert.expect(2);
var form = await testUtils.createView({
View: FormView,
model: "demo_entry",
data: {
demo_entry: {
fields: {
test_field: {string: "Test Field", type: "char"},
},
records: [{id: 1, test_field: ""}],
},
},
arch:
"<form>" +
'<field name="test_field" widget="dynamic_dropdown" values="_get_test_field_values"/>' +
"</form>",
mockRPC: function (route, args) {
if (args.method === "_get_test_field_values") {
return Promise.resolve([["value", "Title"]]);
}
return this._super.apply(this, arguments);
},
});
assert.containsN(form, "option", 2);
assert.containsOnce(form, "option[value='\"value\"']");
form.destroy();
}
);
QUnit.test(
"values are fetched w/o context (integer)",
async function (assert) {
assert.expect(2);
var form = await testUtils.createView({
View: FormView,
model: "demo_entry",
data: {
demo_entry: {
fields: {
test_field: {string: "Test Field", type: "integer"},
},
records: [{id: 1, test_field: 0}],
},
},
arch:
"<form>" +
'<field name="test_field" widget="dynamic_dropdown" values="_get_test_field_values"/>' +
"</form>",
mockRPC: function (route, args) {
if (args.method === "_get_test_field_values") {
return Promise.resolve([[0, "Title"]]);
}
return this._super.apply(this, arguments);
},
});
assert.containsN(form, "option", 2);
assert.containsOnce(form, "option[value='0']");
form.destroy();
}
);
QUnit.test(
"values are fetched w/o context (selection)",
async function (assert) {
assert.expect(2);
var form = await testUtils.createView({
View: FormView,
model: "demo_entry",
data: {
demo_entry: {
fields: {
test_field: {
string: "Test Field",
type: "selection",
},
},
records: [{id: 1, test_field: ""}],
},
},
arch:
"<form>" +
'<field name="test_field" widget="dynamic_dropdown" values="_get_test_field_values"/>' +
"</form>",
mockRPC: function (route, args) {
if (args.method === "_get_test_field_values") {
return Promise.resolve([["value", "Title"]]);
}
return this._super.apply(this, arguments);
},
});
assert.containsN(form, "option", 2);
assert.containsOnce(form, "option[value='\"value\"']");
form.destroy();
}
);
QUnit.test(
"values are fetched with changing context",
async function (assert) {
assert.expect(6);
var form = await testUtils.createView({
View: FormView,
model: "demo_entry",
data: {
demo_entry: {
fields: {
other_field: {string: "Other Field", type: "char"},
test_field: {string: "Test Field", type: "char"},
},
records: [{id: 1, other_field: "", test_field: ""}],
},
},
arch:
"<form>" +
'<field name="other_field" />' +
'<field name="test_field" widget="dynamic_dropdown" values="_get_test_field_values" context="{\'step\': other_field}"/>' +
"</form>",
mockRPC: function (route, args) {
if (args.method === "_get_test_field_values") {
if (args.kwargs.context.step === "step-1") {
return Promise.resolve([["value", "Title"]]);
} else if (args.kwargs.context.step === "step-2") {
return Promise.resolve([
["value", "Title"],
["value_2", "Title 2"],
]);
}
return Promise.resolve([]);
}
return this._super.apply(this, arguments);
},
});
await testUtils.fields.editAndTrigger(
form.$('.o_field_widget[name="other_field"]'),
"step-1",
["input"]
);
assert.containsN(form, "option", 2);
assert.containsOnce(form, "option[value='\"value\"']");
await testUtils.fields.editAndTrigger(
form.$('.o_field_widget[name="other_field"]'),
"step-2",
["input"]
);
assert.containsN(form, "option", 3);
assert.containsOnce(form, "option[value='\"value\"']");
assert.containsOnce(form, "option[value='\"value_2\"']");
await testUtils.fields.editAndTrigger(
form.$('.o_field_widget[name="other_field"]'),
"step-other",
["input"]
);
assert.containsN(form, "option", 1);
form.destroy();
}
);
});
}
);