diff --git a/docs/changelog.md b/docs/changelog.md index 957ef78b..0308873a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -6,6 +6,37 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ## [Unreleased] +### Fixed +- **Hardware Button Type: Critical Startup Bugs (2026-02-04)** + - Fixed multiple critical bugs in Hardware button type that caused issues after system reboot + - **Bug 1: Printer Deletion on Every Startup** + - **Root Cause**: Code was migrating receipt printer from printer list to server terminal on every startup, then deleting it + - **Solution**: Added check to only migrate printer if server terminal doesn't already have one configured + - **Impact**: Printers no longer disappear or get replaced with defaults after reboot + - **Bug 2: Terminal Iterator Not Reset** + - **Root Cause**: When cleaning up multiple server terminals, the iterator wasn't reset before the second processing loop + - **Solution**: Added `ti = settings->TermList()` to reset iterator after cleanup + - **Impact**: Terminal initialization now processes all terminals correctly + - **Bug 3: Multiple Terminals Marked as Server** + - **Root Cause**: `have_server` count wasn't updated after marking a terminal as server, allowing multiple terminals to be set as server in single startup + - **Solution**: Added `have_server = 1` after each `IsServer(1)` call + - **Impact**: Only one terminal is now marked as server + - **Bug 4: FindServer Priority Issue** + - **Root Cause**: `FindServer()` checked display_host match before IsServer flag, potentially returning wrong terminal + - **Solution**: Changed to two-pass approach - first find explicit server, then check display_host match + - **Impact**: Existing server terminal is always found first + - **⚠️ KNOWN ISSUE**: Duplicate "Server" displays may still appear in some configurations - investigation ongoing + - **Files modified**: `main/data/manager.cc`, `main/data/settings.cc` + +- **Tender Settings Button Type: Null Pointer and Record Navigation Bugs (2026-02-04)** + - Fixed bugs in TenderSetZone::KillRecord that could cause crashes and navigation issues + - **Changes Made**: + - Added null pointer checks before accessing record data in all sections (Discounts, Coupons, Credit Cards, Comps, Employee Meals) + - Added consistent `display_id` and `record_no` handling for all sections (previously only Discounts had this) + - **Root Cause**: KillRecord didn't check if `FindXxxByRecord()` returned nullptr before dereferencing, and sections 1-4 didn't update navigation state after deletion + - **Solution**: Added null checks and consistent record navigation handling across all tender types + - **Files modified**: `zone/settings_zone.cc` + ### Added - **🚀 Recommendations for Modern POS Transformation (2026-01-24)** - **Phase 1: Complete Current Modernization (1-2 months)** diff --git a/main/data/manager.cc b/main/data/manager.cc index b4210cda..db4245de 100644 --- a/main/data/manager.cc +++ b/main/data/manager.cc @@ -1263,21 +1263,30 @@ int StartSystem(int my_use_net) TermInfo *ti = settings->TermList(); if (have_server > 1) { + // Multiple terminals are marked as server - keep only the first one int found = 0; while (ti != nullptr) { - if (ti->display_host.size() > 0) + if (ti->IsServer()) { if (found) + { + // Clear server flag on all but the first server found ti->IsServer(0); + } else { + // First server found - update its display host and keep it ti->display_host.Set(displaystr.data()); found = 1; } } ti = ti->next; } + // Save settings after cleaning up duplicate servers + settings->Save(); + // Reset ti to the head of the list for the second loop + ti = settings->TermList(); } while (ti != nullptr) { @@ -1290,6 +1299,7 @@ int StartSystem(int my_use_net) { ti->display_host.Set(displaystr.data()); ti->IsServer(1); + have_server = 1; // Update count to prevent setting another terminal as server } else if (ti->IsServer()) { @@ -1317,6 +1327,7 @@ int StartSystem(int my_use_net) // this entry isn't explicitly set as server, but we got a match on // the display string, so we'll set it now. ti->IsServer(1); + have_server = 1; // Update count to prevent setting another terminal as server } ti = ti->next; } @@ -1486,6 +1497,7 @@ int StartSystem(int my_use_net) // Add local terminal ReportLoader("Opening Local Terminal"); + int term_count_before = settings->TermCount(); TermInfo *ti = settings->FindServer(displaystr.data()); if (ti == nullptr) { @@ -1494,17 +1506,30 @@ int StartSystem(int my_use_net) return 1; } ti->display_host.Set(displaystr.data()); + + // If FindServer created a new terminal, save settings to persist it + // This prevents creating duplicate server terminals on every restart + if (settings->TermCount() > term_count_before) + { + settings->Save(); + } - pi = settings->FindPrinterByType(PRINTER_RECEIPT); - if (pi) + // Only migrate receipt printer to server terminal if the server terminal + // does not already have a printer configured. This prevents repeatedly + // deleting printers from the printer list on every startup. + if (ti->printer_model == MODEL_NONE || ti->printer_host.empty()) { - ti->printer_host.Set(pi->host); - ti->printer_port = pi->port; - ti->printer_model = pi->model; + pi = settings->FindPrinterByType(PRINTER_RECEIPT); + if (pi) + { + ti->printer_host.Set(pi->host); + ti->printer_port = pi->port; + ti->printer_model = pi->model; - settings->Remove(pi); - delete pi; - settings->Save(); + settings->Remove(pi); + delete pi; + settings->Save(); + } } if (num_terms > 0) diff --git a/main/data/settings.cc b/main/data/settings.cc index a62a9689..62000ccc 100644 --- a/main/data/settings.cc +++ b/main/data/settings.cc @@ -4473,16 +4473,35 @@ TermInfo *Settings::FindServer(const genericChar* displaystr) TermInfo *retti = nullptr; TermInfo *ti = term_list.Head(); + // First pass: look for a terminal explicitly marked as server while (ti != nullptr) { - if (ti->IsServer() || (strcmp(displaystr, ti->display_host.Value()) == 0)) + if (ti->IsServer()) { retti = ti; - break; // exit the loop + break; } ti = ti->next; } + // Second pass: if no server found, look for matching display_host + if (retti == nullptr) + { + ti = term_list.Head(); + while (ti != nullptr) + { + if (strcmp(displaystr, ti->display_host.Value()) == 0) + { + retti = ti; + // Mark this terminal as the server since it matches our display + retti->IsServer(1); + break; + } + ti = ti->next; + } + } + + // If still no match, create a new server terminal if (retti == nullptr) { retti = new TermInfo; diff --git a/zone/settings_zone.cc b/zone/settings_zone.cc index 6082d831..5f73cd09 100644 --- a/zone/settings_zone.cc +++ b/zone/settings_zone.cc @@ -2999,35 +2999,66 @@ int TenderSetZone::KillRecord(Terminal *term, int record) default: { DiscountInfo *ds = settings->FindDiscountByRecord(record); - ds->active = 0; - if (ds->next != nullptr) - display_id = ds->next->id; - else - record_no = -1; + if (ds) + { + ds->active = 0; + if (ds->next != nullptr) + display_id = ds->next->id; + else + record_no = -1; + } return 0; } case 1: { CouponInfo *cp = settings->FindCouponByRecord(record); - cp->active = 0; + if (cp) + { + cp->active = 0; + if (cp->next != nullptr) + display_id = cp->next->id; + else + record_no = -1; + } return 0; } case 2: { CreditCardInfo *cc = settings->FindCreditCardByRecord(record); - cc->active = 0; + if (cc) + { + cc->active = 0; + if (cc->next != nullptr) + display_id = cc->next->id; + else + record_no = -1; + } return 0; } case 3: { CompInfo *cm = settings->FindCompByRecord(record); - cm->active = 0; + if (cm) + { + cm->active = 0; + if (cm->next != nullptr) + display_id = cm->next->id; + else + record_no = -1; + } return 0; } case 4: { MealInfo *mi = settings->FindMealByRecord(record); - mi->active = 0; + if (mi) + { + mi->active = 0; + if (mi->next != nullptr) + display_id = mi->next->id; + else + record_no = -1; + } return 0; } }