[ADD] service_event_base: for managing service events and bookings#5
[ADD] service_event_base: for managing service events and bookings#5shsa-odoo wants to merge 9 commits into
Conversation
Commit 1: Module Foundation + Hooks
- Module structure (`__manifest__.py`, `__init__.py`)
- Pre-init hook (table preparation, SQL cleanup)
- Post-init hook (default data, categories)
- Init function (indexes, SQL views)
Module Structure:
service_event_base/
├── __init__.py # Module entry point
├── __manifest__.py # Module metadata and dependencies
├── hooks.py # Lifecycle hooks (pre-init, post-init)
├── models/ # Business models
│ ├── __init__.py
│ ├── service_event_category.py
│ ├── service_event_tag.py
│ ├── service_event.py
│ └── service_booking.py
├── security/ # Access control
│ ├── security.xml
│ └── ir.model.access.csv
├── data/ # Default data
│ ├── sequences.xml
│ └── categories.xml
├── views/ # User interface
│ ├── service_event_views.xml
│ ├── service_booking_views.xml
│ └── menus.xml
└── demo/ # Demo data (optional)
└── demo_data.xml
Core ORM implementation with relationships, computed fields, workflow states, constraints, views, and security Implement service.event and service.booking models with full ORM features: - service.event: name, description, price, category, tags, bookings - service.booking: auto-generated numbers, workflow states, validations - Relationships: Many2one, Many2many, One2many - Computed fields: booking_count, amount, display_name - Constraints: SQL (positive prices) and Python (date validation) - Views: Odoo 18 compatible list/form views with state buttons - Security: Full CRUD access rights for both models - Menus: Navigation structure for events and bookings
What We Accomplished: Service Event Model: ✅ Capacity management (capacity field) ✅ Advanced computed fields: booking_count_confirmed (stored) - count of confirmed bookings only total_revenue (stored) - sum of confirmed booking amounts available_seats (non-stored) - real-time availability calculation ✅ Event scheduling: start_datetime, end_datetime, duration Inverse function on end_datetime (bidirectional computation) ✅ Complex Python constraints: Capacity validation (prevent overbooking) Datetime range validation Price consistency checks ✅ SQL constraints (positive capacity, positive duration) Service Booking Model: ✅ Onchange methods: _onchange_event_id() - Auto-fill amount, warn about low availability _onchange_partner_id() - Customer-specific logic placeholder _onchange_booking_date() - Weekend booking warnings ✅ Default value functions demonstrated Views: ✅ Event list view: capacity, booking stats, available seats, revenue ✅ Event form view: Capacity & Availability group, Schedule group
Service Event Model Enhancements: - ✅ Pricing logic - Early bird pricing (early_bird_price, early_bird_deadline) - Discount percentage (discount_percentage) - Final price computation (final_price) - Price calculation method (get_applicable_price) - ✅ Event lifecycle management - State workflow (draft → published → registration_closed → completed/cancelled) - Lifecycle action methods (publish, close_registration, mark_completed, cancel, reset_to_draft) - Registration status (registration_open computed field) - ✅ Business metrics - Fill rate (% of capacity filled) - Revenue per seat - Cancellation rate - ✅ Business validation - Early bird price must be < regular price - Early bird deadline must be before event start - Prevent publishing without price/category - Check booking allowed method Service Booking Model Enhancements: - ✅ Waitlist management - Waitlisted state added to workflow - Auto-waitlist when event full - Waitlist position tracking - Auto-promotion when spots open - Manual promotion method - ✅ Enhanced booking validation - Check event is published and registration open - Prevent duplicate bookings (same customer + event) - Auto-populate amount from event price - Validate event hasn't started - ✅ Business logic - Cascade cancellation (event cancelled → bookings cancelled) - Auto-promotion from waitlist on cancellation Views Updated: - ✅ Event list: state badges, pricing fields, business metrics - ✅ Event form: lifecycle status bar with action buttons, pricing section, metrics dashboard - ✅ Booking list: waitlist state and position - ✅ Booking form: waitlist alert, promote button
Security architecture (Odoo 19 privilege-based approach) 2 security groups with permission comparison table 3-layer security implementation (model → record → field) All 8 record rules documented Files modified with line counts Code examples for privilege, record rules, and field security
…, Graph, Pivot) - Add Kanban views with QWeb templates for events and bookings - Add Calendar views for event scheduling (month/week/day modes) - Add Graph (bar charts) and Pivot views for analytics - Enhance Search views with filters, grouping, and search panels - Add color field to service.event model for visual categorization - Update actions to support multiple view modes
| /src/ | ||
|
|
||
| # markdown documentation files | ||
| *.md No newline at end of file |
There was a problem hiding this comment.
add new line after it.
- Created service_event_website module (separate from base) - Added /events listing page (GET, auth=public) - Added /events/<id> detail page (model converter) - Added /events/book submission (POST, CSRF protected) - Added booking confirmation page - Implemented sitemap integration for SEO - Created QWeb templates (listing, detail, confirmation, error) - Demonstrated: GET/POST methods, auth types, model converters, CSRF protection
Implement 3 JSON-RPC endpoints for dynamic event data retrieval: - /api/event/price: Get pricing with quantity calculation - /api/event/availability: Check real-time seat availability - /api/event/validate: Validate booking data before submission Features: - JSON-RPC 2.0 protocol - CORS support for cross-origin requests - Standardized response format (success/error structure) - Public authentication for frontend access - Comprehensive error handling and validation
Add self-service portal for customers to manage bookings: - /my/bookings listing with pagination (10 per page) - /my/booking/<id> detail view with full information - Sorting: date, name, status - Filtering: all, draft, confirmed, attended, cancelled - Security: record rules restrict to own bookings only - Portal menu integration with booking counter - Responsive Bootstrap 5 templates Portal users can: - View all their bookings - See booking details and event information - Access published events only - Cannot modify or delete bookings through portal Security features: - Portal record rules (partner_id matching) - Access token support for sharing - Published events only for portal/public - Read-only access to bookings
|
|
||
| execute_safe_sql( | ||
| """ | ||
| CREATE EXTENSION IF NOT EXISTS pg_trgm; |
There was a problem hiding this comment.
The pg_trgm module provides functions and operators for determining the similarity of alphanumeric text based on trigram matching.
A trigram is a group of three consecutive characters taken from a string.
For example, the set of trigrams in the string “cat” is “ c”, “ ca”, “cat”, and “at ”. The set of trigrams in the string “foo|bar” is “ f”, “ fo”, “foo”, “oo ”, “ b”, “ ba”, “bar”, and “ar ”.
For more refer : https://www.postgresql.org/docs/current/pgtrgm.html
| currency_id = fields.Many2one( | ||
| 'res.currency', | ||
| string='Currency', | ||
| related='event_id.currency_id', | ||
| store=True, | ||
| help='Currency from the event', | ||
| ) |
There was a problem hiding this comment.
related field is Odoo saying:
“Don’t store your own value. Borrow it from another field.
If the source changes, you change.”
It’s like a mirror. Not a twin. Not a copy. A mirror.
In your case:
Your model does NOT own currency_id
It looks at event_id.currency_id
Whatever currency the event has → this record shows the same one
| # ======================================================================== | ||
|
|
||
| parent_id = fields.Many2one( | ||
| comodel_name='service.event.category', |
There was a problem hiding this comment.
Both are same : )
parent_id = fields.Many2one(
'service.event.category',
string='Parent Category',
)
| child_ids = fields.One2many( | ||
| comodel_name='service.event.category', | ||
| inverse_name='parent_id', | ||
| string='Sub-categories', | ||
| help='Child categories under this category', | ||
| ) |
There was a problem hiding this comment.
inverse_name vs inverse function
inverse_name
• Used with One2many fields
• Defines the database relationship
• Points to the Many2one field (foreign key) on the other model
• Is a string (field name)
• Mandatory for One2many
• No Python logic involved
• Works at ORM / SQL relationship level
inverse function
• Used with computed fields
• Handles write-back logic when a computed field is edited
• Is a Python method
• Optional
• Contains business logic
• Does not define any database relationship
|
|
||
| <!-- Placeholder: Security groups will be defined in Commit 5 --> | ||
|
|
||
| </data> |
| <!-- Rule: Service Users - See Only Published Events --> | ||
| <record id="service_event_user_rule" model="ir.rule"> | ||
| <field name="name">Service Event: User - Published Only</field> | ||
| <field name="model_id" ref="model_service_event"/> |
| <record id="service_event_category_manager_rule" model="ir.rule"> | ||
| <field name="name">Service Event Category: Manager - Full Access</field> | ||
| <field name="model_id" ref="model_service_event_category"/> | ||
| <field name="domain_force">[(1, '=', 1)]</field> | ||
| <field name="groups" eval="[(4, ref('group_service_event_manager'))]"/> | ||
| <field name="perm_read" eval="True"/> | ||
| <field name="perm_write" eval="True"/> | ||
| <field name="perm_create" eval="True"/> | ||
| <field name="perm_unlink" eval="True"/> | ||
| </record> |
There was a problem hiding this comment.
Isn't this already done .csv? Then why this?
Ans:
CSV = whether you can touch the model
Rules = which records your fingers are allowed to touch
| <record id="service_event_manager_rule" model="ir.rule"> | ||
| <field name="name">Service Event: Manager - All Events</field> | ||
| <field name="model_id" ref="model_service_event"/> | ||
| <field name="domain_force">[(1, '=', 1)]</field> |
There was a problem hiding this comment.
domain
• Adds a filter condition
• Is combined (ANDed) with other record rules
• Cannot override other rules
• Respects existing restrictions
• Used for normal / restrictive access
• Safer, additive behavior
Example meaning:
“User can access records that match this condition, along with all other rules.”
Vs
domain_force
• Overrides all other record rule domains
• Replaces the final domain instead of adding to it
• Ignores other restrictive rules
• Used for full access or hard overrides
• Dangerous if misused
Example meaning:
“Forget all other rules — use only this domain.”
Description in progress.