Thursday, April 2, 2026

How to make changeable price of product on POS ?

 since version 2.2.4, we support feature changeable price of product on POS, it mean cashier can change price of product when add it to cart. You can see how it work follow image below

image
click item on cart, you see price , click on price 

image

change price and click Save.

To active this feature on our plugin, pls copy this code to end of file yourtheme/function.php

if(!function_exists('pricechangeable_op_product_data'))
{
    function pricechangeable_op_product_data($product_data,$product){
        $product_data['allow_change_price'] = true;
        
        return $product_data;
    }
}
add_filter('op_product_data','pricechangeable_op_product_data',10,2);
after done, goto pos , logout and login again and see effect.


This no need sine version 3.9. it implement as default plugin function https://prnt.sc/qnwfx8

How to ignore send email with new order made by POS ?

 Few my customer have request ignore send order receipt to customer via email with order made by POS. This is guide how to do it .


Add this code below to end of file : your_current_theme/function.php

if(!function_exists('custom_op_woocommerce_email_recipient_customer_invoice'))
{
    function custom_op_woocommerce_email_recipient_customer_invoice($recipient,$_order)
    {
        $billing_email = $_order->get_billing_email();
        $is_pos = get_post_meta($_order->get_id(),'_op_order_source',true);
        if($is_pos == 'openpos')
        {
            $recipient = '';
        }
        return $recipient;
    }
}
add_filter('woocommerce_email_recipient_customer_completed_order','custom_op_woocommerce_email_recipient_customer_invoice',10,2);
If you want disable all order email notification made from POS ( included customer + admin) , Add this code below to end of file : your_current_theme/function.php

if(!function_exists('custom_op_woocommerce_disable_email'))
{
    function custom_op_woocommerce_disable_email($recipient,$_order)
    {
        global $op_session_data;
        if($op_session_data)
        {
            $recipient = '';
        }
        return $recipient;
    }
}

add_filter('woocommerce_email_recipient_customer_completed_order','custom_op_woocommerce_disable_email',10,2);
add_filter('woocommerce_email_recipient_customer_invoice','custom_op_woocommerce_disable_email',10,2);
add_filter('woocommerce_email_recipient_customer_on_hold_order','custom_op_woocommerce_disable_email',10,2);
add_filter('woocommerce_email_recipient_customer_processing_order','custom_op_woocommerce_disable_email',10,2);
add_filter('woocommerce_email_recipient_customer_refunded_order','custom_op_woocommerce_disable_email',10,2);

add_filter('woocommerce_email_recipient_new_order','custom_op_woocommerce_disable_email',10,2);
add_filter('woocommerce_email_recipient_cancelled_order','custom_op_woocommerce_disable_email',10,2);
add_filter('woocommerce_email_recipient_failed_order','custom_op_woocommerce_disable_email',10,2);

How to display Variation product on grid instead select variable option

 Some use case, customer don't want select variation attribute, they want direct product on grid / search result. To do this , just simple add this filter code to your_active_theme/functions.php

if(!function_exists('custom_op_product_display_all'))
{
    function custom_op_product_display_all($product_data,$product){
        $product_data['display'] = true;
        $_product = wc_get_product($product_data['id']);
        if($_product)
        {

            if($_product->get_type() == 'variable')
            {
                $product_data['display'] = false; // option if you hide parent variable product

            }
        }
        if($product->post_type == 'product_variation')
        {
            $parent_id = $product->post_parent;
            if($parent_id)
            {
                $parent_product = wc_get_product($parent_id);
                $category = $parent_product->get_category_ids();

                $product_data['categories'] = $category;

            }
        }

        return $product_data;
    }
}
add_filter('op_product_data','custom_op_product_display_all',100,2);


And after done, just resync product list follow this way 

image

And this is result:

image
 

How to disable InStore features

 There are few my customer don't want update one by one for instore feature, they want all product on all outlet is same. you can do it by place this code to your_active_theme/functions.php


