diff --git a/.github/workflows/update-pot.yml b/.github/workflows/update-pot.yml
index 5283215..752382b 100644
--- a/.github/workflows/update-pot.yml
+++ b/.github/workflows/update-pot.yml
@@ -13,7 +13,7 @@ jobs:
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Setup PHP with tools
uses: shivammathur/setup-php@v2
diff --git a/includes/API/Orders.php b/includes/API/Orders.php
deleted file mode 100644
index fc5945b..0000000
--- a/includes/API/Orders.php
+++ /dev/null
@@ -1,454 +0,0 @@
-request = $request;
- $this->posted = $this->request->get_json_params();
-
- if ( 'POST' == $request->get_method() ) {
- $this->incoming_shop_order();
- }
-
- add_filter( 'rest_request_before_callbacks', array( $this, 'rest_request_before_callbacks' ), 10, 3 );
- add_filter( 'woocommerce_rest_shop_order_object_query', array( $this, 'order_query' ), 10, 2 );
- add_filter(
- 'woocommerce_rest_pre_insert_shop_order_object',
- array(
- $this,
- 'pre_insert_shop_order_object',
- ),
- 10,
- 3
- );
- add_filter( 'woocommerce_rest_prepare_shop_order_object', array( $this, 'order_response' ), 10, 3 );
- add_filter( 'woocommerce_order_get_items', array( $this, 'order_get_items' ), 10, 3 );
-
- add_action( 'woocommerce_rest_set_order_item', array( $this, 'rest_set_order_item' ), 10, 2 );
- add_filter(
- 'woocommerce_product_variation_get_attributes',
- array(
- $this,
- 'product_variation_get_attributes',
- ),
- 10,
- 2
- );
- add_action( 'woocommerce_before_order_object_save', array( $this, 'before_order_object_save' ), 10, 2 );
- add_filter( 'posts_clauses', array( $this, 'orderby_additions' ), 10, 2 );
- add_filter( 'option_woocommerce_tax_based_on', array( $this, 'tax_based_on' ), 10, 2 );
- }
-
- /**
- * Filters the response before executing any REST API callbacks.
- *
- * We can use this filter to bypass data validation checks
- *
- * @param mixed|WP_Error|WP_HTTP_Response|WP_REST_Response $response Result to send to the client.
- * Usually a WP_REST_Response or WP_Error.
- * @param array $handler Route handler used for the request.
- * @param WP_REST_Request $request Request used to generate the response.
- */
- public function rest_request_before_callbacks( $response, $handler, $request ) {
- if ( is_wp_error( $response ) ) {
- // Check if the error code 'rest_invalid_param' exists
- if ( $response->get_error_message( 'rest_invalid_param' ) ) {
- // Get the error data for 'rest_invalid_param'
- $error_data = $response->get_error_data( 'rest_invalid_param' );
-
- // Check if the invalid parameter was 'line_items'
- if ( \array_key_exists( 'line_items', $error_data['params'] ) ) {
- // Get the 'line_items' details
- $line_items_details = $error_data['details']['line_items'];
-
- // Check if 'line_items[X][quantity]' has 'rest_invalid_type'
- // Use a regular expression to match 'line_items[X][quantity]', where X is a number
- if ( 'rest_invalid_type' === $line_items_details['code'] &&
- preg_match( '/^line_items\[\d+\]\[quantity\]$/', $line_items_details['data']['param'] ) ) {
- if ( woocommerce_pos_get_settings( 'general', 'decimal_qty' ) ) {
- unset( $error_data['params']['line_items'], $error_data['details']['line_items'] );
- }
- }
-
- // Check if 'line_items[X][parent_name]' has 'rest_invalid_type'
- // Use a regular expression to match 'line_items[X][parent_name]', where X is a number
- if ( 'rest_invalid_type' === $line_items_details['code'] &&
- preg_match( '/^line_items\[\d+\]\[parent_name\]$/', $line_items_details['data']['param'] ) ) {
- unset( $error_data['params']['line_items'], $error_data['details']['line_items'] );
- }
- }
-
- // Check if the invalid parameter was 'billing'
- if ( \array_key_exists( 'billing', $error_data['params'] ) ) {
- // Get the 'billing' details
- $billing_details = $error_data['details']['billing'];
-
- // Check if 'billing' has 'rest_invalid_email'
- if ( 'rest_invalid_email' === $billing_details['code'] ) {
- unset( $error_data['params']['billing'], $error_data['details']['billing'] );
- }
- }
-
- // Check if the invalid parameter was 'orderby'
- if ( \array_key_exists( 'orderby', $error_data['params'] ) ) {
- // Get the 'orderby' details
- $orderby_details = $error_data['details']['orderby'];
-
- // Get the 'orderby' request
- $orderby_request = $request->get_param( 'orderby' );
-
- // Extended 'orderby' values
- $orderby_extended = array(
- 'status',
- 'customer_id',
- 'payment_method',
- 'total',
- );
-
- // Check if 'orderby' has 'rest_not_in_enum', but is in the extended 'orderby' values
- if ( 'rest_not_in_enum' === $orderby_details['code'] && \in_array( $orderby_request, $orderby_extended, true ) ) {
- unset( $error_data['params']['orderby'], $error_data['details']['orderby'] );
- }
- }
-
- // Check if $error_data['params'] is empty
- if ( empty( $error_data['params'] ) ) {
- return null;
- }
- // Remove old error data and add new error data
- $error_message = 'Invalid parameter(s): ' . implode( ', ', array_keys( $error_data['params'] ) ) . '.';
-
- $response->remove( 'rest_invalid_param' );
- $response->add( 'rest_invalid_param', $error_message, $error_data );
- }
- }
-
- return $response;
- }
-
-
-
- public function incoming_shop_order(): void {
- $raw_data = $this->request->get_json_params();
-
- /*
- * WC REST validation enforces email address for orders
- * this hack allows guest orders to bypass this validation
- */
- if ( isset( $raw_data['customer_id'] ) && 0 == $raw_data['customer_id'] ) {
- add_filter(
- 'is_email',
- function ( $result, $email ) {
- if ( ! $email ) {
- return true;
- }
-
- return $result;
- },
- 10,
- 2
- );
- }
- }
-
- /**
- * Filters the value of the woocommerce_tax_based_on option.
- *
- * @param mixed $value Value of the option.
- * @param string $option Option name.
- */
- public function tax_based_on( $value, $option ) {
- $tax_based_on = 'base'; // default value is base
-
- // try to get POS tax settings from order meta
- $raw_data = $this->request->get_json_params();
- if ( isset( $raw_data['meta_data'] ) ) {
- foreach ( $raw_data['meta_data'] as $meta ) {
- if ( '_woocommerce_pos_tax_based_on' == $meta['key'] ) {
- $tax_based_on = $meta['value'];
- }
- }
- }
-
- return $tax_based_on;
- }
-
- /**
- * Filter the query arguments for a request.
- *
- * @param array $args Key value array of query var to query value.
- * @param WP_REST_Request $request The request used.
- *
- * @return array $args Key value array of query var to query value.
- */
- public function order_query( $args, WP_REST_Request $request ) {
- return $args;
- }
-
- /**
- * @param $order
- * @param $request
- * @param $creating
- */
- public function pre_insert_shop_order_object( $order, $request, $creating ) {
- return $order;
- }
-
- /**
- * @param WP_REST_Response $response The response object.
- * @param WC_Order $order Object data.
- * @param WP_REST_Request $request Request object.
- *
- * @return WP_REST_Response
- */
- public function order_response( WP_REST_Response $response, WC_Order $order, WP_REST_Request $request ): WP_REST_Response {
- $data = $response->get_data();
-
- // Add UUID to order
- $this->maybe_add_post_uuid( $order );
-
- // Add payment link to the order.
- $pos_payment_url = add_query_arg(
- array(
- 'pay_for_order' => true,
- 'key' => $order->get_order_key(),
- ),
- get_home_url( null, '/wcpos-checkout/order-pay/' . $order->get_id() )
- );
-
- $response->add_link( 'payment', $pos_payment_url, array( 'foo' => 'bar' ) );
-
- // Add receipt link to the order.
- $pos_receipt_url = get_home_url( null, '/wcpos-checkout/wcpos-receipt/' . $order->get_id() );
- $response->add_link( 'receipt', $pos_receipt_url );
-
- // Make sure we parse the meta data before returning the response
- $order->save_meta_data(); // make sure the meta data is saved
- $data['meta_data'] = $this->parse_meta_data( $order );
-
- $response->set_data( $data );
- // $this->log_large_rest_response( $response, $order->get_id() );
-
- return $response;
- }
-
- /**
- * @param $items WC_Order_Item[]
- * @param $order WC_Order
- * @param $item_type string[] ['line_item' | 'fee' | 'shipping' | 'tax' | 'coupon']
- *
- * @return WC_Order_Item[]
- */
- public function order_get_items( array $items, WC_Order $order, array $item_type ): array {
- foreach ( $items as $item ) {
- $this->maybe_add_order_item_uuid( $item );
- }
-
- return $items;
- }
-
- /**
- * @param $item
- * @param $posted
- *
- * @throws Exception
- */
- public function rest_set_order_item( $item, $posted ): void {
- /*
- * fixes two problems wth WC REST API
- * - variation meta_data with 'any' are not being saved
- * - default variation meta_data is always added (not unique)
- */
- if ( isset( $posted['variation_id'] ) && 0 !== $posted['variation_id'] ) {
- $variation = wc_get_product( (int) $posted['variation_id'] );
- $valid_keys = array();
-
- if ( \is_callable( array( $variation, 'get_variation_attributes' ) ) ) {
- foreach ( $variation->get_variation_attributes() as $attribute_name => $attribute ) {
- $valid_keys[] = str_replace( 'attribute_', '', $attribute_name );
- }
-
- if ( isset( $posted['meta_data'] ) && \is_array( $posted['meta_data'] ) ) {
- foreach ( $posted['meta_data'] as $meta ) {
- // fix initial item creation
- if ( isset( $meta['attr_id'] ) ) {
- if ( 0 == $meta['attr_id'] ) {
- // not a taxonomy
- if ( \in_array( strtolower( $meta['display_key'] ), $valid_keys, true ) ) {
- $item->add_meta_data( strtolower( $meta['display_key'] ), $meta['display_value'], true );
- }
- } else {
- $taxonomy = wc_attribute_taxonomy_name_by_id( $meta['attr_id'] );
-
- $terms = get_the_terms( (int) $posted['product_id'], $taxonomy );
- if ( ! empty( $terms ) ) {
- foreach ( $terms as $term ) {
- if ( $term->name === $meta['display_value'] ) {
- $item->add_meta_data( $taxonomy, $term->slug, true );
- }
- }
- }
- }
- }
- // fix subsequent overwrites
- if ( wc_attribute_taxonomy_id_by_name( $meta['key'] ) || \in_array( $meta['key'], $valid_keys, true ) ) {
- $item->add_meta_data( $meta['key'], $meta['value'], true );
- }
- }
- }
- }
- }
- }
-
- /**
- * @param $value
- * @param WC_Product_Variation $variation
- *
- * @return void
- */
- public function product_variation_get_attributes( $value, WC_Product_Variation $variation ) {
- /*
- * - could fix 'any' options here using raw posted data
- * - may be useful for product title generation
- */
-
- return $value;
- }
-
- /**
- * Add custom 'created_via' prop for POS orders, used in WC Admin display.
- *
- * @param WC_Order $order The object being saved.
- *
- * @throws WC_Data_Exception
- */
- public function before_order_object_save( WC_Order $order ): void {
- if ( 0 === $order->get_id() ) {
- $order->set_created_via( PLUGIN_NAME );
- }
-
- /**
- * Add cashier user id to order meta
- * Note: There should only be one cashier per order, currently this will overwrite previous cashier id.
- */
- $user_id = get_current_user_id();
- $cashier_id = $order->get_meta( '_pos_user' );
-
- if ( ! $cashier_id ) {
- $order->update_meta_data( '_pos_user', $user_id );
- }
- }
-
- /**
- * Filters all query clauses at once, for convenience.
- *
- * Covers the WHERE, GROUP BY, JOIN, ORDER BY, DISTINCT,
- * fields (SELECT), and LIMIT clauses.
- *
- * @param string[] $clauses {
- * Associative array of the clauses for the query.
- *
- * @var string The WHERE clause of the query.
- * @var string The GROUP BY clause of the query.
- * @var string The JOIN clause of the query.
- * @var string The ORDER BY clause of the query.
- * @var string The DISTINCT clause of the query.
- * @var string The SELECT clause of the query.
- * @var string The LIMIT clause of the query.
- * }
- *
- * @param WP_Query $wp_query The WP_Query instance (passed by reference).
- */
- public function orderby_additions( array $clauses, WP_Query $wp_query ): array {
- global $wpdb;
-
- $order = $wp_query->query_vars['order'] ?? 'DESC';
-
- if ( isset( $wp_query->query_vars['orderby'] ) ) {
-
- // add option to order by status
- if ( 'status' === $wp_query->query_vars['orderby'] ) {
- $clauses['join'] .= " LEFT JOIN {$wpdb->prefix}posts AS order_posts ON {$wpdb->prefix}posts.ID = order_posts.ID ";
- $clauses['orderby'] = ' order_posts.post_status ' . $order;
- }
-
- // add option to order by customer_id
- if ( 'customer_id' === $wp_query->query_vars['orderby'] ) {
- $clauses['join'] .= " LEFT JOIN {$wpdb->prefix}postmeta AS customer_meta ON {$wpdb->prefix}posts.ID = customer_meta.post_id ";
- $clauses['where'] .= " AND customer_meta.meta_key = '_customer_user' ";
- $clauses['orderby'] = ' customer_meta.meta_value ' . $order;
- }
-
- // add option to order by payment_method
- if ( 'payment_method' === $wp_query->query_vars['orderby'] ) {
- $clauses['join'] .= " LEFT JOIN {$wpdb->prefix}postmeta AS payment_method_meta ON {$wpdb->prefix}posts.ID = payment_method_meta.post_id ";
- $clauses['where'] .= " AND payment_method_meta.meta_key = '_payment_method' ";
- $clauses['orderby'] = ' payment_method_meta.meta_value ' . $order;
- }
-
- // add option to order by total
- if ( 'total' === $wp_query->query_vars['orderby'] ) {
- $clauses['join'] .= " LEFT JOIN {$wpdb->prefix}postmeta AS total_meta ON {$wpdb->prefix}posts.ID = total_meta.post_id ";
- $clauses['where'] .= " AND total_meta.meta_key = '_order_total' ";
- $clauses['orderby'] = ' total_meta.meta_value+0 ' . $order;
- }
- }
-
- return $clauses;
- }
-
- /**
- * Returns array of all order ids.
- *
- * @param array $fields
- *
- * @return array|WP_Error
- */
- public function get_all_posts( array $fields = array() ): array {
- $args = array(
- 'limit' => -1,
- 'return' => 'ids',
- 'status' => array_keys( wc_get_order_statuses() ), // Get valid order statuses
- );
-
- $order_query = new WC_Order_Query( $args );
-
- try {
- $order_ids = $order_query->get_orders();
-
- return array_map( array( $this, 'format_id' ), $order_ids );
- } catch ( Exception $e ) {
- Logger::log( 'Error fetching order IDs: ' . $e->getMessage() );
-
- return new WP_Error(
- 'woocommerce_pos_rest_cannot_fetch',
- 'Error fetching order IDs.',
- array( 'status' => 500 )
- );
- }
- }
-}
diff --git a/includes/i18n.php b/includes/i18n.php
index 9bd837a..07d6b92 100644
--- a/includes/i18n.php
+++ b/includes/i18n.php
@@ -14,4 +14,11 @@
namespace WCPOS\WooCommercePOS;
class i18n {
+
+ /**
+ * Load the plugin text domain for translation.
+ */
+ public function construct() {
+ load_plugin_textdomain( 'woocommerce-pos', false, PLUGIN_PATH . '/languages/' );
+ }
}
diff --git a/templates/legacy-receipt.php b/templates/legacy-receipt.php
deleted file mode 100644
index 3136b01..0000000
--- a/templates/legacy-receipt.php
+++ /dev/null
@@ -1,308 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{#formatAddress billing_address title=""}}
-
-
- {{formatAddress shipping_address title=""}}
-
-
-
-
- |
- {{order_number}} |
-
-
- |
- {{formatDate completed_at format="MMMM Do YYYY, h:mm:ss a"}} |
-
- {{#if cashier}}
-
- |
- {{cashier.first_name}} {{cashier.last_name}} |
-
- {{/if}}
-
- |
- {{payment_details.method_title}} |
-
- {{#if billing_address.email}}
-
-
-
- |
- {{billing_address.email}} |
-
- {{/if}}
- {{#if billing_address.phone}}
-
-
-
- |
- {{billing_address.phone}} |
-
- {{/if}}
-
-
-
-
-
-
- |
- |
-
-
- |
-
-
-
- {{#each line_items}}
-
-
- {{name}}
- {{#with meta}}
-
- {{#each meta}}
- - {{#if label}}{{label}}{{else}}{{key}}{{/if}}:
- - {{value}}
- {{/each}}
-
- {{/with}}
- |
- {{number quantity precision="auto"}} |
-
- {{#if on_sale}}
- {{{money subtotal}}}
- {{{money total}}}
- {{else}}
- {{{money total}}}
- {{/if}}
- |
-
- {{/each}}
-
-
-
- : |
- {{{money subtotal}}} |
-
- {{#if has_discount}}
-
- : |
- {{{money cart_discount negative=true}}} |
-
- {{/if}}
- {{#each shipping_lines}}
-
- {{method_title}}: |
- {{{money total}}} |
-
- {{/each}}
- {{#each fee_lines}}
-
- {{name}}: |
- {{{money total}}} |
-
- {{/each}}
- {{#if has_tax}}
- {{#if itemized}}
- {{#each tax_lines}}
- {{#if has_tax}}
-
-
- {{#if ../../incl_tax}}(
- ){{/if}}
- {{title}}:
- |
- {{{money total}}} |
-
- {{/if}}
- {{/each}}
- {{else}}
-
-
- {{#if incl_tax}}(
- ){{/if}}
- countries->tax_or_vat() ); ?>
- |
- {{{money total_tax}}} |
-
- {{/if}}
- {{/if}}
-
- {{#if has_order_discount}}
-
- : |
- {{{money order_discount negative=true}}} |
-
- {{/if}}
-
-
-
-
- :
- |
- {{{money total}}} |
-
- {{#if payment_details.method_pos_cash}}
-
- : |
- {{{money payment_details.method_pos_cash.tendered}}} |
-
-
- : |
- {{{money payment_details.method_pos_cash.change}}} |
-
- {{/if}}
-
-
-{{note}}
-
-
diff --git a/templates/payment.php b/templates/payment.php
index e6366c3..bb63178 100644
--- a/templates/payment.php
+++ b/templates/payment.php
@@ -59,48 +59,48 @@
background-color: #ffffff !important;
}
- .woocommerce-pos-troubleshooting {
- border-left: 4px solid #007cba; /* For the blue vertical line */
- padding: 5px 10px; /* Padding around the text */
- background: #fff; /* White background */
- box-shadow: 0 1px 1px rgba(0,0,0,.04); /* Subtle shadow effect */
- }
+ .woocommerce-pos-troubleshooting {
+ border-left: 4px solid #007cba; /* For the blue vertical line */
+ padding: 5px 10px; /* Padding around the text */
+ background: #fff; /* White background */
+ box-shadow: 0 1px 1px rgba(0,0,0,.04); /* Subtle shadow effect */
+ }
.woocommerce-pos-troubleshooting p.link {
margin: 0;
}
- .woocommerce-pos-troubleshooting h3 {
- margin: 0.5em 0;
- padding: 0;
- font-size: 1em;
- font-weight: 600;
- }
-
- .woocommerce-pos-troubleshooting div {
- flex: 1;
- margin-right: 20px;
- }
-
- .woocommerce-pos-troubleshooting input[type="checkbox"] {
- margin-right: 5px;
- }
-
- .woocommerce-pos-troubleshooting button {
- margin-top: 20px;
- background: #007cba;
- border: none;
- color: #fff;
- padding: 10px 15px;
- border-radius: 3px;
- cursor: pointer;
- }
-
- .woocommerce-pos-troubleshooting button:hover {
- background: #005a87;
- }
-
- .woocommerce-error {
+ .woocommerce-pos-troubleshooting h3 {
+ margin: 0.5em 0;
+ padding: 0;
+ font-size: 1em;
+ font-weight: 600;
+ }
+
+ .woocommerce-pos-troubleshooting div {
+ flex: 1;
+ margin-right: 20px;
+ }
+
+ .woocommerce-pos-troubleshooting input[type="checkbox"] {
+ margin-right: 5px;
+ }
+
+ .woocommerce-pos-troubleshooting button {
+ margin-top: 20px;
+ background: #007cba;
+ border: none;
+ color: #fff;
+ padding: 10px 15px;
+ border-radius: 3px;
+ cursor: pointer;
+ }
+
+ .woocommerce-pos-troubleshooting button:hover {
+ background: #005a87;
+ }
+
+ .woocommerce-error {
background-color: #f8d7da;
color: #721c24;
border-color: #f5c6cb;
@@ -264,7 +264,8 @@
>
-
+queue;
$scriptHandles = $wp_scripts->queue;
@@ -279,53 +280,55 @@
woocommerce_pos_get_settings( 'checkout', 'dequeue_script_handles' )
);
-$mergedStyleHandles = array_unique(array_merge($styleHandles, $style_exclude_list));
-$mergedScriptHandles = array_unique(array_merge($scriptHandles, $script_exclude_list));
+$mergedStyleHandles = array_unique( array_merge( $styleHandles, $style_exclude_list ) );
+$mergedScriptHandles = array_unique( array_merge( $scriptHandles, $script_exclude_list ) );
?>
-
+ :
display_name ); ?>
-
+ :
ID ? esc_html__( 'Guest', 'woocommerce-pos' ) : esc_html( $woocommerce_pos_customer->display_name ); ?>
@@ -383,19 +386,28 @@
get_items( 'coupon' );
-if ( $coupons ) {
- echo '
' . __('Applied coupons', 'woocommerce') . '
';
- echo '
';
- foreach ( $coupons as $coupon ) {
- echo '- ' . esc_html( $coupon->get_code() ) . '
';
- }
- echo '
';
-}
-?>
+ if ( $coupons ) {
+ echo '
' . __( 'Applied coupons', 'woocommerce' ) . '
';
+ echo '
';
+ foreach ( $coupons as $coupon ) {
+ echo '- ' . esc_html( $coupon->get_code() ) . '
';
+ }
+ echo '
';
+ }
+ ?>
- $order, 'available_gateways' => $available_gateways, 'order_button_text' => $order_button_text ) ); ?>
+ $order,
+ 'available_gateways' => $available_gateways,
+ 'order_button_text' => $order_button_text,
+ )
+ );
+ ?>
diff --git a/templates/receipt.php b/templates/receipt.php
index 874b889..881041d 100644
--- a/templates/receipt.php
+++ b/templates/receipt.php
@@ -178,7 +178,7 @@
if ( $pos_user ) {
$user = get_user_by( 'id', $pos_user );
$user_name = $user->display_name;
- echo '' . esc_html__( 'Cashier:', 'woocommerce-pos' ) . ' ' . esc_html( $user_name ) . '';
+ echo '' . esc_html__( 'Cashier', 'woocommerce-pos' ) . ': ' . esc_html( $user_name ) . '';
}
?>
get_payment_method_title() ) { ?>