Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🚀 enhance: Indexed DB integration to sync products over counters and improve frontend products search #126

Open
wants to merge 34 commits into
base: develop
Choose a base branch
from

Conversation

devAsadNur
Copy link
Member

@devAsadNur devAsadNur commented Oct 24, 2022

The IndexedDB has been integrated to sync products over multiple counters. Also, improved frontend product searching.

Summary by CodeRabbit

  • New Features

    • Introduced a comprehensive set of methods for managing a product database using IndexedDB, including creating, inserting, retrieving, updating, and deleting products.
    • Added a refresh icon in the Home component to refresh product logs and update the IndexedDB with the latest data.
  • Improvements

    • Enhanced user feedback during refresh operations with loading states and toast notifications.
    • Updated styles for the new refresh icon and animations.
  • Chores

    • Ensured the IndexedDB for products is created if it does not already exist during the component lifecycle.

…interval to 15 seconds. Decremented product logs counter counts at the time of deleting any counter
@devAsadNur devAsadNur self-assigned this Oct 24, 2022
@devAsadNur devAsadNur added the Needs Dev Review This PR needs review by a developer label Oct 31, 2022
assets/src/frontend/components/ProductSearch.vue Outdated Show resolved Hide resolved
assets/src/frontend/components/ProductSearch.vue Outdated Show resolved Hide resolved
includes/Ajax.php Outdated Show resolved Hide resolved
include_once ABSPATH . 'wp-admin/includes/upgrade.php';

$tables = [
"CREATE TABLE IF NOT EXISTS `{$wpdb->prefix}wepos_product_logs` (

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use IF NOT EXISTS while creating tables using dbDelta as it will synchronize the schema chnages automatically.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bhai, in case of future changes to the table schema, we'll implement the changes manually using upgraders. So, I'm leaving IF NOT EXISTS here.

includes/Installer.php Outdated Show resolved Hide resolved
includes/Installer.php Outdated Show resolved Hide resolved
includes/Installer.php Outdated Show resolved Hide resolved
4) NOT NULL DEFAULT 0.0000,
`product_stock` bigint signed NULL,
`counter_counts` bigint unsigned NULL DEFAULT 0,
PRIMARY KEY (`id`)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also adding index for product_id would be better.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bhai, the table rows are going to be created/updated/deleted frequently while products are sold or edited by multiple counters since this is the product logs table. So, we are not implementing indexing here.

includes/ProductsLog.php Outdated Show resolved Hide resolved
includes/Admin/Updates/upgrade-1.2.5.php Outdated Show resolved Hide resolved
@tanmayjay tanmayjay added Needs Author Reply and removed Needs Dev Review This PR needs review by a developer labels Feb 21, 2023
@devAsadNur devAsadNur requested a review from Aunshon May 28, 2024 10:32
Copy link

coderabbitai bot commented May 28, 2024

Walkthrough

The recent updates enhance the product management capabilities within the wePOS system by implementing a module for managing a product database using IndexedDB. This includes functionalities for creating and manipulating product entries, allowing for efficient searching, updating, and deletion of products. Additionally, a new feature for refreshing product logs has been introduced in the Home component, improving data synchronization and user experience.

Changes

Files/Groups Change Summary
assets/src/utils/productIndexedDb.js Introduced methods for managing a product database using IndexedDB, including creation, insertion, retrieval, searching, updating, and deletion of products.
assets/src/frontend/components/Home.vue Added a refresh feature for product logs, including methods for fetching logs and managing loading states. Updated lifecycle hooks and UI elements for better user interaction.

Assessment against linked issues

