Skip to content

Product Charges & Fees#


Description#

Available in version 10.11.00, the Product Charges and Fees module allows you to configure additional charges or fees that automatically add to a customer’s basket when they select specific products. These charges or fees are represented as separate products and will display in the basket as “child” items linked to the main product, ensuring clarity for the customer.

Because these items are treated as standard products, they follow the same rules for tax code assignment and returns, ensuring that taxes and refunds are handled accurately without requiring manual adjustments.

Note

Product Charges & Fees will work with the wish list and manage quotes items

Common Use Cases#

This feature is particularly useful in scenarios like:

  • Core Charges for Automotive Products: For example, purchasing an automotive battery may include a refundable core charge.
  • Environmental Fees: A non-refundable recycling fee may also apply when buying certain products, such as car batteries.

Installation#

To begin using this feature, install the module by navigating to Settings > Modules and searching for Product Charges and Fees. Once installed, a productcharges item will be created along with several new sections within the admin interface.

The productcharges item, when initialized, will load all charges linked to the current product residing in l.all_settings:product and make tokens available for the charge and the linked product. The basic fields of the charge name, description, and price will be available.

Updated Admin Settings#

Catalog > Charges & Fees#

The Charges & Fees section is a batch list that displays all charges or fees set up within the system. Key fields here include Code, Name, Price, and Type.

Charges & Fees Batchlist

From this screen, you can create new charges by selecting Add Charge Type.

Each charge has an Edit Charge screen that allows for configuration of pricing, taxability, description, and rules for payment and shipping, along with any tax module-specific rules if applicable. There are three available types including Core, Fee, or Other, these types are primarily for categorization. Additionally, this screen contains Products and Collections tabs for assigning products to the charge.

Charges & Fees Edit Screen

When assigning products to a charge, you have the option to override the charge price, making the assignment process a two-stage operation rather than a single toggle. Importantly, when a core charge price is overridden for a collection, only products not already assigned to the charge will have the price overwritten. If a product has been assigned to the charge through another collection or a manual assignment, it will be skipped during processing, avoiding duplicate charges. For server efficiency, only one collection can be assigned at a time due to the processing load with large collections. In the batch list, a new column called Overridden Charge Price will display for clarity, alongside the standard product fields.

Product Assignment

Catalog > Edit Product#

In the Edit Product section, a new Charges & Fees tab appears for each product, listing all charges assigned to that product. Like in the main Charges & Fees section, assigning charges here is a two-step process, allowing you to override the price of each charge as needed.

Shadows Updates#

Upon installing the module, updates to the Shadows framework may enhance the user experience. The updates listed below were introduced in Shadows version 10.11.00 to provide a seamless experience with the Product Charges and Fees module. While these updates are not required for the module to function correctly, they are recommended for an improved user experience. Additionally, the module’s flexibility allows you to create your own custom user experience tailored to your store’s specific needs. You can find all the updates made to the Shadows framework for the Product Charges and Fees module within our GitHub.

Charges and Fees Display#

New installs of Shadows Version 10.11.00 will include two fragments, product_display_productcharges and group_productcharges. These fragments have been created as an example layout for the Product Charges and Fees. Adding these fragments within the basket/order items loop or the product display template will show the product charges in clean and seamless UI for customers. To create the fragment navigate to User Interface > Fragments and select the Create Fragment button.

product_display_productcharges fragment#

Create a fragment with the code of product_display_productcharges and name of Product Display Product Charges then add the the following fragment code:

product_display_productcharges Fragment Template Code

This fragment has template code that will output productcharges:chargetypes array items. Specifically the name and amount of each Product Charge/Fee that is assigned to the item. (The description will also be available within a dialog.) This item needs the productcharges assigned for it to include the productcharges:chargetypes array. This fragment has been added to the Page: Product Display prod-product_display template.

group_productcharges fragment#

Create a fragment with the code of group_productcharges and name of Basket/Order Group Product Charges then add the the following fragment code:

group_productcharges Fragment Template Code

This fragment has template code that will output group:children array items. Specifically the name and amount of each Product Charge/Fee that is assigned to the item. (The description will also be available within a dialog.) This fragment has been added to the following templates within the Shadows framework:

Basket Page - Basket Contents#

