From 90ab4f2e8730963c4b514bec4b903bec8fd0db22 Mon Sep 17 00:00:00 2001 From: Earth Patel Date: Mon, 29 Jan 2024 14:33:15 +0530 Subject: [PATCH] [IMP] l10n_in: add the hsn autocomplete widget - In this commit, we make some requests in js to get a list of HSN codes to match with the input entered by the user in the field. - And for the options attribute in hsn_autocomplete widget, when a user selects an option from the list, the hsn_decription will be stored in the field specified by the user. task-3646598 closes odoo/odoo#151507 Signed-off-by: Quentin Lejeune (qle) --- addons/l10n_in/__manifest__.py | 5 + .../hsn_autocomplete/hsn_autocomplete.js | 118 ++++++++++++++++++ .../hsn_autocomplete/hsn_autocomplete.xml | 19 +++ .../l10n_in/views/product_template_view.xml | 4 +- 4 files changed, 144 insertions(+), 2 deletions(-) create mode 100644 addons/l10n_in/static/src/components/hsn_autocomplete/hsn_autocomplete.js create mode 100644 addons/l10n_in/static/src/components/hsn_autocomplete/hsn_autocomplete.xml diff --git a/addons/l10n_in/__manifest__.py b/addons/l10n_in/__manifest__.py index 337a5bd19c7..2cfb4cd8d60 100644 --- a/addons/l10n_in/__manifest__.py +++ b/addons/l10n_in/__manifest__.py @@ -50,4 +50,9 @@ Sheet, now only Vertical format has been permitted Which is Supported By Odoo. 'demo/product_demo.xml', ], 'license': 'LGPL-3', + 'assets': { + 'web.assets_backend': [ + 'l10n_in/static/src/components/**/*', + ], + }, } diff --git a/addons/l10n_in/static/src/components/hsn_autocomplete/hsn_autocomplete.js b/addons/l10n_in/static/src/components/hsn_autocomplete/hsn_autocomplete.js new file mode 100644 index 00000000000..0ad186b6f0e --- /dev/null +++ b/addons/l10n_in/static/src/components/hsn_autocomplete/hsn_autocomplete.js @@ -0,0 +1,118 @@ +/** @odoo-module **/ + +import { AutoComplete } from "@web/core/autocomplete/autocomplete"; +import { useChildRef } from "@web/core/utils/hooks"; +import { registry } from "@web/core/registry"; +import { _t } from "@web/core/l10n/translation"; +import { CharField, charField } from "@web/views/fields/char/char_field"; +import { useInputField } from "@web/views/fields/input_field_hook"; + +const l10N_IN_HSN_SERVICE_URL = "https://services.gst.gov.in/commonservices/hsn/search/qsearch"; + +export class L10nInHsnAutoComplete extends CharField { + static template = "l10n_in.hsnAutoComplete"; + static components = { + ...CharField.components, + AutoComplete, + }; + static props = { + ...CharField.props, + l10nInHsnDescription: { type: String, optional: true }, + }; + + setup() { + super.setup(); + this.inputRef = useChildRef(); + useInputField({ + getValue: () => this.props.record.data[this.props.name] || "", + parse: (v) => this.parse(v), + ref: this.inputRef, + }); + } + + async getHsnSuggestions(value) { + const suggestions = []; + const onlyDigits = !isNaN(value) && value.indexOf(" ") < 0; + const params = [ + { type: "byCode", category: "null" }, // For code + { type: "byDesc", category: "P" }, // For products + { type: "byDesc", category: "S" }, // For services + ]; + const filteredParams = onlyDigits ? [params[0]] : params.slice(1); + try { + await Promise.all( + filteredParams.map(async (param) => { + const controller = new AbortController(); + const signal = controller.signal; + setTimeout(() => controller.abort(), 5000); + const res = await fetch( + `${l10N_IN_HSN_SERVICE_URL}?inputText=${value}&selectedType=${param.type}&category=${param.category}`, + { signal } + ); + if (!res.ok) { + throw new Error(res.statusText); + } + const resData = await res.json(); + if (resData.data) { + suggestions.push( + ...resData.data + .filter((item) => item.c.length > 3) + .map((item) => ({ + label: item.c, + description: item.n, + })) + ); + } + }) + ); + } catch (e) { + suggestions.push({ + label: _t("Could not contact API"), + unselectable: false, + }); + console.warn("HSN Autocomplete API error:", e); + } + return suggestions; + } + + get sources() { + return [ + { + options: async (request) => { + if (request?.length > 2) { + return await this.getHsnSuggestions(request); + } else { + return []; + } + }, + optionTemplate: "hsn_autocomplete.DropdownOption", + placeholder: _t("Searching..."), + }, + ]; + } + + onSelect(option) { + const data = { [this.props.name]: option.label }; + if (this.props.l10nInHsnDescription) { + data[this.props.l10nInHsnDescription] = option.description; + } + this.props.record.update(data); + } +} + +export const l10nInHsnAutoComplete = { + ...charField, + component: L10nInHsnAutoComplete, + supportedOptions: [ + { + label: _t("hsn description field"), + name: "hsn_description_field", + type: "string", + }, + ], + extractProps: ({ options }) => ({ + l10nInHsnDescription: options.hsn_description_field, + }), +}; + +registry.category("fields").add("l10n_in_hsn_autocomplete", l10nInHsnAutoComplete); diff --git a/addons/l10n_in/static/src/components/hsn_autocomplete/hsn_autocomplete.xml b/addons/l10n_in/static/src/components/hsn_autocomplete/hsn_autocomplete.xml new file mode 100644 index 00000000000..56aec6a28ee --- /dev/null +++ b/addons/l10n_in/static/src/components/hsn_autocomplete/hsn_autocomplete.xml @@ -0,0 +1,19 @@ + + + + + + + +
+ +
+
+ + diff --git a/addons/l10n_in/views/product_template_view.xml b/addons/l10n_in/views/product_template_view.xml index 4321338005c..7d007767a3d 100644 --- a/addons/l10n_in/views/product_template_view.xml +++ b/addons/l10n_in/views/product_template_view.xml @@ -7,8 +7,8 @@ - - + +