function custom_op_warehouse_is_instore($result){
    return true;
}
add_filter('op_warehouse_is_instore','custom_op_warehouse_is_instore',10,1);

How to disable Cart Custom Discount / Cart Custom Item Discount on POS

 You can do it by add this code to your_theme/functions.php


function custom_pos_discount_permission($session_response_data){
    
    $session_response_data['setting']['pos_disable_item_discount'] = 'yes';
    $session_response_data['setting']['pos_disable_cart_discount'] = 'yes';
    return $session_response_data;

}
add_filter('op_get_login_cashdrawer_data','custom_pos_discount_permission',10,1);

How to connect Open POS with stripe terminal "Verifone P400"

 on video https://www.youtube.com/watch?v=YrO7Gh7JHR8&list=PL5HioxGp7f8bbtcro2qdSm1EhPQnHPiQ9&index=31&t=82s


at video description. it already suggest to download and install free plugin:

https://wpos.app/openpos-addons/#ex-openpos-stripe-terminal


after install , goto admin/pos/setting/ payment setting , enable, save , and enter stripe information for connection
- goto admin/pos/register edit your register, choose terminal device you want for your register
- goto pos panel , clean browser cache, close and open again + test again ..

#note: make sure your terminal device should set currency + location in stripe terminal

How to change products / categories/ Table Grid size ?

 Since version 2.6.0 . Our plugin support add custom size for category / product grid.

To do it . Just copy and paste code below to  end of file
wp-content/themes/your_current_theme/function.php

if(!function_exists('custom_op_get_login_cashdrawer_data_grid'))
{
    function custom_op_get_login_cashdrawer_data_grid($session_response_data){
        $session_response_data['setting']['pos_cat_grid_column'] = 5;
        $session_response_data['setting']['pos_cat_grid_row'] = 5;

        $session_response_data['setting']['pos_product_grid_column'] = 5;
        $session_response_data['setting']['pos_product_grid_row'] = 5;


        $session_response_data['setting']['pos_desk_grid_row'] = 3;
        $session_response_data['setting']['pos_desk_grid_column'] = 4;
        return $session_response_data;
    }
}
add_filter('op_get_login_cashdrawer_data','custom_op_get_login_cashdrawer_data_grid',11,1);
in this sample, we change categories to 5x5 and product grid to 5x5, Tables grid to 4x3
You can change the column/row number by your self

since version > 3.0 . this thing already in admin / pos / setting , no need those custom code anymore. 

How to change product Grid to List with Scroll

 I got some question about change product list from grid to List with Scrollable. So, this is guide to do it :


Note: This not recommend with big product list . it make your browser broken / freeze cause much of process data.


#1. change setting column / row on admin / pos / setting / pos layout setting / product grid size with column x row = Your product total

image


#2. Change custom css with scrollable admin / pos / setting / pos layout setting / custom css. This is a sample css i tested. You can use your to make it more beauty . 

.product-main-list-container,
.product-main-list-container .product-main-list{
       overflow: scroll!important;


}
.product-paged-items .product-item{
  min-height: 100px;
}

How to change product grid Product Style

 I got some question about change product grid layout to old version. This post will guide you how to do it :


- Default new version product layout like this one

image

So,  to change back to our old grid style. you can goto Admin / POS / Setting / Pos layout setting / Pos custom css Field

image
and

image

and enter this css code to that field :

.product-main-list .product-item-container .product-item-detail{
   background-size: 100%!important;
   background-position: center center!important;
}
.product-item-container .product-info{
	background: rgba(0, 0, 0, 0.7215686274509804);
   
}
.product-info .product-price {
    text-align: center!important;
    color: #fff!important;
  
}
.product-info .product-sku,
.product-info .product-name  {
    text-align: center!important;
    
}
.product-info .product-name span {
    
    color: #ff6d00!important;
}

Click update setting . And goto POS , REFRESH POS page again to see the effect:

image

That Done.

There are good custom CSS style from customer , I think it good for other customers:

Custom CSS code: 

.product-main-list .product-item-container .product-item-detail{
   background-size: 100%!important;
   background-position: center center!important;
   border-radius: 10px;
}


