fix(orders): record discount usage against the table the code actually came from - #1717
Open
MOHITKOURAV01 wants to merge 1 commit into
Open
Conversation
A discount code resolves from either the coupons table or the promo_codes
table. Both use INT AUTO_INCREMENT primary keys starting at 1, so their id
spaces overlap completely, but the usage-tracking block treated whatever id it
held as a promo_codes id:
UPDATE promo_codes SET usage_count = usage_count + 1 WHERE id = ?
INSERT INTO promo_usage (promo_id, ...) VALUES (?, ...)
Redeeming coupon id 3 therefore incremented promo id 3 and filed a promo_usage
row for a promo that was never used, consuming that campaign's budget and
tripping its per-user limits. Both writes ended in .catch(() => {}), so none of
it surfaced. The paired recordCouponUsage call had the mirror-image problem,
bumping coupons.used_count for a promo_codes redemption.
couponService already reports which table a code came from via isPromoTable;
the order flow was discarding it. It is now carried as appliedPromoSource and
the routing decision lives in a new discountUsageService, testable apart from
order creation. A code that cannot be attributed to a source is logged and left
alone rather than guessed at - writing to the wrong ledger is worse than not
writing. Both failure paths log instead of swallowing.
Closes AnthropicBots#1712
🔍 Quality Gate Report✅ All quality gates passed!
|
🤖 AI Code Review🔴 Score: 50/100 | AI review unavailable at this time. Automated AI review — a human maintainer will also review. |
|
@MOHITKOURAV01 is attempting to deploy a commit to the Bhuvansh's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
💡 Suggested reviewers based on relevant file history: @Aditya8369, @Pcmhacker-hero |
🟡 PR Health Score: 55/100This PR's health score is below the 75/100 threshold for a healthy label.
Improving these signals will help reviewers engage faster and raise your score. 💪 |
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.
What this fixes
Closes #1712
A discount code can resolve from either of two independent tables —
coupons(ledger:coupon_usage) orpromo_codes(ledger:promo_usage). Both useINT AUTO_INCREMENTprimary keys starting at 1, so their id spaces overlap completely. The usage-tracking block increateOrderService()treated whatever id it was holding as apromo_codesid:Redeeming coupon id 3 incremented promo id 3's
usage_countand filed apromo_usagerow for a promo nobody used. Both writes end in.catch(() => {}), so nothing surfaced anywhere.The paired
recordCouponUsage()call above it has the mirror-image problem — it runsUPDATE coupons SET used_count = used_count + 1for a promo_codes redemption. So a single redemption wrote to three tables, two of them wrong.couponService.validateCoupon()has always reported the answer —isPromoTableon the returned object — and the order flow was throwing it away at line 466.What changed
New:
backend/services/discountUsageService.jsThe single place that decides which ledger a redemption belongs to:
resolveDiscountSource(promo, declaredSource)— trusts an explicitly declared source first, thenisPromoTable, then infers fromdiscount_type/discount_value(columns only promo rows carry). Returnsnullwhen there is genuinely nothing to go on.recordDiscountUsage(connection, params)— routes topromo_codes+promo_usage, or delegates tocouponService.recordCouponUsage()forcoupons. Returns the source it wrote to.backend/services/order.service.jsappliedPromoSourcealongsideappliedPromo, set fromcouponVal.coupon.isPromoTableon the coupon path and topromo_codeson the legacyvalidatePromo()path.recordDiscountUsage()call.Deliberate choices
.catch(() => {})swallows are replaced withlogger.error. Neither still fails the order — the order is already paid for — but a broken ledger write is now visible.promo_usage.user_idneeds an identity a guest does not have.createOrderServiceis long enough that the branch could not be tested without standing up a full order.Tests
New
backend/tests/discountUsageService.test.js, 18 cases:promo_codesorpromo_usage, and vice-versaorder.service.jsitself, pinning that the directUPDATE promo_codes/INSERT INTO promo_usagestatements are gone and that the source comes from the validator rather than from the idRan the surrounding suites (
order,checkout,promo,coupon,cart— 474 tests) with no new failures.cartBulkSelection.test.jshas one failure that reproduces unchanged onmainand is unrelated to this branch.Scope
backend/services/order.service.js, one new service, one new test file. No schema, route or frontend changes.CI note
Syntax check is red on this branch, and on every open PR in the repo right now. The single failure is inherited from
main:No file this PR touches is implicated —
shop.jsis untouched here. It is tracked as #1696 with a fix open in #1701. Because Syntax check gates the rest of the workflow, Backend tests and Server boots reportskippingrather than running; both suites pass locally on this branch.The red Vercel check is the repo-wide
Authorization required to deploy(teambhuvanshs-projects) and is unrelated to this change.