Skip to Content
Odoo Menu
  • Sign in
  • Try it free
  • Apps
    Finance
    • Accounting
    • Invoicing
    • Expenses
    • Spreadsheet (BI)
    • Documents
    • Sign
    Sales
    • CRM
    • Sales
    • POS Shop
    • POS Restaurant
    • Subscriptions
    • Rental
    Websites
    • Website Builder
    • eCommerce
    • Blog
    • Forum
    • Live Chat
    • eLearning
    Supply Chain
    • Inventory
    • Manufacturing
    • PLM
    • Purchase
    • Maintenance
    • Quality
    Human Resources
    • Employees
    • Recruitment
    • Time Off
    • Appraisals
    • Referrals
    • Fleet
    Marketing
    • Social Marketing
    • Email Marketing
    • SMS Marketing
    • Events
    • Marketing Automation
    • Surveys
    Services
    • Project
    • Timesheets
    • Field Service
    • Helpdesk
    • Planning
    • Appointments
    Productivity
    • Discuss
    • Artificial Intelligence
    • IoT
    • VoIP
    • Knowledge
    • WhatsApp
    Third party apps Odoo Studio Odoo Cloud Platform
  • Industries
    Retail
    • Book Store
    • Clothing Store
    • Furniture Store
    • Grocery Store
    • Hardware Store
    • Toy Store
    Food & Hospitality
    • Bar and Pub
    • Restaurant
    • Fast Food
    • Guest House
    • Beverage Distributor
    • Hotel
    Real Estate
    • Real Estate Agency
    • Architecture Firm
    • Construction
    • Property Management
    • Gardening
    • Property Owner Association
    Consulting
    • Accounting Firm
    • Odoo Partner
    • Marketing Agency
    • Law firm
    • Talent Acquisition
    • Audit & Certification
    Manufacturing
    • Textile
    • Metal
    • Furnitures
    • Food
    • Brewery
    • Corporate Gifts
    Health & Fitness
    • Sports Club
    • Eyewear Store
    • Fitness Center
    • Wellness Practitioners
    • Pharmacy
    • Hair Salon
    Trades
    • Handyman
    • IT Hardware & Support
    • Solar Energy Systems
    • Shoe Maker
    • Cleaning Services
    • HVAC Services
    Others
    • Nonprofit Organization
    • Environmental Agency
    • Billboard Rental
    • Photography
    • Bike Leasing
    • Software Reseller
    Browse all Industries
  • Community
    Learn
    • Tutorials
    • Documentation
    • Certifications
    • Training
    • Blog
    • Podcast
    Empower Education
    • Education Program
    • Scale Up! Business Game
    • Visit Odoo
    Get the Software
    • Download
    • Compare Editions
    • Releases
    Collaborate
    • Github
    • Forum
    • Events
    • Translations
    • Become a Partner
    • Services for Partners
    • Register your Accounting Firm
    Get Services
    • Find a Partner
    • Find an Accountant
    • Meet an advisor
    • Implementation Services
    • Customer References
    • Support
    • Upgrades
    Github Youtube Twitter Linkedin Instagram Facebook Spotify
    +1 (650) 691-3277
    Get a demo
  • Pricing
  • Help
You need to be registered to interact with the community.
All Posts People Badges
Tags (View all)
odoo accounting v14 pos v15
About this forum
You need to be registered to interact with the community.
All Posts People Badges
Tags (View all)
odoo accounting v14 pos v15
About this forum
Help

POS barcode search not working in offline mode (need hybrid logic)

Subscribe

Get notified when there's activity on this post

This question has been flagged
serverposrpcJs
494 Views
Avatar
Ashilkrishna

Hi everyone,

I am implementing a custom barcode search feature in POS.

 Current behavior:
  • Online mode

    • Server barcode search works
    • Real-time stock works
    • allow_negative_pos_sale works
  • Offline mode

    • I want:

      • Local barcode search
      • Cached stock check
      • No crash
❌ Problem:

When POS is offline, barcode search is not working properly (product not found or stock shows 0 even if available).

 My Code (JS):
/** @odoo-module **/

import { patch } from "@web/core/utils/patch";
import { Navbar } from "@point_of_sale/app/navbar/navbar";
import { useRef } from "@odoo/owl";