.product-item-container .product-info{
    border-radius: 10px!important;


}
div.product-info .product-price {
    text-align: center!important;
    color: #fff!important;
}


.product-info .product-sku,
.product-info .product-name  {
    text-align: center!important;


}




 .product-info .product-name span {
    color: #fafafa!important;


}


div.product-info .product-sku span {
    
    display: none!important;
}




span.product-qty.ng-star-inserted{
    
    display: none!important;
}
.product-info{
    text-shadow: 2px 2px 4px black;
}
And Result:

image

Let enjoy with OpenPOS

How to change POS logo on Pos Panel

 If you want change logo or style in POS panel , you can goto admin / pos / setting / Pos layout setting and use CSS to update style





image

This is a custom css you can use to change POS logo in POS panel

.top-pos-logo-desktop{

 overflow: hidden;

}

.top-pos-logo-desktop p{

            text-indent: -999px;

       background: url(Your_logo_image_url) no-repeat;

        background-size: cover

}

.top-pos-logo-mobile a{
        text-indent: -999px;

        background: url(Your_logo_image_url) no-repeat;
        display: inline-block;
        width: 30px;
        height: 30px;
        background-size: cover;
      }

save change and refresh POS to see effect.

How to change default email domain @open-pos.com

 With openpos, cause wordpress user have email is require. So, with those customer add via openpos but have no email, openpos auto generate a email with email domain @open-pos.com . to change this default , shop owner can change it by add and custom this code to your-theme/functions.php


function custom_customer_demo_domain($demo_email){
     return '@your-custom-domain.com';
}
add_filter('op_customer_demo_domain','custom_customer_demo_domain',10,1);

How to Alway keep sale when logout ?

 some use case , you want keep sale as default, don't want active by default. You can add this code to end of this file yourtheme/functions.php


function custom_keep_sale_setting($session_response_data){

    $session_response_data['setting']['pos_default_clear_sale_data'] = 'no';

   return $session_response_data;
}
add_filter('op_get_login_cashdrawer_data','custom_keep_sale_setting',20,1);

If you want hire the switch save sale on logout popup, you can add this css to admin/pos/setting/pos layout setting / custom css

.keep-sale-option{


    display: none!important;
}

Done. Let REFESH + LOGOUT + LOGIN Pos again to see the effect.

How to addition field for Order in POS

 I got some use case, they want add more field to order via POS. 

Since version 3.3.2, we support add more order field API. So, it look like this image 

image

To do it . we need add those code into wp-content/themes/YOUR_ACTIVE_THEME/function.php

//order custom data
if(!function_exists('custom_order_field'))
{
    function custom_order_field($session_response_data){
        $addition_checkout_fields = array();
        $addition_checkout_fields[] = array(
            'code' => 'po',
            'type' => 'text',
            'label' => 'Test PO',
            'description' => '',
            'require' => 'yes', // yes or no
            'default' => '',
        );
        $session_response_data['setting']['pos_addition_checkout_fields'] = $addition_checkout_fields;
        return $session_response_data;
    }
}
add_filter('op_get_login_cashdrawer_data','custom_order_field',20,1);
if(!function_exists('custom_order_field_data'))
{
    // this function use to save or interactive with other system
    function custom_order_field_data($order,$order_data){
        $order_id = $order->get_id();
        $addition_information = isset($order_data['addition_information']) ? $order_data['addition_information'] : array();
        // continue logic from here to save or interactive with other system
    }
}
add_action('op_add_order_after','custom_order_field_data',10,2);
To display this field in receipt template,  you can use this shortcode in receipt template composer

<%- addition_information.po %>

with 'po' is field code

Woocommerce OpenPos External App Demo

index.php 
<?php

/*
Plugin Name: Woocommerce OpenPos External App Demo
Plugin URI: http://openswatch.com
Description: A Sample App for OpenPOS + Woocommerce.
Author: anhvnit@gmail.com
Author URI: http://openswatch.com/
Version: 1.0
WC requires at least: 2.6
Text Domain: openpos-app
License: GPL version 2 or later - http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*/

