Skip to content

Conversation

@UmakanthKaspa
Copy link

@UmakanthKaspa UmakanthKaspa commented Dec 30, 2025

closes: #33

https://drive.google.com/file/d/16QN09a5FopthD44UcVBzv6OSRbfNHlU7/view?usp=sharing

Summary by CodeRabbit

  • New Features

    • Apply coupon codes at checkout for percentage/flat discounts or free tickets (including optional free add‑ons); totals, taxes and submission reflect applied coupons. Real‑time validation, apply/remove flow, and updated booking summary display.
  • Administration

    • New Coupon management (create/manage codes, types, event/category scope, active flag, usage limits, usage tracking).
  • Tests

    • Comprehensive integration and API tests covering discounts, free‑ticket scenarios, usage limits and validation.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 30, 2025

📝 Walkthrough

Walkthrough

Adds coupon code support end-to-end: new Coupon Code and Coupon Free Add-on doctypes and models, API validation endpoint, booking model logic to apply discounts/free tickets (with tax recalculation), frontend UI for applying/removing coupons, and tests covering coupon scenarios.

Changes

Cohort / File(s) Summary
API
buzz/api.py
Added validate_coupon(coupon_code: str, event: str) -> dict. Updated process_booking(...) signature to accept coupon_code and persist it to bookings.
Coupon DocType & Model
buzz/ticketing/doctype/coupon_code/coupon_code.json, buzz/ticketing/doctype/coupon_code/coupon_code.py, buzz/ticketing/doctype/coupon_code/coupon_code.js
New DocType + server model: coupon types (Free Tickets / Discount), scope (event/category/global), auto-generated code, validation (scope, discount caps), usage tracking, helpers (times_used, free_tickets_claimed). Client script adds event-scoped queries for ticket_type and add_on fields.
Coupon Free Add-on DocType
buzz/ticketing/doctype/coupon_free_add_on/coupon_free_add_on.json, buzz/ticketing/doctype/coupon_free_add_on/coupon_free_add_on.py
New child DocType and minimal Document class to reference free add-ons for free-ticket coupons.
Event Booking DocType & Model
buzz/ticketing/doctype/event_booking/event_booking.json, buzz/ticketing/doctype/event_booking/event_booking.py
Added coupon_code (Link) and discount_amount (Currency, read-only) and tax_label. Added apply_coupon_if_applicable() called from validate() to apply discount or free-ticket logic and recompute taxes; booking stores coupon usage info.
Frontend — Booking Form
dashboard/src/components/BookingForm.vue
UI for entering/applying/removing coupon codes, new validate_coupon resource, coupon state and computed discount/free-ticket logic, tax/totals recomputed post-discount, includes coupon_code in submission payload.
Frontend — Booking Summary
dashboard/src/components/BookingSummary.vue
New props discountAmount, couponApplied, couponType; shows Discount line and updates visibility logic when coupons alter item visibility.
Tests
buzz/ticketing/doctype/coupon_code/test_coupon_code.py
New integration tests covering percentage/flat discounts, usage limits, free tickets (multi/partial/claimed tracking), free add-ons, validation rules (scope/inactive/percentage caps), auto-generated codes, and API validation responses.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant User as User/Client
  participant UI as BookingForm (frontend)
  participant API as buzz.api
  participant Booking as EventBooking model
  participant DB as Database

  User->>UI: Enter coupon code & Apply
  UI->>API: validate_coupon(code, event)
  API->>DB: Fetch coupon by code
  API->>API: Check active, scope, usage availability
  alt invalid
    API-->>UI: { valid: false, error }
    UI-->>User: show error
  else valid
    API-->>UI: { valid: true, coupon_type, details }
    UI->>UI: compute discount/free-ticket impact & update totals
    UI-->>User: show adjusted totals
  end

  User->>UI: Submit booking (includes coupon_code)
  UI->>API: process_booking(payload with coupon_code)
  API->>Booking: create EventBooking -> validate()
  Booking->>DB: load coupon usage & related bookings/attendees
  alt Discount coupon
    Booking->>Booking: compute discount_amount from net -> set discount_amount
  else Free Tickets coupon
    Booking->>Booking: determine eligible attendees, mark ticket/add-on discounts, update free_tickets_claimed
  end
  Booking->>Booking: recalc tax on post-discount total
  Booking-->>DB: persist booking with coupon tracked
  Booking-->>API: return confirmation
  API-->>UI: return booking confirmation
  UI-->>User: show confirmation
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 I found a code beneath a tree,

Free seats and discounts set me free.
I hopped to apply, the totals shrank,
Add-ons smiled and taxes sank.
Hooray — bookings bloom like cloverry!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.66% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main change—adding coupon code functionality for both free tickets and discounts—and is clear and specific.
Linked Issues check ✅ Passed All coding requirements from issue #33 are met: two coupon types (Free Tickets and Discount) with scope rules, usage limits for discounts, free add-ons for free tickets, coupon tracking in bookings, comprehensive unit tests, and frontend integration.
Out of Scope Changes check ✅ Passed All changes directly support coupon functionality; minor text formatting updates in email templates are appropriate maintenance within the scope of the booking system updates.
✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
buzz/ticketing/doctype/coupon_code/coupon_code.py (1)

41-44: Consider validating discount_value is positive.

The percentage cap at 100% is correct, but there's no validation preventing negative or zero discount values, which would be invalid for both percentage and flat amount discounts.

🔎 Proposed enhancement
 def validate_discount_value(self):
+    if self.coupon_type == "Discount" and self.discount_value <= 0:
+        frappe.throw(_("Discount value must be greater than 0"))
     if self.coupon_type == "Discount" and self.discount_type == "Percentage":
         if self.discount_value > 100:
             frappe.throw(_("Percentage discount cannot exceed 100%"))
buzz/ticketing/doctype/coupon_code/test_coupon_code.py (1)

17-42: Consider adding tearDown for test cleanup.

The setUp method creates test fixtures but doesn't clean them up. While Frappe's test framework may handle this, explicitly deleting created documents in tearDown ensures test isolation and prevents test pollution across runs.

🔎 Proposed tearDown method
def tearDown(self):
    """Clean up test fixtures."""
    if hasattr(self, 'test_add_on') and frappe.db.exists("Ticket Add-on", self.test_add_on.name):
        frappe.delete_doc("Ticket Add-on", self.test_add_on.name, force=True)
    if hasattr(self, 'test_ticket_type') and frappe.db.exists("Event Ticket Type", self.test_ticket_type.name):
        frappe.delete_doc("Event Ticket Type", self.test_ticket_type.name, force=True)
buzz/ticketing/doctype/coupon_code/coupon_code.json (1)

63-70: Consider adding a link filter for ticket_type based on selected event.

The ticket_type Link field doesn't filter by the selected event, allowing users to potentially select a ticket type from a different event. Adding a get_query filter would improve UX.

This can be done by adding a client script or using link_filters property:

