v{{ appVersion }}
diff --git a/pos_next/hooks.py b/pos_next/hooks.py
index 2720175b..f90f31b6 100644
--- a/pos_next/hooks.py
+++ b/pos_next/hooks.py
@@ -1,7 +1,7 @@
from pos_next.utils import get_build_version
app_name = "pos_next"
-app_title = "POS Next"
+app_title = "Olko"
app_publisher = "BrainWise"
app_description = "POS built on ERPNext that brings together real-time billing, stock management, multi-user access, offline mode, and direct ERP integration. Run your store or restaurant with confidence and control, while staying 100% open source."
app_email = "support@brainwise.me"
From 1cd6aff04d8a1e5ab3ed006ff34651587470e100 Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Tue, 10 Mar 2026 18:25:48 +0000
Subject: [PATCH 04/25] fix: frontend pos settings not recognizing restaurant
mode
- Added enable_restaurant_mode and default_restaurant_area to POS_SETTINGS_FIELDS in backend to allow api response
- Updated DEFAULT_POS_SETTINGS with restaurant fields
- Added enableRestaurantMode computed property to frontend posSettings store to correctly evaluate settings state
Co-authored-by: hemidirasim <25175153+hemidirasim@users.noreply.github.com>
---
POS/src/stores/posSettings.js | 11 +++++++++++
pos_next/api/constants.py | 4 ++++
2 files changed, 15 insertions(+)
diff --git a/POS/src/stores/posSettings.js b/POS/src/stores/posSettings.js
index 9278faf4..b7127f69 100644
--- a/POS/src/stores/posSettings.js
+++ b/POS/src/stores/posSettings.js
@@ -42,6 +42,9 @@ export const usePOSSettingsStore = defineStore("posSettings", () => {
allow_return_without_invoice: 0,
allow_free_batch_return: 0,
allow_print_draft_invoices: 0,
+ // Restaurant
+ enable_restaurant_mode: 0,
+ default_restaurant_area: "",
// Pricing & Display
decimal_precision: "2",
// Customer Settings
@@ -243,6 +246,11 @@ export const usePOSSettingsStore = defineStore("posSettings", () => {
() => Number.parseInt(settings.value.session_lock_timeout) || 5,
)
+ // Computed - Restaurant
+ const enableRestaurantMode = computed(() =>
+ Boolean(settings.value.enable_restaurant_mode),
+ )
+
// Resource
const settingsResource = createResource({
url: "pos_next.pos_next.doctype.pos_settings.pos_settings.get_pos_settings",
@@ -481,6 +489,9 @@ export const usePOSSettingsStore = defineStore("posSettings", () => {
enableSessionLock,
sessionLockTimeout,
+ // Computed - Restaurant
+ enableRestaurantMode,
+
// Actions
loadSettings,
reloadSettings,
diff --git a/pos_next/api/constants.py b/pos_next/api/constants.py
index ac2625d1..d1da2d34 100644
--- a/pos_next/api/constants.py
+++ b/pos_next/api/constants.py
@@ -39,6 +39,8 @@
"enable_session_lock",
"session_lock_timeout",
"show_variants_as_items",
+ "enable_restaurant_mode",
+ "default_restaurant_area",
]
# Default POS Settings values
@@ -68,4 +70,6 @@
"enable_session_lock": 0,
"session_lock_timeout": 5,
"show_variants_as_items": 0,
+ "enable_restaurant_mode": 0,
+ "default_restaurant_area": "",
}
From e81935f239ee1fb07b57921b00007a5683e264dd Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Tue, 10 Mar 2026 18:39:07 +0000
Subject: [PATCH 05/25] fix: ui showing no tables found when cache is empty
- Updated TableSelector component to forcefully fetch from the network when local indexedDB cache is empty, ensuring newly created tables in Frappe are visible to the user immediately.
Co-authored-by: hemidirasim <25175153+hemidirasim@users.noreply.github.com>
---
POS/src/components/pos/TableSelector.vue | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/POS/src/components/pos/TableSelector.vue b/POS/src/components/pos/TableSelector.vue
index d51b4e90..4f26c9a9 100644
--- a/POS/src/components/pos/TableSelector.vue
+++ b/POS/src/components/pos/TableSelector.vue
@@ -96,6 +96,11 @@ const filteredTables = computed(() => {
onMounted(async () => {
await restaurantStore.loadTablesAndAreas()
+ // If empty from cache, try fetching from network directly
+ if (areas.value.length === 0 || tables.value.length === 0) {
+ await restaurantStore.fetchFromNetwork()
+ }
+
if (areas.value.length > 0) {
selectedArea.value = restaurantStore.defaultArea || areas.value[0].name
}
From be4da05d89ce4af8cb9604d26320a9fe84d80e59 Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Tue, 10 Mar 2026 18:51:38 +0000
Subject: [PATCH 06/25] fix: table selection not transitioning from table view
to item view
- Exported `setRestaurantTable` and `setKdsStatus` actions from the posCart store so that they can be successfully called by components when a table is selected or an order is sent to the kitchen.
Co-authored-by: hemidirasim <25175153+hemidirasim@users.noreply.github.com>
---
POS/src/stores/posCart.js | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/POS/src/stores/posCart.js b/POS/src/stores/posCart.js
index 9a2350a8..675dc805 100644
--- a/POS/src/stores/posCart.js
+++ b/POS/src/stores/posCart.js
@@ -1728,6 +1728,8 @@ export const usePOSCartStore = defineStore("posCart", () => {
selectionMode,
currentDraftId,
offerProcessingState, // Offer processing state for UI feedback
+ restaurantTable,
+ kdsStatus,
// Computed
itemCount,
@@ -1746,6 +1748,8 @@ export const usePOSCartStore = defineStore("posCart", () => {
setDefaultCustomer,
setPendingItem,
clearPendingItem,
+ setRestaurantTable,
+ setKdsStatus,
loadTaxRules,
setTaxInclusive,
submitInvoice,
From 13f54b6db57fe7e1ce5e51ec57a0f20aa65225a5 Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Tue, 10 Mar 2026 19:13:49 +0000
Subject: [PATCH 07/25] fix: update table status on invoice save and add table
to receipt
- Added document hook `on_invoice_update` to Sales Invoice to automatically sync the Restaurant Table status (Occupied, Cleaning, Empty) based on the invoice docstatus.
- Updated `pos_next/fixtures/print_format.json` to visibly render the `restaurant_table` and item `posa_special_instructions` on the printed receipt.
- Verified that `kds_status` and `restaurant_table` correctly flow through the `update_invoice` API when saving Draft/Hold orders, which powers the KDS display.
Co-authored-by: hemidirasim <25175153+hemidirasim@users.noreply.github.com>
---
pos_next/api/restaurant.py | 11 +++++++++++
pos_next/fixtures/print_format.json | 4 ++--
pos_next/hooks.py | 9 +++++++--
3 files changed, 20 insertions(+), 4 deletions(-)
diff --git a/pos_next/api/restaurant.py b/pos_next/api/restaurant.py
index 19dd0aae..4cace7f9 100644
--- a/pos_next/api/restaurant.py
+++ b/pos_next/api/restaurant.py
@@ -1,6 +1,17 @@
import frappe
from frappe import _
+def on_invoice_update(doc, method):
+ """Update table status based on invoice status."""
+ if doc.get("restaurant_table"):
+ if doc.docstatus == 0:
+ frappe.db.set_value("Restaurant Table", doc.restaurant_table, "status", "Occupied")
+ elif doc.docstatus == 1:
+ frappe.db.set_value("Restaurant Table", doc.restaurant_table, "status", "Cleaning")
+ elif doc.docstatus == 2:
+ frappe.db.set_value("Restaurant Table", doc.restaurant_table, "status", "Empty")
+
+
@frappe.whitelist()
def get_tables():
"""Fetch all restaurant areas and tables."""
diff --git a/pos_next/fixtures/print_format.json b/pos_next/fixtures/print_format.json
index 762ef158..04bcf09d 100644
--- a/pos_next/fixtures/print_format.json
+++ b/pos_next/fixtures/print_format.json
@@ -4,7 +4,7 @@
"doctype": "Print Format",
"name": "POS Next Receipt",
"module": "POS Next",
- "html": "\n\n{%- set currency_symbol = frappe.db.get_value(\"Currency\", doc.currency, \"symbol\") -%}\n{%- if not currency_symbol -%}\n\t{%- set currency_symbol = doc.currency -%}\n{%- endif -%}\n\n\n{{ doc.company }}
\nTAX INVOICE
\n\n\n\n\t
Invoice: {{ doc.name }}
\n\t
Date: {{ doc.posting_date }} {{ (doc.posting_time|string).split('.')[0] if doc.posting_time else '' }}
\n\t{%- if doc.customer_name -%}\n\t
Customer: {{ doc.customer_name }}
\n\t{%- endif -%}\n\t{%- if doc.status == \"Partly Paid\" or (doc.outstanding_amount and doc.outstanding_amount > 0 and doc.outstanding_amount < doc.grand_total) -%}\n\t
Status: PARTIAL PAYMENT
\n\t{%- endif -%}\n
\n\n\n\n\n{%- for item in doc.items -%}\n\n\t
{{ item.item_name }}
\n\t
\n\t\t{{ \"%.0f\"|format(item.qty) }} × {{ currency_symbol }} {{ \"%.2f\"|format(item.price_list_rate or item.rate) }}\n\t\t{{ currency_symbol }} {{ \"%.2f\"|format(item.qty * (item.price_list_rate or item.rate)) }}\n\t
\n\t{%- if item.discount_percentage or item.discount_amount -%}\n\t
\n\t\tDiscount{%- if item.discount_percentage -%} ({{ \"%.1f\"|format(item.discount_percentage) }}%){%- endif -%}\n\t\t-{{ currency_symbol }} {{ \"%.2f\"|format(item.discount_amount or 0) }}\n\t
\n\t{%- endif -%}\n\t{%- if item.serial_no -%}\n\t
\n\t\t
Serial No:
\n\t\t
{{ item.serial_no | replace(\"\\n\", \", \") }}
\n\t
\n\t{%- endif -%}\n
\n{%- endfor -%}\n\n\n\n\n{%- if doc.total_taxes_and_charges and doc.total_taxes_and_charges > 0 -%}\n\n\tSubtotal:\n\t{{ currency_symbol }} {{ \"%.2f\"|format(doc.grand_total - doc.total_taxes_and_charges) }}\n
\n\n\tTax:\n\t{{ currency_symbol }} {{ \"%.2f\"|format(doc.total_taxes_and_charges) }}\n
\n{%- endif -%}\n{%- if doc.discount_amount -%}\n\n\tAdditional Discount{%- if doc.additional_discount_percentage -%} ({{ \"%.1f\"|format(doc.additional_discount_percentage) }}%){%- endif -%}:\n\t-{{ currency_symbol }} {{ \"%.2f\"|format(doc.discount_amount|abs) }}\n
\n{%- endif -%}\n\n\tTOTAL:\n\t{{ currency_symbol }} {{ \"%.2f\"|format(doc.grand_total) }}\n
\n\n\n{%- if doc.payments -%}\n\nPayments:
\n{%- for payment in doc.payments -%}\n\n\t{{ payment.mode_of_payment }}:\n\t{{ currency_symbol }} {{ \"%.2f\"|format(payment.amount) }}\n
\n{%- endfor -%}\n\n\tTotal Paid:\n\t{{ currency_symbol }} {{ \"%.2f\"|format(doc.paid_amount or 0) }}\n
\n{%- if doc.change_amount > 0 -%}\n\n\tChange:\n\t{{ currency_symbol }} {{ \"%.2f\"|format(doc.change_amount) }}\n
\n{%- endif -%}\n{%- if doc.outstanding_amount and doc.outstanding_amount > 0 -%}\n\n\tBALANCE DUE:\n\t{{ currency_symbol }} {{ \"%.2f\"|format(doc.outstanding_amount) }}\n
\n{%- endif -%}\n{%- endif -%}\n\n\n",
+ "html": "\n\n{%- set currency_symbol = frappe.db.get_value(\"Currency\", doc.currency, \"symbol\") -%}\n{%- if not currency_symbol -%}\n\t{%- set currency_symbol = doc.currency -%}\n{%- endif -%}\n\n\n{{ doc.company }}
\nTAX INVOICE
\n\n\n\n\t
Invoice: {{ doc.name }}
\n\t
Date: {{ doc.posting_date }} {{ (doc.posting_time|string).split('.')[0] if doc.posting_time else '' }}
\n\t{%- if doc.customer_name -%}\n\t
Customer: {{ doc.customer_name }}
\n\t{%- endif -%}\n\t{%- if doc.status == \"Partly Paid\" or (doc.outstanding_amount and doc.outstanding_amount > 0 and doc.outstanding_amount < doc.grand_total) -%}\n\t
Status: PARTIAL PAYMENT
\n\t{%- endif -%}\n
\n\n\n\n\n{%- for item in doc.items -%}\n\n\t
{{ item.item_name }}
\n\t
\n\t\t{{ \"%.0f\"|format(item.qty) }} \u00d7 {{ currency_symbol }} {{ \"%.2f\"|format(item.price_list_rate or item.rate) }}\n\t\t{{ currency_symbol }} {{ \"%.2f\"|format(item.qty * (item.price_list_rate or item.rate)) }}\n\t
\n\t{%- if item.discount_percentage or item.discount_amount -%}\n\t
\n\t\tDiscount{%- if item.discount_percentage -%} ({{ \"%.1f\"|format(item.discount_percentage) }}%){%- endif -%}\n\t\t-{{ currency_symbol }} {{ \"%.2f\"|format(item.discount_amount or 0) }}\n\t
\n\t{%- endif -%}\n\t{%- if item.serial_no -%}\n\t
\n\t\t
Serial No:
\n\t\t
{{ item.serial_no | replace(\"\\n\", \", \") }}
\n\t
\n\t{%- endif -%}\n
\n{%- endfor -%}\n\n\n\n\n{%- if doc.total_taxes_and_charges and doc.total_taxes_and_charges > 0 -%}\n\n\tSubtotal:\n\t{{ currency_symbol }} {{ \"%.2f\"|format(doc.grand_total - doc.total_taxes_and_charges) }}\n
\n\n\tTax:\n\t{{ currency_symbol }} {{ \"%.2f\"|format(doc.total_taxes_and_charges) }}\n
\n{%- endif -%}\n{%- if doc.discount_amount -%}\n\n\tAdditional Discount{%- if doc.additional_discount_percentage -%} ({{ \"%.1f\"|format(doc.additional_discount_percentage) }}%){%- endif -%}:\n\t-{{ currency_symbol }} {{ \"%.2f\"|format(doc.discount_amount|abs) }}\n
\n{%- endif -%}\n\n\tTOTAL:\n\t{{ currency_symbol }} {{ \"%.2f\"|format(doc.grand_total) }}\n
\n\n\n{%- if doc.payments -%}\n\nPayments:
\n{%- for payment in doc.payments -%}\n\n\t{{ payment.mode_of_payment }}:\n\t{{ currency_symbol }} {{ \"%.2f\"|format(payment.amount) }}\n
\n{%- endfor -%}\n\n\tTotal Paid:\n\t{{ currency_symbol }} {{ \"%.2f\"|format(doc.paid_amount or 0) }}\n
\n{%- if doc.change_amount > 0 -%}\n\n\tChange:\n\t{{ currency_symbol }} {{ \"%.2f\"|format(doc.change_amount) }}\n
\n{%- endif -%}\n{%- if doc.outstanding_amount and doc.outstanding_amount > 0 -%}\n\n\tBALANCE DUE:\n\t{{ currency_symbol }} {{ \"%.2f\"|format(doc.outstanding_amount) }}\n
\n{%- endif -%}\n{%- endif -%}\n\n\n",
"custom_format": 1,
"disabled": 0,
"standard": "No",
@@ -12,4 +12,4 @@
"default_print_language": "en",
"font_size": 12
}
-]
+]
\ No newline at end of file
diff --git a/pos_next/hooks.py b/pos_next/hooks.py
index f90f31b6..ca8d4293 100644
--- a/pos_next/hooks.py
+++ b/pos_next/hooks.py
@@ -219,12 +219,17 @@
"pos_next.api.sales_invoice_hooks.validate",
"pos_next.api.wallet.validate_wallet_payment"
],
+ "on_update": "pos_next.api.restaurant.on_invoice_update",
"before_cancel": "pos_next.api.sales_invoice_hooks.before_cancel",
"on_submit": [
"pos_next.realtime_events.emit_stock_update_event",
- "pos_next.api.wallet.process_loyalty_to_wallet"
+ "pos_next.api.wallet.process_loyalty_to_wallet",
+ "pos_next.api.restaurant.on_invoice_update"
+ ],
+ "on_cancel": [
+ "pos_next.realtime_events.emit_stock_update_event",
+ "pos_next.api.restaurant.on_invoice_update"
],
- "on_cancel": "pos_next.realtime_events.emit_stock_update_event",
"after_insert": "pos_next.realtime_events.emit_invoice_created_event"
},
"POS Profile": {
From 8a70e25233a8f3d410fc3fa2c58f1aa1f99cf5c7 Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Tue, 10 Mar 2026 19:56:41 +0000
Subject: [PATCH 08/25] fix: restore draft when table is clicked and force sync
to backend for kds
- Updated TableSelector.vue to check the draftsStore for an existing draft tied to the selected table. If found, it loads that draft into the cart instead of opening a blank one.
- Updated handleSaveDraft in POSSale.vue to forcefully push the draft invoice to the server via pos_next.api.invoices.update_invoice when a restaurant table is set. This bypasses the typical "offline-first" queue just for KDS orders, ensuring kitchen staff see the order immediately.
Co-authored-by: hemidirasim <25175153+hemidirasim@users.noreply.github.com>
---
POS/src/components/pos/TableSelector.vue | 27 +++++++---
POS/src/components/sale/ItemsSelector.vue | 2 +-
POS/src/composables/useItems.js | 3 +-
POS/src/pages/POSSale.vue | 64 +++++++++++++++++++++--
POS/src/stores/posDrafts.js | 6 +++
pos_next/api/items.py | 2 +-
pos_next/overrides/pricing_rule.py | 29 ----------
7 files changed, 89 insertions(+), 44 deletions(-)
diff --git a/POS/src/components/pos/TableSelector.vue b/POS/src/components/pos/TableSelector.vue
index 4f26c9a9..2739d409 100644
--- a/POS/src/components/pos/TableSelector.vue
+++ b/POS/src/components/pos/TableSelector.vue
@@ -76,12 +76,14 @@
import { ref, computed, onMounted } from "vue"
import { useRestaurantStore } from "@/stores/restaurant"
import { usePOSCartStore } from "@/stores/posCart"
+import { usePOSDraftsStore } from "@/stores/posDrafts"
import { Button, FeatherIcon } from "frappe-ui"
const emit = defineEmits(["table-selected"])
const restaurantStore = useRestaurantStore()
const cartStore = usePOSCartStore()
+const draftsStore = usePOSDraftsStore()
const selectedArea = ref(null)
@@ -107,14 +109,23 @@ onMounted(async () => {
})
const selectTable = async (table) => {
- // Set the table in the cart
- cartStore.setRestaurantTable(table)
-
- // Automatically update table status if it's empty
- if (table.status === "Empty") {
- await restaurantStore.updateTableStatus(table.name, "Occupied")
+ // Check if this table has an active draft
+ await draftsStore.loadDrafts()
+ const tableDraft = draftsStore.drafts.find(d => d.restaurant_table === table.name)
+
+ if (tableDraft) {
+ // Emit a special event so POSSale can handle draft loading gracefully
+ emit("load-table-draft", tableDraft)
+ } else {
+ // Set the table in the cart
+ cartStore.setRestaurantTable(table)
+
+ // Automatically update table status if it's empty
+ if (table.status === "Empty") {
+ await restaurantStore.updateTableStatus(table.name, "Occupied")
+ }
+
+ emit("table-selected", table)
}
-
- emit("table-selected", table)
}
diff --git a/POS/src/components/sale/ItemsSelector.vue b/POS/src/components/sale/ItemsSelector.vue
index bb06e34e..dc7698ea 100644
--- a/POS/src/components/sale/ItemsSelector.vue
+++ b/POS/src/components/sale/ItemsSelector.vue
@@ -839,7 +839,7 @@ const totalPages = computed(() => {
const SEARCH_PLACEHOLDERS = Object.freeze({
auto: __("Auto-Add ON - Type or scan barcode"),
scanner: __("Scanner ON - Enable Auto for automatic addition"),
- default: __("Search by item code, name, item group or scan barcode"),
+ default: __("Search by item code, name or scan barcode"),
})
// Sort configuration
diff --git a/POS/src/composables/useItems.js b/POS/src/composables/useItems.js
index f7fc24a5..ce07480b 100644
--- a/POS/src/composables/useItems.js
+++ b/POS/src/composables/useItems.js
@@ -67,8 +67,7 @@ export function useItems(posProfile, cartItems = ref([])) {
(item) =>
item.item_name?.toLowerCase().includes(term) ||
item.item_code?.toLowerCase().includes(term) ||
- item.barcode?.toLowerCase().includes(term) ||
- item.item_group?.toLowerCase().includes(term)
+ item.barcode?.toLowerCase().includes(term),
)
}
diff --git a/POS/src/pages/POSSale.vue b/POS/src/pages/POSSale.vue
index ec676392..8cad15a0 100644
--- a/POS/src/pages/POSSale.vue
+++ b/POS/src/pages/POSSale.vue
@@ -286,7 +286,7 @@
style="contain: layout style paint"
>
-
+
({
+ item_code: item.item_code,
+ item_name: item.item_name,
+ qty: item.quantity,
+ rate: item.rate,
+ uom: item.uom,
+ warehouse: item.warehouse,
+ posa_special_instructions: item.posa_special_instructions || "",
+ discount_amount: item.discount_amount || 0,
+ discount_percentage: item.discount_percentage || 0
+ }));
+
+ const invoiceData = {
+ doctype: "Sales Invoice",
+ pos_profile: cartStore.posProfile,
+ customer: cartStore.customer?.name || cartStore.customer,
+ restaurant_table: cartStore.restaurantTable?.name,
+ kds_status: cartStore.kdsStatus,
+ items: formattedItems,
+ is_pos: 1,
+ docstatus: 0 // Explicitly draft
+ };
+
+ await call("pos_next.api.invoices.update_invoice", { data: invoiceData });
+ // Update table status
+ await restaurantStore.updateTableStatus(cartStore.restaurantTable.name, "Occupied");
+ } catch (error) {
+ console.error("Failed to sync draft to kitchen:", error);
+ }
+ }
+
cartStore.clearCart();
// Reset cart hash when cart is saved as draft and cleared
previousCartHash = "";
@@ -2242,7 +2287,9 @@ async function handleLoadDraft(draft) {
cartStore.customer,
cartStore.posProfile,
cartStore.appliedOffers,
- cartStore.currentDraftId
+ cartStore.currentDraftId,
+ cartStore.restaurantTable?.name,
+ cartStore.kdsStatus
);
if (!saved) {
@@ -2261,6 +2308,17 @@ async function handleLoadDraft(draft) {
cartStore.setCustomer(draftData.customer);
cartStore.currentDraftId = draft.draft_id; // Set current draft ID
+ if (draftData.restaurant_table) {
+ const tables = restaurantStore.tables;
+ const table = tables.find(t => t.name === draftData.restaurant_table);
+ if (table) {
+ cartStore.setRestaurantTable(table);
+ }
+ }
+ if (draftData.kds_status) {
+ cartStore.setKdsStatus(draftData.kds_status);
+ }
+
// Rebuild incremental cache to recalculate totals
cartStore.rebuildIncrementalCache();
diff --git a/POS/src/stores/posDrafts.js b/POS/src/stores/posDrafts.js
index f3282ddd..795841aa 100644
--- a/POS/src/stores/posDrafts.js
+++ b/POS/src/stores/posDrafts.js
@@ -35,6 +35,8 @@ export const usePOSDraftsStore = defineStore("posDrafts", () => {
posProfile,
appliedOffers = [],
draftId = null,
+ restaurantTable = null,
+ kdsStatus = null
) {
if (invoiceItems.length === 0) {
showWarning(__("Cannot save an empty cart as draft"))
@@ -47,6 +49,8 @@ export const usePOSDraftsStore = defineStore("posDrafts", () => {
customer: customer,
items: invoiceItems,
applied_offers: appliedOffers, // Save applied offers
+ restaurant_table: restaurantTable,
+ kds_status: kdsStatus,
}
let savedDraft
@@ -76,6 +80,8 @@ export const usePOSDraftsStore = defineStore("posDrafts", () => {
items: draft.items || [],
customer: draft.customer,
applied_offers: draft.applied_offers || [], // Restore applied offers
+ restaurant_table: draft.restaurant_table || null,
+ kds_status: draft.kds_status || "Pending",
}
} catch (error) {
console.error("Error loading draft:", error)
diff --git a/pos_next/api/items.py b/pos_next/api/items.py
index c10f6f12..b29fffc7 100644
--- a/pos_next/api/items.py
+++ b/pos_next/api/items.py
@@ -1114,7 +1114,7 @@ def get_items(pos_profile, search_term=None, item_group=None, start=0, limit=20,
search_words = [word.strip() for word in effective_search_term.split() if word.strip()]
# Word-order independent: all words must appear somewhere in item fields
- search_text = "CONCAT(COALESCE(i.name, ''), ' ', COALESCE(i.item_name, ''), ' ', COALESCE(i.item_group, ''), ' ', COALESCE(i.description, ''))"
+ search_text = "CONCAT(COALESCE(i.name, ''), ' ', COALESCE(i.item_name, ''), ' ', COALESCE(i.description, ''))"
word_conditions = " AND ".join([f"{search_text} LIKE %s"] * len(search_words))
# Also match if barcode contains the search term
diff --git a/pos_next/overrides/pricing_rule.py b/pos_next/overrides/pricing_rule.py
index a90d71b5..3b642700 100644
--- a/pos_next/overrides/pricing_rule.py
+++ b/pos_next/overrides/pricing_rule.py
@@ -14,32 +14,6 @@
import frappe
-def _has_pos_only_column():
- """Check whether the current site's Pricing Rule table has the pos_only column.
-
- The monkey-patch in __init__.py is process-wide and affects ALL sites on the
- bench, but only sites with POS Next installed have the pos_only custom field.
- This guard prevents 'Unknown column' errors on sites that share the bench
- but don't have POS Next.
-
- Cached per-site per-worker so the DB introspection runs only once.
- """
- if not hasattr(_has_pos_only_column, "_cache"):
- _has_pos_only_column._cache = {}
-
- site = getattr(frappe.local, "site", None)
- if site in _has_pos_only_column._cache:
- return _has_pos_only_column._cache[site]
-
- try:
- result = frappe.db.has_column("Pricing Rule", "pos_only")
- except Exception:
- result = False
-
- _has_pos_only_column._cache[site] = result
- return result
-
-
def sync_pos_only_to_pricing_rules(doc, method=None):
"""Sync pos_only from Promotional Scheme to its generated Pricing Rules.
@@ -67,9 +41,6 @@ def patch_get_other_conditions(pr_utils):
def _patched_get_other_conditions(conditions, values, args):
conditions = _original_get_other_conditions(conditions, values, args)
- if not _has_pos_only_column():
- return conditions
-
doctype = args.get("doctype", "")
# POS Invoice doctype — always POS, all rules apply
if doctype in ("POS Invoice", "POS Invoice Item"):
From 02543b29797ed33481d578c6c9f6c212939b02da Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Tue, 10 Mar 2026 20:16:02 +0000
Subject: [PATCH 09/25] fix: reset cart state when switching tables and ui
updates
- Updated TableSelector to always clear the cart when a new table is selected to prevent products from different tables bleeding into each other.
- Moved the active table banner and close table button from InvoiceCart to the main POSSale grid view layout.
- Added 'All' area filter default to the TableSelector view.
Co-authored-by: hemidirasim <25175153+hemidirasim@users.noreply.github.com>
---
POS/src/components/pos/TableSelector.vue | 14 ++++++++++++--
POS/src/components/sale/InvoiceCart.vue | 10 ----------
POS/src/pages/POSSale.vue | 16 ++++++++++++++++
3 files changed, 28 insertions(+), 12 deletions(-)
diff --git a/POS/src/components/pos/TableSelector.vue b/POS/src/components/pos/TableSelector.vue
index 2739d409..16ec90cc 100644
--- a/POS/src/components/pos/TableSelector.vue
+++ b/POS/src/components/pos/TableSelector.vue
@@ -13,6 +13,13 @@
+