define('OPENPOS_APP_DIR',plugin_dir_path(__FILE__));
define('OPENPOS_APP_URL',plugins_url('/',__FILE__));


if(!class_exists('OP_App_Abstract'))
{
        $openpos_dir = dirname(rtrim(OPENPOS_APP_DIR , '/') ).'/woocommerce-openpos';
        require_once $openpos_dir.'/lib/abtract-op-app.php';
}

require_once OPENPOS_APP_DIR.'/my-op-app.php';

$myApp = new MyOpApp();

my-op-app.php

<?php
/**
 * Created by PhpStorm.
 * User: anhvnit
 * Date: 3/20/19
 * Time: 11:51
 */
class MyOpApp extends OP_App_Abstract implements OP_App {
    public $key = 'demo_app_key'; // unique
    public $name = 'Demo Open Pos App';
    public $thumb = OPENPOS_APP_URL.'app.png';
    public function render()
    {
       
        header('X-Frame-Options: allow-from *');
        global $in_pos_app;
        $in_pos_app = true;
        $session = $this->get_session();
        $email = $session['email'];
        $session_id = $session['session'];
        $user = wp_signon( array('user_login' => $email,'user_password' => $session_id) );
        require_once OPENPOS_APP_DIR.'/view/view.php';
    }

}

view/view.php

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js" integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU=" crossorigin="anonymous"></script>
<p>Cashier Name : <?php echo $session['name'];?></p>
<p>Login Time : <?php echo $session['logged_time'];?></p>
<p>your app template at here / html / css / js</p>
<p>Dynamic data get from your site</p>
<a href="javascript:void(0)" id="sample-link">test event</a>
<script type="text/javascript">
(function($) {
    $('#sample-link').click(function(){
        var addItemFired = new CustomEvent("openpos.add.to.cart", {"detail": {
            product : {
                "name": "ACMEe5",
                "id": 0,
                "parent_id": 0,
                "sku": "",
                "barcode": "000000022755",
                "price": 16,
                "price_incl_tax": 20,
                "final_price": 16,
                "manage_stock": false, // true or false
                "qty": 1, // limit qty
                "status": "publish",
                "options": [
                    {
                        'label' : "Radio Label",
                        'option_id' : 1,
                        'type' : 'radio',
                        'require' : true,
                        'options' : [
                            {'value_id' : 1, 'label': 'radio value 1','cost' : 5},
                            {'value_id' : 2, 'label': 'radio value 2','cost' : 2},
                         
                        ]
           
                    }
                ],
                "tax": [
                    {
                    "code": "standard_1",
                    "rate": 21,
                    "shipping": "yes",
                    "compound": "no",
                    "rate_id": 1,
                    "label": "Tax"
                    }
                ],
                "tax_amount": 3,
                "price_included_tax": 1,
                "display_special_price": false,
                "allow_change_price": true,
            }
        } });
        parent.dispatchEvent(addItemFired);
    });

}(jQuery));

</script>







How to add new features for OpenPOS plugin ?

 Recently, i got many questions of developer about add more function for POS panel.

The functions not in core of POS, those function like as : check booking , check license key , check contact message .... on POS panel. Because they don't want cashier use other page , they want access all other function inside POS panel only.

After time to research , in since version 2.5.4.2 , we have new screen and api  to display all external function ,i call it as "POS App", Those POS app create by add new plugin from wordpress. They are display on POS like image below

image

With new dashboard, you also able bookmark to display app to home page.

image

Ok, to make new app display on POS panel , you need create new plugin and install it into wordpress site. You can follow the plugin sample we create follow this URL:


https://github.com/anhvnit/openpos-intergrate/tree/master/woocommerce-openpos-app-demo

Or Download install file at :

https://github.com/anhvnit/openpos-intergrate/blob/master/woocommerce-openpos-app-demo.zip
or
Woocommerce OpenPos External App Demo

