Skip to content
  1. Extras
  2. MiniShop3
  3. Frontend interface
  4. Customer account
  5. Order history

Order history

Customer order history page. Shows all orders with status filtering and pagination, plus detailed information for a specific order.

Page structure

ComponentFilePurpose
Base layouttpl.msCustomer.baseCommon wrapper with sidebar
Sidebartpl.msCustomer.sidebarAccount navigation
Order listtpl.msCustomer.ordersOrders table
Order rowtpl.msCustomer.order.rowSingle order in the list
Order detailstpl.msCustomer.order.detailsFull order information

Snippet call

fenom
{'!msCustomer' | snippet : [
    'service' => 'orders',
    'limit' => 20
]}

Parameters

ParameterDefaultDescription
serviceService type (orders)
tpltpl.msCustomer.ordersOrder list chunk
orderTpltpl.msCustomer.order.rowOrder row chunk
detailTpltpl.msCustomer.order.detailsOrder details chunk
limit20Orders per page
unauthorizedTpltpl.msCustomer.unauthorizedChunk for guests
returntplFormat: tpl or data

Operating modes

URLModeDescription
/cabinet/orders/ListOrders table
/cabinet/orders/?order=550e8400-...DetailsOrder information by UUID
/cabinet/orders/?status=2FilterOrders with status 2
/cabinet/orders/?offset=20PaginationSecond page

UUID in order URLs

Since version 1.6, order detail links use UUID instead of numeric ID. This is safer: it does not expose order count and prevents guessing other customers' orders by ID.

URL format: ?order=550e8400-e29b-41d4-a716-446655440000

Customer order cancellation

The customer can cancel an order if its current status is in the allowed list (setting ms3_customer_cancel_allowed_statuses).

How it works

  1. The server passes the {$can_cancel} placeholder for each order
  2. The "Cancel" button is shown only when {$can_cancel} = true
  3. On click — confirmation dialog via ms3Confirm
  4. API call POST /api/v1/customer/orders/{id}/cancel
  5. Page reloads after successful cancellation

Cancel button in chunk

fenom
{if $can_cancel}
<button type="button"
        class="btn btn-sm btn-outline-danger ms3-order-cancel"
        data-order-id="{$id}"
        data-confirm="{'ms3_customer_order_cancel_confirm' | lexicon}">
    {'ms3_customer_order_cancel' | lexicon}
</button>
{/if}

System settings

SettingDefaultDescription
ms3_customer_cancel_allowed_statuses2,3Status IDs that allow cancellation
ms3_status_canceled0Status ID for a canceled order

Order list placeholders

In tpl.msCustomer.orders

PlaceholderTypeDescription
{$orders}stringRendered order rows (HTML)
{$orders_count}intOrders on the page
{$total}intTotal orders
{$statuses}arrayStatuses for filtering
{$pagination}arrayPagination data
{$customer}arrayCustomer data
{$api_url}stringAPI URL for JS operations

In tpl.msCustomer.order.row

PlaceholderTypeDescription
{$id}intOrder ID
{$uuid}stringOrder UUID (for URL)
{$num}stringOrder number (MS-00015)
{$createdon_formatted}stringCreated date
{$cost_formatted}stringOrder total
{$status_id}intStatus ID
{$status_name}stringStatus name
{$status_color}stringStatus color (CSS value)
{$can_cancel}boolWhether the order can be canceled

Order list chunk

fenom
{* tpl.msCustomer.orders *}
{extends 'tpl.msCustomer.base'}