One place you may want to implement the group_productcharges fragment is the Basket Contents area. To implement the fragment on the basket contents template find the following code within the template:

<p>
    <span class="u-text-medium u-block">&mvt:group:formatted_subtotal_comprehensive;</span>
    <mvt:assign name="g.basket_subtotal" value="g.basket_subtotal + l.settings:group:subtotal_comprehensive" />
    <mvt:if expr="l.settings:group:subtotal_base_price_comprehensive GT l.settings:group:subtotal_comprehensive">
        <s class="c-heading--subheading u-block u-color-gray-600">&mvt:group:formatted_subtotal_base_price_comprehensive;</s>
    </mvt:if>
    <mvt:if expr="l.settings:group:upsold">
        <span class="c-heading--subheading u-block">&nbsp;</span>
    </mvt:if>
</p>
Replace the code with the group_productcharges fragment code:

<mvt:fragment code="group_productcharges" />

You can see this update with the Shadows GitHub repository by clicking here.

Child Items#

Within templates containing a foreach of the groups array (or some cases items) a conditional statement will skip over any child items in the array. The following are instructions to update the multiple variations of the foreach loop within the Shadows framework using the Template Search and Replace module:

basket:groups Array Update#

  1. Navigate to Settings > Utilities > Template Search and Replace.
  2. Search for <mvt:foreach iterator="group" array="basket:groups"> to locate all templates with the old field reference.
  3. Enter the following in the Replace input field.
    <mvt:foreach iterator="group" array="basket:groups">
                            <mvt:if expr="l.settings:group:parent_id GT 0">
                                <mvt:comment>Skip child items</mvt:comment>
                                <mvt:foreachcontinue />
                            </mvt:if>
    
  4. Select the templates to update and click Replace Selected.
    1. Basket Contents (bask-basket)
    2. ReadyTheme Content Section: Checkout Basket Summary
  5. Add a Note then select Replace

Warning

Be sure to manually confirm all updates before selecting the Replace action.

order:groups Array Update#

The templates including the order:groups charges array should be updated manually.

  1. Order Status - Order Contents (ords-view_order)
  2. Order Status - Content (Printer Friendly Content) (ords-printer_friendly_content)

Global Settings - Global Mini Basket#

The global mini basket template has a number of updates to the template which should be updated manually. Aside from the addition of the child item conditional and update to basket_count the iterator of the global_minibasket:groups loop has been updated to use group rather than item.

  1. Global Settings - Global Mini Basket (global_minibasket)

Update basket_count#

On various templates, the basket_count field has been renamed to parent_basket_count to display only the parent product count. This update allows for the basket count value displayed throughout the Shadows framework to show the total number of parent items rather than including the child items.

All template updates related to this can be found within the Shadows GitHub repository.

To update all instances of basket_count efficiently:

Install Template Search & Replace module#

  1. Go to Settings > Modules.
  2. Search for Template Search And Replace and click Install.

Warning

Updates using the Template Search and Replace module will occur on the working branch.

Use Template Search & Replace Module#

  1. Navigate to Settings > Utilities > Template Search and Replace.
  2. Search for :basket_count to locate all templates with the old field reference.
  3. Enter :parent_basket_count in the Replace input field.
  4. Select the templates (19 Total) to update and click Replace Selected.
    1. Page: Basket Contents (bask-basket) - 7 Instances
    2. Page: Checkout: Basket Empty (bske) - 1 Instance
    3. Global Settings > Global Header (cssui-global-header) - 4 Instances
    4. Global Settings > Global Mini Basket (global-minibasket) - 2 Instances
    5. Theme Components > Content Section - Checkout Basket Summary - 3 Instances
    6. Theme Components > Content Section - Global “Sticky” Header - 2 Instances
  5. Add a Note then select Replace

This will replace all instances of :basket_count with :parent_basket_count across the selected templates.

Warning

If the framework has been modified there may be other templates that are using basket_count field. Be sure to manually confirm all updates before selecting the Replace action.

Charges CSS#

To add the Product Charges & Fees Shadows CSS navigate to User Interface > CSS/JavaScript Resources and find the following CSS resources to edit:

  • theme.css : Here you can find the majority of the CSS updates needed for the Shadows product charges and fees layout.
  • extensions.css: Here you will find updates regarding the display of the description dialog for the Shadows product charges and fees layout.
  • core.css: Here you will find updates regarding the display of the description dialog for the Shadows product charges and fees layout.

