forked from Techsystech/web
[IMP] web_widget_one2many_product_picker: Refactor code
- fix: Search with wildcard - fix: Search input events on firefox - fix: Update lazy qty when use the tiled form - fix: Handle race conditions - fix: Return promise when recreate 'zombie' record - fix: Remove record - imp: Performance13.0
parent
6aa1f8e7b9
commit
898aca2511
|
@ -86,6 +86,7 @@ Widget options:
|
||||||
* show_discount > Enable/Disable display discount (False by default)
|
* show_discount > Enable/Disable display discount (False by default)
|
||||||
* show_subtotal > Enable/Disable show subtotal (True by default)
|
* show_subtotal > Enable/Disable show subtotal (True by default)
|
||||||
* auto_save > Enable/Disable auto save (False by default)
|
* auto_save > Enable/Disable auto save (False by default)
|
||||||
|
* auto_save_delay > The time (in milliseconds) to wait after the last interaction before launching the autosave (1500 by default)
|
||||||
* all_domain > The domain used in 'All' section ([] by default)
|
* all_domain > The domain used in 'All' section ([] by default)
|
||||||
|
|
||||||
If using auto save feature, you should keep in mind that the "Save" and "Discard" buttons
|
If using auto save feature, you should keep in mind that the "Save" and "Discard" buttons
|
||||||
|
@ -95,6 +96,7 @@ Widget options:
|
||||||
* ignore_warning > Enable/Disable display onchange warnings (False by default)
|
* ignore_warning > Enable/Disable display onchange warnings (False by default)
|
||||||
* instant_search > Enable/Disable instant search mode (False by default)
|
* instant_search > Enable/Disable instant search mode (False by default)
|
||||||
* trigger_refresh_fields > Fields in the main record that dispatch a widget refresh (["partner_id", "currency_id"] by default)
|
* trigger_refresh_fields > Fields in the main record that dispatch a widget refresh (["partner_id", "currency_id"] by default)
|
||||||
|
* auto_focus > Keep the focus on the search box after performing a search (True by default)
|
||||||
|
|
||||||
All widget options are optional.
|
All widget options are optional.
|
||||||
Notice that you can call '_' method to use translations. This only can be used with this widget.
|
Notice that you can call '_' method to use translations. This only can be used with this widget.
|
||||||
|
@ -215,7 +217,8 @@ Known issues / Roadmap
|
||||||
======================
|
======================
|
||||||
|
|
||||||
* Translations in the xml 'options' attribute of the field that use the widget can't be exported automatically to be translated
|
* Translations in the xml 'options' attribute of the field that use the widget can't be exported automatically to be translated
|
||||||
* The product card animations can be improved. Currently the card is recreated, so we lost some elements to apply correct effects.
|
* The product card animations can be improved. Currently the card is recreated, so we lost some elements to apply correct effects
|
||||||
|
* sale.order onchanges that affects to "order_line" subfields will be ommitted to increase the performance
|
||||||
|
|
||||||
Bug Tracker
|
Bug Tracker
|
||||||
===========
|
===========
|
||||||
|
@ -244,6 +247,8 @@ Contributors
|
||||||
* Pedro M. Baeza
|
* Pedro M. Baeza
|
||||||
* David Vidal
|
* David Vidal
|
||||||
|
|
||||||
|
* Giovanni Serra <giovanni@gslab.it>
|
||||||
|
|
||||||
Maintainers
|
Maintainers
|
||||||
~~~~~~~~~~~
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
# Copyright 2020 Tecnativa - Alexandre Díaz
|
# Copyright 2020 Tecnativa - Alexandre Díaz
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
from . import models
|
||||||
|
|
|
@ -6,118 +6,143 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Odoo Server 12.0\n"
|
"Project-Id-Version: Odoo Server 12.0\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2021-02-17 13:45+0000\n"
|
"POT-Creation-Date: 2022-03-04 18:46+0000\n"
|
||||||
"Last-Translator: claudiagn <claudia.gargallo@qubiq.es>\n"
|
"PO-Revision-Date: 2022-03-04 19:49+0100\n"
|
||||||
"Language-Team: none\n"
|
"Last-Translator: <>\n"
|
||||||
|
"Language-Team: \n"
|
||||||
"Language: es\n"
|
"Language: es\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: \n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
"Plural-Forms: \n"
|
||||||
"X-Generator: Weblate 4.3.2\n"
|
"X-Generator: Poedit 3.0\n"
|
||||||
|
|
||||||
#. module: web_widget_one2many_product_picker
|
#. module: web_widget_one2many_product_picker
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_create.xml:6
|
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_create.xml:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Accept"
|
||||||
|
msgstr "Aceptar"
|
||||||
|
|
||||||
|
#. module: web_widget_one2many_product_picker
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_create.xml:0
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Add"
|
msgid "Add"
|
||||||
msgstr "Añadir"
|
msgstr "Añadir"
|
||||||
|
|
||||||
#. module: web_widget_one2many_product_picker
|
#. module: web_widget_one2many_product_picker
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: code:addons/web_widget_one2many_product_picker/static/src/js/widgets/field_one2many_product_picker.js:193
|
#: code:addons/web_widget_one2many_product_picker/static/src/js/widgets/field_one2many_product_picker.js:0
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "Todo"
|
msgstr "Todo"
|
||||||
|
|
||||||
|
#. module: web_widget_one2many_product_picker
|
||||||
|
#: model:ir.model,name:web_widget_one2many_product_picker.model_base
|
||||||
|
msgid "Base"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. module: web_widget_one2many_product_picker
|
#. module: web_widget_one2many_product_picker
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker.xml:35
|
#: code:addons/web_widget_one2many_product_picker/static/src/js/widgets/field_one2many_product_picker.js:0
|
||||||
|
#, python-format
|
||||||
|
msgid "By Name"
|
||||||
|
msgstr "Por Nombre"
|
||||||
|
|
||||||
|
#. module: web_widget_one2many_product_picker
|
||||||
|
#: code:addons/web_widget_one2many_product_picker/models/base.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Can't create the %s: Duplicated product! (Already in database)"
|
||||||
|
msgstr "No se puede crear el %s: Producto duplicado! (Actualmente en la base de datos)"
|
||||||
|
|
||||||
|
#. module: web_widget_one2many_product_picker
|
||||||
|
#: code:addons/web_widget_one2many_product_picker/models/base.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Can't create the %s: Duplicated product! (Inside query)"
|
||||||
|
msgstr "No se puede crear el %s: Producto duplicado! (En la petición)"
|
||||||
|
|
||||||
|
#. module: web_widget_one2many_product_picker
|
||||||
|
#: code:addons/web_widget_one2many_product_picker/models/base.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Can't write the %s: Duplicated product! (Already in database)"
|
||||||
|
msgstr "No se puede escribir el %s: Producto duplicado! (Actualmente en la base de datos)"
|
||||||
|
|
||||||
|
#. module: web_widget_one2many_product_picker
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_create.xml:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Discard"
|
||||||
|
msgstr "Descartar"
|
||||||
|
|
||||||
|
#. module: web_widget_one2many_product_picker
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker.xml:0
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Groups"
|
msgid "Groups"
|
||||||
msgstr "Grupos"
|
msgstr "Grupos"
|
||||||
|
|
||||||
#. module: web_widget_one2many_product_picker
|
#. module: web_widget_one2many_product_picker
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker.xml:43
|
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker.xml:0
|
||||||
|
#, python-format
|
||||||
|
msgid "LOADING..."
|
||||||
|
msgstr "CARGANDO..."
|
||||||
|
|
||||||
|
#. module: web_widget_one2many_product_picker
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker.xml:0
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Lines"
|
msgid "Lines"
|
||||||
msgstr "Línias"
|
msgstr "Líneas"
|
||||||
|
|
||||||
#. module: web_widget_one2many_product_picker
|
#. module: web_widget_one2many_product_picker
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker.xml:67
|
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker.xml:0
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Load More"
|
msgid "Load More"
|
||||||
msgstr "Carga más"
|
msgstr "Cargar más"
|
||||||
|
|
||||||
#. module: web_widget_one2many_product_picker
|
#. module: web_widget_one2many_product_picker
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_modif_price.xml:11
|
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_modif_price.xml:0
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Price:"
|
msgid "Price"
|
||||||
msgstr "Precio:"
|
msgstr "Precio"
|
||||||
|
|
||||||
#. module: web_widget_one2many_product_picker
|
|
||||||
#: model:ir.model,name:web_widget_one2many_product_picker.model_product_product
|
|
||||||
msgid "Product"
|
|
||||||
msgstr "Producto"
|
|
||||||
|
|
||||||
#. module: web_widget_one2many_product_picker
|
#. module: web_widget_one2many_product_picker
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_create.xml:13
|
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_create.xml:0
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Remove"
|
msgid "Remove"
|
||||||
msgstr "Eliminar"
|
msgstr "Eliminar"
|
||||||
|
|
||||||
#. module: web_widget_one2many_product_picker
|
#. module: web_widget_one2many_product_picker
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker.xml:23
|
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker.xml:0
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Search..."
|
msgid "Search..."
|
||||||
msgstr "Buscar..."
|
msgstr "Buscar..."
|
||||||
|
|
||||||
#. module: web_widget_one2many_product_picker
|
#. module: web_widget_one2many_product_picker
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker.xml:58
|
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker.xml:0
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Subtotal:"
|
msgid "Subtotal:"
|
||||||
msgstr "Subtotal:"
|
msgstr "Subtotal:"
|
||||||
|
|
||||||
#. module: web_widget_one2many_product_picker
|
|
||||||
#: model:ir.model.fields,help:web_widget_one2many_product_picker.field_product_product__image_variant_medium
|
|
||||||
msgid ""
|
|
||||||
"This field holds the image used as image for the product variantor product "
|
|
||||||
"image medium, limited to 512x512px."
|
|
||||||
msgstr ""
|
|
||||||
"Aquest camp conté la imatge que s'utilitza com a imatge per al mitjà "
|
|
||||||
"d'imatge del producte que varia el producte, limitada a 512x512px."
|
|
||||||
|
|
||||||
#. module: web_widget_one2many_product_picker
|
|
||||||
#: model:ir.model.fields,help:web_widget_one2many_product_picker.field_product_product__image_variant_big
|
|
||||||
msgid ""
|
|
||||||
"This field holds the image used as image for the product variantor product "
|
|
||||||
"image, limited to 1024x1024px."
|
|
||||||
msgstr ""
|
|
||||||
"Aquest camp conté la imatge que s'utilitza com a imatge per al mitjà "
|
|
||||||
"d'imatge del producte que varia el producte, limitada a 1024x1024px."
|
|
||||||
|
|
||||||
#. module: web_widget_one2many_product_picker
|
|
||||||
#: model:ir.model.fields,field_description:web_widget_one2many_product_picker.field_product_product__image_variant_big
|
|
||||||
msgid "Variant Image Big (Computed)"
|
|
||||||
msgstr "Imagen variante grande (calculada)"
|
|
||||||
|
|
||||||
#. module: web_widget_one2many_product_picker
|
|
||||||
#: model:ir.model.fields,field_description:web_widget_one2many_product_picker.field_product_product__image_variant_medium
|
|
||||||
msgid "Variant Image Medium (Computed)"
|
|
||||||
msgstr "Imagen variante media (calculada)"
|
|
||||||
|
|
||||||
#. module: web_widget_one2many_product_picker
|
#. module: web_widget_one2many_product_picker
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: code:addons/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/record.js:341
|
#: code:addons/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/record.js:0
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "[No widget %s]"
|
msgid "[No widget %s]"
|
||||||
msgstr "[Sin widget %s]"
|
msgstr "[Sin widget %s]"
|
||||||
|
|
||||||
#~ msgid "Add 1"
|
#~ msgid "HTTP Routing"
|
||||||
#~ msgstr "Añadir 1"
|
#~ msgstr "Ruta HTTP"
|
||||||
|
|
||||||
|
#~ msgid "Pricelist Item"
|
||||||
|
#~ msgstr "Item de Lista de precios"
|
||||||
|
|
||||||
|
#~ msgid "Product"
|
||||||
|
#~ msgstr "Producto"
|
||||||
|
|
|
@ -6,6 +6,8 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Odoo Server 13.0\n"
|
"Project-Id-Version: Odoo Server 13.0\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2022-03-04 18:46+0000\n"
|
||||||
|
"PO-Revision-Date: 2022-03-04 18:46+0000\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: \n"
|
"Language-Team: \n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
|
@ -13,6 +15,13 @@ msgstr ""
|
||||||
"Content-Transfer-Encoding: \n"
|
"Content-Transfer-Encoding: \n"
|
||||||
"Plural-Forms: \n"
|
"Plural-Forms: \n"
|
||||||
|
|
||||||
|
#. module: web_widget_one2many_product_picker
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_create.xml:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Accept"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. module: web_widget_one2many_product_picker
|
#. module: web_widget_one2many_product_picker
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_create.xml:0
|
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_create.xml:0
|
||||||
|
@ -27,6 +36,11 @@ msgstr ""
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: web_widget_one2many_product_picker
|
||||||
|
#: model:ir.model,name:web_widget_one2many_product_picker.model_base
|
||||||
|
msgid "Base"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. module: web_widget_one2many_product_picker
|
#. module: web_widget_one2many_product_picker
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: code:addons/web_widget_one2many_product_picker/static/src/js/widgets/field_one2many_product_picker.js:0
|
#: code:addons/web_widget_one2many_product_picker/static/src/js/widgets/field_one2many_product_picker.js:0
|
||||||
|
@ -34,6 +48,32 @@ msgstr ""
|
||||||
msgid "By Name"
|
msgid "By Name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: web_widget_one2many_product_picker
|
||||||
|
#: code:addons/web_widget_one2many_product_picker/models/base.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Can't create the %s: Duplicated product! (Already in database)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: web_widget_one2many_product_picker
|
||||||
|
#: code:addons/web_widget_one2many_product_picker/models/base.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Can't create the %s: Duplicated product! (Inside query)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: web_widget_one2many_product_picker
|
||||||
|
#: code:addons/web_widget_one2many_product_picker/models/base.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Can't write the %s: Duplicated product! (Already in database)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: web_widget_one2many_product_picker
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_create.xml:0
|
||||||
|
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_create.xml:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Discard"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. module: web_widget_one2many_product_picker
|
#. module: web_widget_one2many_product_picker
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker.xml:0
|
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker.xml:0
|
||||||
|
@ -41,6 +81,13 @@ msgstr ""
|
||||||
msgid "Groups"
|
msgid "Groups"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: web_widget_one2many_product_picker
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker.xml:0
|
||||||
|
#, python-format
|
||||||
|
msgid "LOADING..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. module: web_widget_one2many_product_picker
|
#. module: web_widget_one2many_product_picker
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker.xml:0
|
#: code:addons/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker.xml:0
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
from . import base
|
|
@ -0,0 +1,60 @@
|
||||||
|
# Copyright 2022 Tecnativa - Alexandre D. Díaz
|
||||||
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
||||||
|
from odoo import _, api, models
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
|
class BaseModel(models.BaseModel):
|
||||||
|
_inherit = "base"
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _check_product_picker_duplicated_products(self, vals_list):
|
||||||
|
relation = self.env.context.get("product_picker_relation")
|
||||||
|
if relation != self._name or not len(vals_list):
|
||||||
|
return
|
||||||
|
product_field = self.env.context.get("product_picker_product_field")
|
||||||
|
product_ids = [
|
||||||
|
values[product_field] for values in vals_list if product_field in values
|
||||||
|
]
|
||||||
|
num_products = len(product_ids)
|
||||||
|
if not num_products:
|
||||||
|
return
|
||||||
|
elif num_products != len(set(product_ids)):
|
||||||
|
raise ValidationError(
|
||||||
|
_("Can't create the %s: Duplicated product! (Inside query)") % relation
|
||||||
|
)
|
||||||
|
relation_field = self.env.context.get("product_picker_relation_field")
|
||||||
|
# All records have the same 'relation id' when created with the product picker
|
||||||
|
relation_id = vals_list[0][relation_field]
|
||||||
|
# When write maybe need get the value from the record
|
||||||
|
if not relation_id:
|
||||||
|
field_obj = self[relation_field]
|
||||||
|
if field_obj:
|
||||||
|
relation_id = relation_id.id
|
||||||
|
has_product = (
|
||||||
|
self.search(
|
||||||
|
[
|
||||||
|
(relation_field, "=", relation_id),
|
||||||
|
(product_field, "in", product_ids),
|
||||||
|
],
|
||||||
|
count=True,
|
||||||
|
limit=1,
|
||||||
|
)
|
||||||
|
!= 0
|
||||||
|
)
|
||||||
|
if has_product:
|
||||||
|
raise ValidationError(
|
||||||
|
_("Can't create the %s: Duplicated product! (Already in database)")
|
||||||
|
% relation
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.model_create_multi
|
||||||
|
def create(self, vals_list):
|
||||||
|
"""Avoid create lines that have a product currently used when use the product picker"""
|
||||||
|
self._check_product_picker_duplicated_products(vals_list)
|
||||||
|
return super().create(vals_list)
|
||||||
|
|
||||||
|
def write(self, values):
|
||||||
|
"""Avoid write lines that have a product currently used when use the product picker"""
|
||||||
|
self._check_product_picker_duplicated_products([values])
|
||||||
|
return super().write(values)
|
|
@ -44,7 +44,7 @@ Widget options:
|
||||||
* show_discount > Enable/Disable display discount (False by default)
|
* show_discount > Enable/Disable display discount (False by default)
|
||||||
* show_subtotal > Enable/Disable show subtotal (True by default)
|
* show_subtotal > Enable/Disable show subtotal (True by default)
|
||||||
* auto_save > Enable/Disable auto save (False by default)
|
* auto_save > Enable/Disable auto save (False by default)
|
||||||
* all_domain > The domain used in 'All' section ([] by default)
|
* auto_save_delay > The time (in milliseconds) to wait after the last interaction before launching the autosave (1500 by default)
|
||||||
|
|
||||||
If using auto save feature, you should keep in mind that the "Save" and "Discard" buttons
|
If using auto save feature, you should keep in mind that the "Save" and "Discard" buttons
|
||||||
will lose part of its functionality as the document will be saved every time you
|
will lose part of its functionality as the document will be saved every time you
|
||||||
|
@ -53,6 +53,7 @@ Widget options:
|
||||||
* ignore_warning > Enable/Disable display onchange warnings (False by default)
|
* ignore_warning > Enable/Disable display onchange warnings (False by default)
|
||||||
* instant_search > Enable/Disable instant search mode (False by default)
|
* instant_search > Enable/Disable instant search mode (False by default)
|
||||||
* trigger_refresh_fields > Fields in the main record that dispatch a widget refresh (["partner_id", "currency_id"] by default)
|
* trigger_refresh_fields > Fields in the main record that dispatch a widget refresh (["partner_id", "currency_id"] by default)
|
||||||
|
* auto_focus > Keep the focus on the search box after performing a search (True by default)
|
||||||
|
|
||||||
All widget options are optional.
|
All widget options are optional.
|
||||||
Notice that you can call '_' method to use translations. This only can be used with this widget.
|
Notice that you can call '_' method to use translations. This only can be used with this widget.
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
* Translations in the xml 'options' attribute of the field that use the widget can't be exported automatically to be translated
|
* Translations in the xml 'options' attribute of the field that use the widget can't be exported automatically to be translated
|
||||||
* The product card animations can be improved. Currently the card is recreated, so we lost some elements to apply correct effects.
|
* The product card animations can be improved. Currently the card is recreated, so we lost some elements to apply correct effects
|
||||||
|
* sale.order onchanges that affects to "order_line" subfields will be ommitted to increase the performance
|
||||||
|
|
|
@ -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>Web Widget One2Many Product Picker</title>
|
<title>Web Widget One2Many Product Picker</title>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
|
|
||||||
|
@ -474,6 +474,8 @@ You need to define the view fields. The view must be of <tt class="docutils lite
|
||||||
</li>
|
</li>
|
||||||
<li><p class="first">auto_save > Enable/Disable auto save (False by default)</p>
|
<li><p class="first">auto_save > Enable/Disable auto save (False by default)</p>
|
||||||
</li>
|
</li>
|
||||||
|
<li><p class="first">auto_save_delay > The time (in milliseconds) to wait after the last interaction before launching the autosave (1500 by default)</p>
|
||||||
|
</li>
|
||||||
<li><p class="first">all_domain > The domain used in ‘All’ section ([] by default)</p>
|
<li><p class="first">all_domain > The domain used in ‘All’ section ([] by default)</p>
|
||||||
<p>If using auto save feature, you should keep in mind that the “Save” and “Discard” buttons
|
<p>If using auto save feature, you should keep in mind that the “Save” and “Discard” buttons
|
||||||
will lose part of its functionality as the document will be saved every time you
|
will lose part of its functionality as the document will be saved every time you
|
||||||
|
@ -485,6 +487,8 @@ modify/create a record with the widget.</p>
|
||||||
</li>
|
</li>
|
||||||
<li><p class="first">trigger_refresh_fields > Fields in the main record that dispatch a widget refresh ([“partner_id”, “currency_id”] by default)</p>
|
<li><p class="first">trigger_refresh_fields > Fields in the main record that dispatch a widget refresh ([“partner_id”, “currency_id”] by default)</p>
|
||||||
</li>
|
</li>
|
||||||
|
<li><p class="first">auto_focus > Keep the focus on the search box after performing a search (True by default)</p>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>All widget options are optional.
|
<p>All widget options are optional.
|
||||||
Notice that you can call ‘_’ method to use translations. This only can be used with this widget.</p>
|
Notice that you can call ‘_’ method to use translations. This only can be used with this widget.</p>
|
||||||
|
@ -605,7 +609,8 @@ accept changes.</p>
|
||||||
<h1><a class="toc-backref" href="#id10">Known issues / Roadmap</a></h1>
|
<h1><a class="toc-backref" href="#id10">Known issues / Roadmap</a></h1>
|
||||||
<ul class="simple">
|
<ul class="simple">
|
||||||
<li>Translations in the xml ‘options’ attribute of the field that use the widget can’t be exported automatically to be translated</li>
|
<li>Translations in the xml ‘options’ attribute of the field that use the widget can’t be exported automatically to be translated</li>
|
||||||
<li>The product card animations can be improved. Currently the card is recreated, so we lost some elements to apply correct effects.</li>
|
<li>The product card animations can be improved. Currently the card is recreated, so we lost some elements to apply correct effects</li>
|
||||||
|
<li>sale.order onchanges that affects to “order_line” subfields will be ommitted to increase the performance</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="bug-tracker">
|
<div class="section" id="bug-tracker">
|
||||||
|
@ -636,6 +641,8 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
|
||||||
</ul>
|
</ul>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</li>
|
</li>
|
||||||
|
<li><p class="first">Giovanni Serra <<a class="reference external" href="mailto:giovanni@gslab.it">giovanni@gslab.it</a>></p>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="maintainers">
|
<div class="section" id="maintainers">
|
||||||
|
|
|
@ -3,17 +3,35 @@
|
||||||
odoo.define("web_widget_one2many_product_picker.tools", function(require) {
|
odoo.define("web_widget_one2many_product_picker.tools", function(require) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const field_utils = require("web.field_utils");
|
var field_utils = require("web.field_utils");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Truncate floats
|
||||||
|
*
|
||||||
|
* @param {Number} value
|
||||||
|
* @param {Object} field_info
|
||||||
|
* @param {Array} digist
|
||||||
|
* @returns {Number}
|
||||||
|
*/
|
||||||
|
function float(value, field_info, digist) {
|
||||||
|
var options = digist && {digist: digist};
|
||||||
|
return field_utils.format.float(value, field_info, options);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the price with discount
|
* Calculate the price with discount
|
||||||
*
|
*
|
||||||
* @param {Number} price
|
* @param {Number} price
|
||||||
* @param {Number} discount
|
* @param {Number} discount
|
||||||
|
* @param {Array} digist
|
||||||
* @returns {Number}
|
* @returns {Number}
|
||||||
*/
|
*/
|
||||||
function priceReduce(price, discount) {
|
function priceReduce(price, discount, digist) {
|
||||||
return price * (1.0 - discount / 100.0);
|
var price_reduce = price * (1.0 - discount / 100.0);
|
||||||
|
if (digist) {
|
||||||
|
return float(price_reduce, undefined, digist);
|
||||||
|
}
|
||||||
|
return price_reduce;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,12 +52,6 @@ odoo.define("web_widget_one2many_product_picker.tools", function(require) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function float(value, field_info, digits) {
|
|
||||||
return field_utils.format.float(value, field_info, {
|
|
||||||
digits: digits,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
monetary: monetary,
|
monetary: monetary,
|
||||||
float: float,
|
float: float,
|
||||||
|
|
|
@ -6,27 +6,23 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateForm", f
|
||||||
) {
|
) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const core = require("web.core");
|
var core = require("web.core");
|
||||||
const Widget = require("web.Widget");
|
var Widget = require("web.Widget");
|
||||||
const widgetRegistry = require("web.widget_registry");
|
var widgetRegistry = require("web.widget_registry");
|
||||||
const ProductPickerQuickCreateFormView = require("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView")
|
var ProductPickerQuickCreateFormView = require("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView")
|
||||||
.ProductPickerQuickCreateFormView;
|
.ProductPickerQuickCreateFormView;
|
||||||
|
|
||||||
const qweb = core.qweb;
|
var qweb = core.qweb;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This widget render a Form. Used by FieldOne2ManyProductPicker
|
* This widget render a Form. Used by FieldOne2ManyProductPicker
|
||||||
*/
|
*/
|
||||||
const ProductPickerQuickCreateForm = Widget.extend({
|
var ProductPickerQuickCreateForm = Widget.extend({
|
||||||
className: "oe_one2many_product_picker_quick_create",
|
className: "oe_one2many_product_picker_quick_create",
|
||||||
xmlDependencies: [
|
xmlDependencies: [
|
||||||
"/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_create.xml",
|
"/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_create.xml",
|
||||||
],
|
],
|
||||||
|
|
||||||
custom_events: {
|
|
||||||
reload_view: "_onReloadView",
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
|
@ -51,54 +47,53 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateForm", f
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
start: function() {
|
start: function() {
|
||||||
const def1 = this._super.apply(this, arguments);
|
var self = this;
|
||||||
const form_arch = this._generateFormArch();
|
return this._super.apply(this, arguments).then(function() {
|
||||||
const fieldsView = {
|
var form_arch = self._generateFormArch();
|
||||||
arch: form_arch,
|
var fieldsView = {
|
||||||
fields: this.fields,
|
arch: form_arch,
|
||||||
viewFields: this.fields,
|
fields: self.fields,
|
||||||
base_model: this.basicFieldParams.field.relation,
|
viewFields: self.fields,
|
||||||
type: "form",
|
base_model: self.basicFieldParams.field.relation,
|
||||||
model: this.basicFieldParams.field.relation,
|
type: "form",
|
||||||
};
|
model: self.basicFieldParams.field.relation,
|
||||||
|
};
|
||||||
|
|
||||||
const node_context = this.node.attr("context") || "{}";
|
var node_context = self.node.attr("context") || "{}";
|
||||||
this.nodeContext = py.eval(node_context, {
|
self.nodeContext = py.eval(node_context, {
|
||||||
active_id: this.res_id || false,
|
active_id: self.res_id || false,
|
||||||
|
});
|
||||||
|
var refinedContext = _.extend(
|
||||||
|
{},
|
||||||
|
self.main_state.getContext(),
|
||||||
|
self.nodeContext
|
||||||
|
);
|
||||||
|
_.extend(refinedContext, self.editContext);
|
||||||
|
self.formView = new ProductPickerQuickCreateFormView(fieldsView, {
|
||||||
|
context: refinedContext,
|
||||||
|
compareKey: self.compareKey,
|
||||||
|
fieldMap: self.fieldMap,
|
||||||
|
modelName: self.basicFieldParams.field.relation,
|
||||||
|
userContext: self.getSession().user_context,
|
||||||
|
ids: self.res_id ? [self.res_id] : [],
|
||||||
|
currentId: self.res_id || undefined,
|
||||||
|
mode: self.res_id && self.readonly ? "readonly" : "edit",
|
||||||
|
recordID: self.id,
|
||||||
|
index: 0,
|
||||||
|
parentID: self.basicFieldParams.parentID,
|
||||||
|
default_buttons: false,
|
||||||
|
withControlPanel: false,
|
||||||
|
model: self.basicFieldParams.model,
|
||||||
|
mainRecordData: self.getParent().getParent().state,
|
||||||
|
});
|
||||||
|
return self.formView.getController(self).then(function(controller) {
|
||||||
|
self.controller = controller;
|
||||||
|
self.$el.empty();
|
||||||
|
self.controller.appendTo(self.$el);
|
||||||
|
self.trigger_up("back_form_loaded");
|
||||||
|
return controller;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
const refinedContext = _.extend(
|
|
||||||
{},
|
|
||||||
this.main_state.getContext(),
|
|
||||||
this.nodeContext
|
|
||||||
);
|
|
||||||
_.extend(refinedContext, this.editContext);
|
|
||||||
this.formView = new ProductPickerQuickCreateFormView(fieldsView, {
|
|
||||||
context: refinedContext,
|
|
||||||
compareKey: this.compareKey,
|
|
||||||
fieldMap: this.fieldMap,
|
|
||||||
modelName: this.basicFieldParams.field.relation,
|
|
||||||
userContext: this.getSession().user_context,
|
|
||||||
ids: this.res_id ? [this.res_id] : [],
|
|
||||||
currentId: this.res_id || undefined,
|
|
||||||
mode: this.res_id && this.readonly ? "readonly" : "edit",
|
|
||||||
recordID: this.id,
|
|
||||||
index: 0,
|
|
||||||
parentID: this.basicFieldParams.parentID,
|
|
||||||
default_buttons: false,
|
|
||||||
withControlPanel: false,
|
|
||||||
model: this.basicFieldParams.model,
|
|
||||||
mainRecordData: this.getParent().getParent().state,
|
|
||||||
});
|
|
||||||
// If (this.id) {
|
|
||||||
// this.basicFieldParams.model.save(this.id, {savePoint: true});
|
|
||||||
// }
|
|
||||||
const def2 = this.formView.getController(this).then(controller => {
|
|
||||||
this.controller = controller;
|
|
||||||
this.$el.empty();
|
|
||||||
this.controller.appendTo(this.$el);
|
|
||||||
});
|
|
||||||
|
|
||||||
return Promise.all([def1, def2]);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
on_attach_callback: function() {
|
on_attach_callback: function() {
|
||||||
|
@ -112,7 +107,7 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateForm", f
|
||||||
* @returns {String}
|
* @returns {String}
|
||||||
*/
|
*/
|
||||||
_generateFormArch: function() {
|
_generateFormArch: function() {
|
||||||
let template =
|
var template =
|
||||||
"<templates><t t-name='One2ManyProductPicker.QuickCreateForm'>";
|
"<templates><t t-name='One2ManyProductPicker.QuickCreateForm'>";
|
||||||
template += this.basicFieldParams.field.views.form.arch;
|
template += this.basicFieldParams.field.views.form.arch;
|
||||||
template += "</t></templates>";
|
template += "</t></templates>";
|
||||||
|
@ -122,42 +117,6 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateForm", f
|
||||||
record_search: this.searchRecord,
|
record_search: this.searchRecord,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
* @param {CustomEvent} evt
|
|
||||||
*/
|
|
||||||
_onReloadView: function(evt) {
|
|
||||||
this.editContext = {
|
|
||||||
ignore_onchanges: [this.compareKey],
|
|
||||||
base_record_id: evt.data.baseRecordID || null,
|
|
||||||
base_record_res_id: evt.data.baseRecordResID || null,
|
|
||||||
base_record_compare_value: evt.data.baseRecordCompareValue || null,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (evt.data.baseRecordCompareValue === evt.data.compareValue) {
|
|
||||||
this.res_id = evt.data.baseRecordResID;
|
|
||||||
this.id = evt.data.baseRecordID;
|
|
||||||
this.start();
|
|
||||||
} else {
|
|
||||||
this.getParent()
|
|
||||||
._generateVirtualState({}, this.editContext)
|
|
||||||
.then(state => {
|
|
||||||
const data = {};
|
|
||||||
data[this.compareKey] = {
|
|
||||||
operation: "ADD",
|
|
||||||
id: evt.data.compareValue,
|
|
||||||
};
|
|
||||||
this.basicFieldParams.model
|
|
||||||
._applyChange(state.id, data)
|
|
||||||
.then(() => {
|
|
||||||
this.res_id = state.res_id;
|
|
||||||
this.id = state.id;
|
|
||||||
this.start();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
widgetRegistry.add(
|
widgetRegistry.add(
|
||||||
|
|
|
@ -10,19 +10,19 @@ odoo.define(
|
||||||
* is used by the RecordQuickCreate in One2ManyProductPicker views.
|
* is used by the RecordQuickCreate in One2ManyProductPicker views.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const QuickCreateFormView = require("web.QuickCreateFormView");
|
var QuickCreateFormView = require("web.QuickCreateFormView");
|
||||||
const BasicModel = require("web.BasicModel");
|
var BasicModel = require("web.BasicModel");
|
||||||
const core = require("web.core");
|
var core = require("web.core");
|
||||||
|
|
||||||
const qweb = core.qweb;
|
var qweb = core.qweb;
|
||||||
|
|
||||||
BasicModel.include({
|
BasicModel.include({
|
||||||
_applyOnChange: function(values, record, viewType) {
|
_applyOnChange: function(values, record, viewType) {
|
||||||
// Ignore changes by record context 'ignore_onchanges' fields
|
// Ignore changes by record context 'ignore_onchanges' fields
|
||||||
if ("ignore_onchanges" in record.context) {
|
if ("ignore_onchanges" in record.context) {
|
||||||
const ignore_changes = record.context.ignore_onchanges;
|
var ignore_changes = record.context.ignore_onchanges;
|
||||||
for (const index in ignore_changes) {
|
for (var index in ignore_changes) {
|
||||||
const field_name = ignore_changes[index];
|
var field_name = ignore_changes[index];
|
||||||
delete values[field_name];
|
delete values[field_name];
|
||||||
}
|
}
|
||||||
delete record.context.ignore_onchanges;
|
delete record.context.ignore_onchanges;
|
||||||
|
@ -31,7 +31,7 @@ odoo.define(
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const ProductPickerQuickCreateFormRenderer = QuickCreateFormView.prototype.config.Renderer.extend(
|
var ProductPickerQuickCreateFormRenderer = QuickCreateFormView.prototype.config.Renderer.extend(
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
|
@ -45,7 +45,7 @@ odoo.define(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const ProductPickerQuickCreateFormController = QuickCreateFormView.prototype.config.Controller.extend(
|
var ProductPickerQuickCreateFormController = QuickCreateFormView.prototype.config.Controller.extend(
|
||||||
{
|
{
|
||||||
events: _.extend({}, QuickCreateFormView.prototype.events, {
|
events: _.extend({}, QuickCreateFormView.prototype.events, {
|
||||||
"click .oe_record_add": "_onClickAdd",
|
"click .oe_record_add": "_onClickAdd",
|
||||||
|
@ -66,8 +66,9 @@ odoo.define(
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
_applyChanges: function() {
|
_applyChanges: function() {
|
||||||
return this._super.apply(this, arguments).then(() => {
|
var self = this;
|
||||||
this._updateButtons();
|
return this._super.apply(this, arguments).then(function() {
|
||||||
|
self._updateButtons();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -75,17 +76,13 @@ odoo.define(
|
||||||
* Create or accept changes
|
* Create or accept changes
|
||||||
*/
|
*/
|
||||||
auto: function() {
|
auto: function() {
|
||||||
const record = this.model.get(this.handle);
|
var record = this.model.get(this.handle);
|
||||||
if (
|
if (!record.context.has_changes_unconfirmed) {
|
||||||
record.context.has_changes_confirmed ||
|
|
||||||
typeof record.context.has_changes_confirmed === "undefined"
|
|
||||||
) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const state = this._getRecordState();
|
if (this.model.isNew(record.id)) {
|
||||||
if (state === "new") {
|
|
||||||
this._add();
|
this._add();
|
||||||
} else if (state === "dirty") {
|
} else if (this.model.isDirty(record.id)) {
|
||||||
this._change();
|
this._change();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -99,16 +96,19 @@ odoo.define(
|
||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
_getRecordState: function() {
|
_getRecordState: function() {
|
||||||
const record = this.model.get(this.handle);
|
var record = this.model.get(this.handle);
|
||||||
let state = "record";
|
var state = "record";
|
||||||
if (this.model.isNew(record.id)) {
|
if (
|
||||||
|
this.model.isNew(record.id) &&
|
||||||
|
this.model.isPureVirtual(record.id)
|
||||||
|
) {
|
||||||
state = "new";
|
state = "new";
|
||||||
} else if (record.isDirty()) {
|
} else if (record.context.has_changes_unconfirmed) {
|
||||||
state = "dirty";
|
state = "dirty";
|
||||||
}
|
}
|
||||||
if (state === "new") {
|
if (state === "new") {
|
||||||
for (const index in this.mainRecordData.data) {
|
for (var index in this.mainRecordData.data) {
|
||||||
const recordData = this.mainRecordData.data[index];
|
var recordData = this.mainRecordData.data[index];
|
||||||
if (recordData.ref === record.ref) {
|
if (recordData.ref === record.ref) {
|
||||||
if (record.isDirty()) {
|
if (record.isDirty()) {
|
||||||
state = "dirty";
|
state = "dirty";
|
||||||
|
@ -174,8 +174,8 @@ odoo.define(
|
||||||
* @returns {Boolean}
|
* @returns {Boolean}
|
||||||
*/
|
*/
|
||||||
_needReloadCard: function(fields_changed) {
|
_needReloadCard: function(fields_changed) {
|
||||||
for (const index in fields_changed) {
|
for (var index in fields_changed) {
|
||||||
const field = fields_changed[index];
|
var field = fields_changed[index];
|
||||||
if (field === this.fieldMap[this.compareKey]) {
|
if (field === this.fieldMap[this.compareKey]) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -192,51 +192,18 @@ odoo.define(
|
||||||
* @param {ChangeEvent} ev
|
* @param {ChangeEvent} ev
|
||||||
*/
|
*/
|
||||||
_onFieldChanged: function(ev) {
|
_onFieldChanged: function(ev) {
|
||||||
const fields_changed = Object.keys(ev.data.changes);
|
this._super.apply(this, arguments);
|
||||||
if (this._needReloadCard(fields_changed)) {
|
if (!_.isEmpty(ev.data.changes)) {
|
||||||
const field = ev.data.changes[fields_changed[0]];
|
if (this.model.isPureVirtual(this.handle)) {
|
||||||
let new_value = false;
|
this.model.unsetDirty(this.handle);
|
||||||
if (typeof field === "object") {
|
|
||||||
new_value = field.id;
|
|
||||||
} else {
|
|
||||||
new_value = field;
|
|
||||||
}
|
|
||||||
const reload_values = {
|
|
||||||
compareValue: new_value,
|
|
||||||
};
|
|
||||||
const record = this.model.get(this.handle);
|
|
||||||
if ("base_record_id" in record.context) {
|
|
||||||
reload_values.baseRecordID = record.context.base_record_id;
|
|
||||||
reload_values.baseRecordResID =
|
|
||||||
record.context.base_record_res_id;
|
|
||||||
reload_values.baseRecordCompareValue =
|
|
||||||
record.context.base_record_compare_value;
|
|
||||||
} else {
|
|
||||||
let old_value = record.data[this.compareKey];
|
|
||||||
if (typeof old_value === "object") {
|
|
||||||
old_value = old_value.data.id;
|
|
||||||
}
|
|
||||||
reload_values.baseRecordID = record.id;
|
|
||||||
reload_values.baseRecordResID = record.ref;
|
|
||||||
reload_values.baseRecordCompareValue = old_value;
|
|
||||||
}
|
|
||||||
this.trigger_up("reload_view", reload_values);
|
|
||||||
|
|
||||||
// Discard current change
|
|
||||||
ev.data.changes = {};
|
|
||||||
} else {
|
|
||||||
this._super.apply(this, arguments);
|
|
||||||
if (!_.isEmpty(ev.data.changes)) {
|
|
||||||
if (this.model.isPureVirtual(this.handle)) {
|
|
||||||
this.model.unsetDirty(this.handle);
|
|
||||||
}
|
|
||||||
this.model.updateRecordContext(this.handle, {
|
|
||||||
has_changes_confirmed: false,
|
|
||||||
});
|
|
||||||
this.trigger_up("quick_record_updated", {
|
|
||||||
changes: ev.data.changes,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
this.model.updateRecordContext(this.handle, {
|
||||||
|
has_changes_unconfirmed: true,
|
||||||
|
});
|
||||||
|
this.trigger_up("quick_record_updated", {
|
||||||
|
changes: ev.data.changes,
|
||||||
|
highlight: {qty: true},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -244,12 +211,14 @@ odoo.define(
|
||||||
* @returns {Deferred}
|
* @returns {Deferred}
|
||||||
*/
|
*/
|
||||||
_add: function() {
|
_add: function() {
|
||||||
|
var self = this;
|
||||||
if (this._disabled) {
|
if (this._disabled) {
|
||||||
// Don't do anything if we are already creating a record
|
// Don't do anything if we are already creating a record
|
||||||
return Promise.resolve();
|
return $.Deferred().resolve();
|
||||||
}
|
}
|
||||||
this.model.updateRecordContext(this.handle, {
|
this.model.updateRecordContext(this.handle, {
|
||||||
has_changes_confirmed: true,
|
need_notify: true,
|
||||||
|
modified: true,
|
||||||
});
|
});
|
||||||
this._disableQuickCreate();
|
this._disableQuickCreate();
|
||||||
return this.saveRecord(this.handle, {
|
return this.saveRecord(this.handle, {
|
||||||
|
@ -257,105 +226,145 @@ odoo.define(
|
||||||
reload: true,
|
reload: true,
|
||||||
savePoint: true,
|
savePoint: true,
|
||||||
viewType: "form",
|
viewType: "form",
|
||||||
}).then(() => {
|
}).then(function() {
|
||||||
const record = this.model.get(this.handle);
|
var record = self.model.get(self.handle);
|
||||||
this.model.updateRecordContext(this.handle, {saving: true});
|
self.model.updateRecordContext(record.id, {
|
||||||
this.trigger_up("restore_flip_card", {
|
has_changes_unconfirmed: false,
|
||||||
success_callback: () => {
|
lazy_qty: record.data[self.fieldMap.product_uom_qty],
|
||||||
this.trigger_up("create_quick_record", {
|
});
|
||||||
|
self.trigger_up("block_card", {status: true});
|
||||||
|
self.trigger_up("modify_quick_record", {
|
||||||
|
id: record.id,
|
||||||
|
});
|
||||||
|
self.trigger_up("restore_flip_card", {
|
||||||
|
success_callback: function() {
|
||||||
|
self.trigger_up("create_quick_record", {
|
||||||
id: record.id,
|
id: record.id,
|
||||||
callback: () => {
|
on_onchange: function() {
|
||||||
this.model.updateRecordContext(this.handle, {
|
self.trigger_up("block_card", {status: false});
|
||||||
saving: false,
|
self._enableQuickCreate();
|
||||||
});
|
|
||||||
this.model.unsetDirty(this.handle);
|
|
||||||
this._enableQuickCreate();
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
block: true,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_remove: function() {
|
_remove: function() {
|
||||||
|
var self = this;
|
||||||
if (this._disabled) {
|
if (this._disabled) {
|
||||||
return Promise.resolve();
|
return $.Deferred().resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.model.updateRecordContext(this.handle, {
|
||||||
|
need_notify: true,
|
||||||
|
modified: true,
|
||||||
|
});
|
||||||
this._disableQuickCreate();
|
this._disableQuickCreate();
|
||||||
this.trigger_up("restore_flip_card", {block: true});
|
var record = this.model.get(this.handle);
|
||||||
const record = this.model.get(this.handle);
|
this.trigger_up("block_card", {status: true});
|
||||||
this.trigger_up("list_record_remove", {
|
this.trigger_up("modify_quick_record", {
|
||||||
id: record.id,
|
id: record.id,
|
||||||
});
|
});
|
||||||
|
this.trigger_up("restore_flip_card", {
|
||||||
|
success_callback: function() {
|
||||||
|
self.trigger_up("list_record_remove", {
|
||||||
|
id: record.id,
|
||||||
|
on_onchange: function() {
|
||||||
|
self.trigger_up("block_card", {status: false});
|
||||||
|
self._enableQuickCreate();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_change: function() {
|
_change: function() {
|
||||||
const self = this;
|
var self = this;
|
||||||
if (this._disabled) {
|
if (this._disabled) {
|
||||||
// Don't do anything if we are already creating a record
|
// Don't do anything if we are already creating a record
|
||||||
return Promise.resolve();
|
return $.Deferred().resolve();
|
||||||
|
}
|
||||||
|
var record = self.model.get(self.handle);
|
||||||
|
if (
|
||||||
|
!this.model.isDirty(this.handle) ||
|
||||||
|
!record.context.has_changes_unconfirmed
|
||||||
|
) {
|
||||||
|
this.trigger_up("restore_flip_card");
|
||||||
|
return $.Deferred().resolve();
|
||||||
}
|
}
|
||||||
this._disableQuickCreate();
|
|
||||||
this.model.updateRecordContext(this.handle, {
|
|
||||||
has_changes_confirmed: true,
|
|
||||||
});
|
|
||||||
const record = this.model.get(this.handle);
|
|
||||||
|
|
||||||
this.trigger_up("restore_flip_card", {
|
this.model.updateRecordContext(this.handle, {
|
||||||
success_callback: function() {
|
need_notify: true,
|
||||||
// Qty are handled in a special way because can be modified without
|
modified: true,
|
||||||
// wait for server response
|
});
|
||||||
self.model.localData[record.id].data[
|
|
||||||
self.fieldMap.product_uom_qty
|
this._disableQuickCreate();
|
||||||
] = record.data[self.fieldMap.product_uom_qty];
|
// SaveRecord used to make a save point.
|
||||||
// SaveRecord used to make a save point.
|
return this.saveRecord(this.handle, {
|
||||||
self.saveRecord(self.handle, {
|
stayInEdit: true,
|
||||||
stayInEdit: true,
|
reload: true,
|
||||||
reload: true,
|
savePoint: true,
|
||||||
savePoint: true,
|
viewType: "form",
|
||||||
viewType: "form",
|
}).then(function() {
|
||||||
}).then(() => {
|
record = self.model.get(self.handle);
|
||||||
|
self.model.updateRecordContext(record.id, {
|
||||||
|
has_changes_unconfirmed: false,
|
||||||
|
lazy_qty: record.data[self.fieldMap.product_uom_qty],
|
||||||
|
});
|
||||||
|
self.trigger_up("block_card", {status: true});
|
||||||
|
self.trigger_up("modify_quick_record", {
|
||||||
|
id: record.id,
|
||||||
|
});
|
||||||
|
self.trigger_up("restore_flip_card", {
|
||||||
|
success_callback: function() {
|
||||||
self.trigger_up("update_quick_record", {
|
self.trigger_up("update_quick_record", {
|
||||||
id: record.id,
|
id: record.id,
|
||||||
callback: function() {
|
on_onchange: function() {
|
||||||
self.model.unsetDirty(self.handle);
|
self.trigger_up("block_card", {status: false});
|
||||||
self._enableQuickCreate();
|
self._enableQuickCreate();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
},
|
||||||
},
|
});
|
||||||
block: true,
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_discard: function() {
|
_discard: function() {
|
||||||
|
var self = this;
|
||||||
if (this._disabled) {
|
if (this._disabled) {
|
||||||
// Don't do anything if we are already creating a record
|
// Don't do anything if we are already creating a record
|
||||||
return;
|
return $.Deferred().resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
var record = self.model.get(self.handle);
|
||||||
|
if (
|
||||||
|
!this.model.isDirty(this.handle) ||
|
||||||
|
!record.context.has_changes_unconfirmed
|
||||||
|
) {
|
||||||
|
this.trigger_up("restore_flip_card");
|
||||||
|
return $.Deferred().resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
this._disableQuickCreate();
|
|
||||||
this.model.updateRecordContext(this.handle, {
|
this.model.updateRecordContext(this.handle, {
|
||||||
has_changes_confirmed: true,
|
has_changes_unconfirmed: false,
|
||||||
});
|
});
|
||||||
|
this._disableQuickCreate();
|
||||||
// Rollback to restore the save point
|
// Rollback to restore the save point
|
||||||
this.model.discardChanges(this.handle, {
|
this.model.discardChanges(this.handle, {
|
||||||
rollback: true,
|
rollback: true,
|
||||||
});
|
});
|
||||||
const record = this.model.get(this.handle);
|
return this.update({}, {reload: false}).then(function() {
|
||||||
this.trigger_up("quick_record_updated", {
|
record = self.model.get(self.handle);
|
||||||
changes: record.data,
|
self.trigger_up("quick_record_updated", {
|
||||||
});
|
changes: record.data,
|
||||||
|
});
|
||||||
this.update({}, {reload: false}).then(() => {
|
self.trigger_up("restore_flip_card", {
|
||||||
if (!this.model.isNew(record.id)) {
|
success_callback: function() {
|
||||||
this.model.unsetDirty(this.handle);
|
self._updateButtons();
|
||||||
}
|
self._enableQuickCreate();
|
||||||
this.trigger_up("restore_flip_card");
|
},
|
||||||
this._updateButtons();
|
});
|
||||||
this._enableQuickCreate();
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -397,7 +406,7 @@ odoo.define(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const ProductPickerQuickCreateFormView = QuickCreateFormView.extend({
|
var ProductPickerQuickCreateFormView = QuickCreateFormView.extend({
|
||||||
config: _.extend({}, QuickCreateFormView.prototype.config, {
|
config: _.extend({}, QuickCreateFormView.prototype.config, {
|
||||||
Renderer: ProductPickerQuickCreateFormRenderer,
|
Renderer: ProductPickerQuickCreateFormRenderer,
|
||||||
Controller: ProductPickerQuickCreateFormController,
|
Controller: ProductPickerQuickCreateFormController,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* global py */
|
||||||
// Copyright 2020 Tecnativa - Alexandre Díaz
|
// Copyright 2020 Tecnativa - Alexandre Díaz
|
||||||
// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
odoo.define(
|
odoo.define(
|
||||||
|
@ -5,21 +6,23 @@ odoo.define(
|
||||||
function(require) {
|
function(require) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const core = require("web.core");
|
var core = require("web.core");
|
||||||
const Widget = require("web.Widget");
|
var Widget = require("web.Widget");
|
||||||
const ProductPickerQuickModifPriceFormView = require("web_widget_one2many_product_picker.ProductPickerQuickModifPriceFormView")
|
var widgetRegistry = require("web.widget_registry");
|
||||||
|
var ProductPickerQuickModifPriceFormView = require("web_widget_one2many_product_picker.ProductPickerQuickModifPriceFormView")
|
||||||
.ProductPickerQuickModifPriceFormView;
|
.ProductPickerQuickModifPriceFormView;
|
||||||
|
|
||||||
const qweb = core.qweb;
|
var qweb = core.qweb;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This widget render a Form. Used by FieldOne2ManyProductPicker
|
* This widget render a Form. Used by FieldOne2ManyProductPicker
|
||||||
*/
|
*/
|
||||||
const ProductPickerQuickModifPriceForm = Widget.extend({
|
var ProductPickerQuickModifPriceForm = Widget.extend({
|
||||||
className: "oe_one2many_product_picker_quick_modif_price",
|
className: "oe_one2many_product_picker_quick_modif_price",
|
||||||
xmlDependencies: [
|
xmlDependencies: [
|
||||||
"/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_modif_price.xml",
|
"/web_widget_one2many_product_picker/static/src/xml/one2many_product_picker_quick_modif_price.xml",
|
||||||
],
|
],
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
"click .oe_record_change": "_onClickChange",
|
"click .oe_record_change": "_onClickChange",
|
||||||
"click .oe_record_discard": "_onClickDiscard",
|
"click .oe_record_discard": "_onClickDiscard",
|
||||||
|
@ -30,13 +33,14 @@ odoo.define(
|
||||||
*/
|
*/
|
||||||
init: function(parent, options) {
|
init: function(parent, options) {
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
|
this.trigger_up("pause_auto_save");
|
||||||
this.state = options.state;
|
this.state = options.state;
|
||||||
this.main_state = options.main_state;
|
this.main_state = options.main_state;
|
||||||
this.node = options.node;
|
this.node = options.node;
|
||||||
this.fields = options.fields;
|
this.fields = options.fields;
|
||||||
this.fieldMap = options.fieldMap;
|
this.fieldMap = options.fieldMap;
|
||||||
this.searchRecord = options.searchRecord;
|
this.searchRecord = options.searchRecord;
|
||||||
this.fieldsInfo = options.fieldsInfo;
|
this.fieldsInfo = _.extend({}, options.fieldsInfo);
|
||||||
this.readonly = options.readonly;
|
this.readonly = options.readonly;
|
||||||
this.basicFieldParams = options.basicFieldParams;
|
this.basicFieldParams = options.basicFieldParams;
|
||||||
this.canEditPrice = options.canEditPrice;
|
this.canEditPrice = options.canEditPrice;
|
||||||
|
@ -45,56 +49,76 @@ odoo.define(
|
||||||
this.res_id = this.state && this.state.res_id;
|
this.res_id = this.state && this.state.res_id;
|
||||||
this.id = this.state && this.state.id;
|
this.id = this.state && this.state.id;
|
||||||
this.editContext = {};
|
this.editContext = {};
|
||||||
|
this._fieldsInvisible = [];
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
start: function() {
|
start: function() {
|
||||||
const def1 = this._super.apply(this, arguments);
|
var self = this;
|
||||||
const fieldsView = {
|
this._super.apply(this, arguments).then(function() {
|
||||||
arch: this._generateFormArch(),
|
var fieldsView = {
|
||||||
fields: this.fields,
|
arch: self._generateFormArch(),
|
||||||
viewFields: this.fields,
|
fields: self.fields,
|
||||||
base_model: this.basicFieldParams.field.relation,
|
viewFields: self.fields,
|
||||||
type: "form",
|
base_model: self.basicFieldParams.field.relation,
|
||||||
model: this.basicFieldParams.field.relation,
|
type: "form",
|
||||||
};
|
model: self.basicFieldParams.field.relation,
|
||||||
this.formView = new ProductPickerQuickModifPriceFormView(fieldsView, {
|
};
|
||||||
context: this.main_state.getContext(),
|
|
||||||
fieldMap: this.fieldMap,
|
|
||||||
modelName: this.basicFieldParams.field.relation,
|
|
||||||
userContext: this.getSession().user_context,
|
|
||||||
ids: this.res_id ? [this.res_id] : [],
|
|
||||||
currentId: this.res_id || undefined,
|
|
||||||
mode: this.res_id && this.readonly ? "readonly" : "edit",
|
|
||||||
recordID: this.id,
|
|
||||||
index: 0,
|
|
||||||
parentID: this.basicFieldParams.parentID,
|
|
||||||
default_buttons: true,
|
|
||||||
withControlPanel: false,
|
|
||||||
model: this.basicFieldParams.model,
|
|
||||||
parentRecordData: this.basicFieldParams.recordData,
|
|
||||||
currencyField: this.currencyField,
|
|
||||||
disable_autofocus: true,
|
|
||||||
});
|
|
||||||
if (this.id) {
|
|
||||||
this.basicFieldParams.model.save(this.id, {savePoint: true});
|
|
||||||
}
|
|
||||||
const def2 = this.formView.getController(this).then(controller => {
|
|
||||||
this.controller = controller;
|
|
||||||
this.$(".modal-body").empty();
|
|
||||||
this.controller.appendTo(this.$(".modal-body"));
|
|
||||||
this.$el.on("hidden.bs.modal", this._onModalHidden.bind(this));
|
|
||||||
});
|
|
||||||
|
|
||||||
return Promise.all([def1, def2]);
|
var node_context = self.node.attr("context") || "{}";
|
||||||
|
self.nodeContext = py.eval(node_context, {
|
||||||
|
active_id: self.res_id || false,
|
||||||
|
});
|
||||||
|
var refinedContext = _.extend(
|
||||||
|
{},
|
||||||
|
self.main_state.getContext(),
|
||||||
|
self.nodeContext
|
||||||
|
);
|
||||||
|
_.extend(refinedContext, self.editContext);
|
||||||
|
|
||||||
|
self.formView = new ProductPickerQuickModifPriceFormView(
|
||||||
|
fieldsView,
|
||||||
|
{
|
||||||
|
context: refinedContext,
|
||||||
|
fieldMap: self.fieldMap,
|
||||||
|
modelName: self.basicFieldParams.field.relation,
|
||||||
|
userContext: self.getSession().user_context,
|
||||||
|
ids: self.res_id ? [self.res_id] : [],
|
||||||
|
currentId: self.res_id || undefined,
|
||||||
|
mode: self.res_id && self.readonly ? "readonly" : "edit",
|
||||||
|
recordID: self.id,
|
||||||
|
index: 0,
|
||||||
|
parentID: self.basicFieldParams.parentID,
|
||||||
|
default_buttons: true,
|
||||||
|
withControlPanel: false,
|
||||||
|
model: self.basicFieldParams.model,
|
||||||
|
parentRecordData: self.getParent().getParent().state,
|
||||||
|
currencyField: self.currencyField,
|
||||||
|
disable_autofocus: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (self.id) {
|
||||||
|
self.basicFieldParams.model.save(self.id, {savePoint: true});
|
||||||
|
}
|
||||||
|
return self.formView.getController(self).then(function(controller) {
|
||||||
|
self.controller = controller;
|
||||||
|
self.$(".modal-body").empty();
|
||||||
|
self.controller.appendTo(self.$(".modal-body"));
|
||||||
|
self.$el.on("hidden.bs.modal", self._onModalHidden.bind(self));
|
||||||
|
self.$el.find(".oe_record_change").removeClass("d-none");
|
||||||
|
return controller;
|
||||||
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
destroy: function() {
|
destroy: function() {
|
||||||
|
this._restoreNoFetch();
|
||||||
|
this.trigger_up("resume_auto_save");
|
||||||
this.$el.off("hidden.bs.modal");
|
this.$el.off("hidden.bs.modal");
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
},
|
},
|
||||||
|
@ -108,29 +132,32 @@ odoo.define(
|
||||||
* @returns {String}
|
* @returns {String}
|
||||||
*/
|
*/
|
||||||
_generateFormArch: function() {
|
_generateFormArch: function() {
|
||||||
const wanted_field_states = this._getWantedFieldState();
|
var wanted_field_states = this._getWantedFieldState();
|
||||||
let template =
|
var template =
|
||||||
"<templates><t t-name='One2ManyProductPicker.QuickModifPrice.Form'>";
|
"<templates><t t-name='One2ManyProductPicker.QuickModifPrice.Form'>";
|
||||||
template += this.basicFieldParams.field.views.form.arch;
|
template += this.basicFieldParams.field.views.form.arch;
|
||||||
template += "</t></templates>";
|
template += "</t></templates>";
|
||||||
qweb.add_template(template);
|
qweb.add_template(template);
|
||||||
const $arch = $(
|
var $arch = $(
|
||||||
qweb.render("One2ManyProductPicker.QuickModifPrice.Form", {
|
qweb.render("One2ManyProductPicker.QuickModifPrice.Form", {
|
||||||
field_map: this.fieldMap,
|
field_map: this.fieldMap,
|
||||||
record_search: this.searchRecord,
|
record_search: this.searchRecord,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const field_names = Object.keys(
|
var field_names = Object.keys(
|
||||||
this.basicFieldParams.field.views.form.fields
|
this.basicFieldParams.field.views.form.fields
|
||||||
);
|
);
|
||||||
let gen_arch = "<form><group>";
|
var gen_arch = "<form><group>";
|
||||||
for (const index in field_names) {
|
for (var index in field_names) {
|
||||||
const field_name = field_names[index];
|
var field_name = field_names[index];
|
||||||
const $field = $arch.find("field[name='" + field_name + "']");
|
var $field = $arch.find("field[name='" + field_name + "']");
|
||||||
const modifiers = $field.attr("modifiers")
|
var modifiers = $field.attr("modifiers")
|
||||||
? JSON.parse($field.attr("modifiers"))
|
? JSON.parse($field.attr("modifiers"))
|
||||||
: {};
|
: {};
|
||||||
|
if (!modifiers.invisible && !(field_name in wanted_field_states)) {
|
||||||
|
this._fieldsInvisible.push(field_name);
|
||||||
|
}
|
||||||
modifiers.invisible = !(field_name in wanted_field_states);
|
modifiers.invisible = !(field_name in wanted_field_states);
|
||||||
modifiers.readonly = wanted_field_states[field_name];
|
modifiers.readonly = wanted_field_states[field_name];
|
||||||
$field.attr("modifiers", JSON.stringify(modifiers));
|
$field.attr("modifiers", JSON.stringify(modifiers));
|
||||||
|
@ -160,12 +187,40 @@ odoo.define(
|
||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
_getWantedFieldState: function() {
|
_getWantedFieldState: function() {
|
||||||
const wantedFieldState = {};
|
var wantedFieldState = {};
|
||||||
wantedFieldState[this.fieldMap.discount] = !this.canEditDiscount;
|
wantedFieldState[this.fieldMap.discount] = !this.canEditDiscount;
|
||||||
wantedFieldState[this.fieldMap.price_unit] = !this.canEditPrice;
|
wantedFieldState[this.fieldMap.price_unit] = !this.canEditPrice;
|
||||||
return wantedFieldState;
|
return wantedFieldState;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_onClickDiscard: function(ev) {
|
||||||
|
if (this.controller) {
|
||||||
|
this._hideControlButtons(true);
|
||||||
|
this.controller._onClickDiscard(ev);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_onClickChange: function(ev) {
|
||||||
|
var self = this;
|
||||||
|
if (!this.controller) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self._hideControlButtons(true);
|
||||||
|
this.controller._onClickChange(ev).then(function(res) {
|
||||||
|
if (res) {
|
||||||
|
self.$el.modal("hide");
|
||||||
|
} else {
|
||||||
|
self._hideControlButtons(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
|
@ -173,84 +228,28 @@ odoo.define(
|
||||||
this.destroy();
|
this.destroy();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_hideControlButtons: function(status) {
|
||||||
|
this.$el.find(".oe_record_change").toggleClass("d-none", status);
|
||||||
|
this.$el.find(".oe_record_discard").toggleClass("d-none", status);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @param {MouseEvent} ev
|
|
||||||
*/
|
*/
|
||||||
_onClickChange: function(ev) {
|
_restoreNoFetch: function() {
|
||||||
ev.stopPropagation();
|
var record = this.basicFieldParams.model.get(this.id);
|
||||||
const model = this.basicFieldParams.model;
|
for (var field_name of this._fieldsInvisible) {
|
||||||
model.updateRecordContext(this.id, {
|
record.fieldsInfo[record.viewType][field_name].__no_fetch = false;
|
||||||
has_changes_confirmed: true,
|
|
||||||
});
|
|
||||||
const is_virtual = model.isPureVirtual(this.id);
|
|
||||||
|
|
||||||
// If is a 'pure virtual' record, save it in the selected list
|
|
||||||
if (is_virtual) {
|
|
||||||
if (model.isDirty(this.id)) {
|
|
||||||
this._disableQuickCreate();
|
|
||||||
this.controller
|
|
||||||
.saveRecord(this.id, {
|
|
||||||
stayInEdit: true,
|
|
||||||
reload: true,
|
|
||||||
savePoint: true,
|
|
||||||
viewType: "form",
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
this._enableQuickCreate();
|
|
||||||
model.unsetDirty(this.id);
|
|
||||||
this.trigger_up("create_quick_record", {
|
|
||||||
id: this.id,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If is a "normal" record, update it
|
|
||||||
this.trigger_up("update_quick_record", {
|
|
||||||
id: this.id,
|
|
||||||
});
|
|
||||||
model.unsetDirty(this.id);
|
|
||||||
}
|
}
|
||||||
},
|
this._fieldsInvisible = [];
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
* @param {MouseEvent} ev
|
|
||||||
*/
|
|
||||||
_onClickDiscard: function(ev) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
const model = this.basicFieldParams.model;
|
|
||||||
model.discardChanges(this.id, {
|
|
||||||
rollback: true,
|
|
||||||
});
|
|
||||||
this.trigger_up("update_quick_record", {
|
|
||||||
id: this.id,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_disableQuickCreate: function() {
|
|
||||||
// Ensures that the record won't be created twice
|
|
||||||
this.$el.addClass("o_disabled");
|
|
||||||
this.$("input:not(:disabled),button:not(:disabled)")
|
|
||||||
.addClass("o_temporarily_disabled")
|
|
||||||
.attr("disabled", "disabled");
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_enableQuickCreate: function() {
|
|
||||||
// Allows to create again
|
|
||||||
this.$el.removeClass("o_disabled");
|
|
||||||
this.$("input.o_temporarily_disabled,button.o_temporarily_disabled")
|
|
||||||
.removeClass("o_temporarily_disabled")
|
|
||||||
.attr("disabled", false);
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
widgetRegistry.add(
|
||||||
|
"product_picker_quick_modif_price_form",
|
||||||
|
ProductPickerQuickModifPriceForm
|
||||||
|
);
|
||||||
|
|
||||||
return ProductPickerQuickModifPriceForm;
|
return ProductPickerQuickModifPriceForm;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -10,23 +10,24 @@ odoo.define(
|
||||||
* is used by the RecordQuickCreate in One2ManyProductPicker views.
|
* is used by the RecordQuickCreate in One2ManyProductPicker views.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const QuickCreateFormView = require("web.QuickCreateFormView");
|
var QuickCreateFormView = require("web.QuickCreateFormView");
|
||||||
const core = require("web.core");
|
var core = require("web.core");
|
||||||
const tools = require("web_widget_one2many_product_picker.tools");
|
var tools = require("web_widget_one2many_product_picker.tools");
|
||||||
|
|
||||||
const qweb = core.qweb;
|
var qweb = core.qweb;
|
||||||
|
|
||||||
const ProductPickerQuickModifPriceFormRenderer = QuickCreateFormView.prototype.config.Renderer.extend(
|
var ProductPickerQuickModifPriceFormRenderer = QuickCreateFormView.prototype.config.Renderer.extend(
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
start: function() {
|
start: function() {
|
||||||
|
var self = this;
|
||||||
this.$el.addClass(
|
this.$el.addClass(
|
||||||
"oe_one2many_product_picker_form_view o_xxs_form_view"
|
"oe_one2many_product_picker_form_view o_xxs_form_view"
|
||||||
);
|
);
|
||||||
return this._super.apply(this, arguments).then(() => {
|
return this._super.apply(this, arguments).then(function() {
|
||||||
this._appendPrice();
|
self._appendPrice();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -42,7 +43,7 @@ odoo.define(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const ProductPickerQuickModifPriceFormController = QuickCreateFormView.prototype.config.Controller.extend(
|
var ProductPickerQuickModifPriceFormController = QuickCreateFormView.prototype.config.Controller.extend(
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
|
@ -50,18 +51,20 @@ odoo.define(
|
||||||
init: function(parent, model, renderer, params) {
|
init: function(parent, model, renderer, params) {
|
||||||
this.fieldMap = params.fieldMap;
|
this.fieldMap = params.fieldMap;
|
||||||
this.context = params.context;
|
this.context = params.context;
|
||||||
this._super.apply(this, arguments);
|
|
||||||
this.currencyField = params.currencyField;
|
this.currencyField = params.currencyField;
|
||||||
this.parentRecordData = params.parentRecordData;
|
this.parentRecordData = params.parentRecordData;
|
||||||
|
this.fields = parent.state.fields;
|
||||||
|
this._super.apply(this, arguments);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
start: function() {
|
start: function() {
|
||||||
return this._super.apply(this, arguments).then(() => {
|
var self = this;
|
||||||
const record = this.model.get(this.handle);
|
return this._super.apply(this, arguments).then(function() {
|
||||||
this._updatePrice(record.data);
|
var record = self.model.get(self.handle);
|
||||||
|
self._updatePrice(record.data);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -70,8 +73,21 @@ odoo.define(
|
||||||
*/
|
*/
|
||||||
_onFieldChanged: function(ev) {
|
_onFieldChanged: function(ev) {
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
const record = this.model.get(this.handle);
|
if (!_.isEmpty(ev.data.changes)) {
|
||||||
this._updatePrice(_.extend({}, record.data, ev.data.changes));
|
this.model.updateRecordContext(this.handle, {
|
||||||
|
has_changes_unconfirmed: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
_applyChanges: function() {
|
||||||
|
return this._super.apply(this, arguments).then(() => {
|
||||||
|
var record = this.model.get(this.handle);
|
||||||
|
this._updatePrice(record.data);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -79,7 +95,7 @@ odoo.define(
|
||||||
* @param {Object} values
|
* @param {Object} values
|
||||||
*/
|
*/
|
||||||
_updatePrice: function(values) {
|
_updatePrice: function(values) {
|
||||||
const price_reduce = tools.priceReduce(
|
var price_reduce = tools.priceReduce(
|
||||||
values[this.fieldMap.price_unit],
|
values[this.fieldMap.price_unit],
|
||||||
values[this.fieldMap.discount]
|
values[this.fieldMap.discount]
|
||||||
);
|
);
|
||||||
|
@ -88,16 +104,136 @@ odoo.define(
|
||||||
.html(
|
.html(
|
||||||
tools.monetary(
|
tools.monetary(
|
||||||
price_reduce,
|
price_reduce,
|
||||||
this.getParent().state.fields[this.fieldMap.price_unit],
|
this.fields[this.fieldMap.price_unit],
|
||||||
this.currencyField,
|
this.currencyField,
|
||||||
values
|
values
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @param {MouseEvent} ev
|
||||||
|
*/
|
||||||
|
_onClickChange: function(ev) {
|
||||||
|
var self = this;
|
||||||
|
var def = $.Deferred();
|
||||||
|
ev.stopPropagation();
|
||||||
|
this.model.updateRecordContext(this.handle, {
|
||||||
|
has_changes_unconfirmed: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!this.model.isDirty(this.handle)) {
|
||||||
|
return def.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._disableQuickCreate();
|
||||||
|
this.trigger_up("quick_record_updated", {
|
||||||
|
changes: _.extend(
|
||||||
|
{},
|
||||||
|
this.model.localData[this.handle].data,
|
||||||
|
this.model.localData[this.handle]._changes
|
||||||
|
),
|
||||||
|
highlight: {price: true},
|
||||||
|
});
|
||||||
|
|
||||||
|
this.model.updateRecordContext(this.handle, {
|
||||||
|
need_notify: true,
|
||||||
|
modified: true,
|
||||||
|
});
|
||||||
|
this.trigger_up("block_card", {status: true});
|
||||||
|
this.trigger_up("modify_quick_record", {
|
||||||
|
id: this.handle,
|
||||||
|
});
|
||||||
|
|
||||||
|
var is_virtual = this.model.isPureVirtual(this.handle);
|
||||||
|
// If is a 'pure virtual' record, save it in the selected list
|
||||||
|
if (is_virtual) {
|
||||||
|
this.saveRecord(this.handle, {
|
||||||
|
stayInEdit: true,
|
||||||
|
reload: true,
|
||||||
|
savePoint: true,
|
||||||
|
viewType: "form",
|
||||||
|
}).then(function() {
|
||||||
|
def.resolve(true);
|
||||||
|
self.trigger_up("create_quick_record", {
|
||||||
|
id: self.handle,
|
||||||
|
on_onchange: function() {
|
||||||
|
self.trigger_up("block_card", {status: false});
|
||||||
|
self._enableQuickCreate();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
def.resolve(true);
|
||||||
|
// If is a "normal" record, update it
|
||||||
|
this.trigger_up("update_quick_record", {
|
||||||
|
id: this.handle,
|
||||||
|
on_onchange: function() {
|
||||||
|
self.trigger_up("block_card", {status: false});
|
||||||
|
self._enableQuickCreate();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return def;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @param {MouseEvent} ev
|
||||||
|
*/
|
||||||
|
_onClickDiscard: function(ev) {
|
||||||
|
var self = this;
|
||||||
|
ev.stopPropagation();
|
||||||
|
if (!this.model.isDirty(this.handle)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
this.model.discardChanges(this.handle, {
|
||||||
|
rollback: true,
|
||||||
|
});
|
||||||
|
this.model.updateRecordContext(this.handle, {
|
||||||
|
need_notify: false,
|
||||||
|
modified: false,
|
||||||
|
});
|
||||||
|
this.trigger_up("block_card", {status: true});
|
||||||
|
this.trigger_up("modify_quick_record", {
|
||||||
|
id: this.handle,
|
||||||
|
});
|
||||||
|
this.trigger_up("update_quick_record", {
|
||||||
|
id: this.handle,
|
||||||
|
on_onchange: function() {
|
||||||
|
self.trigger_up("block_card", {status: false});
|
||||||
|
self._enableQuickCreate();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_disableQuickCreate: function() {
|
||||||
|
// Ensures that the record won't be created twice
|
||||||
|
this.$el.addClass("o_disabled");
|
||||||
|
this.$("input:not(:disabled),button:not(:disabled)")
|
||||||
|
.addClass("o_temporarily_disabled")
|
||||||
|
.attr("disabled", "disabled");
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_enableQuickCreate: function() {
|
||||||
|
// Allows to create again
|
||||||
|
this.$el.removeClass("o_disabled");
|
||||||
|
this.$("input.o_temporarily_disabled,button.o_temporarily_disabled")
|
||||||
|
.removeClass("o_temporarily_disabled")
|
||||||
|
.attr("disabled", false);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const ProductPickerQuickModifPriceFormView = QuickCreateFormView.extend({
|
var ProductPickerQuickModifPriceFormView = QuickCreateFormView.extend({
|
||||||
config: _.extend({}, QuickCreateFormView.prototype.config, {
|
config: _.extend({}, QuickCreateFormView.prototype.config, {
|
||||||
Renderer: ProductPickerQuickModifPriceFormRenderer,
|
Renderer: ProductPickerQuickModifPriceFormRenderer,
|
||||||
Controller: ProductPickerQuickModifPriceFormController,
|
Controller: ProductPickerQuickModifPriceFormController,
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -5,15 +5,14 @@ odoo.define(
|
||||||
function(require) {
|
function(require) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const core = require("web.core");
|
var core = require("web.core");
|
||||||
const BasicRenderer = require("web.BasicRenderer");
|
var BasicRenderer = require("web.BasicRenderer");
|
||||||
const One2ManyProductPickerRecord = require("web_widget_one2many_product_picker.One2ManyProductPickerRecord");
|
var One2ManyProductPickerRecord = require("web_widget_one2many_product_picker.One2ManyProductPickerRecord");
|
||||||
const ProductPickerQuickCreateForm = require("web_widget_one2many_product_picker.ProductPickerQuickCreateForm");
|
|
||||||
|
|
||||||
const qweb = core.qweb;
|
var qweb = core.qweb;
|
||||||
|
|
||||||
/* This is the renderer of the main widget */
|
/* This is the renderer of the main widget */
|
||||||
const One2ManyProductPickerRenderer = BasicRenderer.extend({
|
var One2ManyProductPickerRenderer = BasicRenderer.extend({
|
||||||
className: "oe_one2many_product_picker_view",
|
className: "oe_one2many_product_picker_view",
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
|
@ -23,8 +22,7 @@ odoo.define(
|
||||||
record_flip: "_onRecordFlip",
|
record_flip: "_onRecordFlip",
|
||||||
},
|
},
|
||||||
|
|
||||||
DELAY_GET_RECORDS: 150,
|
_instant_search_onchange_delay: 250,
|
||||||
MIN_PERC_GET_RECORDS: 0.9,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
|
@ -59,15 +57,6 @@ odoo.define(
|
||||||
_.invoke(_.compact(this.widgets), "on_detach_callback");
|
_.invoke(_.compact(this.widgets), "on_detach_callback");
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Object} widget
|
|
||||||
*/
|
|
||||||
removeWidget: function(widget) {
|
|
||||||
const index = this.widgets.indexOf(widget);
|
|
||||||
widget.destroy();
|
|
||||||
delete this.widgets[index];
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
|
@ -95,18 +84,33 @@ odoo.define(
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
updateState: function(state, params) {
|
updateState: function(state, params) {
|
||||||
const force_update = params.force;
|
var self = this;
|
||||||
|
var force_update = params.force;
|
||||||
delete params.force;
|
delete params.force;
|
||||||
const sparams = _.extend({}, params, {noRender: true});
|
var sparams = _.extend({}, params, {noRender: true});
|
||||||
if (!force_update && _.isEqual(this.state.data, state.data)) {
|
if (!force_update && _.isEqual(this.state.data, state.data)) {
|
||||||
return this._super(state, sparams);
|
return this._super(state, sparams);
|
||||||
}
|
}
|
||||||
const old_state = _.clone(this.state.data);
|
var old_state = _.clone(this.state.data);
|
||||||
return this._super(state, sparams).then(() => {
|
return this._super(state, sparams).then(function() {
|
||||||
this._updateStateRecords(old_state);
|
self._updateStateRecords(old_state);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
canBeUpdated: function() {
|
||||||
|
var model = this.getParent().getBasicFieldParams().model;
|
||||||
|
for (var widget of this.widgets) {
|
||||||
|
if (!widget.state) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var record = model.localData[widget.state.id];
|
||||||
|
if (record.context.in_timeout) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Because this widget doesn't support comments/sections line types
|
* Because this widget doesn't support comments/sections line types
|
||||||
* we need check if the line is valid to be shown.
|
* we need check if the line is valid to be shown.
|
||||||
|
@ -117,53 +121,41 @@ odoo.define(
|
||||||
*/
|
*/
|
||||||
_isValidLineState: function(state) {
|
_isValidLineState: function(state) {
|
||||||
return (
|
return (
|
||||||
|
state &&
|
||||||
state.data[this.options.field_map.product] &&
|
state.data[this.options.field_map.product] &&
|
||||||
|
state.data[this.options.field_map.product].data &&
|
||||||
|
typeof state.data[this.options.field_map.product].data.id !==
|
||||||
|
"undefined"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
getProductIdFromState: function(state) {
|
||||||
|
return (
|
||||||
|
state &&
|
||||||
|
state.data[this.options.field_map.product] &&
|
||||||
|
state.data[this.options.field_map.product].data &&
|
||||||
state.data[this.options.field_map.product].data.id
|
state.data[this.options.field_map.product].data.id
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
getWidgetsByProduct: function(product_id) {
|
||||||
* @private
|
var self = this;
|
||||||
* @param {Object} state_a
|
return _.filter(this.widgets, function(item) {
|
||||||
* @param {Object} state_b
|
return (
|
||||||
* @returns {Boolean}
|
self.getProductIdFromState(item.state) === product_id ||
|
||||||
*/
|
item.recordSearch.id === product_id
|
||||||
_isEqualState: function(state_a, state_b) {
|
);
|
||||||
if (state_a.id === state_b.id) {
|
});
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const product_id_a =
|
|
||||||
state_a.data[this.options.field_map.product].data.id;
|
|
||||||
const product_uom_id_a =
|
|
||||||
state_a.data[this.options.field_map.product_uom].data.id;
|
|
||||||
const product_id_b =
|
|
||||||
state_b.data[this.options.field_map.product].data.id;
|
|
||||||
const product_uom_id_b =
|
|
||||||
state_b.data[this.options.field_map.product_uom].data.id;
|
|
||||||
|
|
||||||
return (
|
|
||||||
product_id_a === product_id_b &&
|
|
||||||
product_uom_id_a === product_uom_id_b
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
getWidgetsWithoutOnchange: function() {
|
||||||
* @private
|
var model = this.getParent().getBasicFieldParams().model;
|
||||||
* @param {Object} state
|
return _.filter(this.widgets, function(item) {
|
||||||
* @returns {Boolean}
|
return (
|
||||||
*/
|
model.localData[item.state.id] &&
|
||||||
_existsWidgetWithState: function(state) {
|
model.localData[item.state.id].context.not_onchange
|
||||||
for (let eb = this.widgets.length - 1; eb >= 0; --eb) {
|
);
|
||||||
const widget = this.widgets[eb];
|
});
|
||||||
if (
|
|
||||||
widget &&
|
|
||||||
widget.state &&
|
|
||||||
this._isEqualState(widget.state, state)
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -175,58 +167,62 @@ odoo.define(
|
||||||
* @param {Array} states
|
* @param {Array} states
|
||||||
* @returns {Array}
|
* @returns {Array}
|
||||||
*/
|
*/
|
||||||
_processStatesToDestroy: function(states) {
|
_processStatesToDestroy: function(old_states) {
|
||||||
// Get widgets to destroy
|
var self = this;
|
||||||
// Update states only affect to "non pure virtual" records
|
// States to remove
|
||||||
const to_destroy = [];
|
// In 12.0, Odoo generates new ids for the states, so
|
||||||
const to_add = [];
|
// all states will be removed and restored because it's
|
||||||
for (const state of states) {
|
// not possible identify a record without this id
|
||||||
for (let e = this.widgets.length - 1; e >= 0; --e) {
|
var to_destroy_ids = [];
|
||||||
const widget = this.widgets[e];
|
for (var index in old_states) {
|
||||||
if (widget && this._isEqualState(widget.state, state)) {
|
var old_state = old_states[index];
|
||||||
// If already exists a widget for the product don't try create a new one
|
if (!this._isValidLineState(old_state)) {
|
||||||
let recreated = false;
|
continue;
|
||||||
if (!this._existsWidgetWithState(widget.state)) {
|
}
|
||||||
// Get the new state ID if exists to link it with the new record
|
var in_current_state = _.some(this.state.data, function(state) {
|
||||||
// This happens when remove a record that have a new state info
|
return (
|
||||||
for (
|
self._isValidLineState(state) && state.id === old_state.id
|
||||||
let eb = this.state.data.length - 1;
|
);
|
||||||
eb >= 0;
|
});
|
||||||
--eb
|
if (!in_current_state) {
|
||||||
) {
|
to_destroy_ids.push(old_state.id);
|
||||||
const state = this.state.data[eb];
|
}
|
||||||
if (!this._isValidLineState(state)) {
|
}
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (this._isEqualState(state, widget.state)) {
|
|
||||||
widget.recreate(state);
|
|
||||||
recreated = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!recreated) {
|
|
||||||
widget.markToDestroy();
|
|
||||||
to_destroy.push(widget);
|
|
||||||
const search_record = _.omit(
|
|
||||||
widget.recordSearch,
|
|
||||||
"__id"
|
|
||||||
);
|
|
||||||
|
|
||||||
to_add.push([
|
var model = this.getParent().getBasicFieldParams().model;
|
||||||
[search_record],
|
var to_destroy = [];
|
||||||
{
|
for (var widget of this.widgets) {
|
||||||
no_attach_widgets: false,
|
if (!widget) {
|
||||||
no_process_records: false,
|
continue;
|
||||||
position: widget.state.id,
|
}
|
||||||
},
|
|
||||||
]);
|
// Verify that doesn't exists any dead widget
|
||||||
}
|
// This is necessary beceause auto-save uses
|
||||||
|
// ADD + SAVE that generates two different
|
||||||
|
// state ids
|
||||||
|
var state_has_onchange =
|
||||||
|
widget.state && !widget.state.context.not_onchange;
|
||||||
|
var state_has_modified =
|
||||||
|
widget.state && !widget.state.context.modified;
|
||||||
|
if (
|
||||||
|
!state_has_modified &&
|
||||||
|
state_has_onchange &&
|
||||||
|
!model.isPureVirtual(widget.state.id)
|
||||||
|
) {
|
||||||
|
to_destroy.push(widget);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var index_destroy in to_destroy_ids) {
|
||||||
|
const state_id = to_destroy_ids[index_destroy];
|
||||||
|
if (widget.state.id === state_id) {
|
||||||
|
to_destroy.push(widget);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [to_destroy, to_add];
|
return to_destroy;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -236,67 +232,71 @@ odoo.define(
|
||||||
* @private
|
* @private
|
||||||
* @returns {Array}
|
* @returns {Array}
|
||||||
*/
|
*/
|
||||||
_processCurrentStates: function() {
|
_processCurrentStates: function(old_states) {
|
||||||
|
var to_destroy = this._processStatesToDestroy(old_states);
|
||||||
// Records to Update or Create
|
// Records to Update or Create
|
||||||
const model = this.getParent().getBasicFieldParams().model;
|
var model = this.getParent().getBasicFieldParams().model;
|
||||||
const to_destroy = [];
|
var to_add = [];
|
||||||
const to_add = [];
|
for (var index in this.state.data) {
|
||||||
for (const index in this.state.data) {
|
var state = this.state.data[index];
|
||||||
const state = this.state.data[index];
|
|
||||||
if (!this._isValidLineState(state)) {
|
if (!this._isValidLineState(state)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let exists = false;
|
var exists = false;
|
||||||
let search_record_index = false;
|
var search_record_index = false;
|
||||||
let search_record = false;
|
var search_record = false;
|
||||||
for (let e = this.widgets.length - 1; e >= 0; --e) {
|
for (var widget of this.widgets) {
|
||||||
const widget = this.widgets[e];
|
if (!widget) {
|
||||||
if (!widget || !widget.state) {
|
|
||||||
// Already processed widget (deleted)
|
// Already processed widget (deleted)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const is_equal_state = this._isEqualState(widget.state, state);
|
var record = model.get(widget.state.id);
|
||||||
if (widget.isMarkedToDestroy()) {
|
// Re-use widgets is possible
|
||||||
exists = true;
|
var is_to_destroy = _.findIndex(to_destroy, widget) >= 0;
|
||||||
} else if (is_equal_state) {
|
var is_widget_usable =
|
||||||
const record = model.get(widget.state.id);
|
widget.state.id === state.id ||
|
||||||
model.updateRecordContext(state.id, {
|
widget.recordSearch.id ===
|
||||||
lazy_qty: record.context.lazy_qty || 0,
|
state.data[this.options.field_map.product].data.id;
|
||||||
});
|
if (is_widget_usable) {
|
||||||
|
if (is_to_destroy) {
|
||||||
|
to_destroy = _.without(to_destroy, widget);
|
||||||
|
}
|
||||||
|
if (record) {
|
||||||
|
model.updateRecordContext(state.id, {
|
||||||
|
lazy_qty: record.context.lazy_qty || 0,
|
||||||
|
saving: record.context.saving || false,
|
||||||
|
need_notify: record.context.need_notify || false,
|
||||||
|
need_save: record.context.need_save || false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Ensure use the updated state
|
||||||
widget.recreate(state);
|
widget.recreate(state);
|
||||||
exists = true;
|
exists = true;
|
||||||
break;
|
break;
|
||||||
}
|
} else if (
|
||||||
if (
|
widget.state &&
|
||||||
!is_equal_state &&
|
!model.isPureVirtual(widget.state.id) &&
|
||||||
widget.recordSearch.id ===
|
widget.recordSearch.id ===
|
||||||
state.data[this.options.field_map.product].data.id
|
state.data[this.options.field_map.product].data.id
|
||||||
) {
|
) {
|
||||||
// Is a new record (can be other record for the same 'search record' or a replacement for a pure virtual)
|
|
||||||
search_record_index = widget.state.id;
|
|
||||||
search_record = widget.recordSearch;
|
search_record = widget.recordSearch;
|
||||||
const record = model.get(widget.state.id);
|
const in_search_records = _.some(
|
||||||
model.updateRecordContext(state.id, {
|
this.search_records,
|
||||||
lazy_qty: record.context.lazy_qty || 0,
|
function(item) {
|
||||||
});
|
return item.id === search_record.id;
|
||||||
}
|
}
|
||||||
|
);
|
||||||
// Remove "pure virtual" records that have the same product that the new record
|
if (in_search_records) {
|
||||||
if (
|
// Is a new record (can be other record for the same 'search record')
|
||||||
widget.is_virtual &&
|
search_record_index = widget.state.id;
|
||||||
this._isEqualState(widget.state, state)
|
}
|
||||||
) {
|
|
||||||
to_destroy.push(widget);
|
|
||||||
delete this.widgets[e];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state.data = _.compact(this.state.data);
|
|
||||||
|
|
||||||
// Add to create the new record
|
// Add to create the new record
|
||||||
if (!exists && search_record_index) {
|
if (!exists && search_record_index) {
|
||||||
const new_search_record = _.extend({}, search_record, {
|
var new_search_record = _.extend({}, search_record, {
|
||||||
__id: state.id,
|
__id: state.id,
|
||||||
});
|
});
|
||||||
to_add.push([
|
to_add.push([
|
||||||
|
@ -309,68 +309,121 @@ odoo.define(
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return [to_add.reverse(), to_destroy];
|
||||||
|
},
|
||||||
|
|
||||||
return [to_destroy, to_add];
|
/**
|
||||||
|
* This method checks and appends the missing
|
||||||
|
* 'pure virtual' records
|
||||||
|
*
|
||||||
|
* @returns {Deferred}
|
||||||
|
*/
|
||||||
|
checkVirtualRecords: function() {
|
||||||
|
if (this.search_group.name === "main_lines") {
|
||||||
|
return $.when();
|
||||||
|
}
|
||||||
|
var tasks = [];
|
||||||
|
var to_add = this._processVirtualRecords();
|
||||||
|
for (var params of to_add) {
|
||||||
|
tasks.push(this.appendSearchRecords.apply(this, params)[0]);
|
||||||
|
}
|
||||||
|
return $.when(tasks);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method checks the current widgets to generate the
|
||||||
|
* missing 'pure virtual' record objects.
|
||||||
|
*
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
_processVirtualRecords: function() {
|
||||||
|
var model = this.getParent().getBasicFieldParams().model;
|
||||||
|
var products_done = [];
|
||||||
|
var to_add = [];
|
||||||
|
for (var search_record of this.search_records) {
|
||||||
|
var widgets = this.getWidgetsByProduct(search_record.id);
|
||||||
|
if (_.isEmpty(widgets)) {
|
||||||
|
to_add.push([
|
||||||
|
[_.omit(search_record, "__id")],
|
||||||
|
{
|
||||||
|
no_attach_widgets: true,
|
||||||
|
no_process_records: true,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only add 'pure virtual' records if don't have a line
|
||||||
|
var existing_widgets = _.filter(widgets, function(widget) {
|
||||||
|
return !widget.isMarkedToDestroy();
|
||||||
|
});
|
||||||
|
var need_virtual = !_.some(existing_widgets, function(widget) {
|
||||||
|
return widget.state && !model.isPureVirtual(widget.state.id);
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
need_virtual &&
|
||||||
|
products_done.indexOf(search_record.id) === -1
|
||||||
|
) {
|
||||||
|
var has_virtual = _.some(existing_widgets, function(widget) {
|
||||||
|
return (
|
||||||
|
!widget.state ||
|
||||||
|
(widget.state && model.isPureVirtual(widget.state.id))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if (!has_virtual) {
|
||||||
|
var search_record_index = _.max(widgets, function(widget) {
|
||||||
|
return widget.$el.index();
|
||||||
|
}).state.id;
|
||||||
|
to_add.push([
|
||||||
|
[_.omit(search_record, "__id")],
|
||||||
|
{
|
||||||
|
no_attach_widgets: true,
|
||||||
|
no_process_records: true,
|
||||||
|
position: search_record_index,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
products_done.push(search_record.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return to_add;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When the state change this method tries to update current records, delete
|
* When the state change this method tries to update current records, delete
|
||||||
* or update them.
|
* or update them.
|
||||||
* Thanks to this we don't need re-render 'pure virtual' records.
|
* Thanks to this we don't need re-render 'pure virtual' records.
|
||||||
|
* NOTE: The first load of the records don't trigger this method.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {Object} old_states
|
* @param {Object} old_states
|
||||||
* @returns {Deferred}
|
* @returns {Deferred}
|
||||||
*/
|
*/
|
||||||
_updateStateRecords: function(old_states) {
|
_updateStateRecords: function(old_states) {
|
||||||
// States to remove
|
var self = this;
|
||||||
const states_to_destroy = [];
|
var record_defs = this._processCurrentStates(old_states);
|
||||||
for (const index in old_states) {
|
var to_add_current = record_defs[0];
|
||||||
const old_state = old_states[index];
|
var to_destroy = record_defs[1];
|
||||||
if (!this._isValidLineState(old_state)) {
|
_.invoke(to_destroy, "markToDestroy");
|
||||||
continue;
|
var currentTasks = [];
|
||||||
}
|
for (var params of to_add_current) {
|
||||||
let found = false;
|
|
||||||
for (const e in this.state.data) {
|
|
||||||
const current_state = this.state.data[e];
|
|
||||||
if (!this._isValidLineState(current_state)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (this._isEqualState(current_state, old_state)) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
states_to_destroy.push(old_state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const def = $.Deferred();
|
|
||||||
this.state.data = _.compact(this.state.data);
|
|
||||||
const [to_destroy_old, to_add_virtual] = this._processStatesToDestroy(
|
|
||||||
states_to_destroy
|
|
||||||
);
|
|
||||||
|
|
||||||
const [
|
|
||||||
destroyed_current,
|
|
||||||
to_add_current,
|
|
||||||
] = this._processCurrentStates();
|
|
||||||
|
|
||||||
const currentTasks = [];
|
|
||||||
const to_add = [].concat(to_add_current, to_add_virtual);
|
|
||||||
for (const params of to_add) {
|
|
||||||
currentTasks.push(this.appendSearchRecords.apply(this, params)[0]);
|
currentTasks.push(this.appendSearchRecords.apply(this, params)[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Promise.all(currentTasks).then(() => {
|
return $.when(currentTasks)
|
||||||
_.invoke(to_destroy_old, "destroy");
|
.then(function() {
|
||||||
_.invoke(destroyed_current, "destroy");
|
return self.checkVirtualRecords();
|
||||||
this.widgets = _.difference(this.widgets, to_destroy_old);
|
})
|
||||||
def.resolve();
|
.then(function() {
|
||||||
});
|
var widgets_to_destroy = _.filter(self.widgets, function(
|
||||||
|
widget
|
||||||
return def;
|
) {
|
||||||
|
return widget.isMarkedToDestroy();
|
||||||
|
});
|
||||||
|
self.widgets = _.difference(self.widgets, widgets_to_destroy);
|
||||||
|
_.invoke(widgets_to_destroy, "destroy");
|
||||||
|
return true;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
clearRecords: function() {
|
clearRecords: function() {
|
||||||
|
@ -413,12 +466,12 @@ odoo.define(
|
||||||
*/
|
*/
|
||||||
_sort_search_data: function(datas) {
|
_sort_search_data: function(datas) {
|
||||||
if (this.search_group.name === "main_lines") {
|
if (this.search_group.name === "main_lines") {
|
||||||
const field_name = this.options.field_map.product;
|
var field_name = this.options.field_map.product;
|
||||||
for (const index_datas in datas) {
|
for (var index_datas in datas) {
|
||||||
const data = datas[index_datas];
|
var data = datas[index_datas];
|
||||||
|
|
||||||
for (const index_state in this.state.data) {
|
for (var index_state in this.state.data) {
|
||||||
const state_data = this.state.data[index_state];
|
var state_data = this.state.data[index_state];
|
||||||
if (
|
if (
|
||||||
this._isValidLineState(state_data) &&
|
this._isValidLineState(state_data) &&
|
||||||
state_data.data[field_name].res_id === data.id
|
state_data.data[field_name].res_id === data.id
|
||||||
|
@ -427,9 +480,11 @@ odoo.define(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const sorted_datas = _.chain(datas)
|
var sorted_datas = _.chain(datas)
|
||||||
.sortBy("_order_value")
|
.sortBy("_order_value")
|
||||||
.map(item => _.omit(item, "_order_value"))
|
.map(function(item) {
|
||||||
|
return _.omit(item, "_order_value");
|
||||||
|
})
|
||||||
.value()
|
.value()
|
||||||
.reverse();
|
.reverse();
|
||||||
return sorted_datas;
|
return sorted_datas;
|
||||||
|
@ -446,9 +501,10 @@ odoo.define(
|
||||||
* @returns {Array}
|
* @returns {Array}
|
||||||
*/
|
*/
|
||||||
_processSearchRecords: function(results) {
|
_processSearchRecords: function(results) {
|
||||||
const field_name = this.options.field_map.product;
|
var model = this.getParent().getBasicFieldParams().model;
|
||||||
const records = [];
|
var field_name = this.options.field_map.product;
|
||||||
const states = [];
|
var records = [];
|
||||||
|
var states = [];
|
||||||
|
|
||||||
var test_values = function(field_value, record_search) {
|
var test_values = function(field_value, record_search) {
|
||||||
return (
|
return (
|
||||||
|
@ -458,59 +514,62 @@ odoo.define(
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const index in results) {
|
for (var index in results) {
|
||||||
const record_search = results[index];
|
var record_search = results[index];
|
||||||
let state_data_found = false;
|
|
||||||
|
|
||||||
// Analyze 'pure virtual' records
|
var widget_created = false;
|
||||||
// Pure virtual records aren't linked with field list
|
|
||||||
// so we need search them linked in the widgets.
|
for (var index_data in this.state.data) {
|
||||||
for (const index_widget in this.widgets) {
|
var state_record = this.state.data[index_data];
|
||||||
const widget = this.widgets[index_widget];
|
var field_value = state_record.data[field_name];
|
||||||
if (widget.isMarkedToDestroy()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (
|
if (
|
||||||
record_search.__id === widget.state.id ||
|
!this._isValidLineState(state_record) ||
|
||||||
(!record_search.__id &&
|
!test_values(field_value, record_search)
|
||||||
widget.recordSearch.id === record_search.id)
|
|
||||||
) {
|
) {
|
||||||
state_data_found = true;
|
|
||||||
if (widget.state) {
|
|
||||||
states.push(widget.state);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If already exists a widget with the search result
|
|
||||||
// avoid create a new one
|
|
||||||
if (state_data_found) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Analyze field records
|
|
||||||
// If not found any widget we need create a new one
|
|
||||||
// linked with the state record
|
|
||||||
for (const index_data in this.state.data) {
|
|
||||||
const state_record = this.state.data[index_data];
|
|
||||||
if (!this._isValidLineState(state_record)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const field_value = state_record.data[field_name];
|
widget_created = true;
|
||||||
if (test_values(field_value, record_search)) {
|
// At this point the result has a state (line)
|
||||||
|
// Search if already exists a widget using the state
|
||||||
|
var widget = _.find(this.widgets, function(widget) {
|
||||||
|
return (
|
||||||
|
!widget.isMarkedToDestroy() &&
|
||||||
|
widget.state &&
|
||||||
|
widget.state.id === state_record.id
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if (widget) {
|
||||||
|
// Don't need create a new widget (record)
|
||||||
|
states.push(widget.state);
|
||||||
|
} else {
|
||||||
|
// Need create a new widget linked with the state
|
||||||
records.push(
|
records.push(
|
||||||
_.extend({}, record_search, {
|
_.extend({}, record_search, {
|
||||||
__id: state_record.id,
|
__id: state_record.id,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
states.push(state_record);
|
states.push(state_record);
|
||||||
state_data_found = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (widget_created) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!state_data_found) {
|
var widgets = this.getWidgetsByProduct(record_search.id);
|
||||||
records.push(record_search);
|
// Only can exists 'pure virtual' if no 'lines' assigned
|
||||||
|
if (_.isEmpty(widgets)) {
|
||||||
|
var has_virtual = _.some(widgets, function(widget) {
|
||||||
|
return (
|
||||||
|
!widget.isMarkedToDestroy() &&
|
||||||
|
(!widget.state ||
|
||||||
|
(widget.state &&
|
||||||
|
model.isPureVirtual(widget.state.id)))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if (!has_virtual) {
|
||||||
|
// The result need a 'pure virtual' record
|
||||||
|
records.push(_.omit(record_search, "__id"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -526,8 +585,8 @@ odoo.define(
|
||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
_getRecordDataById: function(id) {
|
_getRecordDataById: function(id) {
|
||||||
for (const index in this.state.data) {
|
for (var index in this.state.data) {
|
||||||
const record = this.state.data[index];
|
var record = this.state.data[index];
|
||||||
if (record.id === id) {
|
if (record.id === id) {
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
|
@ -561,73 +620,55 @@ odoo.define(
|
||||||
* @private
|
* @private
|
||||||
* @param {Array} search_records
|
* @param {Array} search_records
|
||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
|
* @returns {Array}
|
||||||
*/
|
*/
|
||||||
_appendSearchRecords: function(search_records, options) {
|
_appendSearchRecords: function(search_records, options) {
|
||||||
const processed_info = options.no_process_records
|
var self = this;
|
||||||
? search_records
|
var processed_info =
|
||||||
: this._processSearchRecords(search_records);
|
!options.no_process_records &&
|
||||||
const records_to_add = processed_info.records || search_records;
|
this._processSearchRecords(search_records);
|
||||||
_.each(records_to_add, search_record => {
|
var records_to_add =
|
||||||
const state_data = this._getRecordDataById(search_record.__id);
|
(processed_info && processed_info.records) || search_records;
|
||||||
const widget_options = this._getRecordOptions(search_record);
|
_.each(records_to_add, function(search_record) {
|
||||||
widget_options.renderer_widget_index = this.widgets.length;
|
// Get record state (if can)
|
||||||
const ProductPickerRecord = new One2ManyProductPickerRecord(
|
var state_data = self._getRecordDataById(search_record.__id);
|
||||||
this,
|
var widget_options = self._getRecordOptions(search_record);
|
||||||
|
widget_options.renderer_widget_index = self.widgets.length;
|
||||||
|
var ProductPickerRecord = new One2ManyProductPickerRecord(
|
||||||
|
self,
|
||||||
state_data,
|
state_data,
|
||||||
widget_options
|
widget_options
|
||||||
);
|
);
|
||||||
this.widgets.push(ProductPickerRecord);
|
self.widgets.push(ProductPickerRecord);
|
||||||
|
|
||||||
// Simulate new lines to dispatch get_default & onchange's to get the
|
// Simulate new lines to dispatch get_default & onchange's to get the
|
||||||
// relevant data to print. This case increase the TTI time.
|
// relevant data to print. This case increase the TTI time.
|
||||||
if (!state_data) {
|
if (!state_data) {
|
||||||
const defVirtualState = ProductPickerRecord.generateVirtualState(
|
var defVirtualState = ProductPickerRecord.generateVirtualState({
|
||||||
this.options.instant_search
|
onchange_delay: self.options.instant_search
|
||||||
);
|
? self._instant_search_onchange_delay
|
||||||
this.defsVirtualState.push(defVirtualState);
|
: 0,
|
||||||
|
});
|
||||||
|
self.defsVirtualState.push(defVirtualState);
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point the widget will use the existing state (line) or
|
// At this point the widget will use the existing state (line) or
|
||||||
// a simple state data. Using simple state data instead of waiting for
|
// a simple state data. Using simple state data instead of waiting for
|
||||||
// complete state (default + onchange) gives a low FCP time.
|
// complete state (default + onchange) gives a low FCP time.
|
||||||
const def = $.Deferred();
|
var def = ProductPickerRecord.appendTo(self.$recordsContainer).then(
|
||||||
ProductPickerRecord.appendTo(this.$recordsContainer).then(
|
|
||||||
function(widget, widget_position) {
|
function(widget, widget_position) {
|
||||||
if (typeof widget_position !== "undefined") {
|
if (typeof widget_position !== "undefined") {
|
||||||
const $elm = this.$el.find(
|
var $elm = self.$el.find(
|
||||||
`[data-card-id="${widget_position}"]:first`
|
`[data-card-id="${widget_position}"]:first`
|
||||||
);
|
);
|
||||||
widget.$el.insertBefore($elm);
|
widget.$el.insertAfter($elm);
|
||||||
}
|
}
|
||||||
def.resolve();
|
}.bind(self, ProductPickerRecord, options.position)
|
||||||
}.bind(this, ProductPickerRecord, options.position)
|
|
||||||
);
|
);
|
||||||
this.defs.push(def);
|
self.defs.push(def);
|
||||||
});
|
});
|
||||||
// Destroy unused
|
|
||||||
if (options.cleanup) {
|
return records_to_add;
|
||||||
const num_widgets = this.widgets.length;
|
|
||||||
for (
|
|
||||||
let index_widget = num_widgets - 1;
|
|
||||||
index_widget >= 0;
|
|
||||||
--index_widget
|
|
||||||
) {
|
|
||||||
const widget = this.widgets[index_widget];
|
|
||||||
let found_state = false;
|
|
||||||
for (const state of processed_info.states) {
|
|
||||||
if (widget.state && widget.state.id === state.id) {
|
|
||||||
found_state = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found_state && widget.state) {
|
|
||||||
widget.destroy();
|
|
||||||
delete this.widgets[index_widget];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Clean widget array
|
|
||||||
this.widgets = _.compact(this.widgets);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -645,25 +686,80 @@ odoo.define(
|
||||||
* @returns {Array}
|
* @returns {Array}
|
||||||
*/
|
*/
|
||||||
appendSearchRecords: function(search_records, options = {}) {
|
appendSearchRecords: function(search_records, options = {}) {
|
||||||
|
var self = this;
|
||||||
|
if (options.clear) {
|
||||||
|
this.clearRecords();
|
||||||
|
}
|
||||||
this.trigger_up("loading_records");
|
this.trigger_up("loading_records");
|
||||||
this.defs = [];
|
this.defs = [];
|
||||||
this.defsVirtualState = [];
|
this.defsVirtualState = [];
|
||||||
const cur_widget_index = this.widgets.length;
|
var cur_widget_index = this.widgets.length;
|
||||||
this._appendSearchRecords(search_records, options);
|
this._appendSearchRecords(search_records, options);
|
||||||
|
|
||||||
const defs = this.defs;
|
var defs = this.defs;
|
||||||
delete this.defs;
|
delete this.defs;
|
||||||
const defsVirtualState = this.defsVirtualState;
|
var defsVirtualState = this.defsVirtualState;
|
||||||
delete this.defsVirtualState;
|
delete this.defsVirtualState;
|
||||||
return [
|
return [
|
||||||
Promise.all(defs).then(() => {
|
$.when(defs).then(function() {
|
||||||
if (!options.no_attach_widgets && this._isInDom) {
|
if (!options.no_attach_widgets && self._isInDom) {
|
||||||
const new_widgets = this.widgets.slice(cur_widget_index);
|
var new_widgets = self.widgets.slice(cur_widget_index);
|
||||||
_.invoke(new_widgets, "on_attach_callback");
|
_.invoke(new_widgets, "on_attach_callback");
|
||||||
}
|
}
|
||||||
|
// Destroy unused
|
||||||
|
if (options.cleanup) {
|
||||||
|
self.search_records = _.compact(search_records);
|
||||||
|
var widgets_to_destroy = _.filter(self.widgets, function(
|
||||||
|
widget
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
widget.isMarkedToDestroy() ||
|
||||||
|
!_.some(self.search_records, function(
|
||||||
|
search_record
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
search_record.id ===
|
||||||
|
widget.recordSearch.id &&
|
||||||
|
!_.some(self.widgets, function(
|
||||||
|
comp_widget
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
comp_widget !== widget &&
|
||||||
|
comp_widget.state &&
|
||||||
|
comp_widget.recordSearch.id ===
|
||||||
|
widget.recordSearch.id
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
_.invoke(widgets_to_destroy, "destroy");
|
||||||
|
self.widgets = _.difference(
|
||||||
|
self.widgets,
|
||||||
|
widgets_to_destroy
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
self.search_records = self.search_records || [];
|
||||||
|
for (var search_record of search_records) {
|
||||||
|
var has_search_record = _.some(
|
||||||
|
self.search_records,
|
||||||
|
function(item) {
|
||||||
|
return (
|
||||||
|
item.id === search_record.id &&
|
||||||
|
item.__id === search_record.__id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (!has_search_record) {
|
||||||
|
self.search_records.push(search_record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}),
|
}),
|
||||||
Promise.all(defsVirtualState).then(() => {
|
$.when(defsVirtualState).then(function() {
|
||||||
this.trigger_up("loading_records", {finished: true});
|
self.trigger_up("loading_records", {finished: true});
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
@ -682,8 +778,8 @@ odoo.define(
|
||||||
* @param {Integer} index
|
* @param {Integer} index
|
||||||
*/
|
*/
|
||||||
doWidgetFlip: function(index) {
|
doWidgetFlip: function(index) {
|
||||||
const widget = this.widgets[index];
|
var widget = this.widgets[index];
|
||||||
const $actived_card = this.$el.find(".active");
|
var $actived_card = this.$el.find(".active");
|
||||||
if (widget.$card.hasClass("active")) {
|
if (widget.$card.hasClass("active")) {
|
||||||
widget.$card.removeClass("active");
|
widget.$card.removeClass("active");
|
||||||
widget.$card.find(".oe_flip_card_front").removeClass("d-none");
|
widget.$card.find(".oe_flip_card_front").removeClass("d-none");
|
||||||
|
@ -692,11 +788,11 @@ odoo.define(
|
||||||
widget._processWidgetFields(widget.$back);
|
widget._processWidgetFields(widget.$back);
|
||||||
widget._processWidgets(widget.$back);
|
widget._processWidgets(widget.$back);
|
||||||
widget._processDynamicFields();
|
widget._processDynamicFields();
|
||||||
$.when(widget.defs).then(() => {
|
$.when(widget.defs).then(function() {
|
||||||
$actived_card.removeClass("active");
|
$actived_card.removeClass("active");
|
||||||
$actived_card.find(".oe_flip_card_front").removeClass("d-none");
|
$actived_card.find(".oe_flip_card_front").removeClass("d-none");
|
||||||
widget.$card.addClass("active");
|
widget.$card.addClass("active");
|
||||||
setTimeout(() => {
|
setTimeout(function() {
|
||||||
widget.$(".oe_flip_card_front").addClass("d-none");
|
widget.$(".oe_flip_card_front").addClass("d-none");
|
||||||
}, 200);
|
}, 200);
|
||||||
});
|
});
|
||||||
|
@ -711,17 +807,25 @@ odoo.define(
|
||||||
* @param {CustomEvent} evt
|
* @param {CustomEvent} evt
|
||||||
*/
|
*/
|
||||||
_onRecordFlip: function(evt) {
|
_onRecordFlip: function(evt) {
|
||||||
const prev_widget_index = evt.data.prev_widget_index;
|
var prev_widget_index = evt.data.prev_widget_index;
|
||||||
if (typeof prev_widget_index !== "undefined") {
|
if (
|
||||||
|
typeof prev_widget_index !== "undefined" &&
|
||||||
|
this.widgets[prev_widget_index]
|
||||||
|
) {
|
||||||
// Only check 'back' widgets so there is where the form was created
|
// Only check 'back' widgets so there is where the form was created
|
||||||
for (const index in this.widgets[prev_widget_index].widgets.back) {
|
for (var index in this.widgets[prev_widget_index].widgets.back) {
|
||||||
const widget = this.widgets[prev_widget_index].widgets.back[
|
var widget = this.widgets[prev_widget_index].widgets.back[
|
||||||
index
|
index
|
||||||
];
|
];
|
||||||
if (widget instanceof ProductPickerQuickCreateForm) {
|
if (
|
||||||
|
widget.controller &&
|
||||||
|
widget.className ===
|
||||||
|
"oe_one2many_product_picker_quick_create"
|
||||||
|
) {
|
||||||
widget.controller.auto();
|
widget.controller.auto();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.widgets[prev_widget_index].recreate();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
// Copyright 2021 Tecnativa - Alexandre Díaz
|
|
||||||
// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
||||||
odoo.define("web_widget_one2many_product_picker.BasicController", function(require) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const BasicController = require("web.BasicController");
|
|
||||||
|
|
||||||
BasicController.include({
|
|
||||||
/**
|
|
||||||
* This is necessary to refresh 'one2many_product_picker' when some 'trigger_refresh_fields' fields changes.
|
|
||||||
*
|
|
||||||
* @override
|
|
||||||
*/
|
|
||||||
_confirmChange: function(id, fields, e) {
|
|
||||||
id = id || this.handle;
|
|
||||||
return this._super.apply(this, arguments).then(() => {
|
|
||||||
if (this.renderer && !_.isEmpty(this.renderer.allFieldWidgets)) {
|
|
||||||
const product_picker_widgets = _.filter(
|
|
||||||
this.renderer.allFieldWidgets[id],
|
|
||||||
item => item.attrs.widget === "one2many_product_picker"
|
|
||||||
);
|
|
||||||
_.invoke(
|
|
||||||
product_picker_widgets,
|
|
||||||
"onDocumentConfirmChanges",
|
|
||||||
fields,
|
|
||||||
e
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -3,7 +3,8 @@
|
||||||
odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) {
|
odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const BasicModel = require("web.BasicModel");
|
var BasicModel = require("web.BasicModel");
|
||||||
|
var FieldOne2ManyProductPicker = require("web_widget_one2many_product_picker.FieldOne2ManyProductPicker");
|
||||||
|
|
||||||
BasicModel.include({
|
BasicModel.include({
|
||||||
/**
|
/**
|
||||||
|
@ -36,12 +37,21 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {String} id
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
isSaving: function(id) {
|
||||||
|
var data = this.localData[id];
|
||||||
|
return data._virtual || false;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {String} id
|
* @param {String} id
|
||||||
* @returns {Boolean}
|
* @returns {Boolean}
|
||||||
*/
|
*/
|
||||||
isPureVirtual: function(id) {
|
isPureVirtual: function(id) {
|
||||||
const data = this.localData[id];
|
var data = this.localData[id];
|
||||||
return data._virtual || false;
|
return data._virtual || false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -50,7 +60,7 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) {
|
||||||
* @param {Boolean} status
|
* @param {Boolean} status
|
||||||
*/
|
*/
|
||||||
setPureVirtual: function(id, status) {
|
setPureVirtual: function(id, status) {
|
||||||
const data = this.localData[id];
|
var data = this.localData[id];
|
||||||
if (status) {
|
if (status) {
|
||||||
data._virtual = true;
|
data._virtual = true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -62,9 +72,9 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) {
|
||||||
* @param {String} id
|
* @param {String} id
|
||||||
*/
|
*/
|
||||||
unsetDirty: function(id) {
|
unsetDirty: function(id) {
|
||||||
const data = this.localData[id];
|
var data = this.localData[id];
|
||||||
data._isDirty = false;
|
data._isDirty = false;
|
||||||
this._visitChildren(data, r => {
|
this._visitChildren(data, function(r) {
|
||||||
r._isDirty = false;
|
r._isDirty = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -81,14 +91,14 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = this.localData[id];
|
var data = this.localData[id];
|
||||||
const to_remove = [];
|
var to_remove = [];
|
||||||
this._visitChildren(data, item => {
|
this._visitChildren(data, function(item) {
|
||||||
to_remove.push(item.id);
|
to_remove.push(item.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
to_remove.reverse();
|
to_remove.reverse();
|
||||||
for (const remove_id of to_remove) {
|
for (var remove_id of to_remove) {
|
||||||
this.removeLine(remove_id);
|
this.removeLine(remove_id);
|
||||||
delete this.localData[remove_id];
|
delete this.localData[remove_id];
|
||||||
}
|
}
|
||||||
|
@ -103,7 +113,7 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) {
|
||||||
*
|
*
|
||||||
* @param {Object} record
|
* @param {Object} record
|
||||||
* @param {Object} params
|
* @param {Object} params
|
||||||
* @returns {Promise}
|
* @returns {Deferred}
|
||||||
*/
|
*/
|
||||||
_makeDefaultRecordNoDatapoint: function(record, params) {
|
_makeDefaultRecordNoDatapoint: function(record, params) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -128,15 +138,20 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._rpc({
|
return this._rpc(
|
||||||
model: record.model,
|
{
|
||||||
method: "default_get",
|
model: record.model,
|
||||||
args: [fields_key],
|
method: "default_get",
|
||||||
context: params.context,
|
args: [fields_key],
|
||||||
}).then(function(result) {
|
context: params.context,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
shadow: true,
|
||||||
|
}
|
||||||
|
).then(function(result) {
|
||||||
// Interrupt point (used in instant search)
|
// Interrupt point (used in instant search)
|
||||||
if (!self.exists(record.id)) {
|
if (!self.exists(record.id)) {
|
||||||
return Promise.reject();
|
return $.Deferred().reject();
|
||||||
}
|
}
|
||||||
|
|
||||||
// We want to overwrite the default value of the handle field (if any),
|
// We want to overwrite the default value of the handle field (if any),
|
||||||
|
@ -157,44 +172,43 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) {
|
||||||
.applyDefaultValues(record.id, result, {fieldNames: fieldNames})
|
.applyDefaultValues(record.id, result, {fieldNames: fieldNames})
|
||||||
.then(function() {
|
.then(function() {
|
||||||
if (!self.exists(record.id)) {
|
if (!self.exists(record.id)) {
|
||||||
return Promise.reject();
|
return $.Deferred().reject();
|
||||||
}
|
}
|
||||||
var def = new Promise(function(resolve, reject) {
|
var def = $.Deferred();
|
||||||
// Interrupt point (used in instant search)
|
// Interrupt point (used in instant search)
|
||||||
if (!self.exists(record.id)) {
|
if (!self.exists(record.id)) {
|
||||||
return Promise.reject();
|
return $.Deferred().reject();
|
||||||
}
|
}
|
||||||
var always = function() {
|
var always = function() {
|
||||||
if (record._warning) {
|
if (record._warning) {
|
||||||
if (params.allowWarning) {
|
if (params.allowWarning) {
|
||||||
delete record._warning;
|
delete record._warning;
|
||||||
} else {
|
} else {
|
||||||
reject();
|
def.reject();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
resolve();
|
}
|
||||||
};
|
def.resolve();
|
||||||
self._performOnChange(record, fields_key)
|
};
|
||||||
.then(always)
|
self._performOnChange(record, fields_key)
|
||||||
.guardedCatch(always);
|
.then(always)
|
||||||
});
|
.guardedCatch(always);
|
||||||
return def;
|
return def;
|
||||||
})
|
})
|
||||||
.then(function() {
|
.then(function() {
|
||||||
if (!self.exists(record.id)) {
|
if (!self.exists(record.id)) {
|
||||||
return Promise.reject();
|
return $.Deferred().reject();
|
||||||
}
|
}
|
||||||
return self._fetchRelationalData(record);
|
return self._fetchRelationalData(record);
|
||||||
})
|
})
|
||||||
.then(function() {
|
.then(function() {
|
||||||
if (!self.exists(record.id)) {
|
if (!self.exists(record.id)) {
|
||||||
return Promise.reject();
|
return $.Deferred().reject();
|
||||||
}
|
}
|
||||||
return self._postprocess(record);
|
return self._postprocess(record);
|
||||||
})
|
})
|
||||||
.then(function() {
|
.then(function() {
|
||||||
if (!self.exists(record.id)) {
|
if (!self.exists(record.id)) {
|
||||||
return Promise.reject();
|
return $.Deferred().reject();
|
||||||
}
|
}
|
||||||
// Save initial changes, so they can be restored later,
|
// Save initial changes, so they can be restored later,
|
||||||
// if we need to discard.
|
// if we need to discard.
|
||||||
|
@ -215,11 +229,11 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) {
|
||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
createVirtualDatapoint: function(listID, options) {
|
createVirtualDatapoint: function(listID, options) {
|
||||||
const list = this.localData[listID];
|
var list = this.localData[listID];
|
||||||
const context = _.extend({}, this._getContext(list), options.context);
|
var context = _.extend({}, this._getContext(list), options.context);
|
||||||
|
|
||||||
const position = options ? options.position : "top";
|
var position = options ? options.position : "top";
|
||||||
const params = {
|
var params = {
|
||||||
context: context,
|
context: context,
|
||||||
fields: list.fields,
|
fields: list.fields,
|
||||||
fieldsInfo: list.fieldsInfo,
|
fieldsInfo: list.fieldsInfo,
|
||||||
|
@ -228,6 +242,7 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) {
|
||||||
viewType: list.viewType,
|
viewType: list.viewType,
|
||||||
allowWarning: true,
|
allowWarning: true,
|
||||||
doNotSetDirty: true,
|
doNotSetDirty: true,
|
||||||
|
postonchange_values: options.onchange_values,
|
||||||
};
|
};
|
||||||
|
|
||||||
var targetView = params.viewType;
|
var targetView = params.viewType;
|
||||||
|
@ -251,7 +266,7 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) {
|
||||||
fields = _.defaults({}, fields, parentRecord.fields);
|
fields = _.defaults({}, fields, parentRecord.fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
const record = this._makeDataPoint({
|
var record = this._makeDataPoint({
|
||||||
modelName: list.model,
|
modelName: list.model,
|
||||||
fields: fields,
|
fields: fields,
|
||||||
fieldsInfo: fieldsInfo,
|
fieldsInfo: fieldsInfo,
|
||||||
|
@ -263,7 +278,8 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) {
|
||||||
this.setPureVirtual(record.id, true);
|
this.setPureVirtual(record.id, true);
|
||||||
this.updateRecordContext(record.id, {
|
this.updateRecordContext(record.id, {
|
||||||
ignore_warning: true,
|
ignore_warning: true,
|
||||||
not_onchange: true,
|
not_onchange: true, // To know is the record has the initial onchange applied
|
||||||
|
shadow: true, // To avoid show the loading backdrop
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -280,11 +296,16 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) {
|
||||||
* @returns {Deferred}
|
* @returns {Deferred}
|
||||||
*/
|
*/
|
||||||
createVirtualRecord: function(listID, options) {
|
createVirtualRecord: function(listID, options) {
|
||||||
const list = this.localData[listID];
|
var self = this;
|
||||||
const context = _.extend({}, this._getContext(list), options.context);
|
var list = this.localData[listID];
|
||||||
|
var context = _.extend(
|
||||||
|
{shadow: true},
|
||||||
|
this._getContext(list),
|
||||||
|
options.context
|
||||||
|
);
|
||||||
|
|
||||||
const position = options ? options.position : "top";
|
var position = options ? options.position : "top";
|
||||||
const params = {
|
var params = {
|
||||||
context: context,
|
context: context,
|
||||||
fields: list.fields,
|
fields: list.fields,
|
||||||
fieldsInfo: list.fieldsInfo,
|
fieldsInfo: list.fieldsInfo,
|
||||||
|
@ -295,18 +316,17 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) {
|
||||||
doNotSetDirty: true,
|
doNotSetDirty: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
return new Promise(resolve => {
|
return this._makeDefaultRecord(list.model, params).then(function(recordID) {
|
||||||
this._makeDefaultRecord(list.model, params).then(recordID => {
|
self.setPureVirtual(recordID, true);
|
||||||
this.setPureVirtual(recordID, true);
|
self.updateRecordContext(recordID, {
|
||||||
this.updateRecordContext(recordID, {
|
ignore_warning: true,
|
||||||
ignore_warning: true,
|
not_onchange: true,
|
||||||
not_onchange: true,
|
shadow: true,
|
||||||
});
|
|
||||||
resolve({
|
|
||||||
record: this.get(recordID),
|
|
||||||
params: params,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
return {
|
||||||
|
record: self.get(recordID),
|
||||||
|
params: params,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -319,16 +339,29 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) {
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
_performOnChange: function(record) {
|
_performOnChange: function(record) {
|
||||||
if (record && record.context && record.context.ignore_warning) {
|
if (record && record.context) {
|
||||||
const this_mp = _.clone(this);
|
record.context.not_onchange = false;
|
||||||
const super_call = this.trigger_up;
|
var this_mp = _.clone(this);
|
||||||
this_mp.trigger_up = function(event_name, data) {
|
if (record.context.shadow) {
|
||||||
if (event_name === "warning" && data.type === "dialog") {
|
// Force use 'shadow'
|
||||||
// Do nothing
|
var super__rpc_call = this._rpc;
|
||||||
return;
|
this_mp._rpc = function(params, options) {
|
||||||
}
|
options = options || {};
|
||||||
return super_call.apply(this, arguments);
|
options.shadow = true;
|
||||||
}.bind(this);
|
return super__rpc_call.call(this, params, options);
|
||||||
|
}.bind(this);
|
||||||
|
}
|
||||||
|
if (record.context.ignore_warning) {
|
||||||
|
var super_trigger_up_call = this.trigger_up;
|
||||||
|
// Avoid show warnings
|
||||||
|
this_mp.trigger_up = function(event_name, data) {
|
||||||
|
if (event_name === "warning" && data.type === "dialog") {
|
||||||
|
// Do nothing
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return super_trigger_up_call.apply(this, arguments);
|
||||||
|
}.bind(this);
|
||||||
|
}
|
||||||
return this._super.apply(this_mp, arguments);
|
return this._super.apply(this_mp, arguments);
|
||||||
}
|
}
|
||||||
return this._super.apply(this, arguments);
|
return this._super.apply(this, arguments);
|
||||||
|
@ -343,17 +376,94 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) {
|
||||||
*/
|
*/
|
||||||
_applyOnChange: function(values, record) {
|
_applyOnChange: function(values, record) {
|
||||||
if (!this.exists(record.id)) {
|
if (!this.exists(record.id)) {
|
||||||
return Promise.reject();
|
return $.Deferred().reject();
|
||||||
}
|
}
|
||||||
return this._super.apply(this, arguments);
|
return this._super.apply(this, arguments);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow add multiple records at the same time
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
_applyX2ManyChange: function(record, fieldName, command) {
|
||||||
|
if (command.operation === "ADD_MULTIPLE") {
|
||||||
|
var self = this;
|
||||||
|
var localID =
|
||||||
|
(record._changes && record._changes[fieldName]) ||
|
||||||
|
record.data[fieldName];
|
||||||
|
var list = this.localData[localID];
|
||||||
|
list._changes = list._changes || [];
|
||||||
|
// For now, we are in the context of a one2many field
|
||||||
|
// the command should look like this:
|
||||||
|
// { operation: 'ADD', ids: [id0,id1,id2,...] }
|
||||||
|
// The corresponding record may contain value for fields that
|
||||||
|
// are unknown in the list (e.g. fields that are in the
|
||||||
|
// subrecord form view but not in the kanban or list view), so
|
||||||
|
// to ensure that onchanges are correctly handled, we extend the
|
||||||
|
// list's fields with those in the created record
|
||||||
|
_.each(command.ids, function(id) {
|
||||||
|
var newRecord = self.localData[id];
|
||||||
|
_.defaults(list.fields, newRecord.fields);
|
||||||
|
_.defaults(list.fieldsInfo, newRecord.fieldsInfo);
|
||||||
|
newRecord.fields = list.fields;
|
||||||
|
newRecord.fieldsInfo = list.fieldsInfo;
|
||||||
|
newRecord.viewType = list.viewType;
|
||||||
|
list._cache[newRecord.res_id] = newRecord.id;
|
||||||
|
list._changes.push(command);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this._super.apply(this, arguments);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is necessary to avoid calculate onchanges that
|
||||||
|
* affects to order_line.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
_buildOnchangeSpecs: function(record) {
|
||||||
|
var specs = this._super.apply(this, arguments);
|
||||||
|
// This is necessary to improve the performance
|
||||||
|
if (record.model === "sale.order" && specs) {
|
||||||
|
// Its a change from product picker?
|
||||||
|
// WORKAROUND: Done in this way to reutilice odoo methods
|
||||||
|
var need_clean = false;
|
||||||
|
var parent_controller = this.getParent();
|
||||||
|
if (
|
||||||
|
parent_controller &&
|
||||||
|
parent_controller.renderer &&
|
||||||
|
!_.isEmpty(parent_controller.renderer.allFieldWidgets)
|
||||||
|
) {
|
||||||
|
var order_line_widget = _.find(
|
||||||
|
parent_controller.renderer.allFieldWidgets[
|
||||||
|
parent_controller.handle
|
||||||
|
],
|
||||||
|
{name: "order_line"}
|
||||||
|
);
|
||||||
|
need_clean =
|
||||||
|
order_line_widget instanceof FieldOne2ManyProductPicker &&
|
||||||
|
!_.isEmpty(this.localData[record.data.order_line]);
|
||||||
|
}
|
||||||
|
if (need_clean) {
|
||||||
|
var new_specs = _.clone(specs);
|
||||||
|
for (var key in specs) {
|
||||||
|
if (key.startsWith("order_line.")) {
|
||||||
|
delete new_specs[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new_specs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return specs;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {String} recordID
|
* @param {String} recordID
|
||||||
* @returns {Boolean}
|
* @returns {Boolean}
|
||||||
*/
|
*/
|
||||||
hasChanges: function(recordID) {
|
hasChanges: function(recordID) {
|
||||||
const record = this.localData[recordID];
|
var record = this.localData[recordID];
|
||||||
return record && !_.isEmpty(record._changes);
|
return record && !_.isEmpty(record._changes);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -368,7 +478,7 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) {
|
||||||
* @param {Number} limit
|
* @param {Number} limit
|
||||||
* @param {Number} offset
|
* @param {Number} offset
|
||||||
* @param {Object} context
|
* @param {Object} context
|
||||||
* @returns {Promise}
|
* @returns {Deferred}
|
||||||
*/
|
*/
|
||||||
fetchNameSearchFull: function(
|
fetchNameSearchFull: function(
|
||||||
model_fields,
|
model_fields,
|
||||||
|
@ -382,6 +492,7 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) {
|
||||||
offset,
|
offset,
|
||||||
context
|
context
|
||||||
) {
|
) {
|
||||||
|
var self = this;
|
||||||
return this._rpc({
|
return this._rpc({
|
||||||
model: model,
|
model: model,
|
||||||
method: "name_search",
|
method: "name_search",
|
||||||
|
@ -392,9 +503,11 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) {
|
||||||
limit: this.limit,
|
limit: this.limit,
|
||||||
context: context || {},
|
context: context || {},
|
||||||
},
|
},
|
||||||
}).then(results => {
|
}).then(function(results) {
|
||||||
const record_ids = results.map(item => item[0]);
|
var record_ids = results.map(function(item) {
|
||||||
return this.fetchGenericRecords(
|
return item[0];
|
||||||
|
});
|
||||||
|
return self.fetchGenericRecords(
|
||||||
model_fields,
|
model_fields,
|
||||||
model,
|
model,
|
||||||
[["id", "in", record_ids]],
|
[["id", "in", record_ids]],
|
||||||
|
@ -416,7 +529,7 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) {
|
||||||
* @param {Number} limit
|
* @param {Number} limit
|
||||||
* @param {Number} offset
|
* @param {Number} offset
|
||||||
* @param {Object} context
|
* @param {Object} context
|
||||||
* @returns {Promise}
|
* @returns {Deferred}
|
||||||
*/
|
*/
|
||||||
fetchGenericRecords: function(
|
fetchGenericRecords: function(
|
||||||
model_fields,
|
model_fields,
|
||||||
|
@ -428,6 +541,7 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) {
|
||||||
offset,
|
offset,
|
||||||
context
|
context
|
||||||
) {
|
) {
|
||||||
|
var self = this;
|
||||||
return this._rpc({
|
return this._rpc({
|
||||||
model: model,
|
model: model,
|
||||||
method: "search_read",
|
method: "search_read",
|
||||||
|
@ -437,13 +551,13 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function(require) {
|
||||||
offset: offset,
|
offset: offset,
|
||||||
orderBy: orderby,
|
orderBy: orderby,
|
||||||
kwargs: {context: context},
|
kwargs: {context: context},
|
||||||
}).then(result => {
|
}).then(function(result) {
|
||||||
for (const index in result) {
|
for (var index in result) {
|
||||||
const record = result[index];
|
var record = result[index];
|
||||||
for (const fieldName in record) {
|
for (var fieldName in record) {
|
||||||
const field = model_fields[fieldName];
|
var field = model_fields[fieldName];
|
||||||
if (field.type !== "many2one") {
|
if (field.type !== "many2one") {
|
||||||
record[fieldName] = this._parseServerValue(
|
record[fieldName] = self._parseServerValue(
|
||||||
model_fields[fieldName],
|
model_fields[fieldName],
|
||||||
record[fieldName]
|
record[fieldName]
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
// Copyright 2021 Tecnativa - Alexandre Díaz
|
||||||
|
// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
odoo.define("web_widget_one2many_product_picker.FormController", function(require) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var FormController = require("web.FormController");
|
||||||
|
|
||||||
|
FormController.include({
|
||||||
|
custom_events: _.extend({}, FormController.prototype.custom_events, {
|
||||||
|
using_product_picker: "_onUsingProductPicker",
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable product picker while saving
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
saveRecord: function() {
|
||||||
|
var self = this;
|
||||||
|
var always = function(changedFields) {
|
||||||
|
self.renderer.invokeProductPicker(self.handle, "onDocumentSave", false);
|
||||||
|
return changedFields;
|
||||||
|
};
|
||||||
|
this.renderer.invokeProductPicker(this.handle, "onDocumentSave", true);
|
||||||
|
return this._super
|
||||||
|
.apply(this, arguments)
|
||||||
|
.then(always)
|
||||||
|
.guardedCatch(always);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is necessary to refresh 'one2many_product_picker' when some 'trigger_refresh_fields' fields changes.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
_confirmChange: function(id, fields, e) {
|
||||||
|
var self = this;
|
||||||
|
id = id || this.handle;
|
||||||
|
return this._super.apply(this, arguments).then(function(resetWidgets) {
|
||||||
|
if (self.renderer) {
|
||||||
|
self.renderer.invokeProductPicker(
|
||||||
|
id,
|
||||||
|
"onDocumentConfirmChanges",
|
||||||
|
fields,
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return resetWidgets;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @param {CustomEvent} ev
|
||||||
|
*/
|
||||||
|
_onUsingProductPicker: function(ev) {
|
||||||
|
this.model.updateRecordContext(this.handle, {
|
||||||
|
product_picker_field: ev.data.field,
|
||||||
|
product_picker_product_field: ev.data.product_field,
|
||||||
|
product_picker_relation: ev.data.relation,
|
||||||
|
product_picker_relation_field: ev.data.relation_field,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2020 Tecnativa - Alexandre Díaz
|
||||||
|
// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
odoo.define("web_widget_one2many_product_picker.FormRenderer", function(require) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var FormRenderer = require("web.FormRenderer");
|
||||||
|
|
||||||
|
FormRenderer.include({
|
||||||
|
/**
|
||||||
|
* Invoke the selected method on all product picer defined
|
||||||
|
*/
|
||||||
|
invokeProductPicker: function(recordID, method_name, ...params) {
|
||||||
|
if (_.isEmpty(this.allFieldWidgets)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var product_picker_widgets = _.filter(
|
||||||
|
this.allFieldWidgets[recordID],
|
||||||
|
function(item) {
|
||||||
|
return item.attrs.widget === "one2many_product_picker";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
_.invoke(product_picker_widgets, method_name, ...params);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,22 +1,22 @@
|
||||||
/* global py */
|
/* global py */
|
||||||
// Copyright 2020 Tecnativa - Alexandre Díaz
|
// Copyright 2020 Tecnativa - Alexandre Díaz
|
||||||
// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
odoo.define("web_widget_one2many_product_picker.BasicView", function(require) {
|
odoo.define("web_widget_one2many_product_picker.FormView", function(require) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const core = require("web.core");
|
var core = require("web.core");
|
||||||
const pyUtils = require("web.py_utils");
|
var pyUtils = require("web.py_utils");
|
||||||
const BasicView = require("web.BasicView");
|
var FormView = require("web.FormView");
|
||||||
|
|
||||||
const _t = core._t;
|
var _t = core._t;
|
||||||
|
|
||||||
// Add ref to _() -> _t() call
|
// Add ref to _() -> _t() call
|
||||||
const PY_t = new py.PY_def.fromJSON(function() {
|
var PY_t = new py.PY_def.fromJSON(function() {
|
||||||
const args = py.PY_parseArgs(arguments, ["str"]);
|
var args = py.PY_parseArgs(arguments, ["str"]);
|
||||||
return py.str.fromJSON(_t(args.str.toJSON()));
|
return py.str.fromJSON(_t(args.str.toJSON()));
|
||||||
});
|
});
|
||||||
|
|
||||||
BasicView.include({
|
FormView.include({
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,27 @@
|
||||||
.oe_field_one2many_product_picker {
|
.oe_field_one2many_product_picker {
|
||||||
|
/** FIX AUTO-SCROLL ON SAMSUNG DEVICES **/
|
||||||
|
* {
|
||||||
|
overflow-anchor: none !important;
|
||||||
|
scroll-snap-stop: normal !important;
|
||||||
|
overscroll-behavior: unset !important;
|
||||||
|
scroll-behavior: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container,
|
||||||
|
header,
|
||||||
|
footer,
|
||||||
|
.container-fluid,
|
||||||
|
body,
|
||||||
|
div,
|
||||||
|
span,
|
||||||
|
section {
|
||||||
|
overflow-anchor: none !important;
|
||||||
|
scroll-snap-stop: normal !important;
|
||||||
|
overscroll-behavior: unset !important;
|
||||||
|
scroll-behavior: unset !important;
|
||||||
|
}
|
||||||
|
/** END FIX **/
|
||||||
|
|
||||||
&.oe_field_one2many_product_picker_maximized {
|
&.oe_field_one2many_product_picker_maximized {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
@ -15,6 +38,17 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
.badge {
|
||||||
|
filter: opacity(0.6);
|
||||||
|
}
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
|
> div.loading {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
width: unset !important;
|
width: unset !important;
|
||||||
}
|
}
|
||||||
|
@ -30,6 +64,19 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.loading {
|
||||||
|
position: absolute;
|
||||||
|
background-color: white;
|
||||||
|
padding: 0.5em;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
border: 2px solid gray;
|
||||||
|
z-index: 2;
|
||||||
|
box-shadow: 2px 2px 5px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.o_cp_buttons {
|
.o_cp_buttons {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
|
@ -57,6 +104,11 @@
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.o_catch_attention {
|
||||||
|
animation: none;
|
||||||
|
outline: 1px solid fade-in(theme-color("warning"), 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
.oe_flip_card {
|
.oe_flip_card {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
@ -67,9 +119,17 @@
|
||||||
height $one2many-product-picker-transition-3d-time;
|
height $one2many-product-picker-transition-3d-time;
|
||||||
height: $one2many-product-picker-card-min-height;
|
height: $one2many-product-picker-card-min-height;
|
||||||
|
|
||||||
|
&.blocked {
|
||||||
|
.badge {
|
||||||
|
filter: opacity(0.7);
|
||||||
|
}
|
||||||
|
div.loading {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&.disabled {
|
&.disabled {
|
||||||
filter: grayscale(100%);
|
filter: grayscale(1);
|
||||||
opacity: 0.5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.oe_flip_card_maximized {
|
&.oe_flip_card_maximized {
|
||||||
|
@ -138,7 +198,7 @@
|
||||||
transform-style: preserve-3d;
|
transform-style: preserve-3d;
|
||||||
|
|
||||||
.img-fluid {
|
.img-fluid {
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%) !important;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
|
@ -234,6 +294,7 @@
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 0;
|
left: 0;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
.oe_one2many_product_picker_form_buttons {
|
.oe_one2many_product_picker_form_buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -263,4 +324,39 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Extra tools **/
|
||||||
|
.icon-waiting {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clears the 'X' from Internet Explorer */
|
||||||
|
input[type="search"]::-ms-clear {
|
||||||
|
display: none;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
input[type="search"]::-ms-reveal {
|
||||||
|
display: none;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clears the 'X' from Chrome */
|
||||||
|
input[type="search"]::-webkit-search-decoration,
|
||||||
|
input[type="search"]::-webkit-search-cancel-button,
|
||||||
|
input[type="search"]::-webkit-search-results-button,
|
||||||
|
input[type="search"]::-webkit-search-results-decoration {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Modal Price **/
|
||||||
|
.oe_product_picker_quick_modif_price {
|
||||||
|
.modal-body {
|
||||||
|
min-height: 7em;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,12 @@
|
||||||
aria-describedby="btnGroupAddon2"
|
aria-describedby="btnGroupAddon2"
|
||||||
/>
|
/>
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
|
<button
|
||||||
|
id="product_picker_clear_input"
|
||||||
|
class='o_fullscreen btn btn-secondary'
|
||||||
|
>
|
||||||
|
<i class='fa fa-eraser' />
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
id="product_picker_maximize"
|
id="product_picker_maximize"
|
||||||
class='o_fullscreen btn btn-primary'
|
class='o_fullscreen btn btn-primary'
|
||||||
|
@ -88,7 +94,7 @@
|
||||||
</t>
|
</t>
|
||||||
<t t-name="One2ManyProductPicker.ActionButton">
|
<t t-name="One2ManyProductPicker.ActionButton">
|
||||||
<div class="safezone d-inline-block float-left m-0 pb-2 pr-2 text-left">
|
<div class="safezone d-inline-block float-left m-0 pb-2 pr-2 text-left">
|
||||||
<t t-if="is_saving && lazy_qty > 0">
|
<t t-if="need_notify || need_save || is_saving">
|
||||||
<span
|
<span
|
||||||
class="badge record_saving badge-warning font-weight-bold rounded-0 mt-0 px-2 py-3 product_qty"
|
class="badge record_saving badge-warning font-weight-bold rounded-0 mt-0 px-2 py-3 product_qty"
|
||||||
><span class="lazy_product_qty" t-esc="lazy_qty || '1'" /> x <t
|
><span class="lazy_product_qty" t-esc="lazy_qty || '1'" /> x <t
|
||||||
|
@ -97,15 +103,23 @@
|
||||||
</t>
|
</t>
|
||||||
<t t-elif="!is_virtual">
|
<t t-elif="!is_virtual">
|
||||||
<span
|
<span
|
||||||
t-att-data-field="field_map[field_uom_qty]"
|
t-attf-class="badge record_saving {{modified && 'badge-warning' || 'badge-success'}} font-weight-bold rounded-0 mt-0 px-2 py-3 product_qty"
|
||||||
t-attf-data-esc="'{{floatFixed(field_map[field_uom_qty])}}' + ' x ' + {{field_map[field_uom]}}.data.display_name"
|
><span
|
||||||
t-attf-class="badge {{modified && 'badge-warning' || 'badge-success'}} font-weight-bold rounded-0 mt-0 px-2 py-3 product_qty"
|
t-att-data-field="field_map[field_uom_qty]"
|
||||||
/>
|
t-attf-data-esc="str(floatFixed('{{field_map[field_uom_qty]}}'))"
|
||||||
|
class="lazy_product_qty"
|
||||||
|
/> x <span
|
||||||
|
t-att-data-field="field_map[field_uom]"
|
||||||
|
t-attf-data-esc="obj.{{field_map[field_uom]}}.data.display_name"
|
||||||
|
/></span>
|
||||||
</t>
|
</t>
|
||||||
<t t-else="">
|
<t t-else="">
|
||||||
<span
|
<span
|
||||||
class="badge badge-primary font-weight-bold rounded-0 mt-0 px-2 py-3 add_product"
|
class="badge badge-primary font-weight-bold rounded-0 mt-0 px-2 py-3 add_product"
|
||||||
><i class="fa fa-plus" /> 1 <t
|
><i class="fa fa-plus" /><span
|
||||||
|
class="lazy_product_qty"
|
||||||
|
t-esc="lazy_qty || '1'"
|
||||||
|
/> x <t
|
||||||
t-esc="state.data[field_map[field_uom]].data.display_name"
|
t-esc="state.data[field_map[field_uom]].data.display_name"
|
||||||
/></span>
|
/></span>
|
||||||
</t>
|
</t>
|
||||||
|
@ -116,12 +130,12 @@
|
||||||
<t t-if="show_discount">
|
<t t-if="show_discount">
|
||||||
<span
|
<span
|
||||||
t-att-data-field="field_map.discount"
|
t-att-data-field="field_map.discount"
|
||||||
t-attf-data-esc="str({{field_map.discount}} * -1.0) +'%'"
|
t-attf-data-esc="str(obj.{{field_map.discount}} * -1.0) +'%'"
|
||||||
class="badge badge-dark discount_price font-weight-bold rounded-0 mt-1 p-2"
|
class="badge badge-dark discount_price font-weight-bold rounded-0 mt-1 p-2"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
t-att-data-field="field_map.price_unit"
|
t-att-data-field="field_map.price_unit"
|
||||||
t-attf-data-esc="'{{monetary('price_unit',true)}}'"
|
data-esc="str(monetary('price_unit'))"
|
||||||
class="badge font-weight-bold rounded-0 original_price"
|
class="badge font-weight-bold rounded-0 original_price"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
|
@ -132,7 +146,7 @@
|
||||||
<t t-else="has_onchange">
|
<t t-else="has_onchange">
|
||||||
<span
|
<span
|
||||||
t-att-data-field="field_map.price_unit"
|
t-att-data-field="field_map.price_unit"
|
||||||
t-attf-data-esc="'{{monetary('price_unit',true)}}'"
|
data-esc="str(monetary('price_unit'))"
|
||||||
class="badge badge-info price_unit font-weight-bold rounded-0 mt-1 p-2"
|
class="badge badge-info price_unit font-weight-bold rounded-0 mt-1 p-2"
|
||||||
/>
|
/>
|
||||||
</t>
|
</t>
|
||||||
|
@ -140,7 +154,7 @@
|
||||||
</t>
|
</t>
|
||||||
<t t-name="One2ManyProductPicker.FlipCard.Front">
|
<t t-name="One2ManyProductPicker.FlipCard.Front">
|
||||||
<div
|
<div
|
||||||
t-attf-class="oe_flip_card_front p-0 {{((modified || is_saving) && 'border-warning') || (state && !is_virtual && 'border-success') || ''}}"
|
t-attf-class="oe_flip_card_front p-0 {{((need_notify || need_save || modified || is_saving) && 'border-warning') || (state && !is_virtual && 'border-success') || ''}}"
|
||||||
>
|
>
|
||||||
<t t-if="state">
|
<t t-if="state">
|
||||||
<div class="indicator_zones float-left">
|
<div class="indicator_zones float-left">
|
||||||
|
@ -150,7 +164,7 @@
|
||||||
<span
|
<span
|
||||||
data-field="display_name"
|
data-field="display_name"
|
||||||
class="oe_one2many_product_picker_title position-absolute fixed-bottom p-1"
|
class="oe_one2many_product_picker_title position-absolute fixed-bottom p-1"
|
||||||
data-esc="display_name"
|
data-esc="obj.display_name"
|
||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
alt=""
|
alt=""
|
||||||
|
@ -175,6 +189,9 @@
|
||||||
</t>
|
</t>
|
||||||
<t t-name="One2ManyProductPicker.FlipCard.Back">
|
<t t-name="One2ManyProductPicker.FlipCard.Back">
|
||||||
<div class="oe_flip_card_back">
|
<div class="oe_flip_card_back">
|
||||||
|
<span class="icon-waiting">
|
||||||
|
<i class="fa fa-cog fa-spin fa-3x fa-fw" />
|
||||||
|
</span>
|
||||||
<widget
|
<widget
|
||||||
name="product_picker_quick_create_form"
|
name="product_picker_quick_create_form"
|
||||||
t-att-compare-key="field_map.product_uom"
|
t-att-compare-key="field_map.product_uom"
|
||||||
|
@ -183,12 +200,11 @@
|
||||||
</t>
|
</t>
|
||||||
<t t-name="One2ManyProductPicker.FlipCard">
|
<t t-name="One2ManyProductPicker.FlipCard">
|
||||||
<div
|
<div
|
||||||
class="oe_flip_container p-1 col-12 col-sm-8 col-md-6 col-lg-4 col-xl-3 col-xxl-2"
|
class="oe_flip_container p-1 col-12 col-sm-6 col-md-4 col-lg-4 col-xl-4 col-xxl-2"
|
||||||
t-att-data-card-id="state && state.id || record_search.id"
|
t-att-data-card-id="state && state.id || record_search.id"
|
||||||
>
|
>
|
||||||
<div
|
<div t-attf-class="oe_flip_card {{!state && 'disabled' || ''}}">
|
||||||
t-attf-class="oe_flip_card {{!state && 'disabled' || (auto_save && (!is_virtual || is_saving) && !state.data.id && 'blocked') || ''}}"
|
<div class="loading">LOADING...</div>
|
||||||
>
|
|
||||||
<div class="oe_flip_card_inner text-center">
|
<div class="oe_flip_card_inner text-center">
|
||||||
<t t-call="One2ManyProductPicker.FlipCard.Front" />
|
<t t-call="One2ManyProductPicker.FlipCard.Front" />
|
||||||
<t t-call="One2ManyProductPicker.FlipCard.Back" />
|
<t t-call="One2ManyProductPicker.FlipCard.Back" />
|
||||||
|
@ -207,12 +223,13 @@
|
||||||
>
|
>
|
||||||
<div class="modal-dialog modal-sm modal-dialog-centered" role="document">
|
<div class="modal-dialog modal-sm modal-dialog-centered" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-body p-0" />
|
<div class="modal-body p-0">
|
||||||
|
<span class="icon-waiting">
|
||||||
|
<i class="fa fa-cog fa-spin fa-3x fa-fw" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button
|
<button class="btn btn-success oe_record_change mr-auto d-none">
|
||||||
class="btn btn-success oe_record_change mr-auto"
|
|
||||||
data-dismiss="modal"
|
|
||||||
>
|
|
||||||
<i class="fa fa-check" />
|
<i class="fa fa-check" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
|
|
@ -2,22 +2,22 @@
|
||||||
<t t-name="One2ManyProductPicker.QuickCreate.FormButtons">
|
<t t-name="One2ManyProductPicker.QuickCreate.FormButtons">
|
||||||
<div class="oe_one2many_product_picker_form_buttons">
|
<div class="oe_one2many_product_picker_form_buttons">
|
||||||
<t t-if="state == 'new'">
|
<t t-if="state == 'new'">
|
||||||
<button t-attf-class="btn btn-primary oe_record_add">Add</button>
|
<button t-attf-class="btn btn-primary oe_record_add w-100">Add</button>
|
||||||
</t>
|
</t>
|
||||||
<t t-elif="state == 'dirty'">
|
<t t-elif="state == 'dirty'">
|
||||||
<button class="btn btn-success oe_record_change w-100">
|
<button class="btn btn-success oe_record_change w-50">
|
||||||
<i class="fa fa-check" />
|
<i class="fa fa-check" /> Accept
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-warning oe_record_discard ml-1">
|
<button class="btn btn-warning oe_record_discard ml-1 w-50">
|
||||||
<i class="fa fa-times" />
|
<i class="fa fa-times" /> Discard
|
||||||
</button>
|
</button>
|
||||||
</t>
|
</t>
|
||||||
<t t-else="">
|
<t t-else="">
|
||||||
<button class="btn btn-danger oe_record_remove w-100"><i
|
<button class="btn btn-danger oe_record_remove w-50"><i
|
||||||
class="fa fa-trash"
|
class="fa fa-trash"
|
||||||
/> Remove</button>
|
/> Remove</button>
|
||||||
<button class="btn btn-warning oe_record_discard ml-1">
|
<button class="btn btn-warning oe_record_discard ml-1 w-50">
|
||||||
<i class="fa fa-times" />
|
<i class="fa fa-times" /> Discard
|
||||||
</button>
|
</button>
|
||||||
</t>
|
</t>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -53,17 +53,21 @@
|
||||||
type="text/javascript"
|
type="text/javascript"
|
||||||
src="/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/quick_modif_price_form.js"
|
src="/web_widget_one2many_product_picker/static/src/js/views/One2ManyProductPicker/quick_modif_price_form.js"
|
||||||
/>
|
/>
|
||||||
<script
|
|
||||||
type="text/javascript"
|
|
||||||
src="/web_widget_one2many_product_picker/static/src/js/views/basic_view.js"
|
|
||||||
/>
|
|
||||||
<script
|
<script
|
||||||
type="text/javascript"
|
type="text/javascript"
|
||||||
src="/web_widget_one2many_product_picker/static/src/js/views/basic_model.js"
|
src="/web_widget_one2many_product_picker/static/src/js/views/basic_model.js"
|
||||||
/>
|
/>
|
||||||
<script
|
<script
|
||||||
type="text/javascript"
|
type="text/javascript"
|
||||||
src="/web_widget_one2many_product_picker/static/src/js/views/basic_controller.js"
|
src="/web_widget_one2many_product_picker/static/src/js/views/form_view.js"
|
||||||
|
/>
|
||||||
|
<script
|
||||||
|
type="text/javascript"
|
||||||
|
src="/web_widget_one2many_product_picker/static/src/js/views/form_controller.js"
|
||||||
|
/>
|
||||||
|
<script
|
||||||
|
type="text/javascript"
|
||||||
|
src="/web_widget_one2many_product_picker/static/src/js/views/form_renderer.js"
|
||||||
/>
|
/>
|
||||||
<script
|
<script
|
||||||
type="text/javascript"
|
type="text/javascript"
|
||||||
|
|
Loading…
Reference in New Issue