Se ha producido un error al procesar la plantilla.
Java method "com.liferay.commerce.product.type.grouped.model.impl.CPDefinitionGroupedEntryImpl.getEntryCProduct()" threw an exception when invoked on com.liferay.commerce.product.type.grouped.model.impl.CPDefinitionGroupedEntryImpl object "{\"mvccVersion\": 0, \"uuid\": \"c9baff1b-49b6-04b6-3cad-abee039f6dc7\", \"CPDefinitionGroupedEntryId\": 206188, \"groupId\": 59881, \"companyId\": 20097, \"userId\": 20125, \"userName\": \"Test Test\", \"createDate\": \"2023-07-06 09:26:30.296\", \"modifiedDate\": \"2023-07-06 09:26:30.296\", \"CPDefinitionId\": 206097, \"entryCProductId\": 206154, \"priority\": 0.0, \"quantity\": 1}"; see cause exception in the Java stack trace.

----
FTL stack trace ("~" means nesting-related):
	- Failed at: #assign childProduct = groupedEntry.g...  [in template "20097#20123#1714650" at line 141, column 17]
----
1<#-- 
2  BTI - Detalle de producto Commerce publico y privado 
3--> 
4 
5<#assign enableSalesforcePrices = true> 
6<#assign isLoggedIn = themeDisplay.isSignedIn()> 
7<#assign CPDefinitionLocalService = serviceLocator.findService("com.liferay.commerce.product.service.CPDefinitionLocalService")> 
8<#assign specificationValueService = serviceLocator.findService("com.liferay.commerce.product.service.CPDefinitionSpecificationOptionValueLocalService")> 
9<#assign fileEntryLocalService = serviceLocator.findService("com.liferay.document.library.kernel.service.DLAppLocalService")> 
10<#assign dlurlHelper = serviceLocator.findService("com.liferay.document.library.util.DLURLHelper")> 
11<#assign jsonFactoryUtil = serviceLocator.findService("com.liferay.portal.kernel.json.JSONFactoryUtil")> 
12<#assign commerceContext = renderRequest.getAttribute("COMMERCE_CONTEXT")> 
13<#assign cpDefinition = CPDefinitionLocalService.getCPDefinition(cpCatalogEntry.getCPDefinitionId())> 
14<#assign cpDefinitions = [cpDefinition]> 
15<#assign productType = cpCatalogEntry.getProductTypeName()!"simple"> 
16<#assign cpInstances = cpDefinition.getCPInstances()![]> 
17<#assign cpInstanceId = 0> 
18<#if cpInstances?has_content> 
19  <#assign cpInstanceId = cpInstances[0].getCPInstanceId()> 
20</#if> 
21 
22<#assign commerceOrderId = 0> 
23<#assign accountEntryId = 0> 
24<#assign blocked = false> 
25<#if isLoggedIn> 
26  <#attempt> 
27    <#assign commerceOrderId = (commerceContext.getCommerceOrder().getCommerceOrderId())!0> 
28  <#recover> 
29  </#attempt> 
30  <#attempt> 
31    <#assign accountEntry = commerceContext.getAccountEntry()> 
32    <#assign accountEntryId = accountEntry.getAccountEntryId()> 
33    <#assign blocked = accountEntry.getExpandoBridge().getAttribute("Blocked")!false> 
34  <#recover> 
35  </#attempt> 
36</#if> 
37 
38<#assign productName = cpCatalogEntry.getName()!""> 
39<#assign shortDescription = cpCatalogEntry.getShortDescription()!""> 
40<#assign description = cpDefinition.getDescription()!""> 
41<#assign externalReferenceCode = cpDefinition.getCProduct().getExternalReferenceCode()!""> 
42<#assign specificationValues = specificationValueService.getCPDefinitionSpecificationOptionValues(cpDefinition.getCPDefinitionId(), 0)![]> 
43 
44<#assign productImageURL = ""> 
45<#attempt> 
46  <#assign productImages = cpDefinition.getCPAttachmentFileEntries(0, -1)> 
47  <#if productImages?has_content> 
48    <#assign productAttachment = productImages?sort_by("createDate")?last> 
49    <#assign productFileEntry = fileEntryLocalService.getFileEntry(productAttachment.getFileEntryId())> 
50    <#assign productImageURL = dlurlHelper.getDownloadURL(productFileEntry, productFileEntry.getFileVersion(), themeDisplay, "")> 
51  </#if> 
52<#recover> 
53</#attempt> 
54<#if !productImageURL?has_content> 
55  <#attempt> 
56    <#assign productImageURL = cpContentHelper.getDefaultImageFileURL(accountEntryId, cpDefinition.getCPDefinitionId())!""> 
57  <#recover> 
58  </#attempt> 
59</#if> 
60 
61<#assign groupedEntries = []> 
62<#assign groupedLoadError = false> 
63<#if productType == "grouped"> 
64  <#attempt> 
65    <#assign groupedEntries = groupedCPTypeHelper.getCPDefinitionGroupedEntry(accountEntryId, commerceContext.getCommerceChannelGroupId(), cpDefinition.getCPDefinitionId())![]> 
66  <#recover> 
67    <#assign groupedLoadError = true> 
68  </#attempt> 
69</#if> 
70 
71<#assign pricesService = ""> 
72<#assign pricesServiceAvailable = false> 
73<#assign simplePriceAvailable = false> 
74<#assign simpleUnitValue = 0> 
75<#assign simpleOriginalValue = 0> 
76<#assign simpleFormattedPrice = ""> 
77<#assign simpleFormattedOriginalPrice = ""> 
78<#if isLoggedIn && enableSalesforcePrices> 
79  <#attempt> 
80    <#assign pricesService = serviceLocator.findService("com.bti.commerce.integration.api.services.ext.PricesServiceExt")> 
81    <#assign pricesServiceAvailable = true> 
82    <#if productType == "simple"> 
83      <#assign simplePrice = pricesService.getPricesWithDiscount(cpDefinitions, false, commerceContext)?values[0]> 
84      <#assign simpleUnitValue = simplePrice.getUnitValue()!0> 
85      <#assign simpleOriginalValue = simplePrice.getUnitValueOriginal()!simpleUnitValue> 
86      <#assign simpleFormattedPrice = pricesService.formatValue(simpleUnitValue)!""> 
87      <#assign simpleFormattedOriginalPrice = pricesService.formatValue(simpleOriginalValue)!""> 
88      <#assign simplePriceAvailable = simpleUnitValue gt 0> 
89    </#if> 
90  <#recover> 
91    <#assign pricesServiceAvailable = false> 
92    <#assign simplePriceAvailable = false> 
93  </#attempt> 
94</#if> 
95 
96<#assign detailId = "bti-product-detail-" + cpDefinition.getCPDefinitionId()?c> 
97 
98<article class="bti-product-detail" id="${detailId}"> 
99  <div class="bti-product-detail__inner"> 
100    <div class="bti-product-detail__gallery"> 
101      <#if productImageURL?has_content> 
102        <img class="bti-product-detail__image" src="${htmlUtil.escapeAttribute(productImageURL)}" alt="${htmlUtil.escapeAttribute(productName)}"> 
103      </#if> 
104    </div> 
105 
106    <div class="bti-product-detail__info"> 
107      <#if isLoggedIn> 
108        <div class="bti-product-detail__commerce-actions"> 
109          <@liferay_commerce_ui["add-to-wish-list"] CPCatalogEntry=cpCatalogEntry /> 
110        </div> 
111      </#if> 
112 
113      <div class="bti-product-detail__summary"> 
114        <#if externalReferenceCode?has_content> 
115          <p class="bti-product-detail__reference"><span class="bti-product-detail__reference-label">REF.</span><span class="bti-product-detail__reference-value">${externalReferenceCode?html}</span></p> 
116        </#if> 
117        <h1 class="bti-product-detail__title">${productName?html}</h1> 
118        <#if shortDescription?has_content><div class="bti-product-detail__description">${shortDescription}</div></#if> 
119        <#if isLoggedIn && enableSalesforcePrices> 
120          <div class="bti-product-detail__price"> 
121            <#if simplePriceAvailable> 
122              <#if simpleOriginalValue gt simpleUnitValue><span class="bti-product-detail__price-original">${simpleFormattedOriginalPrice?html}</span></#if> 
123              <span class="bti-product-detail__price-current">${simpleFormattedPrice?html}</span> 
124            <#else><span class="bti-product-detail__price-status">Precio no disponible</span></#if> 
125          </div> 
126        </#if> 
127      </div> 
128 
129      <div class="bti-product-detail__delivery"> 
130        <svg class="bti-product-detail__delivery-icon" viewBox="0 0 24 24" fill="none" aria-hidden="true"><path fill="currentColor" d="M5.73 18.27A2.49 2.49 0 0 1 5 16.5H2.77l.22-1h2.27A2.5 2.5 0 0 1 10 15.5h4.36L16.56 6H5.61l.02-.1A1.12 1.12 0 0 1 6.73 5h11.08L17 8.5h2.08L21.79 12l-.87 4.5h-1.15a2.5 2.5 0 0 1-5 0H10a2.5 2.5 0 0 1-4.27 1.77ZM15.89 13.25h4.65l.18-.89-2.14-2.86h-1.82l-.87 3.75ZM7.5 18a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm9.77 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path></svg> 
131        <p>Entrega gratis en 24h si haces tu pedido antes de las 17h.</p> 
132      </div> 
133 
134      <#if productType == "grouped"> 
135        <div class="bti-product-detail__variants"> 
136          <#if groupedLoadError> 
137            <p class="bti-product-detail__status" role="status">No se han podido cargar las variantes.</p> 
138          <#elseif groupedEntries?has_content> 
139            <div class="bti-product-detail__table-scroll"><table class="bti-product-detail__table"><thead><tr><th scope="col"><span class="sr-only">Seleccionar</span></th><th scope="col">Referencia</th><th scope="col">Cantidad</th></tr></thead><tbody> 
140              <#list groupedEntries as groupedEntry> 
141                <#assign childProduct = groupedEntry.getEntryCProduct()> 
142                <#assign childDefinition = groupedEntry.getEntryCPDefinition()> 
143                <#assign childInstances = childDefinition.getCPInstances()![]> 
144                <#if childInstances?has_content> 
145                  <#assign childInstanceId = childInstances[0].getCPInstanceId()> 
146                  <tr><td><input class="bti-product-detail__variant-check" type="checkbox" value="${childInstanceId?c}" aria-label="Seleccionar ${childProduct.getExternalReferenceCode()?html}"></td><td>${childProduct.getExternalReferenceCode()?html}</td><td><input class="bti-product-detail__variant-quantity" type="number" min="1" max="10000" value="1" data-sku-id="${childInstanceId?c}" aria-label="Cantidad para ${childProduct.getExternalReferenceCode()?html}"></td></tr> 
147                </#if> 
148              </#list> 
149            </tbody></table></div> 
150          <#else><p class="bti-product-detail__status" role="status">Producto no disponible.</p></#if> 
151        </div> 
152      </#if> 
153 
154      <div class="bti-product-detail__purchase"> 
155        <div class="bti-product-detail__buttons"> 
156          <#if isLoggedIn> 
157            <#if blocked> 
158              <span class="bti-product-detail__button bti-product-detail__button--disabled" aria-disabled="true">Compra no disponible</span> 
159            <#elseif productType == "simple" && cpInstanceId gt 0> 
160              <div class="bti-product-detail__quantity" aria-label="Cantidad"><button class="bti-product-detail__quantity-button" type="button" data-quantity-step="-1" aria-label="Reducir cantidad">-</button><output class="bti-product-detail__quantity-value" data-quantity-value aria-live="polite">1</output><button class="bti-product-detail__quantity-button" type="button" data-quantity-step="1" aria-label="Aumentar cantidad">+</button></div> 
161              <div class="bti-product-detail__native-cart"><@liferay_commerce_ui["add-to-cart"] CPCatalogEntry=cpCatalogEntry cPInstanceId=cpInstanceId /></div> 
162            <#elseif productType == "grouped" && groupedEntries?has_content> 
163              <button class="bti-product-detail__button bti-product-detail__button--secondary" type="button" data-grouped-add disabled>Añadir al carrito</button> 
164            </#if> 
165          <#else> 
166            <a class="bti-product-detail__button bti-product-detail__button--primary" href="#formulario-contacto">Solicitar información</a> 
167            <a class="bti-product-detail__button bti-product-detail__button--secondary" href="${themeDisplay.getURLSignIn()?html}">Iniciar sesión o registrarse</a> 
168          </#if> 
169        </div> 
170        <div class="bti-product-detail__catalog-link"> 
171          <@liferay_commerce_ui["compare-checkbox"] CPCatalogEntry=cpCatalogEntry label="Comparar con otros productos" /> 
172        </div> 
173        <p class="bti-product-detail__status" data-purchase-status aria-live="polite" hidden></p> 
174      </div> 
175    </div> 
176  </div> 
177 
178  <#assign notesContent = ""> 
179  <#attempt><#assign notesRaw = cpDefinition.getExpandoBridge().getAttribute("notes")!""> 
180    <#if notesRaw?has_content && notesRaw != '{"de":"","pt":"","en":"","it":"","fr":"","es":""}'><#assign notesJson = jsonFactoryUtil.createJSONObject(notesRaw)><#assign notesContent = notesJson.getString(locale?split("_")[0])!""></#if> 
181  <#recover></#attempt> 
182  <#assign clinicalContent = ""> 
183  <#attempt><#assign clinicalRaw = cpDefinition.getExpandoBridge().getAttribute("clinicalIndications")!""> 
184    <#if clinicalRaw?has_content && clinicalRaw != '{"de":"","pt":"","en":"","it":"","fr":"","es":""}'><#assign clinicalJson = jsonFactoryUtil.createJSONObject(clinicalRaw)><#assign clinicalContent = clinicalJson.getString(locale?split("_")[0])!""></#if> 
185  <#recover></#attempt> 
186  <#function hasAdditionalContent value> 
187    <#return htmlUtil.stripHtml(value)?replace("&nbsp;", " ")?replace("[\\s\\u00A0\\u200B-\\u200D\\uFEFF]", "", "r")?has_content> 
188  </#function> 
189  <#assign accordionDescription = description> 
190  <#if !hasAdditionalContent(accordionDescription)><#assign accordionDescription = shortDescription></#if> 
191  <#assign populatedSpecifications = specificationValues?filter(value -> hasAdditionalContent(value.getValueCurrentValue()!""))> 
192  <#assign hasDescription = hasAdditionalContent(accordionDescription)> 
193  <#assign hasSpecifications = populatedSpecifications?has_content && productType != "grouped"> 
194  <#assign hasClinicalContent = hasAdditionalContent(clinicalContent)> 
195  <#assign hasNotesContent = hasAdditionalContent(notesContent)> 
196 
197  <#if hasNotesContent || hasDescription || hasSpecifications || hasClinicalContent> 
198    <div class="bti-product-detail__additional"> 
199      <#if hasNotesContent><div class="bti-product-detail__notice" role="note">${notesContent}</div></#if> 
200      <#if hasDescription> 
201        <details class="bti-product-detail__accordion"><summary>Descripción</summary><div class="bti-product-detail__accordion-content">${accordionDescription}</div></details> 
202      </#if> 
203      <#if hasSpecifications> 
204        <details class="bti-product-detail__accordion"><summary>Especificaciones</summary><dl class="bti-product-detail__specifications"><#list populatedSpecifications as specificationValue><div class="bti-product-detail__specification"><dt>${specificationValue.getCPSpecificationOption().getTitleCurrentValue()?html}</dt><dd>${specificationValue.getValueCurrentValue()?html}</dd></div></#list></dl></details> 
205      </#if> 
206      <#if hasClinicalContent> 
207        <details class="bti-product-detail__accordion"><summary>Más información</summary><div class="bti-product-detail__accordion-content">${clinicalContent}</div></details> 
208      </#if> 
209    </div> 
210  </#if> 
211</article> 
212 
213<style> 
214  .bti-product-detail { background-color: var(--bti-color-brand-light); color: var(--bti-color-content-high); width: 100%; } 
215  .bti-product-detail__inner { display: grid; gap: var(--bti-spacing-8); grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); margin: 0 auto; max-width: var(--bti-content-max-width); padding: var(--bti-spacing-8) var(--bti-spacing-6); } 
216  .bti-product-detail__gallery { align-items: center; display: flex; justify-content: center; min-width: 0; } 
217  .bti-product-detail__image { display: block; height: auto; max-height: calc(var(--bti-spacing-10) * 8); max-width: 100%; object-fit: contain; width: 100%; } 
218  .bti-product-detail__info, .bti-product-detail__summary, .bti-product-detail__purchase { display: flex; flex-direction: column; } 
219  .bti-product-detail__info { gap: var(--bti-spacing-8); min-width: 0; } 
220  .bti-product-detail__summary { gap: var(--bti-spacing-4); } 
221  .bti-product-detail__commerce-actions { align-items: center; display: flex; gap: var(--bti-spacing-4); justify-content: flex-end; } 
222  .bti-product-detail__commerce-actions .add-to-wish-list, .bti-product-detail__commerce-actions .add-to-wish-list-wrapper { margin: 0; } 
223  .bti-product-detail__commerce-actions .add-to-wish-list button, .bti-product-detail__commerce-actions .btn-add-to-wish-list { align-items: center; background: transparent; border: 0; color: var(--bti-color-content-high); display: inline-flex; height: var(--bti-spacing-6); justify-content: center; padding: 0; width: var(--bti-spacing-6); } 
224  .bti-product-detail__commerce-actions .text-truncate-inline, .bti-product-detail__commerce-actions .text-truncate { display: none !important; } 
225  .bti-product-detail__reference { align-items: center; display: flex; gap: var(--bti-spacing-2); margin: 0; } 
226  .bti-product-detail__reference-label { border: var(--bti-border-width) var(--bti-border-style) var(--bti-color-sand-70); border-radius: var(--bti-radius-xs); font-family: var(--bti-font-body); font-size: var(--bti-fs-body-s); line-height: var(--bti-lh-body-m); padding: var(--bti-spacing-2) var(--bti-spacing-3); } 
227  .bti-product-detail__reference-value, .bti-product-detail__title, .bti-product-detail__price-current { font-family: var(--bti-font-headline); } 
228  .bti-product-detail__reference-value { font-size: var(--bti-fs-title-m); font-weight: var(--bti-fw-semibold); line-height: var(--bti-lh-title-m); } 
229  .bti-product-detail__title { color: var(--bti-color-content-high); font-size: var(--bti-fs-headline-s); font-weight: var(--bti-fw-regular); line-height: var(--bti-lh-headline-s); margin: 0; } 
230  .bti-product-detail__description, .bti-product-detail__accordion-content { color: var(--bti-color-content-medium); font-family: var(--bti-font-body); font-size: var(--bti-fs-body-l); line-height: var(--bti-lh-body-l); padding-bottom: var(--bti-spacing-4); } 
231  .bti-product-detail__description p { margin: 0; } 
232  .bti-product-detail__price { align-items: baseline; display: flex; gap: var(--bti-spacing-3); } 
233  .bti-product-detail__price-current { color: var(--bti-color-brand-purple); font-size: var(--bti-fs-headline-s); line-height: var(--bti-lh-headline-s); } 
234  .bti-product-detail__price-original { color: var(--bti-color-content-medium); font-family: var(--bti-font-body); text-decoration: line-through; } 
235  .bti-product-detail__price-status, .bti-product-detail__status { color: var(--bti-color-content-medium); font-family: var(--bti-font-body); font-size: var(--bti-fs-body-s); line-height: var(--bti-lh-body-s); } 
236  .bti-product-detail__delivery { align-items: center; display: flex; gap: var(--bti-spacing-2); } 
237  .bti-product-detail__delivery p { font-family: var(--bti-font-body); font-size: var(--bti-fs-body-m); line-height: var(--bti-lh-body-m); margin: 0; } 
238  .bti-product-detail__delivery-icon { flex-shrink: 0; height: var(--bti-spacing-5); width: var(--bti-spacing-5); } 
239  .bti-product-detail__variants, .bti-product-detail__table-scroll { max-width: 100%; overflow-x: auto; } 
240  .bti-product-detail__table { border-collapse: collapse; font-family: var(--bti-font-body); font-size: var(--bti-fs-body-s); line-height: var(--bti-lh-body-s); width: 100%; } 
241  .bti-product-detail__table th, .bti-product-detail__table td { border-bottom: var(--bti-border-width) var(--bti-border-style) var(--bti-color-content-disabled); padding: var(--bti-spacing-3); text-align: left; } 
242  .bti-product-detail__variant-check { accent-color: var(--bti-color-brand-purple); height: var(--bti-spacing-4); width: var(--bti-spacing-4); } 
243  .bti-product-detail__variant-quantity { border: var(--bti-border-width) var(--bti-border-style) var(--bti-color-content-disabled); color: var(--bti-color-content-high); font: inherit; max-width: calc(var(--bti-spacing-10) + var(--bti-spacing-4)); padding: var(--bti-spacing-2); } 
244  .bti-product-detail__purchase { gap: var(--bti-spacing-3); } 
245  .bti-product-detail__buttons { align-items: stretch; display: flex; gap: var(--bti-spacing-3); } 
246  .bti-product-detail__quantity { align-items: center; display: inline-flex; flex-shrink: 0; gap: var(--bti-spacing-3); } 
247  .bti-product-detail__quantity-button, .bti-product-detail__quantity-value { align-items: center; background: transparent; border: var(--bti-border-width) var(--bti-border-style) var(--bti-color-sand-70); color: var(--bti-color-content-high); display: inline-flex; font-family: var(--bti-font-body); font-size: var(--bti-fs-body-l); height: calc(var(--bti-spacing-6) * 2); justify-content: center; margin: 0; padding: 0; width: calc(var(--bti-spacing-6) * 2); } 
248  .bti-product-detail__quantity-button { cursor: pointer; } 
249  .bti-product-detail__quantity-button:disabled { background-color: var(--bti-color-content-disabled); cursor: not-allowed; } 
250  .bti-product-detail__button, .bti-product-detail__native-cart .btn-add-to-cart { align-items: center; border: var(--bti-border-width) var(--bti-border-style) transparent; border-radius: 0; display: inline-flex; font-family: var(--bti-font-body); font-size: var(--bti-fs-body-m); font-weight: var(--bti-fw-semibold); justify-content: center; line-height: var(--bti-lh-body-m); min-height: calc(var(--bti-spacing-6) * 2); padding: var(--bti-spacing-3) var(--bti-spacing-6); text-align: center; text-decoration: none; width: 100%; } 
251  .bti-product-detail__button--primary, .bti-product-detail__native-cart .btn-add-to-cart { background-color: var(--bti-color-brand-purple); color: var(--bti-color-content-inverse); } 
252  .bti-product-detail__button--secondary { background-color: transparent; border-color: var(--bti-color-content-disabled); color: var(--bti-color-brand-purple); } 
253  .bti-product-detail__button--disabled { background-color: var(--bti-color-content-disabled); color: var(--bti-color-content-medium); } 
254  .bti-product-detail__button:hover, .bti-product-detail__button:focus-visible, .bti-product-detail__native-cart .btn-add-to-cart:hover, .bti-product-detail__native-cart .btn-add-to-cart:focus-visible { background-color: var(--bti-color-brand-pink); border-color: var(--bti-color-brand-pink); color: var(--bti-color-content-inverse); text-decoration: none; } 
255  .bti-product-detail__native-cart { flex: 1; min-width: 0; } 
256  .bti-product-detail__native-cart .add-to-cart.mb-2 { margin-bottom: 0 !important; } 
257  .bti-product-detail__native-cart .add-to-cart-wrapper > .mb-3 { margin-bottom: 0 !important; } 
258  .bti-product-detail__native-cart .btn-add-to-cart { gap: var(--bti-spacing-2); } 
259  .bti-product-detail__native-cart .btn-add-to-cart .text-truncate-inline, .bti-product-detail__native-cart .btn-add-to-cart .text-truncate { display: inline !important; max-width: none; overflow: visible; } 
260  .bti-product-detail__native-cart .quantity-selector, .bti-product-detail__native-cart .quantity-selector-container, .bti-product-detail__native-cart [data-quantity-selector], .bti-product-detail__native-cart .quantity-selector__input { display: none !important; } 
261  .bti-product-detail__catalog-link { align-items: center; color: var(--bti-color-brand-purple); display: inline-flex; font-family: var(--bti-font-body); font-size: var(--bti-fs-body-m); font-weight: var(--bti-fw-semibold); gap: var(--bti-spacing-1); line-height: var(--bti-lh-body-m); text-decoration: none; width: fit-content; } 
262  .bti-product-detail__catalog-link .compare-checkbox-root { margin: 0; } 
263  .bti-product-detail__catalog-link .custom-control-label { color: inherit; cursor: pointer; font: inherit; } 
264  .bti-product-detail__catalog-link .custom-control-label-text { color: inherit; } 
265  .bti-product-detail__catalog-link .custom-control-label::before { background-color: transparent !important; border: 0 !important; height: var(--bti-spacing-5); left: 0; top: 50%; transform: translateY(-50%); width: var(--bti-spacing-5); } 
266  .bti-product-detail__catalog-link .custom-control-label::after { background-image: none !important; border: 0 !important; content: "+"; color: currentColor; font-family: var(--bti-font-body); font-size: var(--bti-fs-title-m); height: auto; left: 0; line-height: var(--bti-lh-title-m); opacity: 1; top: 50%; transform: translateY(-50%); width: var(--bti-spacing-5); } 
267  .bti-product-detail__catalog-link .custom-control-input:checked ~ .custom-control-label::after { content: "-"; } 
268  .bti-product-detail__catalog-link .custom-control-input:focus-visible ~ .custom-control-label { outline: var(--bti-border-width) var(--bti-border-style) currentColor; outline-offset: var(--bti-spacing-1); } 
269  .bti-product-detail__catalog-link:hover, .bti-product-detail__catalog-link:focus-within { color: var(--bti-color-brand-pink); text-decoration: none; } 
270  .bti-product-detail__additional { margin: 0 auto; max-width: var(--bti-content-max-width); padding: 0 var(--bti-spacing-6) var(--bti-spacing-10); } 
271  .bti-product-detail__notice { background-color: var(--bti-color-info-bg); color: var(--bti-color-info-dark); font-family: var(--bti-font-body); margin-bottom: var(--bti-spacing-6); padding: var(--bti-spacing-3); } 
272  .bti-product-detail__accordion { border-bottom: var(--bti-border-width) var(--bti-border-style) var(--bti-color-content-disabled); } 
273  .bti-product-detail__accordion:first-of-type { border-top: var(--bti-border-width) var(--bti-border-style) var(--bti-color-content-disabled); } 
274  .bti-product-detail__accordion summary { align-items: center; color: var(--bti-color-content-high); cursor: pointer; display: flex; font-family: var(--bti-font-body); font-size: var(--bti-fs-title-m); font-weight: var(--bti-fw-medium); justify-content: space-between; line-height: var(--bti-lh-title-m); list-style: none; padding: var(--bti-spacing-4) 0; } 
275  .bti-product-detail__accordion summary::-webkit-details-marker { display: none; } 
276  .bti-product-detail__accordion summary::after { content: "+"; flex-shrink: 0; font-family: var(--bti-font-body); font-size: var(--bti-fs-title-l); line-height: var(--bti-lh-title-l); } 
277  .bti-product-detail__accordion[open] summary::after { content: "-"; } 
278  .bti-product-detail__specifications { margin: 0; padding-bottom: var(--bti-spacing-6); } 
279  .bti-product-detail__specification { display: grid; gap: var(--bti-spacing-4); grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); padding: var(--bti-spacing-2) 0; } 
280  .bti-product-detail__specification dt, .bti-product-detail__specification dd { font-family: var(--bti-font-body); margin: 0; } 
281  .bti-product-detail__specification dd { color: var(--bti-color-content-medium); text-align: right; } 
282  @media (max-width: 991px) { .bti-product-detail__inner { gap: var(--bti-spacing-6); grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); padding: var(--bti-spacing-6) var(--bti-spacing-5); } .bti-product-detail__buttons { flex-direction: column; gap: var(--bti-spacing-4); } .bti-product-detail__additional { padding-left: var(--bti-spacing-5); padding-right: var(--bti-spacing-5); } } 
283  @media (max-width: 767px) { .bti-product-detail__inner { display: flex; flex-direction: column; padding: var(--bti-spacing-5) var(--bti-spacing-4); } .bti-product-detail__info { gap: var(--bti-spacing-6); } .bti-product-detail__description, .bti-product-detail__accordion-content { font-size: var(--bti-fs-body-m); line-height: var(--bti-lh-body-m); } .bti-product-detail__commerce-actions { justify-content: flex-start; } .bti-product-detail__delivery { align-items: flex-start; } .bti-product-detail__quantity { gap: var(--bti-spacing-2); justify-content: center; } .bti-product-detail__additional { padding: 0 var(--bti-spacing-4) var(--bti-spacing-8); } } 
284</style> 
285 
286<#if isLoggedIn && productType == "simple" && cpInstanceId gt 0 && !blocked> 
287<script> 
288(function () { 
289  var root = document.getElementById('${detailId}'); 
290  if (!root) { return; } 
291  var cartButton = root.querySelector('.bti-product-detail__native-cart .btn-add-to-cart'); 
292  var nativeQuantity = root.querySelector('.bti-product-detail__native-cart input.quantity-selector, .bti-product-detail__native-cart input[name*="quantity"], .bti-product-detail__native-cart [data-quantity-selector] input'); 
293  var quantityValue = root.querySelector('[data-quantity-value]'); 
294  var stepButtons = Array.prototype.slice.call(root.querySelectorAll('[data-quantity-step]')); 
295  var quantity = Math.max(1, parseInt(nativeQuantity ? nativeQuantity.value : '1', 10) || 1); 
296  if (cartButton && !cartButton.querySelector('[data-bti-cart-label]')) { 
297    var existingLabel = cartButton.querySelector('.text-truncate'); 
298    if (existingLabel) { existingLabel.textContent = 'Añadir al carrito'; existingLabel.setAttribute('data-bti-cart-label', 'true'); } 
299    else { var cartLabel = document.createElement('span'); cartLabel.setAttribute('data-bti-cart-label', 'true'); cartLabel.textContent = 'Añadir al carrito'; cartButton.insertBefore(cartLabel, cartButton.firstChild); } 
300  } 
301  function renderQuantity() { 
302    if (quantityValue) { quantityValue.textContent = String(quantity); } 
303    if (nativeQuantity) { nativeQuantity.value = String(quantity); nativeQuantity.dispatchEvent(new Event('change', {bubbles: true})); nativeQuantity.dispatchEvent(new Event('input', {bubbles: true})); } 
304    stepButtons.forEach(function (button) { button.disabled = button.getAttribute('data-quantity-step') === '-1' && quantity <= 1; }); 
305  } 
306  stepButtons.forEach(function (button) { button.addEventListener('click', function () { quantity = Math.max(1, quantity + parseInt(button.getAttribute('data-quantity-step'), 10)); renderQuantity(); }); }); 
307  renderQuantity(); 
308})(); 
309</script> 
310</#if> 
311 
312<#if isLoggedIn && productType == "grouped" && groupedEntries?has_content && !blocked> 
313<script> 
314(function () { 
315  var root = document.getElementById('${detailId}'); 
316  if (!root) { return; } 
317  var addButton = root.querySelector('[data-grouped-add]'); 
318  var status = root.querySelector('[data-purchase-status]'); 
319  var checks = Array.prototype.slice.call(root.querySelectorAll('.bti-product-detail__variant-check')); 
320  function selectedChecks() { return checks.filter(function (check) { return check.checked; }); } 
321  function updateButton() { if (addButton) { addButton.disabled = selectedChecks().length === 0; } } 
322  function showStatus(message, error) { if (!status) { return; } status.hidden = !message; status.textContent = message; status.setAttribute('data-error', error ? 'true' : 'false'); } 
323  checks.forEach(function (check) { check.addEventListener('change', updateButton); }); 
324  if (addButton) { addButton.addEventListener('click', async function () { 
325    var selected = selectedChecks(); 
326    if (!selected.length || !${commerceOrderId?c}) { return; } 
327    addButton.disabled = true; showStatus('Añadiendo productos al carrito...', false); 
328    try { 
329      for (var index = 0; index < selected.length; index++) { 
330        var check = selected[index]; 
331        var quantityInput = root.querySelector('.bti-product-detail__variant-quantity[data-sku-id="' + check.value + '"]'); 
332        var quantity = Math.max(1, parseInt(quantityInput ? quantityInput.value : '1', 10) || 1); 
333        var response = await Liferay.Util.fetch('/o/headless-commerce-delivery-cart/v1.0/carts/${commerceOrderId?c}/items/', {body: JSON.stringify({options: '[]', quantity: quantity, skuId: Number(check.value)}), headers: {'Content-Type': 'application/json'}, method: 'POST'}); 
334        if (!response.ok) { throw new Error(String(response.status)); } 
335      } 
336      showStatus('Productos añadidos al carrito.', false); Liferay.fire('commerce:product-added-to-cart', {}); 
337    } catch (error) { showStatus('No se han podido añadir los productos al carrito.', true); console.error('[BTI Product Detail] grouped add-to-cart', error); } 
338    finally { updateButton(); } 
339  }); } 
340})(); 
341</script> 
342</#if> 

