Saltar al contenido principal

DNN · Cart bridge

A diferencia de WooCommerce (que tiene eventos jQuery estandarizados), los módulos de e-commerce de DNN no comparten una API común. El cart bridge en DNN es manual: tu código emite window.dispatchEvent(new CustomEvent('neuroon:cart-update')) en los puntos donde tu módulo modifica el carrito.

Contrato del puente

El widget escucha neuroon:cart-update y, al recibirlo, llama a config.cart.onGetCart() para releer el carrito desde tu host. Ignora event.detail — sólo dispara sin payload (verificado en widget/src/context/CartContext.tsx:165-191).

Esto significa que tu trabajo en DNN es:

  1. Implementar cart.onGetCart() en JS para que devuelva el CartState desde tu módulo (Razor lo renderiza inline, o un endpoint AJAX lo expone).
  2. Emitir neuroon:cart-update cada vez que tu módulo muta el carrito (add, remove, update, clear).

Nada de event.detail: aunque dispares con { detail: cartState }, el listener actual lo descarta. El estado siempre llega vía onGetCart().

Schema del CartState

Lo que tu cart.onGetCart() debe devolver:

type CartItem = {
key: string; // identificador único de la línea (ID en tu tabla o GUID)
id: string; // tu externalId / productId (lo que mandas en sync)
name: string;
price: string; // formateado por el host: "12,50 €"
quantity: number;
image?: string;
url?: string;
variant?: string; // "Talla: 42, Color: Negro"
externalId?: string; // host-platform id si difiere del id Neuroon
maxQuantity?: number;
};

type CartState = {
items: CartItem[];
totalItems: number;
subtotal: string; // formateado por el host
total: string; // formateado por el host
currency: string; // ISO 4217: "EUR", "USD"
shipping?: CartShippingInfo; // ver Widget → Cart integration para el schema completo
};

Importante: price, subtotal, total son strings ya formateados por el host (con currency, separadores locales). El widget no recalcula. Esto evita disputas de redondeo entre regiones.

Razor partial reutilizable

Crea un partial que cualquier vista de carrito pueda renderizar:

@model CartPayload
<script>
(function() {
var detail = @Html.Raw(JsonConvert.SerializeObject(Model));
window.dispatchEvent(new CustomEvent('neuroon:cart-update', { detail: detail }));
})();
</script>

Y desde tu vista de "Add to cart":

@{
var payload = new CartPayload {
Items = currentCart.Lines.Select(l => new CartItem {
ProductId = l.Sku,
Name = l.ProductName,
Price = l.UnitPrice,
Quantity = l.Quantity,
ImageUrl = l.ImageUrl
}).ToList(),
Total = currentCart.Total,
Currency = currentCart.Currency
};
}
@Html.Partial("_NeuroonCartUpdate", payload)

Hook por módulo (DNN Commerce, NB_Store, Hotcakes)

Cada módulo expone su propio mecanismo. Estas son las superficies habituales; verifica con la versión exacta de tu módulo (los nombres pueden variar entre majors).

NB_Store / NBrightBuy

NB_Store usa eventos AJAX en el frontend: nbsAddItem, nbsRemoveItem, nbsUpdateQty. Engánchalos desde el Skin (Skin.ascx) o desde un módulo propio cargado en todas las páginas:

<script>
(function() {
function debounce(fn, ms) {
var t; return function() { clearTimeout(t); t = setTimeout(fn, ms); };
}
var ping = debounce(function() {
window.dispatchEvent(new CustomEvent('neuroon:cart-update'));
}, 250);

document.addEventListener('nbsAddItem', ping);
document.addEventListener('nbsRemoveItem', ping);
document.addEventListener('nbsUpdateQty', ping);
})();
</script>

Server-side (al renderizar la página de carrito o cualquier vista que muestre líneas):