{
  "fieldname": "ticket_type",
  "fieldtype": "Link",
  "label": "Ticket Type",
  "options": "Event Ticket Type",
  "depends_on": "eval:doc.coupon_type == 'Free Tickets'",
  "mandatory_depends_on": "eval:doc.coupon_type == 'Free Tickets'",
  "link_filters": "[[\"Event Ticket Type\", \"event\", \"=\", \"eval:doc.event\"]]"
}

Alternatively, add validation in the Python controller to reject mismatched ticket types.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1cc63bf and 23a5cab.

📒 Files selected for processing (13)
  • buzz/api.py
  • buzz/ticketing/doctype/coupon_code/__init__.py
  • buzz/ticketing/doctype/coupon_code/coupon_code.js
  • buzz/ticketing/doctype/coupon_code/coupon_code.json
  • buzz/ticketing/doctype/coupon_code/coupon_code.py
  • buzz/ticketing/doctype/coupon_code/test_coupon_code.py
  • buzz/ticketing/doctype/coupon_free_add_on/__init__.py
  • buzz/ticketing/doctype/coupon_free_add_on/coupon_free_add_on.json
  • buzz/ticketing/doctype/coupon_free_add_on/coupon_free_add_on.py
  • buzz/ticketing/doctype/event_booking/event_booking.json
  • buzz/ticketing/doctype/event_booking/event_booking.py
  • dashboard/src/components/BookingForm.vue
  • dashboard/src/components/BookingSummary.vue
