296 lines
12 KiB
Python
296 lines
12 KiB
Python
# -*- coding: utf-8 -*-
|
|
import base64
|
|
import json
|
|
import logging
|
|
from datetime import date
|
|
|
|
from odoo import fields, http
|
|
from odoo.http import request
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
ELIGIBILITY_KEYS = (
|
|
'tncsc_born_tamil_nadu',
|
|
'tncsc_pio_oci',
|
|
'tncsc_tamil_speaker_resident',
|
|
'tncsc_family_tamil_indian',
|
|
'tncsc_other_citizenship',
|
|
)
|
|
|
|
MONTH_OPTIONS = [
|
|
('1', 'January'), ('2', 'February'), ('3', 'March'), ('4', 'April'),
|
|
('5', 'May'), ('6', 'June'), ('7', 'July'), ('8', 'August'),
|
|
('9', 'September'), ('10', 'October'), ('11', 'November'), ('12', 'December'),
|
|
]
|
|
|
|
|
|
class TNCSCMembershipController(http.Controller):
|
|
|
|
@http.route('/membership/check_username', type='jsonrpc', auth='public', methods=['POST'], website=True)
|
|
def check_username(self, username=None, **kwargs):
|
|
if not username:
|
|
return {'available': False, 'message': 'Username is required.'}
|
|
username = username.strip()
|
|
if len(username) < 3:
|
|
return {'available': False, 'message': 'Username must be at least 3 characters.'}
|
|
|
|
count = request.env['res.users'].sudo().search_count([('login', '=', username)])
|
|
if count > 0:
|
|
return {'available': False, 'message': 'Username is already taken.'}
|
|
return {'available': True, 'message': 'Username is available.'}
|
|
|
|
@http.route('/membership/check_email', type='jsonrpc', auth='public', methods=['POST'], website=True)
|
|
def check_email(self, email=None, **kwargs):
|
|
if not email:
|
|
return {'available': False, 'message': 'Email is required.'}
|
|
email = email.strip()
|
|
if '@' not in email or '.' not in email:
|
|
return {'available': False, 'message': 'Please enter a valid email address.'}
|
|
|
|
user_count = request.env['res.users'].sudo().search_count([('email', '=', email)])
|
|
partner_count = request.env['res.partner'].sudo().search_count([
|
|
('email', '=', email),
|
|
('membership_tier_id', '!=', False),
|
|
])
|
|
if user_count > 0 or partner_count > 0:
|
|
return {'available': False, 'message': 'An account with this email already exists.'}
|
|
return {'available': True, 'message': 'Email is available.'}
|
|
|
|
def _get_form_context(self, selected_code=None, errors=None, form_data=None):
|
|
Tier = request.env['community.membership.tier'].sudo()
|
|
tiers = Tier.search([('active', '=', True)], order='sequence, id')
|
|
|
|
selected_tier = None
|
|
if selected_code:
|
|
selected_tier = tiers.filtered(lambda t: t.code == selected_code)
|
|
if not selected_tier and selected_code.isdigit():
|
|
selected_tier = tiers.filtered(lambda t: t.id == int(selected_code))
|
|
if selected_tier:
|
|
selected_tier = selected_tier[0]
|
|
if not selected_tier and tiers:
|
|
selected_tier = tiers[0]
|
|
|
|
countries = request.env['res.country'].sudo().search([], order='name asc')
|
|
ca_country = request.env.ref('base.ca', raise_if_not_found=False)
|
|
if not ca_country:
|
|
ca_country = request.env['res.country'].sudo().search([('code', '=', 'CA')], limit=1)
|
|
|
|
current_year = fields.Date.today().year
|
|
birth_years = list(range(current_year - 14, current_year - 100, -1))
|
|
|
|
return {
|
|
'tiers': tiers,
|
|
'selected_tier': selected_tier,
|
|
'countries': countries,
|
|
'default_country_id': ca_country.id if ca_country else False,
|
|
'birth_months': MONTH_OPTIONS,
|
|
'birth_years': birth_years,
|
|
'errors': errors or [],
|
|
'form_data': form_data or {},
|
|
}
|
|
|
|
@http.route(['/membership/join'], type='http', auth='public', methods=['GET', 'POST'], website=True, sitemap=True)
|
|
def membership_join(self, **post):
|
|
if request.httprequest.method == 'GET':
|
|
tier_code = post.get('tier') or 'FAM'
|
|
values = self._get_form_context(selected_code=tier_code)
|
|
return request.render('tncsc_deployment.page_membership_join', values)
|
|
|
|
# Handle POST submission
|
|
errors = []
|
|
tier_id = post.get('tier_id')
|
|
username = (post.get('username') or '').strip()
|
|
email = (post.get('email') or '').strip().lower()
|
|
password = post.get('password') or ''
|
|
confirm_password = post.get('confirm_password') or ''
|
|
first_name = (post.get('first_name') or '').strip()
|
|
last_name = (post.get('last_name') or '').strip()
|
|
phone = (post.get('phone') or '').strip()
|
|
street = (post.get('street') or '').strip()
|
|
city = (post.get('city') or '').strip()
|
|
zip_code = (post.get('zip') or '').strip()
|
|
country_id = post.get('country_id')
|
|
birth_month = post.get('birth_month') or ''
|
|
birth_year = post.get('birth_year') or ''
|
|
terms_accepted = bool(post.get('membership_terms_accepted'))
|
|
coupon_code = (post.get('coupon_code') or '').strip()
|
|
|
|
tier = None
|
|
if tier_id:
|
|
tier = request.env['community.membership.tier'].sudo().browse(int(tier_id))
|
|
if not tier.exists():
|
|
tier = None
|
|
|
|
if not tier:
|
|
errors.append('Please select a valid membership plan.')
|
|
|
|
if not username or len(username) < 3:
|
|
errors.append('Username must be at least 3 characters long.')
|
|
elif request.env['res.users'].sudo().search_count([('login', '=', username)]) > 0:
|
|
errors.append(f'The username "{username}" is already taken. Please choose another username.')
|
|
|
|
if not email or '@' not in email:
|
|
errors.append('Please provide a valid email address.')
|
|
else:
|
|
existing_user = request.env['res.users'].sudo().search_count([('email', '=', email)])
|
|
existing_partner = request.env['res.partner'].sudo().search_count([
|
|
('email', '=', email),
|
|
('membership_tier_id', '!=', False),
|
|
])
|
|
if existing_user > 0 or existing_partner > 0:
|
|
errors.append(f'An account or membership application with email "{email}" already exists.')
|
|
|
|
if not password or len(password) < 6:
|
|
errors.append('Password must be at least 6 characters long.')
|
|
elif password != confirm_password:
|
|
errors.append('Passwords do not match.')
|
|
|
|
if not first_name:
|
|
errors.append('First Name is required.')
|
|
if not last_name:
|
|
errors.append('Last Name is required.')
|
|
|
|
if not terms_accepted:
|
|
errors.append('You must accept the TNCSC Constitution and Membership Terms to proceed.')
|
|
|
|
# Minimum age validation if tier requires it
|
|
today = date.today()
|
|
calculated_age = None
|
|
if birth_year and birth_year.isdigit():
|
|
b_year = int(birth_year)
|
|
b_month = int(birth_month) if birth_month and birth_month.isdigit() else 1
|
|
calculated_age = today.year - b_year
|
|
if today.month < b_month:
|
|
calculated_age -= 1
|
|
|
|
if tier and tier.min_age and calculated_age < tier.min_age:
|
|
errors.append(
|
|
f'The {tier.name} plan requires a minimum age of {tier.min_age} years '
|
|
f'(calculated age: {calculated_age}).'
|
|
)
|
|
|
|
# File uploads
|
|
avatar_file = request.httprequest.files.get('avatar_file')
|
|
proof_file = request.httprequest.files.get('proof_file')
|
|
|
|
if tier and tier.requires_proof and not proof_file:
|
|
# Alert requirement if mandatory
|
|
pass
|
|
|
|
if errors:
|
|
values = self._get_form_context(
|
|
selected_code=tier.code if tier else post.get('tier_id'),
|
|
errors=errors,
|
|
form_data=post,
|
|
)
|
|
return request.render('tncsc_deployment.page_membership_join', values)
|
|
|
|
# Create Member Partner
|
|
partner_vals = {
|
|
'first_name': first_name,
|
|
'last_name': last_name,
|
|
'name': f"{first_name} {last_name}".strip(),
|
|
'email': email,
|
|
'phone': phone,
|
|
'street': street,
|
|
'city': city,
|
|
'zip': zip_code,
|
|
'country_id': int(country_id) if country_id and country_id.isdigit() else False,
|
|
'birth_month': str(birth_month) if birth_month else False,
|
|
'birth_year': int(birth_year) if birth_year and birth_year.isdigit() else False,
|
|
'membership_tier_id': tier.id,
|
|
'membership_approval_state': 'pending',
|
|
'membership_terms_accepted': True,
|
|
'company_id': request.env.company.id,
|
|
}
|
|
|
|
# TNCSC Eligibility Answers
|
|
for k in ELIGIBILITY_KEYS:
|
|
ans = post.get(k)
|
|
if ans in ('yes', 'no'):
|
|
partner_vals[k] = ans
|
|
|
|
if avatar_file and avatar_file.filename:
|
|
try:
|
|
partner_vals['image_1920'] = base64.b64encode(avatar_file.read())
|
|
except Exception as e:
|
|
_logger.warning("Failed to read uploaded avatar: %s", e)
|
|
|
|
if proof_file and proof_file.filename:
|
|
try:
|
|
partner_vals['membership_proof_document'] = base64.b64encode(proof_file.read())
|
|
partner_vals['membership_proof_filename'] = proof_file.filename
|
|
except Exception as e:
|
|
_logger.warning("Failed to read proof document: %s", e)
|
|
|
|
partner = request.env['res.partner'].sudo().create(partner_vals)
|
|
|
|
# Create Portal User
|
|
try:
|
|
portal_group = request.env.ref('base.group_portal')
|
|
user_vals = {
|
|
'name': partner.name,
|
|
'login': username,
|
|
'email': email,
|
|
'partner_id': partner.id,
|
|
'groups_id': [(6, 0, [portal_group.id])],
|
|
}
|
|
user = request.env['res.users'].sudo().create(user_vals)
|
|
user.sudo().password = password
|
|
except Exception as e:
|
|
_logger.exception("Failed to create portal user for new member: %s", e)
|
|
|
|
# Process Family Members if applicable
|
|
if tier.allows_family_members:
|
|
fam_first_names = request.httprequest.form.getlist('family_first_name[]')
|
|
fam_last_names = request.httprequest.form.getlist('family_last_name[]')
|
|
fam_emails = request.httprequest.form.getlist('family_email[]')
|
|
fam_phones = request.httprequest.form.getlist('family_phone[]')
|
|
|
|
for idx in range(len(fam_first_names)):
|
|
f_first = (fam_first_names[idx] or '').strip()
|
|
f_last = (fam_last_names[idx] or '').strip()
|
|
if f_first or f_last:
|
|
f_email = (fam_emails[idx] or '').strip() if idx < len(fam_emails) else ''
|
|
f_phone = (fam_phones[idx] or '').strip() if idx < len(fam_phones) else ''
|
|
request.env['res.partner'].sudo().create({
|
|
'first_name': f_first,
|
|
'last_name': f_last,
|
|
'name': f"{f_first} {f_last}".strip(),
|
|
'email': f_email or False,
|
|
'phone': f_phone or False,
|
|
'family_head_id': partner.id,
|
|
'company_id': request.env.company.id,
|
|
})
|
|
|
|
# Post signup note on partner chatter
|
|
coupon_note = f"<br/><b>Coupon Applied:</b> {coupon_code}" if coupon_code else ""
|
|
partner.sudo().message_post(
|
|
body=(
|
|
f"<b>Online Membership Application Submitted</b><br/>"
|
|
f"<b>Tier:</b> {tier.name} ({tier.code}) - ${tier.price}<br/>"
|
|
f"<b>Username:</b> {username}<br/>"
|
|
f"<b>Status:</b> Pending Board Approval"
|
|
f"{coupon_note}"
|
|
),
|
|
subtype_xmlid='mail.mt_note',
|
|
)
|
|
|
|
# Send application received confirmation email
|
|
try:
|
|
template = request.env.ref('community_membership.mail_template_membership_application_received', raise_if_not_found=False)
|
|
if template:
|
|
template.sudo().send_mail(partner.id, force_send=True)
|
|
except Exception as e:
|
|
_logger.warning("Failed to send application confirmation email: %s", e)
|
|
|
|
return request.render('tncsc_deployment.page_membership_thank_you', {
|
|
'partner': partner,
|
|
'tier': tier,
|
|
'username': username,
|
|
})
|
|
|
|
@http.route('/membership/thank-you', type='http', auth='public', website=True)
|
|
def membership_thank_you(self, **kw):
|
|
return request.render('tncsc_deployment.page_membership_thank_you', {})
|