summaryrefslogtreecommitdiff
path: root/addons/partner_autocomplete/static/src/js/partner_autocomplete_fieldchar.js
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/partner_autocomplete/static/src/js/partner_autocomplete_fieldchar.js
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/partner_autocomplete/static/src/js/partner_autocomplete_fieldchar.js')
-rw-r--r--addons/partner_autocomplete/static/src/js/partner_autocomplete_fieldchar.js337
1 files changed, 337 insertions, 0 deletions
diff --git a/addons/partner_autocomplete/static/src/js/partner_autocomplete_fieldchar.js b/addons/partner_autocomplete/static/src/js/partner_autocomplete_fieldchar.js
new file mode 100644
index 00000000..37a2b19c
--- /dev/null
+++ b/addons/partner_autocomplete/static/src/js/partner_autocomplete_fieldchar.js
@@ -0,0 +1,337 @@
+odoo.define('partner.autocomplete.fieldchar', function (require) {
+'use strict';
+
+var basic_fields = require('web.basic_fields');
+var core = require('web.core');
+var field_registry = require('web.field_registry');
+var AutocompleteMixin = require('partner.autocomplete.Mixin');
+
+var QWeb = core.qweb;
+
+var FieldChar = basic_fields.FieldChar;
+
+/**
+ * FieldChar extension to suggest existing companies when changing the company
+ * name on a res.partner view (indeed, it is designed to change the "name",
+ * "website" and "image" fields of records of this model).
+ */
+var FieldAutocomplete = FieldChar.extend(AutocompleteMixin, {
+ className: 'o_field_partner_autocomplete',
+ debounceSuggestions: 400,
+ resetOnAnyFieldChange: true,
+
+ jsLibs: [
+ '/partner_autocomplete/static/lib/jsvat.js'
+ ],
+
+ events: _.extend({}, FieldChar.prototype.events, {
+ 'keyup': '_onKeyup',
+ 'mousedown .o_partner_autocomplete_suggestion': '_onMousedown',
+ 'focusout': '_onFocusout',
+ 'mouseenter .o_partner_autocomplete_suggestion': '_onHoverDropdown',
+ 'click .o_partner_autocomplete_suggestion': '_onSuggestionClicked',
+ }),
+
+ /**
+ * @constructor
+ * Prepares the basic rendering of edit mode by setting the root to be a
+ * div.dropdown.open.
+ * @see FieldChar.init
+ */
+ init: function () {
+ this._super.apply(this, arguments);
+
+ // If the autocomplete is applied to vat field, only search valid vat number
+ this.onlyVAT = this.name === 'vat';
+
+ if (this.mode === 'edit') {
+ this.tagName = 'div';
+ this.className += ' dropdown open';
+ }
+
+ if (this.debounceSuggestions > 0) {
+ this._suggestCompanies = _.debounce(this._suggestCompanies.bind(this), this.debounceSuggestions);
+ }
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * Check if the autocomplete should be active
+ * Active :
+ * - only when creating new record
+ * - on model res.partner and is_company=true
+ * - on model res.company
+ *
+ * @returns {boolean}
+ * @private
+ */
+ _isActive: function () {
+ return this.model === 'res.company' ||
+ (
+ this.model === 'res.partner'
+ && this.record.data.is_company
+ && !(this.record.data && this.record.data.id)
+ );
+ },
+
+ /**
+ *
+ * @private
+ */
+ _removeDropdown: function () {
+ if (this.$dropdown) {
+ this.$dropdown.remove();
+ this.$dropdown = undefined;
+ }
+ },
+
+ /**
+ * Adds the <input/> element and prepares it. Note: the dropdown rendering
+ * is handled outside of the rendering routine (but instead by reacting to
+ * user input).
+ *
+ * @override
+ * @private
+ */
+ _renderEdit: function () {
+ this.$el.empty();
+ // Prepare and add the input
+ this._prepareInput().appendTo(this.$el);
+ },
+
+ /**
+ * Selects the given company suggestions by notifying changes to the view
+ * for the "name", "website" and "image" fields. This is of course intended
+ * to work only with the "res.partner" form view.
+ *
+ * @private
+ * @param {Object} company
+ */
+ _selectCompany: function (company) {
+ var self = this;
+ this._getCreateData(company).then(function (data) {
+ if (data.logo) {
+ var logoField = self.model === 'res.partner' ? 'image_1920' : 'logo';
+ data.company[logoField] = data.logo;
+ }
+
+ // Some fields are unnecessary in res.company
+ if (self.model === 'res.company') {
+ var fields = 'comment,child_ids,bank_ids,additional_info'.split(',');
+ fields.forEach(function (field) {
+ delete data.company[field];
+ });
+ }
+
+ self._setOne2ManyField('bank_ids', data.company.bank_ids);
+ delete data.company.bank_ids;
+
+ self.trigger_up('field_changed', {
+ dataPointID: self.dataPointID,
+ changes: data.company,
+ onSuccess: function () {
+ // update the input's value directly
+ if (self.onlyVAT)
+ self.$input.val(self._formatValue(company.vat));
+ else
+ self.$input.val(self._formatValue(company.name));
+ },
+ });
+ });
+ this._removeDropdown();
+ },
+
+ _setOne2ManyField: function (field, list) {
+ var self = this;
+ var viewType = this.record.viewType;
+ if (list && this.record.fieldsInfo[viewType] && this.record.fieldsInfo[viewType][field]) {
+ list.forEach(function (item) {
+ var changes = {};
+ changes[field] = {
+ operation: 'CREATE',
+ data: item,
+ };
+
+ self.trigger_up('field_changed', {
+ dataPointID: self.dataPointID,
+ changes: changes,
+ });
+ });
+ }
+ },
+
+ /**
+ * Shows the dropdown with the suggestions. If one is
+ * already opened, it removes the old one before rerendering the dropdown.
+ *
+ * @private
+ */
+ _showDropdown: function () {
+ this._removeDropdown();
+ if (this.suggestions.length > 0) {
+ this.$dropdown = $(QWeb.render('partner_autocomplete.dropdown', {
+ suggestions: this.suggestions,
+ }));
+ this.$dropdown.appendTo(this.$el);
+ }
+ },
+
+ /**
+ * Shows suggestions according to the given value.
+ * Note: this method is debounced (@see init).
+ *
+ * @private
+ * @param {string} value - searched term
+ */
+ _suggestCompanies: function (value) {
+ var self = this;
+ if (this._validateSearchTerm(value, this.onlyVAT) && this._isOnline()) {
+ return this._autocomplete(value).then(function (suggestions) {
+ if (suggestions && suggestions.length) {
+ self.suggestions = suggestions;
+ self._showDropdown();
+ } else {
+ self._removeDropdown();
+ }
+ });
+ } else {
+ this._removeDropdown();
+ }
+ },
+
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Called on focusout -> removes the suggestions dropdown.
+ *
+ * @private
+ */
+ _onFocusout: function () {
+ this._removeDropdown();
+ },
+
+ /**
+ * Called when hovering a suggestion in the dropdown -> sets it as active.
+ *
+ * @private
+ * @param {Event} e
+ */
+ _onHoverDropdown: function (e) {
+ this.$dropdown.find('.active').removeClass('active');
+ $(e.currentTarget).parent().addClass('active');
+ },
+
+ /**
+ * @override of FieldChar (called when the user is typing text)
+ * Checks the <input/> value and shows suggestions according to
+ * this value.
+ *
+ * @private
+ */
+ _onInput: function () {
+ this._super.apply(this, arguments);
+ if (this._isActive()) {
+ this._suggestCompanies(this.$input.val());
+ }
+ },
+
+ /**
+ * @override of FieldChar
+ * Changes the "up" and "down" key behavior when the dropdown is opened (to
+ * navigate through dropdown suggestions).
+ * Triggered by keydown to execute the navigation multiple times when the
+ * user keeps the "down" or "up" pressed.
+ *
+ * @private
+ * @param {Event} e
+ */
+ _onKeydown: function (e) {
+ switch (e.which) {
+ case $.ui.keyCode.UP:
+ case $.ui.keyCode.DOWN:
+ if (!this.$dropdown) {
+ break;
+ }
+ e.preventDefault();
+ var $suggestions = this.$dropdown.children();
+ var $active = $suggestions.filter('.active');
+ var $to;
+ if ($active.length) {
+ $to = e.which === $.ui.keyCode.DOWN ?
+ $active.next() :
+ $active.prev();
+ } else {
+ $to = $suggestions.first();
+ }
+ if ($to.length) {
+ $active.removeClass('active');
+ $to.addClass('active');
+ }
+ return;
+ }
+ this._super.apply(this, arguments);
+ },
+
+ /**
+ * Called on keyup events to:
+ * -> remove the suggestions dropdown when hitting the "escape" key
+ * -> select the highlighted suggestion when hitting the "enter" key
+ *
+ * @private
+ * @param {Event} e
+ */
+ _onKeyup: function (e) {
+ switch (e.which) {
+ case $.ui.keyCode.ESCAPE:
+ e.preventDefault();
+ this._removeDropdown();
+ break;
+ case $.ui.keyCode.ENTER:
+ if (!this.$dropdown) {
+ break;
+ }
+ e.preventDefault();
+ var $active = this.$dropdown.find('.o_partner_autocomplete_suggestion.active');
+ if (!$active.length) {
+ return;
+ }
+ this._selectCompany(this.suggestions[$active.data('index')]);
+ break;
+ }
+ },
+
+ /**
+ * Called on mousedown event on a suggestion -> prevent default
+ * action so that the <input/> element does not lose the focus.
+ *
+ * @private
+ * @param {Event} e
+ */
+ _onMousedown: function (e) {
+ e.preventDefault(); // prevent losing focus on suggestion click
+ },
+
+ /**
+ * Called when a dropdown suggestion is clicked -> trigger_up changes for
+ * some fields in the view (not only this <input/> one) with the associated
+ * data (@see _selectCompany).
+ *
+ * @private
+ * @param {Event} e
+ */
+ _onSuggestionClicked: function (e) {
+ e.preventDefault();
+ this._selectCompany(this.suggestions[$(e.currentTarget).data('index')]);
+ },
+});
+
+field_registry.add('field_partner_autocomplete', FieldAutocomplete);
+
+return FieldAutocomplete;
+});