> ## Documentation Index
> Fetch the complete documentation index at: https://docs.supercycle.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Calculated fields

> Derive read-only custom field values from a Liquid formula

Calculated fields are read-only custom fields whose value is computed from a [Liquid](https://shopify.github.io/liquid/) formula. A single formula can pull from both [Supercycle custom fields](/documentation/configuration/custom-data/custom-fields) and [Shopify metafields](https://help.shopify.com/en/manual/custom-data/metafields) on the same record, so you can derive values across any custom data you're already capturing. For example, you can multiply a daily rate by a duration to get a total, or set a flag based on a metafield.

Calculated fields are available on the same resources as custom fields (**items**, **cycles**, and **consignors**) and appear alongside regular custom fields in the **Custom fields** section on the record. Their value updates on demand via a **Recalculate** button on each field.

## Create a calculated field

<Steps>
  <Step title="Open custom data settings">
    In Shopify Admin, head to **Supercycle** > **Settings** > **Custom data** and choose **Items**, **Cycles**, or **Consignors**.
  </Step>

  <Step title="Add a calculated definition">
    Click **Add definition** on the calculated fields card.
  </Step>

  <Step title="Configure the field">
    Set up your calculated field:

    * **Name**: Display name shown on the record (e.g., "Total rental cost").
    * **Key**: Auto-generated identifier based on the name (can be manually edited).
    * **Result type**: The type of value the formula produces. Choose from single line text, multi line text, money, integer, boolean, date, or date and time.
    * **Formula**: A Liquid expression that computes the value.
  </Step>

  <Step title="Save definition">
    Click **Save** to create the calculated field.
  </Step>
</Steps>

<Note>
  The result type cannot be changed after creation. Choose the appropriate type when creating the definition.
</Note>

## Writing formulas

Formulas use [Liquid](https://shopify.github.io/liquid/), the same templating language Shopify themes and notifications use. A formula is any Liquid expression that renders to a value matching the field's result type.

The form lists the available variables as clickable pills. Click one to insert it into your formula.

### General rules

* Formulas are evaluated in **strict mode**. Referencing an undefined variable causes the formula to fail.
* Only **scalar** custom fields are referenceable. Reference fields (customer, cycle, item, return), color, URL, and JSON custom fields are not available in formulas.
* A calculated field cannot reference another calculated field.
* **Money values are always in cents** (integer). For example, `$15.00` is `1500`. A money-typed formula must also produce cents.

<Warning>
  Money fields are represented in **cents** inside formulas. Multiply and divide accordingly when combining money with other numbers, and remember that the result of a money-typed formula also has to be in cents.
</Warning>

For example, to compute a 10% discount off a daily rate stored as money:

```liquid theme={null}
{{ fields.daily_rate | times: 0.9 }}
```

To convert an integer number of dollars into a money result, multiply by `100`:

```liquid theme={null}
{{ fields.dollars | times: 100 }}
```

### Liquid basics

Liquid has two kinds of markup:

* **Output**: `{{ ... }}` renders a value. Everything a formula produces has to end up inside output tags.
* **Tags**: `{% ... %}` control logic and don't render anything themselves (e.g. `{% if %}`, `{% assign %}`, `{% case %}`).

Values are transformed with **filters**, chained with `|`:

```liquid theme={null}
{{ fields.daily_rate | times: fields.days | plus: fields.cleaning_fee }}
```

Common filters you'll use in formulas:

<AccordionGroup>
  <Accordion title="Arithmetic">
    `plus`, `minus`, `times`, `divided_by`, `modulo`

    ```liquid theme={null}
    {{ fields.rate | times: fields.days }}
    ```
  </Accordion>

  <Accordion title="Rounding">
    `round`, `ceil`, `floor`

    ```liquid theme={null}
    {{ fields.total | divided_by: 3 | round }}
    ```
  </Accordion>

  <Accordion title="Fallback">
    `default` substitutes a value when the input is empty.

    ```liquid theme={null}
    {{ fields.rate | default: 0 }}
    ```
  </Accordion>

  <Accordion title="Text">
    `upcase`, `downcase`, `capitalize`, `append`, `prepend`

    ```liquid theme={null}
    {{ fields.sku | prepend: "SC-" }}
    ```
  </Accordion>

  <Accordion title="Dates">
    `date` formats a date with a `strftime` string.

    ```liquid theme={null}
    {{ fields.starts_on | date: "%Y-%m-%d" }}
    ```
  </Accordion>
</AccordionGroup>

See the [Liquid reference](https://shopify.github.io/liquid/) for the full list.

### Conditional logic

Use `{% if %}` to branch. The formula still has to render a value that matches the result type, so return one on every branch:

```liquid theme={null}
{% if item.fields.replacement_cost > 50000 %}true{% else %}false{% endif %}
```

For more than two branches, `{% case %}` is often cleaner:

```liquid theme={null}
{% case fields.tier %}
  {% when "gold" %}   {{ fields.base_rate | times: 0.8 }}
  {% when "silver" %} {{ fields.base_rate | times: 0.9 }}
  {% else %}          {{ fields.base_rate }}
{% endcase %}
```

### Working with intermediate values

`{% assign %}` lets you name intermediate values so complex formulas stay readable:

```liquid theme={null}
{% assign gross = fields.daily_rate | times: fields.days %}
{% assign discount = gross | times: fields.discount_percent | divided_by: 100 %}
{{ gross | minus: discount }}
```

## Liquid variable reference

The variables available in a formula depend on the calculated field's owner. Custom field keys are shop-defined; below they are shown as `fields.<key>` and `metafields.<key>`.

### Scalar custom field types

Every `fields.<key>` variable takes its Liquid type from the underlying scalar custom field definition:

| Custom field type        | Liquid type | Example                 | Notes             |
| ------------------------ | ----------- | ----------------------- | ----------------- |
| `money`                  | integer     | `1500`                  | Cents             |
| `integer`                | integer     | `3`                     |                   |
| `boolean`                | boolean     | `true` / `false`        |                   |
| `single_line_text_field` | string      | `"hello"`               |                   |
| `multi_line_text_field`  | string      | `"hello\nworld"`        |                   |
| `date`                   | string      | `"2024-01-15"`          | ISO 8601 date     |
| `date_time`              | string      | `"2024-01-15T12:30:00"` | ISO 8601 datetime |

### Liquid references by owner

The variables available in a formula depend on the calculated field's owner. Expand an owner below to see every variable it exposes, along with its Liquid type and where the value comes from.

<AccordionGroup>
  <Accordion title="Item">
    <ResponseField name="fields.<key>" type="varies">
      One entry per scalar custom field defined on items, keyed by the definition's key. The Liquid type mirrors the custom field type: `money` and `integer` are integers (money in cents), `boolean` is a boolean, and text, `date`, and `date_time` are strings.
    </ResponseField>

    <ResponseField name="variant" type="object">
      The item's linked Shopify variant. Only available when the item has a linked variant.

      <Expandable title="variant properties">
        <ResponseField name="variant.price" type="integer">
          Shopify purchase price in cents.
        </ResponseField>

        <ResponseField name="variant.unit_cost" type="integer">
          Shopify unit cost in cents. May be `nil`.
        </ResponseField>

        <ResponseField name="variant.metafields.<key>" type="string">
          Locally stored Shopify variant metafield value, keyed by metafield key.
        </ResponseField>

        <ResponseField name="variant.product" type="object">
          The variant's parent product.

          <Expandable title="product properties">
            <ResponseField name="variant.product.metafields.<key>" type="string">
              Shopify product metafield value, keyed by metafield key.
            </ResponseField>
          </Expandable>
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Accordion>

  <Accordion title="Cycle">
    <ResponseField name="fields.<key>" type="varies">
      One entry per scalar custom field defined on cycles, keyed by the definition's key. The Liquid type mirrors the custom field type: `money` and `integer` are integers (money in cents), `boolean` is a boolean, and text, `date`, and `date_time` are strings.
    </ResponseField>

    <ResponseField name="item" type="object">
      The rented item. Includes the item's own fields and, when the item has a linked Shopify variant, its variant data.

      <Expandable title="item properties">
        <ResponseField name="item.fields.<key>" type="varies">
          One entry per scalar custom field defined on items, keyed by the definition's key.
        </ResponseField>

        <ResponseField name="item.variant" type="object">
          The item's linked Shopify variant. Only available when the item has a linked variant.

          <Expandable title="variant properties">
            <ResponseField name="item.variant.price" type="integer">
              Shopify purchase price in cents.
            </ResponseField>

            <ResponseField name="item.variant.unit_cost" type="integer">
              Shopify unit cost in cents. May be `nil`.
            </ResponseField>

            <ResponseField name="item.variant.metafields.<key>" type="string">
              Locally stored Shopify variant metafield value, keyed by metafield key.
            </ResponseField>

            <ResponseField name="item.variant.product" type="object">
              The variant's parent product.

              <Expandable title="product properties">
                <ResponseField name="item.variant.product.metafields.<key>" type="string">
                  Shopify product metafield value, keyed by metafield key.
                </ResponseField>
              </Expandable>
            </ResponseField>
          </Expandable>
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Accordion>

  <Accordion title="Consignor">
    <ResponseField name="fields.<key>" type="varies">
      One entry per scalar custom field defined on consignors, keyed by the definition's key. The Liquid type mirrors the custom field type: `money` and `integer` are integers (money in cents), `boolean` is a boolean, and text, `date`, and `date_time` are strings.
    </ResponseField>
  </Accordion>
</AccordionGroup>

More information on using Liquid can be found [on Shopify's developer documentation](https://shopify.dev/docs/api/liquid).

### Allowed result types

A calculated field's output must match its declared result type. Result types are the same scalar set as the referenceable custom field types:

* `money` (stored as cents, e.g. `"4500"`)
* `integer`
* `boolean`
* `single_line_text_field`
* `multi_line_text_field`
* `date`
* `date_time`

## Example formulas

### Miles completed during a cycle (integer)

Set on a **cycle**. Record the item's odometer reading at drop-off and return, and store the difference as the miles completed on this cycle.

```liquid theme={null}
{{ fields.odometer_return | minus: fields.odometer_start }}
```

### Total miles completed on an item (integer)

Set on an **item**. Accumulate miles across cycles by adding this cycle's contribution to the running total already stored on the item.

```liquid theme={null}
{{ fields.total_miles | plus: fields.miles_this_cycle }}
```

<Note>
  Because calculated fields recalculate on demand, click **Recalculate** on the item's `total_miles` field after each cycle to roll its value forward.
</Note>

### Time to breakeven, in cycles (integer)

Set on an **item**. Divide the remaining cost to recover (acquisition cost minus revenue collected so far) by the average revenue per cycle, using the Shopify variant's `unit_cost` as the acquisition cost and a merchant-managed `average_revenue_per_cycle` metafield on the product.

```liquid theme={null}
{% assign remaining = variant.unit_cost | minus: fields.total_revenue %}
{{ remaining | divided_by: variant.product.metafields.average_revenue_per_cycle | ceil }}
```

### Has broken even (boolean)

Set on an **item**. Flag items whose total revenue has met or exceeded their acquisition cost.

```liquid theme={null}
{% if fields.total_revenue >= variant.unit_cost %}true{% else %}false{% endif %}
```

## Recalculating values

Calculated fields don't update automatically when the fields they depend on change. To update a value:

<Steps>
  <Step title="Open the record">
    Open the item, cycle, or consignor that owns the calculated field.
  </Step>

  <Step title="Find the calculated field">
    Scroll to the **Custom fields** section and locate the calculated field you want to update.
  </Step>

  <Step title="Recalculate">
    Click the field, then press **Recalculate**. The formula is rendered against the record's current field values and the result is saved.
  </Step>
</Steps>

<Warning>
  If the formula is invalid, or the rendered result doesn't match the declared result type (for example, the formula produces `"abc"` for a **money** field), the recalculation fails and the previously stored value is left unchanged. Fix the formula in **Settings** > **Custom data** and try again.
</Warning>