{block 'content'}
<div class="ms3-customer-orders" data-api-url="{$api_url|default:''}">
    <div class="card shadow-sm">
        <div class="card-header bg-primary text-white">
            <h5 class="mb-0">{'ms3_customer_orders_title' | lexicon}</h5>
        </div>
        <div class="card-body">
            {* Status filter *}
            {if $statuses}
            <form class="mb-4" method="get" action="">
                <div class="row align-items-end">
                    <div class="col-md-4">
                        <label for="status-filter" class="form-label">
                            {'ms3_customer_orders_filter_by_status' | lexicon}
                        </label>
                        <select class="form-select" id="status-filter" name="status"
                                onchange="this.form.submit()">
                            <option value="">
                                {'ms3_customer_orders_all_statuses' | lexicon}
                            </option>
                            {foreach $statuses as $status}
                            <option value="{$status.id}" {if $status.selected}selected{/if}>
                                {$status.name}
                            </option>
                            {/foreach}
                        </select>
                    </div>
                    <div class="col-md-2">
                        <button type="button" class="btn btn-secondary"
                                onclick="location.href='?'">
                            {'ms3_customer_orders_reset_filter' | lexicon}
                        </button>
                    </div>
                </div>
            </form>
            {/if}

            {* Order list *}
            {if $orders_count > 0}
            <div class="table-responsive">
                <table class="table table-hover align-middle ms3-order-table">
                    <thead class="table-light">
                        <tr>
                            <th>{'ms3_customer_order_num' | lexicon}</th>
                            <th>{'ms3_customer_order_date' | lexicon}</th>
                            <th>{'ms3_customer_order_status' | lexicon}</th>
                            <th class="text-end">{'ms3_customer_order_total' | lexicon}</th>
                            <th class="col-actions"></th>
                        </tr>
                    </thead>
                    <tbody>
                        {$orders}
                    </tbody>
                </table>
            </div>

            {* Pagination *}
            {if $pagination.total_pages > 1}
            <nav aria-label="{'ms3_customer_orders_pagination' | lexicon}">
                <ul class="pagination justify-content-center">
                    {if $pagination.has_prev}
                    <li class="page-item">
                        <a class="page-link" href="?offset={$pagination.prev_offset}">
                            {'ms3_customer_orders_prev' | lexicon}
                        </a>
                    </li>
                    {/if}

                    {foreach $pagination.pages as $page}
                    <li class="page-item {if $page.active}active{/if}">
                        <a class="page-link" href="?offset={$page.offset}">
                            {$page.num}
                        </a>
                    </li>
                    {/foreach}

                    {if $pagination.has_next}
                    <li class="page-item">
                        <a class="page-link" href="?offset={$pagination.next_offset}">
                            {'ms3_customer_orders_next' | lexicon}
                        </a>
                    </li>
                    {/if}
                </ul>
            </nav>
            {/if}

            <div class="text-muted small mt-3">
                {'ms3_customer_orders_total' | lexicon}: {$total}
            </div>
            {else}
            <div class="alert alert-info" role="alert">
                {'ms3_customer_orders_empty' | lexicon}
            </div>
            {/if}
        </div>
    </div>
</div>
{/block}

Order row chunk

fenom
{* tpl.msCustomer.order.row *}
<tr>
    <td>
        <a href="{'ms3_customer_orders_page_id' | option | url}?order={$uuid}"
           class="fw-semibold text-decoration-none">
{$num}
        </a>
    </td>
    <td class="text-nowrap">
        {$createdon_formatted}
    </td>
    <td>
        <span class="badge" style="background-color: {$status_color};">
            {$status_name}
        </span>
    </td>
    <td class="text-end text-nowrap fw-bold">
        {$cost_formatted} {'ms3_frontend_currency' | lexicon}
    </td>
    <td class="text-end">
        <div class="d-flex gap-2 justify-content-end" role="group">
            <a href="{'ms3_customer_orders_page_id' | option | url}?order={$uuid}"
               class="btn btn-sm btn-outline-primary"
               title="{'ms3_customer_order_view' | lexicon}">
                {'ms3_customer_order_view' | lexicon}
            </a>
            {if $can_cancel}
            <button type="button"
                    class="btn btn-sm btn-outline-danger ms3-order-cancel"
                    data-order-id="{$id}"
                    data-confirm="{'ms3_customer_order_cancel_confirm' | lexicon}"
                    title="{'ms3_customer_order_cancel' | lexicon}">
                {'ms3_customer_order_cancel' | lexicon}
            </button>
            {/if}
        </div>
    </td>
</tr>

Order details placeholders

In tpl.msCustomer.order.details

PlaceholderTypeDescription
{$order}arrayOrder data
{$order.id}intOrder ID
{$order.num}stringOrder number
{$order.status_name}stringStatus name
{$order.status_color}stringStatus color
{$order.createdon_formatted}stringCreated date
{$order.comment}stringOrder comment
{$order.can_cancel}boolWhether the order can be canceled
{$products}arrayOrder products
{$delivery}arrayDelivery method
{$payment}arrayPayment method
{$address}arrayDelivery address
{$total}arrayOrder totals
{$customer}arrayCustomer data

Product in order ({$products})

