Skip to content
This repository has been archived by the owner on Mar 7, 2024. It is now read-only.

Latest commit

 

History

History
676 lines (578 loc) · 22.6 KB

File metadata and controls

676 lines (578 loc) · 22.6 KB

Static Bundles

Table of contents

Overview

The static bundles solution handles the concept of putting together multiple related products for a total-sum price. An example static bundle is selling a camera, bag, and SD card for a single bundled price. The customer purchases a single bundle and then receives two or more distinct inventory items as selected by merchandising.

This process has been achieved by creating a new product with a specific type that references all the items within the bundle. On adding this new item to the cart, an API extension platform-extension-static-bundles is executed for cart requests to manage the child items during checkout. The final order includes the bundle items with pricing and a list of child line items for fulfillment.

The Merchant Center custom application in this solution assists merchandisers in creating and managing static bundles.

Technology

Features

Bundles List

The landing page for the static bundles custom application is a list displaying the static bundles within the commercetools project.

Bundles List

  • Pagination
    • Default page size of 30,
    • Displayed when the number of static bundles exceeds the page size
  • Sorting
    • Sortable columns are Name, Price, and Last Modified
    • Initial sort is Last Modified in descending order (most recent to least recent bundles)
  • Search for a bundle using product projections search
  • Filter
    • By bundle category (must be set on bundle from Merchant Center Products application. Ex: https://mc.us-central1.gcp.commercetools.com/{projectKey}/products/{bundleId})
    • By product variant within bundle

Bundle Creation

Create Bundle

  • Single step to create a static bundle with basic information
  • Search for a product variant using product projections search
  • Automatic slug creation based on the name of the bundle, which can be modified to suit your needs

Bundle Details

Manage bundle information, add images, and determine pricing strategies.

  • Manage the bundle's published status
  • Delete unpublished bundle

General

Edit the bundle's general information.

Bundle Details - General

Images

Bundle Details - Images

  • Add image(s) to the bundle from the images of the bundle's components (product variants)
  • Add image(s) directly to the bundle via Merchant Center
  • Remove images
  • Manage other image properties via Merchant Center

Prices

View the prices of the bundle components in various scopes to determine its prices.

Bundle Details - Prices

  • View bundle component prices in different price scopes
  • Add a price via Merchant Center
  • View prices via Merchant Center

Architecture

Product

A bundle is a commercetools product with only a master variant. The bundle components are stored as attributes of the master variant in accordance with the bundle product types.

Product Types

StaticBundleParent

  • products - Array of StaticBundleChildVariant - Required
    The components of the bundle
  • productsSearch - Array of String - Required
    The bundle components stringified as {productId}/{variantId} and used to filter the bundle list

StaticBundleChildVariant

A bundle component, which is a product variant.

  • variant-id - Number - Required
    The ID of the product variant.
  • quantity - Number - Required
    The number of the component within the bundle.
  • sku - String
    The SKU of the product variant. Used for display purposes within the product search when viewing a bundle.
  • product-ref - Product Reference
    A reference to the product associated with the bundle product variant. Used to create the product search value.
  • product-name - LocalizedString
    The name of the product associated with the bundle product variant. Used for display purposes within the product search when viewing a bundle.

Note: If any of the underlying values of the selected product variant or product change, the attribute values will not be updated until the bundle component in question is re-selected and saved on the bundle with the new values.

StaticBundleParent
StaticBundleChildVariant

Sample Bundle

Click to expand
{
  "id": "c7e3e38e-870d-4359-8fbf-1cc4b4c53eae",
  "version": 74,
  "productType": {
    "typeId": "product-type",
    "id": "1a1b24be-ae4a-4b98-a2cd-3225d5e7ce04",
    "obj": {
      "id": "1a1b24be-ae4a-4b98-a2cd-3225d5e7ce04",
      "version": 6,
      "createdAt": "2019-08-26T14:38:06.189Z",
      "lastModifiedAt": "2019-10-09T13:13:52.005Z",
      "lastModifiedBy": {
        "isPlatformClient": true,
        "user": {
          "typeId": "user",
          "id": "f2303e24-a06c-458c-8986-363b8cf208c4"
        }
      },
      "createdBy": {
        "clientId": "RftQf1edB6sYPMh7uq8GisJ_",
        "isPlatformClient": false
      },
      "name": "StaticBundleParent",
      "description": "A static bundle of product variants",
      "classifier": "Complex",
      "attributes": [
        {
          "name": "products",
          "label": { "en": "Products" },
          "isRequired": false,
          "type": {
            "name": "set",
            "elementType": {
              "name": "nested",
              "typeReference": {
                "typeId": "product-type",
                "id": "25583dd7-4177-4c3d-908b-a5030ca2d029"
              }
            }
          },
          "attributeConstraint": "None",
          "isSearchable": true,
          "inputHint": "SingleLine",
          "displayGroup": "Other"
        },
        {
          "name": "productSearch",
          "label": { "en": "Products (Search)" },
          "isRequired": false,
          "type": { "name": "set", "elementType": { "name": "text" } },
          "attributeConstraint": "None",
          "isSearchable": true,
          "inputHint": "SingleLine",
          "displayGroup": "Other"
        }
      ],
      "key": "static-bundle-parent"
    }
  },
  "name": { "en": "Travel Bundle", "de": "Noch ein Bundle" },
  "description": {
    "de": "Alles was Sie brauchen, um zu wandern.",
    "en": "Everything you need to wander."
  },
  "categories": [],
  "categoryOrderHints": {},
  "slug": { "en": "travel-bundle" },
  "masterVariant": {
    "id": 1,
    "prices": [
      {
        "value": {
          "type": "centPrecision",
          "currencyCode": "USD",
          "centAmount": 17339,
          "fractionDigits": 2
        },
        "id": "d7f33a98-5f4f-4490-819b-0e4a396d4ed2"
      },
      {
        "value": {
          "type": "centPrecision",
          "currencyCode": "USD",
          "centAmount": 16000,
          "fractionDigits": 2
        },
        "id": "06e1f3be-fbb6-4125-bb18-61fe310d2ef9",
        "customerGroup": {
          "typeId": "customer-group",
          "id": "182489ba-57a0-486b-ab91-c3bc7d30976b",
          "obj": {
            "id": "182489ba-57a0-486b-ab91-c3bc7d30976b",
            "version": 2,
            "createdAt": "2019-05-31T14:02:03.178Z",
            "lastModifiedAt": "2019-09-27T16:33:43.807Z",
            "lastModifiedBy": {
              "isPlatformClient": true,
              "user": {
                "typeId": "user",
                "id": "f2303e24-a06c-458c-8986-363b8cf208c4"
              }
            },
            "createdBy": {
              "clientId": "AS2q4W4InSWREs5JQBltxP5V",
              "isPlatformClient": false
            },
            "name": "Platinum",
            "key": "platinum"
          }
        }
      }
    ],
    "images": [
      {
        "url": "https://s3-eu-west-1.amazonaws.com/commercetools-maximilian/products/072599_1_medium.jpg",
        "dimensions": { "w": 0, "h": 0 }
      },
      {
        "url": "https://s3-eu-west-1.amazonaws.com/commercetools-maximilian/products/084058_1_large.jpg",
        "dimensions": { "w": 0, "h": 0 }
      },
      {
        "url": "https://s3-eu-west-1.amazonaws.com/commercetools-maximilian/products/085391_1_large.jpg",
        "dimensions": { "w": 0, "h": 0 }
      },
      {
        "url": "https://s3-eu-west-1.amazonaws.com/commercetools-maximilian/products/085314_1_medium.jpg",
        "dimensions": { "w": 0, "h": 0 }
      }
    ],
    "attributes": [
      {
        "name": "products",
        "value": [
          [
            { "value": 1, "name": "variant-id" },
            { "value": "A0E200000002ABW", "name": "sku" },
            { "value": 2, "name": "quantity" },
            {
              "value": {
                "typeId": "product",
                "id": "52b4bf82-4050-4dfc-be35-75c1621cd63b"
              },
              "name": "product-ref"
            },
            {
              "value": {
                "de": "Polaroidkamera „Spirit 600 CL“ Impossible schwarz",
                "en": "Polaroid Camera “Spirit 600 CL“ Impossible black"
              },
              "name": "product-name"
            }
          ],
          [
            { "value": 2, "name": "variant-id" },
            { "value": "M0E20000000EH7O", "name": "sku" },
            { "value": 1, "name": "quantity" },
            {
              "value": {
                "typeId": "product",
                "id": "038d1661-f61e-4b01-976c-0dfb523056ba"
              },
              "name": "product-ref"
            },
            {
              "value": {
                "de": "Shorts Maison Scotch multi",
                "en": "Shorts Maison Scotch multi"
              },
              "name": "product-name"
            }
          ],
          [
            { "value": 7, "name": "variant-id" },
            { "value": "M0E20000000DJQB", "name": "sku" },
            { "value": 1, "name": "quantity" },
            {
              "value": {
                "typeId": "product",
                "id": "e999a590-0321-4956-bb87-cafb84b3c660"
              },
              "name": "product-ref"
            },
            {
              "value": { "en": "Shirt Pinko beige", "de": "Bluse Pinko beige" },
              "name": "product-name"
            }
          ],
          [
            { "value": 1, "name": "variant-id" },
            { "value": "M0E20000000F67V", "name": "sku" },
            { "value": 1, "name": "quantity" },
            {
              "value": {
                "typeId": "product",
                "id": "eb861953-7620-4c56-8f05-8e659af20e5a"
              },
              "name": "product-ref"
            },
            {
              "value": { "en": "Hogan – Sneaker", "de": "Hogan – Sneaker" },
              "name": "product-name"
            }
          ],
          [
            { "value": 7, "name": "variant-id" },
            { "value": "M0E20000000F753", "name": "sku" },
            { "value": 3, "name": "quantity" },
            {
              "value": {
                "typeId": "product",
                "id": "79ee9759-e37b-4b0c-b615-fa46dd2aa77b"
              },
              "name": "product-ref"
            },
            {
              "value": { "de": "Cycle – Jeans", "en": "Cycle – Jeans" },
              "name": "product-name"
            }
          ]
        ]
      },
      {
        "name": "productSearch",
        "value": [
          "52b4bf82-4050-4dfc-be35-75c1621cd63b/1",
          "038d1661-f61e-4b01-976c-0dfb523056ba/2",
          "e999a590-0321-4956-bb87-cafb84b3c660/7",
          "eb861953-7620-4c56-8f05-8e659af20e5a/1",
          "79ee9759-e37b-4b0c-b615-fa46dd2aa77b/7"
        ]
      }
    ],
    "assets": []
  },
  "variants": [],
  "searchKeywords": {},
  "hasStagedChanges": true,
  "published": true,
  "key": "c7e3e38e-870d-4359-8fbf-1cc4b4c53eae",
  "taxCategory": {
    "typeId": "tax-category",
    "id": "a1af21d4-444c-409d-82a6-c3163b28b8e9",
    "obj": {
      "id": "a1af21d4-444c-409d-82a6-c3163b28b8e9",
      "version": 1,
      "createdAt": "2019-05-31T14:01:54.035Z",
      "lastModifiedAt": "2019-05-31T14:01:54.035Z",
      "lastModifiedBy": {
        "clientId": "AS2q4W4InSWREs5JQBltxP5V",
        "isPlatformClient": false
      },
      "createdBy": {
        "clientId": "AS2q4W4InSWREs5JQBltxP5V",
        "isPlatformClient": false
      },
      "name": "standard",
      "rates": [
        {
          "name": "20% incl.",
          "amount": 0.2,
          "includedInPrice": true,
          "country": "AT",
          "id": "8us0Y-Ay",
          "subRates": []
        },
        {
          "name": "10% incl.",
          "amount": 0.1,
          "includedInPrice": true,
          "country": "US",
          "id": "EGg9RL8e",
          "subRates": []
        },
        {
          "name": "19% incl.",
          "amount": 0.19,
          "includedInPrice": true,
          "country": "DE",
          "id": "FIHSMRUP",
          "subRates": []
        }
      ],
      "key": "standard"
    }
  },
  "createdAt": "2019-08-29T18:25:03.256Z",
  "lastModifiedAt": "2020-07-31T15:37:26.719Z"
}

Configuration

A terraform script initializes the commercetools project for using static bundles. Prior to using static bundles, this terraform script must be executed against the commercetools project and will deploy:

Installation

Simply run yarn from the repository root to install the application's dependencies.

Development

Start the development server

Run the following command to start the development server and launch the application:

yarn start

If this is the first time running the application locally, open an .env.local file. Based on your region, you may find it necessary to modify the values in the env file.

Troubleshooting

graphql_error.invalid_token error

{: .no_toc }

Log out of Merchant Center. Log back in, then return to the custom application and reload.

Do's and Don'ts

{: .no_toc }

  • Don't use the application development login screen to authenticate.
  • Do make sure you are logged in to Merchant Center before developing or running a custom application.

Linting & Formatting

Formatting code

Run the following command to format JS, CSS, JSON and GraphQL files

yarn format

Linting code

Run the following command to lint JS, CSS, and GraphQL files

yarn lint
Linting GraphQL Queries

{: .no_toc }

A prerequisite for linting GraphQL queries is generating a schema.graphql file, which contains the Types exposed by CTP API. Every time the API introduces new Types, Queries or Mutations, the local schema.graphql must be updated.

Generating CTP GraphQL schema

{: .no_toc }

  1. If you haven't done so already, create an API client under Settings -> Developer Settings in Merchant Center for your project
  2. Generate an access token using the Client Credentials flow
  3. Export both your Merchant Center project key and generated access token as environment variables
  4. Retrieve schema with graphql-cli
export PROJECT_KEY={project_key}
export AUTH_TOKEN={access_token}
npx graphql-cli get-schema

Git Hooks

Git hooks are configured using Husky. The root workspace runs all workspace hooks using Lerna (example repository). The hooks are configured as follows:

  • Pre-commit: JS, CSS, and GraphQL files are linted (ESLint/Stylelint) and formatted (Prettier). Fixes are automatically added to Git.
  • Commit Message: Commit messages are linted against the conventional commit format using commitlint

Tests

Run the following command to run the tests:

yarn test

To run the tests in watch mode:

yarn test:watch

To run the tests with coverage:

yarn test:coverage

Build & Deployment

Run the following command to build the production bundles with webpack:

yarn build

Please check for deployment examples documentation here.

NOTE: Be sure to set the env vars for the placeholders in custom-application-config.mjs.

  • Example: For AWS deployment, env variables can be set using the file env.aws. For other deployments, duplicate the file and set values accordingly.

For more information on how to use .env files, check official documentation.

Registration with Merchant Center

After deploying the custom application, it needs to be registered with a Merchant Center project.

Configuration Values

{: .no_toc }

  • Main Route Path: bundle-manager
  • Link Permissions: Manage Products, View Products

Merchant Center Registration

Complete the Solution: Your Implementation Responsibilities

To complete the bundles solution, you will need to supply a product detail page (PDP) for the static bundle products in your frontend implementation. Depending on your use case, the PDP can utilize the bundle attributes to display the individual bundle components, or you can rely on the bundle product's images to convey that information. Using the latter option means that you can use the same PDP implementation to display all of your commercetools products.

Support

Please create an issue in the repository for all support requests related to the static bundles solution.