upgrade v16
separación de las funcionalidades del PoS en un módulo aparte
This commit is contained in:
parent
70679c57a3
commit
308b3c4353
19 changed files with 184 additions and 386 deletions
1
pos_product_library/__init__.py
Normal file
1
pos_product_library/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
from . import models
|
30
pos_product_library/__manifest__.py
Normal file
30
pos_product_library/__manifest__.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
'name': 'PoS Product Library',
|
||||
'category': 'Product',
|
||||
'summary': 'Add product_library fields to point of sale',
|
||||
'version': "16.0.1.0.0",
|
||||
'description': """
|
||||
|
||||
Añade los campos de product_library al TPV
|
||||
==================================================
|
||||
* Hace búsquedas en el TPV de los campos autor y género.
|
||||
* Muestra el campo formato en el PoS en los productos y en las líneas de la orden.
|
||||
* Muestra los campos de product_library en el popup de info del producto.
|
||||
* Impide la búsqueda en el TPV en las descripciones.
|
||||
""",
|
||||
'author': 'Criptomart',
|
||||
'depends': [
|
||||
'product_library',
|
||||
'point_of_sale',
|
||||
],
|
||||
"assets": {
|
||||
"point_of_sale.assets": [
|
||||
"pos_product_library/static/src/js/**/*.js",
|
||||
"pos_product_library/static/src/xml/**/*.xml",
|
||||
],
|
||||
},
|
||||
'demo': [],
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
'application': True,
|
||||
}
|
2
pos_product_library/models/__init__.py
Normal file
2
pos_product_library/models/__init__.py
Normal file
|
@ -0,0 +1,2 @@
|
|||
from . import pos_session
|
||||
|
15
pos_product_library/models/pos_session.py
Normal file
15
pos_product_library/models/pos_session.py
Normal file
|
@ -0,0 +1,15 @@
|
|||
from odoo import models
|
||||
|
||||
|
||||
class PosSession(models.Model):
|
||||
_inherit = 'pos.session'
|
||||
|
||||
def _loader_params_product_product(self):
|
||||
result = super()._loader_params_product_product()
|
||||
result['search_params']['fields'].append('formato')
|
||||
result['search_params']['fields'].append('autor');
|
||||
result['search_params']['fields'].append('editorial');
|
||||
result['search_params']['fields'].append('genero');
|
||||
result['search_params']['fields'].append('subtitle');
|
||||
result['search_params']['fields'].append('fecha_entrada');
|
||||
return result
|
33
pos_product_library/static/src/js/screens.js
Normal file
33
pos_product_library/static/src/js/screens.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
odoo.define('product_library.screens', function (require) {
|
||||
"use strict";
|
||||
|
||||
var ProductScreen = require('point_of_sale.ProductScreen');
|
||||
var Registries = require('point_of_sale.Registries');
|
||||
|
||||
const LibraryProductScreen = (ProductScreen) =>
|
||||
class extends ProductScreen {
|
||||
_searchProduct(event) {
|
||||
const query = event.target.value.trim().toLowerCase();
|
||||
if (query) {
|
||||
const products = this.env.pos.db.get_product_by_category(0).filter(product => {
|
||||
return product.display_name.toLowerCase().includes(query) ||
|
||||
product.autor.toLowerCase().includes(query) ||
|
||||
product.editorial.toLowerCase().includes(query);
|
||||
});
|
||||
this.env.pos.db.add_products(products);
|
||||
} else {
|
||||
this.env.pos.db.add_products(this.env.pos.db.get_product_by_category(0));
|
||||
}
|
||||
this.render();
|
||||
}
|
||||
|
||||
mounted() {
|
||||
super.mounted();
|
||||
this.el.querySelector('.searchbox input').addEventListener('input', this._searchProduct.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
Registries.Component.extend(ProductScreen, LibraryProductScreen);
|
||||
|
||||
return LibraryProductScreen;
|
||||
});
|
61
pos_product_library/static/src/xml/pos.xml
Normal file
61
pos_product_library/static/src/xml/pos.xml
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t
|
||||
t-name="ProductItem"
|
||||
t-inherit="point_of_sale.ProductItem"
|
||||
t-inherit-mode="extension"
|
||||
owl="1"
|
||||
>
|
||||
<xpath expr="//div[hasclass('product-content')]" position="inside">
|
||||
<div class="flex">
|
||||
<br/>
|
||||
<span><b> <t t-esc="props.product.formato"/></b></span>
|
||||
</div>
|
||||
</xpath>
|
||||
</t>
|
||||
<t t-inherit="point_of_sale.Orderline" t-inherit-mode="extension">
|
||||
<xpath expr="//span[hasclass('product-name')]" position="inside">
|
||||
<span>
|
||||
<br/>
|
||||
Formato: <t t-out="props.line.get_product().formato" />
|
||||
</span>
|
||||
</xpath>
|
||||
</t>
|
||||
<t
|
||||
t-name="ProductInfoPopup"
|
||||
t-inherit="point_of_sale.ProductInfoPopup"
|
||||
t-inherit-mode="extension"
|
||||
owl="1"
|
||||
>
|
||||
<xpath expr="//div[hasclass('section-product-info-title')]" position="after">
|
||||
<div class="section-product-info">
|
||||
<br/>
|
||||
<table class="mobile-table">
|
||||
<tr>
|
||||
<td>Formato:</td>
|
||||
<td><t t-out="props.product.formato" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Subtitulo:</td>
|
||||
<td><t t-out="props.product.subtitle" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Autor:</td>
|
||||
<td><t t-out="props.product.author" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Editorial:</td>
|
||||
<td><t t-out="props.product.editorial" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Entrada:</td>
|
||||
<td><t t-out="props.product.fecha_entrada" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Género:</td>
|
||||
<td><t t-out="props.product.genero" /></td>
|
||||
</tr>
|
||||
</div>
|
||||
</xpath>
|
||||
</t>
|
||||
</templates>
|
|
@ -8,32 +8,17 @@
|
|||
Addon mejora de Odoo para librerías y tiendas de discos
|
||||
==================================================
|
||||
* Añade campos a product: Editorial, autor, género, formato, fecha de edición, fecha de entrada, colección, subcolección.
|
||||
* Hace búsquedas en el TPV y backend en esos campos nuevos.
|
||||
* Búsqueda sin acentos en el PoS.
|
||||
* Muestra el campo formato en el PoS en los productos y en las líneas de la orden.
|
||||
* Funcionalidad de pos_empty_default_image para mostrar el formato, en el addon original sobrescribe el widget y no se puede mostraar.
|
||||
|
||||
* Hace búsquedas en la ficha de productos del backend en esos campos nuevos.
|
||||
""",
|
||||
'author': 'Criptomart',
|
||||
'depends': [
|
||||
'product',
|
||||
'point_of_sale',
|
||||
],
|
||||
'external_dependencies': {'python': [], 'bin': []},
|
||||
'data': [
|
||||
'views/product.xml',
|
||||
'views/templates.xml',
|
||||
'views/pos_config.xml',
|
||||
],
|
||||
'qweb': [
|
||||
'static/src/xml/pos.xml',
|
||||
],
|
||||
'demo': [],
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
'application': True,
|
||||
"post_load": None,
|
||||
"pre_init_hook": None,
|
||||
"post_init_hook": None,
|
||||
"uninstall_hook": None,
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
from . import pos_config
|
||||
from . import product_template
|
||||
#from . import product_product
|
||||
|
||||
from . import product_product
|
||||
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
# Copyright (C) 2020 - Today:
|
||||
# GRAP (http://www.grap.coop)
|
||||
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo import models, fields
|
||||
|
||||
|
||||
class PosConfig(models.Model):
|
||||
_inherit = 'pos.config'
|
||||
|
||||
iface_fixed_font_size = fields.Integer(
|
||||
string="Fixed Font Size",
|
||||
help="Font size of the product name, when it has no image."
|
||||
" Set '0' will set adaptative font-size, depending on the"
|
||||
" length of the name.")
|
|
@ -1,19 +1,11 @@
|
|||
# Copyright (C) 2017 - Today:
|
||||
# GRAP (http://www.grap.coop)
|
||||
# Akretion (http://www.akretion.com)
|
||||
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo import models, fields, api
|
||||
|
||||
from odoo import models, fields
|
||||
|
||||
class ProductProduct(models.Model):
|
||||
_inherit = 'product.product'
|
||||
|
||||
@api.depends('image')
|
||||
def _compute_has_image(self):
|
||||
for product in self:
|
||||
product.has_image = product.image
|
||||
|
||||
has_image = fields.Boolean(
|
||||
compute='_compute_has_image', string='Has Image', store=True)
|
||||
formato = fields.Char('Formato', related='product_tmpl_id.formato', store=True)
|
||||
autor = fields.Char('Autor', related='product_tmpl_id.autor', store=True)
|
||||
editorial = fields.Char('Editorial', related='product_tmpl_id.editorial', store=True)
|
||||
genero = fields.Char('Genero', related='product_tmpl_id.genero', store=True)
|
||||
subtitle = fields.Char('Subtitulo', related='product_tmpl_id.subtitle', store=True)
|
||||
fecha_entrada = fields.Char('Fecha de entrada', related='product_tmpl_id.fecha_entrada', store=True)
|
|
@ -1,13 +1,8 @@
|
|||
# Copyright (C) 2021: Criptomart (https://criptomart.net)
|
||||
# @author Santi Noreña (<santi@criptomart.net>)
|
||||
# Copyright (C) 2021-2024: Criptomart (https://criptomart.net)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
import logging
|
||||
|
||||
from odoo import tools, models, fields, api, _
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class ProductTemplate(models.Model):
|
||||
_inherit = "product.template"
|
||||
|
||||
|
@ -17,7 +12,6 @@ class ProductTemplate(models.Model):
|
|||
pais_edicion = fields.Char('Pais de edicion')
|
||||
colacion = fields.Char('Colacion')
|
||||
autor = fields.Char('Autor')
|
||||
isbn = fields.Char('ISBN')
|
||||
coleccion = fields.Char('Coleccion')
|
||||
subcoleccion = fields.Char('Subcoleccion')
|
||||
idioma = fields.Char('Idioma')
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
.pos .product .format {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 2px;
|
||||
vertical-align: top;
|
||||
color: white;
|
||||
line-height: 13px;
|
||||
background: #7f82ac;
|
||||
padding: 2px 5px;
|
||||
border-radius: 2px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.pos .order .orderline .format {
|
||||
color: #57C;
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright (C) 2014 - Today: GRAP (http://www.grap.coop)
|
||||
@author Julien WESTE
|
||||
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
*/
|
||||
|
||||
.product-img-without-image{
|
||||
height:25px !important;
|
||||
}
|
||||
|
||||
.product-name-without-image{
|
||||
bottom:auto !important;
|
||||
top:25px !important;
|
||||
padding-top:13px !important;
|
||||
height:80px !important;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
word-wrap: break-word;
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
odoo.define('product_library', function (require) {
|
||||
|
||||
"use strict";
|
||||
var db = require("point_of_sale.DB");
|
||||
db.include({
|
||||
|
||||
normalize_characters: function (product) {
|
||||
// The normalization extract out combining diacritical marks
|
||||
// All those diacritics in range [\u0300-\u036f].
|
||||
// See https://en.wikipedia.org/wiki/Combining_Diacritical_Marks.
|
||||
// All the diacritics are removed by the code below.
|
||||
return product.normalize("NFD")
|
||||
.replace(/[\u0300-\u036f]/g, "")
|
||||
.replace(/[\u0152-\u0153]/g, "oe");
|
||||
},
|
||||
|
||||
add_custom_fields: function(product) {
|
||||
if(product) {
|
||||
var str = product.display_name;
|
||||
if(product.default_code){
|
||||
str += '|' + product.default_code;
|
||||
}
|
||||
if(product.autor){
|
||||
str += '|' + product.autor;
|
||||
}
|
||||
if(product.genero){
|
||||
str += '|' + product.genero;
|
||||
}
|
||||
if(product.editorial){
|
||||
str += '|' + product.editorial;
|
||||
}
|
||||
/*
|
||||
if (product.description) {
|
||||
str += '|' + product.description;
|
||||
}
|
||||
if (product.description_sale) {
|
||||
str += '|' + product.description_sale;
|
||||
}
|
||||
*/
|
||||
str = product.id + ':' + str.replace(':','') + '\n';
|
||||
}
|
||||
return str;
|
||||
},
|
||||
|
||||
_product_search_string: function(product) {
|
||||
console.log("product : " + JSON.stringify(product, null, 4));
|
||||
var str = this.add_custom_fields(product);
|
||||
console.log("string : " + str);
|
||||
var norm = this.normalize_characters(str);
|
||||
console.log("normalizado : " + norm);
|
||||
return norm;
|
||||
},
|
||||
/*
|
||||
search_product_in_category: function (category_id, query) {
|
||||
return this._super(category_id, this.add_custom_fields(query));
|
||||
},*/
|
||||
});
|
||||
|
||||
return db;
|
||||
|
||||
});
|
|
@ -1,31 +0,0 @@
|
|||
odoo.define('product_library.product_library', function (require) {
|
||||
"use strict";
|
||||
|
||||
var models = require('point_of_sale.models');
|
||||
//var screens = require('point_of_sale.screens');
|
||||
//var core = require('web.core');
|
||||
//var gui = require('point_of_sale.gui');
|
||||
//var _t = core._t;
|
||||
|
||||
//models.load_fields("product.product", ['has_image']);
|
||||
|
||||
/* ********************************************************
|
||||
Overload models.PosModel
|
||||
******************************************************** */
|
||||
|
||||
var _super_posmodel = models.PosModel.prototype;
|
||||
models.PosModel = models.PosModel.extend({
|
||||
initialize: function (session, attributes) {
|
||||
|
||||
//this.member_categories = [];
|
||||
|
||||
var product_model = _.find(this.models, function(model){ return model.model === 'product.product'; });
|
||||
//product_model.fields.push('default_code');
|
||||
product_model.fields.push('autor');
|
||||
product_model.fields.push('editorial');
|
||||
product_model.fields.push('genero');
|
||||
product_model.fields.push('formato');
|
||||
return _super_posmodel.initialize.apply(this, arguments);
|
||||
}
|
||||
});
|
||||
});
|
|
@ -1,42 +0,0 @@
|
|||
odoo.define('product_library.widgets', function (require) {
|
||||
"use strict";
|
||||
|
||||
var screens = require('point_of_sale.screens');
|
||||
|
||||
var core = require('web.core');
|
||||
|
||||
var QWeb = core.qweb;
|
||||
|
||||
//don't try to get an image if we know the product ain't one
|
||||
var ProductListImageWidget = screens.ProductListWidget.include({
|
||||
get_product_image_url: function(product){
|
||||
if (product.has_image)
|
||||
return this._super(product);
|
||||
},
|
||||
|
||||
// Change product display if product has no image;
|
||||
render_product: function(product){
|
||||
if (product.has_image){
|
||||
return this._super(product);
|
||||
}
|
||||
else {
|
||||
var current_pricelist = this._get_active_pricelist();
|
||||
var cache_key = this.calculate_cache_key(product, current_pricelist);
|
||||
var cached = this.product_cache.get_node(cache_key);
|
||||
if(!cached){
|
||||
var product_html = QWeb.render('ProductNoImage',{
|
||||
widget: this,
|
||||
product: product,
|
||||
pricelist: current_pricelist,
|
||||
});
|
||||
var product_node = document.createElement('div');
|
||||
product_node.innerHTML = product_html;
|
||||
product_node = product_node.childNodes[1];
|
||||
this.product_cache.cache_node(cache_key,product_node);
|
||||
return product_node;
|
||||
}
|
||||
return cached;
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
|
@ -1,69 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!--Copyright 2021 Criptomart <https://criptomart.net> -->
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-extend="Product">
|
||||
<t t-jquery=".product-name" t-operation="after">
|
||||
<!-- <t t-if="widget.pos.config.show_format && product.type == 'product'"> -->
|
||||
<span t-attf-class="format" >
|
||||
<t t-esc="product.formato" />
|
||||
</span>
|
||||
</t>
|
||||
<!-- </t> -->
|
||||
</t>
|
||||
|
||||
<t t-extend="Orderline">
|
||||
<!-- <t t-jquery=".product-name" t-operation="after">
|
||||
<span t-attf-class="format" >
|
||||
<t t-esc="product.formato" />
|
||||
</span>
|
||||
</t> -->
|
||||
<t t-jquery=".product-name" t-operation="after">
|
||||
<!-- <t t-if="widget.pos.config.show_format && line.get_product().type == 'product'" > -->
|
||||
<div t-attf-class="format" >
|
||||
<t t-esc="line.get_product().formato" />
|
||||
</div>
|
||||
<!-- </t> -->
|
||||
</t>
|
||||
</t>
|
||||
<!--
|
||||
<t t-name="ProductNoImage">
|
||||
<article class='product' t-att-data-product-id="product.id" tabindex="0" t-attf-aria-labelledby="article_product_#{product.id}">
|
||||
<t t-set="name_length" t-value="product.display_name.length" />
|
||||
<t t-if='widget.pos.config.iface_fixed_font_size'>
|
||||
<t t-set="font_size" t-value="widget.pos.config.iface_fixed_font_size" />
|
||||
</t>
|
||||
<t t-else="">
|
||||
<t t-set="font_size" t-value="
|
||||
name_length >=120 and 11
|
||||
or name_length >=90 and 12
|
||||
or name_length >=60 and 13
|
||||
or name_length>=50 and 14
|
||||
or name_length>=40 and 16
|
||||
or 20"/>
|
||||
</t>
|
||||
<div class="product-img-without-image">
|
||||
<t t-if="!product.to_weight">
|
||||
<span class="price-tag">
|
||||
<t t-esc="widget.format_currency(product.get_price(pricelist, 1),'Product Price')"/>
|
||||
</span>
|
||||
</t>
|
||||
<t t-if="product.to_weight">
|
||||
<span class="price-tag">
|
||||
<t t-esc="widget.format_currency(product.get_price(pricelist, 1),'Product Price')+'/'+widget.pos.units_by_id[product.uom_id[0]].name"/>
|
||||
</span>
|
||||
</t>
|
||||
<t t-if="product.formato">
|
||||
<span class="format">
|
||||
<t t-esc="product.formato"/>
|
||||
</span>
|
||||
</t>
|
||||
</div>
|
||||
<div class="product-name-without-image" t-attf-id="article_product_#{product.id}" t-attf-style="font-size: #{font_size}px !important; line-height: #{font_size}px !important;">
|
||||
<t t-esc="product.display_name"/>
|
||||
</div>
|
||||
</article>
|
||||
</t>
|
||||
-->
|
||||
</templates>
|
||||
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2020 Creu Blanca
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
|
||||
|
||||
<odoo>
|
||||
<!--
|
||||
<record model="ir.ui.view" id="pos_config_view_form">
|
||||
<field name="model">pos.config</field>
|
||||
<field name="inherit_id" ref="point_of_sale.pos_config_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='iface_big_scrollbars']/../.." position="after">
|
||||
|
||||
<div class="col-12 col-lg-6 o_setting_box">
|
||||
<div class="o_setting_right_pane">
|
||||
<label for="iface_fixed_font_size" string="Fixed Font Size"/>
|
||||
<div class="text-muted">
|
||||
Font size of the product name, when it has no image. Set '0' will set adaptative font-size, depending on the length of the name.
|
||||
</div>
|
||||
<div class="content-group mt16">
|
||||
<field name="iface_fixed_font_size"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
-->
|
||||
</odoo>
|
|
@ -6,6 +6,7 @@
|
|||
<field name="inherit_id" ref="product.product_template_only_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="barcode" position="after">
|
||||
<field name="subtitle"/>
|
||||
<field name="autor"/>
|
||||
<field name="genero"/>
|
||||
<field name="formato"/>
|
||||
|
@ -18,11 +19,17 @@
|
|||
<field name="subcoleccion"/>
|
||||
<field name="idioma"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_product_template_search_library" model="ir.ui.view">
|
||||
<field name="name">product.template.search.library</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="product.product_template_search_view" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="name" position="after">
|
||||
<div>
|
||||
<label for="subtitle" />
|
||||
<field name="subtitle"/>
|
||||
</div>
|
||||
<field name="autor" />
|
||||
<field name="editorial" />
|
||||
<field name="genero" />
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<!--
|
||||
<template id="assets_backend" name="product_library" inherit_id="point_of_sale.assets">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/product_library/static/src/js/models.js"></script>
|
||||
<script type="text/javascript" src="/product_library/static/src/js/db.js"/>
|
||||
<script type="text/javascript" src="/product_library/static/src/js/widgets.js"></script>
|
||||
</xpath>
|
||||
<xpath expr="//link[@id='pos-stylesheet']" position="after">
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="/product_library/static/src/css/pos.css"
|
||||
/>
|
||||
</xpath>
|
||||
</template>
|
||||
-->
|
||||
<!--
|
||||
<template
|
||||
id="assets_backend_web"
|
||||
name="product_library_backend_assets"
|
||||
inherit_id="web.assets_backend"
|
||||
>
|
||||
<xpath expr="." position="inside">
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="/product_library/static/src/js/test_pos_format.js"
|
||||
/>
|
||||
</xpath>
|
||||
</template>
|
||||
-->
|
||||
</odoo>
|
||||
|
Loading…
Add table
Reference in a new issue