FieldDescription
{$product.product_id}Product ID
{$product.pagetitle}Name
{$product.article}SKU
{$product.count}Quantity
{$product.price}Price (formatted)
{$product.old_price}Old price
{$product.cost}Line total (formatted)
{$product.weight}Weight
{$product.options}Product options (array)

Totals ({$total})

FieldDescription
{$total.cost}Total to pay
{$total.cart_cost}Products cost
{$total.delivery_cost}Delivery cost
{$total.weight}Total weight

Order details chunk

fenom
{* tpl.msCustomer.order.details *}
<div class="ms3-customer-order-details" data-api-url="{$api_url|default:''}">
    {* Back navigation *}
    <div class="mb-3">
        <a href="{'ms3_customer_orders_page_id' | option | url}"
           class="btn btn-sm btn-outline-secondary">
{'ms3_customer_orders_back' | lexicon}
        </a>
    </div>

    {* Order information *}
    <div class="card shadow-sm mb-4">
        <div class="card-header bg-primary text-white">
            <div class="d-flex justify-content-between align-items-center flex-wrap gap-2">
                <h5 class="mb-0">
                    {'ms3_customer_order_title' | lexicon}{$order.num}
                </h5>
                <div class="d-flex align-items-center gap-2">
                    {if $order.can_cancel}
                    <button type="button"
                            class="btn btn-sm btn-light ms3-order-cancel"
                            data-order-id="{$order.id}"
                            data-confirm="{'ms3_customer_order_cancel_confirm' | lexicon}">
                        {'ms3_customer_order_cancel' | lexicon}
                    </button>
                    {/if}
                    <span class="badge bg-light text-dark"
                          style="color: {$order.status_color} !important;">
                        {$order.status_name}
                    </span>
                </div>
            </div>
        </div>
        <div class="card-body">
            <p class="text-muted mb-2">
                <small>
                    {'ms3_customer_order_created' | lexicon}: {$order.createdon_formatted}
                </small>
            </p>

            {* Products table *}
            <div class="table-responsive">
                <table class="table table-hover align-middle ms3-order-table">
                    <thead class="table-light">
                        <tr>
                            <th>{'ms3_cart_title' | lexicon}</th>
                            <th class="text-center col-count">{'ms3_cart_count' | lexicon}</th>
                            <th class="text-end col-price">{'ms3_cart_price' | lexicon}</th>
                            <th class="text-end col-cost">{'ms3_cart_cost' | lexicon}</th>
                        </tr>
                    </thead>
                    <tbody>
                        {foreach $products as $product}
                        <tr>
                            <td>
                                <div class="fw-semibold">{$product.pagetitle}</div>
                                {if $product.article}
                                <div class="small text-muted">
                                    {'ms3_frontend_article' | lexicon}: {$product.article}
                                </div>
                                {/if}
                                {if $product.options && count($product.options) > 0}
                                <div class="small text-muted mt-1">
                                    {foreach $product.options as $option => $value}
                                    {$option}: {$value}{if !$value@last}; {/if}
                                    {/foreach}
                                </div>
                                {/if}
                            </td>
                            <td class="text-center text-nowrap">
                                <span class="badge bg-secondary">
                                    {$product.count} {'ms3_frontend_count_unit' | lexicon}
                                </span>
                            </td>
                            <td class="text-end text-nowrap">
                                {if $product.old_price && $product.old_price > $product.price}
                                <div class="text-decoration-line-through text-muted small">
                                    {$product.old_price}
                                </div>
                                {/if}
                                <div class="fw-semibold">{$product.price}</div>
                                <small class="text-muted">{'ms3_frontend_currency' | lexicon}</small>
                            </td>
                            <td class="text-end text-nowrap fw-bold">
                                {$product.cost} {'ms3_frontend_currency' | lexicon}
                            </td>
                        </tr>
                        {/foreach}
                    </tbody>
                    <tfoot class="table-light">
                        <tr>
                            <td colspan="3" class="text-end fw-bold">
                                {'ms3_frontend_cart_total' | lexicon}:
                            </td>
                            <td class="text-end fw-bold">
                                {$total.cart_cost} {'ms3_frontend_currency' | lexicon}
                            </td>
                        </tr>
                        {if $total.delivery_cost}
                        <tr>
                            <td colspan="3" class="text-end">
                                {'ms3_frontend_delivery' | lexicon}:
                            </td>
                            <td class="text-end">
                                {$total.delivery_cost} {'ms3_frontend_currency' | lexicon}
                            </td>
                        </tr>
                        {/if}
                        <tr class="fw-bold">
                            <td colspan="3" class="text-end fs-5">
                                {'ms3_frontend_total' | lexicon}:
                            </td>
                            <td class="text-end fs-5">
                                {$total.cost} {'ms3_frontend_currency' | lexicon}
                            </td>
                        </tr>
                    </tfoot>
                </table>
            </div>
        </div>
    </div>

    {* Delivery address *}
    {if $address && count($address) > 0}
    <div class="card shadow-sm mb-4">
        <div class="card-header bg-light">
            <h6 class="mb-0">{'ms3_frontend_delivery_address' | lexicon}</h6>
        </div>
        <div class="card-body">
            <p class="mb-0">
                {if $address.index}{$address.index}, {/if}
                {$address.city}
                {if $address.region}, {$address.region}{/if}
                {if $address.country}, {$address.country}{/if}
                <br>
                {$address.street}
                {if $address.building}, bld. {$address.building}{/if}
                {if $address.entrance}, ent. {$address.entrance}{/if}
                {if $address.floor}, fl. {$address.floor}{/if}
                {if $address.room}, apt. {$address.room}{/if}
                {if $address.metro}
                <br>
                <small class="text-muted">Metro {$address.metro}</small>
                {/if}
                {if $address.text_address}
                <br>
                <small class="fst-italic text-muted">{$address.text_address}</small>
                {/if}
            </p>
        </div>
    </div>
    {/if}

    {* Delivery and payment *}
    <div class="row">
        {if $delivery && count($delivery) > 0}
        <div class="col-md-6">
            <div class="card shadow-sm mb-4">
                <div class="card-header bg-light">
                    <h6 class="mb-0">{'ms3_frontend_delivery_method' | lexicon}</h6>
                </div>
                <div class="card-body">
                    <p class="mb-0">{$delivery.name | lexicon}</p>
                    {if $delivery.description}
                    <small class="text-muted">{$delivery.description}</small>
                    {/if}
                </div>
            </div>
        </div>
        {/if}

        {if $payment && count($payment) > 0}
        <div class="col-md-6">
            <div class="card shadow-sm mb-4">
                <div class="card-header bg-light">
                    <h6 class="mb-0">{'ms3_frontend_payment_method' | lexicon}</h6>
                </div>
                <div class="card-body">
                    <p class="mb-0">{$payment.name | lexicon}</p>
                    {if $payment.description}
                    <small class="text-muted">{$payment.description}</small>
                    {/if}
                </div>
            </div>
        </div>
        {/if}
    </div>

    {* Comment *}
    {if $order.comment}
    <div class="card shadow-sm mb-4">
        <div class="card-header bg-light">
            <h6 class="mb-0">{'ms3_frontend_comment' | lexicon}</h6>
        </div>
        <div class="card-body">
            <p class="mb-0">{$order.comment}</p>
        </div>
    </div>
    {/if}
