What's New
Stay up to date with the latest features, improvements, and fixes
What's New in Margin Master
Stay up to date with the latest features, improvements, and fixes.
MarginMaster v2026.720.41308 v2026.720.41308
July 20, 2026 15 changes-
Added safeguards to prevent future dialog occlusion issues
All modal dialog code paths now verify that the main window is not topmost before opening a dialog, and log a warning if they ever find it set. This provides a safety net at the boundary that matters most and ensures that if a future code change reintroduces the topmost timing issue, the next report will identify the specific code path responsible rather than requiring extensive log analysis.
-
Added safety check before opening modal dialogs
Added a guarantee that ensures the main window is never topmost when opening any modal dialog. This runs as a boundary check before every ShowDialog call and includes diagnostic logging to identify any future code paths that might inadvertently set the window topmost, preventing recurrence of this issue.
-
Report when strategy execution runs with no rules defined
Strategy execution now warns in the log when a strategy has zero pricing rule steps, and the execution summary explicitly states "no steps ran — no prices were changed" instead of showing a misleading "✓ COMPLETED SUCCESSFULLY" message. The log now separately reports rule step count vs. total phases to make empty strategies immediately visible.
-
Warn when active strategy is missing after restore
If the saved active strategy name doesn't exist after database load (case- and whitespace-insensitively), the app now logs a warning identifying which strategy was requested, which are available, and which fallback was selected, instead of silently switching strategies.
-
Modal dialogs no longer open behind main window
The main window's logic to bring itself to the front was temporarily setting the window as "topmost" and queuing the reset at low priority, which could be delayed for minutes on a busy system. If a modal dialog (like the Data Aging check at startup) opened during this window, it would render behind the main window while still disabling it. Since these dialogs don't appear in the taskbar or Alt+Tab, users had no way to access them, making the application appear completely frozen. The topmost flag is now reset immediately and synchronously, eliminating the vulnerable window. Added defensive checks before opening any modal to ensure the owner window is never topmost, with warnings logged if a future code change reintroduces the issue.
-
Modal dialogs no longer open behind main window
Fixed a timing issue where the main window's "bring to front" logic could leave it topmost when opening modal dialogs. When a non-topmost dialog is owned by a topmost window, it renders behind the owner while still disabling it. Since all dialogs in Margin Master are styled as tool windows without taskbar entries, an occluded dialog made the app appear completely frozen with no recovery path (not visible in taskbar or Alt+Tab). The window now immediately resets its topmost state synchronously instead of deferring the reset, eliminating the problematic window where dialogs could open.
-
Strategy backup now detects and prevents empty rule exports
The export process was reading pricing rules from database tables that were never populated, resulting in backup files that contained zero rules even while the store's pricing strategies were in active daily use. Backups now check whether any rules were actually collected and flag the file as empty if strategies were requested but nothing was found. Auto-backup to cloud now refuses to upload rule-less backups, preventing them from displacing known-good backups in cloud retention history.
-
Detect and prevent empty strategy backups from displacing good history
Fixed MM-1320, where a store's automatic strategy backups silently captured zero pricing rules for months while rules were in daily use. When the rules were eventually lost, every backup in cloud history was empty and worthless. The app now detects when a backup contains no steps, custom groups, SKU exceptions, cost breaks, min/max strategies, or minimum margins, logs a warning, writes the local file for inspection, but refuses to upload it to the cloud so it cannot push good backups out of retention.
-
Strategy restore no longer deletes rules it cannot replace
The restore process deleted all existing pricing rules first, then only re-inserted rules if the backup file contained them. Restoring an empty backup therefore wiped working strategies and wrote nothing back — an unrecoverable loss. All three sync methods (strategy steps, custom groups, and SKU exceptions) now skip deletion and log a warning when the backup has no content for that strategy, leaving existing rules untouched.
-
Prevent restores from deleting pricing rules when backup has none
Fixed a destructive restore behavior where importing a backup with an empty strategy would delete all existing live rules for that strategy and restore nothing. If a backup contains a named strategy but zero rules for it, the restore now skips that strategy entirely and leaves existing rules untouched, with a warning logged for each skipped strategy, custom group collection, and SKU exception collection.
-
Restore success message now accurately reports missing rules
The "restore completed successfully" dialog tested whether the backup contained at least one strategy by name, but ignored whether that strategy had any actual pricing rules. A backup with one named strategy carrying zero rules showed "success" with no warning. Both cloud and local restore paths now use a comprehensive check that looks for any kind of pricing rule content (steps, custom groups, SKU exceptions, cost breaks, min/max strategies, or minimum margins) and clearly warn when none were restored.
-
Strategy execution now reports when no pricing steps exist
Execution logs now distinguish between the total step count (which always includes two special system phases) and the actual pricing rule count. When a strategy has zero rules, execution now logs a warning and the completion summary displays "Strategy execution completed, but no steps ran — no prices were changed" with guidance to check that rules exist, instead of reporting plain success.
-
Active strategy fallback now logs when switching strategies
When the saved active strategy name is not found (such as after restoring a backup that didn't contain that strategy), the system now logs which strategy was requested, which one it fell back to, and warns that the requested strategy's pricing rules will not run. Strategy name matching is now case- and whitespace-insensitive to match the behavior elsewhere in the pricing system.
-
Warn users when restoring a backup with no pricing rules
The restore confirmation dialog now explicitly states when a backup file contained no pricing strategy rules, making it clear that settings and views were restored but no strategies were applied. Previously, the dialog reported plain success even when 100% of the store's pricing rules were missing from the backup.
-
Removed unreachable legacy migration code
Removed MigratePricingStrategiesAsync and two helper methods (347 lines) that were never called from anywhere in the application. This code path read from tables that were never populated (the root cause of the empty backups) and contained a scheduled deletion of the live GlobalFileConfig strategy sections 30 days post-migration that would have destroyed every pricing strategy fleet-wide if it had ever executed.
MarginMaster v2026.719.17366 v2026.719.17366
July 19, 2026 5 changes-
Added comprehensive test coverage for Compass rebuild routing
Added CompassRebuildRoutingTests with 7 tests covering both the routing logic and the safety guards, including specific tests that pin the exact failure scenario reported by store 6324 (four Compass stores losing all main tables on a fresh database).
-
Compass rebuild routing now uses consistent logic across all code paths
Fixed a "split-brain" routing issue where POS import and main table rebuild used different criteria to identify Compass stores. Import routed on UseMySqlCompass alone, but rebuild additionally required a vestigial flag (ImportEpicorDataUsingMySqlImportManager) that defaults to false on fresh databases. This sent Compass stores down the Epicor-FTP rebuild path, which queries the [Epicor] table that doesn't exist on Compass installations, causing all insert statements to fail silently.
-
Main table reconciliation no longer drops all tables when store list query fails
Added a safety guard to prevent the Store Manager reconcile from dropping every per-store main table when the authoritative store list query fails. Previously, QueryAsync returns an empty list (not null) on SQL errors like missing tables, so the null check never fired. An empty expected-store list was interpreted as "no store is expected," licensing deletion of all main tables. The new HasUsableExpectedStoreList check treats both null and empty lists as unusable, refusing to proceed with destructive operations.
-
Rebuild now aborts early when POS table is missing
Added an up-front check in RebuildAceMainTablesFromPosTableCoreAsync to verify the POS table exists before attempting index creation and inserts. Previously, a missing POS table was discovered only when DDL and insert statements threw SQL 208 errors, which were swallowed and logged as "Inserted -1 rows" (success-shaped text for a failed operation). The rebuild now aborts immediately with a clear error message.
-
Failed insert operations now logged as errors instead of successes
Changed logging behavior so that when ExecuteAsync returns -1 (indicating a caught SQL exception), it's logged as an error and post-processing is skipped, rather than reporting "Inserted -1 rows" as if the operation succeeded. This prevents the reconcile logic from counting a failed rebuild as successful and makes troubleshooting much clearer.
MarginMaster v2026.718.38252 v2026.718.38252
July 18, 2026 4 changes(2 deployments combined)-
Background service restart logic after updates
Added a new sentinel setting (AutoUpdateRestartBackgroundService) that is set only when applying an update stops a RUNNING background service. This ensures the service restarts on the new build after a successful update, but never enables a service the user deliberately disabled or never installed. The check runs outside the pending-version block so a failed update that clears the pending version doesn't strand the service stopped until reboot.
-
Automatic background service shutdown before updates
The background service executable lives in the install directory that Velopack must replace during updates. When the service was running, it held a lock on this directory, causing updates to silently fail while appearing successful—the app would restart on the old version indefinitely. Updates now automatically stop the background service before applying, making this an invariant rather than requiring each caller to remember. The service only restarts if it was actually running when stopped; a service the user never installed or deliberately disabled stays off.
-
Failed update detection and error reporting
When an update swap failed (due to locked directories from antivirus, the background service, or multiple instances), the failure was completely invisible—logged only at Warning level, below the telemetry threshold. The version mismatch after restart is the only place this failure is detectable. Failed updates now log at Error level with a clear explanation of the likely cause, ensuring telemetry captures these failures for investigation.
-
Simplified headless update service control
Removed redundant background service stop logic from the headless update installer, since ApplyUpdatesAndRestart now handles this automatically. The headless path now relies on the same invariant as interactive updates, eliminating code duplication and ensuring consistent behavior across all update paths.
