> ## 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 over custom fields, system fields, and metafields

Calculated fields are read-only custom fields whose value comes from a [Liquid](https://shopify.github.io/liquid/) formula. One formula can pull from [custom fields](/documentation/configuration/custom-data/custom-fields), Supercycle's built-in system values, and [Shopify metafields](https://help.shopify.com/en/manual/custom-data/metafields) on the same record. For example, divide acquisition cost by completed cycles, or set a flag from a metafield.

Calculated fields are available on items, cycles, and consignors, and appear with the regular custom fields in the **Custom fields** section on the record. Their value updates on demand with **Recalculate**. Definitions live under <Icon icon="shopify" iconType="solid" /> **[Custom data](https://admin.shopify.com/apps/supercycle/settings/custom_data)**.

***

## Create a calculated field

<Steps>
  <Step title="Open custom data settings">
    Go to <Icon icon="shopify" iconType="solid" /> **[Custom data](https://admin.shopify.com/apps/supercycle/settings/custom_data)** and select **Items**, **Cycles**, or **Consignors**.
  </Step>

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

  <Step title="Set up the field">
    Enter a **Name**, the display name shown on the record, such as `Total rental cost`. The **Key** is generated from the name and can be edited. Set the **Result type** to the type the formula produces: single line text, multi line text, money, integer, boolean, date, or date and time. Enter the **Formula** as a Liquid expression.
  </Step>

  <Step title="Save">
    Select **Save**.
  </Step>
</Steps>

<Note>
  The result type can't be changed after creation.
</Note>

When [consignment](/documentation/manage/consignment/overview) is on and the result type is **Money** or **Integer**, definitions on items or cycles show **Include in consignor payouts**. Flagged fields appear as columns on [payout](/documentation/manage/consignment/payouts) breakdown reports. Values recompute when you view the report, and a failing formula shows a blank cell.

***

## Write formulas

Formulas use Liquid, the 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 pills. Select one to insert it into the formula.

### General rules

* Formulas run in strict mode. Referencing an undefined variable makes the formula fail.
* Only scalar custom fields can be referenced. Reference fields (customer, cycle, item, return), color, URL, and JSON fields aren't available in formulas.
* Built-in system fields (completed cycles, acquisition cost, schedule dates, charges, and similar) are available alongside custom fields. The formula picker lists them as pills.
* A calculated field can't reference another calculated field.
* Money values are integers in cents. `$15.00` is `1500`, and a money-typed formula must also produce cents. Multiply and divide accordingly when combining money with other numbers.
* Durations from system fields are in days, for example `utilized_days` and `total_utilized_days`.

To take 10% off a daily rate stored as money:

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

To turn 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, and everything a formula produces has to end up inside output tags. Tags, `{% ... %}`, control logic and render nothing themselves, for example `{% if %}`, `{% assign %}`, and `{% case %}`.

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

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

Common filters 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 %}
```

### Intermediate values

`{% assign %}` names 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, so they're shown as `fields.<key>` and `metafields.<key>`.

### Scalar custom field types

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

| Custom field type        | Liquid type | Example                 | Notes                  |
| ------------------------ | ----------- | ----------------------- | ---------------------- |
| `money`                  | integer     | `1500`                  | Cents                  |
| `integer`                | integer     | `3`                     |                        |
| `boolean`                | boolean     | `true` or `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 date and time |

### Liquid references by owner

Expand an owner to see every variable it exposes, 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="completed_cycles" type="integer">
      Number of completed cycles on the item.
    </ResponseField>

    <ResponseField name="upcoming_cycles" type="integer">
      Number of unfulfilled (upcoming) cycles on the item.
    </ResponseField>

    <ResponseField name="active_cycle_count" type="integer">
      Number of currently active cycles on the item.
    </ResponseField>

    <ResponseField name="acquisition_cost" type="integer">
      Item acquisition cost in cents.
    </ResponseField>

    <ResponseField name="lifecycle_revenue" type="integer">
      Lifetime revenue attributed to the item, in cents.
    </ResponseField>

    <ResponseField name="roi" type="integer">
      Lifetime ROI for the item in cents.
    </ResponseField>

    <ResponseField name="utilization" type="number">
      Share of the item's published lifetime spent utilized, as a fraction from `0` to `1`. `0` when the item has no `published_at`.
    </ResponseField>

    <ResponseField name="total_utilized_days" type="number">
      Total days the item has been utilized, rounded to two decimal places. Fleet age is measured from `published_at` when set.
    </ResponseField>

    <ResponseField name="total_unutilized_days" type="number">
      Total days the item has not been utilized since publish, rounded to two decimal places. `0` when the item has no `published_at`.
    </ResponseField>

    <ResponseField name="visibility" type="string">
      Item visibility status, such as `active` or `draft`.
    </ResponseField>

    <ResponseField name="processing_status" type="string">
      Item processing status.
    </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="fulfill_at" type="date">
      Planned fulfill date for the outbound leg.
    </ResponseField>

    <ResponseField name="fulfilled_at" type="date">
      Date the cycle was fulfilled, when set.
    </ResponseField>

    <ResponseField name="rental_start" type="date">
      Cycle start date.
    </ResponseField>

    <ResponseField name="rental_end" type="date">
      Cycle end date.
    </ResponseField>

    <ResponseField name="receive_at" type="date">
      Planned receive date for the inbound leg.
    </ResponseField>

    <ResponseField name="received_at" type="date">
      Date the cycle was received back, when set.
    </ResponseField>

    <ResponseField name="restock_by" type="date">
      Restock-by date for the inbound leg.
    </ResponseField>

    <ResponseField name="utilized_days" type="number">
      Days this cycle utilized the item, rounded to two decimal places.
    </ResponseField>

    <ResponseField name="original_unit_price" type="integer">
      Original unit price on the line item, in cents.
    </ResponseField>

    <ResponseField name="discounted_unit_price" type="integer">
      Discounted unit price on the line item, in cents.
    </ResponseField>

    <ResponseField name="charges_total" type="integer">
      Subtotal of open (not canceled) charges on the cycle, in cents.
    </ResponseField>

    <ResponseField name="charges_paid" type="integer">
      Amount paid across open charges on the cycle, in cents.
    </ResponseField>

    <ResponseField name="charges_due" type="integer">
      Amount still due across open charges on the cycle, in cents.
    </ResponseField>

    <ResponseField name="deposit_price" type="integer">
      Deposit amount on the cycle in cents, or `0` when there is no deposit.
    </ResponseField>

    <ResponseField name="deposit_refunded" type="integer">
      Deposit amount refunded in cents, or `0` when there is no deposit.
    </ResponseField>

    <ResponseField name="item" type="object">
      The rented item. Includes the item's custom fields, [system fields](/documentation/configuration/custom-data/calculated-fields#liquid-references-by-owner), and, when the item has a linked Shopify variant, its variant data. Nested as `item.*`, for example `item.completed_cycles` or `item.acquisition_cost`.

      <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.completed_cycles" type="integer">
          Number of completed cycles on the item.
        </ResponseField>

        <ResponseField name="item.upcoming_cycles" type="integer">
          Number of unfulfilled (upcoming) cycles on the item.
        </ResponseField>

        <ResponseField name="item.active_cycle_count" type="integer">
          Number of currently active cycles on the item.
        </ResponseField>

        <ResponseField name="item.acquisition_cost" type="integer">
          Item acquisition cost in cents.
        </ResponseField>

        <ResponseField name="item.lifecycle_revenue" type="integer">
          Lifetime revenue attributed to the item, in cents.
        </ResponseField>

        <ResponseField name="item.roi" type="integer">
          Lifetime ROI for the item in cents.
        </ResponseField>

        <ResponseField name="item.utilization" type="number">
          Share of the item's published lifetime spent utilized, as a fraction from `0` to `1`.
        </ResponseField>

        <ResponseField name="item.total_utilized_days" type="number">
          Total days the item has been utilized.
        </ResponseField>

        <ResponseField name="item.total_unutilized_days" type="number">
          Total days the item has not been utilized since publish.
        </ResponseField>

        <ResponseField name="item.visibility" type="string">
          Item visibility status.
        </ResponseField>

        <ResponseField name="item.processing_status" type="string">
          Item processing status.
        </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>

See [Shopify's Liquid reference](https://shopify.dev/docs/api/liquid) for tags and filters.

### Result types

A calculated field's output must match its declared result type. The result types are the same scalar set as the referenceable custom field types: `money` (stored as cents, for example `"4500"`), `integer`, `boolean`, `single_line_text_field`, `multi_line_text_field`, `date`, and `date_time`.

***

## Example formulas

### Miles completed during a cycle (integer)

Set on a cycle. Record the item's odometer reading at handover 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. Because calculated fields recalculate on demand, select **Recalculate** on the item's `total_miles` field after each cycle to roll its value forward.

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

### Average revenue per completed cycle (money)

Set on an item. Divide lifetime revenue by completed cycles. Money stays in cents.

```liquid theme={null}
{% if completed_cycles > 0 %}
  {{ lifecycle_revenue | divided_by: completed_cycles }}
{% else %}
  0
{% endif %}
```

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

Set on an item. Divide the remaining cost to recover by the average revenue per cycle, using system fields for acquisition cost and lifecycle revenue plus an `average_revenue_per_cycle` metafield you maintain on the product.

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

### Has broken even (boolean)

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

```liquid theme={null}
{% if lifecycle_revenue >= acquisition_cost %}true{% else %}false{% endif %}
```

### Cycle yield including charges (money)

Set on a cycle. Add the discounted line price to the charges paid on the cycle.

```liquid theme={null}
{{ discounted_unit_price | plus: charges_paid }}
```

***

## Recalculate a value

Calculated fields don't update on their own when the fields they depend on change.

<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 find the field.
  </Step>

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

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