// En NBrightBuy.NBrightBuyUtils.AddCartItem o tu wrapper, tras persistir:
var snapshot = MapCartToNeuroonState(NBrightBuyUtils.GetCurrentCart(portalId));
ViewBag.NeuroonCart = snapshot; // CartState canónico
return PartialView("_NeuroonCartUpdate", snapshot);

DNN Commerce / Catalook

DNN Commerce usa el BasketController (MVC) con acciones Add, Remove, Update. Patrón:

public class BasketController : Controller
{
public ActionResult Add(int productId, int qty)
{
// ... lógica original ...
var payload = MapBasketToNeuroonState(this.CurrentBasket);
return PartialView("_NeuroonCartUpdate", payload);
}
}

_NeuroonCartUpdate.cshtml (modo push):

@model NeuroonCartState
<script>
window.dispatchEvent(new CustomEvent('neuroon:cart-update', {
detail: @Html.Raw(JsonConvert.SerializeObject(Model))
}));
</script>

Hotcakes Commerce

Hotcakes expone CartController::AddToCart y dispara el evento global Hotcakes.Modules.Cart.OrderAddedToCart. Patrón modo ping (recomendado):

public override void OnOrderItemAdded(OrderItemEventArgs e)
{
base.OnOrderItemAdded(e);
// Render Razor partial that emits ping:
// <script>window.dispatchEvent(new CustomEvent('neuroon:cart-update'));</script>
}

Si tu versión de Hotcakes no expone OnOrderItemAdded, suscríbete vía EventLog o haz polling client-side en cart.ascx.

Módulo propio o módulo no listado

Si tu módulo dispara un evento jQuery (por ejemplo cart:updated), engánchalo de forma global desde el Skin:

<script>
(function() {
var debounceTimer = null;
document.addEventListener('cart:updated', function(e) {
if (debounceTimer) clearTimeout(debounceTimer);
debounceTimer = setTimeout(function() {
// Modo ping (recommended): el widget llama a tu API de carrito
window.dispatchEvent(new CustomEvent('neuroon:cart-update'));
}, 250);
});
})();
</script>

El debounce de 250 ms replica lo que hace el plugin WordPress (ver plugins/wordpress/cart-bridge).

Si tu módulo no está aquí: expón cualquier hook server-side (OnAdd/OnRemove/OnComplete) o un dispatchEvent jQuery, y dispara neuroon:cart-update desde el handler. El widget no necesita saber qué módulo usas.

SSR en Razor (.cshtml)

Si tu carrito se renderiza server-side y el carrito ya viene poblado en el modelo, emite el CustomEvent directamente desde el HTML:

<script>
window.dispatchEvent(new CustomEvent('neuroon:cart-update', {
detail: {
items: [
@foreach (var line in Model.Cart.Lines)
{
<text>{ productId: '@line.Sku', name: '@line.Name.Replace("'", "\\'")', price: @line.Price.ToString(System.Globalization.CultureInfo.InvariantCulture), quantity: @line.Quantity },</text>
}
],
total: @Model.Cart.Total.ToString(System.Globalization.CultureInfo.InvariantCulture),
currency: '@Model.Cart.Currency'
}
}));
</script>

Atención al escaping de comillas en name. Para casos seguros, usa JsonConvert.SerializeObject en lugar de inline string interpolation.

Validar que el bridge funciona

  1. Abre la consola del navegador en una página con producto.
  2. Suscríbete: window.addEventListener('neuroon:cart-update', e => console.log(e.detail));.
  3. Añade un producto al carrito desde tu módulo. Deberías ver el log con el payload completo.

Cuándo NO emitir el payload completo

El widget también acepta el evento sin detail. En ese caso, el widget pide al servidor el estado actualizado del carrito. Esto es útil cuando el carrito es complejo (ofertas, descuentos, tax) y prefieres no exponer ese cálculo en JavaScript:

window.dispatchEvent(new CustomEvent('neuroon:cart-update'));

Próximos pasos