🧰 Additional context used
📓 Path-based instructions (2)
**/*.vue

📄 CodeRabbit inference engine (.github/instructions/frappe-ui.instructions.md)

**/*.vue: Use createResource() from frappe-ui to make backend API calls with configuration for url, params, and methods like fetch() and submit() in Vue components
Use createDocumentResource() to manage individual document records with support for whitelisted methods, events (onError, onSuccess, transform), and methods like reload(), setValue(), delete(), and custom whitelisted method calls
Use useList() hook to create a list resource for fetching records of a DocType from Frappe backend with options for fields, filters, orderBy, pageLength, caching, and events (onError, onSuccess, transform)
Use ListView component with props columns, rows, row-key, and options to render tabular data with support for selection, tooltips, resizable columns, and custom rendering slots
Define ListView columns with required label and key properties, optional width (as multiplier or px/rem), align (start/left, center/middle, end/right), and custom attributes for rendering
Define ListView rows with unique identifier matching the row-key prop, and field values as strings or objects with label property for custom rendering
For grouped ListView rows, structure data as array of objects with group string, collapsed boolean, and rows array containing row objects
Use Badge component with props variant ('solid', 'subtle', 'outline', 'ghost'), theme ('gray', 'blue', 'green', 'orange', 'red'), and size ('sm', 'md', 'lg') for displaying status indicators
Use FormControl component with type prop ('text', 'number', 'email', 'date', 'password', 'search', 'textarea', 'select', 'autocomplete', 'checkbox'), size ('sm', 'md', 'lg', 'xl'), variant ('subtle', 'outline'), and optional prefix and suffix slots
Use toast from frappe-ui for notifications with methods toast.success(), toast.warning(), and toast.error() to provide user feedback
Use Dropdown component with options prop containing action items with label, icon (lucide i...

Files:

  • dashboard/src/components/BookingForm.vue
  • dashboard/src/components/BookingSummary.vue
{main.js,**/*.vue}

📄 CodeRabbit inference engine (.github/instructions/frappe-ui.instructions.md)

For Options API components using resources, register the resourcesPlugin in main.js and declare resources under the resources key as functions, accessible via this.$resources.[name]

Files:

  • dashboard/src/components/BookingForm.vue
  • dashboard/src/components/BookingSummary.vue
🧠 Learnings (5)
📚 Learning: 2025-12-30T06:17:13.946Z
Learnt from: CR
Repo: BuildWithHussain/buzz PR: 0
File: .github/instructions/frappe-ui.instructions.md:0-0
Timestamp: 2025-12-30T06:17:13.946Z
Learning: Applies to **/*.vue : Use `createResource()` from frappe-ui to make backend API calls with configuration for url, params, and methods like `fetch()` and `submit()` in Vue components

Applied to files:

  • dashboard/src/components/BookingForm.vue
📚 Learning: 2025-12-30T06:17:13.946Z
Learnt from: CR
Repo: BuildWithHussain/buzz PR: 0
File: .github/instructions/frappe-ui.instructions.md:0-0
Timestamp: 2025-12-30T06:17:13.946Z
Learning: Applies to **/*.vue : Use `createDocumentResource()` to manage individual document records with support for whitelisted methods, events (onError, onSuccess, transform), and methods like `reload()`, `setValue()`, `delete()`, and custom whitelisted method calls

Applied to files:

  • dashboard/src/components/BookingForm.vue
📚 Learning: 2025-12-30T06:17:13.946Z
Learnt from: CR
Repo: BuildWithHussain/buzz PR: 0
File: .github/instructions/frappe-ui.instructions.md:0-0
Timestamp: 2025-12-30T06:17:13.946Z
Learning: Applies to **/*.vue : Use `toast` from frappe-ui for notifications with methods `toast.success()`, `toast.warning()`, and `toast.error()` to provide user feedback

Applied to files:

  • dashboard/src/components/BookingForm.vue
📚 Learning: 2025-12-30T06:17:13.946Z
Learnt from: CR
Repo: BuildWithHussain/buzz PR: 0
File: .github/instructions/frappe-ui.instructions.md:0-0
Timestamp: 2025-12-30T06:17:13.946Z
Learning: Applies to **/*.vue : Use `Dialog` component with `options` prop for title, message, size ('sm', 'lg', '4xl'), icon, and actions, or use slots (`body-title`, `body-content`, `actions`) for custom content layouts

Applied to files:

  • dashboard/src/components/BookingForm.vue
📚 Learning: 2025-12-30T06:17:13.946Z
Learnt from: CR
Repo: BuildWithHussain/buzz PR: 0
File: .github/instructions/frappe-ui.instructions.md:0-0
Timestamp: 2025-12-30T06:17:13.946Z
Learning: Applies to **/*.vue : Use `useList()` hook to create a list resource for fetching records of a DocType from Frappe backend with options for fields, filters, orderBy, pageLength, caching, and events (onError, onSuccess, transform)

Applied to files:

  • dashboard/src/components/BookingForm.vue
🧬 Code graph analysis (4)
buzz/ticketing/doctype/event_booking/event_booking.py (2)
buzz/ticketing/doctype/event_booking_attendee/event_booking_attendee.py (1)
  • EventBookingAttendee (8-39)
buzz/ticketing/doctype/coupon_code/coupon_code.py (3)
  • is_valid_for_event (54-68)
  • is_usage_available (91-95)
  • get_free_tickets_claimed (73-89)
buzz/ticketing/doctype/coupon_code/coupon_code.py (3)
buzz/ticketing/doctype/coupon_free_add_on/coupon_free_add_on.py (1)
  • CouponFreeAddon (8-9)
buzz/ticketing/doctype/event_booking/event_booking.py (2)
  • validate (41-47)
  • EventBooking (12-234)
buzz/ticketing/doctype/event_booking_attendee/event_booking_attendee.py (1)
  • EventBookingAttendee (8-39)
buzz/api.py (1)
buzz/ticketing/doctype/coupon_code/coupon_code.py (3)
  • is_valid_for_event (54-68)
  • is_usage_available (91-95)
  • get_free_tickets_claimed (73-89)
buzz/ticketing/doctype/coupon_code/test_coupon_code.py (2)
buzz/ticketing/doctype/coupon_code/coupon_code.py (2)
  • get_times_used (70-71)
  • get_free_tickets_claimed (73-89)
buzz/api.py (1)
  • validate_coupon (940-973)
🪛 Ruff (0.14.10)
buzz/ticketing/doctype/event_booking/event_booking.py

31-31: Syntax error in forward annotation: Expected an identifier

(F722)

buzz/ticketing/doctype/coupon_code/coupon_code.py

20-20: Syntax error in forward annotation: Unexpected token at the end of an expression

(F722)


20-20: Undefined name Discount

(F821)


21-21: Undefined name Percentage

(F821)


21-21: Syntax error in forward annotation: Unexpected token at the end of an expression

(F722)

buzz/ticketing/doctype/coupon_code/test_coupon_code.py

659-659: Local variable coupon is assigned to but never used

Remove assignment to unused variable coupon

(F841)


681-681: Local variable coupon is assigned to but never used

Remove assignment to unused variable coupon

(F841)

🔇 Additional comments (26)
buzz/ticketing/doctype/coupon_free_add_on/coupon_free_add_on.py (1)

1-9: LGTM!

Standard boilerplate for a Frappe child table DocType. The commented import frappe can be removed as it's unused.

buzz/ticketing/doctype/coupon_code/coupon_code.js (1)

4-22: LGTM!

The event-scoped query filters for ticket_type and add_on fields are correctly implemented. This ensures users can only select ticket types and add-ons that belong to the selected event.

Note: If frm.doc.event is empty, the filter will still work but return no results, which is acceptable behavior for dependent fields.

buzz/ticketing/doctype/coupon_free_add_on/coupon_free_add_on.json (1)

1-36: LGTM!

Well-configured child table DocType for storing free add-ons associated with a coupon. The required add_on Link field and istable: 1 setting are correctly defined.

buzz/api.py (2)

174-181: LGTM!

The coupon_code parameter integration is clean. The optional parameter with None default allows backward compatibility, and the actual coupon validation occurs during EventBooking.validate() via apply_coupon_if_applicable().


939-973: Well-structured coupon validation endpoint.

The validation flow is correct:

  1. Existence check
  2. Event applicability
  3. Usage availability
  4. Type-specific validation (remaining tickets for Free Tickets)

Note: There's an inherent race condition between validation and booking submission (another user could claim the last free ticket). This is acceptable since apply_coupon_if_applicable() in EventBooking.validate() will perform the authoritative check during the actual booking transaction.

dashboard/src/components/BookingSummary.vue (2)

48-65: LGTM!

The conditional rendering logic for the pricing summary and discount section is well implemented:

  • Pricing summary shows when there's a positive total OR when a coupon was applied with a non-zero subtotal
  • Discount line correctly distinguishes between "Free Tickets" and "Discount" labels
  • Green styling for the discount amount provides good visual feedback

106-117: LGTM!

New props are well-defined with appropriate types and sensible defaults. The couponType as a String with empty default allows flexible handling of both "Free Tickets" and "Discount" types.

buzz/ticketing/doctype/coupon_code/coupon_code.py (3)

32-34: LGTM!

Good auto-naming implementation. The 8-character uppercase hash provides readable coupon codes while allowing custom codes to be set.


54-68: Solid event/category validation logic.

The validation correctly handles:

  • Inactive coupons
  • Universal coupons (no event/category restriction)
  • Event-specific coupons
  • Category-scoped coupons

The str() conversions handle potential type mismatches between document names.


73-89: LGTM!

The query builder usage is correct for counting attendees across submitted bookings. The join ensures only attendees from bookings that used this coupon are counted.

buzz/ticketing/doctype/event_booking/event_booking.json (1)

158-170: LGTM!

The new fields are correctly configured:

  • coupon_code as an editable Link field allows users to apply coupons
  • discount_amount as read-only Currency ensures the discount is computed server-side and cannot be manipulated by users
buzz/ticketing/doctype/event_booking/event_booking.py (4)

41-47: Correct validation order.

The sequence set_total()apply_coupon_if_applicable()apply_taxes_if_applicable() ensures:

  1. Net amount is calculated first
  2. Discount is applied to net amount
  3. Tax is applied to the discounted total

This is the standard order for most tax jurisdictions.


75-77: LGTM!

Tax is now correctly applied to total_amount (after discount) rather than net_amount. This ensures customers are taxed only on what they actually pay.


180-202: Well-implemented discount coupon logic.

The Discount type handling correctly:

  • Calculates percentage discount on net_amount
  • Caps flat discounts at net_amount to prevent negative totals
  • Updates total_amount after applying discount

204-234: Thorough Free Tickets implementation.

The Free Tickets logic correctly handles:

  • Remaining ticket quota based on previously claimed tickets
  • Ticket type matching with type-safe string comparison
  • Per-attendee discount including their eligible free add-ons
  • Clear error message when no matching attendees exist

The discounted counter ensures partial coupon redemption when fewer tickets remain than attendees qualify.

dashboard/src/components/BookingForm.vue (5)

378-426: LGTM on discount calculation logic.

The discount calculation correctly handles:

  • Free Tickets coupons with matching ticket type filtering
  • Capping free tickets to remaining_tickets
  • Including free add-ons only for free ticket holders
  • Percentage and Flat Amount discount types with proper capping

58-112: LGTM on coupon UI section.

Good UX considerations:

  • Coupon section appears for paid events or when a coupon is already applied
  • Enter key submits the coupon code
  • Clear visual feedback for applied coupons with discount details
  • Error messages displayed appropriately

114-128: LGTM on BookingSummary integration.

The new coupon-related props are correctly passed to display discount information in the summary component.


227-231: LGTM on coupon state initialization.

State variables are appropriately initialized with empty/falsy defaults and use reactive refs as expected for Vue 3 composition API.


433-439: LGTM on tax and total calculations.

Tax is correctly applied to the amount after discount, and the final total properly combines the discounted amount with tax.

buzz/ticketing/doctype/coupon_code/test_coupon_code.py (5)

46-82: LGTM on discount coupon tests.

Comprehensive coverage for percentage and flat discounts with clear assertions validating net_amount, discount_amount, and total_amount calculations.


337-431: Excellent cross-booking tracking test.

The test_free_tickets_tracking_across_bookings test thoroughly validates that free ticket counts are correctly tracked across multiple bookings, including the edge case where remaining tickets are less than requested.


591-605: LGTM on auto-generation test.

Validates that coupon codes are auto-generated with expected length (8 characters) when not provided.


655-675: Static analysis false positive - coupon variable is a test fixture.

The coupon variable creates the test fixture in the database. While Ruff flags it as unused, the .insert() side effect is the purpose. The test then validates the coupon by code via the API.


640-706: LGTM on API validation tests.

Good coverage of the validate_coupon API endpoint for discount coupons, free tickets, and invalid codes. Tests correctly validate the response structure.

buzz/ticketing/doctype/coupon_code/coupon_code.json (1)

1-171: LGTM on DocType structure.

The Coupon Code DocType is well-designed with:

  • Unique auto-generated code field as document name
  • Conditional field visibility and mandatory rules based on coupon type
  • Virtual fields for usage tracking (avoids stale counts)
  • Appropriate permissions for System Manager and Event Manager

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 23a5cab and e8a5938.

📒 Files selected for processing (1)
  • buzz/ticketing/doctype/coupon_code/coupon_code.json
🔇 Additional comments (4)
buzz/ticketing/doctype/coupon_code/coupon_code.json (4)

1-24: LGTM!

The DocType configuration is correct with appropriate autoname strategy using the code field.


25-47: LGTM!

Core fields are well-defined with appropriate constraints (unique code, set_only_once, required coupon_type).


63-84: LGTM!

Free Tickets fields are properly configured with appropriate mandatory constraints and conditional visibility.


115-172: LGTM!

Virtual tracking fields, UI layout, and permissions are properly configured. The read-only virtual fields for times_used and free_tickets_claimed ensure accurate tracking.

Comment on lines +93 to +99
{
"depends_on": "eval:doc.coupon_type == 'Discount'",
"fieldname": "discount_value",
"fieldtype": "Float",
"label": "Discount Value",
"mandatory_depends_on": "eval:doc.coupon_type == 'Discount'"
},
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find and examine the Python controller file
fd coupon_code.py --type f

Repository: BuildWithHussain/buzz

Length of output: 169


🏁 Script executed:

#!/bin/bash
# Check the JSON file for the typo at line 102 and examine the discount_value field
sed -n '85,114p' buzz/ticketing/doctype/coupon_code/coupon_code.json

Repository: BuildWithHussain/buzz

Length of output: 907


🏁 Script executed:

#!/bin/bash
# Search for validation logic in the Python controller related to discount_value
if [ -f "buzz/ticketing/doctype/coupon_code/coupon_code.py" ]; then
  cat -n buzz/ticketing/doctype/coupon_code/coupon_code.py | head -150
fi

Repository: BuildWithHussain/buzz

Length of output: 3995


Complete validation for discount_value field.

Validation exists in coupon_code.py (line 42-44) but is incomplete:

  • For Percentage discounts: only checks upper bound (> 100), missing lower bound validation
  • For Flat Amount discounts: no validation exists
  • Negative values are not prevented

Add validation to ensure:

  • Percentage discounts: 0-100 range
  • Flat Amount discounts: positive values only
🤖 Prompt for AI Agents
In buzz/ticketing/doctype/coupon_code/coupon_code.json around lines 93-99 and in
coupon_code.py around lines 42-44, the discount_value validation is incomplete:
add checks so that when coupon_type == "Percentage" discount_value must be
between 0 and 100 inclusive (reject negative and >100), and when coupon_type ==
"Flat Amount" discount_value must be > 0 (reject zero and negatives); implement
these checks in coupon_code.py using the existing validation hook and raise a
ValidationError (or appropriate framework validation) with clear messages for
each failure.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
dashboard/src/components/BookingForm.vue (1)

532-578: Error handling has been added as requested in prior review.

The applyCoupon function now correctly wraps the API call in a try-catch block, addressing the previous review feedback. The error message is properly extracted and displayed to the user.

🧹 Nitpick comments (4)
dashboard/src/components/BookingForm.vue (2)

428-431: Consider clamping to prevent negative values.

If discountAmount ever exceeds netAmount due to floating-point issues or edge cases, amountAfterDiscount could become negative. Consider adding Math.max(0, ...).

🔎 Proposed defensive fix
 const amountAfterDiscount = computed(() => {
-	return netAmount.value - discountAmount.value;
+	return Math.max(0, netAmount.value - discountAmount.value);
 });

569-571: Long toast message may be truncated on smaller screens.

The success toast for Free Tickets includes the ticket type title and remaining count, which could produce a lengthy message. Consider shortening or using a different notification approach for better UX.

buzz/ticketing/doctype/coupon_code/coupon_code.py (2)

72-73: Usage count query is correct but could be optimized with caching.

The get_times_used method queries the database each time it's called. If called multiple times during validation, this could be inefficient. Consider caching within the request lifecycle if performance becomes a concern.


56-70: The separation of event validation and usage checks is intentional and working correctly.

All call sites in the codebase (buzz/api.py:947-951 and buzz/ticketing/doctype/event_booking/event_booking.py:188-192) properly check both is_valid_for_event() and is_usage_available() in sequence. The methods have distinct responsibilities: event/category validation vs. usage limit tracking. However, documenting this implicit contract would improve clarity for future developers.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e8a5938 and 8d6b093.

📒 Files selected for processing (2)
  • buzz/ticketing/doctype/coupon_code/coupon_code.py
  • dashboard/src/components/BookingForm.vue
🧰 Additional context used
📓 Path-based instructions (2)
**/*.vue

📄 CodeRabbit inference engine (.github/instructions/frappe-ui.instructions.md)

**/*.vue: Use createResource() from frappe-ui to make backend API calls with configuration for url, params, and methods like fetch() and submit() in Vue components
Use createDocumentResource() to manage individual document records with support for whitelisted methods, events (onError, onSuccess, transform), and methods like reload(), setValue(), delete(), and custom whitelisted method calls
Use useList() hook to create a list resource for fetching records of a DocType from Frappe backend with options for fields, filters, orderBy, pageLength, caching, and events (onError, onSuccess, transform)
Use ListView component with props columns, rows, row-key, and options to render tabular data with support for selection, tooltips, resizable columns, and custom rendering slots
Define ListView columns with required label and key properties, optional width (as multiplier or px/rem), align (start/left, center/middle, end/right), and custom attributes for rendering
Define ListView rows with unique identifier matching the row-key prop, and field values as strings or objects with label property for custom rendering
For grouped ListView rows, structure data as array of objects with group string, collapsed boolean, and rows array containing row objects
Use Badge component with props variant ('solid', 'subtle', 'outline', 'ghost'), theme ('gray', 'blue', 'green', 'orange', 'red'), and size ('sm', 'md', 'lg') for displaying status indicators
Use FormControl component with type prop ('text', 'number', 'email', 'date', 'password', 'search', 'textarea', 'select', 'autocomplete', 'checkbox'), size ('sm', 'md', 'lg', 'xl'), variant ('subtle', 'outline'), and optional prefix and suffix slots
Use toast from frappe-ui for notifications with methods toast.success(), toast.warning(), and toast.error() to provide user feedback
Use Dropdown component with options prop containing action items with label, icon (lucide i...

Files:

  • dashboard/src/components/BookingForm.vue
{main.js,**/*.vue}

📄 CodeRabbit inference engine (.github/instructions/frappe-ui.instructions.md)

For Options API components using resources, register the resourcesPlugin in main.js and declare resources under the resources key as functions, accessible via this.$resources.[name]

Files:

  • dashboard/src/components/BookingForm.vue
🧠 Learnings (7)
📓 Common learnings
Learnt from: UmakanthKaspa
Repo: BuildWithHussain/buzz PR: 99
File: buzz/ticketing/doctype/coupon_code/coupon_code.json:48-55
Timestamp: 2025-12-30T19:26:44.082Z
Learning: For Free Tickets coupons in buzz/ticketing/doctype/coupon_code/, the event field is mandatory because Free Tickets must specify a ticket_type, and ticket types belong to specific events. The "apply to all events" scope only applies to Discount coupons.
📚 Learning: 2025-12-30T19:26:44.082Z
Learnt from: UmakanthKaspa
Repo: BuildWithHussain/buzz PR: 99
File: buzz/ticketing/doctype/coupon_code/coupon_code.json:48-55
Timestamp: 2025-12-30T19:26:44.082Z
Learning: For Free Tickets coupons in buzz/ticketing/doctype/coupon_code/, the event field is mandatory because Free Tickets must specify a ticket_type, and ticket types belong to specific events. The "apply to all events" scope only applies to Discount coupons.

Applied to files:

  • buzz/ticketing/doctype/coupon_code/coupon_code.py
  • dashboard/src/components/BookingForm.vue
📚 Learning: 2025-12-30T06:17:13.946Z
Learnt from: CR
Repo: BuildWithHussain/buzz PR: 0
File: .github/instructions/frappe-ui.instructions.md:0-0
Timestamp: 2025-12-30T06:17:13.946Z
Learning: Applies to **/*.vue : Use `createResource()` from frappe-ui to make backend API calls with configuration for url, params, and methods like `fetch()` and `submit()` in Vue components

Applied to files:

  • dashboard/src/components/BookingForm.vue
📚 Learning: 2025-12-30T06:17:13.946Z
Learnt from: CR
Repo: BuildWithHussain/buzz PR: 0
File: .github/instructions/frappe-ui.instructions.md:0-0
Timestamp: 2025-12-30T06:17:13.946Z
Learning: Applies to **/*.vue : Use `createDocumentResource()` to manage individual document records with support for whitelisted methods, events (onError, onSuccess, transform), and methods like `reload()`, `setValue()`, `delete()`, and custom whitelisted method calls

Applied to files:

  • dashboard/src/components/BookingForm.vue
📚 Learning: 2025-12-30T06:17:13.946Z
Learnt from: CR
Repo: BuildWithHussain/buzz PR: 0
File: .github/instructions/frappe-ui.instructions.md:0-0
Timestamp: 2025-12-30T06:17:13.946Z
Learning: Applies to **/*.vue : Use `toast` from frappe-ui for notifications with methods `toast.success()`, `toast.warning()`, and `toast.error()` to provide user feedback

Applied to files:

  • dashboard/src/components/BookingForm.vue
📚 Learning: 2025-12-30T06:17:13.946Z
Learnt from: CR
Repo: BuildWithHussain/buzz PR: 0
File: .github/instructions/frappe-ui.instructions.md:0-0
Timestamp: 2025-12-30T06:17:13.946Z
Learning: Applies to **/*.vue : Use `Dialog` component with `options` prop for title, message, size ('sm', 'lg', '4xl'), icon, and actions, or use slots (`body-title`, `body-content`, `actions`) for custom content layouts

Applied to files:

  • dashboard/src/components/BookingForm.vue
📚 Learning: 2025-12-30T06:17:13.946Z
Learnt from: CR
Repo: BuildWithHussain/buzz PR: 0
File: .github/instructions/frappe-ui.instructions.md:0-0
Timestamp: 2025-12-30T06:17:13.946Z
Learning: Applies to **/*.vue : Use `useList()` hook to create a list resource for fetching records of a DocType from Frappe backend with options for fields, filters, orderBy, pageLength, caching, and events (onError, onSuccess, transform)

Applied to files:

  • dashboard/src/components/BookingForm.vue
🧬 Code graph analysis (1)
buzz/ticketing/doctype/coupon_code/coupon_code.py (3)
buzz/ticketing/doctype/coupon_free_add_on/coupon_free_add_on.py (1)
  • CouponFreeAddon (8-9)
buzz/ticketing/doctype/event_booking/event_booking.py (2)
  • validate (41-47)
  • EventBooking (12-234)
buzz/ticketing/doctype/event_booking_attendee/event_booking_attendee.py (1)
  • EventBookingAttendee (8-39)
🪛 Ruff (0.14.10)
buzz/ticketing/doctype/coupon_code/coupon_code.py

20-20: Syntax error in forward annotation: Unexpected token at the end of an expression

(F722)


20-20: Undefined name Discount

(F821)


21-21: Undefined name Percentage

(F821)


21-21: Syntax error in forward annotation: Unexpected token at the end of an expression

(F722)

🔇 Additional comments (12)
dashboard/src/components/BookingForm.vue (6)

59-61: Conditional visibility may hide coupon section unexpectedly.

The condition finalTotal > 0 || couponApplied means the coupon section is hidden when the cart is empty (finalTotal === 0) and no coupon is applied. However, if a user has a Free Tickets coupon that would make their total $0, they cannot apply it until they've added attendees. This seems intentional but consider if users might want to enter the coupon code first.


91-94: Edge case: Display shows "0 free" when no matching attendees.

When matchingAttendeesCount is 0, this displays "(0 eligible, 0 free)" which correctly informs the user but may be confusing. The UX is acceptable since the toast message at line 569-571 guides users to select the correct ticket type.


378-382: LGTM: Matching attendees calculation is correct.

The computed property correctly filters attendees by the coupon's ticket type for Free Tickets coupons.


385-426: Discount calculation logic is well-implemented.

The discount computation correctly handles:

  • Free Tickets: calculates based on matching attendees up to remaining tickets
  • Free add-ons: only applied to attendees receiving free tickets
  • Percentage discounts: calculated on netAmount
  • Flat amount discounts: capped at netAmount to prevent negative totals

580-585: LGTM: removeCoupon properly resets all coupon state.

All coupon-related refs are cleared, ensuring a clean state for re-applying a different coupon.


720-724: Improved error handling for booking submission.

The error handler now extracts messages from the error response properly, with a sensible fallback message.

buzz/ticketing/doctype/coupon_code/coupon_code.py (6)

19-21: Static analysis warnings are false positives.

The Ruff warnings about DF.Literal["Free Tickets", "Discount"] and DF.Literal["Percentage", "Flat Amount"] are false positives. This is valid Frappe auto-generated type annotation syntax within a TYPE_CHECKING block, using Frappe's custom DF.Literal type hint.


32-34: LGTM: Auto-naming generates a readable uppercase hash.

The 8-character uppercase hash provides sufficient uniqueness for coupon codes while being user-friendly.


41-46: Discount validation correctly enforces constraints.

The validation properly ensures:

  • Discount value must be positive
  • Percentage discounts cannot exceed 100%

Based on learnings, Free Tickets coupons don't need discount validation since they use different fields.


52-54: LGTM: Free Tickets require event as per business logic.

Based on learnings, this validation correctly enforces that Free Tickets coupons must have an event since ticket types belong to specific events.


93-97: LGTM: Usage limit check is straightforward.

Returns a tuple with availability status and error message. The check correctly handles max_usage_count = 0 as unlimited.


99-105: Properties provide convenient read-only access.

The @property decorators expose computed values cleanly. Note these query the database on each access, so callers should cache results if needed multiple times.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
buzz/ticketing/doctype/coupon_code/coupon_code.py (1)

75-92: Previous review feedback implemented correctly.

The ticket_type filter on line 88 has been added as suggested in the previous review, ensuring the count matches only attendees with the coupon's ticket type. This aligns with the discount logic in the booking flow.

The docstring could be more precise:

Optional docstring update
-	"""Calculate total attendees from all submitted bookings using this coupon"""
+	"""Calculate total attendees with matching ticket type from all submitted bookings using this coupon"""
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8d6b093 and 4ce3298.

📒 Files selected for processing (1)
  • buzz/ticketing/doctype/coupon_code/coupon_code.py
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: UmakanthKaspa
Repo: BuildWithHussain/buzz PR: 99
File: buzz/ticketing/doctype/coupon_code/coupon_code.json:48-55
Timestamp: 2025-12-30T19:26:44.082Z
Learning: For Free Tickets coupons in buzz/ticketing/doctype/coupon_code/, the event field is mandatory because Free Tickets must specify a ticket_type, and ticket types belong to specific events. The "apply to all events" scope only applies to Discount coupons.
📚 Learning: 2025-12-30T19:26:44.082Z
Learnt from: UmakanthKaspa
Repo: BuildWithHussain/buzz PR: 99
File: buzz/ticketing/doctype/coupon_code/coupon_code.json:48-55
Timestamp: 2025-12-30T19:26:44.082Z
Learning: For Free Tickets coupons in buzz/ticketing/doctype/coupon_code/, the event field is mandatory because Free Tickets must specify a ticket_type, and ticket types belong to specific events. The "apply to all events" scope only applies to Discount coupons.

Applied to files:

  • buzz/ticketing/doctype/coupon_code/coupon_code.py
🪛 Ruff (0.14.10)
buzz/ticketing/doctype/coupon_code/coupon_code.py

20-20: Syntax error in forward annotation: Unexpected token at the end of an expression

(F722)


20-20: Undefined name Discount

(F821)


21-21: Undefined name Percentage

(F821)


21-21: Syntax error in forward annotation: Unexpected token at the end of an expression

(F722)

🔇 Additional comments (4)
buzz/ticketing/doctype/coupon_code/coupon_code.py (4)

32-35: LGTM!

The auto-generation of 8-character uppercase coupon codes is a good approach for ensuring unique codes when users don't provide one manually.


56-70: Well-structured event eligibility check.

The method correctly handles all three scope scenarios (all events, specific event, event category) with appropriate validation and clear error messages.


72-73: LGTM!

Correctly counts only submitted bookings for usage tracking.


94-106: LGTM!

The usage limit check correctly implements unlimited usage when max_usage_count is not set, and the properties provide clean access to computed values.

Comment on lines +36 to +54
def validate(self):
self.validate_discount_value()
self.validate_scope()
self.validate_free_tickets_event()

def validate_discount_value(self):
if self.coupon_type == "Discount":
if self.discount_value <= 0:
frappe.throw(_("Discount value must be greater than 0"))
if self.discount_type == "Percentage" and self.discount_value > 100:
frappe.throw(_("Percentage discount cannot exceed 100%"))

def validate_scope(self):
if self.event and self.event_category:
frappe.throw(_("Select either Event or Category, not both"))

def validate_free_tickets_event(self):
if self.coupon_type == "Free Tickets" and not self.event:
frappe.throw(_("Event is required for Free Tickets coupon"))
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Add validation for ticket_type and number_of_free_tickets for Free Tickets coupons.

The validation pipeline correctly enforces that Free Tickets coupons must have an event, but it doesn't validate that ticket_type and number_of_free_tickets are also required and valid. Line 88 in get_free_tickets_claimed() assumes self.ticket_type exists, which could cause an error if it's None.

🔎 Proposed validation additions
 	def validate_free_tickets_event(self):
 		if self.coupon_type == "Free Tickets" and not self.event:
 			frappe.throw(_("Event is required for Free Tickets coupon"))
+		if self.coupon_type == "Free Tickets" and not self.ticket_type:
+			frappe.throw(_("Ticket Type is required for Free Tickets coupon"))
+		if self.coupon_type == "Free Tickets" and self.number_of_free_tickets <= 0:
+			frappe.throw(_("Number of Free Tickets must be greater than 0"))

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
buzz/ticketing/doctype/coupon_code/coupon_code.py (1)

53-55: Consider adding validation for ticket_type and number_of_free_tickets for Free Tickets coupons.

The validation ensures event is required for Free Tickets, but ticket_type and number_of_free_tickets are also critical. Line 89 in get_free_tickets_claimed() and the discount logic in event_booking.py assume ticket_type exists.

🔎 Proposed validation additions
 	def validate_free_tickets_event(self):
 		if self.coupon_type == "Free Tickets" and not self.event:
 			frappe.throw(_("Event is required for Free Tickets coupon"))
+		if self.coupon_type == "Free Tickets" and not self.ticket_type:
+			frappe.throw(_("Ticket Type is required for Free Tickets coupon"))
+		if self.coupon_type == "Free Tickets" and (not self.number_of_free_tickets or self.number_of_free_tickets <= 0):
+			frappe.throw(_("Number of Free Tickets must be greater than 0"))
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4ce3298 and 9e9750d.

📒 Files selected for processing (6)
  • buzz/api.py
  • buzz/ticketing/doctype/coupon_code/coupon_code.py
  • buzz/ticketing/doctype/coupon_code/test_coupon_code.py
  • buzz/ticketing/doctype/event_booking/event_booking.py
  • dashboard/src/components/BookingForm.vue
  • dashboard/src/components/BookingSummary.vue
🚧 Files skipped from review as they are similar to previous changes (1)
  • dashboard/src/components/BookingSummary.vue
🧰 Additional context used
📓 Path-based instructions (2)
**/*.vue

📄 CodeRabbit inference engine (.github/instructions/frappe-ui.instructions.md)

**/*.vue: Use createResource() from frappe-ui to make backend API calls with configuration for url, params, and methods like fetch() and submit() in Vue components
Use createDocumentResource() to manage individual document records with support for whitelisted methods, events (onError, onSuccess, transform), and methods like reload(), setValue(), delete(), and custom whitelisted method calls
Use useList() hook to create a list resource for fetching records of a DocType from Frappe backend with options for fields, filters, orderBy, pageLength, caching, and events (onError, onSuccess, transform)
Use ListView component with props columns, rows, row-key, and options to render tabular data with support for selection, tooltips, resizable columns, and custom rendering slots
Define ListView columns with required label and key properties, optional width (as multiplier or px/rem), align (start/left, center/middle, end/right), and custom attributes for rendering
Define ListView rows with unique identifier matching the row-key prop, and field values as strings or objects with label property for custom rendering
For grouped ListView rows, structure data as array of objects with group string, collapsed boolean, and rows array containing row objects
Use Badge component with props variant ('solid', 'subtle', 'outline', 'ghost'), theme ('gray', 'blue', 'green', 'orange', 'red'), and size ('sm', 'md', 'lg') for displaying status indicators
Use FormControl component with type prop ('text', 'number', 'email', 'date', 'password', 'search', 'textarea', 'select', 'autocomplete', 'checkbox'), size ('sm', 'md', 'lg', 'xl'), variant ('subtle', 'outline'), and optional prefix and suffix slots
Use toast from frappe-ui for notifications with methods toast.success(), toast.warning(), and toast.error() to provide user feedback
Use Dropdown component with options prop containing action items with label, icon (lucide i...

Files:

  • dashboard/src/components/BookingForm.vue
{main.js,**/*.vue}

📄 CodeRabbit inference engine (.github/instructions/frappe-ui.instructions.md)

For Options API components using resources, register the resourcesPlugin in main.js and declare resources under the resources key as functions, accessible via this.$resources.[name]

Files:

  • dashboard/src/components/BookingForm.vue
🧠 Learnings (7)
📓 Common learnings
Learnt from: UmakanthKaspa
Repo: BuildWithHussain/buzz PR: 99
File: buzz/ticketing/doctype/coupon_code/coupon_code.json:48-55
Timestamp: 2025-12-30T19:26:44.082Z
Learning: For Free Tickets coupons in buzz/ticketing/doctype/coupon_code/, the event field is mandatory because Free Tickets must specify a ticket_type, and ticket types belong to specific events. The "apply to all events" scope only applies to Discount coupons.
📚 Learning: 2025-12-30T19:26:44.082Z
Learnt from: UmakanthKaspa
Repo: BuildWithHussain/buzz PR: 99
File: buzz/ticketing/doctype/coupon_code/coupon_code.json:48-55
Timestamp: 2025-12-30T19:26:44.082Z
Learning: For Free Tickets coupons in buzz/ticketing/doctype/coupon_code/, the event field is mandatory because Free Tickets must specify a ticket_type, and ticket types belong to specific events. The "apply to all events" scope only applies to Discount coupons.

Applied to files:

  • buzz/ticketing/doctype/event_booking/event_booking.py
  • dashboard/src/components/BookingForm.vue
  • buzz/ticketing/doctype/coupon_code/coupon_code.py
  • buzz/api.py
  • buzz/ticketing/doctype/coupon_code/test_coupon_code.py
📚 Learning: 2025-12-30T06:17:13.946Z
Learnt from: CR
Repo: BuildWithHussain/buzz PR: 0
File: .github/instructions/frappe-ui.instructions.md:0-0
Timestamp: 2025-12-30T06:17:13.946Z
Learning: Applies to **/*.vue : Use `createResource()` from frappe-ui to make backend API calls with configuration for url, params, and methods like `fetch()` and `submit()` in Vue components

Applied to files:

  • dashboard/src/components/BookingForm.vue
📚 Learning: 2025-12-30T06:17:13.946Z
Learnt from: CR
Repo: BuildWithHussain/buzz PR: 0
File: .github/instructions/frappe-ui.instructions.md:0-0
Timestamp: 2025-12-30T06:17:13.946Z
Learning: Applies to **/*.vue : Use `createDocumentResource()` to manage individual document records with support for whitelisted methods, events (onError, onSuccess, transform), and methods like `reload()`, `setValue()`, `delete()`, and custom whitelisted method calls

Applied to files:

  • dashboard/src/components/BookingForm.vue
📚 Learning: 2025-12-30T06:17:13.946Z
Learnt from: CR
Repo: BuildWithHussain/buzz PR: 0
File: .github/instructions/frappe-ui.instructions.md:0-0
Timestamp: 2025-12-30T06:17:13.946Z
Learning: Applies to **/*.vue : Use `toast` from frappe-ui for notifications with methods `toast.success()`, `toast.warning()`, and `toast.error()` to provide user feedback

Applied to files:

  • dashboard/src/components/BookingForm.vue
📚 Learning: 2025-12-30T06:17:13.946Z
Learnt from: CR
Repo: BuildWithHussain/buzz PR: 0
File: .github/instructions/frappe-ui.instructions.md:0-0
Timestamp: 2025-12-30T06:17:13.946Z
Learning: Applies to **/*.vue : Use `Dialog` component with `options` prop for title, message, size ('sm', 'lg', '4xl'), icon, and actions, or use slots (`body-title`, `body-content`, `actions`) for custom content layouts

Applied to files:

  • dashboard/src/components/BookingForm.vue
📚 Learning: 2025-12-30T06:17:13.946Z
Learnt from: CR
Repo: BuildWithHussain/buzz PR: 0
File: .github/instructions/frappe-ui.instructions.md:0-0
Timestamp: 2025-12-30T06:17:13.946Z
Learning: Applies to **/*.vue : Use `useList()` hook to create a list resource for fetching records of a DocType from Frappe backend with options for fields, filters, orderBy, pageLength, caching, and events (onError, onSuccess, transform)

Applied to files:

  • dashboard/src/components/BookingForm.vue
🧬 Code graph analysis (4)
buzz/ticketing/doctype/event_booking/event_booking.py (1)
buzz/ticketing/doctype/coupon_code/coupon_code.py (3)
  • is_valid_for_event (57-71)
  • is_usage_available (95-99)
  • get_free_tickets_claimed (76-93)
buzz/ticketing/doctype/coupon_code/coupon_code.py (3)
buzz/ticketing/doctype/coupon_free_add_on/coupon_free_add_on.py (1)
  • CouponFreeAddon (8-9)
buzz/ticketing/doctype/event_booking/event_booking.py (2)
  • validate (42-48)
  • EventBooking (12-235)
buzz/ticketing/doctype/event_booking_attendee/event_booking_attendee.py (1)
  • EventBookingAttendee (8-39)
buzz/api.py (1)
buzz/ticketing/doctype/coupon_code/coupon_code.py (3)
  • is_valid_for_event (57-71)
  • is_usage_available (95-99)
  • get_free_tickets_claimed (76-93)
buzz/ticketing/doctype/coupon_code/test_coupon_code.py (2)
buzz/ticketing/doctype/coupon_code/coupon_code.py (2)
  • get_times_used (73-74)
  • get_free_tickets_claimed (76-93)
buzz/api.py (1)
  • validate_coupon (940-972)
🪛 Ruff (0.14.10)
buzz/ticketing/doctype/event_booking/event_booking.py

32-32: Syntax error in forward annotation: Expected an identifier

(F722)

buzz/ticketing/doctype/coupon_code/coupon_code.py

21-21: Syntax error in forward annotation: Unexpected token at the end of an expression

(F722)


21-21: Undefined name Discount

(F821)


22-22: Undefined name Percentage

(F821)


22-22: Syntax error in forward annotation: Unexpected token at the end of an expression

(F722)

🔇 Additional comments (22)
buzz/api.py (2)

937-972: LGTM! Well-structured coupon validation API.

The validate_coupon function properly validates coupon existence, event applicability, and usage availability before returning type-specific information. The use of frappe.get_cached_doc is appropriate for read-only validation.


174-181: LGTM! Coupon code integration in process_booking.

The coupon_code parameter is correctly added and assigned to the booking document. Server-side validation will occur during booking.insert() via the apply_coupon_if_applicable method in EventBooking.

buzz/ticketing/doctype/event_booking/event_booking.py (5)

28-35: Ignore the static analysis hint - this is valid auto-generated type syntax.

The Ruff F722 warning about "Syntax error in forward annotation" on line 32 is a false positive. The DF.Currency type annotation is valid Frappe type hint syntax within the auto-generated TYPE_CHECKING block.


42-48: LGTM! Correct ordering of validation steps.

The validation flow correctly applies the coupon before taxes, ensuring discounts reduce the taxable amount as expected.


197-203: LGTM! Discount coupon logic is correct.

Percentage discounts are calculated on net_amount, and flat discounts are properly capped to not exceed the net_amount.


205-235: Free Tickets logic correctly handles partial free tickets and add-ons.

The implementation properly:

  1. Calculates remaining free tickets across bookings
  2. Only discounts attendees with matching ticket_type
  3. Applies free add-on discounts for eligible attendees
  4. Throws a clear error if no eligible attendees exist

One note: The attendee.amount = 0 modification on line 222 is intentional to zero out the ticket price for discounted attendees, which will be reflected when the document is saved.


76-78: LGTM! Tax correctly calculated on discounted amount.

Tax is now computed on total_amount (which is net_amount - discount_amount after coupon application), ensuring taxes apply to the post-discount subtotal.

dashboard/src/components/BookingForm.vue (4)

58-124: LGTM! Well-designed coupon UI section.

The coupon UI properly handles:

  • Conditional display based on whether there's a payable amount
  • Input with apply button and loading state
  • Applied coupon display with clear visual feedback (green styling)
  • Remove button for applied coupons
  • Error message display

The use of FormControl follows the coding guidelines for frappe-ui components.


390-441: LGTM! Client-side discount calculations mirror server-side logic.

The discountAmount computed property correctly:

  1. Handles Free Tickets by counting matching attendees and applying ticket + add-on discounts
  2. Handles Discount coupons with percentage and flat amount calculations
  3. Uses Math.min() to cap flat discounts at net amount

This ensures the UI preview matches what the server will calculate.


546-602: LGTM! Proper error handling in coupon functions.

The applyCoupon function correctly:

  • Validates input before submitting
  • Uses try-catch for network error handling (addresses previous review feedback)
  • Shows appropriate toast notifications for success
  • Sets error state for invalid coupons

The removeCoupon function properly resets all coupon-related state.


702-709: LGTM! Coupon code correctly included in booking payload.

The coupon_code is conditionally included only when a coupon is applied, using the trimmed value to ensure consistency with the validation call.

buzz/ticketing/doctype/coupon_code/coupon_code.py (4)

9-31: Ignore static analysis hints - valid Frappe type annotations.

The Ruff F722/F821 warnings about DF.Literal["Free Tickets", "Discount"] and similar are false positives. These are valid Frappe type hint syntax within the auto-generated TYPE_CHECKING block.


33-35: LGTM! Auto-generated coupon code.

The autoname method correctly generates an 8-character uppercase hash when no code is provided, using Frappe's built-in generate_hash function.


76-93: LGTM! Free tickets tracking now correctly filters by ticket_type.

The query properly joins EventBookingAttendee with EventBooking and filters by:

  • coupon_code match
  • docstatus == 1 (submitted bookings only)
  • ticket_type match (ensures accurate count for mixed ticket type bookings)

This addresses the previous review feedback about misaligned counting.


57-71: LGTM! Event/category scope validation is comprehensive.

The is_valid_for_event method correctly:

  1. Checks if coupon is active
  2. Allows usage if no event/category restriction is set
  3. Validates against specific event if set
  4. Validates against event's category if category restriction is set
buzz/ticketing/doctype/coupon_code/test_coupon_code.py (7)

11-42: LGTM! Well-structured test setup.

The setUp method properly creates isolated test fixtures including:

  • A test event with tax disabled for predictable calculations
  • A test ticket type with a known price (500)
  • A test add-on for add-on related tests

46-148: LGTM! Comprehensive discount coupon tests.

The tests cover key discount scenarios:

  • Percentage discount calculation (20% of 1000 = 200)
  • Flat discount calculation (300 off 500)
  • Flat discount capping (1000 discount capped at 500 net amount)

Each test has clear assertions verifying net_amount, discount_amount, and total_amount.


150-250: LGTM! Usage limit and unlimited usage tests.

These tests properly verify:

  • Usage limit enforcement (max_usage_count=2 blocks 3rd booking)
  • Unlimited usage works when max_usage_count=0
  • get_times_used() returns correct count

254-431: LGTM! Free tickets tracking tests are thorough.

The tests verify:

  • All tickets free when within limit
  • Partial free tickets when exceeding limit (2 free of 3)
  • Cross-booking tracking (remaining tickets calculated correctly across multiple bookings)

The assertions correctly validate the discount calculations at each step.


433-475: LGTM! Free add-ons with free tickets test.

This test verifies that when a coupon includes free add-ons:

  • Both the ticket (500) and add-on (200) are included in the discount
  • Total discount is 700, resulting in 0 total amount

477-637: LGTM! Validation tests cover edge cases.

Comprehensive validation testing for:

  • Event scope enforcement (coupon for event A rejected on event B)
  • Inactive coupon rejection
  • Free Tickets requires event validation
  • Percentage discount cannot exceed 100%
  • Auto-generated coupon codes are 8 characters
  • Coupon code tracked in booking document

640-706: LGTM! API endpoint tests validate correct response structure.

The TestValidateCouponAPI class verifies:

  • Discount coupons return correct type and value info
  • Free Tickets coupons return correct remaining_tickets
  • Invalid codes return proper error response

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Coupon codes for sponsors et. al.

1 participant