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
ISO/IEC 15433:2003
Information technology — Telecommunications and information exchange between systems — Private Integrated Services Network — Inter-exchange signalling protocol — Wireless Terminal Authentication supplementary services
Se ha producido un error al procesar la plantilla.
For "." left-hand operand: Expected a hash, but this has evaluated to a string (wrapper: f.t.SimpleScalar):
==> response [in template "34352066712900#33336#null" at line 67, column 24]
----
FTL stack trace ("~" means nesting-related):
- Failed at: #if response?? && response.status?? &... [in template "34352066712900#33336#null" in function "getProductByERC" at line 67, column 5]
----
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>