</div>

Pagination structure

php
[
    'total' => 50,           // Total orders
    'total_pages' => 3,      // Total pages
    'current_page' => 1,     // Current page
    'limit' => 20,           // Per page
    'offset' => 0,           // Offset
    'pages' => [             // Page list
        ['num' => 1, 'offset' => 0, 'active' => true],
        ['num' => 2, 'offset' => 20, 'active' => false],
        ['num' => 3, 'offset' => 40, 'active' => false],
    ],
    'has_prev' => false,     // Has previous page
    'has_next' => true,      // Has next page
    'prev_offset' => 0,      // Previous page offset
    'next_offset' => 20,     // Next page offset
]

Status structure

php
[
    ['id' => 2, 'name' => 'Paid', 'color' => '008000', 'selected' => false],
    ['id' => 3, 'name' => 'Shipped', 'color' => '0000FF', 'selected' => true],
    ['id' => 4, 'name' => 'Delivered', 'color' => '28a745', 'selected' => false],
]

System settings

SettingDescription
ms3_customer_orders_page_idOrders page ID
ms3_customer_cancel_allowed_statusesStatus IDs that allow cancellation (comma-separated)
ms3_status_canceledCanceled order status ID

CSS classes

ClassElement
.ms3-customer-ordersOrder list container
.ms3-customer-order-detailsOrder details container
.ms3-order-tableOrders table
.ms3-order-cancelCancel order button