Adds the multi-step website registration at /school/register (parent -> student -> class), carrying state across steps in the request session (auth='public' - a family with no account yet can register). Finalizing creates/finds the parent partner by email, creates the child partner + student, and creates the enrollment as 'enrolled' or 'waitlist' depending on whether the chosen class.fee_product_id/max_students still has room - generating a draft fee invoice when the class has a fee (a new class.fee/fee_product_id, following the same auto-created-product pattern as community_membership's tiers). LMS glue: enrollment create/write now auto-enrols the student's partner into the class's slide.channel via _action_add_members() when state becomes 'enrolled', and deactivates the slide.channel.partner membership on withdrawal. An hourly cron (_cron_promote_waitlist) fills freed seats from the waitlist in enrollment-date order and emails the parent. Adds scripts/migrate_classroom.py: a standalone, dependency-free (stdlib only) JSON-RPC script that reads a title,url CSV and creates slide.slide records in a target channel - YouTube links become published video slides (source_type='external' lets Odoo's own compute fields resolve youtube_id automatically), everything else becomes an unpublished document slide flagged for manual re-upload/review, since the script can't read Google Drive content itself. Idempotent by (channel_id, title); --dry-run and --force supported. Verified against a live Odoo 19 + Postgres 16 container: 14/14 automated tests pass, plus full manual live runs - walked all three registration steps over real HTTP (with CSRF tokens) and confirmed the resulting enrollment and draft invoice; ran the migration script twice against a real channel and confirmed the second run skipped both already-created slides, with the YouTube slide's youtube_id correctly auto-derived. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
87 lines
3.9 KiB
Python
87 lines
3.9 KiB
Python
from odoo import http
|
|
from odoo.http import request
|
|
|
|
SESSION_PARENT_KEY = 'school_reg_parent'
|
|
SESSION_STUDENT_KEY = 'school_reg_student'
|
|
|
|
|
|
class SchoolRegistrationController(http.Controller):
|
|
|
|
@http.route(['/school/register'], type='http', auth='public', website=True)
|
|
def register_parent(self, **kwargs):
|
|
if http.request.httprequest.method == 'POST':
|
|
request.session[SESSION_PARENT_KEY] = {
|
|
'name': kwargs.get('name', '').strip(),
|
|
'email': kwargs.get('email', '').strip(),
|
|
'phone': kwargs.get('phone', '').strip(),
|
|
}
|
|
return request.redirect('/school/register/student')
|
|
return request.render('community_school.registration_step_parent', {})
|
|
|
|
@http.route(['/school/register/student'], type='http', auth='public', website=True)
|
|
def register_student(self, **kwargs):
|
|
if not request.session.get(SESSION_PARENT_KEY):
|
|
return request.redirect('/school/register')
|
|
if request.httprequest.method == 'POST':
|
|
request.session[SESSION_STUDENT_KEY] = {
|
|
'name': kwargs.get('name', '').strip(),
|
|
'date_of_birth': kwargs.get('date_of_birth') or False,
|
|
'health_notes': kwargs.get('health_notes', '').strip(),
|
|
'emergency_contact_name': kwargs.get('emergency_contact_name', '').strip(),
|
|
'emergency_contact_phone': kwargs.get('emergency_contact_phone', '').strip(),
|
|
}
|
|
return request.redirect('/school/register/class')
|
|
return request.render('community_school.registration_step_student', {})
|
|
|
|
@http.route(['/school/register/class'], type='http', auth='public', website=True)
|
|
def register_class(self, **kwargs):
|
|
if not request.session.get(SESSION_PARENT_KEY) or not request.session.get(SESSION_STUDENT_KEY):
|
|
return request.redirect('/school/register')
|
|
|
|
if request.httprequest.method == 'POST':
|
|
return self._finalize_registration(int(kwargs.get('class_id', 0)))
|
|
|
|
classes = request.env['community.school.class'].sudo().search([('state', '=', 'open')])
|
|
return request.render('community_school.registration_step_class', {'classes': classes})
|
|
|
|
def _finalize_registration(self, class_id):
|
|
Partner = request.env['res.partner'].sudo()
|
|
parent_data = request.session[SESSION_PARENT_KEY]
|
|
student_data = request.session[SESSION_STUDENT_KEY]
|
|
|
|
parent = Partner.search([('email', '=', parent_data['email'])], limit=1) if parent_data['email'] else Partner
|
|
if not parent:
|
|
parent = Partner.create({
|
|
'name': parent_data['name'],
|
|
'email': parent_data['email'],
|
|
'phone': parent_data['phone'],
|
|
})
|
|
|
|
child_partner = Partner.create({'name': student_data['name']})
|
|
student = request.env['community.school.student'].sudo().create({
|
|
'partner_id': child_partner.id,
|
|
'parent_partner_id': parent.id,
|
|
'date_of_birth': student_data['date_of_birth'],
|
|
'health_notes': student_data['health_notes'],
|
|
'emergency_contact_name': student_data['emergency_contact_name'],
|
|
'emergency_contact_phone': student_data['emergency_contact_phone'],
|
|
})
|
|
|
|
klass = request.env['community.school.class'].sudo().browse(class_id)
|
|
state = 'enrolled' if klass.enrolled_count < klass.max_students else 'waitlist'
|
|
enrollment = request.env['community.school.enrollment'].sudo().create({
|
|
'student_id': student.id,
|
|
'class_id': klass.id,
|
|
'state': state,
|
|
})
|
|
if state == 'enrolled' and klass.fee:
|
|
enrollment._create_fee_invoice()
|
|
|
|
request.session.pop(SESSION_PARENT_KEY, None)
|
|
request.session.pop(SESSION_STUDENT_KEY, None)
|
|
|
|
return request.render('community_school.registration_done', {
|
|
'enrollment': enrollment,
|
|
'waitlisted': state == 'waitlist',
|
|
})
|