[IMP] website_event_sale: validation of seats availability in cart.
Attendees are created and deleted on changing the quantity from the cart. Added sale_order_id and sale_order_line_id fields on registrations, allowing to find the order and the order line that created the registration without to rely on origin and ticket. Also fixed check_auto_confirmation, now in multi mode to avoid issues due to the specific behavior of api.one.
This commit is contained in:
committed by
Thibault Delavallée
parent
1656a7e503
commit
e2e38076aa
@@ -277,13 +277,15 @@ class event_registration(models.Model):
|
||||
if self.event_id.seats_max and self.event_id.seats_available < (1 if self.state == 'draft' else 0):
|
||||
raise Warning(_('No more seats available for this event.'))
|
||||
|
||||
@api.one
|
||||
@api.multi
|
||||
def _check_auto_confirmation(self):
|
||||
if self._context.get('registration_force_draft'):
|
||||
return False
|
||||
if self.event_id and self.event_id.state == 'confirm' and self.event_id.auto_confirm and self.event_id.seats_available:
|
||||
return True
|
||||
return False
|
||||
if any(registration.event_id.state != 'confirm' or
|
||||
not registration.event_id.auto_confirm or
|
||||
not registration.event_id.seats_available for registration in self):
|
||||
return False
|
||||
return True
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
|
||||
@@ -305,13 +305,13 @@
|
||||
</header>
|
||||
<sheet string="Registration">
|
||||
<group>
|
||||
<group string="Attendee Information">
|
||||
<group string="Attendee Information" name="attendee">
|
||||
<field name="name"/>
|
||||
<field name="phone"/>
|
||||
<field name="email"/>
|
||||
<field name="partner_id" attrs="{'readonly':[('state', '!=', 'draft')]}"/>
|
||||
</group>
|
||||
<group string="Event Information">
|
||||
<group string="Event Information" name="event">
|
||||
<field name="event_id" readonly="1"/>
|
||||
<field name="date_open" groups="base.group_no_one"/>
|
||||
<field name="date_closed" groups="base.group_no_one"/>
|
||||
|
||||
@@ -74,4 +74,15 @@ class TestEventFlow(TestEventCommon):
|
||||
'date_end': datetime.datetime.now() + relativedelta(days=1),
|
||||
'seats_max': 10,
|
||||
})
|
||||
self.assertEqual(test_event.state, 'draft', 'Event: new event should be in draft state, no auto confirmation')
|
||||
self.assertEqual(
|
||||
test_event.state, 'draft',
|
||||
'Event: new event should be in draft state, no auto confirmation')
|
||||
|
||||
# EventUser create registrations for this event -> no auto confirmation
|
||||
test_reg1 = self.Registration.sudo(self.user_eventuser).create({
|
||||
'name': 'TestReg1',
|
||||
'event_id': test_event.id,
|
||||
})
|
||||
self.assertEqual(
|
||||
test_reg1.state, 'draft',
|
||||
'Event: new registration should not be confirmed with auto_confirmation parameter being False')
|
||||
|
||||
@@ -11,7 +11,7 @@ class event_event(models.Model):
|
||||
|
||||
event_ticket_ids = fields.One2many(
|
||||
'event.event.ticket', 'event_id', string='Event Ticket',
|
||||
default=lambda rec: rec._default_tickets(), copy=True)
|
||||
default=lambda rec: rec._default_tickets(), copy=True)
|
||||
seats_max = fields.Integer(
|
||||
string='Maximum Available Seats',
|
||||
help="The maximum registration level is equal to the sum of the maximum registration of event ticket. " +
|
||||
@@ -149,7 +149,11 @@ class event_registration(models.Model):
|
||||
_inherit = 'event.registration'
|
||||
|
||||
event_ticket_id = fields.Many2one('event.event.ticket', 'Event Ticket')
|
||||
# sale_order_line_id = fields.Many2one('sale.order.line', 'Sale Order Line', ondelete='cascade')
|
||||
# in addition to origin generic fields, add real relational fields to correctly
|
||||
# handle attendees linked to sale orders and their lines
|
||||
# TDE FIXME: maybe add an onchange on sale_order_id + origin
|
||||
sale_order_id = fields.Many2one('sale.order', 'Source Sale Order', ondelete='cascade')
|
||||
sale_order_line_id = fields.Many2one('sale.order.line', 'Sale Order Line', ondelete='cascade')
|
||||
|
||||
@api.one
|
||||
@api.constrains('event_ticket_id', 'state')
|
||||
@@ -157,22 +161,22 @@ class event_registration(models.Model):
|
||||
if self.event_ticket_id.seats_max and self.event_ticket_id.seats_available < 0:
|
||||
raise Warning('No more available seats for this ticket')
|
||||
|
||||
@api.one
|
||||
@api.multi
|
||||
def _check_auto_confirmation(self):
|
||||
res = super(event_registration, self)._check_auto_confirmation()[0]
|
||||
if res and self.origin:
|
||||
orders = self.env['sale.order'].search([('name', '=', self.origin)], limit=1)
|
||||
if orders and orders[0].state == 'draft':
|
||||
res = super(event_registration, self)._check_auto_confirmation()
|
||||
if res:
|
||||
orders = self.env['sale.order'].search([('state', '=', 'draft'), ('id', 'in', self.mapped('sale_order_id').ids)], limit=1)
|
||||
if orders:
|
||||
res = False
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
res = super(event_registration, self).create(vals)
|
||||
if res.origin:
|
||||
if res.origin or res.sale_order_id:
|
||||
message = _("The registration has been created for event %(event_name)s%(ticket)s from sale order %(order)s") % ({
|
||||
'event_name': '<i>%s</i>' % res.event_id.name,
|
||||
'ticket': res.event_ticket_id and _(' with ticket %s') % (('<i>%s</i>') % res.event_ticket_id.name) or '',
|
||||
'order': res.origin})
|
||||
'order': res.origin or res.sale_order_id.name})
|
||||
res.message_post(body=message)
|
||||
return res
|
||||
|
||||
@@ -57,23 +57,34 @@ class sale_order_line(osv.osv):
|
||||
return res
|
||||
|
||||
@api.multi
|
||||
def _update_registrations(self):
|
||||
def _update_registrations(self, confirm=True, registration_data=None):
|
||||
""" Create or update registrations linked to a sale order line. A sale
|
||||
order line has a product_uom_qty attribute that will be the number of
|
||||
registrations linked to this line. This method update existing registrations
|
||||
and create new one for missing one. """
|
||||
registrations = self.env['event.registration'].search([('origin', 'in', list(set([so.name for line in self for so in line.order_id if line.event_id])))])
|
||||
registrations = self.env['event.registration'].search([('sale_order_line_id', 'in', self.ids)])
|
||||
for so_line in [l for l in self if l.event_id]:
|
||||
existing_registrations = [r for r in registrations if r.event_id == so_line.event_id and r.origin == so_line.order_id.name]
|
||||
for registration in existing_registrations:
|
||||
registration.write({'state': 'open'})
|
||||
existing_registrations = registrations.filtered(lambda self: self.sale_order_line_id.id == so_line.id)
|
||||
if confirm:
|
||||
existing_registrations.filtered(lambda self: self.state != 'open').confirm_registration()
|
||||
else:
|
||||
existing_registrations.filtered(lambda self: self.state == 'cancel').do_draft()
|
||||
|
||||
for count in range(int(so_line.product_uom_qty) - len(existing_registrations)):
|
||||
self.env['event.registration'].create({
|
||||
registration = {}
|
||||
if registration_data:
|
||||
registration = registration_data.pop()
|
||||
# TDE CHECK: auto confirmation
|
||||
self.env['event.registration'].with_context(registration_force_draft=True).create({
|
||||
'name': registration.get('name', so_line.order_id.partner_id.name),
|
||||
'phone': registration.get('phone', so_line.order_id.partner_id.phone),
|
||||
'email': registration.get('email', so_line.order_id.partner_id.email),
|
||||
'event_id': so_line.event_id.id,
|
||||
'event_ticket_id': so_line.event_ticket_id.id,
|
||||
'partner_id': so_line.order_id.partner_id.id,
|
||||
'origin': so_line.order_id.name,
|
||||
'sale_order_id': so_line.order_id.id,
|
||||
'sale_order_line_id': so_line.id,
|
||||
})
|
||||
return True
|
||||
|
||||
@@ -84,7 +95,7 @@ class sale_order_line(osv.osv):
|
||||
create registration with sales order
|
||||
'''
|
||||
res = super(sale_order_line, self).button_confirm(cr, uid, ids, context=context)
|
||||
self._update_registrations(cr, uid, ids, context=context)
|
||||
self._update_registrations(cr, uid, ids, confirm=True, context=context)
|
||||
return res
|
||||
|
||||
def onchange_event_ticket_id(self, cr, uid, ids, event_ticket_id=False, context=None):
|
||||
|
||||
@@ -32,8 +32,14 @@
|
||||
<field name="arch" type="xml">
|
||||
<field name="event_id" position="after">
|
||||
<field name="event_ticket_id" domain="[('event_id', '=', event_id)]"/>
|
||||
<field name="origin"/>
|
||||
</field>
|
||||
<group name="event" position="after">
|
||||
<group string="Origin">
|
||||
<field name="origin" attrs="{'invisible': [('sale_order_id', '!=', False)]}"/>
|
||||
<field name="sale_order_id"/>
|
||||
<field name="sale_order_line_id" readonly="1" attrs="{'invisible': [('sale_order_id', '=', False)]}"/>
|
||||
</group>
|
||||
</group>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -43,7 +49,7 @@
|
||||
<field name="inherit_id" ref="event.view_event_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//notebook" position="inside">
|
||||
<xpath expr="//page[@string='Registrations']" position="after">
|
||||
<page string="Ticket Types">
|
||||
<field name="event_ticket_ids" context="{'default_name': name}">
|
||||
<tree string="Payments" editable="bottom">
|
||||
|
||||
@@ -16,11 +16,14 @@ class SaleOrderEventRegistration(models.TransientModel):
|
||||
sale_order_id = res.get('sale_order_id', self._context.get('active_id'))
|
||||
res['sale_order_id'] = sale_order_id
|
||||
sale_order = self.env['sale.order'].browse(res.get('sale_order_id'))
|
||||
registrations = self.env['event.registration'].search([('origin', '=', sale_order.name)])
|
||||
registrations = self.env['event.registration'].search([
|
||||
('sale_order_id', '=', sale_order.id),
|
||||
('event_ticket_id', 'in', sale_order.mapped('order_line.event_ticket_id').ids),
|
||||
('state', '!=', 'cancel')])
|
||||
|
||||
attendee_list = []
|
||||
for so_line in [l for l in sale_order.order_line if l.event_id]:
|
||||
existing_registrations = [r for r in registrations if r.event_id == so_line.event_id]
|
||||
for so_line in [l for l in sale_order.order_line if l.event_ticket_id]:
|
||||
existing_registrations = [r for r in registrations if r.event_ticket_id == so_line.event_ticket_id]
|
||||
for reg in existing_registrations:
|
||||
attendee_list.append({
|
||||
'event_id': reg.event_id.id,
|
||||
@@ -29,11 +32,13 @@ class SaleOrderEventRegistration(models.TransientModel):
|
||||
'name': reg.name,
|
||||
'email': reg.email,
|
||||
'phone': reg.phone,
|
||||
'sale_order_line_id': so_line.id,
|
||||
})
|
||||
for count in range(int(so_line.product_uom_qty) - len(existing_registrations)):
|
||||
attendee_list.append({
|
||||
'event_id': so_line.event_id.id,
|
||||
'event_ticket_id': so_line.event_ticket_id.id,
|
||||
'sale_order_line_id': so_line.id,
|
||||
})
|
||||
res['event_registration_ids'] = attendee_list
|
||||
return res
|
||||
@@ -55,6 +60,7 @@ class RegistrationEditorLine(models.TransientModel):
|
||||
_name = "registration.editor.line"
|
||||
|
||||
editor_id = fields.Many2one('registration.editor')
|
||||
sale_order_line_id = fields.Many2one('sale.order.line', string='Sale Order Line')
|
||||
event_id = fields.Many2one('event.event', string='Event', required=True)
|
||||
registration_id = fields.Many2one('event.registration', 'Original Registration')
|
||||
event_ticket_id = fields.Many2one('event.event.ticket', string='Event Ticket')
|
||||
@@ -72,4 +78,6 @@ class RegistrationEditorLine(models.TransientModel):
|
||||
'phone': self.phone or self.editor_id.sale_order_id.partner_id.phone,
|
||||
'email': self.email or self.editor_id.sale_order_id.partner_id.email,
|
||||
'origin': self.editor_id.sale_order_id.name,
|
||||
'sale_order_id': self.editor_id.sale_order_id.id,
|
||||
'sale_order_line_id': self.sale_order_line_id.id,
|
||||
}
|
||||
|
||||
@@ -57,20 +57,9 @@ class website_event(website_event):
|
||||
order = request.website.sale_get_order(force_create=1)
|
||||
|
||||
registrations = self._process_registration_details(post)
|
||||
registration_ctx = dict(context, registration_force_draft=True)
|
||||
for registration in registrations:
|
||||
ticket = request.registry['event.event.ticket'].browse(cr, SUPERUSER_ID, int(registration['ticket_id']), context=context)
|
||||
order.with_context(event_ticket_id=ticket.id)._cart_update(product_id=ticket.product_id.id, add_qty=1)
|
||||
|
||||
request.registry['event.registration'].create(cr, SUPERUSER_ID, {
|
||||
'name': registration.get('name'),
|
||||
'phone': registration.get('phone'),
|
||||
'email': registration.get('email'),
|
||||
'event_ticket_id': int(registration['ticket_id']),
|
||||
'partner_id': order.partner_id.id,
|
||||
'event_id': event.id,
|
||||
'origin': order.name,
|
||||
}, context=registration_ctx)
|
||||
order.with_context(event_ticket_id=ticket.id)._cart_update(product_id=ticket.product_id.id, add_qty=1, registration_data=[registration])
|
||||
|
||||
return request.redirect("/shop/checkout")
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from openerp import SUPERUSER_ID
|
||||
from openerp.osv import osv, fields
|
||||
from openerp.osv import osv
|
||||
from openerp.tools.translate import _
|
||||
import openerp.addons.decimal_precision as dp
|
||||
|
||||
# defined for access rules
|
||||
|
||||
class sale_order(osv.Model):
|
||||
_inherit = "sale.order"
|
||||
|
||||
@@ -19,7 +18,7 @@ class sale_order(osv.Model):
|
||||
return self.pool.get('sale.order.line').search(cr, SUPERUSER_ID, domain, context=context)
|
||||
|
||||
def _website_product_id_change(self, cr, uid, ids, order_id, product_id, qty=0, line_id=None, context=None):
|
||||
values = super(sale_order,self)._website_product_id_change(cr, uid, ids, order_id, product_id, qty=qty, line_id=line_id, context=None)
|
||||
values = super(sale_order, self)._website_product_id_change(cr, uid, ids, order_id, product_id, qty=qty, line_id=line_id, context=None)
|
||||
|
||||
event_ticket_id = None
|
||||
if context.get("event_ticket_id"):
|
||||
@@ -36,7 +35,7 @@ class sale_order(osv.Model):
|
||||
if event_ticket_id:
|
||||
ticket = self.pool.get('event.event.ticket').browse(cr, uid, event_ticket_id, context=context)
|
||||
if product_id != ticket.product_id.id:
|
||||
raise osv.except_osv(_('Error!'),_("The ticket doesn't match with this product."))
|
||||
raise osv.except_osv(_('Error!'), _("The ticket doesn't match with this product."))
|
||||
|
||||
values['product_id'] = ticket.product_id.id
|
||||
values['event_id'] = ticket.event_id.id
|
||||
@@ -45,3 +44,54 @@ class sale_order(osv.Model):
|
||||
values['name'] = "%s: %s" % (ticket.event_id.name, ticket.name)
|
||||
|
||||
return values
|
||||
|
||||
def _cart_update(self, cr, uid, ids, product_id=None, line_id=None, add_qty=0, set_qty=0, context=None, **kwargs):
|
||||
OrderLine = self.pool['sale.order.line']
|
||||
Attendee = self.pool['event.registration']
|
||||
Ticket = self.pool['event.event.ticket']
|
||||
|
||||
if line_id:
|
||||
line = OrderLine.browse(cr, uid, line_id, context=context)
|
||||
ticket = line.event_ticket_id
|
||||
old_qty = int(line.product_uom_qty)
|
||||
else:
|
||||
line, ticket = None, None
|
||||
ticket_ids = Ticket.search(cr, uid, [('product_id', '=', product_id)], limit=1, context=context)
|
||||
if ticket_ids:
|
||||
ticket = Ticket.browse(cr, uid, ticket_ids[0], context=context)
|
||||
old_qty = 0
|
||||
new_qty = set_qty if set_qty else (add_qty or 0 + old_qty)
|
||||
|
||||
# case: buying tickets for a sold out ticket
|
||||
values = {}
|
||||
if ticket and ticket.seats_available <= 0:
|
||||
values['warning'] = _('Sorry, The %(ticket)s tickets for the %(event)s event are sold out.') % {
|
||||
'ticket': ticket.name,
|
||||
'event': ticket.event_id.name}
|
||||
new_qty, set_qty, add_qty = 0, 0, 0
|
||||
# case: buying tickets, too much attendees
|
||||
elif ticket and new_qty > ticket.seats_available:
|
||||
values['warning'] = _('Sorry, only %(remaining_seats)d seats are still available for the %(ticket)s ticket for the %(event)s event.') % {
|
||||
'remaining_seats': ticket.seats_available,
|
||||
'ticket': ticket.name,
|
||||
'event': ticket.event_id.name}
|
||||
new_qty, set_qty, add_qty = ticket.seats_available, ticket.seats_available, 0
|
||||
|
||||
values.update(super(sale_order, self)._cart_update(
|
||||
cr, uid, ids, product_id, line_id, add_qty, set_qty, context, **kwargs))
|
||||
|
||||
# removing attendees
|
||||
if ticket and new_qty < old_qty:
|
||||
attendees = Attendee.search(
|
||||
cr, uid, [
|
||||
('state', '!=', 'cancel'),
|
||||
('sale_order_id', '=', ids[0])
|
||||
('event_ticket_id', '=', ticket.id)
|
||||
], offset=new_qty, limit=(old_qty-new_qty),
|
||||
order='create_date asc', context=context)
|
||||
Attendee.button_reg_cancel(cr, uid, attendees, context=context)
|
||||
# adding attendees
|
||||
elif ticket and new_qty > old_qty:
|
||||
line = OrderLine.browse(cr, uid, values['line_id'], context=context)
|
||||
line._update_registrations(confirm=False, registration_data=kwargs.get('registration_data', []))
|
||||
return values
|
||||
|
||||
@@ -45,6 +45,17 @@ $('.oe_website_sale').each(function () {
|
||||
$input.val(data.quantity);
|
||||
$('.js_quantity[data-line-id='+line_id+']').val(data.quantity).html(data.quantity);
|
||||
$("#cart_total").replaceWith(data['website_sale.total']);
|
||||
if (data.warning) {
|
||||
var cart_alert = $('.oe_cart').parent().find('#data_warning')
|
||||
if (cart_alert.length == 0) {
|
||||
$('.oe_cart').prepend('<div class="alert alert-danger alert-dismissable" role="alert" id="data_warning">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button> ' + data.warning + '</div>');
|
||||
}
|
||||
else {
|
||||
cart_alert.html('<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button> ' + data.warning);
|
||||
}
|
||||
$input.val(data.quantity);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user