Cart normalizer
The normalizeCart
function is used to map a BigCommerce Cart into the unified SfCart
data model.
Parameters
Name | Type | Default value | Description |
---|---|---|---|
context | NormalizerContext | Context which contains e.g. currency | |
cart | Cart | BigCommerce Cart |
Extending
The SfCart
structure is returned from all Unified Cart Methods such as GetCart
, AddCartLineItem
, and UpdateCartLineItem
. If the SfCart
structure doesn't contain the information you need for your Storefront, you can extend its logic using the addCustomFields
API. The following example demonstrates how to extend SfCart
with a parentId
field.
export const unifiedApiExtension = createUnifiedExtension({
normalizers: {
addCustomFields: [
{
normalizeCart: (context, cart) => ({
parentId: cart.parent_id,
}),
},
],
},
config: {
...
},
});
You can override the normalizeCart
, but it's also available to override the smaller normalizers such as normalizeCartCoupon
, normalizeShippingMethod
, normalizeCartLineItem
.
Source
The normalizeCart
function consists of several smaller normalizers such as normalizeCartCoupon
, normalizeShippingMethod
, and more.
cart.ts
import type { NormalizerContext } from "@/normalizers/types";
import { maybe } from "@shared/utils";
import type { Cart, LineItems } from "@vsf-enterprise/bigcommerce-api";
import type { SfCart, SfCartLineItem } from "@vue-storefront/unified-data-model";
import { defineNormalizer } from "../defineNormalizer";
type CartTotals = Pick<
SfCart,
| "subtotalDiscountedPrice"
| "subtotalRegularPrice"
| "totalCouponDiscounts"
| "totalPrice"
| "totalShippingPrice"
| "totalTax"
>;
export const normalizeCart = defineNormalizer.normalizeCart((context, cart) => {
const ctx = { ...context, currency: cart.currency?.code as string };
const lineItems = normalizeCartLineItems(cart.line_items, ctx);
return {
appliedCoupons:
cart.coupons?.map((coupon) => ctx.normalizers.normalizeCartCoupon(coupon)) ?? [],
billingAddress: null,
customerEmail: maybe(cart.email || null),
id: cart.id as string,
lineItems,
shippingAddress: null,
shippingMethod: null,
totalItems: lineItems.length,
...normalizeCartTotals(cart, lineItems, ctx),
};
});
function normalizeCartTotals(
cart: Cart,
lineItems: SfCartLineItem[],
ctx: NormalizerContext,
): CartTotals {
const lineItemsAmount = lineItems.reduce(
(acc, item) => acc + (item.unitPrice?.regularPrice.amount || 0) * item.quantity,
0,
);
const subtotalRegularPrice = ctx.normalizers.normalizeMoney(+lineItemsAmount.toFixed(2));
return {
subtotalDiscountedPrice: getSubtotalDiscountedPrice(cart, ctx),
subtotalRegularPrice: subtotalRegularPrice,
totalCouponDiscounts: ctx.normalizers.normalizeMoney(
cart.coupons?.reduce((acc, curr) => acc + (curr.discounted_amount ?? 0), 0) ?? 0,
),
totalPrice: ctx.normalizers.normalizeMoney(cart.cart_amount ?? 0),
totalShippingPrice: null, // @TODO shipping price is not available
totalTax: ctx.normalizers.normalizeMoney(0), // @TODO tax is not available
};
}
function getSubtotalDiscountedPrice(cart: Cart, ctx: NormalizerContext) {
const { base_amount = 0, line_items } = cart;
const lineItems = [...(line_items?.physical_items ?? []), ...(line_items?.digital_items ?? [])];
const discounts = lineItems.reduce(
(acc, curr) => acc + (curr.extended_list_price ?? 0) - (curr.extended_sale_price ?? 0),
0,
);
return ctx.normalizers.normalizeMoney(base_amount - discounts);
}
function normalizeCartLineItems(
lineItems: LineItems | null | undefined,
ctx: NormalizerContext,
): SfCartLineItem[] {
if (!lineItems) {
return [];
}
return [
...lineItems.physical_items.map((item) => ctx.normalizers.normalizeCartLineItem(item)),
...lineItems.digital_items.map((item) => ctx.normalizers.normalizeCartLineItem(item)),
];
}