Skip to content
Log in

Verify custom data is available in Liquid

On this page

A mapped custom field that renders as blank has two very different causes, and they look identical on screen:

  • The value never reached the store, so there is nothing to render.
  • The value is present, but the accessor in your template is wrong.

Guessing between them wastes time. Use this process to establish which one you have before changing any Liquid.

Before you start

  • You need edit access to the template you are debugging, so you can add a few temporary lines to it.
  • To complete Step 3 or the POS setup below, you need a POS register you can open and resync, and permission to create Custom Data Mappings and POS Layouts.

Step 1: List the keys the drop actually holds

Every Liquid drop that supports custom data exposes a data collection. Loop over it and print the key names, rather than testing one field at a time.

Add these lines to the template where the field renders blank, on the page you are debugging.

```liquid

KEYS: {% for pair in current_customer.data %}{{ pair[0] }},{% endfor %} ```

To see the values as well:

```liquid

{% for pair in current_customer.data %} {{ pair[0] }} = {{ pair[1] | default: ‘(blank)’ }} {% endfor %} ```

Swap current_customer for whichever drop you are working with, such as product, cart, or current_order.

Read the result as follows.

Output Meaning What to do
Your field name appears The data is present Fix the accessor, see Step 2
Other keys appear, yours does not That field is not mapped or has not synced Check the Custom Data Mapping and field-level security
Only internal keys such as _record and _keyMap The drop carries no custom data Stop testing syntax, see Step 3
Nothing at all The drop is empty or unresolved Confirm the record is loaded, for example that a customer is attached to the cart

:::tip Run this on the storefront and in the POS separately. They resolve custom data through different mechanisms, so a field can be present in one and absent in the other. :::

Step 2: Confirm the accessor

If the key is listed, the value is there and only the accessor is at fault. All three of these are valid:

```liquid

{{ current_customer.data[‘customer_since__c’] }} {{ current_customer.data.customer_since__c }} {% assign key = ‘customer_since__c’ %}{{ current_customer.data[key] }} ```

Use the lowercase Salesforce column name. Bracket notation is the safest form, and it is the only option when the key is held in a variable.

Step 3: Check what reached the device (POS only)

The POS renders against a local copy of the data held in the browser’s IndexedDB, not against Salesforce. Liquid cannot read that store, so check it from the browser console with the POS open.

```js

(async () => { const name = window.storageKey ? storeconnect__${window.storageKey} : ‘storeconnect’; const db = await new Promise((res, rej) => { const r = indexedDB.open(name); r.onsuccess = () => res(r.result); r.onerror = () => rej(r.error); }); console.log(‘stores:’, […db.objectStoreNames]); const rows = await new Promise((res) => { const t = db.transaction(‘contact’).objectStore(‘contact’).getAll(); t.onsuccess = () => res(t.result); t.onerror = () => res([]); }); console.log(‘records:’, rows.length); console.log(‘columns:’, rows[0] && Object.keys(rows[0]).sort()); console.log(‘sample:’, rows[0]); })() ```

Change contact to the object store you are testing. Store names are lowercase Salesforce table names, such as product2, order, or s_c__cart__c.

What the output tells you:

  • columns includes your field — the data is on the device, so the problem is in the template.
  • columns shows only core fields — the POS was never told to fetch the field, so it was left out of the sync. Follow Make a custom field available in the POS below.
  • records: 0 — the store exists but holds no records for this device, so nothing will render from a layout over that object. Check the sync settings and whether records exist for the register’s outlet.
  • The store is missing from stores — that object is not synced to the POS at all.

Make a custom field available in the POS

The storefront and the POS resolve custom data differently. On the storefront a Custom Data Mapping is enough on its own. The POS holds a defined set of columns per object and fetches an extra column only when a POS Layout references it, so a mapping alone is not enough.

  1. Open the POS Layouts list and create a layout for the object. Any Type works, and the layout does not need to be mounted anywhere. It can exist purely to register the fields.
  2. Add a POS Layout Field to that layout for each field you want, setting Field Name to the field’s API name in all lowercase, for example customer_since__c. This works for fields on both standard and custom objects. A POS Layout Filter, or a field named in the layout’s Sort, registers a field the same way.
  3. Create the Custom Data Mapping for each field, setting Object API Name and Field API Name to match the Salesforce field, for example Contact and Customer_Since__c. Adding a mapping triggers a full repoll from Salesforce, so the POS fetches the new columns as part of it.
  4. Resync the POS.

If the mapping already existed before you added the layout field, the repoll has already run and the field will be blank on the device. Resync all POS data to pick it up.

:::warning Adding fields to a POS list layout replaces its default columns rather than adding to them. Create a separate unmounted layout to register fields, rather than adding them to a layout that drives a live screen such as customer_list. :::

To correct a mapping, delete it and create a new one. Editing an existing Custom Data Mapping does not trigger a repoll, so the corrected field is never backfilled.

Once synced, read the field the same way as anywhere else.

```liquid

{{ current_customer.data[‘customer_since__c’] }} ```

Run Step 1 again to confirm the outcome: the key now appears in the drop’s data collection, and the console check in Step 3 lists it under columns on the device.

Which objects can reach the POS

Order, Order Item, and Shipment are supported in POS Layouts, but they are not part of the POS’s default data set. Each one reaches the device only once a layout references it, so they follow the same steps above.

Payment is never downloaded to the POS, so its mapped fields cannot be read in a POS template however they are mapped. Referencing Payment in a POS Layout does not make it available, and no records arrive for it. Read payment data on the storefront instead.

Was this article helpful?

Was this article helpful?