mirror of
https://github.com/bringout/oca-ocb-core.git
synced 2026-04-22 01:11:59 +02:00
19.0 vanilla
This commit is contained in:
parent
d1963a3c3a
commit
2d3ee4855a
7430 changed files with 2687981 additions and 2965473 deletions
|
|
@ -1,21 +1,13 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
import logging
|
||||
import os
|
||||
import pytz
|
||||
|
||||
from odoo import _, api, fields, models, SUPERUSER_ID
|
||||
from odoo.tools import format_date, email_normalize, email_normalize_all
|
||||
from odoo.exceptions import AccessError, ValidationError
|
||||
|
||||
# phone_validation is not officially in the depends of event, but we would like
|
||||
# to have the formatting available in event, not in event_sms -> do a conditional
|
||||
# import just to be sure
|
||||
try:
|
||||
from odoo.addons.phone_validation.tools.phone_validation import phone_format
|
||||
except ImportError:
|
||||
def phone_format(number, country_code, country_phone_code, force_format='INTERNATIONAL', raise_exception=True):
|
||||
return number
|
||||
from odoo.fields import Domain
|
||||
from odoo.tools import email_normalize, format_date, formataddr
|
||||
from odoo.exceptions import ValidationError
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class EventRegistration(models.Model):
|
||||
|
|
@ -23,43 +15,98 @@ class EventRegistration(models.Model):
|
|||
_description = 'Event Registration'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_order = 'id desc'
|
||||
_mail_defaults_to_email = True
|
||||
|
||||
@api.model
|
||||
def _get_random_barcode(self):
|
||||
"""Generate a string representation of a pseudo-random 8-byte number for barcode
|
||||
generation.
|
||||
|
||||
A decimal serialisation is longer than a hexadecimal one *but* it
|
||||
generates a more compact barcode (Code128C rather than Code128A).
|
||||
|
||||
Generate 8 bytes (64 bits) barcodes as 16 bytes barcodes are not
|
||||
compatible with all scanners.
|
||||
"""
|
||||
return str(int.from_bytes(os.urandom(8), 'little'))
|
||||
|
||||
# event
|
||||
event_id = fields.Many2one(
|
||||
'event.event', string='Event', required=True,
|
||||
readonly=True, states={'draft': [('readonly', False)]})
|
||||
'event.event', string='Event', required=True, tracking=True, index=True)
|
||||
is_multi_slots = fields.Boolean(string="Is Event Multi Slots", related="event_id.is_multi_slots")
|
||||
event_slot_id = fields.Many2one(
|
||||
"event.slot", string="Slot", ondelete='restrict', tracking=True, index="btree_not_null",
|
||||
domain="[('event_id', '=', event_id)]")
|
||||
event_ticket_id = fields.Many2one(
|
||||
'event.event.ticket', string='Event Ticket', readonly=True, ondelete='restrict',
|
||||
states={'draft': [('readonly', False)]})
|
||||
'event.event.ticket', string='Ticket Type', ondelete='restrict', tracking=True, index='btree_not_null')
|
||||
active = fields.Boolean(default=True)
|
||||
barcode = fields.Char(string='Barcode', default=lambda self: self._get_random_barcode(), readonly=True, copy=False)
|
||||
# utm informations
|
||||
utm_campaign_id = fields.Many2one('utm.campaign', 'Campaign', index=True, ondelete='set null')
|
||||
utm_campaign_id = fields.Many2one('utm.campaign', 'Campaign', index=True, ondelete='set null')
|
||||
utm_source_id = fields.Many2one('utm.source', 'Source', index=True, ondelete='set null')
|
||||
utm_medium_id = fields.Many2one('utm.medium', 'Medium', index=True, ondelete='set null')
|
||||
# attendee
|
||||
partner_id = fields.Many2one('res.partner', string='Booked by', tracking=1)
|
||||
partner_id = fields.Many2one('res.partner', string='Booked by', tracking=1, index='btree_not_null')
|
||||
name = fields.Char(
|
||||
string='Attendee Name', index='trigram',
|
||||
compute='_compute_name', readonly=False, store=True, tracking=10)
|
||||
email = fields.Char(string='Email', compute='_compute_email', readonly=False, store=True, tracking=11)
|
||||
phone = fields.Char(string='Phone', compute='_compute_phone', readonly=False, store=True, tracking=12)
|
||||
mobile = fields.Char(string='Mobile', compute='_compute_mobile', readonly=False, store=True, tracking=13)
|
||||
compute='_compute_name', readonly=False, store=True, tracking=2)
|
||||
email = fields.Char(string='Email', compute='_compute_email', readonly=False, store=True, tracking=3)
|
||||
phone = fields.Char(string='Phone', compute='_compute_phone', readonly=False, store=True, tracking=4)
|
||||
company_name = fields.Char(
|
||||
string='Company Name', compute='_compute_company_name', readonly=False, store=True, tracking=5)
|
||||
# organization
|
||||
date_closed = fields.Datetime(
|
||||
string='Attended Date', compute='_compute_date_closed',
|
||||
readonly=False, store=True)
|
||||
event_begin_date = fields.Datetime(string="Event Start Date", related='event_id.date_begin', readonly=True)
|
||||
event_end_date = fields.Datetime(string="Event End Date", related='event_id.date_end', readonly=True)
|
||||
event_begin_date = fields.Datetime("Event Start Date", compute="_compute_event_begin_date", search="_search_event_begin_date")
|
||||
event_end_date = fields.Datetime("Event End Date", compute="_compute_event_end_date", search="_search_event_end_date")
|
||||
event_date_range = fields.Char("Date Range", compute="_compute_date_range")
|
||||
event_organizer_id = fields.Many2one(string='Event Organizer', related='event_id.organizer_id', readonly=True)
|
||||
event_user_id = fields.Many2one(string='Event Responsible', related='event_id.user_id', readonly=True)
|
||||
company_id = fields.Many2one(
|
||||
'res.company', string='Company', related='event_id.company_id',
|
||||
store=True, readonly=True, states={'draft': [('readonly', False)]})
|
||||
store=True, readonly=False)
|
||||
state = fields.Selection([
|
||||
('draft', 'Unconfirmed'), ('cancel', 'Cancelled'),
|
||||
('open', 'Confirmed'), ('done', 'Attended')],
|
||||
string='Status', default='draft', readonly=True, copy=False, tracking=True)
|
||||
('draft', 'Unconfirmed'),
|
||||
('open', 'Registered'),
|
||||
('done', 'Attended'),
|
||||
('cancel', 'Cancelled')],
|
||||
string='Status', default='open',
|
||||
readonly=True, copy=False, tracking=6,
|
||||
help='Unconfirmed: registrations in a pending state waiting for an action (specific case, notably with sale status)\n'
|
||||
'Registered: registrations considered taken by a client\n'
|
||||
'Attended: registrations for which the attendee attended the event\n'
|
||||
'Cancelled: registrations cancelled manually')
|
||||
# questions
|
||||
registration_answer_ids = fields.One2many('event.registration.answer', 'registration_id', string='Attendee Answers')
|
||||
registration_answer_choice_ids = fields.One2many('event.registration.answer', 'registration_id', string='Attendee Selection Answers',
|
||||
domain=[('question_type', '=', 'simple_choice')])
|
||||
# scheduled mails
|
||||
mail_registration_ids = fields.One2many(
|
||||
'event.mail.registration', 'registration_id',
|
||||
string="Scheduler Emails", readonly=True)
|
||||
# properties
|
||||
registration_properties = fields.Properties(
|
||||
'Properties', definition='event_id.registration_properties_definition', copy=True)
|
||||
|
||||
_barcode_event_uniq = models.Constraint(
|
||||
'unique(barcode)',
|
||||
'Barcode should be unique',
|
||||
)
|
||||
|
||||
@api.constrains('active', 'state', 'event_id', 'event_slot_id', 'event_ticket_id')
|
||||
def _check_seats_availability(self):
|
||||
tocheck = self.filtered(lambda registration: registration.state in ('open', 'done') and registration.active)
|
||||
for event, registrations in tocheck.grouped('event_id').items():
|
||||
event._verify_seats_availability([
|
||||
(slot, ticket, 0)
|
||||
for slot, ticket in self.env['event.registration']._read_group(
|
||||
[('id', 'in', registrations.ids)],
|
||||
['event_slot_id', 'event_ticket_id']
|
||||
)
|
||||
])
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
ret_vals = super().default_get(fields)
|
||||
utm_mixin_fields = ("campaign_id", "medium_id", "source_id")
|
||||
|
|
@ -78,7 +125,7 @@ class EventRegistration(models.Model):
|
|||
if not registration.name and registration.partner_id:
|
||||
registration.name = registration._synchronize_partner_values(
|
||||
registration.partner_id,
|
||||
fnames=['name']
|
||||
fnames={'name'},
|
||||
).get('name') or False
|
||||
|
||||
@api.depends('partner_id')
|
||||
|
|
@ -87,26 +134,27 @@ class EventRegistration(models.Model):
|
|||
if not registration.email and registration.partner_id:
|
||||
registration.email = registration._synchronize_partner_values(
|
||||
registration.partner_id,
|
||||
fnames=['email']
|
||||
fnames={'email'},
|
||||
).get('email') or False
|
||||
|
||||
@api.depends('partner_id')
|
||||
def _compute_phone(self):
|
||||
for registration in self:
|
||||
if not registration.phone and registration.partner_id:
|
||||
registration.phone = registration._synchronize_partner_values(
|
||||
partner_values = registration._synchronize_partner_values(
|
||||
registration.partner_id,
|
||||
fnames=['phone']
|
||||
).get('phone') or False
|
||||
fnames={'phone'},
|
||||
)
|
||||
registration.phone = partner_values.get('phone') or False
|
||||
|
||||
@api.depends('partner_id')
|
||||
def _compute_mobile(self):
|
||||
def _compute_company_name(self):
|
||||
for registration in self:
|
||||
if not registration.mobile and registration.partner_id:
|
||||
registration.mobile = registration._synchronize_partner_values(
|
||||
if not registration.company_name and registration.partner_id:
|
||||
registration.company_name = registration._synchronize_partner_values(
|
||||
registration.partner_id,
|
||||
fnames=['mobile']
|
||||
).get('mobile') or False
|
||||
fnames={'company_name'},
|
||||
).get('company_name') or False
|
||||
|
||||
@api.depends('state')
|
||||
def _compute_date_closed(self):
|
||||
|
|
@ -117,6 +165,45 @@ class EventRegistration(models.Model):
|
|||
else:
|
||||
registration.date_closed = False
|
||||
|
||||
@api.depends("event_id", "event_slot_id", "partner_id")
|
||||
def _compute_date_range(self):
|
||||
for registration in self:
|
||||
registration.event_date_range = registration.event_id._get_date_range_str(
|
||||
start_datetime=registration.event_slot_id.start_datetime,
|
||||
lang_code=registration.partner_id.lang,
|
||||
)
|
||||
|
||||
@api.depends("event_id", "event_slot_id")
|
||||
def _compute_event_begin_date(self):
|
||||
for registration in self:
|
||||
registration.event_begin_date = registration.event_slot_id.start_datetime or registration.event_id.date_begin
|
||||
|
||||
@api.model
|
||||
def _search_event_begin_date(self, operator, value):
|
||||
return Domain.OR([
|
||||
["&", ("event_slot_id", "!=", False), ("event_slot_id.start_datetime", operator, value)],
|
||||
["&", ("event_slot_id", "=", False), ("event_id.date_begin", operator, value)],
|
||||
])
|
||||
|
||||
@api.depends("event_id", "event_slot_id")
|
||||
def _compute_event_end_date(self):
|
||||
for registration in self:
|
||||
registration.event_end_date = registration.event_slot_id.end_datetime or registration.event_id.date_end
|
||||
|
||||
@api.model
|
||||
def _search_event_end_date(self, operator, value):
|
||||
return Domain.OR([
|
||||
["&", ("event_slot_id", "!=", False), ("event_slot_id.end_datetime", operator, value)],
|
||||
["&", ("event_slot_id", "=", False), ("event_id.date_end", operator, value)],
|
||||
])
|
||||
|
||||
@api.constrains('event_id', 'event_slot_id')
|
||||
def _check_event_slot(self):
|
||||
if any(registration.event_id != registration.event_slot_id.event_id for registration in self if registration.event_slot_id):
|
||||
raise ValidationError(_('Invalid event / slot choice'))
|
||||
if any(not registration.event_slot_id for registration in self if registration.is_multi_slots):
|
||||
raise ValidationError(_('Slot choice is mandatory on multi-slots events.'))
|
||||
|
||||
@api.constrains('event_id', 'event_ticket_id')
|
||||
def _check_event_ticket(self):
|
||||
if any(registration.event_id != registration.event_ticket_id.event_id for registration in self if registration.event_ticket_id):
|
||||
|
|
@ -124,7 +211,7 @@ class EventRegistration(models.Model):
|
|||
|
||||
def _synchronize_partner_values(self, partner, fnames=None):
|
||||
if fnames is None:
|
||||
fnames = ['name', 'email', 'phone', 'mobile']
|
||||
fnames = {'name', 'email', 'phone'}
|
||||
if partner:
|
||||
contact_id = partner.address_get().get('contact', False)
|
||||
if contact_id:
|
||||
|
|
@ -132,17 +219,41 @@ class EventRegistration(models.Model):
|
|||
return dict((fname, contact[fname]) for fname in fnames if contact[fname])
|
||||
return {}
|
||||
|
||||
@api.onchange('event_id')
|
||||
def _onchange_event(self):
|
||||
if self.event_slot_id and self.event_id != self.event_slot_id.event_id:
|
||||
self.event_slot_id = False
|
||||
if self.event_ticket_id and self.event_id != self.event_ticket_id.event_id:
|
||||
self.event_ticket_id = False
|
||||
|
||||
@api.onchange('phone', 'event_id', 'partner_id')
|
||||
def _onchange_phone_validation(self):
|
||||
if self.phone:
|
||||
country = self.partner_id.country_id or self.event_id.country_id or self.env.company.country_id
|
||||
self.phone = self._phone_format(self.phone, country)
|
||||
self.phone = self._phone_format(fname='phone', country=country) or self.phone
|
||||
|
||||
@api.onchange('mobile', 'event_id', 'partner_id')
|
||||
def _onchange_mobile_validation(self):
|
||||
if self.mobile:
|
||||
country = self.partner_id.country_id or self.event_id.country_id or self.env.company.country_id
|
||||
self.mobile = self._phone_format(self.mobile, country)
|
||||
@api.model
|
||||
def register_attendee(self, barcode, event_id):
|
||||
attendee = self.search([('barcode', '=', barcode)], limit=1)
|
||||
if not attendee:
|
||||
return {'error': 'invalid_ticket'}
|
||||
res = attendee._get_registration_summary()
|
||||
if attendee.state == 'cancel':
|
||||
status = 'canceled_registration'
|
||||
elif attendee.state == 'draft':
|
||||
status = 'unconfirmed_registration'
|
||||
elif attendee.event_id.is_finished:
|
||||
status = 'not_ongoing_event'
|
||||
elif attendee.state != 'done':
|
||||
if event_id and attendee.event_id.id != event_id:
|
||||
status = 'need_manual_confirmation'
|
||||
else:
|
||||
attendee.action_set_done()
|
||||
status = 'confirmed_registration'
|
||||
else:
|
||||
status = 'already_registered'
|
||||
res.update({'status': status})
|
||||
return res
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# CRUD
|
||||
|
|
@ -154,7 +265,7 @@ class EventRegistration(models.Model):
|
|||
all_partner_ids = set(values['partner_id'] for values in vals_list if values.get('partner_id'))
|
||||
all_event_ids = set(values['event_id'] for values in vals_list if values.get('event_id'))
|
||||
for values in vals_list:
|
||||
if not values.get('phone') and not values.get('mobile'):
|
||||
if not values.get('phone'):
|
||||
continue
|
||||
|
||||
related_country = self.env['res.country']
|
||||
|
|
@ -164,93 +275,31 @@ class EventRegistration(models.Model):
|
|||
related_country = self.env['event.event'].with_prefetch(all_event_ids).browse(values['event_id']).country_id
|
||||
if not related_country:
|
||||
related_country = self.env.company.country_id
|
||||
values['phone'] = self._phone_format(number=values['phone'], country=related_country) or values['phone']
|
||||
|
||||
for fname in {'mobile', 'phone'}:
|
||||
if values.get(fname):
|
||||
values[fname] = self._phone_format(values[fname], related_country)
|
||||
|
||||
registrations = super(EventRegistration, self).create(vals_list)
|
||||
|
||||
# auto_confirm if possible; if not automatically confirmed, call mail schedulers in case
|
||||
# some were created already open
|
||||
if registrations._check_auto_confirmation():
|
||||
registrations.sudo().action_confirm()
|
||||
elif not self.env.context.get('install_mode', False):
|
||||
# running the scheduler for demo data can cause an issue where wkhtmltopdf runs during
|
||||
# server start and hangs indefinitely, leading to serious crashes
|
||||
# we currently avoid this by not running the scheduler, would be best to find the actual
|
||||
# reason for this issue and fix it so we can remove this check
|
||||
registrations._update_mail_schedulers()
|
||||
registrations = super().create(vals_list)
|
||||
registrations._update_mail_schedulers()
|
||||
return registrations
|
||||
|
||||
def write(self, vals):
|
||||
confirming = vals.get('state') in {'open', 'done'}
|
||||
to_confirm = (self.filtered(lambda registration: registration.state in {'draft', 'cancel'})
|
||||
if confirming else None)
|
||||
ret = super(EventRegistration, self).write(vals)
|
||||
# As these Event(Ticket) methods are model constraints, it is not necessary to call them
|
||||
# explicitly when creating new registrations. However, it is necessary to trigger them here
|
||||
# as changes in registration states cannot be used as constraints triggers.
|
||||
ret = super().write(vals)
|
||||
if confirming:
|
||||
to_confirm.event_id._check_seats_availability()
|
||||
to_confirm.event_ticket_id._check_seats_availability()
|
||||
to_confirm._update_mail_schedulers()
|
||||
|
||||
if not self.env.context.get('install_mode', False):
|
||||
# running the scheduler for demo data can cause an issue where wkhtmltopdf runs
|
||||
# during server start and hangs indefinitely, leading to serious crashes we
|
||||
# currently avoid this by not running the scheduler, would be best to find the
|
||||
# actual reason for this issue and fix it so we can remove this check
|
||||
to_confirm._update_mail_schedulers()
|
||||
if vals.get('state') == 'done':
|
||||
message = _("Attended on %(attended_date)s", attended_date=format_date(env=self.env, value=fields.Datetime.now(), date_format='short'))
|
||||
self._message_log_batch(bodies={registration.id: message for registration in self})
|
||||
|
||||
return ret
|
||||
|
||||
def name_get(self):
|
||||
""" Custom name_get implementation to better differentiate registrations
|
||||
linked to a given partner but with different name (one partner buying
|
||||
several registrations)
|
||||
|
||||
* name, partner_id has no name -> take name
|
||||
* partner_id has name, name void or same -> take partner name
|
||||
* both have name: partner + name
|
||||
def _compute_display_name(self):
|
||||
""" Custom display_name in case a registration is nott linked to an attendee
|
||||
"""
|
||||
ret_list = []
|
||||
for registration in self:
|
||||
if registration.partner_id.name:
|
||||
if registration.name and registration.name != registration.partner_id.name:
|
||||
name = '%s, %s' % (registration.partner_id.name, registration.name)
|
||||
else:
|
||||
name = registration.partner_id.name
|
||||
else:
|
||||
name = registration.name
|
||||
ret_list.append((registration.id, name))
|
||||
return ret_list
|
||||
|
||||
def toggle_active(self):
|
||||
pre_inactive = self - self.filtered(self._active_name)
|
||||
super().toggle_active()
|
||||
# Necessary triggers as changing registration states cannot be used as triggers for the
|
||||
# Event(Ticket) models constraints.
|
||||
if pre_inactive:
|
||||
pre_inactive.event_id._check_seats_availability()
|
||||
pre_inactive.event_ticket_id._check_seats_availability()
|
||||
|
||||
def _check_auto_confirmation(self):
|
||||
""" Checks that all registrations are for `auto-confirm` events. """
|
||||
return all(event.auto_confirm for event in self.event_id)
|
||||
|
||||
def _phone_format(self, number, country):
|
||||
""" Call phone_validation formatting tool function. Returns original
|
||||
number in case formatting cannot be done (no country, wrong info, ...) """
|
||||
if not number or not country:
|
||||
return number
|
||||
new_number = phone_format(
|
||||
number,
|
||||
country.code,
|
||||
country.phone_code,
|
||||
force_format='E164',
|
||||
raise_exception=False,
|
||||
)
|
||||
return new_number if new_number else number
|
||||
registration.display_name = registration.name or f"#{registration.id}"
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# ACTIONS / BUSINESS
|
||||
|
|
@ -278,12 +327,9 @@ class EventRegistration(models.Model):
|
|||
compose_form = self.env.ref('mail.email_compose_message_wizard_form')
|
||||
ctx = dict(
|
||||
default_model='event.registration',
|
||||
default_res_id=self.id,
|
||||
default_use_template=bool(template),
|
||||
default_template_id=template and template.id,
|
||||
default_res_ids=self.ids,
|
||||
default_template_id=template.id if template else False,
|
||||
default_composition_mode='comment',
|
||||
default_email_layout_xmlid="mail.mail_notification_light",
|
||||
name_with_seats_availability=False,
|
||||
)
|
||||
return {
|
||||
'name': _('Compose Email'),
|
||||
|
|
@ -299,54 +345,78 @@ class EventRegistration(models.Model):
|
|||
def _update_mail_schedulers(self):
|
||||
""" Update schedulers to set them as running again, and cron to be called
|
||||
as soon as possible. """
|
||||
if self.env.context.get("install_mode", False):
|
||||
# running the scheduler for demo data can cause an issue where wkhtmltopdf runs during
|
||||
# server start and hangs indefinitely, leading to serious crashes
|
||||
# we currently avoid this by not running the scheduler, would be best to find the actual
|
||||
# reason for this issue and fix it so we can remove this check
|
||||
return
|
||||
|
||||
open_registrations = self.filtered(lambda registration: registration.state == 'open')
|
||||
if not open_registrations:
|
||||
return
|
||||
|
||||
onsubscribe_schedulers = self.env['event.mail'].sudo().search([
|
||||
('event_id', 'in', open_registrations.event_id.ids),
|
||||
('interval_type', '=', 'after_sub')
|
||||
('interval_type', '=', 'after_sub'),
|
||||
])
|
||||
if not onsubscribe_schedulers:
|
||||
return
|
||||
|
||||
onsubscribe_schedulers.update({'mail_done': False})
|
||||
# we could simply call _create_missing_mail_registrations and let cron do their job
|
||||
# but it currently leads to several delays. We therefore call execute until
|
||||
# cron triggers are correctly used
|
||||
onsubscribe_schedulers.with_user(SUPERUSER_ID).execute()
|
||||
# either trigger the cron, either run schedulers immediately (scaling choice)
|
||||
async_scheduler = self.env['ir.config_parameter'].sudo().get_param('event.event_mail_async')
|
||||
if async_scheduler:
|
||||
self.env.ref('event.event_mail_scheduler')._trigger()
|
||||
self.env.ref('mail.ir_cron_mail_scheduler_action')._trigger()
|
||||
else:
|
||||
# we could simply call _create_missing_mail_registrations and let cron do their job
|
||||
# but it currently leads to several delays. We therefore call execute until
|
||||
# cron triggers are correctly used
|
||||
for scheduler in onsubscribe_schedulers:
|
||||
try:
|
||||
scheduler.with_context(
|
||||
event_mail_registration_ids=open_registrations.ids
|
||||
).with_user(SUPERUSER_ID).execute()
|
||||
except Exception as e:
|
||||
_logger.exception("Failed to run scheduler %s", scheduler.id)
|
||||
scheduler._warn_error(e)
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# MAILING / GATEWAY
|
||||
# ------------------------------------------------------------
|
||||
|
||||
def _message_get_suggested_recipients(self):
|
||||
recipients = super(EventRegistration, self)._message_get_suggested_recipients()
|
||||
public_users = self.env['res.users'].sudo()
|
||||
public_groups = self.env.ref("base.group_public", raise_if_not_found=False)
|
||||
if public_groups:
|
||||
public_users = public_groups.sudo().with_context(active_test=False).mapped("users")
|
||||
try:
|
||||
for attendee in self:
|
||||
is_public = attendee.sudo().with_context(active_test=False).partner_id.user_ids in public_users if public_users else False
|
||||
if attendee.partner_id and not is_public:
|
||||
attendee._message_add_suggested_recipient(recipients, partner=attendee.partner_id, reason=_('Customer'))
|
||||
elif attendee.email:
|
||||
attendee._message_add_suggested_recipient(recipients, email=attendee.email, reason=_('Customer Email'))
|
||||
except AccessError: # no read access rights -> ignore suggested recipients
|
||||
pass
|
||||
return recipients
|
||||
@api.model
|
||||
def _mail_template_default_values(self):
|
||||
return {
|
||||
"email_from": "{{ (object.event_id.organizer_id.email_formatted or object.event_id.company_id.email_formatted or user.email_formatted or '') }}",
|
||||
"lang": "{{ object.event_id.lang or object.partner_id.lang }}",
|
||||
"use_default_to": True,
|
||||
}
|
||||
|
||||
def _message_get_default_recipients(self):
|
||||
def _message_compute_subject(self):
|
||||
if self.name:
|
||||
return _(
|
||||
"%(event_name)s - Registration for %(attendee_name)s",
|
||||
event_name=self.event_id.name,
|
||||
attendee_name=self.name,
|
||||
)
|
||||
return _(
|
||||
"%(event_name)s - Registration #%(registration_id)s",
|
||||
event_name=self.event_id.name,
|
||||
registration_id=self.id,
|
||||
)
|
||||
|
||||
def _message_add_default_recipients(self):
|
||||
# Prioritize registration email over partner_id, which may be shared when a single
|
||||
# partner booked multiple seats
|
||||
return {r.id:
|
||||
{
|
||||
'partner_ids': [],
|
||||
'email_to': ','.join(email_normalize_all(r.email)) or r.email,
|
||||
'email_cc': False,
|
||||
} for r in self
|
||||
}
|
||||
results = super()._message_add_default_recipients()
|
||||
for record in self:
|
||||
email_to_lst = results[record.id]['email_to_lst']
|
||||
if len(email_to_lst) == 1:
|
||||
email_normalized = email_normalize(email_to_lst[0])
|
||||
if email_normalized and email_normalized == email_normalize(record.email):
|
||||
results[record.id]['email_to_lst'] = [formataddr((record.name or "", email_normalized))]
|
||||
return results
|
||||
|
||||
def _message_post_after_hook(self, message, msg_vals):
|
||||
if self.email and not self.partner_id:
|
||||
|
|
@ -371,32 +441,27 @@ class EventRegistration(models.Model):
|
|||
# TOOLS
|
||||
# ------------------------------------------------------------
|
||||
|
||||
def get_date_range_str(self, lang_code=False):
|
||||
self.ensure_one()
|
||||
today_tz = pytz.utc.localize(fields.Datetime.now()).astimezone(pytz.timezone(self.event_id.date_tz))
|
||||
event_date_tz = pytz.utc.localize(self.event_begin_date).astimezone(pytz.timezone(self.event_id.date_tz))
|
||||
diff = (event_date_tz.date() - today_tz.date())
|
||||
if diff.days <= 0:
|
||||
return _('today')
|
||||
elif diff.days == 1:
|
||||
return _('tomorrow')
|
||||
elif (diff.days < 7):
|
||||
return _('in %d days') % (diff.days, )
|
||||
elif (diff.days < 14):
|
||||
return _('next week')
|
||||
elif event_date_tz.month == (today_tz + relativedelta(months=+1)).month:
|
||||
return _('next month')
|
||||
else:
|
||||
return _('on %(date)s', date=format_date(self.env, self.event_begin_date, lang_code=lang_code, date_format='medium'))
|
||||
|
||||
def _get_registration_summary(self):
|
||||
self.ensure_one()
|
||||
|
||||
is_date_closed_today = False
|
||||
if self.date_closed:
|
||||
event_tz = pytz.timezone(self.event_id.date_tz)
|
||||
now = fields.Datetime.now(pytz.UTC).astimezone(event_tz)
|
||||
closed_date = self.date_closed.astimezone(event_tz)
|
||||
is_date_closed_today = now.date() == closed_date.date()
|
||||
|
||||
return {
|
||||
'id': self.id,
|
||||
'name': self.name,
|
||||
'partner_id': self.partner_id.id,
|
||||
'ticket_name': self.event_ticket_id.name or _('None'),
|
||||
'slot_name': self.event_slot_id.display_name,
|
||||
'ticket_name': self.event_ticket_id.name,
|
||||
'event_id': self.event_id.id,
|
||||
'event_display_name': self.event_id.display_name,
|
||||
'company_name': self.event_id.company_id and self.event_id.company_id.name or False,
|
||||
'registration_answers': self.registration_answer_ids.filtered('value_answer_id').mapped('display_name'),
|
||||
'company_name': self.company_name,
|
||||
'badge_format': self.event_id.badge_format,
|
||||
'date_closed_formatted': format_date(env=self.env, value=self.date_closed, date_format='short') if self.date_closed else False,
|
||||
'is_date_closed_today': is_date_closed_today,
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue