Skip to content

Promotion Types ​

The Commerce promotion engine has no built-in promotion logic. All promotion types are provided by apps. A promotion type consists of two parts:

  • A Filtrera promotion script (.hpr) that computes discounts against an order
  • A Vue settings component that renders the type's configuration UI in the portal

The Promotion Script ​

Promotion scripts use the .hpr extension and are referenced by a componentId. The runtime provides the script with an order value containing the full order state, and calls the script once per promotion application.

Input ​

The order value is available globally in the script (no param declaration needed):

filtrera
order.orderId          // uuid
order.currencyCode     // text
order.channelKey       // text
order.orderTotal       // number
order.deliveries       // [Delivery]
  .shippingPrice       // number
  .orderLines          // [OrderLine]
    .orderLineId       // uuid
    .productNumber     // text
    .quantity          // number
    .unitPrice         // number

Parameters ​

Declare param statements to receive configuration from the promotion's settings:

filtrera
param threshold: number = 0
param discountIsPercentage: bool = true

Parameter values are set by the portal UI via the settings component and stored on the promotion rule. The runtime injects them when the script executes.

Discount Effects ​

Scripts produce discount effects using two built-in functions:

FunctionDescription
percentage(target, percent)Apply a percentage discount to matching entities
absolute(target, amount)Apply a fixed-amount discount to matching entities

Both functions take a target(predicate) expression that selects which order entities receive the discount:

filtrera
// Discount all order lines by 10%
percentage(target(e => e is OrderLine), 10%)

// Discount a specific delivery by 100%
percentage(target(d => d is Delivery and d.deliveryId == someId), 100%)

// Fixed amount off all order lines
absolute(target(e => e is OrderLine), 50)

A script can produce multiple discount effects — one per from statement.

Messages ​

Scripts can return a user-facing message instead of (or alongside) a discount:

filtrera
from {
  type = 'message'
  message = $'Add {threshold - order.orderTotal} more to qualify!'
}

Messages are surfaced in the rendered cart and can be used to communicate promotion thresholds to the customer.

The Settings Component ​

The settings component is a standard Vue component that receives the promotion context as props:

typescript
interface PromotionTypeSlotContext {
  promotionKey: string
  promotionType: string
  parameters: Record<string, any>
  setParameter: (key: string, value: any) => void
  removeParameter: (key: string) => void
  isNew: boolean
}

Use setParameter and removeParameter to manage the type-specific parameters that will be passed to the .hpr script at runtime:

vue
<script setup lang="ts">
import { computed } from 'vue'

const props = defineProps<{
  promotionKey: string
  promotionType: string
  parameters: Record<string, any>
  setParameter: (key: string, value: any) => void
  removeParameter: (key: string) => void
  isNew: boolean
}>()

const threshold = computed({
  get: () => props.parameters['threshold'] ?? 0,
  set: (value) => {
    if (value > 0) {
      props.setParameter('threshold', value)
    } else {
      props.removeParameter('threshold')
    }
  },
})
</script>

Parameter names must match the param declarations in the .hpr script exactly.

Registering the Type ​

1. Declare the component in h_app.yaml ​

yaml
components:
  - id: promotions/my-promotion-type.hpr

The id determines the componentId you'll reference during registration.

2. Register in portal/index.ts ​

The Commerce app exposes its promotion-type extension point as a service with the ID apps/commerce/promotionTypeProvider. Declare it locally with its contract and register your provider via portal.registerService:

typescript
import { apps, portal } from '@hantera/portal-app'
import MyPromotionSettings from './components/MyPromotionSettings.vue'

interface PromotionTypeProvider {
  types: Array<{
    key: string
    label: string
    componentId: string
    component: any   // Vue component
  }>
}

const promotionTypeService = apps.defineService<PromotionTypeProvider>(
  'apps/commerce/promotionTypeProvider'
)

portal.registerService(promotionTypeService, () => ({
  types: [
    {
      key: 'my-promotion-type',
      label: 'My Promotion Type',
      componentId: 'apps/my-app/promotions/my-promotion-type.hpr',
      component: MyPromotionSettings,
    },
  ],
}))

The key is a stable identifier used to associate portal-created promotions with this type. The componentId must match the fully-qualified ID of the .hpr component as it appears in your app.

Once registered, the type appears in the promotion type selector when creating a new promotion in the portal.

© 2026 Hantera AB. Org. no.: 559242-9582