If you take look to sample plugin , you can see file my-op-app.php with class 

class MyOpApp extends OP_App_Abstract implements OP_App {
      public $key = 'demo_app_key'; // unique    

      public $name = 'Demo Open Pos App';
}

- The extend and implement class is important. The app only display on POS when your class extend follow abstract (OP_App_Abstract) and interface ( OP_App)
- The variable $key is unique , developer should make sure it is unique in your wordpress in case you want create multi POS App.
- File  view/view.php is place to enter content display on POS App, you can use variable $session to get current cashier login session .
In file , you can use to display external information / add  external action, i suggest use ajax in this page when you want use this page to submit , get any information on sub actions.

- Please create a support ticket if you have any question or have any idea to improve our plugin .

Thank you!

How to add more field in add new customer / edit customer on POS

 I got many my customer request about add more field to add / edit customer form in POS . And since version 2.3.6 , you can add more field to pos customer form by this way.

Add those code to end of file : your_theme/function.php


and in this sample, i will add company field to add/edit customer form in POS

#1 : Add customer custom field (company) to POS

if(!function_exists('pricechangeable_op_get_login_cashdrawer_data'))
{
    function pricechangeable_op_get_login_cashdrawer_data($session_response_data){
        $text_field = array(
            'code' => 'test_new_field',
            'type' => 'text',
            'label' => 'Company',
            'placeholder' => 'Enter New Field',
            'description' => '',
            'required' => 'no',
            'allow_customer' => 'yes', // no if not hide on billing
            'allow_shipping' => 'no' // value yes / no to allow display on shipping field
          );
      /*
        $date_field = array(
            'code' => 'dob',
            'type' => 'date',
            'label' => 'DOB',
            'placeholder' => 'Enter birthday',
            'description' => '',
            'allow_shipping' => 'no' // value yes / no to allow display on shipping field
        );
        $select_field = array(
            'code' => 'gender',
            'type' => 'select',
            'label' => 'Gender',
            'options' => array(
                ['value' => 'male','label' => 'Male'],
                ['value' => 'female','label' => 'Female'],
            ),
            'placeholder' => 'Gender',
            'description' => ''
        );
        $multiselect_field = array(
            'code' => 'multiselect',
            'type' => 'multi_select',
            'label' => 'Multi Select',
            'options' => array(
                ['value' => 'male','label' => 'Male'],
                ['value' => 'female','label' => 'Female'],
            ),
            'placeholder' => 'Multi Select',
            'description' => ''
        );
        $radio_field = array(
            'code' => 'radio',
            'type' => 'radio',
            'label' => 'Radio',
            'options' => array(
                ['value' => 'male','label' => 'Male'],
                ['value' => 'female','label' => 'Female'],
            ),
            'placeholder' => 'Radio Sample',
            'description' => ''
        );
        $checkbox_field = array(
            'code' => 'checkbox',
            'type' => 'checkbox',
            'label' => 'Checkbox',
            'options' => array(
                ['value' => 'male','label' => 'Male'],
                ['value' => 'female','label' => 'Female'],
            ),
            'placeholder' => 'Checkbox Sample',
            'description' => ''
        );
       */
        $session_response_data['setting']['openpos_customer_addition_fields'][] = $text_field;
        //$session_response_data['setting']['openpos_customer_addition_fields'][] = $date_field;
        //$session_response_data['setting']['openpos_customer_addition_fields'][] = $select_field;
        //$session_response_data['setting']['openpos_customer_addition_fields'][] = $multiselect_field;
        //$session_response_data['setting']['openpos_customer_addition_fields'][] = $radio_field;
        //$session_response_data['setting']['openpos_customer_addition_fields'][] = $checkbox_field;

        return $session_response_data;
    }
}
add_filter('op_get_login_cashdrawer_data','pricechangeable_op_get_login_cashdrawer_data',10,1);
#2: get customer custom field (company) when get customer data