BTI al día

Ventajas clínicas de los implantes BTI

Diseñados para mejorar la estabilidad y la integración en distintas situaciones clínicas.

Formación postgrado

Superficie UNICCA®

La superficie de los implantes BTI es hidrofílica, por lo que ayudará a tener una relación perfecta con el primer tejido vivo con el que se encuentra en la inserción. Minimiza la adhesión bacteriana, por lo que disminuye el riesgo de contaminación.

Cursos en el instituto Eduardo Anitua

Cursos en el instituto Eduardo Anitua

Permite una mejor adaptación de los tejidos blandos, favoreciendo su estabilidad y mantenimiento a lo largo del tiempo.

Cursos y colaboraciones

Cursos y colaboraciones

Facilita la restauración protésica al estandarizar la conexión y reducir la complejidad en distintas situaciones clínicas.

Título card 4

Descripción card 4

Título card 5

Descripción card 5

Título card 6

Descripción card 6

Título card 7

Descripción card 7

Título card 8

Descripción card 8

Título card 9

Descripción card 9

Título card 10

Descripción card 10

Contacta

Primer plano
Nombre *

Apellidos *

Correo electrónico *

¿Cómo quieres que contactemos contigo? *

Teléfono *

Código postal *

País *

Escribe aquí tu consulta *