LaOsaCoop/Odoo16#109 migrated website_search_products
This commit is contained in:
parent
26dbe222dd
commit
f1a3a75988
11 changed files with 525 additions and 0 deletions
84
website_search_products/README.rst
Normal file
84
website_search_products/README.rst
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
Website Search Products
|
||||||
|
=======================
|
||||||
|
|
||||||
|
This module allows portal users to search and browse products in a structured table
|
||||||
|
with filtering and sorting capabilities using DataTables.
|
||||||
|
|
||||||
|
Features
|
||||||
|
========
|
||||||
|
|
||||||
|
* Display products available in the store in a searchable, sortable table
|
||||||
|
* Filter by product name, category, and price
|
||||||
|
* Show real-time stock availability per location
|
||||||
|
* Restrict access to portal and internal users only
|
||||||
|
* Automatic redirection for portal users after login
|
||||||
|
* Integration with Odoo's standard product and stock management
|
||||||
|
|
||||||
|
Installation
|
||||||
|
============
|
||||||
|
|
||||||
|
Install the module through the Odoo Apps interface or via:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
odoo-bin -d <database> -c <config_file> -i website_search_products
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
=============
|
||||||
|
|
||||||
|
The module requires no additional configuration. It automatically:
|
||||||
|
|
||||||
|
1. Creates a menu item "Products in Shop" under the main website menu
|
||||||
|
2. Sets up routes for inventory search and login redirection
|
||||||
|
3. Filters products to show only those marked as "Available in POS"
|
||||||
|
|
||||||
|
Usage
|
||||||
|
=====
|
||||||
|
|
||||||
|
**For Portal Users:**
|
||||||
|
|
||||||
|
1. Navigate to the website
|
||||||
|
2. Click on "Products in Shop" in the menu
|
||||||
|
3. Browse the product table with real-time stock information
|
||||||
|
4. Use the search box to filter products by name
|
||||||
|
5. Click table headers to sort by different columns
|
||||||
|
|
||||||
|
**For Internal Users:**
|
||||||
|
|
||||||
|
1. After login, you will be redirected to the standard Odoo web interface
|
||||||
|
2. You can still access the product search page manually at ``/inventory/search``
|
||||||
|
|
||||||
|
**API Usage:**
|
||||||
|
|
||||||
|
The module extends ``stock.move`` with a method to retrieve movements by product:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
stock_move_env = self.env['stock.move']
|
||||||
|
result = stock_move_env.get_stock_moves_by_product(
|
||||||
|
date_begin='2024-01-01',
|
||||||
|
date_end='2024-01-31',
|
||||||
|
location=5 # Location ID
|
||||||
|
)
|
||||||
|
# Returns list of dicts with product data and movements
|
||||||
|
|
||||||
|
Known issues / Roadmap
|
||||||
|
======================
|
||||||
|
|
||||||
|
* Consider adding product image thumbnails in the future
|
||||||
|
* Could benefit from advanced filtering by product attributes
|
||||||
|
* May need performance optimization for stores with very large product catalogs
|
||||||
|
|
||||||
|
Credits
|
||||||
|
=======
|
||||||
|
|
||||||
|
Authors:
|
||||||
|
|
||||||
|
* Criptomart
|
||||||
|
* Odoo Community Association (OCA)
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
|
||||||
|
* Your Company Name
|
||||||
|
|
||||||
|
The module uses DataTables.net library for enhanced table functionality.
|
||||||
4
website_search_products/__init__.py
Normal file
4
website_search_products/__init__.py
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from . import controllers
|
||||||
|
from . import models
|
||||||
19
website_search_products/__manifest__.py
Normal file
19
website_search_products/__manifest__.py
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Copyright 2022 Criptomart
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Website Search Products",
|
||||||
|
"summary": "Allows portal users to search products in a table with filters.",
|
||||||
|
"version": "16.0.1.0.0",
|
||||||
|
"license": "AGPL-3",
|
||||||
|
"author": "Criptomart, Odoo Community Association (OCA)",
|
||||||
|
"website": "https://github.com/OCA/website",
|
||||||
|
"depends": ["stock", "website"],
|
||||||
|
"data": [
|
||||||
|
"data/menu.xml",
|
||||||
|
"views/web_templates.xml",
|
||||||
|
],
|
||||||
|
"demo": [],
|
||||||
|
"installable": True,
|
||||||
|
"application": False,
|
||||||
|
}
|
||||||
3
website_search_products/controllers/__init__.py
Normal file
3
website_search_products/controllers/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from . import controller
|
||||||
59
website_search_products/controllers/controller.py
Normal file
59
website_search_products/controllers/controller.py
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import http
|
||||||
|
from odoo.http import request
|
||||||
|
from odoo.addons.website.controllers.main import Website
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class InventoryForm(http.Controller):
|
||||||
|
|
||||||
|
@http.route(["/inventory/search"], type="http", auth="user", website=True)
|
||||||
|
def inventory_form(self, **post):
|
||||||
|
"""Display inventory search form for authorized users."""
|
||||||
|
user = request.env["res.users"].browse(request.uid)
|
||||||
|
|
||||||
|
# Check if user has portal or internal access
|
||||||
|
has_portal = user.has_group("base.group_portal")
|
||||||
|
has_internal = user.has_group("base.group_user")
|
||||||
|
|
||||||
|
if not has_portal and not has_internal:
|
||||||
|
return http.redirect("/web")
|
||||||
|
|
||||||
|
# Fetch active products available in POS
|
||||||
|
products = (
|
||||||
|
request.env["product.template"]
|
||||||
|
.sudo()
|
||||||
|
.search([("active", "=", True), ("available_in_pos", "=", True)])
|
||||||
|
)
|
||||||
|
vals = {"products": products}
|
||||||
|
return request.render("website_search_products.web_list_products", vals)
|
||||||
|
|
||||||
|
|
||||||
|
class WebsiteController(Website):
|
||||||
|
"""Extend Website controller to redirect portal users after login."""
|
||||||
|
|
||||||
|
@http.route(website=True, auth="public")
|
||||||
|
def web_login(self, redirect=None, *args, **kw):
|
||||||
|
"""Handle login redirection based on user group."""
|
||||||
|
response = super().web_login(redirect=redirect, *args, **kw)
|
||||||
|
|
||||||
|
# Check if login was successful
|
||||||
|
if not redirect and request.params.get("login_success"):
|
||||||
|
user = request.env["res.users"].browse(request.uid)
|
||||||
|
|
||||||
|
# Redirect internal users to web, others to inventory search
|
||||||
|
if user.has_group("base.group_user"):
|
||||||
|
# Keep original redirect for internal users
|
||||||
|
redirect = "/web"
|
||||||
|
else:
|
||||||
|
# Redirect portal users to inventory search
|
||||||
|
redirect = "/inventory/search"
|
||||||
|
|
||||||
|
return http.redirect(redirect)
|
||||||
|
|
||||||
|
return response
|
||||||
12
website_search_products/data/menu.xml
Normal file
12
website_search_products/data/menu.xml
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<record id="menu_partner_form" model="website.menu">
|
||||||
|
<field name="name">Products in Shop</field>
|
||||||
|
<field name="url">/inventory/search</field>
|
||||||
|
<field name="parent_id" ref="website.main_menu" />
|
||||||
|
<field name="sequence" type="int">22</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
78
website_search_products/i18n/es.po
Normal file
78
website_search_products/i18n/es.po
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
# Translation of Odoo Server.
|
||||||
|
# This file contains the translation of the following modules:
|
||||||
|
# * website_search_products
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Odoo Server 16.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2026-02-03 17:38+0000\n"
|
||||||
|
"PO-Revision-Date: 2026-02-03 17:38+0000\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: \n"
|
||||||
|
"Plural-Forms: \n"
|
||||||
|
|
||||||
|
#. module: website_search_products
|
||||||
|
#: model_terms:ir.ui.view,arch_db:website_search_products.web_list_products
|
||||||
|
msgid "Category"
|
||||||
|
msgstr "Categoría"
|
||||||
|
|
||||||
|
#. module: website_search_products
|
||||||
|
#: model_terms:ir.ui.view,arch_db:website_search_products.web_list_products
|
||||||
|
msgid "Displayed products"
|
||||||
|
msgstr "Productos mostrados"
|
||||||
|
|
||||||
|
#. module: website_search_products
|
||||||
|
#: model_terms:ir.ui.view,arch_db:website_search_products.web_list_products
|
||||||
|
msgid "In this table you can consult all the products available in the store"
|
||||||
|
msgstr "En esta tabla puedes consultar todos los productos disponibles en la tienda"
|
||||||
|
|
||||||
|
#. module: website_search_products
|
||||||
|
#: model_terms:ir.ui.view,arch_db:website_search_products.web_list_products
|
||||||
|
msgid "Name"
|
||||||
|
msgstr "Nombre"
|
||||||
|
|
||||||
|
#. module: website_search_products
|
||||||
|
#: model_terms:ir.ui.view,arch_db:website_search_products.web_list_products
|
||||||
|
msgid ""
|
||||||
|
"Please note that there may be minor discrepancies, which are updated as we "
|
||||||
|
"perform the recurrent inventories."
|
||||||
|
msgstr "Ten en cuenta que puede haber algunos pequeños desajustes que se van actualizando a medida que realizamos los inventarios de los lunes. "
|
||||||
|
|
||||||
|
#. module: website_search_products
|
||||||
|
#: model_terms:ir.ui.view,arch_db:website_search_products.web_list_products
|
||||||
|
msgid "Price"
|
||||||
|
msgstr "Precio"
|
||||||
|
|
||||||
|
#. module: website_search_products
|
||||||
|
#: model:website.menu,name:website_search_products.menu_partner_form
|
||||||
|
msgid "Products in Shop"
|
||||||
|
msgstr "Productos en tienda"
|
||||||
|
|
||||||
|
#. module: website_search_products
|
||||||
|
#: model_terms:ir.ui.view,arch_db:website_search_products.web_list_products
|
||||||
|
msgid "Products not found"
|
||||||
|
msgstr "Productos no encontrados"
|
||||||
|
|
||||||
|
#. module: website_search_products
|
||||||
|
#: model_terms:ir.ui.view,arch_db:website_search_products.web_list_products
|
||||||
|
msgid "Quantity"
|
||||||
|
msgstr "Cantidad"
|
||||||
|
|
||||||
|
#. module: website_search_products
|
||||||
|
#: model:ir.model.fields,field_description:website_search_products.field_stock_move__smart_search
|
||||||
|
msgid "Smart Search"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: website_search_products
|
||||||
|
#: model:ir.model,name:website_search_products.model_stock_move
|
||||||
|
msgid "Stock Move"
|
||||||
|
msgstr "Movimiento de stock"
|
||||||
|
|
||||||
|
#. module: website_search_products
|
||||||
|
#: model_terms:ir.ui.view,arch_db:website_search_products.web_list_products
|
||||||
|
msgid "UoM"
|
||||||
|
msgstr "UdM"
|
||||||
78
website_search_products/i18n/website_search_products.pot
Normal file
78
website_search_products/i18n/website_search_products.pot
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
# Translation of Odoo Server.
|
||||||
|
# This file contains the translation of the following modules:
|
||||||
|
# * website_search_products
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Odoo Server 16.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2026-02-03 17:38+0000\n"
|
||||||
|
"PO-Revision-Date: 2026-02-03 17:38+0000\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: \n"
|
||||||
|
"Plural-Forms: \n"
|
||||||
|
|
||||||
|
#. module: website_search_products
|
||||||
|
#: model_terms:ir.ui.view,arch_db:website_search_products.web_list_products
|
||||||
|
msgid "Category"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: website_search_products
|
||||||
|
#: model_terms:ir.ui.view,arch_db:website_search_products.web_list_products
|
||||||
|
msgid "Displayed products"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: website_search_products
|
||||||
|
#: model_terms:ir.ui.view,arch_db:website_search_products.web_list_products
|
||||||
|
msgid "In this table you can consult all the products available in the store"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: website_search_products
|
||||||
|
#: model_terms:ir.ui.view,arch_db:website_search_products.web_list_products
|
||||||
|
msgid "Name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: website_search_products
|
||||||
|
#: model_terms:ir.ui.view,arch_db:website_search_products.web_list_products
|
||||||
|
msgid ""
|
||||||
|
"Please note that there may be minor discrepancies, which are updated as we "
|
||||||
|
"perform the recurrent inventories."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: website_search_products
|
||||||
|
#: model_terms:ir.ui.view,arch_db:website_search_products.web_list_products
|
||||||
|
msgid "Price"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: website_search_products
|
||||||
|
#: model:website.menu,name:website_search_products.menu_partner_form
|
||||||
|
msgid "Products in Shop"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: website_search_products
|
||||||
|
#: model_terms:ir.ui.view,arch_db:website_search_products.web_list_products
|
||||||
|
msgid "Products not found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: website_search_products
|
||||||
|
#: model_terms:ir.ui.view,arch_db:website_search_products.web_list_products
|
||||||
|
msgid "Quantity"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: website_search_products
|
||||||
|
#: model:ir.model.fields,field_description:website_search_products.field_stock_move__smart_search
|
||||||
|
msgid "Smart Search"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: website_search_products
|
||||||
|
#: model:ir.model,name:website_search_products.model_stock_move
|
||||||
|
msgid "Stock Move"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: website_search_products
|
||||||
|
#: model_terms:ir.ui.view,arch_db:website_search_products.web_list_products
|
||||||
|
msgid "UoM"
|
||||||
|
msgstr ""
|
||||||
3
website_search_products/models/__init__.py
Normal file
3
website_search_products/models/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from . import stock_move
|
||||||
90
website_search_products/models/stock_move.py
Normal file
90
website_search_products/models/stock_move.py
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import fields, models
|
||||||
|
from datetime import datetime
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class StockMove(models.Model):
|
||||||
|
_inherit = "stock.move"
|
||||||
|
|
||||||
|
def get_stock_moves_by_product(self, date_begin, date_end, location):
|
||||||
|
"""
|
||||||
|
Get stock movements grouped by product for a given date range and location.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
date_begin (str): Start date in format 'YYYY-MM-DD'
|
||||||
|
date_end (str): End date in format 'YYYY-MM-DD'
|
||||||
|
location (int): Location ID to filter movements
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: List of dictionaries with product and movement data
|
||||||
|
"""
|
||||||
|
if date_begin and date_end:
|
||||||
|
moves = self.env["stock.move"].search(
|
||||||
|
[
|
||||||
|
"|",
|
||||||
|
("location_id", "=", int(location)),
|
||||||
|
("location_dest_id", "=", int(location)),
|
||||||
|
("state", "=", "done"),
|
||||||
|
("date", ">=", date_begin),
|
||||||
|
("date", "<=", date_end),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
moves_grouped = {}
|
||||||
|
data = []
|
||||||
|
|
||||||
|
if moves:
|
||||||
|
for move in moves:
|
||||||
|
if move.product_id.id and move.product_id.type == "product":
|
||||||
|
if not moves_grouped.get(move.product_id.id, False):
|
||||||
|
date_end_obj = datetime.strptime(
|
||||||
|
date_end, "%Y-%m-%d"
|
||||||
|
).date() + relativedelta(days=1)
|
||||||
|
moves_grouped[move.product_id.id] = dict([])
|
||||||
|
moves_grouped[move.product_id.id]["name"] = move.product_id.name
|
||||||
|
moves_grouped[move.product_id.id][
|
||||||
|
"category"
|
||||||
|
] = move.product_id.categ_id.name
|
||||||
|
moves_grouped[move.product_id.id][
|
||||||
|
"list_price"
|
||||||
|
] = move.product_id.list_price
|
||||||
|
# Get quantity at start of period
|
||||||
|
moves_grouped[move.product_id.id]["qty_init"] = (
|
||||||
|
move.product_id.with_context(
|
||||||
|
{
|
||||||
|
"to_date": date_begin,
|
||||||
|
"location_ids": [int(location)],
|
||||||
|
}
|
||||||
|
).qty_available
|
||||||
|
)
|
||||||
|
# Get quantity at end of period
|
||||||
|
moves_grouped[move.product_id.id]["qty_end"] = (
|
||||||
|
move.product_id.with_context(
|
||||||
|
{
|
||||||
|
"to_date": date_end_obj,
|
||||||
|
"location_ids": [int(location)],
|
||||||
|
}
|
||||||
|
).qty_available
|
||||||
|
)
|
||||||
|
|
||||||
|
if not moves_grouped[move.product_id.id].get("in"):
|
||||||
|
moves_grouped[move.product_id.id]["in"] = 0.0
|
||||||
|
if not moves_grouped[move.product_id.id].get("out"):
|
||||||
|
moves_grouped[move.product_id.id]["out"] = 0.0
|
||||||
|
|
||||||
|
if int(move.location_id.id) == int(location):
|
||||||
|
moves_grouped[move.product_id.id]["out"] += move.product_qty
|
||||||
|
elif int(move.location_dest_id.id) == int(location):
|
||||||
|
moves_grouped[move.product_id.id]["in"] += move.product_qty
|
||||||
|
|
||||||
|
data = list(moves_grouped.values())
|
||||||
|
|
||||||
|
return data
|
||||||
95
website_search_products/views/web_templates.xml
Normal file
95
website_search_products/views/web_templates.xml
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright 2021 Criptomart
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<template id="web_list_products" name="Product List">
|
||||||
|
<t t-call="website.layout">
|
||||||
|
<div id="wrap">
|
||||||
|
<div class="container">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<t t-if="products">
|
||||||
|
<div class="alert alert-success">
|
||||||
|
Displayed products
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
<t t-if="not products">
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
Products not found
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
<div class="col-md-12" style="text-align: center;">
|
||||||
|
<h2>In this table you can consult all the products available in the store</h2>
|
||||||
|
<h5>Please note that there may be minor discrepancies, which are updated as we perform the recurrent inventories.</h5>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12" style="margin-top: 50px;">
|
||||||
|
<table id="productTable" class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Category</th>
|
||||||
|
<th>Price</th>
|
||||||
|
<th>Quantity</th>
|
||||||
|
<th>UoM</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="stockList">
|
||||||
|
<t t-foreach="products" t-as="product">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span t-esc="product.name" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span t-esc="product.categ_id.name" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span t-esc="product.list_price"
|
||||||
|
t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span t-esc="product.sudo().qty_available"
|
||||||
|
t-options="{'widget': 'float', 'precision': 2}" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span t-esc="product.sudo().uom_id.name" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</t>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<link rel="stylesheet"
|
||||||
|
href="https://cdn.datatables.net/2.0.0/css/dataTables.dataTables.css" />
|
||||||
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
|
<script src="https://cdn.datatables.net/2.0.0/js/dataTables.js"></script>
|
||||||
|
<script>
|
||||||
|
// Esperar a que jQuery esté disponible
|
||||||
|
(function waitForJQuery() {
|
||||||
|
if (typeof jQuery !== 'undefined') {
|
||||||
|
jQuery(document).ready(function($) {
|
||||||
|
$('#productTable').DataTable({
|
||||||
|
"pageLength": 100,
|
||||||
|
"order": [[1, "asc"], [0, "asc"], [2, "desc"]],
|
||||||
|
"layout": {
|
||||||
|
"topStart": "pageLength",
|
||||||
|
"topEnd": "search",
|
||||||
|
"bottomStart": "info",
|
||||||
|
"bottomEnd": "paging"
|
||||||
|
},
|
||||||
|
"language": {
|
||||||
|
"url": "https://cdn.datatables.net/plug-ins/1.13.1/i18n/es-ES.json"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setTimeout(waitForJQuery, 100);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</t>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue