pos_ui_optimization/README.md

204 lines
8.2 KiB
Markdown

# 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 <your_database>
```
---
## 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**