Using Artificial Intelligence (AI) on ASTM standards and related intellectual property is prohibited. Violations will result in suspension of access.
Escribe aquí el texto del aviso...test1
Escribe aquí el texto del aviso...test5 (no permite cerrarlo)
Escribe aquí el texto del aviso...test2
Escribe aquí el texto del aviso...test3
DIN EN ISO 10439-2:2015-05
Petroleum, petrochemical and natural gas industries - Axial and centrifugal compressors and expander-compressors - Part 2: Non-integrally geared centrifugal and axial compressors (ISO 10439-2:2015); English version EN ISO 10439-2:2015
Se ha producido un error al procesar la plantilla.
The following has evaluated to null or missing:
==> languageEntries?filter(entry -> entry.key == keyValue)?first [in template "34352066712900#33336#null" at line 169, column 30]
----
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 match = languageEntries?filte... [in template "34352066712900#33336#null" in function "addAllMatchingLanguagesByField" at line 169, column 13]
----
1<#-- Variables -->
2<#assign isDebug = false>
3<#assign channelResponse = restClient.get("/headless-commerce-delivery-catalog/v1.0/channels?filter=name eq 'Aenor Tienda'")>
4<#assign channel = channelResponse.items[0]>
5<#assign channelId = channel.id>
6<#assign product = getProduct(channelId, CPDefinition_cProductId.getData()) />
7<#assign siteGroup = themeDisplay.getSiteGroup() />
8<#assign currentLocale = themeDisplay.getLocale()>
9<#assign currentLanguage = currentLocale?substring(0,2)>
10
11<#-- Product data -->
12<#assign displayDateProduct = CPDefinition_displayDate.getData() />
13<#assign productId = product.productId />
14<#assign cpDefinitionId = product.id />
15
16<#assign categoriesProduct = getProductCategories(channelId, productId) />
17<#assign hasProductCategoriaTipoEntidadLibro = isVocabularyNameIntoCategories(categoriesProduct, 'entity type', 'libro') />
18<#assign hasProductCategoriaTipoEntidadNorma = isVocabularyNameIntoCategories(categoriesProduct, 'entity type', 'norma') />
19<#assign hasProductCategoriaTipoEntidadColeccionTematica = isVocabularyNameIntoCategories(categoriesProduct, 'entity type', 'coleccion tematica') />
20<#assign hasProductCategoriaTipoOrganismoSAE = isVocabularyNameIntoCategories(categoriesProduct, 'organismos', 'SAE') />
21<#assign hasProductCategoriaTipoOrganismoASTM = isVocabularyNameIntoCategories(categoriesProduct, 'organismos', 'ASTM') />
22
23<#assign categoryProductInfoTematica = getCategoriesByVocabularyAsString(categoriesProduct, "temáticas", " / ", "title") />
24<#assign categoryProductInfoStatus = getCategoriesByVocabularyAsString(categoriesProduct, "status", " / ", "title") />
25
26<#-- Standard Warning -->
27<#assign standardWarning = getStandardWarning(product.externalReferenceCode) />
28
29<#-- PickList de languages -->
30<#assign ercOfListTypeEntryLanguages = 'IDIOMAS_NORMAS_PICKLIST' />
31<#assign languageEntries = getListTypeEntriesByERC(ercOfListTypeEntryLanguages) />
32
33<#-- Specifications/Specifications language -->
34<#assign specificationsLanguagesProduct = getSpecificationsProduct(channelId, productId, 'specificationKey', 'standard-languages') />
35<#assign filteredSpecificationsLanguagesProductTemp = filterOutItems(specificationsLanguagesProduct, 'value', ['BI', 'TR']) />
36<#assign filteredSpecificationsLanguagesProduct = addAllMatchingLanguagesByField(filteredSpecificationsLanguagesProductTemp, languageEntries, 'value', 'title') />
37
38<#-- Specifications/Specifications others -->
39<#assign specificationsCTNProduct = getSpecificationsProduct(channelId, productId, 'specificationKey', 'ctn') />
40<#assign specificationsICSProduct = getSpecificationsProduct(channelId, productId, 'specificationKey', 'ics') />
41<#assign specificationsCurrentStateDateProduct = getSpecificationsProduct(channelId, productId, 'specificationKey', 'current-state-date') />
42
43<#-- Standard/Norma data -->
44<#assign standardRelationsProduct = getStandardRelationsProduct(cpDefinitionId) />
45<#assign standardInfoProduct = getStandardInfoProduct(cpDefinitionId) />
46
47<#assign ercOfListTypeEntryTipoRelacionesNormasSections = 'TIPO_RELACIONES_NORMAS-SECTIONS' />
48<#assign tipoRelacionesNormasSectionsEntries = getListTypeEntriesByERC(ercOfListTypeEntryTipoRelacionesNormasSections) />
49<#assign ercOfListTypeEntryTipoRelacionesNormasTypes = 'TIPO_RELACIONES_NORMAS-TYPE' />
50<#assign tipoRelacionesNormasTypesEntries = getListTypeEntriesByERC(ercOfListTypeEntryTipoRelacionesNormasTypes) />
51<#assign standardRelationsTypesProduct = getStandardRelationsTypesProduct(tipoRelacionesNormasSectionsEntries, tipoRelacionesNormasTypesEntries) />
52
53
54<#-- Functions -->
55<#function getProduct channelId productId>
56 <#return restClient.get("/headless-commerce-delivery-catalog/v1.0/channels/${channelId}/products/${productId}")>
57</#function>
58
59
60<#function getProductByERC erc>
61 <#-- TODO: estamos usando el endpoint del product admin, hay que usar la del Product de Liferay NO admin: headless-commerce-delivery-catalog -->
62 <#-- <#return restClient.get("/headless-commerce-admin-catalog/v1.0/products/by-externalReferenceCode/${erc}")> -->
63 <#assign response = restClient.get("/headless-commerce-admin-catalog/v1.0/products/by-externalReferenceCode/${erc}")>
64 <#-- Si el producto no existe o tiene status NOT_FOUND, devolvemos string vacío -->
65 <#if response?? && response.status?? && response.status == "NOT_FOUND">
66 <#return {}>
67 <#elseif !response??>
68 <#return {}>
69 <#else>
70 <#return response>
71 </#if>
72</#function>
73
74
75<#function getURLsOfProduct product baseURL="" siteFriendlyURL="" languageFieldName="language" urlFieldName="url">
76 <#-- Inicializamos un array vacío -->
77 <#assign urlsArray = []>
78
79 <#-- Validamos que product y product.urls existan -->
80 <#if product?? && product.urls??>
81 <#-- Iteramos sobre los idiomas -->
82 <#list product.urls?keys as lang>
83 <#assign urlValue = product.urls[lang] />
84 <#if urlValue?? && urlValue?has_content>
85 <#-- Aseguramos que la URL empiece con "/p/" -->
86 <#assign cleanUrl = urlValue?starts_with("/")?then(urlValue, "/p/" + urlValue) />
87
88 <#-- Generamos la URL completa -->
89 <#-- Si baseURL tiene contenido, construimos URL completa -->
90 <#if baseURL?? && baseURL?has_content>
91
92 <#-- Aseguramos que el slug empiece con /p/ -->
93 <#assign cleanUrl = urlValue?starts_with("/")?then(urlValue, "/p/" + urlValue) />
94
95 <#-- Tomamos las dos primeras letras del idioma para el prefijo -->
96 <#assign langPrefix = lang?substring(0,2)>
97
98 <#-- Construimos la URL completa -->
99 <#if siteFriendlyURL?? && siteFriendlyURL?has_content>
100 <#assign fullUrl = baseURL + "/" + langPrefix + siteFriendlyURL + cleanUrl>
101 <#else>
102 <#assign fullUrl = baseURL + "/" + langPrefix + cleanUrl>
103 </#if>
104
105 <#else>
106 <#assign fullUrl = cleanUrl>
107 </#if>
108
109 <#-- Creamos el objeto language+url -->
110 <#assign newItem = {(languageFieldName): lang, (urlFieldName): fullUrl} />
111
112 <#-- Lo agregamos al array -->
113 <#assign urlsArray = urlsArray + [newItem] />
114 </#if>
115 </#list>
116 </#if>
117
118 <#return urlsArray>
119</#function>
120
121
122<#function getProductCategories channelId productId>
123 <#return restClient.get("/headless-commerce-delivery-catalog/v1.0/channels/${channelId}/products/${productId}/categories?sort=vocabulary").items>
124</#function>
125
126
127<#function getSpecificationsProduct channelId productId field="" value="">
128 <#assign response = restClient.get("/headless-commerce-delivery-catalog/v1.0/channels/${channelId}/products/${productId}/product-specifications?pageSize=100&sort")>
129 <#assign items = response.items>
130
131 <#-- Si se pasa campo y valor, filtrar -->
132 <#if field?has_content && value?has_content>
133 <#assign items = items?filter(item ->
134 (item[field]??) && (item[field]?string?lower_case == value?lower_case)
135 )>
136 </#if>
137
138 <#return items>
139</#function>
140
141
142<#function filterOutItems items field valuesToExclude>
143 <#-- Si el array o los valores no existen, devolver items sin cambios -->
144 <#if !items?? || !valuesToExclude??>
145 <#return items>
146 </#if>
147
148 <#-- Filtramos: solo mantener los items cuyo campo no esté en la lista -->
149 <#assign filteredItems = items?filter(item ->
150 !(item[field]?? && (valuesToExclude?seq_contains(item[field]?string)))
151 )>
152
153 <#return filteredItems>
154</#function>
155
156
157<#function addAllMatchingLanguagesByField specificationsLanguagesProduct languageEntries specValueField newSpecField>
158
159 <#if specificationsLanguagesProduct?? && languageEntries?? && specValueField?? && newSpecField??>
160
161 <#-- Creamos una copia para no modificar el original directamente -->
162 <#assign updatedSpecs = []>
163
164 <#list specificationsLanguagesProduct as spec>
165 <#-- Buscar coincidencia -->
166 <#assign keyValue = spec[specValueField]>
167 <#assign match = languageEntries?filter(entry -> entry.key == keyValue)?first>
168
169 <#-- Crear una copia del objeto actual -->
170 <#assign newSpec = spec>
171
172 <#-- Si hay match con nombre válido, añadir el nuevo campo -->
173 <#if match?? && match.name?? && match.name?has_content>
174 <#assign newSpec = newSpec + { (newSpecField): match.name }>
175 </#if>
176
177 <#-- Añadir al array final -->
178 <#assign updatedSpecs = updatedSpecs + [newSpec]>
179 </#list>
180
181 <#return updatedSpecs>
182 <#else>
183 <#return specificationsLanguagesProduct>
184 </#if>
185</#function>
186
187
188<#function getListTypeEntriesByERC erc fields="key,name,externalReferenceCode,name_i18n" sort="key" pageSize="1000">
189 <#attempt>
190 <#return restClient.get(
191 "/headless-admin-list-type/v1.0/list-type-definitions/by-external-reference-code/${erc}/list-type-entries?fields=${fields}&sort=${sort}&pageSize=${pageSize}"
192 ).items>
193 <#recover>
194 <#return []>
195 </#attempt>
196</#function>
197
198
199<#function getStandardRelationsProduct cpDefinitionId
200 ercProductFieldName="ercProduct" defaultLang = "es_ES" urlProductCurrentLanguageFieldName="urlProductCurrentLanguage" urlsProductByLangFieldName="urlsProductByLang"
201 languageFieldName="language" urlFieldName="url" sort="type">
202
203 <#assign result = [] />
204
205 <#-- Obtener relaciones desde la API -->
206 <#assign standardRelations = restClient.get("/c/standardrelationses/?filter=r_standardRelations_CPDefinitionId eq '${cpDefinitionId}'&sort=${sort}").items />
207
208 <#-- Iterar sobre cada relación -->
209 <#list standardRelations as standardRelationProduct>
210
211 <#-- Crear ERC del producto relacionado -->
212 <#assign ercProduct = (standardRelationProduct.relatedOrganismStandardName!"relatedOrganismStandardName") + "-" + (standardRelationProduct.relatedStandardId!"relatedStandardId") />
213
214 <#-- Obtener el producto por ERC -->
215 <#assign productByERC = getProductByERC(ercProduct) />
216 <#assign findProductByERC = (productByERC?? && productByERC?has_content)>
217
218 <#-- Obtener URLs del producto -->
219 <#assign baseURL = themeDisplay.getPortalURL()>
220 <#assign siteFriendlyURL = "/web" + siteGroup.getFriendlyURL()>
221 <#assign urlsProductByLang = getURLsOfProduct(productByERC, baseURL, siteFriendlyURL, languageFieldName, urlFieldName) />
222
223 <#-- Obtenemos URLs del producto default y current language -->
224 <#assign selectedURLCurrent = "">
225 <#assign selectedURLDefault = "">
226 <#list urlsProductByLang as item>
227 <#-- URL del idioma actual -->
228 <#if item.language == currentLocale && selectedURLCurrent == "">
229 <#assign selectedURLCurrent = item.url>
230 </#if>
231
232 <#-- URL del idioma por defecto -->
233 <#if item.language == defaultLang && selectedURLDefault == "">
234 <#assign selectedURLDefault = item.url>
235 </#if>
236
237 <#-- Salimos si ya tenemos ambas -->
238 <#if selectedURLCurrent != "" && selectedURLDefault != "">
239 <#break>
240 </#if>
241 </#list>
242
243 <#-- Usamos el idioma actual si existe, sino el por defecto -->
244 <#if selectedURLCurrent != "">
245 <#assign urlCurrentLanguage = selectedURLCurrent>
246 <#else>
247 <#assign urlCurrentLanguage = selectedURLDefault>
248 </#if>
249
250 <#-- Combinar todos los datos del objeto original + nuevos campos -->
251 <#assign resultItem = standardRelationProduct + {
252 (ercProductFieldName): ercProduct,
253 (urlProductCurrentLanguageFieldName): urlCurrentLanguage,
254 (urlsProductByLangFieldName): urlsProductByLang,
255 ("findProductByERC"): findProductByERC
256 } />
257
258 <#-- Añadir al array final -->
259 <#assign result = result + [resultItem] />
260 </#list>
261
262 <#return result>
263</#function>
264
265
266<#function getStandardInfoProduct cpDefinitionId>
267 <#assign response = restClient.get("/c/standardinfos/?filter=r_standardInfo_CPDefinitionId eq '${cpDefinitionId}'")!{} />
268 <#assign items = response.items![] />
269 <#return (items?size > 0)?then(items[0], {}) />
270</#function>
271
272<#function getStandardWarning externalReferenceCode>
273 <#assign response = restClient.get("/c/standardwarnings/?fields=warningTitle,warningDescription&filter=standardId eq '${externalReferenceCode}' and showWarning eq true")!{} />
274 <#assign items = response.items![] />
275 <#return (items?size > 0)?then(items[0], {}) />
276</#function>
277
278<#function getStandardRelationsTypesProduct tipoRelacionesNormasSectionsEntries tipoRelacionesNormasTypesEntries>
279 <#attempt>
280
281 <#-- Obtener los items del endpoint -->
282 <#assign itemsStandardRelationsTypeProduct = restClient.get("/c/standardrelationtypeses/?pageSize=100").items>
283
284 <#-- Crear array vacío para ir acumulando los items enriquecidos -->
285 <#assign enrichedItemsStandardRelationsTypeProduct = []>
286
287 <#assign enrichedItemsStandardRelationsTypeProduct = applyTipoRelacionesNormasEntries(
288 itemsStandardRelationsTypeProduct, tipoRelacionesNormasSectionsEntries,
289 "relatedSection", "section"
290 )>
291
292 <#assign enrichedItemsStandardRelationsTypeProduct = applyTipoRelacionesNormasEntries(
293 enrichedItemsStandardRelationsTypeProduct, tipoRelacionesNormasTypesEntries,
294 "relatedType", "type"
295 )>
296
297 <#assign enrichedItemsStandardRelationsTypeProduct = addKeysFieldNested(
298 enrichedItemsStandardRelationsTypeProduct, "section", "name", "sectionName", "all")
299 >
300
301 <#assign enrichedItemsStandardRelationsTypeProduct = addKeysFieldNested(
302 enrichedItemsStandardRelationsTypeProduct, "section", "key", "sectionKey", "first")
303 >
304
305 <#assign enrichedItemsStandardRelationsTypeProduct = addKeysFieldNested(
306 enrichedItemsStandardRelationsTypeProduct, "section", "externalReferenceCode", "sectionKeyERC", "first", "relatedSection")
307 >
308
309 <#assign enrichedItemsStandardRelationsTypeProduct = addKeysFieldNested(
310 enrichedItemsStandardRelationsTypeProduct, "type", "name", "typeName", "all")
311 >
312
313 <#assign enrichedItemsStandardRelationsTypeProduct = addKeysFieldNested(
314 enrichedItemsStandardRelationsTypeProduct, "type", "key", "typeKey", "all")
315 >
316
317 <#assign enrichedItemsStandardRelationsTypeProduct = addKeysFieldNested(
318 enrichedItemsStandardRelationsTypeProduct, "type", "externalReferenceCode", "typeKeyERC", "all", "relatedType")
319 >
320
321 <#assign enrichedItemsStandardRelationsTypeProductSorted = sortByField(enrichedItemsStandardRelationsTypeProduct, "section?first.key")>
322 <#return enrichedItemsStandardRelationsTypeProductSorted>
323
324 <#recover>
325 <#return []>
326 </#attempt>
327</#function>
328
329
330<#function applyTipoRelacionesNormasEntries
331 itemsStandardRelationsTypeProduct tipoRelacionesNormasEntries
332 relatedEntriesFieldName="relatedEntries" putRelatiedEntriesFieldName="entries"
333 >
334
335 <#assign enrichedItems = []>
336
337 <#list itemsStandardRelationsTypeProduct as item>
338
339 <#assign relatedEntries = []>
340
341 <#list item[putRelatiedEntriesFieldName] as sec>
342 <#assign matchedEntry = {}>
343
344 <#list tipoRelacionesNormasEntries as te>
345 <#if te.key?string?trim == sec.key?string?trim>
346 <#assign matchedEntry = te>
347 <#break>
348 </#if>
349 </#list>
350
351 <#assign relatedEntry = []>
352 <#if matchedEntry?has_content>
353 <#assign relatedEntry = [ matchedEntry ]>
354 </#if>
355
356 <#assign relatedItem = sec + { (relatedEntriesFieldName): relatedEntry }>
357 <#assign relatedEntries = relatedEntries + [ relatedItem ]>
358
359 </#list>
360
361 <#assign enrichedItem = item + {
362 (putRelatiedEntriesFieldName): relatedEntries
363 }>
364
365 <#assign enrichedItems = enrichedItems + [ enrichedItem ]>
366
367 </#list>
368
369 <#return enrichedItems>
370</#function>
371
372
373<#function sortByField items fieldPath sortFieldName="sortKey" order="asc">
374
375 <#-- Array temporal con el campo auxiliar -->
376 <#assign prepared = []>
377
378 <#list items as e>
379 <#-- Evaluar el valor del campo dinámico -->
380 <#assign sortValue = "" />
381 <#if fieldPath == "section?first.key">
382 <#assign sortValue = (e.section?first.key)!""?lower_case>
383 <#elseif fieldPath == "section?first.name">
384 <#assign sortValue = (e.section?first.name)!""?lower_case>
385 <#elseif fieldPath == "type?first.key">
386 <#assign sortValue = (e.type?first.key)!""?lower_case>
387 <#else>
388 <#-- Si el campoPath no está mapeado, usar vacío -->
389 <#assign sortValue = "" />
390 </#if>
391
392 <#-- Agregar objeto enriquecido con campo auxiliar -->
393 <#assign prepared = prepared + [ e + { (sortFieldName): sortValue } ]>
394 </#list>
395
396 <#-- Ordenar -->
397 <#assign sorted = prepared?sort_by(sortFieldName)>
398
399 <#-- Si order = desc, invertir -->
400 <#if order?lower_case == "desc">
401 <#assign sorted = sorted?reverse>
402 </#if>
403
404 <#return sorted>
405</#function>
406
407
408<#function addKeysFieldNested items arrayField nestedField newFieldName mode="first" subArrayField="">
409
410 <#assign enrichedItems = []>
411
412 <#list items as e>
413 <#assign resultValue = "">
414
415 <#-- Detectamos el array del objeto principal -->
416 <#assign targetArray = e[arrayField]![]>
417
418 <#if targetArray?has_content>
419 <#if mode == "all">
420 <#assign keysList = []>
421 <#list targetArray as t>
422 <#if subArrayField?has_content>
423 <#assign subArray = t[subArrayField]![]>
424 <#if subArray?has_content>
425 <#list subArray as sub>
426 <#assign keysList = keysList + [ sub[nestedField]!"" ]>
427 </#list>
428 </#if>
429 <#else>
430 <#assign keysList = keysList + [ t[nestedField]!"" ]>
431 </#if>
432 </#list>
433 <#assign resultValue = keysList?join(", ")>
434 <#elseif mode == "first">
435 <#assign firstItem = targetArray?first>
436 <#if subArrayField?has_content>
437 <#assign subArray = firstItem[subArrayField]![]>
438 <#if subArray?has_content>
439 <#assign resultValue = subArray?first[nestedField]!"" >
440 <#else>
441 <#assign resultValue = "" >
442 </#if>
443 <#else>
444 <#assign resultValue = firstItem[nestedField]!"" >
445 </#if>
446 </#if>
447 <#else>
448 <#assign resultValue = "">
449 </#if>
450
451 <#-- Añadimos el nuevo campo al objeto -->
452 <#assign enrichedItems = enrichedItems + [
453 e + { (newFieldName): resultValue }
454 ]>
455 </#list>
456
457 <#return enrichedItems>
458</#function>
459
460
461<#function isVocabularyNameIntoCategories categories vocabulary name>
462 <#assign found = false />
463
464 <#if categories?has_content && vocabulary?has_content && name?has_content>
465
466 <#assign vocabNorm = normalize(vocabulary) />
467 <#assign nameNorm = normalize(name) />
468
469 <#list categories as category>
470 <#if !found>
471 <#assign catVocabNorm = normalize(category.vocabulary) />
472 <#assign catNameNorm = normalize(category.name) />
473
474 <#if catVocabNorm == vocabNorm && catNameNorm == nameNorm>
475 <#assign found = true />
476 </#if>
477 </#if>
478 </#list>
479
480 </#if>
481
482 <#return found>
483</#function>
484
485
486<#function normalize text onlyAccents = false>
487 <#-- proteger null -->
488 <#if !text?has_content>
489 <#return "">
490 </#if>
491
492 <#assign t = text />
493
494 <#-- quitar acentos -->
495 <#assign t = t
496 ?replace("á","a")?replace("é","e")?replace("í","i")
497 ?replace("ó","o")?replace("ú","u")?replace("ü","u")
498 ?replace("ñ","n")
499 ?replace("Á","A")?replace("É","E")?replace("Í","I")
500 ?replace("Ó","O")?replace("Ú","U")?replace("Ü","U")
501 ?replace("Ñ","N")
502 />
503
504 <#-- si NO es solo acentos, normalización completa -->
505 <#if !onlyAccents>
506 <#assign t = t?lower_case />
507 <#assign t = t?trim />
508 <#assign t = t?replace("\\s+", " ", "r") />
509 </#if>
510
511 <#return t>
512</#function>
513
514
515<#function filterStandardRelationsProductsByFieldType standardRelationsProducts types>
516 <#-- Función para filtrar y obtener elementos por type -->
517 <#-- Normalizamos types a lista en minúsculas -->
518 <#if types?is_string>
519 <#assign typeList = [types?lower_case]>
520 <#else>
521 <#assign typeList = types?map(t -> t?lower_case)>
522 </#if>
523
524 <#assign result = []>
525
526 <#list standardRelationsProducts as standardRelationsProduct>
527 <#if standardRelationsProduct.type?? && typeList?seq_contains(standardRelationsProduct.type?lower_case)>
528 <#assign result += [standardRelationsProduct]>
529 </#if>
530 </#list>
531
532 <#return result>
533</#function>
534
535
536<#function excludeStandardRelationsByFieldType standardRelationsProducts types>
537 <#-- Función para filtrar y quitar elementos por type -->
538 <#-- Normalizamos types a lista en minúsculas -->
539 <#if types?is_string>
540 <#assign typeList = [types?lower_case]>
541 <#else>
542 <#assign typeList = types?map(t -> t?lower_case)>
543 </#if>
544
545 <#assign result = []>
546
547 <#list standardRelationsProducts as standardRelationsProduct>
548 <#-- Solo agregamos si el type NO está en la lista -->
549 <#if !(standardRelationsProduct.type?? && typeList?seq_contains(standardRelationsProduct.type?lower_case))>
550 <#assign result += [standardRelationsProduct]>
551 </#if>
552 </#list>
553
554 <#return result>
555</#function>
556
557
558
559<#function getStandardRelationsProductsByField
560 itemStandardRelationsTypeProduct standardRelationsProducts itemStandardRelationsTypeProductField="typeKeyERC" standardRelationsProductField="type">
561
562 <#assign matched = []>
563
564 <#-- Verificar que el item tenga el campo indicado -->
565 <#if itemStandardRelationsTypeProduct[itemStandardRelationsTypeProductField]?? && itemStandardRelationsTypeProduct[itemStandardRelationsTypeProductField]?has_content>
566
567 <#-- Normalizamos (minusculas, sin espacios a al principio/final y separamos en lista si hay varios valores) -->
568 <#assign itemStandardRelationsTypeProductValues = itemStandardRelationsTypeProduct[itemStandardRelationsTypeProductField]?split(",")?map(v -> v?trim?lower_case)>
569
570 <#-- Recorrer los productos -->
571 <#list standardRelationsProducts as standardRelationsProduct>
572 <#-- Asegurar que el campo del producto existe -->
573 <#if standardRelationsProduct[standardRelationsProductField]?? && standardRelationsProduct[standardRelationsProductField]?has_content>
574
575 <#--
576 - Los elementos de [standardRelationsProducts.type] tienen datos como: REVISED_BY
577 - En los PickList, en este caso la picklist [erc: TIPO_RELACIONES_NORMAS], no permite guardar
578 keys con "_" por lo que existirá una key: REVISEDBY pero entonces comparamos por su
579 campo ERC que si permite "_", entonces tendremos como ERC: REVISED_BY y como key: REVISEDBY.
580 - Entonces buscaremos, usando de la picklist, la key (el ERC) del tipo de relación [itemStandardRelationsTypeProduct.typeKeyERC]
581 en [standardRelationsProducts.type].
582 - Convertimos el texto a minúsculas y quitamos los espacio del principio/fin.
583 - Dentro de [itemStandardRelationsTypeProduct.typeKeyERC] podemos tener
584 varios valores, por ejemplo [typeKey: REPLACED_BY, REPLACEDBY].
585 -->
586 <#assign normalizedProdValue = standardRelationsProduct[standardRelationsProductField]?trim?lower_case>
587
588 <#--
589 Verificar si coincide alguno de los valores del item StandardRelationsTypeProduct con
590 el valor normalizado de standardRelationsProduct
591 -->
592 <#if itemStandardRelationsTypeProductValues?seq_contains(normalizedProdValue)>
593 <#assign matched = matched + [standardRelationsProduct]>
594 </#if>
595 </#if>
596 </#list>
597 </#if>
598
599 <#return matched>
600</#function>
601
602
603<#-- Función que retorna los valores de categorías como string, normalizando vocabulario -->
604<#function getCategoriesByVocabularyAsString categories vocabulary separator=" / " field="name">
605 <#assign matchedValues = []>
606 <#if categories?has_content && vocabulary?has_content>
607 <#list categories as category>
608 <#-- Normalizamos tanto el vocabulary de la categoría como el buscado -->
609 <#if normalize(category.vocabulary, true)?lower_case == normalize(vocabulary, true)?lower_case>
610 <#if field == "title">
611 <#assign matchedValues += [category.title]>
612 <#else>
613 <#assign matchedValues += [category.name]>
614 </#if>
615 </#if>
616 </#list>
617 </#if>
618 <#return matchedValues?join(separator)>
619</#function>
620
621
622<#function getStatusInfo status class="">
623 <#local defaultResult = {
624 "status": status!"",
625 "tagClass": "",
626 "isInForce": false,
627 "isCancelled": false,
628 "isProject": false
629 }>
630
631 <#local trimmedStatus = (status!"")?trim>
632 <#if trimmedStatus == "">
633 <#return defaultResult>
634 </#if>
635
636 <#local normalizedStatus = trimmedStatus?upper_case>
637 <#local tagClass = "">
638 <#if class?has_content>
639 <#local tagClass = class>
640 </#if>
641
642 <#local isInForce = false>
643 <#local isCancelled = false>
644 <#local isProject = false>
645
646 <#if ["EN VIGOR", "IN FORCE", "EM VIGOR", "IN VIGORE"]?seq_contains(normalizedStatus)>
647 <#local tagClass = tagClass + " tag-success">
648 <#local isInForce = true>
649 <#elseif ["ANULADA", "CANCELLED", "ANULADA", "ANNULLATA"]?seq_contains(normalizedStatus)>
650 <#local tagClass = tagClass + " tag-danger">
651 <#local isCancelled = true>
652 <#elseif ["PROYECTO", "PROJECT", "PROJETO", "PROGETTO"]?seq_contains(normalizedStatus)>
653 <#local tagClass = tagClass + " tag-blue">
654 <#local isProject = true>
655 </#if>
656
657 <#return {
658 "status": status,
659 "tagClass": tagClass?trim,
660 "isInForce": isInForce,
661 "isCancelled": isCancelled,
662 "isProject": isProject
663 }>
664</#function>
665
666
667<#macro printObject obj>
668 <#-- Permite hacer un output de un array de objetos o un objeto que se pasa como parámetro -->
669 <#if obj?is_hash>
670 {
671 <#list obj?keys as k>
672 "${k}":
673 <#assign value = obj[k]>
674 <#if value?is_hash || value?is_sequence>
675 <@printObject obj=value/>
676 <#elseif value?is_boolean>
677 ${value?c}
678 <#elseif value?is_number>
679 ${value}
680 <#elseif value?has_content>
681 "${value?string}"
682 <#else>
683 null
684 </#if>
685 <#if k_has_next>, </#if>
686 </#list>
687 }
688 <#elseif obj?is_sequence>
689 [
690 <#list obj as item>
691 <@printObject obj=item/>
692 <#if item_has_next>, </#if>
693 </#list>
694 ]
695 <#elseif obj?is_boolean>
696 ${obj?c}
697 <#elseif obj?is_number>
698 ${obj}
699 <#elseif obj?has_content>
700 "${obj?string}"
701 <#else>
702 null
703 </#if>
704</#macro>
705
706
707<#macro renderStandardRelationsSectionsRows standardRelationsTypesProduct standardRelationsProduct typesToExclude=[] isDebug=false>
708
709 <#assign lastSectionKey = "">
710 <#assign openRow = false>
711 <#assign indexCount = 0>
712
713 <#assign filteredStandardRelationsProduct = standardRelationsProduct >
714 <#if typesToExclude?has_content>
715 <#assign filteredStandardRelationsProduct = excludeStandardRelationsByFieldType(standardRelationsProduct, typesToExclude)>
716 </#if>
717
718 <#list standardRelationsTypesProduct as standardRelationsTypeProduct>
719
720 <#assign currentSectionKey = (standardRelationsTypeProduct.sectionKey)!"" />
721 <#assign standardRelationsProductsByTypeKey = getStandardRelationsProductsByField(standardRelationsTypeProduct, filteredStandardRelationsProduct)>
722 <#assign hasElementsStandardRelationsProducts = (standardRelationsProductsByTypeKey?size > 0)>
723
724 <#if isDebug || hasElementsStandardRelationsProducts>
725
726 <#-- Si la sección cambia, cerramos la fila anterior -->
727 <#if openRow && currentSectionKey != lastSectionKey>
728 </td>
729 </tr>
730 <#assign openRow = false>
731 </#if>
732
733 <#-- Si es una nueva sección, abrimos una nueva fila -->
734 <#if !openRow>
735 <#assign indexCount = 0>
736 <tr
737 data-section-key="${currentSectionKey}"
738 data-section-name="${standardRelationsTypeProduct.sectionName}"
739 data-sort-key="${standardRelationsTypeProduct.sortKey}">
740 <th>
741 <p>${standardRelationsTypeProduct.sectionName}</p>
742 </th>
743 <td data-section-key="${currentSectionKey}">
744 <#assign openRow = true>
745 </#if>
746
747 <#if isDebug>
748 <div class="">
749 <p><strong>typeKey:</strong> ${standardRelationsTypeProduct.typeKey}</p>
750 <p><strong>sectionKey:</strong> ${standardRelationsTypeProduct.sectionKey}</p>
751 <p><strong>sectionName:</strong> ${standardRelationsTypeProduct.sectionName}</p>
752 <p><strong>sortKey:</strong> ${standardRelationsTypeProduct.sortKey}</p>
753 <p><strong>total standardRelationsProductsByTypeKey[${standardRelationsTypeProduct.typeKey}]: </strong> ${standardRelationsProductsByTypeKey?size}</p>
754
755 <p class="mb-0"><strong>standardRelationsTypeProduct:</strong></p>
756 <div class="print-object-json-content mb-3"
757 style="max-height:200px; overflow:auto; border:1px solid #ccc; padding:5px; cursor:pointer;"
758 onclick="const r=document.createRange(); r.selectNodeContents(this); const s=window.getSelection(); s.removeAllRanges(); s.addRange(r);">
759 <@printObject standardRelationsTypeProduct />
760 </div>
761
762 <p class="mb-0"><strong>standardRelationsProductsByTypeKey:</strong></p>
763 <div class="print-object-json-content mb-3"
764 style="max-height:200px; overflow:auto; border:1px solid #ccc; padding:5px; cursor:pointer;"
765 onclick="const r=document.createRange(); r.selectNodeContents(this); const s=window.getSelection(); s.removeAllRanges(); s.addRange(r);">
766 <@printObject standardRelationsProductsByTypeKey />
767 </div>
768 </div>
769 </#if>
770
771 <#if standardRelationsProductsByTypeKey?has_content>
772 <#list standardRelationsProductsByTypeKey as standardRelationProductsByTypeKey>
773 <#assign indexCount = indexCount + 1>
774 <#assign urlProductCurrentLanguage = standardRelationProductsByTypeKey.urlProductCurrentLanguage>
775
776 <p data-section-key="${currentSectionKey}"
777 data-type-key="${standardRelationsTypeProduct.typeKey}" data-type-index="${indexCount}">
778
779 ${standardRelationsTypeProduct.description}
780
781 <#--
782 <#if urlProductCurrentLanguage?? && urlProductCurrentLanguage?has_content>
783 <a href="${urlProductCurrentLanguage}" target="_blank">
784 ${standardRelationProductsByTypeKey.relatedStandardName!""}
785 </a>
786 <#else>
787 <a class="add-link-product-by-erc" data-product-erc="${standardRelationProductsByTypeKey.ercProduct}" href="" target="_blank">
788 ${standardRelationProductsByTypeKey.relatedStandardName!""}
789 </a>
790 </#if>
791 -->
792
793 <a class="add-link-product-by-erc text-decoration-none text-body" data-product-erc="${standardRelationProductsByTypeKey.ercProduct}" href="">
794 ${standardRelationProductsByTypeKey.relatedStandardName!""}
795 </a>
796
797 </p>
798
799 </#list>
800
801 <#if isDebug>
802 <p>
803 <strong>Info:</strong>SI hay (<strong>${standardRelationsProductsByTypeKey?size}</strong>) <strong>Standard/Norma Relation</strong> con el type [<strong>typeKey:</strong> ${standardRelationsTypeProduct.typeKey}]
804 en la sección [<strong>sectionKey:</strong> ${standardRelationsTypeProduct.sectionKey}]
805 </p>
806 </#if>
807 <#else>
808 <#if isDebug>
809 <p>
810 <strong>Info:</strong>NO hay <strong>Standard/Norma Relation</strong> con el type [<strong>typeKey:</strong> ${standardRelationsTypeProduct.typeKey}]
811 en la sección [<strong>sectionKey:</strong> ${standardRelationsTypeProduct.sectionKey}]
812 </p>
813 <#else>
814 <p>N/A</p>
815 </#if>
816 </#if>
817
818 <#-- Separador visual en debug entre elementos standardRelations dentro del mismo section -->
819 <#if isDebug>
820 <#-- Separador visual mejorado -->
821 <div style="margin:10px 0; padding:5px; border-top:2px dashed #007BFF; background-color:#f0f8ff;"></div>
822 </#if>
823
824 <#assign lastSectionKey = currentSectionKey>
825 </#if>
826 </#list>
827
828 <#-- Cerrar la última fila abierta -->
829 <#if openRow>
830 </td>
831 </tr>
832 </#if>
833
834</#macro>
835
836
837
838
839<#-- HTML -->
840<#if hasProductCategoriaTipoEntidadNorma>
841 <div id="ecom-norma-detail" class="ecom-norma">
842 <div class="tabs-section">
843 <div class="tab-content">
844
845 <table>
846
847 <#-- Custom object [StandardWarning] -->
848 <#if standardWarning?? && standardWarning?has_content>
849 <tr class="alert-row">
850 <th class="alert-label">
851 <#if standardWarning.warningTitle??>
852 <strong>${standardWarning.warningTitle}</strong>
853 </#if>
854 </th>
855 <td class="alert-content">
856 <#if standardWarning.warningDescription??>
857 ${standardWarning.warningDescription}
858 </#if>
859 </td>
860 </tr>
861 </#if>
862
863 <#-- Custom object [StandardRelations] > Buscamos si el producto tiene elementos [StandardRelations] con el campo key = ["MOD", "MODIFIED"] -->
864 <#assign findStandardRelationsProductsByTypeModified = filterStandardRelationsProductsByFieldType(standardRelationsProduct, ["MOD", "MODIFIED"])>
865 <#if findStandardRelationsProductsByTypeModified?has_content>
866 <tr class="alert-row">
867 <th class="alert-label">
868 <strong>${languageUtil.get(locale, "ecom-aviso")}</strong>:
869 </th>
870 <td class="alert-content">
871 ${languageUtil.get(locale, "ecom-aviso_modificaciones_text")}
872 </td>
873 </tr>
874 </#if>
875
876 <#-- Producto tiene [categoria: Organismo] > Si el producto tiene la [categoria: Organismo] = SAE -->
877 <#if hasProductCategoriaTipoOrganismoSAE>
878 <tr class="alert-row">
879 <th class="alert-label">
880 <span class="text-danger">
881 <strong>${languageUtil.get(locale, "ecom-aviso_pdf_secure")}</strong>:
882 </span>
883 </th>
884 <td class="alert-content">
885 ${languageUtil.get(locale, "ecom-aviso_pdf_secure_text")}
886 </td>
887 </tr>
888 </#if>
889
890 <#-- Producto tiene [categoria: Organismo] > Si el producto tiene la [categoria: Organismo] = ASTM -->
891 <#if hasProductCategoriaTipoOrganismoASTM>
892 <tr class="alert-row">
893 <th class="alert-label">
894 <strong>${languageUtil.get(locale, "ecom-aviso_astm")}</strong>
895 </th>
896 <td class="alert-content">
897 ${languageUtil.get(locale, "ecom-aviso_astm_text")}
898 </td>
899 </tr>
900 </#if>
901
902 <#-- Categoria producto > Tematicas -->
903 <#if categoryProductInfoTematica?has_content>
904 <tr>
905 <th>${languageUtil.get(locale, "ecom-tematicas")}:</th>
906 <td>${categoryProductInfoTematica}</td>
907 </tr>
908 </#if>
909
910 <#-- Fecha edicion del producto > displayDate del Producto -->
911 <#assign statusInfo = getStatusInfo(categoryProductInfoStatus, "badge status-standard")>
912 <#if statusInfo?? && !statusInfo.isCancelled>
913 <tr>
914 <th>${languageUtil.get(locale, "ecom-fecha_edicion")}:</th>
915 <td>
916
917 <#-- Comentado provisonalmente hasta cambio en integracion
918 <#if displayDateProduct?? && displayDateProduct?has_content>
919 ${displayDateProduct?datetime("d/MM/yy H:mm")?string("yyyy-MM-dd")}
920 <#else>
921 -
922 </#if>
923 -->
924
925 ${specificationsCurrentStateDateProduct
926 ?filter(spec -> spec.value??
927 && spec.value?has_content
928 && spec.value?length == 8)
929 ?map(spec -> spec.value?date("yyyyMMdd")?string("yyyy-MM-dd"))
930 ?join(", ")}
931
932 <#if categoryProductInfoStatus?? && categoryProductInfoStatus?has_content>
933 <div class="${statusInfo.tagClass}">${statusInfo.status}</div>
934 </#if>
935
936 </td>
937 </tr>
938 </#if>
939
940 <#-- Especificacion del producto > current-state-date (Nos indicaron que es la fecha de anulacion de la ficha) -->
941 <#-- Fecha de cancelacion del producto -->
942 <#if statusInfo?? && statusInfo.isCancelled>
943 <#if specificationsCurrentStateDateProduct?has_content>
944 <tr>
945 <th>${languageUtil.get(locale, "ecom-cancellationDate")}:</th>
946 <td>
947
948 ${specificationsCurrentStateDateProduct
949 ?filter(spec -> spec.value??
950 && spec.value?has_content
951 && spec.value?length == 8)
952 ?map(spec -> spec.value?date("yyyyMMdd")?string("yyyy-MM-dd"))
953 ?join(", ")}
954
955 <div class="${statusInfo.tagClass}">${statusInfo.status}</div>
956
957 </td>
958 </tr>
959 </#if>
960 </#if>
961
962 <#-- Custom object [StandardInfo] > confirmationDate -->
963 <#if standardInfoProduct?? && standardInfoProduct.confirmationDate??
964 && standardInfoProduct.confirmationDate?has_content && !standardInfoProduct.confirmationDate?starts_with("0001-01-01")>
965 <tr>
966 <th>${languageUtil.get(locale, "ecom-confirmationDate")}:</th>
967 <td>${standardInfoProduct.confirmationDate?datetime("yyyy-MM-dd'T'HH:mm:ss.SSSX")?string("yyyy-MM-dd")}</td>
968 </tr>
969 </#if>
970
971 <#-- Custom object [StandardInfo] > correctionDate -->
972 <#if standardInfoProduct?? && standardInfoProduct.correctionDate??
973 && standardInfoProduct.correctionDate?has_content && !standardInfoProduct.correctionDate?starts_with("0001-01-01")>
974 <tr>
975 <th>${languageUtil.get(locale, "ecom-correctionDate")}:</th>
976 <td>${standardInfoProduct.correctionDate?datetime("yyyy-MM-dd'T'HH:mm:ss.SSSX")?string("yyyy-MM-dd")}</td>
977 </tr>
978 </#if>
979
980 <#-- Custom object [StandardInfo] > ratificationDate -->
981 <#if standardInfoProduct?? && standardInfoProduct.ratificationDate??
982 && standardInfoProduct.ratificationDate?has_content && !standardInfoProduct.ratificationDate?starts_with("0001-01-01")>
983 <tr>
984 <th>${languageUtil.get(locale, "ecom-ratificationDate")}:</th>
985 <td>${standardInfoProduct.ratificationDate?datetime("yyyy-MM-dd'T'HH:mm:ss.SSSX")?string("yyyy-MM-dd")}</td>
986 </tr>
987 </#if>
988
989 <#-- Especificacion del producto > standard-languages (se aplica filtro para idiomas combinados) -->
990 <#if filteredSpecificationsLanguagesProduct?has_content>
991 <tr>
992 <th>${languageUtil.get(locale, "ecom-idiomas_disponibles")}:</th>
993 <td>${filteredSpecificationsLanguagesProduct?map(spec -> spec.title)?join(", ")}</td>
994 </tr>
995 </#if>
996
997 <#-- Custom object [StandardInfo] > resumen -->
998 <#if standardInfoProduct?? && standardInfoProduct.resumen?has_content>
999 <tr>
1000 <th class="th-content">${languageUtil.get(locale, "ecom-resumen")}:</th>
1001 <td class="td-content">${htmlUtil.unescape(standardInfoProduct.resumen)}</td>
1002 </tr>
1003 </#if>
1004
1005 <#-- Product > expando.keywords -->
1006 <#if product?? && product.expando?? && product.expando.keywords?has_content>
1007 <tr>
1008 <th class="th-content">${languageUtil.get(locale, "ecom-keywords")}:</th>
1009 <td class="td-content">${product.expando.keywords}</td>
1010 </tr>
1011 </#if>
1012
1013 <#-- Custom object [StandardInfo] > scope -->
1014 <#if standardInfoProduct?? && standardInfoProduct.scope?has_content>
1015 <tr>
1016 <th class="th-content">${languageUtil.get(locale, "ecom-scope")}:</th>
1017 <td class="td-content">${standardInfoProduct.scope}</td>
1018 </tr>
1019 </#if>
1020
1021 <#-- Especificacion del producto > ics -->
1022 <#if specificationsICSProduct?has_content>
1023 <tr>
1024 <th>${languageUtil.get(locale, "ecom-ics")}:</th>
1025 <td>
1026 ${specificationsICSProduct?filter(spec -> spec.value?? && spec.value?has_content)?map(spec -> spec.value)?join(", ")}
1027 </td>
1028 </tr>
1029 </#if>
1030
1031 <#-- Especificacion del producto > ctn -->
1032 <#if specificationsCTNProduct?? && specificationsCTNProduct?size gt 0>
1033 <tr>
1034 <th>${languageUtil.get(locale, "ecom-ctn")}:</th>
1035 <td>
1036 ${specificationsCTNProduct?filter(spec -> spec.value?? && spec.value?has_content)?map(spec -> spec.value)?join(", ")}
1037 </td>
1038 </tr>
1039 </#if>
1040
1041 <#-- Para imprimir el contenido de los Objects -->
1042 <#if isDebug>
1043 <div class="">
1044 <p><strong>standardRelationsProduct:</strong></p>
1045 <div class="print-object-json-content mb-3" style="max-height:200px; overflow:auto; border:1px solid #ccc; padding:5px; cursor:pointer;"
1046 onclick="const r=document.createRange(); r.selectNodeContents(this); const s=window.getSelection(); s.removeAllRanges(); s.addRange(r);">
1047 <@printObject standardRelationsProduct />
1048 </div>
1049 </div>
1050 </#if>
1051
1052 <@renderStandardRelationsSectionsRows
1053 standardRelationsTypesProduct = standardRelationsTypesProduct
1054 standardRelationsProduct = standardRelationsProduct
1055 typesToExclude = ["REFERENCE"]
1056 isDebug = isDebug
1057 />
1058
1059 </table>
1060 </div>
1061 </div>
1062 </div>
1063</#if>
Se ha producido un error al procesar la plantilla.
The following has evaluated to null or missing:
==> languageEntries?filter(entry -> entry.key == keyValue)?first [in template "34352066712900#33336#null" at line 151, column 30]
----
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 match = languageEntries?filte... [in template "34352066712900#33336#null" in function "addAllMatchingLanguagesByField" at line 151, column 13]
----
1<#-- Variables -->
2<#assign isDebug = false>
3<#assign channelResponse = restClient.get("/headless-commerce-delivery-catalog/v1.0/channels?filter=name eq 'Aenor Tienda'")>
4<#assign channel = channelResponse.items[0]>
5<#assign channelId = channel.id>
6<#assign product = getProduct(channelId, CPDefinition_cProductId.getData()) />
7<#assign siteGroup = themeDisplay.getSiteGroup() />
8<#assign currentLocale = themeDisplay.getLocale()>
9<#assign currentLanguage = currentLocale?substring(0,2)>
10
11<#-- Product data -->
12<#assign displayDateProduct = CPDefinition_displayDate.getData() />
13<#assign productId = product.productId />
14<#assign cpDefinitionId = product.id />
15
16<#assign categoriesProduct = getProductCategories(channelId, productId) />
17<#assign hasProductCategoriaTipoEntidadLibro = isVocabularyNameIntoCategories(categoriesProduct, 'entity type', 'libro') />
18<#assign hasProductCategoriaTipoEntidadNorma = isVocabularyNameIntoCategories(categoriesProduct, 'entity type', 'norma') />
19<#assign hasProductCategoriaTipoEntidadColeccionTematica = isVocabularyNameIntoCategories(categoriesProduct, 'entity type', 'coleccion tematica') />
20<#assign categoryProductInfoTematica = getCategoriesByVocabularyAsString(categoriesProduct, "temáticas", " / ", "title") />
21<#assign categoryProductInfoStatus = getCategoriesByVocabularyAsString(categoriesProduct, "status", " / ", "title") />
22
23<#-- PickList de languages -->
24<#assign ercOfListTypeEntryLanguages = 'IDIOMAS_NORMAS_PICKLIST' />
25<#assign languageEntries = getListTypeEntriesByERC(ercOfListTypeEntryLanguages) />
26
27<#-- Specifications/Specifications language -->
28<#assign specificationsLanguagesProduct = getSpecificationsProduct(channelId, productId, 'specificationKey', 'standard-languages') />
29<#assign filteredSpecificationsLanguagesProductTemp = filterOutItems(specificationsLanguagesProduct, 'value', ['BI', 'TR']) />
30<#assign filteredSpecificationsLanguagesProduct = addAllMatchingLanguagesByField(filteredSpecificationsLanguagesProductTemp, languageEntries, 'value', 'title') />
31
32<#-- Thematic collection/Coleccion tematica data -->
33<#assign thematicCollectionsProduct = getThematicCollectionProduct(cpDefinitionId) />
34
35
36<#-- Functions -->
37<#function getProduct channelId productId>
38 <#return restClient.get("/headless-commerce-delivery-catalog/v1.0/channels/${channelId}/products/${productId}")>
39</#function>
40
41
42<#function getProductByERC erc>
43 <#-- TODO: estamos usando el endpoint del product admin, hay que usar la del Product de Liferay NO admin: headless-commerce-delivery-catalog -->
44 <#-- <#return restClient.get("/headless-commerce-admin-catalog/v1.0/products/by-externalReferenceCode/${erc}")> -->
45 <#assign response = restClient.get("/headless-commerce-admin-catalog/v1.0/products/by-externalReferenceCode/${erc}")>
46 <#-- Si el producto no existe o tiene status NOT_FOUND, devolvemos string vacío -->
47 <#if response?? && response.status?? && response.status == "NOT_FOUND">
48 <#return {}>
49 <#elseif !response??>
50 <#return {}>
51 <#else>
52 <#return response>
53 </#if>
54</#function>
55
56
57<#function getURLsOfProduct product baseURL="" siteFriendlyURL="" languageFieldName="language" urlFieldName="url">
58 <#-- Inicializamos un array vacío -->
59 <#assign urlsArray = []>
60
61 <#-- Validamos que product y product.urls existan -->
62 <#if product?? && product.urls??>
63 <#-- Iteramos sobre los idiomas -->
64 <#list product.urls?keys as lang>
65 <#assign urlValue = product.urls[lang] />
66 <#if urlValue?? && urlValue?has_content>
67 <#-- Aseguramos que la URL empiece con "/p/" -->
68 <#assign cleanUrl = urlValue?starts_with("/")?then(urlValue, "/p/" + urlValue) />
69
70 <#-- Generamos la URL completa -->
71 <#-- Si baseURL tiene contenido, construimos URL completa -->
72 <#if baseURL?? && baseURL?has_content>
73
74 <#-- Aseguramos que el slug empiece con /p/ -->
75 <#assign cleanUrl = urlValue?starts_with("/")?then(urlValue, "/p/" + urlValue) />
76
77 <#-- Tomamos las dos primeras letras del idioma para el prefijo -->
78 <#assign langPrefix = lang?substring(0,2)>
79
80 <#-- Construimos la URL completa -->
81 <#if siteFriendlyURL?? && siteFriendlyURL?has_content>
82 <#assign fullUrl = baseURL + "/" + langPrefix + siteFriendlyURL + cleanUrl>
83 <#else>
84 <#assign fullUrl = baseURL + "/" + langPrefix + cleanUrl>
85 </#if>
86
87 <#else>
88 <#assign fullUrl = cleanUrl>
89 </#if>
90
91 <#-- Creamos el objeto language+url -->
92 <#assign newItem = {(languageFieldName): lang, (urlFieldName): fullUrl} />
93
94 <#-- Lo agregamos al array -->
95 <#assign urlsArray = urlsArray + [newItem] />
96 </#if>
97 </#list>
98 </#if>
99
100 <#return urlsArray>
101</#function>
102
103
104<#function getProductCategories channelId productId>
105 <#return restClient.get("/headless-commerce-delivery-catalog/v1.0/channels/${channelId}/products/${productId}/categories?sort=vocabulary").items>
106</#function>
107
108
109<#function getSpecificationsProduct channelId productId field="" value="">
110 <#assign response = restClient.get("/headless-commerce-delivery-catalog/v1.0/channels/${channelId}/products/${productId}/product-specifications?pageSize=100&sort")>
111 <#assign items = response.items>
112
113 <#-- Si se pasa campo y valor, filtrar -->
114 <#if field?has_content && value?has_content>
115 <#assign items = items?filter(item ->
116 (item[field]??) && (item[field]?string?lower_case == value?lower_case)
117 )>
118 </#if>
119
120 <#return items>
121</#function>
122
123
124<#function filterOutItems items field valuesToExclude>
125 <#-- Si el array o los valores no existen, devolver items sin cambios -->
126 <#if !items?? || !valuesToExclude??>
127 <#return items>
128 </#if>
129
130 <#-- Filtramos: solo mantener los items cuyo campo no esté en la lista -->
131 <#assign filteredItems = items?filter(item ->
132 !(item[field]?? && (valuesToExclude?seq_contains(item[field]?string)))
133 )>
134
135 <#return filteredItems>
136</#function>
137
138
139<#function addAllMatchingLanguagesByField specificationsLanguagesProduct languageEntries specValueField newSpecField>
140
141 <#if specificationsLanguagesProduct?? && languageEntries?? && specValueField?? && newSpecField??>
142
143 <#-- Creamos una copia para no modificar el original directamente -->
144 <#assign updatedSpecs = []>
145
146 <#list specificationsLanguagesProduct as spec>
147 <#-- Buscar coincidencia -->
148 <#assign keyValue = spec[specValueField]>
149 <#assign match = languageEntries?filter(entry -> entry.key == keyValue)?first>
150
151 <#-- Crear una copia del objeto actual -->
152 <#assign newSpec = spec>
153
154 <#-- Si hay match con nombre válido, añadir el nuevo campo -->
155 <#if match?? && match.name?? && match.name?has_content>
156 <#assign newSpec = newSpec + { (newSpecField): match.name }>
157 </#if>
158
159 <#-- Añadir al array final -->
160 <#assign updatedSpecs = updatedSpecs + [newSpec]>
161 </#list>
162
163 <#return updatedSpecs>
164 <#else>
165 <#return specificationsLanguagesProduct>
166 </#if>
167</#function>
168
169
170<#function getListTypeEntriesByERC erc fields="key,name,externalReferenceCode,name_i18n" sort="key" pageSize="1000">
171 <#attempt>
172 <#return restClient.get(
173 "/headless-admin-list-type/v1.0/list-type-definitions/by-external-reference-code/${erc}/list-type-entries?fields=${fields}&sort=${sort}&pageSize=${pageSize}"
174 ).items>
175 <#recover>
176 <#return []>
177 </#attempt>
178</#function>
179
180
181<#function getThematicCollectionProduct cpDefinitionId>
182 <#assign response = restClient.get("/c/thematiccollections/?filter=r_thematicCollection_CPDefinitionId eq '${cpDefinitionId}'")!{} />
183 <#assign items = response.items![] />
184 <#return (items?size > 0)?then(items[0], {}) />
185</#function>
186
187
188<#function sortByField items fieldPath sortFieldName="sortKey" order="asc">
189
190 <#-- Array temporal con el campo auxiliar -->
191 <#assign prepared = []>
192
193 <#list items as e>
194 <#-- Evaluar el valor del campo dinámico -->
195 <#assign sortValue = "" />
196 <#if fieldPath == "section?first.key">
197 <#assign sortValue = (e.section?first.key)!""?lower_case>
198 <#elseif fieldPath == "section?first.name">
199 <#assign sortValue = (e.section?first.name)!""?lower_case>
200 <#elseif fieldPath == "type?first.key">
201 <#assign sortValue = (e.type?first.key)!""?lower_case>
202 <#else>
203 <#-- Si el campoPath no está mapeado, usar vacío -->
204 <#assign sortValue = "" />
205 </#if>
206
207 <#-- Agregar objeto enriquecido con campo auxiliar -->
208 <#assign prepared = prepared + [ e + { (sortFieldName): sortValue } ]>
209 </#list>
210
211 <#-- Ordenar -->
212 <#assign sorted = prepared?sort_by(sortFieldName)>
213
214 <#-- Si order = desc, invertir -->
215 <#if order?lower_case == "desc">
216 <#assign sorted = sorted?reverse>
217 </#if>
218
219 <#return sorted>
220</#function>
221
222
223<#function addKeysFieldNested items arrayField nestedField newFieldName mode="first" subArrayField="">
224
225 <#assign enrichedItems = []>
226
227 <#list items as e>
228 <#assign resultValue = "">
229
230 <#-- Detectamos el array del objeto principal -->
231 <#assign targetArray = e[arrayField]![]>
232
233 <#if targetArray?has_content>
234 <#if mode == "all">
235 <#assign keysList = []>
236 <#list targetArray as t>
237 <#if subArrayField?has_content>
238 <#assign subArray = t[subArrayField]![]>
239 <#if subArray?has_content>
240 <#list subArray as sub>
241 <#assign keysList = keysList + [ sub[nestedField]!"" ]>
242 </#list>
243 </#if>
244 <#else>
245 <#assign keysList = keysList + [ t[nestedField]!"" ]>
246 </#if>
247 </#list>
248 <#assign resultValue = keysList?join(", ")>
249 <#elseif mode == "first">
250 <#assign firstItem = targetArray?first>
251 <#if subArrayField?has_content>
252 <#assign subArray = firstItem[subArrayField]![]>
253 <#if subArray?has_content>
254 <#assign resultValue = subArray?first[nestedField]!"" >
255 <#else>
256 <#assign resultValue = "" >
257 </#if>
258 <#else>
259 <#assign resultValue = firstItem[nestedField]!"" >
260 </#if>
261 </#if>
262 <#else>
263 <#assign resultValue = "">
264 </#if>
265
266 <#-- Añadimos el nuevo campo al objeto -->
267 <#assign enrichedItems = enrichedItems + [
268 e + { (newFieldName): resultValue }
269 ]>
270 </#list>
271
272 <#return enrichedItems>
273</#function>
274
275
276<#function isVocabularyNameIntoCategories categories vocabulary name>
277 <#assign found = false />
278
279 <#if categories?has_content && vocabulary?has_content && name?has_content>
280
281 <#assign vocabNorm = normalize(vocabulary) />
282 <#assign nameNorm = normalize(name) />
283
284 <#list categories as category>
285 <#if !found>
286 <#assign catVocabNorm = normalize(category.vocabulary) />
287 <#assign catNameNorm = normalize(category.name) />
288
289 <#if catVocabNorm == vocabNorm && catNameNorm == nameNorm>
290 <#assign found = true />
291 </#if>
292 </#if>
293 </#list>
294
295 </#if>
296
297 <#return found>
298</#function>
299
300
301<#function normalize text onlyAccents = false>
302 <#-- proteger null -->
303 <#if !text?has_content>
304 <#return "">
305 </#if>
306
307 <#assign t = text />
308
309 <#-- quitar acentos -->
310 <#assign t = t
311 ?replace("á","a")?replace("é","e")?replace("í","i")
312 ?replace("ó","o")?replace("ú","u")?replace("ü","u")
313 ?replace("ñ","n")
314 ?replace("Á","A")?replace("É","E")?replace("Í","I")
315 ?replace("Ó","O")?replace("Ú","U")?replace("Ü","U")
316 ?replace("Ñ","N")
317 />
318
319 <#-- si NO es solo acentos, normalización completa -->
320 <#if !onlyAccents>
321 <#assign t = t?lower_case />
322 <#assign t = t?trim />
323 <#assign t = t?replace("\\s+", " ", "r") />
324 </#if>
325
326 <#return t>
327</#function>
328
329
330<#-- Función que retorna los valores de categorías como string, normalizando vocabulario -->
331<#function getCategoriesByVocabularyAsString categories vocabulary separator=" / " field="name">
332 <#assign matchedValues = []>
333 <#if categories?has_content && vocabulary?has_content>
334 <#list categories as category>
335 <#-- Normalizamos tanto el vocabulary de la categoría como el buscado -->
336 <#if normalize(category.vocabulary, true)?lower_case == normalize(vocabulary, true)?lower_case>
337 <#if field == "title">
338 <#assign matchedValues += [category.title]>
339 <#else>
340 <#assign matchedValues += [category.name]>
341 </#if>
342 </#if>
343 </#list>
344 </#if>
345 <#return matchedValues?join(separator)>
346</#function>
347
348
349<#macro printObject obj>
350 <#-- Permite hacer un output de un array de objetos o un objeto que se pasa como parámetro -->
351 <#if obj?is_hash>
352 {
353 <#list obj?keys as k>
354 "${k}":
355 <#assign value = obj[k]>
356 <#if value?is_hash || value?is_sequence>
357 <@printObject obj=value/>
358 <#elseif value?is_boolean>
359 ${value?c}
360 <#elseif value?is_number>
361 ${value}
362 <#elseif value?has_content>
363 "${value?string}"
364 <#else>
365 null
366 </#if>
367 <#if k_has_next>, </#if>
368 </#list>
369 }
370 <#elseif obj?is_sequence>
371 [
372 <#list obj as item>
373 <@printObject obj=item/>
374 <#if item_has_next>, </#if>
375 </#list>
376 ]
377 <#elseif obj?is_boolean>
378 ${obj?c}
379 <#elseif obj?is_number>
380 ${obj}
381 <#elseif obj?has_content>
382 "${obj?string}"
383 <#else>
384 null
385 </#if>
386</#macro>
387
388
389
390
391<#-- HTML -->
392<#if hasProductCategoriaTipoEntidadColeccionTematica>
393 <div id="ecom-coleccion_tematica-detail" class="ecom-coleccion_tematica">
394
395 <div class="tabs-section">
396 <div class="tab-content">
397
398 <#-- Fecha edicion del producto > description del Producto -->
399 <div class="ecom-coleccion_tematica-detail-body">
400 ${product.description}
401 </div>
402
403 <#-- Para imprimir el contenido de los Objects -->
404 <#-- Thematic collection/Coleccion tematica data
405 <#if isDebug>
406 <div class="">
407 <p><strong>thematicCollectionsProduct:</strong></p>
408 <div class="print-object-json-content mb-3" style="max-height:200px; overflow:auto; border:1px solid #ccc; padding:5px; cursor:pointer;"
409 onclick="const r=document.createRange(); r.selectNodeContents(this); const s=window.getSelection(); s.removeAllRanges(); s.addRange(r);">
410 <@printObject thematicCollectionsProduct />
411 </div>
412 </div>
413 </#if>
414 -->
415
416 </div>
417 </div>
418 </div>
419</#if>










