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










