Estamos mejorando nuestra Tienda, Portal de clientes y Aenormas para ofrecerte una mejor experiencia.
Si detectas cualquier incidencia o necesitas ayuda durante el proceso de compra, puedes contactarnos a través del chat o escribirnos a normas@aenor.com.
Nuestro equipo estará encantado de ayudarte.
CEN ISO/TR 14745:2015
Listado colecciones temáticas relacionadas
Se ha producido un error al procesar la plantilla.
The following has evaluated to null or missing:
==> channelResponse.items [in template "34352066712900#33336#153601550" at line 16, column 20]
----
Tip: It's the step after the last dot that caused this error, not those before it.
----
Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)??
----
----
FTL stack trace ("~" means nesting-related):
- Failed at: #assign channel = channelResponse.ite... [in template "34352066712900#33336#153601550" at line 16, column 1]
----
1<#--
2 La creación de selector language-format se hace desde el JS
3 usando window.ecomSelectorsLanguageFormatScripts init() > window.ecomSelectorsLanguageFormatScripts _createSelectorsLanguageFormat()
4 que se encuentra en el fragment [dxp-ecom-portal/misc/fragments/Collection Tienda/ECOM-Global_functions/index.js].
5 Necesita los siguientes datos: data-productid="${productId}" , data-erc="${productERC}" y class="selector-language_format"
6-->
7<#--
8 El filtrado de items relacionados segun el tipo de producto se hace desde el fragment
9 [dxp-ecom-portal/misc/fragments/Collection Tienda/ECOM-Related_products/index.js].
10-->
11<#setting url_escaping_charset="UTF-8">
12<meta name="viewport" content="width=device-width, initial-scale=1">
13
14<#-- Channel data -->
15<#assign channelResponse = restClient.get("/headless-commerce-delivery-catalog/v1.0/channels?filter=name eq 'Aenor Tienda'")>
16<#assign channel = channelResponse.items[0]>
17<#assign channelId = channel.id>
18
19<#function getListTypeEntriesByERC erc>
20 <#attempt>
21 <#return restClient.get(
22 "/headless-admin-list-type/v1.0/list-type-definitions/by-external-reference-code/${erc}/list-type-entries?fields=key,name&sort"
23 ).items>
24 <#recover>
25 <#return []>
26 </#attempt>
27</#function>
28
29<#assign LANG_ENTRIES = getListTypeEntriesByERC("IDIOMAS_NORMAS_PICKLIST")>
30
31<#function langLiteral key>
32 <#assign k = (key!"")?upper_case>
33 <#list LANG_ENTRIES as e>
34 <#if (e.key!"")?upper_case == k>
35 <#return e.name!"">
36 </#if>
37 </#list>
38 <#return key!"" >
39</#function>
40
41<section class="list-thematic-collection-section">
42 <div class="container">
43 <div class="row d-flex">
44 <div class="col-12 thematic-collection-container">
45 <#if entries?has_content>
46 <#-- <#assign channelId = getChannelId()> -->
47 <#list entries as entry>
48
49 <#-- Product data -->
50 <#assign product = getProduct(channelId, entry.getCProductId())>
51 <#assign productId = product.productId />
52 <#assign productERC = product.externalReferenceCode />
53 <#assign categoriesProduct = getProductCategories(channelId, productId) />
54 <#assign hasProductCategoriaTipoEntidadLibro = isVocabularyNameIntoCategories(categoriesProduct, 'entity type', 'libro') />
55 <#assign hasProductCategoriaTipoEntidadNorma = isVocabularyNameIntoCategories(categoriesProduct, 'entity type', 'norma') />
56 <#assign hasProductCategoriaTipoEntidadColeccionTematica = isVocabularyNameIntoCategories(categoriesProduct, 'entity type', 'coleccion tematica') />
57 <#assign currentStateDate = getCurrentStateDate(getProductSpecifications(channelId, product.productId))>
58 <#assign status = getStatus(getProductCategories(channelId, product.productId))>
59 <#assign organism = getOrganism(getProductCategories(channelId, product.productId))>
60 <#assign normasDetails = getNormasDetails(product.id)>
61
62 <#assign languages = []>
63 <#list normasDetails as n>
64 <#if n.language?? && n.language?has_content && !languages?seq_contains(n.language)>
65 <#assign languages = languages + [n.language]>
66 </#if>
67 </#list>
68
69 <#assign defaultLang = "">
70 <#assign defaultFmt = "">
71 <#if normasDetails?has_content>
72 <#assign defaultLang = (normasDetails[0].language!"")>
73 <#assign defaultFmt = (normasDetails[0].format!"")>
74 </#if>
75
76 <#assign formatsForDefault = []>
77 <#list normasDetails?filter(nd -> (nd.language!"") == defaultLang) as nd>
78 <#if nd.format?? && nd.format?has_content && !formatsForDefault?seq_contains(nd.format)>
79 <#assign formatsForDefault = formatsForDefault + [nd.format]>
80 </#if>
81 </#list>
82 <#if formatsForDefault?has_content && !formatsForDefault?seq_contains(defaultFmt)>
83 <#assign defaultFmt = formatsForDefault[0]>
84 </#if>
85 <#assign hasOptions = (languages?has_content && formatsForDefault?has_content && defaultLang?has_content && defaultFmt?has_content)>
86
87
88 <div class="item-thematic-collection item-thematic-collection-adt"
89 data-category-type-entity="${
90 hasProductCategoriaTipoEntidadLibro?then(
91 'book',
92 hasProductCategoriaTipoEntidadNorma?then(
93 'standard',
94 hasProductCategoriaTipoEntidadColeccionTematica?then(
95 'collection-thematic',
96 'other'
97 )
98 )
99 )
100 }"
101 data-index="${entry_index + 1}"
102 data-index-total="${entries?size}"
103 data-productid="${product.id?html}"
104 data-title="${product.name?html}"
105 data-entrytype="Norma"
106 data-code="${(product.externalReferenceCode!entry.getCProductId())?html}">
107
108 <#if organism?? && organism?has_content>
109 <div class="tag-standard">${organism}</div>
110 </#if>
111
112 <div class="info-standard">
113 <a href="/p/${entry.getUrl()}" data-senna-off="true">
114 <h3 class="title-standard">${product.name}</h3>
115 </a>
116
117 <div class="status-box">
118
119 <#if status?? && status?has_content>
120 <#assign statusTagClass = ''>
121
122 <#if status?trim?upper_case == 'EN VIGOR'>
123 <#assign statusTagClass = 'green-tag'>
124 <#elseif status?trim?upper_case == 'ANULADA'>
125 <#assign statusTagClass = 'red-tag'>
126 <#elseif status?trim?upper_case == 'PROYECTO'>
127 <#assign statusTagClass = 'blue-tag'>
128 </#if>
129
130 <#if statusTagClass?? && statusTagClass?has_content>
131 <#assign statusTagClass = "status-standard " + statusTagClass>
132 </#if>
133
134 <div class="badge ${statusTagClass}">${status}</div>
135 </#if>
136
137 <#if currentStateDate?? && currentStateDate?has_content>
138 <span class="date-standard">
139 ${currentStateDate?date.iso?string('yyyy-MM-dd')}
140 </span>
141 </#if>
142 </div>
143
144 <div class="description-text">
145 ${product.description}
146 </div>
147
148 <div class="price-container">
149 <span class="price price-ae">
150 </span>
151 </div>
152
153 <div data-productid="${productId}" data-erc="${productERC}" class="options-standard selector-language_format"></div>
154
155 <button class="standard-button" disabled>${languageUtil.get(locale, "ecom-add_cart")}</button>
156 </div>
157
158 </div>
159 </#list>
160 </#if>
161 </div>
162 </div>
163 </div>
164</section>
165
166<#-- Funciones REST -->
167<#function getProduct channelId productId>
168 <#return restClient.get(
169 "/headless-commerce-delivery-catalog/v1.0/channels/${channelId}/products/${productId}"
170 )>
171</#function>
172
173<#function getProductCategories channelId productId>
174 <#return restClient.get("/headless-commerce-delivery-catalog/v1.0/channels/${channelId}/products/${productId}/categories?sort=vocabulary").items>
175</#function>
176
177<#function isVocabularyNameIntoCategories categories vocabulary name>
178 <#assign found = false />
179
180 <#if categories?has_content && vocabulary?has_content && name?has_content>
181
182 <#assign vocabNorm = normalize(vocabulary) />
183 <#assign nameNorm = normalize(name) />
184
185 <#list categories as category>
186 <#if !found>
187 <#assign catVocabNorm = normalize(category.vocabulary) />
188 <#assign catNameNorm = normalize(category.name) />
189
190 <#if catVocabNorm == vocabNorm && catNameNorm == nameNorm>
191 <#assign found = true />
192 </#if>
193 </#if>
194 </#list>
195
196 </#if>
197
198 <#return found>
199</#function>
200
201<#function normalize text onlyAccents = false>
202 <#-- proteger null -->
203 <#if !text?has_content>
204 <#return "">
205 </#if>
206
207 <#assign t = text />
208
209 <#-- quitar acentos -->
210 <#assign t = t
211 ?replace("á","a")?replace("é","e")?replace("í","i")
212 ?replace("ó","o")?replace("ú","u")?replace("ü","u")
213 ?replace("ñ","n")
214 ?replace("Á","A")?replace("É","E")?replace("Í","I")
215 ?replace("Ó","O")?replace("Ú","U")?replace("Ü","U")
216 ?replace("Ñ","N")
217 />
218
219 <#-- si NO es solo acentos, normalización completa -->
220 <#if !onlyAccents>
221 <#assign t = t?lower_case />
222 <#assign t = t?trim />
223 <#assign t = t?replace("\\s+", " ", "r") />
224 </#if>
225
226 <#return t>
227</#function>
228
229<#function getProductSpecifications channelId productId>
230 <#return restClient.get(
231 "/headless-commerce-delivery-catalog/v1.0/channels/${channelId}/products/${productId}/product-specifications?sort"
232 ).items>
233</#function>
234
235<#function getCurrentStateDate specifications>
236 <#list specifications as specification>
237 <#if specification.specificationKey == 'current-state-date'>
238 <#return specification.value>
239 </#if>
240 </#list>
241 <#return "">
242</#function>
243
244<#function getStatus categories>
245 <#list categories as category>
246 <#if category.vocabulary == 'status'>
247 <#return category.title>
248 </#if>
249 </#list>
250 <#return "">
251</#function>
252
253<#function getOrganism categories>
254 <#list categories as category>
255 <#if category.vocabulary == 'organismos'>
256 <#return category.name>
257 </#if>
258 </#list>
259 <#return "">
260</#function>
261
262<#function getNormasDetails productId>
263 <#return restClient.get(
264 "/c/standards/?filter=r_standards_CPDefinitionId eq '${productId}'"
265 ).items>
266</#function>
267
268<#macro printObject obj>
269 <#-- Permite hacer un output de un array de objetos o un objeto que se pasa como parámetro -->
270 <#if obj?is_hash>
271 {
272 <#list obj?keys as k>
273 "${k}":
274 <#assign value = obj[k]>
275 <#if value?is_hash || value?is_sequence>
276 <@printObject obj=value/>
277 <#elseif value?is_boolean>
278 ${value?c}
279 <#elseif value?is_number>
280 ${value}
281 <#elseif value?has_content>
282 "${value?string}"
283 <#else>
284 null
285 </#if>
286 <#if k_has_next>, </#if>
287 </#list>
288 }
289 <#elseif obj?is_sequence>
290 [
291 <#list obj as item>
292 <@printObject obj=item/>
293 <#if item_has_next>, </#if>
294 </#list>
295 ]
296 <#elseif obj?is_boolean>
297 ${obj?c}
298 <#elseif obj?is_number>
299 ${obj}
300 <#elseif obj?has_content>
301 "${obj?string}"
302 <#else>
303 null
304 </#if>
305</#macro>
306<script>
307(function($){
308 async function onAdd(e){
309 e.preventDefault();
310 var $card = $(e.currentTarget).closest('.item-thematic-collection');
311 var entryType = ($card.data('entrytype') || 'ColeccionTematica');
312 var code = ($card.data('code') || '').toString();
313 var codIdioma = ($card.find('.select-language').val() || '').toString();
314 var codFormato = ($card.find('.select-format').val() || '').toString();
315 if (!code) code = ($card.find('.title-standard').text() || '').trim();
316
317 await window.ecomGlobalScripts.functions.addToCart({
318 entryType: entryType,
319 code: code,
320 codIdioma: codIdioma,
321 codFormato: codFormato,
322 price: parseFloat(($card.find('.price-ae').text() || '0').replace(/[^\d,.-]/g, '').replace(',', '.')) || 0,
323 name: ($card.data('title') || '').toString(),
324 amount: 1
325 });
326 }
327
328 function equalizeRowHeights($elements){
329 var rows = {};
330 $elements.each(function(){
331 var $el = $(this);
332 $el.css('height', 'auto');
333 var top = Math.round($el.offset().top);
334 if (!rows[top]) rows[top] = [];
335 rows[top].push($el);
336 });
337 Object.keys(rows).forEach(function(key){
338 var maxH = 0;
339 rows[key].forEach(function($el){ maxH = Math.max(maxH, $el.outerHeight()); });
340 rows[key].forEach(function($el){ $el.height(maxH); });
341 });
342 }
343
344 function adjustHeights(){
345 if($(window).width() > 766.98){
346 equalizeRowHeights($('.list-thematic-collection-section .title-standard'));
347 } else {
348 $('.list-thematic-collection-section .title-standard').css('height', 'auto');
349 }
350 }
351
352 $(function(){
353 $('.list-thematic-collection-section .standard-button').off('click.ae').on('click.ae', function(e){
354 var btn = this;
355 var run = function(){ return onAdd(e); };
356 var helper = window.ecomGlobalScripts?.functions?.withButtonSpinner;
357 if (typeof helper === 'function') {
358 return helper(btn, run, { spinnerClass: 'spinner-border spinner-border-sm text-light', mode: 'replace' });
359 }
360 return run();
361 });
362 adjustHeights();
363 $(window).off('load.standards').on('load.standards', adjustHeights);
364 if (document.fonts && document.fonts.ready) {
365 document.fonts.ready.then(adjustHeights);
366 }
367 setTimeout(adjustHeights, 250);
368 $(window).off('resize.standards').on('resize.standards', adjustHeights);
369 });
370})(jQuery);
371</script>