patch(Navbar.prototype, {
patchName: "pos_barcode_search.Navbar",
setup() {
super.setup();
this.barcodeInput = useRef("barcodeInput");
},
async barcodeKeyHandler(ev) {
if (ev.key === "Enter") {
console.log("Barcode handler triggered!");
const barcode = this.barcodeInput.el.value.trim();
console.log("barcode", barcode);
console.log("this.pos", this.pos);

if (barcode && this.pos) {
// First try to find the product in local models
let product = this.pos.models["product.product"].getBy("barcode", barcode);

// If not found locally, try product packaging
if (!product) {
const productPackaging = this.pos.models["product.packaging"].getBy("barcode", barcode);
product = productPackaging && productPackaging.product_id;
}

// If still not found, search on the server
if (!product) {
try {
const records = await this.pos.data.callRelated(
"pos.session",
"find_product_by_barcode",
[this.pos.session.id, barcode, this.pos.config.id]
);
await this.pos.processProductAttributes();

if (records && records["product.product"].length > 0) {
product = records["product.product"][0];
await this.pos._loadMissingPricelistItems([product]);
}
} catch (error) {
console.error("Error searching for product by barcode:", error);
}
}

if (product) {
// Ensure we have the 'type' field on the product
let productType = product.type;
if (productType === undefined) {
const orm = this.env.services.orm;
const typeData = await orm.read('product.product', [product.id], ['type']);
productType = typeData[0]?.type;
}

if (productType === 'product' || productType === 'consu') { // Both storable and consumable products need stock check
// Check if product allows negative POS sales
const allowNegativeSale = product.allow_negative_pos_sale;
console.log("[BARCODE SEARCH] Product:", product.name, "allow_negative_pos_sale:", allowNegativeSale);
console.log("[BARCODE SEARCH] Full product data:", product);
console.log("[BARCODE SEARCH] Product ID:", product.id);
console.log("[BARCODE SEARCH] All product keys:", Object.keys(product));

// If field is not available, try to fetch it from server
let finalAllowNegativeSale = allowNegativeSale;
if (allowNegativeSale === undefined || allowNegativeSale === null) {
console.log("[BARCODE SEARCH] Field not available in product data, fetching from server...");
try {
const orm = this.env.services.orm;
const fieldData = await orm.read('product.product', [product.id], ['allow_negative_pos_sale']);
finalAllowNegativeSale = fieldData[0]?.allow_negative_pos_sale || false;
console.log("[BARCODE SEARCH] Fetched from server:", finalAllowNegativeSale);
} catch (error) {
console.error("[BARCODE SEARCH] Error fetching field from server:", error);
finalAllowNegativeSale = false;
}
}

// CONDITION: If product has exemption enabled, skip stock restriction
if (finalAllowNegativeSale) {
console.log("[BARCODE SEARCH] Product has exemption enabled - skipping stock restriction");
// Proceed with adding product without stock check
} else {
console.log("[BARCODE SEARCH] Product has no exemption - applying stock restriction");
// Stock check logic from pos_stock_restriction.js
const orm = this.env.services.orm;
const stockData = await orm.read('product.product', [product.id], ['qty_available']);
const qty_available = stockData[0]?.qty_available ?? 0;
console.log("Qty In hand", qty_available);

// Get the current order and count how many of this product are already in the order
const order = this.pos.get_order();
let qty_in_order = 0;
if (order) {
const lines = order.get_orderlines();
console.log("Order lines:", lines);
lines.forEach(line => {
const lineProduct = line.get_product ? line.get_product() : line.product;
// Use get_quantity() if available, otherwise fallback to line.quantity
const lineQty = line.get_quantity ? line.get_quantity() : line.quantity;
console.log("Line product:", lineProduct, "Line product id:", lineProduct && lineProduct.id, "Target product id:", product.id, "Line qty:", lineQty);
if (lineProduct && lineProduct.id === product.id) {
qty_in_order += lineQty;
console.log("Qty in order (matched):", qty_in_order);
}
});
}

// The quantity to add per scan
const qty_needed = 1;

if (qty_available < qty_in_order + qty_needed) {
this.env.services.notification.add(
`Out of stock: ${product.display_name || product.name}`,
{ type: "danger" }
);
return;
}
}
await this.pos.addLineToCurrentOrder({ product_id: product }, {}, false);
this.barcodeInput.el.value = "";
} else {
// Not a storable product (e.g., service or consumable), add without stock check
await this.pos.addLineToCurrentOrder({ product_id: product }, {}, false);
this.barcodeInput.el.value = "";
}
} else {
this.env.services.notification.add("Product not found for barcode: " + barcode, { type: "danger" });
}
}
}
},
});
 My Backend Code:
from odoo import models

class PosConfig(models.Model):
_inherit = 'pos.config'

def _get_pos_ui_product_fields(self):
fields = super()._get_pos_ui_product_fields()
if 'allow_negative_pos_sale' not in fields:
fields.append('allow_negative_pos_sale')
print(f"[DEBUG] Barcode Search - POS UI Product fields: {fields}")

# Force ensure the field is loaded
if 'allow_negative_pos_sale' not in fields:
fields.append('allow_negative_pos_sale')
print(f"[DEBUG] Barcode Search - Force added allow_negative_pos_sale to POS UI fields")

return fields

def _loader_params_product_product(self):
res = super()._loader_params_product_product()
if 'allow_negative_pos_sale' not in res['search_params']['fields']:
res['search_params']['fields'].append('allow_negative_pos_sale')
print(f"[DEBUG] Barcode Search - Product fields being loaded: {res['search_params']['fields']}")