function pricechangeable_op_customer_data($data){
    $customer_id = isset($data['id']) ? $data['id'] : 0;
    if($customer_id)
    {
        $customer = new WC_Customer($customer_id);
        $data['test_new_field'] = get_user_meta( $customer_id, 'test_new_field', true );
    }
    return $data;
}
add_filter('op_customer_data','pricechangeable_op_customer_data',10,1);
#3: Save new customer custom field (company)

add_action('op_add_customer_after',function($customer_id,$session_data,$customer_request_data){
    if(isset($customer_request_data['test_new_field']) && $customer_request_data['test_new_field'])
    {
        $test_new_field  = isset( $customer_request_data['test_new_field'] ) ? $customer_request_data['test_new_field'] : '';
        
        update_user_meta( $customer_id, 'test_new_field', $test_new_field  );
       
    }
},100,3);



#4: Update customer custom field (company)

add_action('op_update_customer_after',function($customer_id,$session_data,$customer_request_data){
    if(isset($customer_request_data['test_new_field']) && $customer_request_data['test_new_field'])
    {
        $test_new_field  = isset($customer_request_data['test_new_field']) ? $customer_request_data['test_new_field'] : '';
        update_user_meta( $customer_id, 'test_new_field', $test_new_field  );
        
    }
    
},100,3);



And this is the result . Please take note logout and login POS again to get effect.

image

#5. Display custom field on receipt composer

<%= customer.test_new_field %>

How to add Kitchen order area on Restaurant ?

As you know , since version 3.1.0 , we already support separate dish to Cook area and Bar area.
It mean when a dish add to table order, it auto separate and display on Cook or Bar area without print receipt and manual print receipt with paper.
But some use case in real life, may be your restaurant need more area , like as Cook area, Bar area, Fries area. Show , how to add Fries area to OpenPOS ? 
Yeah, since version 3.2.0. Openpos support this features. You can follow this code and enter it to end of  your_theme/functions.php 

if(!function_exists('custom_kitchen_area'))
{
    function custom_kitchen_area($result){

        $result['fries'] = array(
            'label' =>  'Fries area',
            'description' => 'Display on Fries area'
        );
        return $result;
    }
}
add_filter('op_list_restaurant_area','custom_kitchen_area',20,1);
After done . In Product edit / new page , you can see follow image below

image

In Kitchen view, a new area display to choose

image

How to add a customer to POS staff

on openpos, default on Store staff page, it show the list of user have role : admin or shop manager. But with those user with those role, they allow access to admin panel. In some case , you don't want store staff access to admin panel, just POS page only. You can use a customer role to do it . Note: This custom just effect to display customer to store staff available assign, not became store staff. To became store staff and able access to POS, admin should update field : "Is POS staff" to Yes and assign to a Register. So, to display user with customer role in Store Staff page, you can add this code to end of your_current_theme/function.php function op_allow_user_roles($roles) { $roles[] = 'customer'; return $roles; } add_filter('op_allow_user_roles','op_allow_user_roles',10,1); In case you want add a menu with link to POS panel in customer my account page, you can add more this code in enf of file your_current_theme/function.php if(!function_exists('op_woocommerce_account_menu_items')) { function op_woocommerce_account_menu_items($items,$endpoints) { $user_id = get_current_user_id(); if($user_id) { $allow_pos = get_user_meta($user_id,'_op_allow_pos',true); if($allow_pos) { $items['dashboard-pos'] = __('Open POS','openpos'); } } return $items; } } if(!function_exists('op_woocommerce_get_endpoint_url')) { function op_woocommerce_get_endpoint_url($url, $endpoint) { $user_id = get_current_user_id(); if($user_id) { $allow_pos = get_user_meta($user_id,'_op_allow_pos',true); if($allow_pos) { if($endpoint == 'dashboard-pos') { $url = 'https://yoursite.com/wp-content/plugins/woocommerce-openpos/pos/'; // Your current pos url } } } return $url; } } add_filter('woocommerce_account_menu_items','op_woocommerce_account_menu_items',10,2); add_filter('woocommerce_get_endpoint_url','op_woocommerce_get_endpoint_url',10,2);

dl

  download