Skip to content

fix(rest/python): apply each discount code once and keep totals non-negative - #267

Open
sauravGit wants to merge 2 commits into
Universal-Commerce-Protocol:mainfrom
sauravGit:fix/python-discount-dedup
Open

sauravGit wants to merge 2 commits into
Universal-Commerce-Protocol:mainfrom
sauravGit:fix/python-discount-dedup

Conversation

@sauravGit

@sauravGit sauravGit commented Oct 27, 2026 •

Copy link
Copy Markdown

Fixes #266

Repeated entries in discounts.codes[] were each applied, so a fixed-amount code listed N times was subtracted N times and the total could go negative through to a completed order.

Changes:

  • Apply each code once per cart and checkout, matched case-insensitively.
  • Report repeats with a discount_code_already_applied warning at the repeated entry's path.
  • Cap fixed-amount discounts at the remaining total so totals never go below zero.
  • Add shared helpers in services/discount_rules.py used by both cart and checkout recalculation.
  • Add cart tests for repeated codes and the zero floor, plus a cart-to-checkout test with repeated codes.

…egative

Repeated entries in discounts.codes[] were each applied, so a fixed-amount code listed N times was subtracted N times and the total could go negative through to a completed order. Apply each code once, report repeats with discount_code_already_applied, and cap fixed-amount discounts at the remaining total. Adds cart and cart-to-checkout tests.
@damaz91 damaz91 added the status:needs-triage Signal that the PR is ready for human triage label Oct 27, 2026

@damaz91 damaz91 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @sauravGit for reporting #266 and putting together such a clean, well-structured fix!

Extracting the shared helpers into services/discount_rules.py keeps CartService and CheckoutService nicely aligned. In particular:

  • Capping fixed_amount discounts against the remaining grand_total before subtracting (while keeping the discount_amount > 0 guard) ensures the receipt ledger always reconciles (sum(non-total entries) == total) and avoids emitting zero-amount discount entries that would violate total.json's exclusiveMaximum: 0 constraint.
  • Preserving discounts.codes as submitted while tracking seen_codes case-insensitively ensures $.discounts.codes[i] in messages[] points to the exact repeated entry, and replace_discount_warnings cleanly prevents stale warnings from accumulating across recalculations.

A couple of small, non-blocking suggestions for optional polish if you'd like:

  • seen_codes when discount_amount == 0 (services/cart_service.py:385, services/checkout_service.py:1468): If an earlier discount has already brought grand_total to 0 (or a percentage discount truncates to 0 minor units) and a subsequent code in codes[] is also repeated (e.g., ["BIG5000", "EXTRA100", "EXTRA100"]), EXTRA100 evaluates to 0 and is omitted from discounts.applied, but its repeat still emits "Code 'EXTRA100' is already applied". Moving seen_codes.add(code.upper()) inside if discount_amount > 0: would ensure discount_code_already_applied is only emitted for codes that actually applied.
  • Direct checkout update test (cart_test.py): Adding a short test exercising PUT /checkout-sessions/{id} directly (applying a repeated code, followed by a PUT that omits discounts to verify warnings don't duplicate, and completing a $0-total checkout) would nicely complement the cart and cart-to-checkout tests.

LGTM!

@damaz91 damaz91 added status:under-review and removed status:needs-triage Signal that the PR is ready for human triage labels Oct 27, 2026
…ady applied

A repeated code was marked seen even when it contributed nothing (the running total was already zero), so its repeat was reported as already applied. Mark a code seen only once it reduces the total. Adds a direct checkout create/update/complete test with a zero total and a cart test for the zero-effect case.
@sauravGit

Copy link
Copy Markdown
Author

Thank you @sauravGit for reporting #266 and putting together such a clean, well-structured fix!

Extracting the shared helpers into services/discount_rules.py keeps CartService and CheckoutService nicely aligned. In particular:

  • Capping fixed_amount discounts against the remaining grand_total before subtracting (while keeping the discount_amount > 0 guard) ensures the receipt ledger always reconciles (sum(non-total entries) == total) and avoids emitting zero-amount discount entries that would violate total.json's exclusiveMaximum: 0 constraint.
  • Preserving discounts.codes as submitted while tracking seen_codes case-insensitively ensures $.discounts.codes[i] in messages[] points to the exact repeated entry, and replace_discount_warnings cleanly prevents stale warnings from accumulating across recalculations.

A couple of small, non-blocking suggestions for optional polish if you'd like:

  • seen_codes when discount_amount == 0 (services/cart_service.py:385, services/checkout_service.py:1468): If an earlier discount has already brought grand_total to 0 (or a percentage discount truncates to 0 minor units) and a subsequent code in codes[] is also repeated (e.g., ["BIG5000", "EXTRA100", "EXTRA100"]), EXTRA100 evaluates to 0 and is omitted from discounts.applied, but its repeat still emits "Code 'EXTRA100' is already applied". Moving seen_codes.add(code.upper()) inside if discount_amount > 0: would ensure discount_code_already_applied is only emitted for codes that actually applied.
  • Direct checkout update test (cart_test.py): Adding a short test exercising PUT /checkout-sessions/{id} directly (applying a repeated code, followed by a PUT that omits discounts to verify warnings don't duplicate, and completing a $0-total checkout) would nicely complement the cart and cart-to-checkout tests.

LGTM!

Thanks for the review, @damaz91! Both suggestions are in this commit 43950fd:

  • seen_codes.add now runs only inside if discount_amount > 0: in both services, so discount_code_already_applied is reported only for codes that actually took effect.
  • Added test_checkout_update_repeated_codes_zero_total_completes, covering a direct checkout create, an update that omits discounts, and completing a $0-total checkout.
  • Also added test_repeated_code_after_zero_effect_is_not_reported for the ["BIG", "EXTRA100", "EXTRA100"] case.

@sauravGit
sauravGit requested a review from damaz91 October 27, 2026 16:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: rest/python: repeated discount code is applied on every repeat, checkout completes with a negative total

2 participants