-
Notifications
You must be signed in to change notification settings - Fork 26
feat: add coupon codes for free tickets and discounts #99
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat: add coupon codes for free tickets and discounts #99
Conversation
📝 WalkthroughWalkthroughAdds 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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
Comment |
There was a problem hiding this 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
setUpmethod creates test fixtures but doesn't clean them up. While Frappe's test framework may handle this, explicitly deleting created documents intearDownensures 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 forticket_typebased on selectedevent.The
ticket_typeLink field doesn't filter by the selectedevent, allowing users to potentially select a ticket type from a different event. Adding aget_queryfilter would improve UX.This can be done by adding a client script or using
link_filtersproperty:{ "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
📒 Files selected for processing (13)
buzz/api.pybuzz/ticketing/doctype/coupon_code/__init__.pybuzz/ticketing/doctype/coupon_code/coupon_code.jsbuzz/ticketing/doctype/coupon_code/coupon_code.jsonbuzz/ticketing/doctype/coupon_code/coupon_code.pybuzz/ticketing/doctype/coupon_code/test_coupon_code.pybuzz/ticketing/doctype/coupon_free_add_on/__init__.pybuzz/ticketing/doctype/coupon_free_add_on/coupon_free_add_on.jsonbuzz/ticketing/doctype/coupon_free_add_on/coupon_free_add_on.pybuzz/ticketing/doctype/event_booking/event_booking.jsonbuzz/ticketing/doctype/event_booking/event_booking.pydashboard/src/components/BookingForm.vuedashboard/src/components/BookingSummary.vue
🧰 Additional context used
📓 Path-based instructions (2)
**/*.vue
📄 CodeRabbit inference engine (.github/instructions/frappe-ui.instructions.md)
**/*.vue: UsecreateResource()from frappe-ui to make backend API calls with configuration for url, params, and methods likefetch()andsubmit()in Vue components
UsecreateDocumentResource()to manage individual document records with support for whitelisted methods, events (onError, onSuccess, transform), and methods likereload(),setValue(),delete(), and custom whitelisted method calls
UseuseList()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)
UseListViewcomponent with propscolumns,rows,row-key, andoptionsto render tabular data with support for selection, tooltips, resizable columns, and custom rendering slots
Define ListView columns with requiredlabelandkeyproperties, optionalwidth(as multiplier or px/rem),align(start/left, center/middle, end/right), and custom attributes for rendering
Define ListView rows with unique identifier matching therow-keyprop, and field values as strings or objects withlabelproperty for custom rendering
For grouped ListView rows, structure data as array of objects withgroupstring,collapsedboolean, androwsarray containing row objects
UseBadgecomponent with propsvariant('solid', 'subtle', 'outline', 'ghost'),theme('gray', 'blue', 'green', 'orange', 'red'), andsize('sm', 'md', 'lg') for displaying status indicators
UseFormControlcomponent withtypeprop ('text', 'number', 'email', 'date', 'password', 'search', 'textarea', 'select', 'autocomplete', 'checkbox'),size('sm', 'md', 'lg', 'xl'),variant('subtle', 'outline'), and optionalprefixandsuffixslots
Usetoastfrom frappe-ui for notifications with methodstoast.success(),toast.warning(), andtoast.error()to provide user feedback
UseDropdowncomponent withoptionsprop containing action items withlabel,icon(lucide i...
Files:
dashboard/src/components/BookingForm.vuedashboard/src/components/BookingSummary.vue
{main.js,**/*.vue}
📄 CodeRabbit inference engine (.github/instructions/frappe-ui.instructions.md)
For Options API components using resources, register the
resourcesPluginin main.js and declare resources under theresourceskey as functions, accessible viathis.$resources.[name]
Files:
dashboard/src/components/BookingForm.vuedashboard/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 frappecan 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_typeandadd_onfields are correctly implemented. This ensures users can only select ticket types and add-ons that belong to the selected event.Note: If
frm.doc.eventis 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_onLink field andistable: 1setting are correctly defined.buzz/api.py (2)
174-181: LGTM!The
coupon_codeparameter integration is clean. The optional parameter withNonedefault allows backward compatibility, and the actual coupon validation occurs duringEventBooking.validate()viaapply_coupon_if_applicable().
939-973: Well-structured coupon validation endpoint.The validation flow is correct:
- Existence check
- Event applicability
- Usage availability
- 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()inEventBooking.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
couponTypeas 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_codeas an editable Link field allows users to apply couponsdiscount_amountas read-only Currency ensures the discount is computed server-side and cannot be manipulated by usersbuzz/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:
- Net amount is calculated first
- Discount is applied to net amount
- 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 thannet_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
discountedcounter 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_bookingstest 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
couponvariable 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_couponAPI 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
There was a problem hiding this 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
📒 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_usedandfree_tickets_claimedensure accurate tracking.
| { | ||
| "depends_on": "eval:doc.coupon_type == 'Discount'", | ||
| "fieldname": "discount_value", | ||
| "fieldtype": "Float", | ||
| "label": "Discount Value", | ||
| "mandatory_depends_on": "eval:doc.coupon_type == 'Discount'" | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find and examine the Python controller file
fd coupon_code.py --type fRepository: 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.jsonRepository: 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
fiRepository: 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.
There was a problem hiding this 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
applyCouponfunction 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
discountAmountever exceedsnetAmountdue to floating-point issues or edge cases,amountAfterDiscountcould become negative. Consider addingMath.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_usedmethod 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-951andbuzz/ticketing/doctype/event_booking/event_booking.py:188-192) properly check bothis_valid_for_event()andis_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
📒 Files selected for processing (2)
buzz/ticketing/doctype/coupon_code/coupon_code.pydashboard/src/components/BookingForm.vue
🧰 Additional context used
📓 Path-based instructions (2)
**/*.vue
📄 CodeRabbit inference engine (.github/instructions/frappe-ui.instructions.md)
**/*.vue: UsecreateResource()from frappe-ui to make backend API calls with configuration for url, params, and methods likefetch()andsubmit()in Vue components
UsecreateDocumentResource()to manage individual document records with support for whitelisted methods, events (onError, onSuccess, transform), and methods likereload(),setValue(),delete(), and custom whitelisted method calls
UseuseList()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)
UseListViewcomponent with propscolumns,rows,row-key, andoptionsto render tabular data with support for selection, tooltips, resizable columns, and custom rendering slots
Define ListView columns with requiredlabelandkeyproperties, optionalwidth(as multiplier or px/rem),align(start/left, center/middle, end/right), and custom attributes for rendering
Define ListView rows with unique identifier matching therow-keyprop, and field values as strings or objects withlabelproperty for custom rendering
For grouped ListView rows, structure data as array of objects withgroupstring,collapsedboolean, androwsarray containing row objects
UseBadgecomponent with propsvariant('solid', 'subtle', 'outline', 'ghost'),theme('gray', 'blue', 'green', 'orange', 'red'), andsize('sm', 'md', 'lg') for displaying status indicators
UseFormControlcomponent withtypeprop ('text', 'number', 'email', 'date', 'password', 'search', 'textarea', 'select', 'autocomplete', 'checkbox'),size('sm', 'md', 'lg', 'xl'),variant('subtle', 'outline'), and optionalprefixandsuffixslots
Usetoastfrom frappe-ui for notifications with methodstoast.success(),toast.warning(), andtoast.error()to provide user feedback
UseDropdowncomponent withoptionsprop containing action items withlabel,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
resourcesPluginin main.js and declare resources under theresourceskey as functions, accessible viathis.$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.pydashboard/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 || couponAppliedmeans 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
matchingAttendeesCountis 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:removeCouponproperly 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"]andDF.Literal["Percentage", "Flat Amount"]are false positives. This is valid Frappe auto-generated type annotation syntax within aTYPE_CHECKINGblock, using Frappe's customDF.Literaltype 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 = 0as unlimited.
99-105: Properties provide convenient read-only access.The
@propertydecorators expose computed values cleanly. Note these query the database on each access, so callers should cache results if needed multiple times.
There was a problem hiding this 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
📒 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_countis not set, and the properties provide clean access to computed values.
| 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")) |
There was a problem hiding this comment.
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"))There was a problem hiding this 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 forticket_typeandnumber_of_free_ticketsfor Free Tickets coupons.The validation ensures
eventis required for Free Tickets, butticket_typeandnumber_of_free_ticketsare also critical. Line 89 inget_free_tickets_claimed()and the discount logic inevent_booking.pyassumeticket_typeexists.🔎 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
📒 Files selected for processing (6)
buzz/api.pybuzz/ticketing/doctype/coupon_code/coupon_code.pybuzz/ticketing/doctype/coupon_code/test_coupon_code.pybuzz/ticketing/doctype/event_booking/event_booking.pydashboard/src/components/BookingForm.vuedashboard/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: UsecreateResource()from frappe-ui to make backend API calls with configuration for url, params, and methods likefetch()andsubmit()in Vue components
UsecreateDocumentResource()to manage individual document records with support for whitelisted methods, events (onError, onSuccess, transform), and methods likereload(),setValue(),delete(), and custom whitelisted method calls
UseuseList()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)
UseListViewcomponent with propscolumns,rows,row-key, andoptionsto render tabular data with support for selection, tooltips, resizable columns, and custom rendering slots
Define ListView columns with requiredlabelandkeyproperties, optionalwidth(as multiplier or px/rem),align(start/left, center/middle, end/right), and custom attributes for rendering
Define ListView rows with unique identifier matching therow-keyprop, and field values as strings or objects withlabelproperty for custom rendering
For grouped ListView rows, structure data as array of objects withgroupstring,collapsedboolean, androwsarray containing row objects
UseBadgecomponent with propsvariant('solid', 'subtle', 'outline', 'ghost'),theme('gray', 'blue', 'green', 'orange', 'red'), andsize('sm', 'md', 'lg') for displaying status indicators
UseFormControlcomponent withtypeprop ('text', 'number', 'email', 'date', 'password', 'search', 'textarea', 'select', 'autocomplete', 'checkbox'),size('sm', 'md', 'lg', 'xl'),variant('subtle', 'outline'), and optionalprefixandsuffixslots
Usetoastfrom frappe-ui for notifications with methodstoast.success(),toast.warning(), andtoast.error()to provide user feedback
UseDropdowncomponent withoptionsprop containing action items withlabel,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
resourcesPluginin main.js and declare resources under theresourceskey as functions, accessible viathis.$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.pydashboard/src/components/BookingForm.vuebuzz/ticketing/doctype/coupon_code/coupon_code.pybuzz/api.pybuzz/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_couponfunction properly validates coupon existence, event applicability, and usage availability before returning type-specific information. The use offrappe.get_cached_docis 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 theapply_coupon_if_applicablemethod 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.Currencytype 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:
- Calculates remaining free tickets across bookings
- Only discounts attendees with matching ticket_type
- Applies free add-on discounts for eligible attendees
- Throws a clear error if no eligible attendees exist
One note: The
attendee.amount = 0modification 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 isnet_amount - discount_amountafter 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
FormControlfollows the coding guidelines for frappe-ui components.
390-441: LGTM! Client-side discount calculations mirror server-side logic.The
discountAmountcomputed property correctly:
- Handles Free Tickets by counting matching attendees and applying ticket + add-on discounts
- Handles Discount coupons with percentage and flat amount calculations
- Uses
Math.min()to cap flat discounts at net amountThis ensures the UI preview matches what the server will calculate.
546-602: LGTM! Proper error handling in coupon functions.The
applyCouponfunction 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
removeCouponfunction properly resets all coupon-related state.
702-709: LGTM! Coupon code correctly included in booking payload.The
coupon_codeis 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
autonamemethod correctly generates an 8-character uppercase hash when no code is provided, using Frappe's built-ingenerate_hashfunction.
76-93: LGTM! Free tickets tracking now correctly filters by ticket_type.The query properly joins
EventBookingAttendeewithEventBookingand filters by:
coupon_codematchdocstatus == 1(submitted bookings only)ticket_typematch (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_eventmethod correctly:
- Checks if coupon is active
- Allows usage if no event/category restriction is set
- Validates against specific event if set
- 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
setUpmethod 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, andtotal_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
TestValidateCouponAPIclass verifies:
- Discount coupons return correct type and value info
- Free Tickets coupons return correct remaining_tickets
- Invalid codes return proper error response
closes: #33
https://drive.google.com/file/d/16QN09a5FopthD44UcVBzv6OSRbfNHlU7/view?usp=sharing
Summary by CodeRabbit
New Features
Administration
Tests
✏️ Tip: You can customize this high-level summary in your review settings.