# Force ensure the field is loaded
if 'allow_negative_pos_sale' not in res['search_params']['fields']:
res['search_params']['fields'].append('allow_negative_pos_sale')
print(f"[DEBUG] Barcode Search - Force added allow_negative_pos_sale field")

# Also ensure we have the basic fields
required_fields = ['id', 'name', 'barcode', 'type', 'allow_negative_pos_sale']
for field in required_fields:
if field not in res['search_params']['fields']:
res['search_params']['fields'].append(field)
print(f"[DEBUG] Barcode Search - Force added required field: {field}")

# Also ensure we have the search domain
if 'search_params' not in res:
res['search_params'] = {}
if 'domain' not in res['search_params']:
res['search_params']['domain'] = []

return res

def _loader_params_product_template(self):
res = super()._loader_params_product_template()
if 'allow_negative_pos_sale' not in res['search_params']['fields']:
res['search_params']['fields'].append('allow_negative_pos_sale')
print(f"[DEBUG] Barcode Search - Product Template fields being loaded: {res['search_params']['fields']}")

# Force ensure the field is loaded
if 'allow_negative_pos_sale' not in res['search_params']['fields']:
res['search_params']['fields'].append('allow_negative_pos_sale')
print(f"[DEBUG] Barcode Search - Force added allow_negative_pos_sale to Product Template fields")

# Also ensure we have the basic fields
required_fields = ['id', 'name', 'type', 'allow_negative_pos_sale']
for field in required_fields:
if field not in res['search_params']['fields']:
res['search_params']['fields'].append(field)
print(f"[DEBUG] Barcode Search - Force added required field to Product Template: {field}")

return res

def _pos_ui_models_to_load(self):
res = super()._pos_ui_models_to_load()
if 'product.product' not in res:
res.append('product.product')
print(f"[DEBUG] Barcode Search - POS UI Models to load: {res}")

# Force ensure product.product is loaded
if 'product.product' not in res:
res.append('product.product')
print(f"[DEBUG] Barcode Search - Force added product.product to POS UI Models")

# Also ensure we have the basic models
required_models = ['product.product', 'product.template']
for model in required_models:
if model not in res:
res.append(model)
print(f"[DEBUG] Barcode Search - Force added required model: {model}")

return res

My Requirement:

I need a best hybrid logic:

  • If online → use server (dynamic data)
  • If offline → use local POS data (no RPC calls)
  • Barcode search should work in both cases

Can anyone help me modify this code to:

  • Work correctly in offline mode
  • Keep my existing online functionality
  • Ensure stock and barcode search works properly offline

Thanks in advance!



0
Avatar
Discard
Enjoying the discussion? Don't just read, join in!

Create an account today to enjoy exclusive features and engage with our awesome community!

Sign up
Related Posts Replies Views Activity
Is there any event after and event before functionality openerp 7
server pos
Avatar
0
Mar 15
539
Settings Page can't be accessed - RPC_ERROR Odoo Server Error
server rpc errors
Avatar
Avatar
1
Dec 22
4965
RPC_ERROR: ValueError: Invalid field 'brand_id' on model 'product.template' after "changing something""
server error rpc
Avatar
0
Dec 22
7346
How to access sale.order.line objects from javascript when calling sale.order with a rpc call ?
pos sale.order.line rpc
Avatar
0
Feb 21
4347
I can't send tickets by email at the point of sale, odoo 13 community
server pos email log
Avatar
0
Mar 24
2380
Community
  • Tutorials
  • Documentation
  • Forum
Open Source
  • Download
  • Github
  • Runbot
  • Translations
Services
  • Odoo.sh Hosting
  • Support
  • Upgrade
  • Custom Developments
  • Education
  • Find an Accountant
  • Find a Partner
  • Become a Partner
About us
  • Our company
  • Brand Assets
  • Contact us
  • Jobs
  • Events
  • Podcast
  • Blog
  • Customers
  • Legal • Privacy
  • Security
الْعَرَبيّة Català 简体中文 繁體中文 (台灣) Čeština Dansk Nederlands English Suomi Français Deutsch हिंदी Bahasa Indonesia Italiano 日本語 한국어 (KR) Lietuvių kalba Język polski Português (BR) română русский язык Slovenský jazyk Slovenščina Español (América Latina) Español Svenska ภาษาไทย Türkçe українська Tiếng Việt

Odoo is a suite of open source business apps that cover all your company needs: CRM, eCommerce, accounting, inventory, point of sale, project management, etc.

Odoo's unique value proposition is to be at the same time very easy to use and fully integrated.

Website made with

Odoo Experience on YouTube

1. Use the live chat to ask your questions.
2. The operator answers within a few minutes.

Live support on Youtube
Watch now