Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)**
Expand Down
43 changes: 34 additions & 9 deletions main/data/manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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())
{
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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)
{
Expand All @@ -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)
Expand Down
23 changes: 21 additions & 2 deletions main/data/settings.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
49 changes: 40 additions & 9 deletions zone/settings_zone.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down