# POS UI Optimization **Odoo 19 | Custom Addon** Optimizes the Point of Sale UI for resource-constrained devices and adds a **switchable portrait display mode** designed for 6-inch touchscreen terminals. --- ## Features ### 1. Incremental Product Rendering Prevents low-RAM devices from freezing when the product catalogue is large. - Only **40 products** are rendered on initial load - More products load automatically as the user scrolls down - Resets the counter when the search word or category changes ### 2. Incremental Order Line Rendering Same approach for the order/cart display. - Only **20 order lines** rendered initially - Scrolling loads more lines progressively - Ensures the selected line is always visible (expands count if needed) ### 3. Hide Invoice Button Hides the "Invoice" button on the payment stage (Payment Screen) and expands the Customer/Partner button to full width, preventing cashiers from using this button. ### 4. Portrait Mode (6-inch Display) A dedicated compact layout for portrait-orientation small screens. Designed for devices like 6-inch Android POS terminals where the standard Odoo two-column layout clutters the screen. #### Layout ``` ┌──────────────────────────────────────┐ │ Navbar (compact) │ ├──────────────────────────────────────┤ │ │ │ Active tab pane: │ │ Products / Cart / Numpad │ │ │ ├──────────────────────────────────────┤ │ TOTAL: Rp X,XX [Checker] [Send] [Pay]│ ← always visible ├──────────────────────────────────────┤ │ [Products] [Cart] [Numpad] │ ← bottom tab bar └──────────────────────────────────────┘ ``` #### Key Differences from Standard Mode | Feature | Standard | Portrait | |---|---|---| | Layout | Two-column (order left, products right) | Single pane with bottom tabs | | Categories | Horizontal scrollable chips | Compact hierarchical select dropdown | | Product grid | `auto-fill minmax(115px)` | Fixed 2 columns | | Product images | Full aspect ratio | Capped at 90px height | | Numpad | Shown inline in left pane | Dedicated "Numpad" tab (shows only selected line) | | Actions (Pay/Send/Checker) | Inside left pane action pad | Always-visible bottom strip | | Navbar | Full labels | Compact with smaller padding | #### Category Dropdown In portrait mode, the category selector is transformed into a compact dropdown list. It lists all active POS categories formatted hierarchically with indentation, making it extremely easy to filter products on small touch screens. #### Selected Line Actions (Note & Delete) The Numpad tab contains actions for modifying the selected orderline: - **Note**: Triggers the standard customer note input dialog. - **Delete**: Instantly removes the selected line from the cart. #### Numpad Single Line Focus To prevent overlaps on 5.8-inch screens: - **Cart Tab**: Shows only the order lines (full height). The numpad is hidden. - **Numpad Tab**: Hides all non-selected lines and hides the order summary block, showing only the selected line and the numpad/actions. If no item is selected, a friendly guide card is displayed. #### Table Checker Button The **Checker** button is available in the bottom pay strip in portrait mode. It allows waiters or cashiers to quickly print a basic table checker receipt. The button is visible only when: 1. The `basic_receipt` option is enabled in POS settings. 2. The current cashier's role is either `waiter` or `cashier`. The button is disabled when the current order is empty. #### Auto-Activation Portrait mode **auto-activates** when the browser viewport width is **< 400 px** on first load. A manual override is always available via the burger menu (☰ → Switch to Portrait/Standard Mode). The choice is saved to `localStorage`. #### Send Button Behavior The **Send** button appears only when: 1. The POS is in restaurant mode (`module_pos_restaurant = True`) 2. At least one **Preparation Category** is configured in POS → Kitchen Printers settings 3. The current order has unsent changes (`nbrOfChanges > 0`) 4. The order is not a direct sale / refund This mirrors Odoo's standard restaurant `swapButton` logic exactly. --- ### 5. Safe Release Table Guard (Multi-Device) Prevents an accidental order cancellation when multiple POS devices share the same login account in a restaurant setup. **The Problem** When Device X places orders on a table, Device Y sees the table turn purple (occupied). If Device Y opens that table before the order has fully synced locally, it may see an empty order and display the **Release table** button. Clicking Release on Device Y calls ``action_pos_order_cancel`` on the server, permanently cancelling the order created on Device X. **The Fix** Before executing the Release action, this feature performs a quick server-side check: 1. An RPC call to ``pos.order.check_table_has_real_orders(table_id)`` is made. 2. If the server reports real orders with non-zero lines exist on this table: - The release is **blocked**. - An alert dialog informs the user: *"This table has N active order(s) placed from another device."* - After the user acknowledges, ``deviceSync.readDataFromServer()`` is triggered so the real orders from Device X appear on Device Y. - The user is sent back to the Floor Screen with the correct table state. 3. If the server confirms no real orders exist, the Release proceeds as normal. **Offline Fallback** If the RPC call fails (e.g. device is offline), the guard falls back to the original ``unbookTable()`` behaviour to avoid blocking legitimate releases. --- ## Installation 1. Copy the `pos_ui_optimization` folder into your `customaddons` directory 2. Restart the Odoo server 3. Go to **Apps** → search for **POS UI Optimization** → Install 4. Reload your POS session ## Upgrade ```bash ./odoo-bin -u pos_ui_optimization -d ``` --- ## Compatibility | Item | Value | |---|---| | Odoo version | 19.0 | | Depends on | `point_of_sale` | | Compatible with | `pos_restaurant`, `pos_custom_access`, `pos_kitchen_printer` | | License | LGPL-3 | --- ## File Structure ``` pos_ui_optimization/ ├── __manifest__.py ├── README.md ├── static/ │ └── src/ │ ├── app/ │ │ ├── components/ │ │ │ ├── category_selector/ │ │ │ │ ├── category_selector_patch.js # Hierarchical dropdown list │ │ │ │ └── category_selector_patch.xml │ │ │ ├── navbar/ │ │ │ │ ├── navbar_patch.js # Display Mode toggle in burger menu │ │ │ │ └── navbar_patch.xml │ │ │ └── order_display/ │ │ │ ├── order_display_patch.js # Incremental order line rendering │ │ │ └── order_display_patch.xml │ │ ├── screens/ │ │ │ ├── payment_screen/ │ │ │ │ └── payment_screen_patch.xml # Hide Invoice button patch │ │ │ └── product_screen/ │ │ │ ├── order_summary/ │ │ │ │ └── safe_release_patch.js # Multi-device Release guard │ │ │ ├── portrait_mode_patch.js # Tab state (products/cart/numpad) │ │ │ ├── portrait_screen.xml # Portrait layout XML patch │ │ │ ├── product_screen_patch.js # Incremental product rendering │ │ │ └── product_screen_patch.xml │ │ └── services/ │ │ └── portrait_mode.js # Auto-detect + localStorage service │ └── scss/ │ └── portrait.scss # All portrait CSS (scoped to .pos-portrait) ``` --- ## Author **Suherdy Yacob**