sashank1989 commited on
Commit ·
2126cb7
1
Parent(s): 5e69f93
feat: add per-item comments in POS cart with notes in cumulative KDS view
Browse files- backend/internal/handlers/external_handler.go +12 -2
- backend/internal/models/models.go +2 -1
- backend/server.log +15 -0
- backend/server_out.log +0 -0
- frontend/src/routes/(app)/kds/+page.svelte +10 -2
- frontend/src/routes/(app)/pos/+page.svelte +69 -16
- tools/mock_whatsapp.py +62 -0
- tools/setup_whatsapp_test.py +21 -0
backend/internal/handlers/external_handler.go
CHANGED
|
@@ -8,6 +8,7 @@ import (
|
|
| 8 |
"encoding/json"
|
| 9 |
"fmt"
|
| 10 |
"io"
|
|
|
|
| 11 |
"net/http"
|
| 12 |
"time"
|
| 13 |
|
|
@@ -152,10 +153,17 @@ func (h *WhatsAppHandler) sendTextMessage(to, message string) {
|
|
| 152 |
h.DB.First(&config)
|
| 153 |
|
| 154 |
if config.WhatsAppToken == "" || config.WhatsAppPhoneID == "" {
|
|
|
|
| 155 |
return
|
| 156 |
}
|
| 157 |
|
| 158 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
|
| 160 |
payload := map[string]interface{}{
|
| 161 |
"messaging_product": "whatsapp",
|
|
@@ -172,10 +180,12 @@ func (h *WhatsAppHandler) sendTextMessage(to, message string) {
|
|
| 172 |
client := &http.Client{}
|
| 173 |
resp, err := client.Do(req)
|
| 174 |
if err != nil {
|
|
|
|
| 175 |
return
|
| 176 |
}
|
| 177 |
defer resp.Body.Close()
|
| 178 |
-
io.ReadAll(resp.Body)
|
|
|
|
| 179 |
|
| 180 |
h.DB.Create(&models.NotificationLog{
|
| 181 |
Type: "whatsapp",
|
|
|
|
| 8 |
"encoding/json"
|
| 9 |
"fmt"
|
| 10 |
"io"
|
| 11 |
+
"log"
|
| 12 |
"net/http"
|
| 13 |
"time"
|
| 14 |
|
|
|
|
| 153 |
h.DB.First(&config)
|
| 154 |
|
| 155 |
if config.WhatsAppToken == "" || config.WhatsAppPhoneID == "" {
|
| 156 |
+
log.Println("[WA] Skipping: token or phone_id empty")
|
| 157 |
return
|
| 158 |
}
|
| 159 |
|
| 160 |
+
baseURL := "https://graph.facebook.com/v18.0"
|
| 161 |
+
if config.WhatsAppBaseURL != "" {
|
| 162 |
+
baseURL = config.WhatsAppBaseURL
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
url := fmt.Sprintf("%s/%s/messages", baseURL, config.WhatsAppPhoneID)
|
| 166 |
+
log.Printf("[WA] Sending to %s via %s", to, url)
|
| 167 |
|
| 168 |
payload := map[string]interface{}{
|
| 169 |
"messaging_product": "whatsapp",
|
|
|
|
| 180 |
client := &http.Client{}
|
| 181 |
resp, err := client.Do(req)
|
| 182 |
if err != nil {
|
| 183 |
+
log.Printf("[WA] HTTP error: %v", err)
|
| 184 |
return
|
| 185 |
}
|
| 186 |
defer resp.Body.Close()
|
| 187 |
+
body, _ := io.ReadAll(resp.Body)
|
| 188 |
+
log.Printf("[WA] Response: %d %s", resp.StatusCode, string(body))
|
| 189 |
|
| 190 |
h.DB.Create(&models.NotificationLog{
|
| 191 |
Type: "whatsapp",
|
backend/internal/models/models.go
CHANGED
|
@@ -94,8 +94,9 @@ type RestaurantConfig struct {
|
|
| 94 |
PrinterIP string `json:"printer_ip" gorm:"type:text"`
|
| 95 |
PrinterPort int `json:"printer_port" gorm:"type:integer;default:9100"`
|
| 96 |
KOTPrinterIP string `json:"kot_printer_ip" gorm:"type:text"`
|
| 97 |
-
WhatsAppToken
|
| 98 |
WhatsAppPhoneID string `json:"-" gorm:"type:text"`
|
|
|
|
| 99 |
SwiggyAPIKey string `json:"-" gorm:"type:text"`
|
| 100 |
ZomatoAPIKey string `json:"-" gorm:"type:text"`
|
| 101 |
|
|
|
|
| 94 |
PrinterIP string `json:"printer_ip" gorm:"type:text"`
|
| 95 |
PrinterPort int `json:"printer_port" gorm:"type:integer;default:9100"`
|
| 96 |
KOTPrinterIP string `json:"kot_printer_ip" gorm:"type:text"`
|
| 97 |
+
WhatsAppToken string `json:"-" gorm:"type:text"`
|
| 98 |
WhatsAppPhoneID string `json:"-" gorm:"type:text"`
|
| 99 |
+
WhatsAppBaseURL string `json:"whatsapp_base_url" gorm:"type:text"` // Override for testing (default: https://graph.facebook.com/v18.0)
|
| 100 |
SwiggyAPIKey string `json:"-" gorm:"type:text"`
|
| 101 |
ZomatoAPIKey string `json:"-" gorm:"type:text"`
|
| 102 |
|
backend/server.log
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
2026/06/19 01:04:27 === HSS POS System ===
|
| 2 |
+
2026/06/19 01:04:27 Starting server...
|
| 3 |
+
2026/06/19 01:04:27 [DB] Running auto-migrations...
|
| 4 |
+
2026/06/19 01:04:27 [DB] Indexes created successfully
|
| 5 |
+
2026/06/19 01:04:27 [DB] SQLite database initialized successfully at ./data/restaurant.db
|
| 6 |
+
2026/06/19 01:04:27 [DB] Seed data loaded successfully
|
| 7 |
+
2026/06/19 01:04:27 [WS] WebSocket hub started
|
| 8 |
+
2026/06/19 01:04:27 Server starting on http://localhost:8080
|
| 9 |
+
2026/06/19 01:04:27 API docs: http://localhost:8080/api/v1
|
| 10 |
+
2026/06/19 01:04:27 WebSocket: ws://localhost:8080/ws
|
| 11 |
+
2026/06/19 01:04:27 Environment: development
|
| 12 |
+
2026/06/19 01:04:39 [WA] Sending to 919876543210 via http://127.0.0.1:9876/1234567890/messages
|
| 13 |
+
2026/06/19 01:04:39 [WA] HTTP error: Post "http://127.0.0.1:9876/1234567890/messages": EOF
|
| 14 |
+
2026/06/19 01:06:15 [WA] Sending to 919876543210 via http://127.0.0.1:9876/1234567890/messages
|
| 15 |
+
2026/06/19 01:06:15 [WA] Response: 200 {"messaging_product": "whatsapp", "contacts": [{"input": "919876543210", "wa_id": "919876543210"}], "messages": [{"id": "wamid.mock_1781811375"}]}
|
backend/server_out.log
ADDED
|
File without changes
|
frontend/src/routes/(app)/kds/+page.svelte
CHANGED
|
@@ -86,8 +86,10 @@
|
|
| 86 |
if (item.is_cancelled) continue;
|
| 87 |
if (item.status === 'ready' || kot.status === 'ready') continue;
|
| 88 |
const name = item.product_name || item.name || 'Item';
|
| 89 |
-
|
| 90 |
-
const
|
|
|
|
|
|
|
| 91 |
entry.total += item.quantity;
|
| 92 |
if (cat === 'dining') entry.dine += item.quantity;
|
| 93 |
else entry.parcel += item.quantity;
|
|
@@ -288,6 +290,9 @@
|
|
| 288 |
<span class="text-sm font-medium text-gray-800 truncate mr-2">{item.name}</span>
|
| 289 |
<span class="flex-shrink-0 bg-amber-100 text-amber-800 text-xs font-bold rounded-full px-2.5 py-0.5">x{item.total}</span>
|
| 290 |
</div>
|
|
|
|
|
|
|
|
|
|
| 291 |
<div class="flex gap-2 mt-1">
|
| 292 |
{#if item.dine > 0}
|
| 293 |
<span class="text-[11px] text-blue-600 font-medium">Dine {item.dine}</span>
|
|
@@ -326,6 +331,9 @@
|
|
| 326 |
<span class="text-sm font-medium text-gray-800 truncate mr-2">{item.name}</span>
|
| 327 |
<span class="flex-shrink-0 bg-amber-100 text-amber-800 text-xs font-bold rounded-full px-2.5 py-0.5">x{item.total}</span>
|
| 328 |
</div>
|
|
|
|
|
|
|
|
|
|
| 329 |
<div class="flex gap-2 mt-0.5">
|
| 330 |
{#if item.dine > 0}
|
| 331 |
<span class="text-[11px] text-blue-600 font-medium">Dine {item.dine}</span>
|
|
|
|
| 86 |
if (item.is_cancelled) continue;
|
| 87 |
if (item.status === 'ready' || kot.status === 'ready') continue;
|
| 88 |
const name = item.product_name || item.name || 'Item';
|
| 89 |
+
const notes = item.notes || '';
|
| 90 |
+
const key = notes ? `${name}||${notes}` : name;
|
| 91 |
+
if (!itemMap.has(key)) itemMap.set(key, { name, notes, total: 0, dine: 0, parcel: 0 });
|
| 92 |
+
const entry = itemMap.get(key);
|
| 93 |
entry.total += item.quantity;
|
| 94 |
if (cat === 'dining') entry.dine += item.quantity;
|
| 95 |
else entry.parcel += item.quantity;
|
|
|
|
| 290 |
<span class="text-sm font-medium text-gray-800 truncate mr-2">{item.name}</span>
|
| 291 |
<span class="flex-shrink-0 bg-amber-100 text-amber-800 text-xs font-bold rounded-full px-2.5 py-0.5">x{item.total}</span>
|
| 292 |
</div>
|
| 293 |
+
{#if item.notes}
|
| 294 |
+
<p class="text-[11px] text-amber-600 italic truncate mt-0.5">"{item.notes}"</p>
|
| 295 |
+
{/if}
|
| 296 |
<div class="flex gap-2 mt-1">
|
| 297 |
{#if item.dine > 0}
|
| 298 |
<span class="text-[11px] text-blue-600 font-medium">Dine {item.dine}</span>
|
|
|
|
| 331 |
<span class="text-sm font-medium text-gray-800 truncate mr-2">{item.name}</span>
|
| 332 |
<span class="flex-shrink-0 bg-amber-100 text-amber-800 text-xs font-bold rounded-full px-2.5 py-0.5">x{item.total}</span>
|
| 333 |
</div>
|
| 334 |
+
{#if item.notes}
|
| 335 |
+
<p class="text-[11px] text-amber-600 italic truncate mt-0.5">"{item.notes}"</p>
|
| 336 |
+
{/if}
|
| 337 |
<div class="flex gap-2 mt-0.5">
|
| 338 |
{#if item.dine > 0}
|
| 339 |
<span class="text-[11px] text-blue-600 font-medium">Dine {item.dine}</span>
|
frontend/src/routes/(app)/pos/+page.svelte
CHANGED
|
@@ -61,6 +61,7 @@
|
|
| 61 |
|
| 62 |
// Mobile cart visibility
|
| 63 |
let mobileCartOpen = $state(false);
|
|
|
|
| 64 |
|
| 65 |
// ── Load menu ──
|
| 66 |
onMount(async () => {
|
|
@@ -417,15 +418,41 @@
|
|
| 417 |
onclick={() => cartStore.updateQuantity(item.cartId, item.quantity + 1)}
|
| 418 |
>+</button>
|
| 419 |
</div>
|
| 420 |
-
<
|
| 421 |
-
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 428 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 429 |
</div>
|
| 430 |
{/each}
|
| 431 |
{/if}
|
|
@@ -535,15 +562,41 @@
|
|
| 535 |
onclick={() => cartStore.updateQuantity(item.cartId, item.quantity + 1)}
|
| 536 |
>+</button>
|
| 537 |
</div>
|
| 538 |
-
<
|
| 539 |
-
|
| 540 |
-
|
| 541 |
-
|
| 542 |
-
|
| 543 |
-
|
| 544 |
-
|
| 545 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 546 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 547 |
</div>
|
| 548 |
{/each}
|
| 549 |
</div>
|
|
|
|
| 61 |
|
| 62 |
// Mobile cart visibility
|
| 63 |
let mobileCartOpen = $state(false);
|
| 64 |
+
let editingNoteId = $state(null);
|
| 65 |
|
| 66 |
// ── Load menu ──
|
| 67 |
onMount(async () => {
|
|
|
|
| 418 |
onclick={() => cartStore.updateQuantity(item.cartId, item.quantity + 1)}
|
| 419 |
>+</button>
|
| 420 |
</div>
|
| 421 |
+
<div class="flex items-center gap-1">
|
| 422 |
+
<button
|
| 423 |
+
class="p-2 text-gray-400 hover:text-amber-600 transition-colors {item.notes ? 'text-amber-500' : ''}"
|
| 424 |
+
onclick={() => { editingNoteId = editingNoteId === item.cartId ? null : item.cartId; }}
|
| 425 |
+
title="Add note"
|
| 426 |
+
>
|
| 427 |
+
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
| 428 |
+
<path stroke-linecap="round" stroke-linejoin="round" d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z" />
|
| 429 |
+
</svg>
|
| 430 |
+
</button>
|
| 431 |
+
<button
|
| 432 |
+
class="p-2 text-gray-400 hover:text-pos-danger transition-colors"
|
| 433 |
+
onclick={() => cartStore.removeItem(item.cartId)}
|
| 434 |
+
>
|
| 435 |
+
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
| 436 |
+
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
| 437 |
+
</svg>
|
| 438 |
+
</button>
|
| 439 |
+
</div>
|
| 440 |
</div>
|
| 441 |
+
{#if item.notes && editingNoteId !== item.cartId}
|
| 442 |
+
<p class="mt-1 text-xs text-amber-600 italic truncate">"{item.notes}"</p>
|
| 443 |
+
{/if}
|
| 444 |
+
{#if editingNoteId === item.cartId}
|
| 445 |
+
<div class="mt-2">
|
| 446 |
+
<input
|
| 447 |
+
type="text"
|
| 448 |
+
class="w-full px-2 py-1.5 text-xs border border-amber-300 rounded-md focus:outline-none focus:ring-1 focus:ring-amber-400 bg-amber-50"
|
| 449 |
+
placeholder="e.g. less spicy, no onion, extra cheese..."
|
| 450 |
+
value={item.notes}
|
| 451 |
+
oninput={(e) => cartStore.setItemNotes(item.cartId, e.target.value)}
|
| 452 |
+
onkeydown={(e) => { if (e.key === 'Enter') editingNoteId = null; }}
|
| 453 |
+
/>
|
| 454 |
+
</div>
|
| 455 |
+
{/if}
|
| 456 |
</div>
|
| 457 |
{/each}
|
| 458 |
{/if}
|
|
|
|
| 562 |
onclick={() => cartStore.updateQuantity(item.cartId, item.quantity + 1)}
|
| 563 |
>+</button>
|
| 564 |
</div>
|
| 565 |
+
<div class="flex items-center gap-1">
|
| 566 |
+
<button
|
| 567 |
+
class="p-2 text-gray-400 hover:text-amber-600 {item.notes ? 'text-amber-500' : ''}"
|
| 568 |
+
onclick={() => { editingNoteId = editingNoteId === item.cartId ? null : item.cartId; }}
|
| 569 |
+
title="Add note"
|
| 570 |
+
>
|
| 571 |
+
<svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
| 572 |
+
<path stroke-linecap="round" stroke-linejoin="round" d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z" />
|
| 573 |
+
</svg>
|
| 574 |
+
</button>
|
| 575 |
+
<button
|
| 576 |
+
class="p-2 text-gray-400 hover:text-pos-danger"
|
| 577 |
+
onclick={() => cartStore.removeItem(item.cartId)}
|
| 578 |
+
>
|
| 579 |
+
<svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
| 580 |
+
<path stroke-linecap="round" stroke-linejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
| 581 |
+
</svg>
|
| 582 |
+
</button>
|
| 583 |
+
</div>
|
| 584 |
</div>
|
| 585 |
+
{#if item.notes && editingNoteId !== item.cartId}
|
| 586 |
+
<p class="mt-1 text-xs text-amber-600 italic truncate">"{item.notes}"</p>
|
| 587 |
+
{/if}
|
| 588 |
+
{#if editingNoteId === item.cartId}
|
| 589 |
+
<div class="mt-2">
|
| 590 |
+
<input
|
| 591 |
+
type="text"
|
| 592 |
+
class="w-full px-3 py-2 text-sm border border-amber-300 rounded-lg focus:outline-none focus:ring-1 focus:ring-amber-400 bg-amber-50"
|
| 593 |
+
placeholder="e.g. less spicy, no onion, extra cheese..."
|
| 594 |
+
value={item.notes}
|
| 595 |
+
oninput={(e) => cartStore.setItemNotes(item.cartId, e.target.value)}
|
| 596 |
+
onkeydown={(e) => { if (e.key === 'Enter') editingNoteId = null; }}
|
| 597 |
+
/>
|
| 598 |
+
</div>
|
| 599 |
+
{/if}
|
| 600 |
</div>
|
| 601 |
{/each}
|
| 602 |
</div>
|
tools/mock_whatsapp.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Mock Meta WhatsApp Graph API Server
|
| 3 |
+
Simulates https://graph.facebook.com/v18.0/{phone_id}/messages
|
| 4 |
+
Uses Flask for robust HTTP handling with Go clients.
|
| 5 |
+
"""
|
| 6 |
+
import sys
|
| 7 |
+
import json
|
| 8 |
+
from datetime import datetime
|
| 9 |
+
from http.server import HTTPServer, BaseHTTPRequestHandler
|
| 10 |
+
|
| 11 |
+
class MockWhatsAppAPI(BaseHTTPRequestHandler):
|
| 12 |
+
protocol_version = "HTTP/1.1"
|
| 13 |
+
|
| 14 |
+
def do_POST(self):
|
| 15 |
+
content_length = int(self.headers.get('Content-Length', 0))
|
| 16 |
+
body = self.rfile.read(content_length) if content_length > 0 else b''
|
| 17 |
+
|
| 18 |
+
try:
|
| 19 |
+
data = json.loads(body) if body else {}
|
| 20 |
+
except (json.JSONDecodeError, ValueError):
|
| 21 |
+
data = {"raw": body.decode('utf-8', errors='replace')}
|
| 22 |
+
|
| 23 |
+
# Print the outbound message
|
| 24 |
+
to = data.get('to', '?') if isinstance(data, dict) else '?'
|
| 25 |
+
msg_type = data.get('type', '?') if isinstance(data, dict) else '?'
|
| 26 |
+
|
| 27 |
+
print(f"[{datetime.now().strftime('%H:%M:%S')}] -> To: {to} | Type: {msg_type} | Path: {self.path}", flush=True)
|
| 28 |
+
|
| 29 |
+
if isinstance(data, dict) and data.get('type') == 'text':
|
| 30 |
+
text_body = data.get('text', {}).get('body', '')
|
| 31 |
+
lines = text_body.split('\n')
|
| 32 |
+
for line in lines[:5]:
|
| 33 |
+
print(f" {line}", flush=True)
|
| 34 |
+
if len(lines) > 5:
|
| 35 |
+
print(f" ... ({len(lines)} lines total)", flush=True)
|
| 36 |
+
|
| 37 |
+
# Respond like Meta
|
| 38 |
+
response = json.dumps({
|
| 39 |
+
"messaging_product": "whatsapp",
|
| 40 |
+
"contacts": [{"input": to, "wa_id": to}],
|
| 41 |
+
"messages": [{"id": f"wamid.mock_{int(datetime.now().timestamp())}"}]
|
| 42 |
+
}).encode('utf-8')
|
| 43 |
+
|
| 44 |
+
self.send_response(200)
|
| 45 |
+
self.send_header('Content-Type', 'application/json')
|
| 46 |
+
self.send_header('Content-Length', str(len(response)))
|
| 47 |
+
self.send_header('Connection', 'close')
|
| 48 |
+
self.end_headers()
|
| 49 |
+
self.wfile.write(response)
|
| 50 |
+
|
| 51 |
+
def log_message(self, format, *args):
|
| 52 |
+
pass # Suppress default access logs
|
| 53 |
+
|
| 54 |
+
if __name__ == '__main__':
|
| 55 |
+
port = 9876
|
| 56 |
+
server = HTTPServer(('127.0.0.1', port), MockWhatsAppAPI)
|
| 57 |
+
print(f"Mock WhatsApp API on http://127.0.0.1:{port}", flush=True)
|
| 58 |
+
print("Waiting for messages...", flush=True)
|
| 59 |
+
try:
|
| 60 |
+
server.serve_forever()
|
| 61 |
+
except KeyboardInterrupt:
|
| 62 |
+
print("Stopped.")
|
tools/setup_whatsapp_test.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sqlite3
|
| 2 |
+
conn = sqlite3.connect(r'C:\Users\SSASHANK\restaurant-pos\backend\data\restaurant.db')
|
| 3 |
+
|
| 4 |
+
# Check if notification_logs table exists
|
| 5 |
+
cursor = conn.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='notification_logs'")
|
| 6 |
+
table = cursor.fetchone()
|
| 7 |
+
print(f"Table exists: {table is not None}")
|
| 8 |
+
|
| 9 |
+
# Check config values server would read
|
| 10 |
+
cursor = conn.execute("SELECT whats_app_token, whats_app_phone_id, whats_app_base_url FROM restaurant_configs LIMIT 1")
|
| 11 |
+
row = cursor.fetchone()
|
| 12 |
+
print(f"Token: '{row[0]}'")
|
| 13 |
+
print(f"PhoneID: '{row[1]}'")
|
| 14 |
+
print(f"BaseURL: '{row[2]}'")
|
| 15 |
+
|
| 16 |
+
# Check if any products exist (for menu)
|
| 17 |
+
cursor = conn.execute("SELECT COUNT(*) FROM products WHERE is_active=1 AND is_available=1")
|
| 18 |
+
count = cursor.fetchone()[0]
|
| 19 |
+
print(f"Active products: {count}")
|
| 20 |
+
|
| 21 |
+
conn.close()
|