Charges JS#

There were updates made to the extensions.js resource for the dialog on the charges description to function properly with Flex Components. To add the Product Charges & Fees Shadows JS updates navigate to User Interface > CSS/JavaScript Resources and find the following CSS resources to edit:

  • extensions.js: Here you will find updates regarding the display of the description dialog with for Flex Components.

Attribute Machine JavaScript#

To incorporate attribute machine with product charges and fees an update needs to be made to the attribute machine head template(s). The update can be found in the Shadows GitHub repository.

Update the following:

AttributeMachine.prototype.Generate_Discount = function (discount) {
    let discount_div;

    discount_div = document.createElement('div');
    discount_div.innerHTML = discount.descrip + ': ' + discount.formatted_discount;

    return discount_div;
};

Updated product charges and fees attribute machine head template:

AttributeMachine.prototype.Generate_Discount = function (discount) {
    const charge = document.createElement('div');
    charge.classList.add('t-product-charge', 't-product-charge--discount');

    const chargeName = document.createElement('div');
    chargeName.classList.add('t-product-charge__name');
    chargeName.innerHTML = discount.descrip;

    const chargeAmount = document.createElement('div');
    chargeAmount.classList.add('t-product-charge__amount');
    chargeAmount.innerHTML = `-${discount.formatted_discount}`;

    charge.appendChild(chargeName);
    charge.appendChild(chargeAmount);

    return charge;
};

Provisioning#

Provisioning available to add, update, delete, assign, and unassign (also from collections):

<ProductChargeType_Add>
    <Type>core,fee,other</Type>
    <Code>code</Code>
    <Name>name</Name>
    <Price>1.11</Price>
    <Cost>2.22</Cost>
    <Weight>3.33</Weight>
    <Description>description</Description>
    <Taxable>true|false</Taxable>
</ProductChargeType_Add>

<ProductChargeType_Update charge_code="existing">
    <Type>core,fee,other</Type>
    <Code>code</Code>
    <Name>name</Name>
    <Price>1.11</Price>
    <Cost>2.22</Cost>
    <Weight>3.33</Weight>
    <Description>description</Description>
    <Taxable>true|false</Taxable>
</ProductChargeType_Update>

<ProductChargeType_Delete charge_code="existing" />

<ProductChargeTypeProduct_Assign charge_code="charge" product_code="existing">
    <!-- Optional -->
    <OverridePrice>true|false</OverridePrice>
    <Price>9.95</Price>
</ProductChargeTypeProduct_Assign>

<ProductChargeTypeProduct_Update charge_code="charge" product_code="existing">
    <OverridePrice>false</OverridePrice>
    <Price>0.00</Price>
</ProductChargeTypeProduct_Update>

<ProductChargeTypeProduct_Unassign charge_code="charge" product_code="existing" />

<ProductChargeTypeCollection_Assign charge_code="test" collection_code="collection">                
    <!-- Optional -->
    <OverridePrice>true|false</OverridePrice>
    <Price>9.95</Price>
</ProductChargeTypeCollection_Assign>

<ProductChargeTypeCollection_Update charge_code="test" collection_code="collection">
    <OverridePrice>false</OverridePrice>
    <Price>0.00</Price>
</ProductChargeTypeCollection_Update>

<ProductChargeTypeCollection_Unassign charge_code="test" collection_code="collection" />

Additional Notes#

Database Update#

This feature includes a database update which introduces a type field to enhance product identification and item management. When type is not specified or left blank, and a matching product exists (i.e., orderitem:product_id is non-zero), the system will automatically assign a type of product. This ensures consistent handling of items linked to product codes.

The following functions now support an optional type parameter:

  • BasketItem_Read
  • OrderItem_Read
  • QuoteItem_Read

Updated Item Structure#

The item structure has been revised to include additional fields. A new variable, parent_xxx_count, now tracks the count of parent products, while each item record will include a :parent_id field to identify parent-child relationships.

The following functions have been updated to incorporate parent_id:

  • QuoteItem_Insert
  • OrderItem_Insert_LowLevel
  • WishListItem_Insert
  • BasketItem_Insert_LowLevel