feat(loyalty): global earning rate overhaul - #169
Merged
Conversation
Implement global loyalty earning with tri-state product overrides (NULL inherits, 0 disabled, >0 custom). - Added global_cashback_percent setting to DB (v39 migration). - Validated loyalty rates on settings, products APIs and CSV uploads to strictly enforce 0-100 range. - Resolved trailing whitespace and type conversion gaps (Number vs parseFloat vs Boolean). - Tests cover full inheritance path and negative payloads. Closes #81
…e custom frontend rate
…global cashback percent
… React decimal entry glitches
itsbkm
force-pushed
the
feature/loyalty-global-rate
branch
from
July 30, 2026 16:13
339e104 to
8cea66e
Compare
…rsion Rebasing this branch onto main surfaced a real collision: this PR's migration and an unrelated main-branch migration (#173's add_users_tokens_valid_after) both claimed version 39. Renumbered this one to 40 to fix that, which broke this test's hardcoded `m.version === 39` lookup — switched to finding it by name instead so a future renumber can't silently break this assertion again. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
# Conflicts: # main/db.ts
…it opt-in Three fixes on top of the global earning rate work so it can merge and so it does not change what an upgraded store pays out. Migration numbering. main gained migrations 40 (v2_cloud_defaults_and_tax_toggle) and 41 (support_ticket_outbox) while this branch was open, and this one was also 40. runMigrations() derives its target from the last array entry and throws when current > target, so a duplicate 40 either bricks startup on every upgraded install or is silently skipped and never seeds the setting. Renumbered to 42. Preserve existing cb_percent. Dropped UPDATE products SET cb_percent = NULL WHERE cb_percent = 0 Under the tri-state, 0 means "earns nothing" and NULL means "inherit the global rate". The old schema default was 0, so that statement converted essentially every product in every existing install into an inherit. It looks harmless only because the global rate seeds to 0 - the damage lands later, when an owner sets a rate and every product they had deliberately excluded silently starts paying out. "Never configured" and "deliberately zero" are indistinguishable in the old data, which is exactly why the migration must not guess. Give the owner the choice instead. GET /api/products/loyalty/global-rate-candidates counts products currently earning nothing, and POST .../apply-global-rate switches them to inherit. Settings > Loyalty surfaces the count and the action only when there is something to apply. Without this the feature is inert on every upgraded install: the owner sets a rate and nothing happens, because no product is NULL. Also reverted the schema-health suppression for the cb_percent default. Rather than silencing the detector, createSchema() keeps DEFAULT 0 so a fresh install and an upgraded one have an identical products table - SQLite cannot alter a column default without rebuilding the table. The tri-state does not depend on the default; every insert path passes cb_percent explicitly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implement global loyalty earning with tri-state product overrides.
Summary
Key Changes
settings.global_cashback_percentto 0.GET/PUT /api/settings/loyaltyto return and persistglobal_cashback_percent.Closes #81