Files
xadm 786cbc724f
CI / Server (push) Has been cancelled
Linters / Frappe Linter (push) Has been cancelled
Linters / Vulnerable Dependency Check (push) Has been cancelled
UI Tests / Playwright E2E Tests (push) Has been cancelled
Initialize fork and rebrand app to event_manager
2026-05-11 09:56:57 +02:00

369 lines
12 KiB
Markdown

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
At the start of every session, read `.claude/memory/memory.md` to load project context.
After completing significant work (new patterns, architectural decisions, solved problems),
update `.claude/memory/memory.md`. Keep it under 300 lines — summarize when it grows.
---
## This defines the best practices to write backend code in the Frappe Framework
* Frappe Framework is a full-stack web application framework that contains all the necessary components for building modern web applications.
* It provides background workers using Redis, real-time updates using sockets, and a database layer using MariaDB.
* Bench is the official command-line tool for managing Frappe applications.
## Backend Development
### JSON & Request Handling
* Always use built-in functions for parsing JSON:
* `frappe.parse_json` (handles dicts, lists, and JSON strings safely)
* Never use `json.loads` directly on request data.
* For outbound HTTP requests (calling external APIs), use:
* `frappe.integration.utils.make_get_request`
* `frappe.integration.utils.make_post_request`
* `frappe.integration.utils.make_put_request`
* `frappe.integration.utils.make_patch_request`
### Datatype Conversion & Utilities
* For converting datatypes (e.g. str → int, str → float, etc.) use built-in helpers:
* `frappe.utils.data.cint`
* `frappe.utils.data.cstr`
* `frappe.utils.data.flt`
* `frappe.utils.data.getdate`
* `frappe.utils.data.get_datetime`
* `frappe.utils.data` contains most conversion and formatting helpers you will ever need:
* date / datetime parsing
* currency formatting
* number formatting
* Do NOT create custom utility functions for these conversions.
* If unsure, ask before implementing.
### DocType Access Patterns
* When fetching an existing DocType, prefer:
* `frappe.get_cached_doc`
* Use `frappe.get_doc` when:
* creating a new document
* To create a new doc go to bench console via bench --site sitename console and use frappe.new_doc("DocType") and then create the doc, don't create the doc via json as the validations doesn't run
### Optimization
* Don't use get_doc or get_cached_doc inside for loop it creates n+1 db problem use frappe.get_all with all the params required and then loop over that list
### Database Access
* Prefer ORM methods:
* `frappe.get_all`
* `frappe.get_list`
* `frappe.db.get_value`
* Avoid raw SQL absolutely.
### Permissions & Security
* Always respect user permissions.
* Use `ignore_permissions=True` only when absolutely required and justified.
### Background Jobs & Performance
* For long-running or heavy operations, always use:
* `frappe.enqueue`
* Never block request-response cycles with heavy business logic.
### Error Handling & Logging
* Use `frappe.throw` or specific exceptions like `frappe.ValidationError` for user-facing errors.
* Use `frappe.log_error` for unexpected or system-level exceptions.
* Avoid bare `except:` blocks.
### General Guidelines
* Prefer framework conventions over custom implementations.
* Keep business logic out of controllers where possible.
* Write readable, predictable, and maintainable code.
## Frontend Development
1. Always use async/await; avoid callback-based patterns and nested promises.
2. Use Frappe-provided APIs for server calls: `frappe.call` with `async: true`. Prefer Promise-based usage over callbacks.
3. Use Frappe's global JS helpers instead of native JS equivalents:
* `cstr()` instead of `String()`
* `cint()` instead of `parseInt()`
* `flt()` instead of `parseFloat()`
* `is_null()` instead of manual null/undefined/empty checks
* `format_currency()` for currency formatting
## Crawling
Always use gemini as much as possible for getting the context, to get the help use gemini --help
For checking if the site works you can use the agent-browser use agent-browser --help to get the context for it
## Commands
### Frontend (Dashboard)
```bash
# dev server
yarn dev # or: cd dashboard && yarn dev
# build for production
yarn build # outputs to event_manager/public/dashboard + event_manager/www/dashboard.html
# lint/format frontend
cd dashboard && yarn lint
```
### Backend (Python)
Always run bench migrate after doctype schema changes.
```bash
# linting/formatting (via pre-commit)
pre-commit run --all-files
# run ruff directly
ruff check event_manager/
ruff format event_manager/
# install app to site
bench --site [site-name] install-app event_manager
```
Use bench --help to see how to work with frappe bench, e.g. bench execute, bench console, etc. are very useful
### Testing
There are unit tests, run using bench run-tests. Site name is event_manager.localhost, but if not found, ask user for it. The credentials are Administrator/admin.
* To test in UI, use agent-browser.
* For frontend changes use :8080 since yarn dev server is running.
* Use in headed mode unless specified
## Architecture
**Three-tier stack:**
1. **Backend**: Frappe Framework (Python) - DocTypes, API, permissions, scheduler
2. **Dashboard**: Vue 3 + FrappeUI + Vite - attendee/sponsor/checkin UI
**Core entity**: `Pohodex Event Manager Event` DocType drives everything (tickets, sponsors, schedule, payments).
**Main modules** (inside `event_manager/`):
- `events/` - Event, Venue, Category, Talks, Sponsors, Check-ins
- `ticketing/` - Bookings, Tickets, Add-ons, Cancellations, Coupons
- `proposals/` - Talk Proposals, Sponsorship Enquiries
- `event_manager/` - Settings, Custom Fields
- `api.py` - whitelisted API methods for dashboard
- `payments.py` - integration with frappe/payments app
**Frontend structure** (inside `dashboard/`):
- `src/pages/` - route components (BookTickets, TicketDetails, CheckInScanner, etc)
- `src/components/` - BookingForm, dialogs, shared UI
- `src/composables/` - reusable logic (useTicketValidation, usePaymentSuccess, etc)
- `src/data/` - frappe-ui resources for API calls
- Vite builds to `event_manager/public/dashboard/`, router base is `/dashboard`
**Key flows:**
- Booking: load event data → fill form → create booking → generate payment link → on payment auth → submit booking → generate tickets + QR + email
- Ticket actions: transfer, cancel, change add-on (window checks from Pohodex Event Manager Settings)
- Sponsorship: enquiry → approval → payment link → payment auth → create sponsor record
- Check-in: scan QR → validate → create check-in record (requires Frontdesk Manager role)
**Integrations:**
- `frappe/payments` required for payment gateways
- `buildwithhussain/zoom_integration` optional for webinar creation/registration
## Key Paths for Common Tasks
**Booking changes**: `event_manager/api.py`, `event_manager/ticketing/doctype/event_booking/`, `dashboard/src/components/BookingForm.vue`
**Ticket lifecycle**: `event_manager/ticketing/doctype/event_ticket/`, `dashboard/src/pages/TicketDetails.vue`
**Sponsorships**: `event_manager/proposals/doctype/sponsorship_enquiry/`, `dashboard/src/pages/SponsorshipDetails.vue`
**Check-in**: `event_manager/api.py` (validate_ticket_for_checkin, checkin_ticket), `dashboard/src/pages/CheckInScanner.vue`
**Event config**: `event_manager/events/doctype/buzz_event/`
**Reports**: `event_manager/events/report/` and `event_manager/ticketing/report/`
## Joining or creating report
"Never write `frappe.db.sql` again"
===========================================================
1. **Ban `frappe.db.sql` in new code**
* Add a pre-commit rule or CI step that greps for `\.db\.sql` and fails the build.
* Legacy code => wrap in `frappe.db.sql("...", as_dict=1)` and add a `# TODO-QB` comment so the next refactor is trackable.
2. **Use the typed entry point**
```python
from frappe.query_builder import DocType, Field
from frappe.query_builder.functions import Count, Sum, Coalesce, Date
```
Never `import pypika` directly; the `frappe.qb` namespace already returns the correct `MariaDB/PostgreSQL` dialect.
3. **Parameterise, never interpolate**
```python
# Bad
frappe.db.sql(f"... {user_input}") # injection bomb
# Good
frappe.qb.from_(...).where(table.field == user_input) # auto-escaped
```
4. **Prefer joins over N+1**
```python
so = DocType("Sales Order")
si = DocType("Sales Invoice")
query = (
frappe.qb.from_(so)
.left_join(si)
.on(so.name == si.sales_order)
.select(so.name, si.name)
.where(so.customer == customer)
)
```
One round-trip, no loops.
5. **Sub-queries > raw SQL strings**
Need *"latest row per group"*?
```python
latest = (
frappe.qb.from_(si)
.select(si.name)
.where(si.sales_order == so.name)
.orderby(si.creation, order=Order.desc)
.limit(1)
)
query = frappe.qb.from_(so).where(so.name == latest)
```
Keeps everything composable and dialect-agnostic.
6. **Use `case` for conditional aggregates**
```python
from frappe.query_builder.functions import Case
paid_amt = Sum(
Case()
.when(si.status == "Paid", si.grand_total)
.else_(0)
)
```
7. **Respect Frappe field casing**
* SQL column: `grand_total`
* Frappe field: `grand_total`
* No back-ticks needed; QB adds the correct quotes per DB.
8. **Use `as_dict=True` or ORM objects**
```python
rows = query.run(as_dict=True) # list[dict]
docs = query.run(as_dict=False) # list[tuple]
obj = frappe.get_doc("Doctype", pk) # when you need the full DocType hooks
```
9. **Pagination with `limit_page_length` and `limit_start`**
```python
query = query.limit(limit_page_length).offset(limit_start)
```
Same pattern the REST API uses.
10. **Index-friendly WHERE order**
Put indexed columns first (`company`, `customer`, `status`) so MariaDB/PostgreSQL can use composite indexes.
11. **Avoid `SELECT *` in reports**
Explicit list of fields keeps wire-size small and prevents breaking changes when new fields are added.
13. **Cache heavy aggregations**
```python
@frappe.whitelist()
@redis_cache(ttl=300)
def get_dashboard_stats(company):
inv = DocType("Sales Invoice")
total = frappe.qb.from_(inv).select(Sum(inv.grand_total)).where(inv.company == company).run()
return total[0][0] or 0
```
Quick migration template
----------------------
Legacy:
```python
rows = frappe.db.sql("""
select name, grand_total
from `tabSales Invoice`
where customer = %s
and docstatus = 1
""", customer, as_dict=1)
```
QB equivalent:
```python
si = DocType("Sales Invoice")
rows = (
frappe.qb.from_(si)
.select(si.name, si.grand_total)
.where((si.customer == customer) & (si.docstatus == 1))
.run(as_dict=True)
)
```
### Report Patterns
- Entry: `def execute(filters): return get_columns(), get_data(filters)`
- QB imports: `from frappe.query_builder import DocType` + `functions.Sum, Case, Count`
- Build lookup maps first, then loop + merge (avoid N+1)
- Caching: `@redis_cache(ttl=seconds)` for conversion factors
### Token-Saving Workflow
- Default to `/model haiku` for routine edits, `/model sonnet` for moderate tasks
- Use `/model opus` ONLY for architecture, debugging complex issues
- Use `/compact` after completing each subtask
- Use `gemini -p "prompt"` via stdin to read/summarize files without burning Claude tokens
- Scope tasks narrowly: one feature/fix per session
- Dump progress to `.claude/memory/scratch.md` before session ends
- At session START: read `.claude/memory/scratch.md` — if it has content, resume from there
- At session END (or when user says "dump progress"): fill in scratch.md with current task state
- After resuming from scratch.md, clear it once the task is complete
### Variable and Function naming convention
- always use full names for variables don't use abbreviations for ex: use
"for row in rows" instead of "for r in rows"
proxy_sku = DocType("Proxy SKU") instead of ps = DocType("Proxy SKU")
- avoid starting python functions with underscore "_" unless it's a private version behind a whitelisted function (e.g. `get_dial_codes` (whitelisted) -> `_get_dial_codes` (cached logic))
- use camelCase in JS and follow the surrounding code style in the project
- always put imports at the top of the file, never inside functions
## Notes
- Read `ARCHITECTURE.md` for comprehensive details on data model, API surface, flows