Introduction
If you use Shopify POS Pro, you can customise two printed receipts: your sales receipt and a gift receipt. POS Cafe provides prebuilt receipt code that turns one of these into a clean Order Ticket for your barista/kitchen.
Our Order Ticket code will:
- Print only products created via POS Cafe
- Group items by Prep Area so you can cut/torn sections for different stations
Setting up Order Tickets
Step 1
In your Shopify admin, go to Sales channels » Point of Sale » Settings.
Step 2
Choose Receipts.
Step 3
Select Customize your receipts.
Step 4
Click Edit code.
Step 5
In the code editor, update the four files highlighted below. Open each file, select all, and paste in the matching snippet from the Code Snippets section.
Step 6
Click Save. Use Preview options to test different order examples, then print a test order from your POS to verify layout at your printer width.
Code Snippets
Which option should I choose?
- Sales receipt as Order Ticket — the default sales receipt in Shopify will print the formatted order ticket (this is the receipt that can be set to autoprint after every order); the gift receipt prints your normal sales receipt;
- Gift receipt as Order Ticket — Your sales receipt remains the normal customer receipt; your cashier will need to tap the gift receipt button to print the formatted order ticket.
Select the accordion that matches your choice, then copy each snippet into the corresponding file.
Use this option if you would like your sales receipt to print as an order ticket, and your gift receipt to print as your sales receipt.
{% comment %}
POS Cafe - Receipts 3.7
Receipts/sales-receipt.liquid
Order ticket header section.
It is your responsibility to ensure that the printed receipts comply with all the applicable laws.
{% endcomment %}
{% assign location_id = location.id | append: "" %}
{% assign display_total_header_1 = "76500435181" | split: "," %}
{% # Default value %}
{% assign display_total_header = true %}
{% # Location overrides %}
{% if display_total_header_1 contains location_id %}
{% assign display_total_header = true %}
{% endif %}
{% # Display order note %}
{% assign display_order_note = true %}
{% # End of the settings definition %}
<!doctype html>
<html>
<head>
<meta charset='UTF-8'>
<meta
name='viewport'
content='user-scalable=no'
>
<style>
:root {
--font-smallest: 0.6875rem;
--font-small: 0.875rem;
--font-medium: 1rem;
--font-large: 1.5rem;
--font-largest: 3rem;
--font-family: system-ui;
}
* {
box-sizing: border-box;
}
body {
margin: 2rem 1rem 2.5rem 1rem;
font-family: var(--font-family);
font-weight: 400;
font-size: var(--font-large);
background: white;
}
p {
margin: 0;
}
.receipt-box {
border-color: black;
border-radius: 0.7rem;
border: 0.3rem solid;
margin: 2.5rem 0.3125rem;
padding: 1.75rem 1.25rem;
text-align: center;
font-weight: bold;
font-size: var(--font-large);
}
.receipt-box .channel-label {
font-weight: bold;
font-size: var(--font-large);
}
.note {
padding-top: 1.35rem;
border-top: 0.25rem dotted black;
font-size: var(--font-large);
}
.note-title {
font-weight: bold;
padding-bottom: 0.3125rem;
font-size: var(--font-large);
}
</style>
</head>
<body>
{% render 'line-items' %}
{% if display_order_note %}
{% if order.note != nil and order.note != blank %}
<section class='note'>
<p class='note-title'>{{ 'receipt.order_note' | t }}</p>
<p>{{ order.note | escape }}</p>
</section>
{% endif %}
{% endif %}
</body>
</html>
{% comment %}
POS Cafe - Receipts 3.7
Sections/line-items.liquid
Order ticket line items section.
It is your responsibility to ensure that the printed receipts comply with all the applicable laws.
{% endcomment %}
<style>
.variant-name,
.line-item-quantity,
.line-item-group { font-size: var(--font-large); }
.line-items-header {
width: 100%;
border-bottom: 0.3rem solid black;
display: flex;
justify-content: space-between;
padding: 0 0.3125rem;
}
.line-item-row { padding: 1.35rem 0; display: flex; justify-content: space-between; }
.line-item-row-bottom-border { border-bottom: 0.25rem dotted black; }
.line-item-title, .line-item-quantity { padding: 0 0.3125rem; }
.line-item-title .product-name,
.line-item-quantity,
.line-items-header { font-size: 3rem; }
.line-item-title .prep-area,
.line-item-quantity,
.line-items-header {
font-size: 3rem;
font-weight:bold;
text-align:center;
margin-bottom:0.5rem;
}
.dine-option {
font-size: 2rem;
font-weight: 700;
text-transform: uppercase;
margin-top: 0.25rem;
}
li { font-size: 2rem; }
hr.cut-here { border-top: 10px dashed black; }
</style>
<div>
<section>
{% # Set variables %}
{% assign poscafe = "POS Cafe" %}
{% assign poscafe_norm = poscafe | downcase %}
{% assign unique_prep_areas = '' %}
{% assign has_order_name = false %}
{% assign order_name = '' %}
{% assign has_channel = false %}
{% assign channel = "Online" %}
{% assign show_channel = false %} {% # toggle: show/hide the channel line in header %}
{% assign hidden_keys = 'item_type,modifier_variants,_modifiervariants,prep_area,channel,service_type' | split: ',' %}
{% comment %} First pass: capture order name/channel, and collect unique prep areas for Cafe items {% endcomment %}
{% for line_item in order.line_items %}
{%- comment -%} Precise Cafe detection: item_type == POS Cafe; fallback to any value == POS Cafe {%- endcomment -%}
{% assign item_type_norm = '' %}
{% assign any_poscafe_value = false %}
{% for attribute in line_item.custom_attributes %}
{% assign key_norm = attribute[0] | strip | downcase | replace: ' ', '_' %}
{% assign val_norm = attribute[1] | strip | downcase %}
{% if key_norm == 'item_type' %}{% assign item_type_norm = val_norm %}{% endif %}
{% if val_norm == poscafe_norm %}{% assign any_poscafe_value = true %}{% endif %}
{% endfor %}
{% assign is_cafe_item = false %}
{% if item_type_norm == poscafe_norm %}
{% assign is_cafe_item = true %}
{% elsif any_poscafe_value %}
{% assign is_cafe_item = true %}
{% endif %}
{% if has_order_name == false %}
{% for attribute in line_item.custom_attributes %}
{% if attribute[0] == 'name' %}
{% assign has_order_name = true %}
{% assign order_name = attribute[1] | strip_newlines | strip %}
{% break %}
{% endif %}
{% endfor %}
{% endif %}
{% if has_channel == false %}
{% for attribute in line_item.custom_attributes %}
{% if attribute[0] == 'channel' %}
{% assign has_channel = true %}
{% assign channel = attribute[1] | strip_newlines | strip %}
{% break %}
{% endif %}
{% endfor %}
{% endif %}
{% if is_cafe_item %}
{%- capture item_prep_area -%}
{% for attribute in line_item.custom_attributes %}
{% if attribute[0] == 'prep_area' %}
{{ attribute[1] | strip_newlines | strip }}
{% endif %}
{% endfor %}
{%- endcapture -%}
{% assign item_prep_area = item_prep_area | strip %}
{% if item_prep_area != blank %}
{% assign prep_area_exists = false %}
{% if unique_prep_areas != '' %}
{% assign existing_areas = unique_prep_areas | split: ',' %}
{% for existing_area in existing_areas %}
{% if existing_area == item_prep_area %}{% assign prep_area_exists = true %}{% break %}{% endif %}
{% endfor %}
{% endif %}
{% if prep_area_exists == false %}
{% if unique_prep_areas == '' %}
{% assign unique_prep_areas = item_prep_area %}
{% else %}
{% assign unique_prep_areas = unique_prep_areas | append: ',' | append: item_prep_area %}
{% endif %}
{% endif %}
{% endif %}
{% endif %}
{% endfor %}
{% assign unique_prep_areas_array = unique_prep_areas | split: ',' | sort %}
{% comment %} Count cafe items without a prep area {% endcomment %}
{% assign items_without_prep_area_count = 0 %}
{% for line_item in order.line_items %}
{% assign item_type_norm = '' %}
{% assign any_poscafe_value = false %}
{% for attribute in line_item.custom_attributes %}
{% assign key_norm = attribute[0] | strip | downcase | replace: ' ', '_' %}
{% assign val_norm = attribute[1] | strip | downcase %}
{% if key_norm == 'item_type' %}{% assign item_type_norm = val_norm %}{% endif %}
{% if val_norm == poscafe_norm %}{% assign any_poscafe_value = true %}{% endif %}
{% endfor %}
{% assign is_cafe_item = false %}
{% if item_type_norm == poscafe_norm %}
{% assign is_cafe_item = true %}
{% elsif any_poscafe_value %}
{% assign is_cafe_item = true %}
{% endif %}
{% assign has_prep_area = false %}
{% for attribute in line_item.custom_attributes %}
{% if attribute[0] == 'prep_area' and attribute[1] != blank %}{% assign has_prep_area = true %}{% endif %}
{% endfor %}
{% if is_cafe_item and has_prep_area == false %}
{% assign items_without_prep_area_count = items_without_prep_area_count | plus: 1 %}
{% endif %}
{% endfor %}
{% comment %} Cafe items WITHOUT prep area {% endcomment %}
{% if items_without_prep_area_count > 0 %}
<div class='receipt-box'>
{% if has_order_name %}
<p class='gift-receipt' style="font-size: 3rem;">{{ order_name }}</p>
<p>{{ order.name }}</p>
<p style="font-weight: normal;margin-top:10px;"><small>{{ order.created_at | date: "%b %d, %Y – %I:%M %p" }}</small></p>
{% if show_channel %}<p class="channel-label" style="font-weight: normal;margin-top:10px;">{{ channel }}</p>{% endif %}
{% else %}
<p class='gift-receipt' style="font-size: 3rem;">{{ order.name }}</p>
<p style="font-weight: normal;margin-top:10px;"><small>{{ order.created_at | date: "%b %d, %Y – %I:%M %p" }}</small></p>
{% if show_channel %}<p class="channel-label" style="font-weight: normal;margin-top:10px;">{{ channel }}</p>{% endif %}
{% endif %}
</div>
{% for line_item in order.line_items %}
{% assign item_type_norm = '' %}
{% assign any_poscafe_value = false %}
{% for attribute in line_item.custom_attributes %}
{% assign key_norm = attribute[0] | strip | downcase | replace: ' ', '_' %}
{% assign val_norm = attribute[1] | strip | downcase %}
{% if key_norm == 'item_type' %}{% assign item_type_norm = val_norm %}{% endif %}
{% if val_norm == poscafe_norm %}{% assign any_poscafe_value = true %}{% endif %}
{% endfor %}
{% assign is_cafe_item = false %}
{% if item_type_norm == poscafe_norm %}
{% assign is_cafe_item = true %}
{% elsif any_poscafe_value %}
{% assign is_cafe_item = true %}
{% endif %}
{% if is_cafe_item %}
{%- capture current_item_prep_area -%}
{% for attribute in line_item.custom_attributes %}
{% if attribute[0] == 'prep_area' %}{{ attribute[1] | strip_newlines | strip }}{% endif %}
{% endfor %}
{%- endcapture -%}
{% assign current_item_prep_area = current_item_prep_area | strip %}
{% if current_item_prep_area == blank %}
<div class='line-item-row'>
<div class='line-item-title'>
<p class='product-name'>{{ line_item.quantity }} x {{ line_item.name | escape }}</p>
{% comment %} First variant description (if any) {% endcomment %}
{% assign variant_description = '' %}
{% for vd in line_item.variant_descriptions %}
{% if vd != blank %}
{% assign variant_description = vd %}
{% break %}
{% endif %}
{% endfor %}
{% comment %} Service Type line (captured while building list below) {% endcomment %}
{% assign service_type_value = '' %}
{% comment %} Normalized product title for duplicate-key filtering {% endcomment %}
{% if line_item.product %}
{% assign prod_title_norm = line_item.product.title | strip | downcase %}
{% else %}
{% assign prod_title_norm = line_item.name | split: ' - ' | first | strip | downcase %}
{% endif %}
{% comment %} De-dupe container for list values {% endcomment %}
{% assign seen_values = '||' %}
{% capture attributes_html %}
{% if variant_description != blank %}
{% assign v_clean = variant_description | strip | escape %}
{% assign v_norm = v_clean | downcase %}
{% assign v_key = '|' | append: v_norm | append: '|' %}
{% unless seen_values contains v_key %}
<li>{{ v_clean }}</li>
{% assign seen_values = seen_values | append: v_key %}
{% endunless %}
{% endif %}
{% for attribute in line_item.custom_attributes %}
{% assign key_norm_raw = attribute[0] | strip | downcase %}
{% assign key_norm = key_norm_raw | replace: ' ', '_' %}
{% assign key_words = key_norm_raw | replace: '_', ' ' %}
{% if service_type_value == '' and key_norm == 'service_type' %}
{% assign service_type_value = attribute[1] | strip | escape %}
{% endif %}
{%- comment -%} Skip underscore-prefixed internal keys {%- endcomment -%}
{% assign first_char = key_norm | slice: 0, 1 %}
{% if first_char == '_' %}
{% continue %}
{% endif %}
{% if hidden_keys contains key_norm or key_words == prod_title_norm %}
{% continue %}
{% endif %}
{% assign value_clean = attribute[1] | split: '@' | first | strip | escape %}
{% if value_clean != blank %}
{% assign val_norm = value_clean | downcase %}
{% assign needle = '|' | append: val_norm | append: '|' %}
{% unless seen_values contains needle %}
<li>{{ value_clean }}</li>
{% assign seen_values = seen_values | append: needle %}
{% endunless %}
{% endif %}
{% endfor %}
{% endcapture %}
{% if service_type_value != blank %}<p class="dine-option">{{ service_type_value }}</p>{% endif %}
{% assign attributes_trimmed = attributes_html | strip %}
{% if attributes_trimmed != blank %}<ul>{{ attributes_trimmed }}</ul>{% endif %}
</div>
</div>
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% comment %} Group cafe items BY prep area {% endcomment %}
{% for area in unique_prep_areas_array %}
{% unless forloop.first %}<div class='line-item-row-bottom-border'></div>{% endunless %}
<div class='receipt-box'>
{% if has_order_name %}
<p class='gift-receipt' style="font-size: 3rem;">{{ order_name }}</p>
<p>{{ order.name }}</p>
<p style="font-weight: normal;margin-top:10px;"><small>{{ order.created_at | date: "%b %d, %Y – %I:%M %p" }}</small></p>
{% if show_channel %}<p class="channel-label" style="font-weight: normal;margin-top:10px;">{{ channel }}</p>{% endif %}
{% else %}
<p class='gift-receipt' style="font-size: 3rem;">{{ order.name }}</p>
<p style="font-weight: normal;margin-top:10px;"><small>{{ order.created_at | date: "%b %d, %Y – %I:%M %p" }}</small></p>
{% if show_channel %}<p class="channel-label" style="font-weight: normal;margin-top:10px;">{{ channel }}</p>{% endif %}
{% endif %}
</div>
<div class='line-item-title'>
<p class='prep-area'>{{ area }}</p>
</div>
{% for line_item in order.line_items %}
{% assign item_type_norm = '' %}
{% assign any_poscafe_value = false %}
{% for attribute in line_item.custom_attributes %}
{% assign key_norm = attribute[0] | strip | downcase | replace: ' ', '_' %}
{% assign val_norm = attribute[1] | strip | downcase %}
{% if key_norm == 'item_type' %}{% assign item_type_norm = val_norm %}{% endif %}
{% if val_norm == poscafe_norm %}{% assign any_poscafe_value = true %}{% endif %}
{% endfor %}
{% assign is_cafe_item = false %}
{% if item_type_norm == poscafe_norm %}
{% assign is_cafe_item = true %}
{% elsif any_poscafe_value %}
{% assign is_cafe_item = true %}
{% endif %}
{% if is_cafe_item %}
{%- capture current_item_prep_area -%}
{% for attribute in line_item.custom_attributes %}
{% if attribute[0] == 'prep_area' %}{{ attribute[1] | strip_newlines | strip }}{% endif %}
{% endfor %}
{%- endcapture -%}
{% assign current_item_prep_area = current_item_prep_area | strip %}
{% if current_item_prep_area == area %}
<div class='line-item-row'>
<div class='line-item-title'>
<p class='product-name'>{{ line_item.quantity }} x {{ line_item.name | escape }}</p>
{% comment %} First variant description (if any) {% endcomment %}
{% assign variant_description = '' %}
{% for vd in line_item.variant_descriptions %}
{% if vd != blank %}
{% assign variant_description = vd %}
{% break %}
{% endif %}
{% endfor %}
{% assign service_type_value = '' %}
{% if line_item.product %}
{% assign prod_title_norm = line_item.product.title | strip | downcase %}
{% else %}
{% assign prod_title_norm = line_item.name | split: ' - ' | first | strip | downcase %}
{% endif %}
{% assign seen_values = '||' %}
{% capture attributes_html %}
{% if variant_description != blank %}
{% assign v_clean = variant_description | strip | escape %}
{% assign v_norm = v_clean | downcase %}
{% assign v_key = '|' | append: v_norm | append: '|' %}
{% unless seen_values contains v_key %}
<li>{{ v_clean }}</li>
{% assign seen_values = seen_values | append: v_key %}
{% endunless %}
{% endif %}
{% for attribute in line_item.custom_attributes %}
{% assign key_norm_raw = attribute[0] | strip | downcase %}
{% assign key_norm = key_norm_raw | replace: ' ', '_' %}
{% assign key_words = key_norm_raw | replace: '_', ' ' %}
{% if service_type_value == '' and key_norm == 'service_type' %}
{% assign service_type_value = attribute[1] | strip | escape %}
{% endif %}
{% assign first_char = key_norm | slice: 0, 1 %}
{% if first_char == '_' %}
{% continue %}
{% endif %}
{% if hidden_keys contains key_norm or key_words == prod_title_norm %}
{% continue %}
{% endif %}
{% assign value_clean = attribute[1] | split: '@' | first | strip | escape %}
{% if value_clean != blank %}
{% assign val_norm = value_clean | downcase %}
{% assign needle = '|' | append: val_norm | append: '|' %}
{% unless seen_values contains needle %}
<li>{{ value_clean }}</li>
{% assign seen_values = seen_values | append: needle %}
{% endunless %}
{% endif %}
{% endfor %}
{% endcapture %}
{% if service_type_value != blank %}<p class="dine-option">{{ service_type_value }}</p>{% endif %}
{% assign attributes_trimmed = attributes_html | strip %}
{% if attributes_trimmed != blank %}<ul>{{ attributes_trimmed }}</ul>{% endif %}
</div>
</div>
{% endif %}
{% endif %}
{% endfor %}
{% endfor %}
</section>
</div>
{% comment %}
POS Cafe - Receipts 3.7
Receipts/gift-receipt.liquid
Customer receipt header section.
It is your responsibility to ensure that the printed receipts comply with all the applicable laws.
{% endcomment %}
{% assign location_id = location.id | append: "" %}
{% assign display_total_header_1 = "83448037594" | split: "," %}
{% # Default value %}
{% assign display_total_header = true %}
{% # Location overrides %}
{% if display_total_header_1 contains location_id %}
{% assign display_total_header = true %}
{% endif %}
{% # End of the settings definition %}
<!doctype html>
<html>
<head>
<meta charset='UTF-8'>
<meta
name='viewport'
content='user-scalable=no'
>
<style>
:root {
--font-smallest: 0.6875rem;
--font-small: 0.875rem;
--font-medium: 1rem;
--font-large: 1.5rem;
--font-largest: 3rem;
--font-family: system-ui;
}
* {
box-sizing: border-box;
}
body {
margin: 2rem 1rem 2.5rem 1rem;
font-family: var(--font-family);
font-weight: 400;
font-size: var(--font-large);
background: white;
}
p {
margin: 0;
}
.price-box {
border-radius: 0.7rem;
border: 0.3rem solid black;
margin: 2.5rem 0.3125rem;
padding: 1.75rem 1.25rem;
text-align: center;
font-weight: bold;
font-size: var(--font-large);
}
.price-box .total {
font-size: var(--font-largest);
}
</style>
</head>
<body>
{% # This section is used by POS UI extensions to show information for the header %}
{% render 'extensions', extension_blocks: extension_blocks.header %}
{% render 'header' %}
{% if display_total_header %}
<div class='price-box'>
<p class='receipt-total-header'>{{ 'receipt.total' | t }}</p>
<p class='total'>{{ order.total_price | money | escape }}</p>
</div>
{% endif %}
{% render 'gift-receipt-line-items' %}
{% render 'transactions' %}
{% render 'footer' %}
{% # This section is required in Germany %}
{% render 'kassenSichV-compliance' %}
{% # This section is used by POS UI extensions to show information for the footer %}
{% render 'extensions', extension_blocks: extension_blocks.footer %}
</body>
</html>
{% comment %}
POS Cafe - Receipts 3.7
Sections/gift-receipt-line-items.liquid
Customer receipt line items section.
It is your responsibility to ensure that the printed receipts comply with all the applicable laws.
{% endcomment %}
{% assign location_id = location.id | append: "" %}
{% comment %}
Feature toggles (adjust per merchant or per location if needed)
{% endcomment %}
{% assign display_product_sku = false %}
{% assign display_sales_attribution = false %}
{% assign display_compare_at_price = true %}
{% comment %} Hide items with item_type/Item Type == "Modifier" {% endcomment %}
{% assign exclude_modifiers = true %}
{% comment %} If true and product has tag "hidden-price", hide prices on receipt {% endcomment %}
{% assign hide_price_for_hidden_tag = false %}
{% comment %} If false, suppress the attributes list entirely {% endcomment %}
{% assign show_attributes = false %}
{% comment %} Show "Part of <bundle/group)" line. Default off. {% endcomment %}
{% assign show_line_item_group = false %}
{% comment %}
Internal lists (normalized keys: downcase + spaces -> underscores)
{% endcomment %}
{% assign modifier_keys = 'item_type,itemtype' | split: ',' %}
{% assign hidden_attr_keys = 'item_type,modifier_variants,_modifiervariants,prep_area,channel' | split: ',' %}
<style>
.variant-name,
.line-item-quantity,
.product_sku,
.sale_attribution,
.selling-plan-name,
.line-item-group { font-size: var(--font-large); }
.line-items-header {
width: 100%;
border-bottom: 0.3rem solid black;
display: flex;
justify-content: space-between;
padding: 0 0.3125rem;
}
.line-item-row { padding: 1.35rem 0; display: flex; justify-content: space-between; }
.line-item-row-bottom-border { border-bottom: 0.25rem dotted black; }
.line-item-title { word-wrap: break-word; max-width: 70%; }
.price, .compare-at-price { padding: 0 0.3125rem; }
.line-item-title .product-name, .price, .line-items-header, .product-discount { font-weight: 700; }
.compare-at-price { text-decoration: line-through; font-size: var(--font-medium); justify-content: flex-end; }
.line-item-container { padding-left: 0.3125rem; padding-right: 0.3125rem; }
.discounts-container { padding: 0 0.3125rem; display: flex; justify-content: space-between; flex-direction: column; }
.discount-row { display: flex; flex-direction: row; }
.discount-code { width: 60%; }
.discount-price { width: 40%; text-align: right; }
.price--hidden { opacity: 0.75; }
</style>
<div class='line-items'>
<header class='line-items-header'>
<p>{{ 'receipt.item' | t }}</p>
<p>{{ 'receipt.price' | t }}</p>
</header>
<section>
{% for line_item in order.line_items %}
{%- comment -%} Identify Modifiers via line-item properties (no parentheses) {%- endcomment -%}
{% assign is_modifier = false %}
{% for attribute in line_item.custom_attributes %}
{% assign key_norm = attribute[0] | strip | downcase | replace: ' ', '_' %}
{% assign val_norm = attribute[1] | strip | downcase %}
{% if modifier_keys contains key_norm and val_norm == 'modifier' %}
{% assign is_modifier = true %}
{% break %}
{% endif %}
{% endfor %}
{%- comment -%} Optionally hide prices for products tagged 'hidden-price' {%- endcomment -%}
{% assign hide_price = false %}
{% if hide_price_for_hidden_tag and line_item.product and line_item.product.tags contains 'hidden-price' %}
{% assign hide_price = true %}
{% endif %}
{% unless exclude_modifiers and is_modifier %}
<div class='line-item'>
<div class='line-item-row line-item-container'>
<div class='line-item-title'>
<p class='product-name'>{{ line_item.name | escape }}</p>
{%- comment -%}
Variant descriptions:
- If show_attributes == false: show standard variant <p> lines (Shopify default).
- If show_attributes == true: we'll insert the first variant as the first <li> instead.
{%- endcomment -%}
{% if show_attributes == false %}
{% for variant_description in line_item.variant_descriptions %}
{% if variant_description != blank %}
<p class='variant-name'>{{ variant_description }}</p>
{% endif %}
{% endfor %}
{% endif %}
{% if display_product_sku and line_item.sku %}
<p class='product_sku'>{{ line_item.sku }}</p>
{% endif %}
{% if line_item.quantity > 1 %}
{% if hide_price %}
<div class='line-item-quantity'>({{ line_item.quantity }} x —)</div>
{% else %}
<div class='line-item-quantity'>({{ line_item.quantity }} x {{ line_item.price | money | escape }})</div>
{% endif %}
{% endif %}
{% if show_line_item_group and line_item.line_item_group.title %}
<p class='line-item-group'>
{{ 'receipt.part_of' | t }} {{ line_item.line_item_group.title }}
</p>
{% endif %}
{% if display_sales_attribution and line_item.staff_member_description %}
<p class='sale_attribution'>
{{ 'receipt.sold_by_line_item' | t }} {{ line_item.staff_member_description }}
</p>
{% endif %}
{% if show_attributes %}
{%- comment -%}
Build attributes list:
- Put first variant_description at the top as <li>.
- Filter hidden keys and underscore-prefixed internal keys.
- Skip attributes whose key equals the product title (normalized) to avoid “Standard”/duplicates.
- Trim any "@price" suffix.
- De-duplicate values (case-insensitive).
{%- endcomment -%}
{% comment %} capture first variant description {% endcomment %}
{% assign first_variant_description = '' %}
{% for vd in line_item.variant_descriptions %}
{% if vd != blank %}
{% assign first_variant_description = vd %}
{% break %}
{% endif %}
{% endfor %}
{% comment %} normalized product title for duplicate-key filtering {% endcomment %}
{% if line_item.product %}
{% assign prod_title_norm = line_item.product.title | strip | downcase %}
{% else %}
{% assign prod_title_norm = line_item.name | split: ' - ' | first | strip | downcase %}
{% endif %}
{% assign seen_values = '||' %}
{% capture attributes_html %}
{% if first_variant_description != blank %}
{% assign v_clean = first_variant_description | strip | escape %}
{% assign v_norm = v_clean | downcase %}
{% assign v_key = '|' | append: v_norm | append: '|' %}
{% unless seen_values contains v_key %}
<li>{{ v_clean }}</li>
{% assign seen_values = seen_values | append: v_key %}
{% endunless %}
{% endif %}
{% for attribute in line_item.custom_attributes %}
{% assign key_norm_raw = attribute[0] | strip | downcase %}
{% assign key_norm = key_norm_raw | replace: ' ', '_' %}
{% assign key_words = key_norm_raw | replace: '_', ' ' %}
{%- comment -%} skip underscore-prefixed internal keys {%- endcomment -%}
{% assign first_char = key_norm | slice: 0, 1 %}
{% if first_char == '_' %}
{% continue %}
{% endif %}
{% unless hidden_attr_keys contains key_norm or key_words == prod_title_norm %}
{% assign value_clean = attribute[1] | split: '@' | first | strip | escape %}
{% if value_clean != blank %}
{% assign val_norm = value_clean | downcase %}
{% assign needle = '|' | append: val_norm | append: '|' %}
{% unless seen_values contains needle %}
<li>{{ value_clean }}</li>
{% assign seen_values = seen_values | append: needle %}
{% endunless %}
{% endif %}
{% endunless %}
{% endfor %}
{% endcapture %}
{% assign attributes_trimmed = attributes_html | strip %}
{% if attributes_trimmed != blank %}
<ul>{{ attributes_trimmed }}</ul>
{% endif %}
{% endif %}
{% for selling_plan in line_item.selling_plans %}
{% if selling_plan.name != blank %}
<p class='selling-plan-name'>{{ selling_plan.name | escape }}</p>
{% endif %}
{% endfor %}
</div>
<div>
{% if hide_price %}
<div class='price price--hidden'>—</div>
{% else %}
<div class='price'>{{ line_item.total_price | money | escape }}</div>
{% if display_compare_at_price and line_item.compare_at_price_description %}
<div class='line-item-row compare-at-price'>
{{ line_item.compare_at_price_description | money | escape }}
</div>
{% endif %}
{% endif %}
</div>
</div>
{% if line_item.discounts.size > 0 %}
<div class='discounts-container'>
{% for discount in line_item.discounts %}
{% assign discount_amount_num = discount.amount | plus: 0 %}
{% if discount.description or discount_amount_num > 0 %}
<div class='discount-row'>
<p class='discount-code'>{{ discount.description | escape }}</p>
{% if discount_amount_num > 0 %}
<p class='discount-price price'>-{{ discount.amount | money | escape }}</p>
{% endif %}
</div>
{% endif %}
{% endfor %}
</div>
{% endif %}
{% for refund in line_item.refunds %}
<p class='refunded-product'>
{% assign created_at = refund.created_at | date %}
{{ 'receipt.refunded_on' | t: quantity: refund.refund_line_item.quantity, date: created_at }}
</p>
{% endfor %}
</div>
<div class='line-item-row-bottom-border'></div>
{% endunless %}
{% endfor %}
</section>
</div>
Use this option if you would like your gift receipt to print as an
order ticket, and your sales receipt to print as your sales receipt. Note,
in this configuration, you do not need to edit your
Receipts/sales-receipt.liquid
code snippet.
{% comment %}
POS Cafe - Receipts 3.7
Sections/line-items.liquid
Customer receipt line items section.
It is your responsibility to ensure that the printed receipts comply with all the applicable laws.
{% endcomment %}
{% assign location_id = location.id | append: "" %}
{% comment %}
Feature toggles (adjust per merchant or per location if needed)
{% endcomment %}
{% assign display_product_sku = false %}
{% assign display_sales_attribution = false %}
{% assign display_compare_at_price = true %}
{% comment %} Hide items with item_type/Item Type == "Modifier" {% endcomment %}
{% assign exclude_modifiers = true %}
{% comment %} If true and product has tag "hidden-price", hide prices on receipt {% endcomment %}
{% assign hide_price_for_hidden_tag = false %}
{% comment %} If false, suppress the attributes list entirely {% endcomment %}
{% assign show_attributes = false %}
{% comment %} Show "Part of <bundle/group)" line. Default off. {% endcomment %}
{% assign show_line_item_group = false %}
{% comment %}
Internal lists (normalized keys: downcase + spaces -> underscores)
{% endcomment %}
{% assign modifier_keys = 'item_type,itemtype' | split: ',' %}
{% assign hidden_attr_keys = 'item_type,modifier_variants,_modifiervariants,prep_area,channel' | split: ',' %}
<style>
.variant-name,
.line-item-quantity,
.product_sku,
.sale_attribution,
.selling-plan-name,
.line-item-group { font-size: var(--font-large); }
.line-items-header {
width: 100%;
border-bottom: 0.3rem solid black;
display: flex;
justify-content: space-between;
padding: 0 0.3125rem;
}
.line-item-row { padding: 1.35rem 0; display: flex; justify-content: space-between; }
.line-item-row-bottom-border { border-bottom: 0.25rem dotted black; }
.line-item-title { word-wrap: break-word; max-width: 70%; }
.price, .compare-at-price { padding: 0 0.3125rem; }
.line-item-title .product-name, .price, .line-items-header, .product-discount { font-weight: 700; }
.compare-at-price { text-decoration: line-through; font-size: var(--font-medium); justify-content: flex-end; }
.line-item-container { padding-left: 0.3125rem; padding-right: 0.3125rem; }
.discounts-container { padding: 0 0.3125rem; display: flex; justify-content: space-between; flex-direction: column; }
.discount-row { display: flex; flex-direction: row; }
.discount-code { width: 60%; }
.discount-price { width: 40%; text-align: right; }
.price--hidden { opacity: 0.75; }
</style>
<div class='line-items'>
<header class='line-items-header'>
<p>{{ 'receipt.item' | t }}</p>
<p>{{ 'receipt.price' | t }}</p>
</header>
<section>
{% for line_item in order.line_items %}
{%- comment -%} Identify Modifiers via line-item properties (no parentheses) {%- endcomment -%}
{% assign is_modifier = false %}
{% for attribute in line_item.custom_attributes %}
{% assign key_norm = attribute[0] | strip | downcase | replace: ' ', '_' %}
{% assign val_norm = attribute[1] | strip | downcase %}
{% if modifier_keys contains key_norm and val_norm == 'modifier' %}
{% assign is_modifier = true %}
{% break %}
{% endif %}
{% endfor %}
{%- comment -%} Optionally hide prices for products tagged 'hidden-price' {%- endcomment -%}
{% assign hide_price = false %}
{% if hide_price_for_hidden_tag and line_item.product and line_item.product.tags contains 'hidden-price' %}
{% assign hide_price = true %}
{% endif %}
{% unless exclude_modifiers and is_modifier %}
<div class='line-item'>
<div class='line-item-row line-item-container'>
<div class='line-item-title'>
<p class='product-name'>{{ line_item.name | escape }}</p>
{%- comment -%}
Variant descriptions:
- If show_attributes == false: show standard variant <p> lines (Shopify default).
- If show_attributes == true: we'll insert the first variant as the first <li> instead.
{%- endcomment -%}
{% if show_attributes == false %}
{% for variant_description in line_item.variant_descriptions %}
{% if variant_description != blank %}
<p class='variant-name'>{{ variant_description }}</p>
{% endif %}
{% endfor %}
{% endif %}
{% if display_product_sku and line_item.sku %}
<p class='product_sku'>{{ line_item.sku }}</p>
{% endif %}
{% if line_item.quantity > 1 %}
{% if hide_price %}
<div class='line-item-quantity'>({{ line_item.quantity }} x —)</div>
{% else %}
<div class='line-item-quantity'>({{ line_item.quantity }} x {{ line_item.price | money | escape }})</div>
{% endif %}
{% endif %}
{% if show_line_item_group and line_item.line_item_group.title %}
<p class='line-item-group'>
{{ 'receipt.part_of' | t }} {{ line_item.line_item_group.title }}
</p>
{% endif %}
{% if display_sales_attribution and line_item.staff_member_description %}
<p class='sale_attribution'>
{{ 'receipt.sold_by_line_item' | t }} {{ line_item.staff_member_description }}
</p>
{% endif %}
{% if show_attributes %}
{%- comment -%}
Build attributes list:
- Put first variant_description at the top as <li>.
- Filter hidden keys and underscore-prefixed internal keys.
- Skip attributes whose key equals the product title (normalized) to avoid “Standard”/duplicates.
- Trim any "@price" suffix.
- De-duplicate values (case-insensitive).
{%- endcomment -%}
{% comment %} capture first variant description {% endcomment %}
{% assign first_variant_description = '' %}
{% for vd in line_item.variant_descriptions %}
{% if vd != blank %}
{% assign first_variant_description = vd %}
{% break %}
{% endif %}
{% endfor %}
{% comment %} normalized product title for duplicate-key filtering {% endcomment %}
{% if line_item.product %}
{% assign prod_title_norm = line_item.product.title | strip | downcase %}
{% else %}
{% assign prod_title_norm = line_item.name | split: ' - ' | first | strip | downcase %}
{% endif %}
{% assign seen_values = '||' %}
{% capture attributes_html %}
{% if first_variant_description != blank %}
{% assign v_clean = first_variant_description | strip | escape %}
{% assign v_norm = v_clean | downcase %}
{% assign v_key = '|' | append: v_norm | append: '|' %}
{% unless seen_values contains v_key %}
<li>{{ v_clean }}</li>
{% assign seen_values = seen_values | append: v_key %}
{% endunless %}
{% endif %}
{% for attribute in line_item.custom_attributes %}
{% assign key_norm_raw = attribute[0] | strip | downcase %}
{% assign key_norm = key_norm_raw | replace: ' ', '_' %}
{% assign key_words = key_norm_raw | replace: '_', ' ' %}
{%- comment -%} skip underscore-prefixed internal keys {%- endcomment -%}
{% assign first_char = key_norm | slice: 0, 1 %}
{% if first_char == '_' %}
{% continue %}
{% endif %}
{% unless hidden_attr_keys contains key_norm or key_words == prod_title_norm %}
{% assign value_clean = attribute[1] | split: '@' | first | strip | escape %}
{% if value_clean != blank %}
{% assign val_norm = value_clean | downcase %}
{% assign needle = '|' | append: val_norm | append: '|' %}
{% unless seen_values contains needle %}
<li>{{ value_clean }}</li>
{% assign seen_values = seen_values | append: needle %}
{% endunless %}
{% endif %}
{% endunless %}
{% endfor %}
{% endcapture %}
{% assign attributes_trimmed = attributes_html | strip %}
{% if attributes_trimmed != blank %}
<ul>{{ attributes_trimmed }}</ul>
{% endif %}
{% endif %}
{% for selling_plan in line_item.selling_plans %}
{% if selling_plan.name != blank %}
<p class='selling-plan-name'>{{ selling_plan.name | escape }}</p>
{% endif %}
{% endfor %}
</div>
<div>
{% if hide_price %}
<div class='price price--hidden'>—</div>
{% else %}
<div class='price'>{{ line_item.total_price | money | escape }}</div>
{% if display_compare_at_price and line_item.compare_at_price_description %}
<div class='line-item-row compare-at-price'>
{{ line_item.compare_at_price_description | money | escape }}
</div>
{% endif %}
{% endif %}
</div>
</div>
{% if line_item.discounts.size > 0 %}
<div class='discounts-container'>
{% for discount in line_item.discounts %}
{% assign discount_amount_num = discount.amount | plus: 0 %}
{% if discount.description or discount_amount_num > 0 %}
<div class='discount-row'>
<p class='discount-code'>{{ discount.description | escape }}</p>
{% if discount_amount_num > 0 %}
<p class='discount-price price'>-{{ discount.amount | money | escape }}</p>
{% endif %}
</div>
{% endif %}
{% endfor %}
</div>
{% endif %}
{% for refund in line_item.refunds %}
<p class='refunded-product'>
{% assign created_at = refund.created_at | date %}
{{ 'receipt.refunded_on' | t: quantity: refund.refund_line_item.quantity, date: created_at }}
</p>
{% endfor %}
</div>
<div class='line-item-row-bottom-border'></div>
{% endunless %}
{% endfor %}
</section>
</div>
{% comment %}
POS Cafe - Receipts 3.7
Receipts/gift-receipt.liquid
Order ticket header section.
It is your responsibility to ensure that the printed receipts comply with all the applicable laws.
{% endcomment %}
{% assign location_id = location.id | append: "" %}
{% assign display_total_header_1 = "76500435181" | split: "," %}
{% # Default value %}
{% assign display_total_header = true %}
{% # Location overrides %}
{% if display_total_header_1 contains location_id %}
{% assign display_total_header = true %}
{% endif %}
{% # Display order note %}
{% assign display_order_note = true %}
{% # End of the settings definition %}
<!doctype html>
<html>
<head>
<meta charset='UTF-8'>
<meta
name='viewport'
content='user-scalable=no'
>
<style>
:root {
--font-smallest: 0.6875rem;
--font-small: 0.875rem;
--font-medium: 1rem;
--font-large: 1.5rem;
--font-largest: 3rem;
--font-family: system-ui;
}
* {
box-sizing: border-box;
}
body {
margin: 2rem 1rem 2.5rem 1rem;
font-family: var(--font-family);
font-weight: 400;
font-size: var(--font-large);
background: white;
}
p {
margin: 0;
}
.receipt-box {
border-color: black;
border-radius: 0.7rem;
border: 0.3rem solid;
margin: 2.5rem 0.3125rem;
padding: 1.75rem 1.25rem;
text-align: center;
font-weight: bold;
font-size: var(--font-large);
}
.receipt-box .channel-label {
font-weight: bold;
font-size: var(--font-large);
}
.note {
padding-top: 1.35rem;
border-top: 0.25rem dotted black;
font-size: var(--font-large);
}
.note-title {
font-weight: bold;
padding-bottom: 0.3125rem;
font-size: var(--font-large);
}
</style>
</head>
<body>
{% render 'gift-receipt-line-items' %}
{% if display_order_note %}
{% if order.note != nil and order.note != blank %}
<section class='note'>
<p class='note-title'>{{ 'receipt.order_note' | t }}</p>
<p>{{ order.note | escape }}</p>
</section>
{% endif %}
{% endif %}
</body>
</html>
{% comment %}
POS Cafe - Receipts 3.7
Sections/gift-receipt-line-items.liquid
Order ticket line items section.
It is your responsibility to ensure that the printed receipts comply with all the applicable laws.
{% endcomment %}
<style>
.variant-name,
.line-item-quantity,
.line-item-group { font-size: var(--font-large); }
.line-items-header {
width: 100%;
border-bottom: 0.3rem solid black;
display: flex;
justify-content: space-between;
padding: 0 0.3125rem;
}
.line-item-row { padding: 1.35rem 0; display: flex; justify-content: space-between; }
.line-item-row-bottom-border { border-bottom: 0.25rem dotted black; }
.line-item-title, .line-item-quantity { padding: 0 0.3125rem; }
.line-item-title .product-name,
.line-item-quantity,
.line-items-header { font-size: 3rem; }
.line-item-title .prep-area,
.line-item-quantity,
.line-items-header {
font-size: 3rem;
font-weight:bold;
text-align:center;
margin-bottom:0.5rem;
}
.dine-option {
font-size: 2rem;
font-weight: 700;
text-transform: uppercase;
margin-top: 0.25rem;
}
li { font-size: 2rem; }
hr.cut-here { border-top: 10px dashed black; }
</style>
<div>
<section>
{% # Set variables %}
{% assign poscafe = "POS Cafe" %}
{% assign poscafe_norm = poscafe | downcase %}
{% assign unique_prep_areas = '' %}
{% assign has_order_name = false %}
{% assign order_name = '' %}
{% assign has_channel = false %}
{% assign channel = "Online" %}
{% assign show_channel = false %} {% # toggle: show/hide the channel line in header %}
{% assign hidden_keys = 'item_type,modifier_variants,_modifiervariants,prep_area,channel,service_type' | split: ',' %}
{% comment %} First pass: capture order name/channel, and collect unique prep areas for Cafe items {% endcomment %}
{% for line_item in order.line_items %}
{%- comment -%} Precise Cafe detection: item_type == POS Cafe; fallback to any value == POS Cafe {%- endcomment -%}
{% assign item_type_norm = '' %}
{% assign any_poscafe_value = false %}
{% for attribute in line_item.custom_attributes %}
{% assign key_norm = attribute[0] | strip | downcase | replace: ' ', '_' %}
{% assign val_norm = attribute[1] | strip | downcase %}
{% if key_norm == 'item_type' %}{% assign item_type_norm = val_norm %}{% endif %}
{% if val_norm == poscafe_norm %}{% assign any_poscafe_value = true %}{% endif %}
{% endfor %}
{% assign is_cafe_item = false %}
{% if item_type_norm == poscafe_norm %}
{% assign is_cafe_item = true %}
{% elsif any_poscafe_value %}
{% assign is_cafe_item = true %}
{% endif %}
{% if has_order_name == false %}
{% for attribute in line_item.custom_attributes %}
{% if attribute[0] == 'name' %}
{% assign has_order_name = true %}
{% assign order_name = attribute[1] | strip_newlines | strip %}
{% break %}
{% endif %}
{% endfor %}
{% endif %}
{% if has_channel == false %}
{% for attribute in line_item.custom_attributes %}
{% if attribute[0] == 'channel' %}
{% assign has_channel = true %}
{% assign channel = attribute[1] | strip_newlines | strip %}
{% break %}
{% endif %}
{% endfor %}
{% endif %}
{% if is_cafe_item %}
{%- capture item_prep_area -%}
{% for attribute in line_item.custom_attributes %}
{% if attribute[0] == 'prep_area' %}
{{ attribute[1] | strip_newlines | strip }}
{% endif %}
{% endfor %}
{%- endcapture -%}
{% assign item_prep_area = item_prep_area | strip %}
{% if item_prep_area != blank %}
{% assign prep_area_exists = false %}
{% if unique_prep_areas != '' %}
{% assign existing_areas = unique_prep_areas | split: ',' %}
{% for existing_area in existing_areas %}
{% if existing_area == item_prep_area %}{% assign prep_area_exists = true %}{% break %}{% endif %}
{% endfor %}
{% endif %}
{% if prep_area_exists == false %}
{% if unique_prep_areas == '' %}
{% assign unique_prep_areas = item_prep_area %}
{% else %}
{% assign unique_prep_areas = unique_prep_areas | append: ',' | append: item_prep_area %}
{% endif %}
{% endif %}
{% endif %}
{% endif %}
{% endfor %}
{% assign unique_prep_areas_array = unique_prep_areas | split: ',' | sort %}
{% comment %} Count cafe items without a prep area {% endcomment %}
{% assign items_without_prep_area_count = 0 %}
{% for line_item in order.line_items %}
{% assign item_type_norm = '' %}
{% assign any_poscafe_value = false %}
{% for attribute in line_item.custom_attributes %}
{% assign key_norm = attribute[0] | strip | downcase | replace: ' ', '_' %}
{% assign val_norm = attribute[1] | strip | downcase %}
{% if key_norm == 'item_type' %}{% assign item_type_norm = val_norm %}{% endif %}
{% if val_norm == poscafe_norm %}{% assign any_poscafe_value = true %}{% endif %}
{% endfor %}
{% assign is_cafe_item = false %}
{% if item_type_norm == poscafe_norm %}
{% assign is_cafe_item = true %}
{% elsif any_poscafe_value %}
{% assign is_cafe_item = true %}
{% endif %}
{% assign has_prep_area = false %}
{% for attribute in line_item.custom_attributes %}
{% if attribute[0] == 'prep_area' and attribute[1] != blank %}{% assign has_prep_area = true %}{% endif %}
{% endfor %}
{% if is_cafe_item and has_prep_area == false %}
{% assign items_without_prep_area_count = items_without_prep_area_count | plus: 1 %}
{% endif %}
{% endfor %}
{% comment %} Cafe items WITHOUT prep area {% endcomment %}
{% if items_without_prep_area_count > 0 %}
<div class='receipt-box'>
{% if has_order_name %}
<p class='gift-receipt' style="font-size: 3rem;">{{ order_name }}</p>
<p>{{ order.name }}</p>
<p style="font-weight: normal;margin-top:10px;"><small>{{ order.created_at | date: "%b %d, %Y – %I:%M %p" }}</small></p>
{% if show_channel %}<p class="channel-label" style="font-weight: normal;margin-top:10px;">{{ channel }}</p>{% endif %}
{% else %}
<p class='gift-receipt' style="font-size: 3rem;">{{ order.name }}</p>
<p style="font-weight: normal;margin-top:10px;"><small>{{ order.created_at | date: "%b %d, %Y – %I:%M %p" }}</small></p>
{% if show_channel %}<p class="channel-label" style="font-weight: normal;margin-top:10px;">{{ channel }}</p>{% endif %}
{% endif %}
</div>
{% for line_item in order.line_items %}
{% assign item_type_norm = '' %}
{% assign any_poscafe_value = false %}
{% for attribute in line_item.custom_attributes %}
{% assign key_norm = attribute[0] | strip | downcase | replace: ' ', '_' %}
{% assign val_norm = attribute[1] | strip | downcase %}
{% if key_norm == 'item_type' %}{% assign item_type_norm = val_norm %}{% endif %}
{% if val_norm == poscafe_norm %}{% assign any_poscafe_value = true %}{% endif %}
{% endfor %}
{% assign is_cafe_item = false %}
{% if item_type_norm == poscafe_norm %}
{% assign is_cafe_item = true %}
{% elsif any_poscafe_value %}
{% assign is_cafe_item = true %}
{% endif %}
{% if is_cafe_item %}
{%- capture current_item_prep_area -%}
{% for attribute in line_item.custom_attributes %}
{% if attribute[0] == 'prep_area' %}{{ attribute[1] | strip_newlines | strip }}{% endif %}
{% endfor %}
{%- endcapture -%}
{% assign current_item_prep_area = current_item_prep_area | strip %}
{% if current_item_prep_area == blank %}
<div class='line-item-row'>
<div class='line-item-title'>
<p class='product-name'>{{ line_item.quantity }} x {{ line_item.name | escape }}</p>
{% comment %} First variant description (if any) {% endcomment %}
{% assign variant_description = '' %}
{% for vd in line_item.variant_descriptions %}
{% if vd != blank %}
{% assign variant_description = vd %}
{% break %}
{% endif %}
{% endfor %}
{% comment %} Service Type line (captured while building list below) {% endcomment %}
{% assign service_type_value = '' %}
{% comment %} Normalized product title for duplicate-key filtering {% endcomment %}
{% if line_item.product %}
{% assign prod_title_norm = line_item.product.title | strip | downcase %}
{% else %}
{% assign prod_title_norm = line_item.name | split: ' - ' | first | strip | downcase %}
{% endif %}
{% comment %} De-dupe container for list values {% endcomment %}
{% assign seen_values = '||' %}
{% capture attributes_html %}
{% if variant_description != blank %}
{% assign v_clean = variant_description | strip | escape %}
{% assign v_norm = v_clean | downcase %}
{% assign v_key = '|' | append: v_norm | append: '|' %}
{% unless seen_values contains v_key %}
<li>{{ v_clean }}</li>
{% assign seen_values = seen_values | append: v_key %}
{% endunless %}
{% endif %}
{% for attribute in line_item.custom_attributes %}
{% assign key_norm_raw = attribute[0] | strip | downcase %}
{% assign key_norm = key_norm_raw | replace: ' ', '_' %}
{% assign key_words = key_norm_raw | replace: '_', ' ' %}
{% if service_type_value == '' and key_norm == 'service_type' %}
{% assign service_type_value = attribute[1] | strip | escape %}
{% endif %}
{%- comment -%} Skip underscore-prefixed internal keys {%- endcomment -%}
{% assign first_char = key_norm | slice: 0, 1 %}
{% if first_char == '_' %}
{% continue %}
{% endif %}
{% if hidden_keys contains key_norm or key_words == prod_title_norm %}
{% continue %}
{% endif %}
{% assign value_clean = attribute[1] | split: '@' | first | strip | escape %}
{% if value_clean != blank %}
{% assign val_norm = value_clean | downcase %}
{% assign needle = '|' | append: val_norm | append: '|' %}
{% unless seen_values contains needle %}
<li>{{ value_clean }}</li>
{% assign seen_values = seen_values | append: needle %}
{% endunless %}
{% endif %}
{% endfor %}
{% endcapture %}
{% if service_type_value != blank %}<p class="dine-option">{{ service_type_value }}</p>{% endif %}
{% assign attributes_trimmed = attributes_html | strip %}
{% if attributes_trimmed != blank %}<ul>{{ attributes_trimmed }}</ul>{% endif %}
</div>
</div>
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% comment %} Group cafe items BY prep area {% endcomment %}
{% for area in unique_prep_areas_array %}
{% unless forloop.first %}<div class='line-item-row-bottom-border'></div>{% endunless %}
<div class='receipt-box'>
{% if has_order_name %}
<p class='gift-receipt' style="font-size: 3rem;">{{ order_name }}</p>
<p>{{ order.name }}</p>
<p style="font-weight: normal;margin-top:10px;"><small>{{ order.created_at | date: "%b %d, %Y – %I:%M %p" }}</small></p>
{% if show_channel %}<p class="channel-label" style="font-weight: normal;margin-top:10px;">{{ channel }}</p>{% endif %}
{% else %}
<p class='gift-receipt' style="font-size: 3rem;">{{ order.name }}</p>
<p style="font-weight: normal;margin-top:10px;"><small>{{ order.created_at | date: "%b %d, %Y – %I:%M %p" }}</small></p>
{% if show_channel %}<p class="channel-label" style="font-weight: normal;margin-top:10px;">{{ channel }}</p>{% endif %}
{% endif %}
</div>
<div class='line-item-title'>
<p class='prep-area'>{{ area }}</p>
</div>
{% for line_item in order.line_items %}
{% assign item_type_norm = '' %}
{% assign any_poscafe_value = false %}
{% for attribute in line_item.custom_attributes %}
{% assign key_norm = attribute[0] | strip | downcase | replace: ' ', '_' %}
{% assign val_norm = attribute[1] | strip | downcase %}
{% if key_norm == 'item_type' %}{% assign item_type_norm = val_norm %}{% endif %}
{% if val_norm == poscafe_norm %}{% assign any_poscafe_value = true %}{% endif %}
{% endfor %}
{% assign is_cafe_item = false %}
{% if item_type_norm == poscafe_norm %}
{% assign is_cafe_item = true %}
{% elsif any_poscafe_value %}
{% assign is_cafe_item = true %}
{% endif %}
{% if is_cafe_item %}
{%- capture current_item_prep_area -%}
{% for attribute in line_item.custom_attributes %}
{% if attribute[0] == 'prep_area' %}{{ attribute[1] | strip_newlines | strip }}{% endif %}
{% endfor %}
{%- endcapture -%}
{% assign current_item_prep_area = current_item_prep_area | strip %}
{% if current_item_prep_area == area %}
<div class='line-item-row'>
<div class='line-item-title'>
<p class='product-name'>{{ line_item.quantity }} x {{ line_item.name | escape }}</p>
{% comment %} First variant description (if any) {% endcomment %}
{% assign variant_description = '' %}
{% for vd in line_item.variant_descriptions %}
{% if vd != blank %}
{% assign variant_description = vd %}
{% break %}
{% endif %}
{% endfor %}
{% assign service_type_value = '' %}
{% if line_item.product %}
{% assign prod_title_norm = line_item.product.title | strip | downcase %}
{% else %}
{% assign prod_title_norm = line_item.name | split: ' - ' | first | strip | downcase %}
{% endif %}
{% assign seen_values = '||' %}
{% capture attributes_html %}
{% if variant_description != blank %}
{% assign v_clean = variant_description | strip | escape %}
{% assign v_norm = v_clean | downcase %}
{% assign v_key = '|' | append: v_norm | append: '|' %}
{% unless seen_values contains v_key %}
<li>{{ v_clean }}</li>
{% assign seen_values = seen_values | append: v_key %}
{% endunless %}
{% endif %}
{% for attribute in line_item.custom_attributes %}
{% assign key_norm_raw = attribute[0] | strip | downcase %}
{% assign key_norm = key_norm_raw | replace: ' ', '_' %}
{% assign key_words = key_norm_raw | replace: '_', ' ' %}
{% if service_type_value == '' and key_norm == 'service_type' %}
{% assign service_type_value = attribute[1] | strip | escape %}
{% endif %}
{% assign first_char = key_norm | slice: 0, 1 %}
{% if first_char == '_' %}
{% continue %}
{% endif %}
{% if hidden_keys contains key_norm or key_words == prod_title_norm %}
{% continue %}
{% endif %}
{% assign value_clean = attribute[1] | split: '@' | first | strip | escape %}
{% if value_clean != blank %}
{% assign val_norm = value_clean | downcase %}
{% assign needle = '|' | append: val_norm | append: '|' %}
{% unless seen_values contains needle %}
<li>{{ value_clean }}</li>
{% assign seen_values = seen_values | append: needle %}
{% endunless %}
{% endif %}
{% endfor %}
{% endcapture %}
{% if service_type_value != blank %}<p class="dine-option">{{ service_type_value }}</p>{% endif %}
{% assign attributes_trimmed = attributes_html | strip %}
{% if attributes_trimmed != blank %}<ul>{{ attributes_trimmed }}</ul>{% endif %}
</div>
</div>
{% endif %}
{% endif %}
{% endfor %}
{% endfor %}
</section>
</div>
Shopify POS print options
In POS » Settings » Receipts, review your printing preferences for each location (e.g., whether to automatically print after checkout, and which printer to use). If you use the Gift receipt as Order Ticket option, staff will print the Order Ticket by tapping the gift receipt button on the completion screen.
Troubleshooting
Issue | Solution |
---|---|
No items appear on the Order Ticket? | Only POS Cafe items are printed. Ensure items were created in POS Cafe and (optionally) that their prep_area is set. |
Station headings look wrong? | Set/adjust Prep Areas in POS Cafe; items without a prep area print in the “no prep area” section. |
Need to revert? | Click on the undo icon |