Objective Addressed Explanation
Ensure all products can be found by searching (Issue #125)
Improve product loading performance (Issue #125)
Handle large datasets efficiently (Issue #125)

Poem

In the database where products dwell,
A search so quick, all will excel.
With IndexedDB, the treasures align,
Finding goods is simply divine.
No more delays, just swift delight,
WePOS shines, oh what a sight! 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

Outside diff range and nitpick comments (3)
includes/REST/ProductsLogController.php (1)

217-217: Ensure that error messages are localized for better user experience.

Consider using localization functions for error messages to support multiple languages.

Also applies to: 255-255, 321-321, 409-409

assets/src/frontend/components/ProductSearch.vue (1)

245-250: Optimize the search functionality by caching results or using more efficient search algorithms.

Consider implementing caching mechanisms or optimizing the search algorithm to improve performance.

assets/src/frontend/components/Home.vue (1)

1241-1248: Use CSS animations responsibly.

While the rotation animation on the refresh icon is a nice visual touch, ensure it does not distract from the user experience, especially in a fast-paced environment like a POS system.

Review Details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits Files that changed from the base of the PR and between 72fead3 and 19bb681.
Files selected for processing (18)
  • assets/less/style.less (1 hunks)
  • assets/src/frontend/components/CustomerSearch.vue (8 hunks)
  • assets/src/frontend/components/Home.vue (8 hunks)
  • assets/src/frontend/components/ProductSearch.vue (6 hunks)
  • assets/src/utils/Bootstrap.js (3 hunks)
  • assets/src/utils/productIndexedDb.js (1 hunks)
  • assets/src/utils/productLogs.js (1 hunks)
  • includes/Admin/Updates.php (1 hunks)
  • includes/Admin/Updates/upgrade-1.3.0.php (1 hunks)
  • includes/Ajax.php (1 hunks)
  • includes/Assets.php (2 hunks)
  • includes/Installer.php (1 hunks)
  • includes/ProductsLog.php (1 hunks)
  • includes/REST/Manager.php (2 hunks)
  • includes/REST/ProductsLogController.php (1 hunks)
  • includes/Uninstaller.php (1 hunks)
  • includes/functions.php (1 hunks)
  • wepos.php (3 hunks)
Files skipped from review due to trivial changes (2)
  • assets/less/style.less
  • assets/src/frontend/components/CustomerSearch.vue
Additional Context Used
Biome (23)
assets/src/utils/Bootstrap.js (16)

46-66: This function expression can be turned into an arrow function.


68-72: This function expression can be turned into an arrow function.


79-81: This function expression can be turned into an arrow function.


88-88: The computed expression can be simplified without the use of a string literal.


89-89: The computed expression can be simplified without the use of a string literal.


90-90: The computed expression can be simplified without the use of a string literal.


91-91: The computed expression can be simplified without the use of a string literal.


92-92: The computed expression can be simplified without the use of a string literal.


93-93: The computed expression can be simplified without the use of a string literal.


94-94: The computed expression can be simplified without the use of a string literal.


95-95: The computed expression can be simplified without the use of a string literal.


96-96: The computed expression can be simplified without the use of a string literal.


116-119: This function expression can be turned into an arrow function.


121-130: This function expression can be turned into an arrow function.


113-131: This function expression can be turned into an arrow function.


114-114: This let declares a variable that is only assigned once.

assets/src/utils/productIndexedDb.js (4)

85-94: Prefer for...of instead of forEach.


153-153: This let declares a variable that is only assigned once.


156-156: This let declares a variable that is only assigned once.


158-158: This let declares a variable that is only assigned once.

assets/src/utils/productLogs.js (3)

3-12: Prefer for...of instead of forEach.


16-16: Template literals are preferred over string concatenation.


18-18: Template literals are preferred over string concatenation.

Additional comments not posted (21)
includes/Uninstaller.php (2)

20-22: The run method is well-implemented and clear in its purpose.


31-44: Ensure that the capabilities being removed are not required elsewhere in the application.

includes/Ajax.php (2)

19-21: The constructor is correctly set up to register the heartbeat handlers.


34-44: Ensure that all data received from the front end is properly sanitized and unslashed.

Verification successful

The verification process has confirmed that the data received from the front end is properly sanitized and unslashed in the relevant files.

  • The $_POST and $_GET variables are handled with appropriate sanitization functions such as wp_unslash, sanitize_text_field, and others.
  • The specific instances in the includes/Ajax.php file and other related files show proper sanitization practices.
Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that all data received from the front end is properly sanitized.

# Test: Search for unsanitized data usage. Expect: No unsanitized data.
rg --type php '$_POST|$_GET'

Length of output: 30



Script:

#!/bin/bash
# Description: Verify that all data received from the front end is properly sanitized.

# Search for all instances of $_POST and $_GET in the codebase.
rg --type php '\$_POST|\$_GET' -l | xargs -I {} sh -c 'echo "File: {}"; rg --type php "\$_POST|\$_GET" {}'

# Check if these instances are properly sanitized.
rg --type php 'wp_unslash|sanitize_text_field|sanitize_email|sanitize_key|sanitize_meta|sanitize_mime_type|sanitize_option|sanitize_sql_orderby|sanitize_textarea_field|sanitize_title|sanitize_title_for_query|sanitize_title_with_dashes|sanitize_user'

Length of output: 2657

includes/Admin/Updates/upgrade-1.3.0.php (1)

10-12: The update function is well-structured and clear in its purpose.

includes/Installer.php (2)

20-23: The run method is well-implemented and clear in its purpose.


32-55: Ensure that the capabilities being added are appropriate and not excessive.

Verification successful

The capabilities publish_shop_orders and list_users are used in multiple places within the codebase, including their addition and removal in the installer and uninstaller, and checks in the REST controller. This indicates that these capabilities are necessary and appropriately scoped.

  • includes/Installer.php: Adds the capabilities.
  • includes/Uninstaller.php: Removes the capabilities.
  • includes/REST/PaymentController.php: Checks for the publish_shop_orders capability.

These usages suggest that the capabilities are not excessive and are appropriately managed within the codebase.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that added capabilities are appropriate and not excessive.

# Test: Search for usage of added capabilities. Expect: No excessive or inappropriate capabilities.
rg --type php 'publish_shop_orders|list_users'

Length of output: 906

includes/Admin/Updates.php (1)

18-18: The addition of the new update entry for version 1.3.0 is correctly implemented.

assets/src/utils/Bootstrap.js (2)

97-98: LGTM! Adding productIndexedDb and productLogs to the global window.wepos object facilitates their access across the application.


14-15: Ensure that the imported modules productIndexedDb and productLogs are correctly implemented and exported in their respective files.

Verification successful

The modules productIndexedDb and productLogs are correctly implemented and exported in their respective files.

  • assets/src/utils/productIndexedDb.js
  • assets/src/utils/productLogs.js
Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify the correct implementation and export of modules in productIndexedDb.js and productLogs.js.

# Test: Search for the export statements in productIndexedDb.js and productLogs.js. Expect: Correct export statements.
rg --type js --files-with-matches 'export' src/utils/productIndexedDb.js src/utils/productLogs.js

Length of output: 232



Script:

#!/bin/bash
# Description: Locate the correct paths for productIndexedDb and productLogs.

# Search for the files named productIndexedDb.js and productLogs.js in the repository.
fd productIndexedDb.js
fd productLogs.js

Length of output: 113



Script:

#!/bin/bash
# Description: Verify the correct implementation and export of modules in productIndexedDb.js and productLogs.js.

# Test: Search for the export statements in productIndexedDb.js and productLogs.js. Expect: Correct export statements.
rg --type js --files-with-matches 'export' assets/src/utils/productIndexedDb.js assets/src/utils/productLogs.js

Length of output: 180

includes/REST/Manager.php (1)

24-31: LGTM! The addition of ProductsLogController to the class map ensures that it is loaded and its routes are registered, which is crucial for handling product logs.

includes/Assets.php (1)

196-196: Ensure that the 'heartbeat' script is correctly registered before enqueuing to avoid potential issues.

Verification successful

The 'heartbeat' script is not registered within the codebase, but it is enqueued in includes/Assets.php. This suggests that 'heartbeat' might be a default script provided by WordPress, which does not require explicit registration.

  • includes/Assets.php: wp_enqueue_script( 'heartbeat' );
Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that the 'heartbeat' script is registered before being enqueued.

# Test: Search for the registration of the 'heartbeat' script. Expect: Registration statement before the enqueue.
rg --type php 'wp_register_script.*heartbeat' includes/Assets.php

Length of output: 67



Script:

#!/bin/bash
# Description: Verify that the 'heartbeat' script is registered before being enqueued.

# Test: Search for the registration of the 'heartbeat' script in the entire codebase.
rg --type php 'wp_register_script.*heartbeat'

Length of output: 47



Script:

#!/bin/bash
# Description: Verify if 'heartbeat' is a default script provided by WordPress.

# Test: Search for 'heartbeat' in the WordPress core scripts documentation or source code.
rg --type php 'heartbeat' wp-includes

Length of output: 89



Script:

#!/bin/bash
# Description: Verify if 'heartbeat' is a default script provided by WordPress.

# Test: Search for 'heartbeat' in the entire repository to verify if it is a default script.
rg --type php 'heartbeat'

Length of output: 484

wepos.php (3)

275-277: Ensure proper error handling during plugin activation.

The activation method now uses an Installer class to run installation tasks. It's crucial to ensure that any potential errors during the installation process are handled gracefully. Consider adding error handling mechanisms or logging to capture and respond to any issues that might occur during activation.

try {
    $installer = new WeDevs\WePOS\Installer();
    $installer->run();
} catch (Exception $e) {
    // Handle exception or log error
}

288-290: Ensure proper error handling during plugin deactivation.

Similar to the activation process, the deactivation method uses an Uninstaller class. Proper error handling should also be implemented here to manage any exceptions or errors that might arise during the deactivation process.

try {
    $uninstaller = new WeDevs\WePOS\Uninstaller();
    $uninstaller->run();
} catch (Exception $e) {
    // Handle exception or log error
}

333-337: Initialization of new components in the plugin architecture.

The addition of new components such as common, rest, assets, products_log, and ajax in the init_classes method is a good architectural move. It modularizes the code and separates concerns, making the system more maintainable and scalable. Ensure that these components are properly documented and tested to maintain code quality.

includes/REST/ProductsLogController.php (1)

32-32: Ensure proper sanitization and validation of the 'id' parameter.

Verification successful

The 'id' parameter is defined and used in the includes/REST/ProductsLogController.php file. It is associated with the absint sanitization callback, which is appropriate for integer values.

  • register_rest_route( $this->namespace, '/' . $this->base. '/(?P<id>[\d]+)', [
  • 'id' => [ 'description' => __( 'Unique identifier for the object.', 'wepos' ),
  • $args['counter_id'] = $request['id'];
  • $data['id'] = intval( $item['id'] );

These usages indicate that the 'id' parameter is properly sanitized and validated.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that the 'id' parameter is properly sanitized and validated.

# Test: Search for the sanitization and validation logic for 'id' parameter.
rg --type php $'sanitize_callback'

Length of output: 1698



Script:

#!/bin/bash
# Description: Verify the specific usage of the 'id' parameter in the context of the sanitize_callback.

# Test: Search for the definition and usage of the 'id' parameter in the ProductsLogController.php file.
rg --type php 'id' includes/REST/ProductsLogController.php

Length of output: 783

includes/ProductsLog.php (2)

283-290: Optimize bulk insert operations to improve performance.
[PERFORMANCE]
Consider using prepared statements for bulk inserts to enhance performance and security.


75-76: Ensure proper permission checks are in place to prevent unauthorized access.

Also applies to: 106-108, 306-307

Verification successful

The search results indicate that permission checks using current_user_can are present in the includes/ProductsLog.php file. Here are the relevant lines:

  • includes/ProductsLog.php: if ( ! current_user_can( 'manage_woocommerce' ) && ! current_user_can( 'cashier' ) )

Given these findings, it appears that proper permission checks are in place to prevent unauthorized access. Therefore, the concern mentioned in the review comment is addressed.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that permission checks are properly implemented.

# Test: Search for permission checks in the codebase.
rg --type php $'current_user_can'

Length of output: 3316

assets/src/frontend/components/Home.vue (3)

37-46: Add a tooltip to enhance user interaction with the refresh products feature.

The addition of a refresh products section with a tooltip provides a clear and user-friendly way to trigger product updates. This aligns with the PR's goal to improve product search and synchronization.


604-604: Ensure proper initialization of productLogsLoading and related states.

Initializing productLogsLoading to false ensures that the loading state is managed correctly, which is crucial for the new product refresh functionality.


1198-1206: Verify the creation of IndexedDB on component creation.

The proactive check and creation of the IndexedDB when the component is created is a good practice, ensuring that the database is ready for use as soon as the component is loaded.

Comment on lines +938 to +946
} else if ( this.isProductsStorageUpdateRequired() ) {
// Remove existing products from the IndexedDB storage.
wepos.productIndexedDb.deleteAllProducts();

// Insert products to the IndexedDB storage.
wepos.productIndexedDb.insertProducts( this.products );

// Store products IndexedDB updating time to Local Storage.
localStorage.setItem( 'productsStorageUpdatedOn', dayjs().unix() );
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optimize the handling of IndexedDB operations.

Consider abstracting IndexedDB operations into a separate method to improve code readability and maintainability.

- wepos.productIndexedDb.deleteAllProducts();
- wepos.productIndexedDb.insertProducts(this.products);
- localStorage.setItem('productsStorageUpdatedOn', dayjs().unix());
+ this.updateIndexedDBProducts();

And then define updateIndexedDBProducts method in the methods section.


Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
} else if ( this.isProductsStorageUpdateRequired() ) {
// Remove existing products from the IndexedDB storage.
wepos.productIndexedDb.deleteAllProducts();
// Insert products to the IndexedDB storage.
wepos.productIndexedDb.insertProducts( this.products );
// Store products IndexedDB updating time to Local Storage.
localStorage.setItem( 'productsStorageUpdatedOn', dayjs().unix() );
} else if ( this.isProductsStorageUpdateRequired() ) {
this.updateIndexedDBProducts();
```
And then define `updateIndexedDBProducts` method in the methods section:
```suggestion
methods: {
updateIndexedDBProducts() {
// Remove existing products from the IndexedDB storage.
wepos.productIndexedDb.deleteAllProducts();
// Insert products to the IndexedDB storage.
wepos.productIndexedDb.insertProducts(this.products);
// Store products IndexedDB updating time to Local Storage.
localStorage.setItem('productsStorageUpdatedOn', dayjs().unix());
},
// other methods...
}

* @return \WP_Error|\WP_HTTP_Response|\WP_REST_Response
*/
public function get_items( $request ) {
$args['counter_id'] = $request['id'];
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consistency in accessing request parameters: Use a consistent method to access request parameters to avoid confusion and potential bugs.

- $args['counter_id'] = $request['id'];
+ $args['counter_id'] = $request->get_param('id');

Also applies to: 135-135


Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
$args['counter_id'] = $request['id'];
$args['counter_id'] = $request->get_param('id');

Comment on lines +958 to +985
refreshProducts() {
this.fetchProductLogs( wepos.current_cashier.counter_id );
},
fetchProductLogs( counterId ) {
let fetchingToast = {
title: this.__( 'Products already updated!', 'wepos' ),
type: 'info'
}

this.productLogsLoading = true;

wepos.api.get( wepos.rest.root + wepos.rest.posversion + '/product/logs/' + counterId )
.done( ( response, status, xhr ) => {
if ( response.length > 0 ) {
wepos.productLogs.updateProductsToIndexedDb( response );
wepos.productLogs.updateProductLogsData( counterId );

fetchingToast.title = this.__( 'Products refreshed successfully!', 'wepos' );
fetchingToast.type = 'success';
}
} ).fail( ( response, status, xhr ) => {
fetchingToast.title = this.__( 'Failed to refresh products!', 'wepos' );
fetchingToast.type = 'error';
} ).then( ( response, status, xhr ) => {
this.productLogsLoading = false;

this.toast( fetchingToast );
} );
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improve error handling in the fetchProductLogs method.

Currently, the method does not handle potential API failures gracefully. Consider adding error handling to improve the robustness of the product log fetching process.

+ .fail( (error) => {
+     console.error('Failed to fetch product logs:', error);
+     this.productLogsLoading = false;
+ });

Committable suggestion was skipped due low confidence.


$request->set_query_params( [ 'context' => 'view' ] );

$product_logs = wepos()->products_log->get_product_logs( $args );
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tip

Codebase Verification

Consider handling potential exceptions or errors from external calls.

The search results indicate that there is some error handling implemented in the codebase (e.g., in includes/Admin/Settings.php). However, the specific lines in includes/REST/ProductsLogController.php (lines 97 and 139) do not show any error handling for the external call to wepos()->products_log->get_product_logs($args).

  • includes/REST/ProductsLogController.php (lines 97 and 139)
Analysis chain

Consider handling potential exceptions or errors from external calls.

Also applies to: 139-139

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that error handling is implemented for external calls.

# Test: Search for error handling patterns in the codebase.
rg --type php $'try {'

Length of output: 97



Script:

#!/bin/bash
# Description: Verify that error handling is implemented for external calls.

# Test: Search for error handling patterns in the codebase.
rg --type php 'try {'

Length of output: 97



Script:

#!/bin/bash
# Description: Verify that error handling is implemented for external calls.

# Test: Search for error handling patterns in the codebase.
rg --type php 'try \{'

Length of output: 66

Comment on lines +276 to +282
if ( null !== product.stock && 0 === Number( product.stock ) ) {
return false;
}

if ( 0 === Number( product.price ) ) {
return false;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactor the eligibility check to make it more concise and readable.

- if ( null !== product.stock && 0 === Number( product.stock ) ) {
-     return false;
- }
- if ( 0 === Number( product.price ) ) {
-     return false;
- }
- return true;
+ return product.stock !== null && Number(product.stock) > 0 && Number(product.price) > 0;

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
if ( null !== product.stock && 0 === Number( product.stock ) ) {
return false;
}
if ( 0 === Number( product.price ) ) {
return false;
}
return product.stock !== null && Number(product.stock) > 0 && Number(product.price) > 0;

Comment on lines +216 to +217
return new \WP_Error( 'failed-to-insert', __( 'Failed to insert product log data', 'wepos' ) );
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Standardize error handling across the class to improve maintainability.

Consider using a helper function to handle common error scenarios.

Also applies to: 255-256, 321-322, 409-410

Comment on lines +289 to +316
* Get cashier data by user id.
*
* @param int $user_id
*
* @return array|\WP_Error
*/
function wepos_get_cashier_data_by_user_id( $user_id = 0 ) {
global $wpdb;

$user_data = get_userdata( $user_id );

if ( empty( $user_data ) || empty( array_intersect( [ 'administrator', 'cashier' ], $user_data->roles ) ) ) {
return [];
}

$cashier_data = $wpdb->get_row(
$wpdb->prepare(
"SELECT user_id, outlet_id, counter_id, is_logged_in FROM {$wpdb->prefix}wepos_login WHERE user_id=%d AND `is_logged_in`='1'",
$user_id
)
);

if ( empty( $cashier_data ) ) {
return [];
}

return $cashier_data;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure proper error handling in wepos_get_cashier_data_by_user_id to return a more informative error message when the user data is not found or the user does not have the required roles.

-    if ( empty( $user_data ) || empty( array_intersect( [ 'administrator', 'cashier' ], $user_data->roles ) ) ) {
+    if ( empty( $user_data ) ) {
+        return new WP_Error('user_not_found', 'The user with the provided ID does not exist.');
+    }
+    if ( empty( array_intersect( [ 'administrator', 'cashier' ], $user_data->roles ) ) ) {
+        return new WP_Error('invalid_role', 'The user does not have the required roles.');
     }

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
* Get cashier data by user id.
*
* @param int $user_id
*
* @return array|\WP_Error
*/
function wepos_get_cashier_data_by_user_id( $user_id = 0 ) {
global $wpdb;
$user_data = get_userdata( $user_id );
if ( empty( $user_data ) || empty( array_intersect( [ 'administrator', 'cashier' ], $user_data->roles ) ) ) {
return [];
}
$cashier_data = $wpdb->get_row(
$wpdb->prepare(
"SELECT user_id, outlet_id, counter_id, is_logged_in FROM {$wpdb->prefix}wepos_login WHERE user_id=%d AND `is_logged_in`='1'",
$user_id
)
);
if ( empty( $cashier_data ) ) {
return [];
}
return $cashier_data;
}
* Get cashier data by user id.
*
* @param int $user_id
*
* @return array|\WP_Error
*/
function wepos_get_cashier_data_by_user_id( $user_id = 0 ) {
global $wpdb;
$user_data = get_userdata( $user_id );
if ( empty( $user_data ) ) {
return new WP_Error('user_not_found', 'The user with the provided ID does not exist.');
}
if ( empty( array_intersect( [ 'administrator', 'cashier' ], $user_data->roles ) ) ) {
return new WP_Error('invalid_role', 'The user does not have the required roles.');
}
$cashier_data = $wpdb->get_row(
$wpdb->prepare(
"SELECT user_id, outlet_id, counter_id, is_logged_in FROM {$wpdb->prefix}wepos_login WHERE user_id=%d AND `is_logged_in`='1'",
$user_id
)
);
if ( empty( $cashier_data ) ) {
return [];
}
return $cashier_data;
}

Comment on lines +3 to +12
productLogs.forEach( productLog => {
wepos.productIndexedDb.updateProduct( {
id: Number( productLog.product_id ),
title: productLog.product_title,
type: productLog.product_type,
sku: productLog.product_sku,
price: productLog.product_price,
stock: productLog.product_stock,
} );
} );
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using for...of instead of forEach for better performance and readability.

- productLogs.forEach( productLog => {
+ for (const productLog of productLogs) {
  wepos.productIndexedDb.updateProduct({
    id: Number(productLog.product_id),
    title: productLog.product_title,
    type: productLog.product_type,
    sku: productLog.product_sku,
    price: productLog.product_price,
    stock: productLog.product_stock,
  });
- });
+ }

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
productLogs.forEach( productLog => {
wepos.productIndexedDb.updateProduct( {
id: Number( productLog.product_id ),
title: productLog.product_title,
type: productLog.product_type,
sku: productLog.product_sku,
price: productLog.product_price,
stock: productLog.product_stock,
} );
} );
for (const productLog of productLogs) {
wepos.productIndexedDb.updateProduct( {
id: Number( productLog.product_id ),
title: productLog.product_title,
type: productLog.product_type,
sku: productLog.product_sku,
price: productLog.product_price,
stock: productLog.product_stock,
} );
}

Comment on lines +16 to +19
wepos.api.put( wepos.rest.root + wepos.rest.posversion + '/product/logs/' + counterId )
.fail( response => {
alert( response.status + ' : ' + response.responseJSON.message );
} );
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace string concatenation with template literals and consider using a more user-friendly alert system like SweetAlert.

- wepos.api.put(wepos.rest.root + wepos.rest.posversion + '/product/logs/' + counterId)
+ wepos.api.put(`${wepos.rest.root}${wepos.rest.posversion}/product/logs/${counterId}`)
.fail(response => {
- alert(response.status + ' : ' + response.responseJSON.message);
+ Swal.fire({
+   icon: 'error',
+   title: 'Oops...',
+   text: `${response.status} : ${response.responseJSON.message}`,
+ });
});

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
wepos.api.put( wepos.rest.root + wepos.rest.posversion + '/product/logs/' + counterId )
.fail( response => {
alert( response.status + ' : ' + response.responseJSON.message );
} );
wepos.api.put(`${wepos.rest.root}${wepos.rest.posversion}/product/logs/${counterId}`)
.fail(response => {
Swal.fire({
icon: 'error',
title: 'Oops...',
text: `${response.status} : ${response.responseJSON.message}`,
});
});

Comment on lines +21 to +59
function wepos_create_product_log_tables() {
global $wpdb;

include_once ABSPATH . 'wp-admin/includes/upgrade.php';

$collate = $wpdb->get_charset_collate();
$tables = [
"CREATE TABLE IF NOT EXISTS `{$wpdb->prefix}wepos_product_logs` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`product_id` bigint unsigned NOT NULL,
`product_title` text(255) NOT NULL,
`product_type` varchar(100) NOT NULL,
`product_sku` varchar(100) NULL,
`product_price` decimal (19,
4) NOT NULL DEFAULT 0.0000,
`product_stock` bigint signed NULL,
`counter_counts` bigint unsigned NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 {$collate};",

"CREATE TABLE IF NOT EXISTS `{$wpdb->prefix}wepos_product_log_counters` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`product_log_id` bigint unsigned NOT NULL,
`counter_id` bigint unsigned NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (product_log_id) REFERENCES {$wpdb->prefix}wepos_product_logs (id) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1 {$collate};",
];

// Include 'upgrade.php' if not included earlier.
if ( ! function_exists( 'dbDelta' ) ) {
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
}

// Create tables through looping.
foreach ( $tables as $key => $table ) {
dbDelta( $table );
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider optimizing the data types and table creation logic.

- "CREATE TABLE IF NOT EXISTS `{$wpdb->prefix}wepos_product_logs` (
+ "CREATE TABLE `{$wpdb->prefix}wepos_product_logs` (
- `product_title` text(255) NOT NULL,
+ `product_title` varchar(255) NOT NULL,
- `product_type` varchar(100) NOT NULL,
+ `product_type` varchar(50) NOT NULL,
- `product_sku` varchar(100) NULL,
+ `product_sku` varchar(50) NULL,
- `product_price` decimal (19, 4) NOT NULL DEFAULT 0.0000,
+ `product_price` decimal(10, 2) NOT NULL DEFAULT 0.0000,
- `product_stock` bigint signed NULL,
+ `product_stock` int signed NULL,
- `counter_counts` bigint unsigned NULL DEFAULT 0,
+ `counter_counts` int unsigned NULL DEFAULT 0,
- FOREIGN KEY (product_log_id) REFERENCES {$wpdb->prefix}wepos_product_logs (id) ON DELETE CASCADE
+ FOREIGN KEY (product_log_id) REFERENCES {$wpdb->prefix}wepos_product_logs (id) ON DELETE CASCADE ON UPDATE CASCADE
);

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
function wepos_create_product_log_tables() {
global $wpdb;
include_once ABSPATH . 'wp-admin/includes/upgrade.php';
$collate = $wpdb->get_charset_collate();
$tables = [
"CREATE TABLE IF NOT EXISTS `{$wpdb->prefix}wepos_product_logs` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`product_id` bigint unsigned NOT NULL,
`product_title` text(255) NOT NULL,
`product_type` varchar(100) NOT NULL,
`product_sku` varchar(100) NULL,
`product_price` decimal (19,
4) NOT NULL DEFAULT 0.0000,
`product_stock` bigint signed NULL,
`counter_counts` bigint unsigned NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 {$collate};",
"CREATE TABLE IF NOT EXISTS `{$wpdb->prefix}wepos_product_log_counters` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`product_log_id` bigint unsigned NOT NULL,
`counter_id` bigint unsigned NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (product_log_id) REFERENCES {$wpdb->prefix}wepos_product_logs (id) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1 {$collate};",
];
// Include 'upgrade.php' if not included earlier.
if ( ! function_exists( 'dbDelta' ) ) {
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
}
// Create tables through looping.
foreach ( $tables as $key => $table ) {
dbDelta( $table );
}
}
function wepos_create_product_log_tables() {
global $wpdb;
include_once ABSPATH . 'wp-admin/includes/upgrade.php';
$collate = $wpdb->get_charset_collate();
$tables = [
"CREATE TABLE `{$wpdb->prefix}wepos_product_logs` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`product_id` bigint unsigned NOT NULL,
`product_title` varchar(255) NOT NULL,
`product_type` varchar(50) NOT NULL,
`product_sku` varchar(50) NULL,
`product_price` decimal(10, 2) NOT NULL DEFAULT 0.0000,
`product_stock` int signed NULL,
`counter_counts` int unsigned NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 {$collate};",
"CREATE TABLE IF NOT EXISTS `{$wpdb->prefix}wepos_product_log_counters` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`product_log_id` bigint unsigned NOT NULL,
`counter_id` bigint unsigned NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (product_log_id) REFERENCES {$wpdb->prefix}wepos_product_logs (id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1 {$collate};",
];
// Include 'upgrade.php' if not included earlier.
if ( ! function_exists( 'dbDelta' ) ) {
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
}
// Create tables through looping.
foreach ( $tables as $key => $table ) {
dbDelta( $table );
}
}

@shashwatahalder01
Copy link
Contributor

Scenario:
The search doesn't work. Also, nothing is stored in indexedDB.

Steps to reproduce:

  1. search for a product on the pos dashboard

Screenshot:
Screenshot at May 30 09-15-31
Screenshot at May 30 09-16-02

@shashwatahalder01

This comment was marked as resolved.

@shashwatahalder01 shashwatahalder01 added Bug Something isn't working and removed QA In Progress labels May 30, 2024
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 19bb681 and 397a5c9.

Files selected for processing (1)
  • assets/src/utils/productIndexedDb.js (1 hunks)
Additional context used
Biome
assets/src/utils/productIndexedDb.js

[error] 85-94: Prefer for...of instead of forEach.


[error] 153-153: This let declares a variable that is only assigned once.


[error] 156-156: This let declares a variable that is only assigned once.


[error] 158-158: This let declares a variable that is only assigned once.

const transaction = db.transaction( ["ProductsDB"], "readwrite" );
const objectStore = transaction.objectStore( "ProductsDB" );

let condition = new RegExp( searchKeyword, 'i' );
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactor to use const for variables that are assigned only once.

- let condition = new RegExp( searchKeyword, 'i' );
- let products = event.target.result;
- let result = products.filter( ( product ) => {
+ const condition = new RegExp( searchKeyword, 'i' );
+ const products = event.target.result;
+ const result = products.filter( ( product ) => {

Also applies to: 156-156, 158-158

Committable suggestion was skipped due low confidence.

Tools
Biome

[error] 153-153: This let declares a variable that is only assigned once.

@shashwatahalder01
Copy link
Contributor

Scenario:
DB error while building the PR

DB Error:

[03-Jun-2024 06:55:45 UTC] WordPress database error Table 'wepos.pos_wepos_login' doesn't exist for query SELECT user_id, outlet_id, counter_id, is_logged_in FROM pos_wepos_login WHERE user_id=1 AND `is_logged_in`='1' made by require('wp-admin/plugins.php'), require_once('wp-admin/admin.php'), require_once('wp-load.php'), require_once('wp-config.php'), require_once('wp-settings.php'), do_action('init'), WP_Hook->do_action, WP_Hook->apply_filters, WePOS->init_classes, WeDevs\WePOS\ProductsLog->__construct, wepos_get_cashier_data_by_user_id
[03-Jun-2024 06:55:45 UTC] WordPress database error Table 'wepos.pos_wepos_login' doesn't exist for query SELECT user_id, outlet_id, counter_id, is_logged_in FROM pos_wepos_login WHERE user_id=1 AND `is_logged_in`='1' made by require('wp-admin/plugins.php'), require_once('wp-admin/admin-header.php'), do_action('admin_enqueue_scripts'), WP_Hook->do_action, WP_Hook->apply_filters, WeDevs\WePOS\Assets->register, WeDevs\WePOS\Assets->register_localize, wepos_get_cashier_data_by_user_id
[03-Jun-2024 06:55:45 UTC] WordPress database error Table 'wepos.pos_wepos_login' doesn't exist for query SELECT user_id, outlet_id, counter_id, is_logged_in FROM pos_wepos_login WHERE user_id=1 AND `is_logged_in`='1' made by require('wp-admin/admin-ajax.php'), require_once('wp-load.php'), require_once('wp-config.php'), require_once('wp-settings.php'), do_action('init'), WP_Hook->do_action, WP_Hook->apply_filters, WePOS->init_classes, WeDevs\WePOS\ProductsLog->__construct, wepos_get_cashier_data_by_user_id
[03-Jun-2024 06:55:47 UTC] WordPress database error Table 'wepos.pos_wepos_login' doesn't exist for query SELECT user_id, outlet_id, counter_id, is_logged_in FROM pos_wepos_login WHERE user_id=1 AND `is_logged_in`='1' made by require('index.php'), require('wp-blog-header.php'), require_once('wp-load.php'), require_once('wp-config.php'), require_once('wp-settings.php'), do_action('init'), WP_Hook->do_action, WP_Hook->apply_filters, WePOS->init_classes, WeDevs\WePOS\ProductsLog->__construct, wepos_get_cashier_data_by_user_id
[03-Jun-2024 06:55:47 UTC] WordPress database error Table 'wepos.pos_wepos_login' doesn't exist for query SELECT user_id, outlet_id, counter_id, is_logged_in FROM pos_wepos_login WHERE user_id=1 AND `is_logged_in`='1' made by require('index.php'), require('wp-blog-header.php'), require_once('wp-load.php'), require_once('wp-config.php'), require_once('wp-settings.php'), do_action('init'), WP_Hook->do_action, WP_Hook->apply_filters, WePOS->init_classes, WeDevs\WePOS\ProductsLog->__construct, wepos_get_cashier_data_by_user_id

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Outside diff range and nitpick comments (3)
includes/Admin/Updates.php (1)

Line range hint 1-138: Consider enhancing error handling and logging.

The overall structure of the Updates class is solid and follows good practices. However, consider the following improvements:

  1. Add error handling in the perform_updates method, especially when including upgrade files.
  2. Implement logging for successful updates and any errors encountered during the update process.

These enhancements would improve the robustness of the update process and make debugging easier in case of issues.

Here's a suggested modification to the perform_updates method:

public function perform_updates() {
    if ( ! $this->is_needs_update() ) {
        return;
    }

    $installed_version = get_option( 'we_pos_version' );

    foreach ( self::$updates as $version => $path ) {
        if ( version_compare( $installed_version, $version, '<' ) ) {
            $full_path = WEPOS_INCLUDES . '/admin/updates/' . $path;
            if ( file_exists( $full_path ) ) {
                try {
                    include $full_path;
                    update_option( 'we_pos_version', $version );
                    error_log( "WePOS: Successfully updated to version $version" );
                } catch ( Exception $e ) {
                    error_log( "WePOS: Error updating to version $version: " . $e->getMessage() );
                    // Consider how to handle failed updates. Maybe break the loop or continue?
                }
            } else {
                error_log( "WePOS: Update file not found for version $version" );
            }
        }
    }

    update_option( 'we_pos_version', WEPOS_VERSION );

    $url = wp_unslash( add_query_arg( [ 'page' => 'wepos' ], admin_url( 'admin.php' ) ) );
    $location = esc_url( $url ) . '#/settings';
    wp_redirect( $location );
    exit();
}
includes/Installer.php (1)

34-34: Ensure consistency in @since version tags

The docblocks use both @since WEPOS_SINCE and @since WEPOS_LITE_SINCE. Please ensure that the correct version constant is used consistently throughout the codebase to avoid confusion.

Also applies to: 85-85

assets/src/frontend/components/Home.vue (1)

39-41: Improve accessibility by adding aria-label to the refresh button

To enhance accessibility for users relying on screen readers, consider adding an aria-label attribute to the refresh button to clearly describe its purpose.

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 397a5c9 and 3e76019.

Files selected for processing (6)
  • assets/src/frontend/components/Home.vue (8 hunks)
  • includes/Admin/Updates.php (2 hunks)
  • includes/Installer.php (3 hunks)
  • includes/REST/Manager.php (2 hunks)
  • includes/functions.php (1 hunks)
  • wepos.php (2 hunks)
Additional comments not posted (12)
includes/Admin/Updates.php (4)

2-2: LGTM: Namespace declaration added.

The addition of the namespace WeDevs\WePOS\Admin is a good practice. It correctly reflects the file's location in the project structure and helps in organizing the code.


5-5: LGTM: ABSPATH check added for security.

The addition of the ABSPATH check is a good security practice. It prevents direct access to the file, which is important for maintaining the integrity of the plugin.


Line range hint 1-138: Summary: Good improvements, with suggestions for further enhancements.

The changes made to this file are minimal but significant:

  1. Addition of namespace improves code organization.
  2. ABSPATH check enhances security.
  3. New update entry (1.3.0) indicates ongoing development.

The overall structure and functionality of the Updates class remain solid. However, consider the suggested improvements for error handling and logging to further enhance the robustness of the update process.

Overall, these changes contribute positively to the maintainability and security of the plugin.


23-23: Verify the upgrade file and version number.

The addition of the new entry for version 1.3.0 is good. However, please ensure that:

  1. The file 'upgrade-1.3.0.php' exists in the correct directory.
  2. The version number 1.3.0 aligns with the intended release version for this PR.

Run the following script to verify the upgrade file's existence:

Verification successful

Upgrade File and Version Number Verified Successfully.

The file upgrade-1.3.0.php exists in the includes/Admin/Updates/ directory and contains the appropriate update functions for version 1.3.0. The version number aligns with the intended release for this PR.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the existence of the upgrade file and check its content

# Test 1: Check if the upgrade file exists
if fd -t f "upgrade-1.3.0.php" --exec echo "Upgrade file found: {}"; then
    echo "Upgrade file exists."
else
    echo "Error: Upgrade file 'upgrade-1.3.0.php' not found."
fi

# Test 2: Check the content of the upgrade file
fd -t f "upgrade-1.3.0.php" --exec cat {}

Length of output: 2029

includes/REST/Manager.php (4)

2-5: LGTM: Improved code organization and security.

The addition of the namespace declaration and the ABSPATH check enhances code organization and security. These are good practices in WordPress plugin development.


Line range hint 1-214: Suggestion: Ensure compatibility with IndexedDB integration.

While the changes in this file are minimal, it's crucial to verify that the existing methods, particularly product_response, tax_response, category_response, and validate_item_stock_before_order, are fully compatible with the new IndexedDB integration mentioned in the PR objectives.

Please conduct thorough testing to ensure these methods work correctly with the IndexedDB integration, especially for large product lists. Consider the following test scenarios:

  1. Test product_response with a large number of products (e.g., 3000+) to ensure it handles the load efficiently.
  2. Verify that tax_response and category_response work correctly with products stored in IndexedDB.
  3. Test validate_item_stock_before_order with products fetched from IndexedDB to ensure stock validation remains accurate.

If possible, provide test results or update test cases to cover these scenarios.


Line range hint 1-214: Summary: Changes support PR objectives, but further verification needed.

The changes to this file, particularly the addition of the ProductsLogController, align well with the PR objectives of improving product management. However, given the scope of the changes described in the PR (IndexedDB integration and improved frontend product search), there are a few key points that require attention:

  1. Ensure that the newly added ProductsLogController is fully implemented and tested.
  2. Verify that all existing methods in this file, especially those dealing with product responses and stock validation, are compatible with the new IndexedDB integration.
  3. Consider adding or updating unit tests to cover the interaction between these REST endpoints and the IndexedDB storage.

These verifications are crucial to ensure that the enhancements don't introduce any regressions in the existing functionality while supporting the new features.


28-34: LGTM: Added ProductsLogController to class map.

The addition of the ProductsLogController to the class map is consistent with existing entries and aligns with the PR objective of improving product management.

Please ensure that the ProductsLogController.php file exists and is correctly implemented. Run the following script to verify:

Verification successful

Verified: ProductsLogController has been successfully added.

The ProductsLogController has been correctly added to the class map, and the corresponding file exists with the expected structure and necessary methods.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify the existence and basic structure of ProductsLogController.php

# Check if the file exists
if [ ! -f "includes/REST/ProductsLogController.php" ]; then
    echo "Error: ProductsLogController.php does not exist in the expected location."
    exit 1
fi

# Check for the class definition
if ! grep -q "class ProductsLogController" "includes/REST/ProductsLogController.php"; then
    echo "Error: ProductsLogController class is not defined in the file."
    exit 1
fi

# Check for the register_routes method
if ! grep -q "public function register_routes()" "includes/REST/ProductsLogController.php"; then
    echo "Warning: register_routes() method is not found in ProductsLogController. Ensure it's implemented."
fi

echo "ProductsLogController.php exists and contains basic expected structure."

Length of output: 375

wepos.php (4)

Line range hint 1-465: Overall approval with suggestions for verification

The changes in this file appear to be part of a larger update to the wePOS plugin. The main modifications include:

  1. Refactoring the deactivation process to use a dedicated Uninstaller class.
  2. Adding new ProductsLog and Ajax functionalities.
  3. Updating the plugin version to 1.2.8.

These changes seem to improve the code structure and add new features. However, to ensure the quality and stability of the update, please:

  1. Thoroughly test the new Uninstaller class to ensure it correctly handles all deactivation tasks.
  2. Provide documentation for the new ProductsLog and Ajax functionalities.
  3. Ensure comprehensive testing of the new features and their integration with existing code.
  4. Verify version number consistency across all plugin files and update the changelog accordingly.

286-288: Approve change, but verify Uninstaller implementation

The refactoring of the deactivate method to use a dedicated Uninstaller class is a good practice for better code organization. However, we need to ensure that the Uninstaller class properly handles all deactivation tasks, including those previously done in this method (such as removing user capabilities).

Please run the following script to verify the Uninstaller class implementation:

#!/bin/bash
# Description: Verify the Uninstaller class implementation

# Test: Check if the Uninstaller class exists and has a run method
ast-grep --lang php --pattern 'class Uninstaller {
  $$$
  public function run() {
    $$$
  }
  $$$
}'

# Test: Check if the Uninstaller class handles user capability removal
rg --type php 'remove_cap|remove_role|delete_option.*user_roles' -A 5 -B 5

Line range hint 18-18: Approve version update, verify consistency and documentation

The update of the plugin version to 1.2.8 is noted and approved. To ensure a smooth release:

  1. Please verify that this version number is consistent across all relevant files (e.g., README, changelog).
  2. Ensure that all changes for this version are properly documented in the changelog.
  3. Confirm that all new features and changes have been thoroughly tested.

Please run the following script to verify version consistency and documentation:

#!/bin/bash
# Description: Verify version consistency and documentation

# Test: Check for version number in other files
rg --type-add 'text:*.{md,txt}' --type php --type text '1\.2\.8'

# Test: Look for a changelog entry for this version
rg --type-add 'text:*.{md,txt}' --type text '## \[1\.2\.8\]' -A 10

# Test: Check for any TODO or FIXME comments that might need addressing before release
rg --type php 'TODO|FIXME'

335-336: Approve additions, but request more information

The addition of 'products_log' and 'ajax' classes to the container is approved. These additions likely enhance the functionality of the POS system. However, to ensure they are properly integrated:

  1. Could you provide more information about the purpose and implementation of these new classes?
  2. Have these additions been thoroughly tested to ensure they don't introduce any performance issues or conflicts with existing functionality?

Please run the following script to verify the implementation of these new classes:

Comment on lines +288 to +316
/**
* Get cashier data by user id.
*
* @param int $user_id
*
* @return array|\WP_Error
*/
function wepos_get_cashier_data_by_user_id( $user_id = 0 ) {
global $wpdb;

$user_data = get_userdata( $user_id );

if ( empty( $user_data ) || empty( array_intersect( [ 'administrator', 'cashier' ], $user_data->roles ) ) ) {
return [];
}

$cashier_data = $wpdb->get_row(
$wpdb->prepare(
"SELECT user_id, outlet_id, counter_id, is_logged_in FROM {$wpdb->prefix}wepos_login WHERE user_id=%d AND `is_logged_in`='1'",
$user_id
)
);

if ( empty( $cashier_data ) ) {
return [];
}

return $cashier_data;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Improve error handling and add database error checks

The function looks good overall, especially with the use of prepared statements for the database query. However, there are a few improvements we can make:

  1. Implement more informative error handling as suggested in the past review.
  2. Add checks for potential database errors and table existence.

Here's a suggested implementation with improved error handling and database checks:

 function wepos_get_cashier_data_by_user_id( $user_id = 0 ) {
     global $wpdb;
 
     $user_data = get_userdata( $user_id );
 
-    if ( empty( $user_data ) || empty( array_intersect( [ 'administrator', 'cashier' ], $user_data->roles ) ) ) {
-        return [];
+    if ( empty( $user_data ) ) {
+        return new WP_Error('user_not_found', 'The user with the provided ID does not exist.');
+    }
+    if ( empty( array_intersect( [ 'administrator', 'cashier' ], $user_data->roles ) ) ) {
+        return new WP_Error('invalid_role', 'The user does not have the required roles.');
     }
 
+    // Check if the table exists
+    $table_name = $wpdb->prefix . 'wepos_login';
+    if($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name) {
+        return new WP_Error('table_not_found', 'The required database table does not exist.');
+    }
+
     $cashier_data = $wpdb->get_row(
         $wpdb->prepare(
-            "SELECT user_id, outlet_id, counter_id, is_logged_in FROM {$wpdb->prefix}wepos_login WHERE user_id=%d AND `is_logged_in`='1'",
+            "SELECT user_id, outlet_id, counter_id, is_logged_in FROM $table_name WHERE user_id=%d AND `is_logged_in`='1'",
             $user_id
         )
     );
 
+    if ( $wpdb->last_error ) {
+        return new WP_Error('database_error', 'A database error occurred: ' . $wpdb->last_error);
+    }
+
     if ( empty( $cashier_data ) ) {
-        return [];
+        return new WP_Error('no_active_session', 'No active cashier session found for this user.');
     }
 
     return $cashier_data;
 }

These changes will provide more informative error messages and handle potential database issues.

The overall structure of the function and the use of prepared statements for the database query are good practices. These improvements will make the function more robust and easier to debug.

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* Get cashier data by user id.
*
* @param int $user_id
*
* @return array|\WP_Error
*/
function wepos_get_cashier_data_by_user_id( $user_id = 0 ) {
global $wpdb;
$user_data = get_userdata( $user_id );
if ( empty( $user_data ) || empty( array_intersect( [ 'administrator', 'cashier' ], $user_data->roles ) ) ) {
return [];
}
$cashier_data = $wpdb->get_row(
$wpdb->prepare(
"SELECT user_id, outlet_id, counter_id, is_logged_in FROM {$wpdb->prefix}wepos_login WHERE user_id=%d AND `is_logged_in`='1'",
$user_id
)
);
if ( empty( $cashier_data ) ) {
return [];
}
return $cashier_data;
}
/**
* Get cashier data by user id.
*
* @param int $user_id
*
* @return array|\WP_Error
*/
function wepos_get_cashier_data_by_user_id( $user_id = 0 ) {
global $wpdb;
$user_data = get_userdata( $user_id );
if ( empty( $user_data ) ) {
return new WP_Error('user_not_found', 'The user with the provided ID does not exist.');
}
if ( empty( array_intersect( [ 'administrator', 'cashier' ], $user_data->roles ) ) ) {
return new WP_Error('invalid_role', 'The user does not have the required roles.');
}
// Check if the table exists
$table_name = $wpdb->prefix . 'wepos_login';
if($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name) {
return new WP_Error('table_not_found', 'The required database table does not exist.');
}
$cashier_data = $wpdb->get_row(
$wpdb->prepare(
"SELECT user_id, outlet_id, counter_id, is_logged_in FROM $table_name WHERE user_id=%d AND `is_logged_in`='1'",
$user_id
)
);
if ( $wpdb->last_error ) {
return new WP_Error('database_error', 'A database error occurred: ' . $wpdb->last_error);
}
if ( empty( $cashier_data ) ) {
return new WP_Error('no_active_session', 'No active cashier session found for this user.');
}
return $cashier_data;
}

Comment on lines +89 to +121
public function create_tables() {
global $wpdb;

include_once ABSPATH . 'wp-admin/includes/upgrade.php';

$collate = $wpdb->get_charset_collate();
$tables = [
"CREATE TABLE IF NOT EXISTS `{$wpdb->prefix}wepos_product_logs` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`product_id` bigint unsigned NOT NULL,
`product_title` text(255) NOT NULL,
`product_type` varchar(100) NOT NULL,
`product_sku` varchar(100) NULL,
`product_price` decimal (19,
4) NOT NULL DEFAULT 0.0000,
`product_stock` bigint signed NULL,
`counter_counts` bigint unsigned NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 {$collate};",

"CREATE TABLE IF NOT EXISTS `{$wpdb->prefix}wepos_product_log_counters` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`product_log_id` bigint unsigned NOT NULL,
`counter_id` bigint unsigned NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (product_log_id) REFERENCES {$wpdb->prefix}wepos_product_logs (id) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1 {$collate};",
];

foreach ( $tables as $key => $table ) {
dbDelta( $table );
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for dbDelta execution

It is advisable to handle and log any errors that may occur during the execution of dbDelta when creating tables. This will aid in debugging and ensure that any issues during table creation are promptly identified.

Comment on lines +102 to +103
`product_price` decimal (19,
4) NOT NULL DEFAULT 0.0000,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Correct the syntax for the DECIMAL data type of product_price

The DECIMAL data type definition should not include a line break between the precision and scale. It should be defined as DECIMAL(19, 4) without any interruption.

Apply this diff to fix the syntax:

- `product_price` decimal (19,
- 4) NOT NULL DEFAULT 0.0000,
+ `product_price` decimal(19, 4) NOT NULL DEFAULT 0.0000,
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
`product_price` decimal (19,
4) NOT NULL DEFAULT 0.0000,
`product_price` decimal(19, 4) NOT NULL DEFAULT 0.0000,

"CREATE TABLE IF NOT EXISTS `{$wpdb->prefix}wepos_product_logs` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`product_id` bigint unsigned NOT NULL,
`product_title` text(255) NOT NULL,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove length specification from 'TEXT' data type for product_title

The TEXT data type in MySQL does not accept a length parameter. Specifying text(255) is invalid syntax. You should remove the length specification.

Apply this diff to correct the syntax:

- `product_title` text(255) NOT NULL,
+ `product_title` text NOT NULL,
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
`product_title` text(255) NOT NULL,
`product_title` text NOT NULL,

Comment on lines +113 to +114
PRIMARY KEY (`id`),
FOREIGN KEY (product_log_id) REFERENCES {$wpdb->prefix}wepos_product_logs (id) ON DELETE CASCADE
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure proper syntax for the FOREIGN KEY constraint

To maintain correct SQL syntax, remove the comma after the PRIMARY KEY definition since the FOREIGN KEY constraint follows it as the last item in the table definition.

Apply this diff to correct the syntax:

-     PRIMARY KEY (`id`),
+     PRIMARY KEY (`id`)
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
PRIMARY KEY (`id`),
FOREIGN KEY (product_log_id) REFERENCES {$wpdb->prefix}wepos_product_logs (id) ON DELETE CASCADE
PRIMARY KEY (`id`)
FOREIGN KEY (product_log_id) REFERENCES {$wpdb->prefix}wepos_product_logs (id) ON DELETE CASCADE

Comment on lines +1060 to +1062
if ( ! this.productsStorageUpdatedOn || this.productsStorageUpdatedOn < dayjs().subtract( 7, 'days' ).unix() ) {
return true;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure correct comparison by parsing productsStorageUpdatedOn to a number

The value of productsStorageUpdatedOn retrieved from localStorage is a string. Comparing it directly to a number may lead to incorrect results due to type coercion. To ensure proper comparison, parse productsStorageUpdatedOn to an integer before the comparison.

Apply this diff to fix the comparison:

isProductsStorageUpdateRequired() {
    if ( ! this.productsStorageUpdatedOn || 
-         this.productsStorageUpdatedOn < dayjs().subtract( 7, 'days' ).unix() ) {
+         parseInt(this.productsStorageUpdatedOn, 10) < dayjs().subtract( 7, 'days' ).unix() ) {
        return true;
    }
    return false;
}
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ( ! this.productsStorageUpdatedOn || this.productsStorageUpdatedOn < dayjs().subtract( 7, 'days' ).unix() ) {
return true;
}
if ( ! this.productsStorageUpdatedOn ||
parseInt(this.productsStorageUpdatedOn, 10) < dayjs().subtract( 7, 'days' ).unix() ) {
return true;
}

Comment on lines +1306 to +1312
async beforeCreate() {
const dbName = 'ProductsDB';
const isExistsProductsDB = ( await window.indexedDB.databases() ).map( db => db.name ).includes( dbName );

if ( ! isExistsProductsDB ) {
wepos.productIndexedDb.createProductsDB();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure browser compatibility when using indexedDB.databases()

The indexedDB.databases() method is experimental and not supported in all browsers, such as Safari and Internet Explorer. Relying on it may cause runtime errors in unsupported browsers. Consider checking for its availability before using it or providing a fallback mechanism. Alternatively, attempt to open the database and handle exceptions if it doesn't exist.

Apply this diff to handle browser compatibility:

async beforeCreate() {
+   if ('databases' in indexedDB) {
        const dbName             = 'ProductsDB';
        const isExistsProductsDB = ( await window.indexedDB.databases() ).map( db => db.name ).includes( dbName );
        if ( ! isExistsProductsDB ) {
            wepos.productIndexedDb.createProductsDB();
        }
+   } else {
+       // Fallback for browsers without indexedDB.databases()
+       const request = indexedDB.open('ProductsDB');
+       request.onupgradeneeded = (event) => {
+           wepos.productIndexedDb.createProductsDB();
+       };
+       request.onsuccess = (event) => {
+           // Database exists
+       };
+       request.onerror = (event) => {
+           // Handle errors
+       };
+   }
}
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async beforeCreate() {
const dbName = 'ProductsDB';
const isExistsProductsDB = ( await window.indexedDB.databases() ).map( db => db.name ).includes( dbName );
if ( ! isExistsProductsDB ) {
wepos.productIndexedDb.createProductsDB();
}
async beforeCreate() {
if ('databases' in indexedDB) {
const dbName = 'ProductsDB';
const isExistsProductsDB = ( await window.indexedDB.databases() ).map( db => db.name ).includes( dbName );
if ( ! isExistsProductsDB ) {
wepos.productIndexedDb.createProductsDB();
}
} else {
// Fallback for browsers without indexedDB.databases()
const request = indexedDB.open('ProductsDB');
request.onupgradeneeded = (event) => {
wepos.productIndexedDb.createProductsDB();
};
request.onsuccess = (event) => {
// Database exists
};
request.onerror = (event) => {
// Handle errors
};
}
}

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Outside diff range and nitpick comments (3)
assets/src/utils/productIndexedDb.js (3)

7-8: Enhance error messages for better debugging.

Currently, the error messages in request.onerror are generic. Including the error code or message can aid in debugging.

Example:

-7     console.error( "An error occurred with IndexedDB" );
-8     console.error( event );
+7     console.error( `IndexedDB Error: ${event.target.errorCode}` );

Also applies to: 31-32, 66-67, 205-206, 233-234


1-255: Ensure consistent return types across all methods.

Some methods (getAllProducts, getProductsBySearchKeyword, updateProduct) return Promises, while others (insertProduct, insertProducts, deleteProductById, deleteAllProducts, createProductsDB) do not. For consistency and better asynchronous handling, consider returning Promises from all methods.

This allows callers to use async/await or .then() uniformly across all database operations.


42-44: Review the necessity of both logging and rejecting errors.

In your error handlers, you log the error to the console and then reject with the same error message. This might be redundant. Decide whether both actions are necessary or if one suffices.

Also applies to: 77-79, 114-117, 146-147, 184-187, 216-218, 244-246

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 3e76019 and 8e9d7c6.

Files selected for processing (1)
  • assets/src/utils/productIndexedDb.js (1 hunks)
Additional comments not posted (1)
assets/src/utils/productIndexedDb.js (1)

193-195: Confirm 'updateProduct' properly resolves the Promise.

In the updateProduct method, ensure that the Promise is resolved upon successful completion of the put operation.

Comment on lines +6 to +11
request.onerror = ( event ) => {
console.error( "An error occurred with IndexedDB" );
console.error( event );

reject( event );
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Error: 'reject' is not defined in these functions.

In the methods createProductsDB, insertProduct, insertProducts, deleteProductById, and deleteAllProducts, you are calling reject(event); within the request.onerror handler. However, reject is not defined in these functions since they are not returning Promises. This will cause a ReferenceError at runtime.

To fix this issue, you have two options:

  1. Remove the reject(event); calls if you do not intend to use Promises.
  2. Refactor the functions to return Promises, allowing you to use reject.

Option 1: Remove reject(event); calls

6     request.onerror = ( event ) => {
7         console.error( "An error occurred with IndexedDB" );
8         console.error( event );
9 
-10        reject( event );
11    };

Apply similar changes to the other functions where reject(event); is called without a defined reject.

Option 2: Refactor functions to return Promises

Example for insertProduct:

26     insertProduct( product = {} ) {
+27        return new Promise( ( resolve, reject ) => {
28            let db;
29            const request = indexedDB.open( "ProductsDB", 1 );

30            request.onerror = ( event ) => {
31                console.error( "An error occurred with IndexedDB" );
32                console.error( event );

33                reject( event );
34            };
35
36            request.onsuccess = ( event ) => {
37                db = event.target.result;
38
39                db.onerror = ( event ) => {
40                    console.error( `wePOS IndexedDB Database error: \
${event.target.error.code} - ${event.target.error.message}` );
41
42                    reject( `wePOS IndexedDB Database error: \
${event.target.error.code} - ${event.target.error.message}` );
43                };
44
45                const transaction = db.transaction( ["ProductsDB"], "readwrite" );
46                const objectStore = transaction.objectStore( "ProductsDB" );
47
48                objectStore.add( product );
+49                resolve();
50            };
+51        });
52    },

Apply this pattern to the other functions to ensure reject is defined.

Also applies to: 30-35, 65-70, 204-209, 232-237

Comment on lines +50 to +57
objectStore.add( {
id: product.id,
name: product.name,
type: product.type,
sku: product.sku,
price: product.price,
stock: product.stock_quantity
} );
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid unnecessary object reconstruction when adding products.

In insertProduct and insertProducts, you're creating a new object by mapping properties from product. Since the product object likely already contains the necessary properties, you can add it directly to the object store.

Example for insertProduct:

50     objectStore.add( {
51         id: product.id,
52         name: product.name,
53         type: product.type,
54         sku: product.sku,
55         price: product.price,
56         stock: product.stock_quantity
57     } );
+    // Ensure 'product' has the correct structure
+    // objectStore.add( product );

Note: Before making this change, verify that the product object aligns with the structure expected by the object store.

Also applies to: 86-93

Comment on lines +85 to +94
products.forEach( ( product ) => {
objectStore.add( {
id: product.id,
name: product.name,
type: product.type,
sku: product.sku,
price: product.price,
stock: product.stock_quantity
} );
} );
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle errors during bulk product insertion.

In insertProducts, if an error occurs while adding a product (e.g., due to a duplicate key), it may fail silently. Consider handling errors for each add operation.

products.forEach( ( product ) => {
    const request = objectStore.add( product );
    request.onerror = ( event ) => {
        console.error( `Error adding product ID ${product.id}: \
${event.target.error.message}` );
        // Optional: handle the error, e.g., retry or skip
    };
});

Comment on lines +41 to +45
// Generic error handler for all errors targeted at this database's requests.
console.error( `wePOS IndexedDB Database error: ${event.target.error.code} - ${event.target.error.message}` );

reject( `wePOS IndexedDB Database error: ${event.target.error.code} - ${event.target.error.message}` );
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Simplify and centralize error handling to reduce duplication.

The db.onerror handler is repeated in multiple functions with identical code. Consider refactoring this into a shared error handling function or determine if setting db.onerror is necessary in each function.

Example:

function handleDbError( event ) {
    console.error( `wePOS IndexedDB Database error: \
${event.target.error.code} - ${event.target.error.message}` );
    // Optional: handle rejection if within a Promise
}

Then, in your methods:

db.onerror = handleDbError;

Also applies to: 75-80, 113-118, 145-148, 183-188, 215-219, 243-247

Comment on lines 153 to 160
let condition = new RegExp( searchKeyword, 'i' );

objectStore.getAll().onsuccess = ( event ) => {
let products = event.target.result;

let result = products.filter( ( product ) => {
return condition.test( product.name ) || condition.test( product.id ) || condition.test( product.sku );
} );
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use 'const' for variables that are not reassigned.

Variables condition, products, and result are not reassigned after initialization. Using const enhances code readability and prevents accidental reassignments.

153     - let condition = new RegExp( searchKeyword, 'i' );
153     + const condition = new RegExp( searchKeyword, 'i' );

156     - let products = event.target.result;
156     + const products = event.target.result;

158     - let result = products.filter( ( product ) => {
158     + const result = products.filter( ( product ) => {
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let condition = new RegExp( searchKeyword, 'i' );
objectStore.getAll().onsuccess = ( event ) => {
let products = event.target.result;
let result = products.filter( ( product ) => {
return condition.test( product.name ) || condition.test( product.id ) || condition.test( product.sku );
} );
const condition = new RegExp( searchKeyword, 'i' );
objectStore.getAll().onsuccess = ( event ) => {
const products = event.target.result;
const result = products.filter( ( product ) => {
return condition.test( product.name ) || condition.test( product.id ) || condition.test( product.sku );
} );

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Outside diff range and nitpick comments (3)
assets/src/utils/productIndexedDb.js (3)

98-128: Streamline getAllProducts method for better code organization

The getAllProducts method is structured well, but there are opportunities for improvement:

  1. There's redundant error handling code.
  2. The db.onerror handler is set but never used in this context.

Consider the following refactoring:

getAllProducts() {
    return new Promise((resolve, reject) => {
        const request = indexedDB.open("ProductsDB", 1);

        request.onerror = (event) => {
            console.error("An error occurred with IndexedDB");
            reject(event);
        };

        request.onsuccess = (event) => {
            const db = event.target.result;
            const transaction = db.transaction(["ProductsDB"], "readonly");
            const objectStore = transaction.objectStore("ProductsDB");

            const getAllRequest = objectStore.getAll();

            getAllRequest.onerror = (event) => {
                reject(`Error retrieving products: ${event.target.error}`);
            };

            getAllRequest.onsuccess = (event) => {
                resolve(event.target.result);
            };
        };
    });
},

This refactoring:

  1. Removes the redundant db.onerror handler.
  2. Uses a read-only transaction since we're only reading data.
  3. Adds specific error handling for the getAll request.
  4. Simplifies the success handler.

The overall structure of returning a Promise and using IndexedDB API is correct.


130-174: Optimize getProductsBySearchKeyword method for better performance

The getProductsBySearchKeyword method is well-structured, but there are opportunities for optimization:

  1. It currently iterates through all products, which could be slow for large datasets.
  2. The search is performed in memory, which might not be efficient for very large datasets.

Consider the following optimizations:

  1. Use an index for searching if possible. If you frequently search by name or SKU, create a specific index for these fields.
  2. Limit the number of results returned to improve performance for large datasets.
  3. Use a more efficient search algorithm if possible, such as a prefix search or full-text search if supported by your IndexedDB implementation.

Here's a potential optimization using an index:

getProductsBySearchKeyword(searchKeyword = "") {
    return new Promise((resolve, reject) => {
        const request = indexedDB.open("ProductsDB", 1);

        request.onerror = (event) => {
            console.error("An error occurred with IndexedDB");
            reject(event);
        };

        request.onsuccess = (event) => {
            const db = event.target.result;
            const transaction = db.transaction(["ProductsDB"], "readonly");
            const objectStore = transaction.objectStore("ProductsDB");
            const nameIndex = objectStore.index("name");

            const range = IDBKeyRange.bound(searchKeyword, searchKeyword + '\uffff');
            const request = nameIndex.openCursor(range);

            const results = [];
            request.onsuccess = (event) => {
                const cursor = event.target.result;
                if (cursor) {
                    results.push(cursor.value);
                    cursor.continue();
                } else {
                    resolve(results);
                }
            };

            request.onerror = (event) => {
                reject(`Error searching products: ${event.target.error}`);
            };
        };
    });
},

This optimization:

  1. Uses the "name" index for searching, which can be faster for large datasets.
  2. Uses a key range to find products that start with the search keyword.
  3. Still allows for case-insensitive search by modifying the range creation if needed.

The overall structure and error handling are good. These optimizations can help improve performance for larger datasets.


176-206: Streamline updateProduct method for better code organization

The updateProduct method is structured well, but there are opportunities for improvement:

  1. There's redundant error handling code.
  2. The db.onerror handler is set but never used in this context.

Consider the following refactoring:

updateProduct(product) {
    return new Promise((resolve, reject) => {
        const request = indexedDB.open("ProductsDB", 1);

        request.onerror = (event) => {
            console.error("An error occurred with IndexedDB");
            reject(event);
        };

        request.onsuccess = (event) => {
            const db = event.target.result;
            const transaction = db.transaction(["ProductsDB"], "readwrite");
            const objectStore = transaction.objectStore("ProductsDB");

            const updateRequest = objectStore.put(product);

            updateRequest.onerror = (event) => {
                reject(`Error updating product: ${event.target.error}`);
            };

            updateRequest.onsuccess = (event) => {
                resolve(event.target.result);
            };
        };
    });
},

This refactoring:

  1. Removes the redundant db.onerror handler.
  2. Adds specific error handling for the put request.
  3. Simplifies the success handler.

The overall structure of returning a Promise and using IndexedDB API is correct.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 8e9d7c6 and e78a3cc.

📒 Files selected for processing (2)
  • assets/src/frontend/components/Home.vue (9 hunks)
  • assets/src/utils/productIndexedDb.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • assets/src/frontend/components/Home.vue

Comment on lines +2 to +24
createProductsDB() {
let db;
const request = indexedDB.open( "ProductsDB", 1 );

request.onerror = ( event ) => {
console.error( "An error occurred with IndexedDB" );
console.error( event );

reject( event );
}

// This method is only invoked after changing IndexedDB version.
request.onupgradeneeded = ( event ) => {
db = event.target.result;
const objectStore = db.createObjectStore( "ProductsDB", { keyPath: "id" } );

objectStore.createIndex( "name", "name", { unique: false } );
objectStore.createIndex( "type", "type", { unique: false } );
objectStore.createIndex( "sku", "sku", { unique: false } );
objectStore.createIndex( "price", "price", { unique: false } );
objectStore.createIndex( "stock", "stock", { unique: false } );
};
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix error handling in createProductsDB method

The createProductsDB method has an issue in its error handling:

  1. The reject function is called in the onerror callback, but it's not defined as this method doesn't return a Promise.
  2. There's no success callback to confirm the database creation.

To resolve these issues:

  1. Remove the reject(event); call or refactor the method to return a Promise.
  2. Add an onsuccess handler to confirm the database creation.

Here's a suggested implementation:

createProductsDB() {
    return new Promise((resolve, reject) => {
        const request = indexedDB.open("ProductsDB", 1);

        request.onerror = (event) => {
            console.error("An error occurred with IndexedDB");
            console.error(event);
            reject(event);
        };

        request.onsuccess = (event) => {
            console.log("ProductsDB created successfully");
            resolve(event.target.result);
        };

        request.onupgradeneeded = (event) => {
            const db = event.target.result;
            const objectStore = db.createObjectStore("ProductsDB", { keyPath: "id" });

            objectStore.createIndex("name", "name", { unique: false });
            objectStore.createIndex("type", "type", { unique: false });
            objectStore.createIndex("sku", "sku", { unique: false });
            objectStore.createIndex("price", "price", { unique: false });
            objectStore.createIndex("stock", "stock", { unique: false });
        };
    });
},

This implementation wraps the database creation in a Promise, properly handles errors, and confirms successful creation.

Comment on lines +26 to +59
insertProduct( product = {} ) {
let db;
const request = indexedDB.open( "ProductsDB", 1 );

request.onerror = ( event ) => {
console.error( "An error occurred with IndexedDB" );
console.error( event );

reject( event );
};

request.onsuccess = ( event ) => {
db = event.target.result;

db.onerror = ( event ) => {
// Generic error handler for all errors targeted at this database's requests.
console.error( `wePOS IndexedDB Database error: ${event.target.error.code} - ${event.target.error.message}` );

reject( `wePOS IndexedDB Database error: ${event.target.error.code} - ${event.target.error.message}` );
}

const transaction = db.transaction( ["ProductsDB"], "readwrite" );
const objectStore = transaction.objectStore( "ProductsDB" );

objectStore.add( {
id: product.id,
name: product.name,
type: product.type,
sku: product.sku,
price: product.price,
stock: product.stock_quantity
} );
}
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Refactor insertProduct method for better error handling and Promise support

The insertProduct method has several issues:

  1. It uses reject without being in a Promise context.
  2. It doesn't return a Promise, making it difficult to know when the operation is complete.
  3. There's unnecessary object reconstruction when adding the product.

Here's a suggested refactoring:

insertProduct(product = {}) {
    return new Promise((resolve, reject) => {
        const request = indexedDB.open("ProductsDB", 1);

        request.onerror = (event) => {
            console.error("An error occurred with IndexedDB");
            reject(event);
        };

        request.onsuccess = (event) => {
            const db = event.target.result;
            const transaction = db.transaction(["ProductsDB"], "readwrite");
            const objectStore = transaction.objectStore("ProductsDB");

            const addRequest = objectStore.add(product);

            addRequest.onerror = (event) => {
                reject(`Error adding product: ${event.target.error}`);
            };

            addRequest.onsuccess = (event) => {
                resolve(event.target.result);
            };
        };
    });
},

This refactoring:

  1. Wraps the operation in a Promise for better async handling.
  2. Simplifies error handling.
  3. Directly adds the product object without reconstruction.
  4. Provides proper resolution or rejection based on the operation's outcome.

Consider applying similar refactoring to other methods in this file for consistency and improved error handling.

Comment on lines +61 to +96
insertProducts( products = [] ) {
let db;
const request = indexedDB.open( "ProductsDB", 1 );

request.onerror = ( event ) => {
console.error( "An error occurred with IndexedDB" );
console.error( event );

reject( event );
};

request.onsuccess = ( event ) => {
db = event.target.result;

db.onerror = ( event ) => {
// Generic error handler for all errors targeted at this database's requests.
console.error( `wePOS IndexedDB Database error: ${event.target.error.code} - ${event.target.error.message}` );

reject( `wePOS IndexedDB Database error: ${event.target.error.code} - ${event.target.error.message}` );
}

const transaction = db.transaction( ["ProductsDB"], "readwrite" );
const objectStore = transaction.objectStore( "ProductsDB" );

products.forEach( ( product ) => {
objectStore.add( {
id: product.id,
name: product.name,
type: product.type,
sku: product.sku,
price: product.price,
stock: product.stock_quantity
} );
} );
}
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Improve insertProducts method for better error handling and Promise support

The insertProducts method has several issues:

  1. It uses reject without being in a Promise context.
  2. It doesn't return a Promise, making it difficult to know when all insertions are complete.
  3. There's no error handling for individual product insertions.

Here's a suggested refactoring:

insertProducts(products = []) {
    return new Promise((resolve, reject) => {
        const request = indexedDB.open("ProductsDB", 1);

        request.onerror = (event) => {
            console.error("An error occurred with IndexedDB");
            reject(event);
        };

        request.onsuccess = (event) => {
            const db = event.target.result;
            const transaction = db.transaction(["ProductsDB"], "readwrite");
            const objectStore = transaction.objectStore("ProductsDB");

            const insertPromises = products.map(product => 
                new Promise((resolveProduct, rejectProduct) => {
                    const addRequest = objectStore.add(product);
                    addRequest.onerror = (event) => rejectProduct(`Error adding product ${product.id}: ${event.target.error}`);
                    addRequest.onsuccess = () => resolveProduct();
                })
            );

            Promise.all(insertPromises)
                .then(() => resolve())
                .catch((error) => reject(error));

            transaction.oncomplete = () => resolve();
            transaction.onerror = (event) => reject(event);
        };
    });
},

This refactoring:

  1. Wraps the operation in a Promise for better async handling.
  2. Handles errors for both the database opening and individual product insertions.
  3. Uses Promise.all to track all insertions and resolve when all are complete.
  4. Provides proper resolution or rejection based on the operation's outcome.

Consider applying similar refactoring to other methods in this file for consistency and improved error handling.

Comment on lines +208 to +234
deleteProductById( id ) {
let db;
const request = indexedDB.open( "ProductsDB", 1 );

request.onerror = ( event ) => {
console.error( "An error occurred with IndexedDB" );
console.error( event );

reject( event );
};

request.onsuccess = ( event ) => {
db = event.target.result;

db.onerror = ( event ) => {
// Generic error handler for all errors targeted at this database's requests.
console.error( `wePOS IndexedDB Database error: ${event.target.error.code} - ${event.target.error.message}` );

reject( `wePOS IndexedDB Database error: ${event.target.error.code} - ${event.target.error.message}` );
}

const transaction = db.transaction( ["ProductsDB"], "readwrite" );
const objectStore = transaction.objectStore( "ProductsDB" );

objectStore.delete( id );
}
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Refactor deleteProductById method for better error handling and Promise support

The deleteProductById method has several issues:

  1. It doesn't return a Promise, making it difficult to know when the operation is complete.
  2. It uses reject without being in a Promise context.
  3. There's no success handling to confirm the deletion.

Here's a suggested refactoring:

deleteProductById(id) {
    return new Promise((resolve, reject) => {
        const request = indexedDB.open("ProductsDB", 1);

        request.onerror = (event) => {
            console.error("An error occurred with IndexedDB");
            reject(event);
        };

        request.onsuccess = (event) => {
            const db = event.target.result;
            const transaction = db.transaction(["ProductsDB"], "readwrite");
            const objectStore = transaction.objectStore("ProductsDB");

            const deleteRequest = objectStore.delete(id);

            deleteRequest.onerror = (event) => {
                reject(`Error deleting product: ${event.target.error}`);
            };

            deleteRequest.onsuccess = () => {
                resolve();
            };
        };
    });
},

This refactoring:

  1. Wraps the operation in a Promise for better async handling.
  2. Adds specific error handling for the delete request.
  3. Provides proper resolution or rejection based on the operation's outcome.

Consider applying similar refactoring to other methods in this file for consistency and improved error handling.

Comment on lines +236 to +262
deleteAllProducts() {
let db;
const request = indexedDB.open( "ProductsDB", 1 );

request.onerror = ( event ) => {
console.error( "An error occurred with IndexedDB" );
console.error( event );

reject( event );
};

request.onsuccess = ( event ) => {
db = event.target.result;

db.onerror = ( event ) => {
// Generic error handler for all errors targeted at this database's requests.
console.error( `wePOS IndexedDB Database error: ${event.target.error.code} - ${event.target.error.message}` );

reject( `wePOS IndexedDB Database error: ${event.target.error.code} - ${event.target.error.message}` );
}

const transaction = db.transaction( ["ProductsDB"], "readwrite" );
const objectStore = transaction.objectStore( "ProductsDB" );

objectStore.clear();
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Refactor deleteAllProducts method for better error handling and Promise support

The deleteAllProducts method has several issues:

  1. It doesn't return a Promise, making it difficult to know when the operation is complete.
  2. It uses reject without being in a Promise context.
  3. There's no success handling to confirm the deletion of all products.

Here's a suggested refactoring:

deleteAllProducts() {
    return new Promise((resolve, reject) => {
        const request = indexedDB.open("ProductsDB", 1);

        request.onerror = (event) => {
            console.error("An error occurred with IndexedDB");
            reject(event);
        };

        request.onsuccess = (event) => {
            const db = event.target.result;
            const transaction = db.transaction(["ProductsDB"], "readwrite");
            const objectStore = transaction.objectStore("ProductsDB");

            const clearRequest = objectStore.clear();

            clearRequest.onerror = (event) => {
                reject(`Error clearing products: ${event.target.error}`);
            };

            clearRequest.onsuccess = () => {
                resolve();
            };
        };
    });
},

This refactoring:

  1. Wraps the operation in a Promise for better async handling.
  2. Adds specific error handling for the clear request.
  3. Provides proper resolution or rejection based on the operation's outcome.

Consider applying similar refactoring to other methods in this file for consistency and improved error handling.

Comment on lines +1 to +263
results.push( product );
}

cursor.continue();
} else {
resolve( results );
}
}
}
} );
},

updateProduct( product ) {
return new Promise( ( resolve, reject ) => {
let db;
const request = indexedDB.open( "ProductsDB", 1 );

request.onerror = ( event ) => {
console.error( "An error occurred with IndexedDB" );
console.error( event );

reject( event );
};

request.onsuccess = ( event ) => {
db = event.target.result;

db.onerror = ( event ) => {
// Generic error handler for all errors targeted at this database's requests.
console.error( `wePOS IndexedDB Database error: ${event.target.error.code} - ${event.target.error.message}` );

reject( `wePOS IndexedDB Database error: ${event.target.error.code} - ${event.target.error.message}` );
}

const transaction = db.transaction( ["ProductsDB"], "readwrite" );
const objectStore = transaction.objectStore( "ProductsDB" );

objectStore.put( product ).onsuccess = ( event ) => {
resolve( event.target.result );
};
}
});
},

deleteProductById( id ) {
let db;
const request = indexedDB.open( "ProductsDB", 1 );

request.onerror = ( event ) => {
console.error( "An error occurred with IndexedDB" );
console.error( event );

reject( event );
};

request.onsuccess = ( event ) => {
db = event.target.result;

db.onerror = ( event ) => {
// Generic error handler for all errors targeted at this database's requests.
console.error( `wePOS IndexedDB Database error: ${event.target.error.code} - ${event.target.error.message}` );

reject( `wePOS IndexedDB Database error: ${event.target.error.code} - ${event.target.error.message}` );
}

const transaction = db.transaction( ["ProductsDB"], "readwrite" );
const objectStore = transaction.objectStore( "ProductsDB" );

objectStore.delete( id );
}
},

deleteAllProducts() {
let db;
const request = indexedDB.open( "ProductsDB", 1 );

request.onerror = ( event ) => {
console.error( "An error occurred with IndexedDB" );
console.error( event );

reject( event );
};

request.onsuccess = ( event ) => {
db = event.target.result;

db.onerror = ( event ) => {
// Generic error handler for all errors targeted at this database's requests.
console.error( `wePOS IndexedDB Database error: ${event.target.error.code} - ${event.target.error.message}` );

reject( `wePOS IndexedDB Database error: ${event.target.error.code} - ${event.target.error.message}` );
}

const transaction = db.transaction( ["ProductsDB"], "readwrite" );
const objectStore = transaction.objectStore( "ProductsDB" );

objectStore.clear();
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Implement consistent error handling and Promise usage across the file

After reviewing the entire file, there are several patterns that could be improved:

  1. Inconsistent Promise usage: Some methods return Promises while others don't.
  2. Redundant error handling code across methods.
  3. Inconsistent error logging strategy.

Consider the following improvements:

  1. Ensure all methods return Promises for consistent asynchronous operation handling.
  2. Create a utility function for opening the database to reduce redundant code:
function openDatabase() {
    return new Promise((resolve, reject) => {
        const request = indexedDB.open("ProductsDB", 1);
        request.onerror = (event) => {
            console.error("An error occurred with IndexedDB");
            reject(event);
        };
        request.onsuccess = (event) => resolve(event.target.result);
        // Include onupgradeneeded here if needed
    });
}
  1. Implement a consistent error logging strategy. For example:
function logError(method, error) {
    console.error(`Error in ${method}:`, error);
}
  1. Use these utility functions in your methods:
getAllProducts() {
    return openDatabase().then(db => {
        return new Promise((resolve, reject) => {
            const transaction = db.transaction(["ProductsDB"], "readonly");
            const objectStore = transaction.objectStore("ProductsDB");
            const request = objectStore.getAll();
            request.onerror = (event) => {
                logError("getAllProducts", event);
                reject(event);
            };
            request.onsuccess = (event) => resolve(event.target.result);
        });
    });
},

By implementing these changes consistently across all methods, you can significantly improve the code's maintainability and error handling.

Consider creating a separate file for database operations and utility functions. This separation of concerns will make the code more modular and easier to maintain.

@shashwatahalder01 shashwatahalder01 added QA In Progress and removed Needs Testing This requires further testing labels Oct 3, 2024
@shashwatahalder01
Copy link
Contributor

shashwatahalder01 commented Oct 4, 2024

Scenario:
db while activating dokan lite (with pr)
previous issue

Steps to reproduce:

  1. switch to pr branch
  2. Activate Wepos Lite

DB Error:

[04-Oct-2024 06:03:05 UTC] WordPress database error Table 'wepos1.wp_wepos_login' doesn't exist for query SELECT user_id, outlet_id, counter_id, is_logged_in FROM wp_wepos_login WHERE user_id=1 AND `is_logged_in`='1' made by require('wp-admin/plugins.php'), require_once('wp-admin/admin-header.php'), do_action('admin_enqueue_scripts'), WP_Hook->do_action, WP_Hook->apply_filters, WeDevs\WePOS\Assets->register, WeDevs\WePOS\Assets->register_localize, wepos_get_cashier_data_by_user_id
[04-Oct-2024 06:03:12 UTC] WordPress database error Table 'wepos1.wp_wepos_login' doesn't exist for query SELECT user_id, outlet_id, counter_id, is_logged_in FROM wp_wepos_login WHERE user_id=1 AND `is_logged_in`='1' made by require('index.php'), require('wp-blog-header.php'), require_once('wp-load.php'), require_once('wp-config.php'), require_once('wp-settings.php'), do_action('init'), WP_Hook->do_action, WP_Hook->apply_filters, WePOS->init_classes, WeDevs\WePOS\ProductsLog->__construct, wepos_get_cashier_data_by_user_id

Screenshot:
Screenshot at Oct 04 12-05-08

@shashwatahalder01
Copy link
Contributor

Scenario:
If the upgrader is run, index db doesn't get populated.

Steps to reproduce:

  1. run the upgrader
  2. go to view pos

Screenshot:
Screenshot at Oct 04 12-32-02
Screenshot at Oct 04 12-31-50

@shashwatahalder01
Copy link
Contributor

shashwatahalder01 commented Oct 4, 2024

Scenario:
Product sync/refresh doesn't work. console error exists

Steps to reproduce:

  1. go to view pos
  2. refresh products

Screenshot:
Screenshot at Oct 04 11-30-52

@shashwatahalder01
Copy link
Contributor

Scenario:
Produce search result should be exact(only one product matching the search parameter) or the result should be in reverse order to show the exact matched product on top

Screenshot:
Screenshot at Oct 04 10-28-43

@shashwatahalder01
Copy link
Contributor

@devAsadNur, kindly update the target upgrader version to the next release version(1.2.9).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Something isn't working Dev Review Done Urgent
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[POS Dashboard]: Can not find product by searching if there is a lot of product listed.
4 participants