fix: restore coupon usage when invoices are cancelled#206
fix: restore coupon usage when invoices are cancelled#206my-dev-jour wants to merge 2 commits intoBrainWise-DEV:developfrom
Conversation
Code reviewFound 1 issue:
POSNext/pos_next/api/sales_invoice_hooks.py Lines 158 to 167 in 1e9b0c3 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
|
Addressed. I added an early return for |
engahmed1190
left a comment
There was a problem hiding this comment.
Code Review — REQUEST CHANGES ❌
Summary
Adds on_cancel hook for Sales Invoice to restore POS coupon usage via decrement_coupon_usage. Good edge case handling for return invoices and missing coupon table.
Test Results
4/5 tests pass, 1 fails. test_rollback_coupon_usage_skips_when_coupon_table_missing fails because the frappe.db.table_exists mock doesn't properly intercept the Werkzeug LocalProxy when running under bench execute.
Correctness
Logic is correct — on_cancel delegates to rollback_coupon_usage, which:
- Skips return invoices (correct — returns copy
coupon_codebut shouldn't affect usage) - Skips when coupon table doesn't exist (graceful degradation)
- Wraps
decrement_coupon_usagein try/except to not block cancellation
hooks.py change: Correctly converts on_cancel from string to list. Good.
Issue: frappe.db.commit() in decrement_coupon_usage (BLOCKING)
decrement_coupon_usage internally calls frappe.db.commit(). When called from an on_cancel hook, this commits the coupon decrement mid-transaction. If subsequent hooks or Frappe's own cancel logic fails:
- The coupon usage count is already decremented (committed)
- But the invoice cancellation rolls back
- Result: coupon usage is decremented without a corresponding cancellation — data inconsistency
This is the same issue PR #207 fixes for the increment path. The decrement path needs the same treatment.
Required Fix
Remove frappe.db.commit() from decrement_coupon_usage, or create an atomic version analogous to consume_coupon_usage from PR #207. The coupon decrement must participate in the same transaction as the invoice cancellation.
Additional Suggestions (non-blocking)
- Idempotency: No guard against double-decrement if cancel is retried.
decrement_coupon_usagecheckscoupon.used > 0before decrementing (floor at zero), which is acceptable but not ideal - Missing test: No test verifying behavior when
decrement_coupon_usageraises an exception (the try/except path). Assertfrappe.log_erroris called - Locking: No
SELECT FOR UPDATEon the decrement path — two simultaneous cancellations using the same coupon could race. Low risk (cancellations are rare) but worth noting for symmetry with #207 - Fix the test failure: Mock
frappe.dbatfrappe.local.dblevel or restructure to avoid Werkzeug LocalProxy issues
Verdict
The overall approach is correct. Remove the frappe.db.commit() from decrement_coupon_usage to maintain transactional integrity with the document's cancel operation, then this is ready to merge.
Summary
Testing
Closes #195