diff options
28 files changed, 650 insertions, 269 deletions
diff --git a/addons/event/__openerp__.py b/addons/event/__openerp__.py index b98f9d4e5ae..f6336e72c34 100644 --- a/addons/event/__openerp__.py +++ b/addons/event/__openerp__.py @@ -29,6 +29,8 @@ Key Features 'res_config_view.xml', 'email_template.xml', 'views/event.xml', + 'event_report.xml', + 'views/report_registrationbadge.xml', ], 'demo': [ 'event_demo.xml', diff --git a/addons/event/event.py b/addons/event/event.py index 322d7d6bcca..454454b0f9a 100644 --- a/addons/event/event.py +++ b/addons/event/event.py @@ -81,7 +81,7 @@ class event_event(models.Model): store=True, readonly=True, compute='_compute_seats') @api.multi - @api.depends('seats_max', 'registration_ids.state', 'registration_ids.nb_register') + @api.depends('seats_max', 'registration_ids.state') def _compute_seats(self): """ Determine reserved, available, reserved but unconfirmed and used seats. """ # initialize fields to 0 @@ -94,7 +94,7 @@ class event_event(models.Model): 'open': 'seats_reserved', 'done': 'seats_used', } - query = """ SELECT event_id, state, sum(nb_register) + query = """ SELECT event_id, state, count(event_id) FROM event_registration WHERE event_id IN %s AND state IN ('draft', 'open', 'done') GROUP BY event_id, state @@ -110,7 +110,7 @@ class event_event(models.Model): # Registration fields registration_ids = fields.One2many( - 'event.registration', 'event_id', string='Registrations', + 'event.registration', 'event_id', string='Attendees', readonly=False, states={'done': [('readonly', True)]}) count_registrations = fields.Integer(string='Registrations', compute='_count_registrations') @@ -245,36 +245,6 @@ class event_event(models.Model): """ Confirm Event and send confirmation email to all register peoples """ self.confirm_event() - @api.one - def subscribe_to_event(self): - """ Subscribe the current user to a given event """ - user = self.env.user - num_of_seats = int(self._context.get('ticket', 1)) - regs = self.registration_ids.filtered(lambda reg: reg.user_id == user) - # the subscription is done as SUPERUSER_ID because in case we share the - # kanban view, we want anyone to be able to subscribe - if not regs: - regs = regs.sudo().create({ - 'event_id': self.id, - 'email': user.email, - 'name': user.name, - 'user_id': user.id, - 'nb_register': num_of_seats, - }) - else: - regs.write({'nb_register': num_of_seats}) - if regs._check_auto_confirmation(): - regs.sudo().confirm_registration() - - @api.one - def unsubscribe_to_event(self): - """ Unsubscribe the current user from a given event """ - # the unsubscription is done as SUPERUSER_ID because in case we share - # the kanban view, we want anyone to be able to unsubscribe - user = self.env.user - regs = self.sudo().registration_ids.filtered(lambda reg: reg.user_id == user) - regs.button_reg_cancel() - @api.onchange('type') def _onchange_type(self): if self.type: @@ -289,35 +259,31 @@ class event_event(models.Model): res = self.env['ir.actions.act_window'].for_xml_id('event', 'action_report_event_registration') res['context'] = { "search_default_event_id": self.id, - "group_by": ['event_date:day'], + "group_by": ['create_date:day'], } return res class event_registration(models.Model): _name = 'event.registration' - _description = 'Event Registration' + _description = 'Attendee' _inherit = ['mail.thread', 'ir.needaction_mixin'] _order = 'name, create_date desc' origin = fields.Char( string='Source Document', readonly=True, - help="Reference of the sales order which created the registration") # funny we refer sale orders... event is not sale related - nb_register = fields.Integer( - string='Number of Participants', required=True, default=1, - readonly=True, states={'draft': [('readonly', False)]}) + help="Reference of the document that created the registration, for example a sale order") event_id = fields.Many2one( 'event.event', string='Event', required=True, readonly=True, states={'draft': [('readonly', False)]}) partner_id = fields.Many2one( - 'res.partner', string='Partner', + 'res.partner', string='Contact', states={'done': [('readonly', True)]}) date_open = fields.Datetime(string='Registration Date', readonly=True) date_closed = fields.Datetime(string='Attended Date', readonly=True) reply_to = fields.Char(string='Reply-to Email', related='event_id.reply_to', readonly=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) - user_id = fields.Many2one('res.users', string='User', states={'done': [('readonly', True)]}) company_id = fields.Many2one( 'res.company', string='Company', related='event_id.company_id', store=True, readonly=True, states={'draft': [('readonly', False)]}) @@ -327,17 +293,18 @@ class event_registration(models.Model): string='Status', default='draft', readonly=True, copy=False, track_visibility='onchange') email = fields.Char(string='Email') phone = fields.Char(string='Phone') - name = fields.Char(string='Name', select=True) + name = fields.Char(string='Attendee Name', select=True) @api.one - @api.constrains('event_id', 'state', 'nb_register') + @api.constrains('event_id', 'state') def _check_seats_limit(self): - if self.event_id.seats_max and \ - self.event_id.seats_available < (self.nb_register if self.state == 'draft' else 0): - raise Warning(_('Only %s seats available for this event') % self.event_id.seats_available if self.event_id.seats_available > 0 else _('No more seats available for this event.')) + 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 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 @@ -345,7 +312,7 @@ class event_registration(models.Model): @api.model def create(self, vals): res = super(event_registration, self).create(vals) - if res._check_auto_confirmation(): + if res._check_auto_confirmation()[0]: res.sudo().confirm_registration() return res @@ -402,6 +369,6 @@ class event_registration(models.Model): contact_id = self.partner_id.address_get().get('default', False) if contact_id: contact = self.env['res.partner'].browse(contact_id) - self.name = contact.name - self.email = contact.email - self.phone = contact.phone + self.name = self.name or contact.name + self.email = self.email or contact.email + self.phone = self.phone or contact.phone diff --git a/addons/event/event_demo.xml b/addons/event/event_demo.xml index 41aeaf2f2fd..8e2bccf0d08 100644 --- a/addons/event/event_demo.xml +++ b/addons/event/event_demo.xml @@ -217,7 +217,6 @@ <field name="phone">003281588558</field> <field name="event_id" ref="event_1"/> <field name="partner_id" ref="base.res_partner_2"/> - <field name="nb_register">5</field> </record> <record id="reg_1_2" model="event.registration"> @@ -226,7 +225,6 @@ <field name="phone">+ 1 64 61 04 01</field> <field name="partner_id" ref="base.res_partner_1"/> <field name="event_id" ref="event_1"/> - <field name="nb_register">10</field> </record> <record id="reg_0_2" model="event.registration"> @@ -235,7 +233,6 @@ <field name="phone">+41 21 619 10 04 </field> <field name="event_id" ref="event_2"/> <field name="partner_id" ref="base.res_partner_12"/> - <field name="nb_register">5</field> </record> </data> diff --git a/addons/event_sale/event_sale_report.xml b/addons/event/event_report.xml index 1d9e21bc742..cdbc36cc3d8 100644 --- a/addons/event_sale/event_sale_report.xml +++ b/addons/event/event_report.xml @@ -17,17 +17,14 @@ <field name="header_spacing">0</field> <field name="dpi">80</field> </record> - <report - id="action_report_registrationbadge" - model="event.registration" - string="Badge" - report_type="qweb-html" - name="event_sale.report_registrationbadge" - file="event_sale.report_registrationbadge" - /> - <record id="action_report_registrationbadge" model="ir.actions.report.xml"> - <field name="paperformat_id" ref="event_sale.paperformat_euro_lowmargin"/> + + <report id="action_report_registration_badge" model="event.registration" + string="Badge" report_type="qweb-html" + name="event.report_registration_badge" + file="event.report_registration_badge"/> + <record model="ir.actions.report.xml" id="event.action_report_registration_badge"> + <field name="paperformat_id" ref="event.paperformat_euro_lowmargin"/> </record> </data> -</openerp>
\ No newline at end of file +</openerp> diff --git a/addons/event/event_view.xml b/addons/event/event_view.xml index 891a6da5fe5..9371dbe25e9 100644 --- a/addons/event/event_view.xml +++ b/addons/event/event_view.xml @@ -46,7 +46,7 @@ <record id="act_event_registration_from_event" model="ir.actions.act_window"> <field name="res_model">event.registration</field> <field name="view_type">form</field> - <field name="name">Registrations</field> + <field name="name">Attendees</field> <field name="view_mode">tree,form,calendar,graph</field> <field name="context">{'search_default_event_id': active_id, 'default_event_id': active_id}</field> </record> @@ -75,7 +75,7 @@ class="oe_stat_button oe_inline" icon="fa-github" help="Register with this event"> - <field name="count_registrations" widget="statinfo" string="Registrations"/> + <field name="count_registrations" widget="statinfo" string="Attendees"/> </button> </div> <group name="main_field_group"> @@ -190,11 +190,9 @@ <h4> <a name="%(act_event_registration_from_event)d" type="action"> <t t-esc="record.seats_reserved.raw_value" > Attendees</t> - <t t-if="(record.seats_reserved.raw_value + record.seats_unconfirmed.raw_value) > 0 ">/ - <t t-esc="record.seats_reserved.raw_value + record.seats_unconfirmed.raw_value"/> Expected - </t> </a> - <t t-if="record.seats_reserved.raw_value > 0"> + <t t-if="(record.seats_reserved.raw_value + record.seats_unconfirmed.raw_value) > 0 ">/ + <t t-esc="record.seats_reserved.raw_value + record.seats_unconfirmed.raw_value"/> Expected <a name="action_event_registration_report" type="object" >(Report)</a> </t> </h4> @@ -273,7 +271,7 @@ <menuitem name="Events" id="menu_event_event" action="action_event_view" parent="event.event_main_menu" /> - <!-- EVENTS/REGISTRATIONS/EVENTS --> + <!-- EVENT.REGISTRATION VIEWS --> <record model="ir.ui.view" id="view_event_registration_tree"> <field name="name">event.registration.tree</field> <field name="model">event.registration</field> @@ -284,14 +282,8 @@ <field name="name"/> <field name="email"/> <field name="event_id" /> - <field name="nb_register"/> - <field name="user_id" invisible="1"/> - <field name="origin"/> <field name="state"/> <field name="message_unread" invisible="1"/> - <button name="registration_open" string="Confirm Registration" states="draft" type="object" icon="gtk-apply"/> - <button name="button_reg_close" string="Attended the Event" states="open" type="object" icon="gtk-jump-to"/> - <button name="button_reg_cancel" string="Cancel Registration" states="draft,open" type="object" icon="gtk-cancel"/> </tree> </field> </record> @@ -309,22 +301,17 @@ <field name="state" nolabel="1" colspan="2" widget="statusbar" statusbar_visible="draft,open,done"/> </header> <sheet string="Registration"> - <label for="event_id" class="oe_edit_only"/> - <h1> - <field name="event_id" domain="[('state','in',('draft','confirm'))]"/> - </h1> <group> - <group> - <field name="partner_id" attrs="{'readonly':[('state','!=', 'draft')]}"/> + <group string="Attendee Information"> <field name="name"/> <field name="phone"/> <field name="email"/> + <field name="partner_id" attrs="{'readonly':[('state', '!=', 'draft')]}"/> </group> - <group> - <field name="create_date" groups="base.group_no_one"/> + <group string="Event Information"> + <field name="event_id" readonly="1"/> + <field name="date_open" groups="base.group_no_one"/> <field name="date_closed" groups="base.group_no_one"/> - <field name="nb_register"/> - <field name="user_id" attrs="{'readonly':[('state','!=', 'draft')]}" context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'event.group_event_user']}"/> </group> </group> </sheet> @@ -354,7 +341,6 @@ <field name="arch" type="xml"> <graph string="Registration" type="bar"> <field name="event_id" type="row"/> - <field name= "nb_register" type="measure"/> </graph> </field> </record> @@ -364,15 +350,11 @@ <field name="model">event.registration</field> <field name="arch" type="xml"> <search string="Event Registration"> - <field name="name" string="Participant" filter_domain="['|','|',('name','ilike',self),('email','ilike',self),('origin','ilike',self)]"/> + <field name="id" string="Registration ID"/> + <field name="name" string="Participant" filter_domain="['|', '|', ('name', 'ilike', self), ('email', 'ilike', self), ('origin', 'ilike', self)]"/> <filter string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/> <separator/> - <filter string="New" name="draft" domain="[('state','=','draft')]" help="Registrations in unconfirmed state"/> - <filter string="Confirmed" domain="[('state','=','open')]" help="Confirmed registrations"/> - <separator/> - <filter string="My Registrations" help="My Registrations" domain="[('user_id','=',uid)]"/> <field name="event_id"/> - <field name="user_id"/> <field name="partner_id"/> <group expand="0" string="Group By"> <filter string="Responsible" domain="[]" context="{'group_by':'user_id'}"/> @@ -387,7 +369,7 @@ </record> <record model="ir.actions.act_window" id="action_registration"> - <field name="name">Registrations</field> + <field name="name">Attendees</field> <field name="res_model">event.registration</field> <field name="view_type">form</field> <field name="domain"></field> @@ -396,7 +378,7 @@ <field name="search_view_id" ref="view_registration_search"/> </record> - <menuitem name="Registrations" + <menuitem name="Attendees" id="menu_action_registration" parent="event.event_main_menu" action="action_registration" groups="event.group_event_manager,event.group_event_user"/> diff --git a/addons/event/report/report_event_registration.py b/addons/event/report/report_event_registration.py index 5e56ce5cb2f..d1a9b5c40b8 100644 --- a/addons/event/report/report_event_registration.py +++ b/addons/event/report/report_event_registration.py @@ -29,6 +29,7 @@ class report_event_registration(models.Model): _order = 'event_date desc' _auto = False + create_date = fields.Datetime('Creation Date', readonly=True) event_date = fields.Datetime('Event Date', readonly=True) event_id = fields.Many2one('event.event', 'Event', required=True) draft_state = fields.Integer(' # No of Draft Registrations') @@ -40,7 +41,6 @@ class report_event_registration(models.Model): registration_state = fields.Selection([('draft', 'Draft'), ('confirm', 'Confirmed'), ('done', 'Attended'), ('cancel', 'Cancelled')], 'Registration State', readonly=True, required=True) event_state = fields.Selection([('draft', 'Draft'), ('confirm', 'Confirmed'), ('done', 'Done'), ('cancel', 'Cancelled')], 'Event State', readonly=True, required=True) user_id = fields.Many2one('res.users', 'Event Responsible', readonly=True) - user_id_registration = fields.Many2one('res.users', 'Register', readonly=True) name_registration = fields.Char('Participant / Contact Name', readonly=True) company_id = fields.Many2one('res.company', 'Company', readonly=True) @@ -54,14 +54,14 @@ class report_event_registration(models.Model): e.id::varchar || '/' || coalesce(r.id::varchar,'') AS id, e.id AS event_id, e.user_id AS user_id, - r.user_id AS user_id_registration, r.name AS name_registration, + r.create_date AS create_date, e.company_id AS company_id, e.date_begin AS event_date, count(r.id) AS nbevent, - sum(r.nb_register) AS nbregistration, - CASE WHEN r.state IN ('draft') THEN r.nb_register ELSE 0 END AS draft_state, - CASE WHEN r.state IN ('open','done') THEN r.nb_register ELSE 0 END AS confirm_state, + count(r.event_id) AS nbregistration, + CASE WHEN r.state IN ('draft') THEN count(r.event_id) ELSE 0 END AS draft_state, + CASE WHEN r.state IN ('open','done') THEN count(r.event_id) ELSE 0 END AS confirm_state, e.type AS event_type, e.seats_max AS seats_max, e.state AS event_state, @@ -72,10 +72,8 @@ class report_event_registration(models.Model): GROUP BY event_id, - user_id_registration, r.id, registration_state, - r.nb_register, event_type, e.id, e.date_begin, diff --git a/addons/event/tests/test_event_flow.py b/addons/event/tests/test_event_flow.py index 59f2970d37b..3af0fb9185c 100644 --- a/addons/event/tests/test_event_flow.py +++ b/addons/event/tests/test_event_flow.py @@ -20,7 +20,7 @@ class TestEventFlow(TestEventCommon): 'name': 'TestEvent', 'date_begin': datetime.datetime.now() + relativedelta(days=-1), 'date_end': datetime.datetime.now() + relativedelta(days=1), - 'seats_max': 10, + 'seats_max': 2, }) self.assertEqual(test_event.state, 'confirm', 'Event: auto_confirmation of event failed') @@ -28,33 +28,30 @@ class TestEventFlow(TestEventCommon): test_reg1 = self.Registration.sudo(self.user_eventuser).create({ 'name': 'TestReg1', 'event_id': test_event.id, - 'nb_register': 7, }) self.assertEqual(test_reg1.state, 'open', 'Event: auto_confirmation of registration failed') - self.assertEqual(test_event.seats_reserved, 7, 'Event: wrong number of reserved seats after confirmed registration') + self.assertEqual(test_event.seats_reserved, 1, 'Event: wrong number of reserved seats after confirmed registration') test_reg2 = self.Registration.sudo(self.user_eventuser).create({ 'name': 'TestReg2', 'event_id': test_event.id, - 'nb_register': 3, }) self.assertEqual(test_reg2.state, 'open', 'Event: auto_confirmation of registration failed') - self.assertEqual(test_event.seats_reserved, 10, 'Event: wrong number of reserved seats after confirmed registration') + self.assertEqual(test_event.seats_reserved, 2, 'Event: wrong number of reserved seats after confirmed registration') # EventUser create registrations for this event: too much registrations with self.assertRaises(ValidationError): self.Registration.sudo(self.user_eventuser).create({ 'name': 'TestReg3', 'event_id': test_event.id, - 'nb_register': 1, }) # EventUser validates registrations test_reg1.button_reg_close() self.assertEqual(test_reg1.state, 'done', 'Event: wrong state of attended registration') - self.assertEqual(test_event.seats_used, 7, 'Event: incorrect number of attendees after closing registration') + self.assertEqual(test_event.seats_used, 1, 'Event: incorrect number of attendees after closing registration') test_reg2.button_reg_close() self.assertEqual(test_reg1.state, 'done', 'Event: wrong state of attended registration') - self.assertEqual(test_event.seats_used, 10, 'Event: incorrect number of attendees after closing registration') + self.assertEqual(test_event.seats_used, 2, 'Event: incorrect number of attendees after closing registration') # EventUser closes the event test_event.button_done() @@ -63,6 +60,7 @@ class TestEventFlow(TestEventCommon): with self.assertRaises(Warning): test_event.button_cancel() + @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.models') def test_10_advanced_event_flow(self): """ Avanced event flow: no auto confirmation, manage minimum / maximum diff --git a/addons/event/views/report_registrationbadge.xml b/addons/event/views/report_registrationbadge.xml new file mode 100644 index 00000000000..69156f4a184 --- /dev/null +++ b/addons/event/views/report_registrationbadge.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data> + <template id="report_registration_badge"> + <t t-call="report.html_container"> + <t t-foreach="docs" t-as="o"> + <div class="page"> + <div class="row" style="border-bottom:1px dashed black;"> + <div class="col-xs-6 text-center" style="padding-right:7mm; border-right:1px dashed black; height: 147mm; padding-top: 7mm"> + <div class="row"> + <div class="col-xs-12"> + <span t-if="o.event_id.organizer_id.company_id.logo_web and o.event_id.organizer_id.is_company"> + <img t-att-src="'data:image/png;base64,%s' % o.event_id.organizer_id.company_id.logo_web" style="max-height:1cm; max-width:4cm;"/> + </span> + <h4 t-field="o.event_id.name"/> + <h5>( <i class="fa fa-clock-o"></i> <span itemprop="startDate" t-field="o.event_id.date_begin" t-field-options='{"hide_seconds":"True"}'> </span> <i>to</i> <span itemprop="endDate" t-field="o.event_id.date_end" t-field-options='{"hide_seconds":"True"}'> </span> )</h5> + </div> + </div> + <div class="row"> + <div class="col-xs-12 text-center"> + <small><h3 t-if="o.partner_id.is_company == True" t-field="o.partner_id.name"/><h5 t-field="o.name"/></small> + </div> + </div> + <div class="row"> + <div class="col-xs-12 mt32 text-center" style="background: lightgrey;"> + <h3>FREE TICKET</h3> + </div> + </div> + </div> + <!-- Inner right --> + <div class="col-xs-6 text-center" style="padding-left:7mm; height: 149mm; padding-top: 2mm;"> + </div> + </div> + </div> + </t> + </t> + </template> + </data> +</openerp> diff --git a/addons/event_sale/__init__.py b/addons/event_sale/__init__.py index c7b5ac7e1f2..e2b122e1a08 100644 --- a/addons/event_sale/__init__.py +++ b/addons/event_sale/__init__.py @@ -1,3 +1,4 @@ # -*- coding: utf-8 -*- import models +import wizard diff --git a/addons/event_sale/__openerp__.py b/addons/event_sale/__openerp__.py index d2300d05a17..42c528d6862 100644 --- a/addons/event_sale/__openerp__.py +++ b/addons/event_sale/__openerp__.py @@ -25,9 +25,9 @@ this event. 'views/product.xml', 'views/sale_order.xml', 'event_sale_data.xml', - 'event_sale_report.xml', 'views/report_registrationbadge.xml', 'security/ir.model.access.csv', + 'wizard/event_edit_registration.xml', ], 'demo': ['event_demo.xml'], 'test': ['test/confirm.yml'], diff --git a/addons/event_sale/models/event.py b/addons/event_sale/models/event.py index 8fc46f5e22b..5b66d895fe1 100644 --- a/addons/event_sale/models/event.py +++ b/addons/event_sale/models/event.py @@ -78,7 +78,7 @@ class event_ticket(models.Model): seats_used = fields.Integer(compute='_compute_seats', store=True) @api.multi - @api.depends('seats_max', 'registration_ids.state', 'registration_ids.nb_register') + @api.depends('seats_max', 'registration_ids.state') def _compute_seats(self): """ Determine reserved, available, reserved but unconfirmed and used seats. """ # initialize fields to 0 @@ -91,7 +91,7 @@ class event_ticket(models.Model): 'open': 'seats_reserved', 'done': 'seats_used', } - query = """ SELECT event_ticket_id, state, sum(nb_register) + query = """ SELECT event_ticket_id, state, count(event_id) FROM event_registration WHERE event_ticket_id IN %s AND state IN ('draft', 'open', 'done') GROUP BY event_ticket_id, state @@ -121,9 +121,30 @@ 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') @api.one - @api.constrains('event_ticket_id', 'nb_register', 'state') + @api.constrains('event_ticket_id', 'state') def _check_ticket_seats_limit(self): 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 + 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 = False + return res + + @api.model + def create(self, vals): + res = super(event_registration, self).create(vals) + if res.origin: + 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}) + res.message_post(body=message) + return res diff --git a/addons/event_sale/models/sale_order.py b/addons/event_sale/models/sale_order.py index 6705f00160a..e5d26b7d281 100644 --- a/addons/event_sale/models/sale_order.py +++ b/addons/event_sale/models/sale_order.py @@ -1,7 +1,21 @@ # -*- coding: utf-8 -*- +from openerp import api from openerp.osv import fields, osv -from openerp.tools.translate import _ + + +class sale_order(osv.osv): + _inherit = "sale.order" + + def action_button_confirm(self, cr, uid, ids, context=None): + # TDE note: This method works on a list of one id (see sale/sale.py) so working on ids[0] seems safe. + res = super(sale_order, self).action_button_confirm(cr, uid, ids, context=context) + redirect_to_event_registration = any(line.event_id for order in self.browse(cr, uid, ids, context=context) for line in order.order_line) + if redirect_to_event_registration: + event_ctx = dict(context, default_sale_order_id=ids[0]) + return self.pool.get('ir.actions.act_window').for_xml_id(cr, uid, 'event_sale', 'action_sale_order_event_registration', event_ctx) + else: + return res class sale_order_line(osv.osv): @@ -35,34 +49,36 @@ class sale_order_line(osv.osv): event_ok=False) return res + @api.multi + def _update_registrations(self): + """ 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])))]) + 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'}) + + for count in range(int(so_line.product_uom_qty) - len(existing_registrations)): + self.env['event.registration'].create({ + '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, + }) + return True + def button_confirm(self, cr, uid, ids, context=None): + """ Override confirmation of the sale order line in order to create + or update the possible event registrations linked to the sale. """ ''' create registration with sales order ''' - context = dict(context or {}) - registration_obj = self.pool.get('event.registration') - for order_line in self.browse(cr, uid, ids, context=context): - if order_line.event_id: - dic = { - 'name': order_line.order_id.partner_invoice_id.name, - 'partner_id': order_line.order_id.partner_id.id, - 'nb_register': int(order_line.product_uom_qty), - 'email': order_line.order_id.partner_id.email, - 'phone': order_line.order_id.partner_id.phone, - 'origin': order_line.order_id.name, - 'event_id': order_line.event_id.id, - 'event_ticket_id': order_line.event_ticket_id and order_line.event_ticket_id.id or None, - } - - if order_line.event_ticket_id: - message = _("The registration has been created for event <i>%s</i> with the ticket <i>%s</i> from the Sale Order %s. ") % (order_line.event_id.name, order_line.event_ticket_id.name, order_line.order_id.name) - else: - message = _("The registration has been created for event <i>%s</i> from the Sale Order %s. ") % (order_line.event_id.name, order_line.order_id.name) - - context.update({'mail_create_nolog': True}) - registration_id = registration_obj.create(cr, uid, dic, context=context) - registration_obj.message_post(cr, uid, [registration_id], body=message, context=context) - return super(sale_order_line, self).button_confirm(cr, uid, ids, context=context) + res = super(sale_order_line, self).button_confirm(cr, uid, ids, context=context) + self._update_registrations(cr, uid, ids, context=context) + return res def onchange_event_ticket_id(self, cr, uid, ids, event_ticket_id=False, context=None): price = event_ticket_id and self.pool["event.event.ticket"].browse(cr, uid, event_ticket_id, context=context).price or False diff --git a/addons/event_sale/security/ir.model.access.csv b/addons/event_sale/security/ir.model.access.csv index 410458d4b0f..8a4959087c6 100644 --- a/addons/event_sale/security/ir.model.access.csv +++ b/addons/event_sale/security/ir.model.access.csv @@ -1,3 +1,3 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_event_event_ticket_user,event.event.ticket.user,event_sale.model_event_event_ticket,event.group_event_user,1,0,0,0 -access_event_event_ticket_admin,event.event.ticket.admin,event_sale.model_event_event_ticket,event.group_event_manager,1,1,1,1
\ No newline at end of file +access_event_event_ticket_admin,event.event.ticket.admin,event_sale.model_event_event_ticket,event.group_event_manager,1,1,1,1 diff --git a/addons/event_sale/test/confirm.yml b/addons/event_sale/test/confirm.yml index d7e3e610aac..1f5afbcfc8c 100644 --- a/addons/event_sale/test/confirm.yml +++ b/addons/event_sale/test/confirm.yml @@ -34,21 +34,27 @@ name: sale order line event_id: event - - I confirm the sale order + In the event registration I add some attendee detail lines. i choose event product - - !workflow {model: sale.order, ref: order1, action: order_confirm} + !record {model: registration.editor, id: attendee_detail}: + sale_order_id: order1 + event_registration_ids: + - event_id: event + name: 'Administrator' + email: 'abc@example.com' - - I check if the sale order is confirmed + I click apply to create attendees - - !assert {model: sale.order, id: order1}: - - state == 'manual' + !python {model: registration.editor}: | + self.action_make_registration(cr, uid, ref('attendee_detail')) - I check if a registration is created - !python {model: event.registration}: | + from openerp.osv import osv + from openerp.tools.translate import _ order_id = ref('order1') order = self.pool.get('sale.order').browse(cr, uid,order_id) - registration_ids = self.search(cr,uid,[('origin','=',order.name)]) + registration_ids = self.search(cr,uid,[('origin', '=', order.name)]) if registration_ids == []: raise osv.except_osv(_('Error!'),_("The registration is not created.")) - diff --git a/addons/event_sale/views/event.xml b/addons/event_sale/views/event.xml index dd4c4393bbc..5b8344fb55e 100644 --- a/addons/event_sale/views/event.xml +++ b/addons/event_sale/views/event.xml @@ -20,6 +20,7 @@ <field name="arch" type="xml"> <field name="event_id" position="after"> <field name="event_ticket_id"/> + <field name="origin"/> </field> </field> </record> @@ -29,8 +30,9 @@ <field name="model">event.registration</field> <field name="inherit_id" ref="event.view_event_registration_form" /> <field name="arch" type="xml"> - <field name="user_id" position="after"> + <field name="event_id" position="after"> <field name="event_ticket_id" domain="[('event_id', '=', event_id)]"/> + <field name="origin"/> </field> </field> </record> diff --git a/addons/event_sale/views/report_registrationbadge.xml b/addons/event_sale/views/report_registrationbadge.xml index f9f33c9b2b9..93148ecf115 100644 --- a/addons/event_sale/views/report_registrationbadge.xml +++ b/addons/event_sale/views/report_registrationbadge.xml @@ -1,52 +1,39 @@ <?xml version="1.0" encoding="utf-8"?> <openerp> <data> - <template id="report_registrationbadge"> + <template id="event.report_registration_badge"> <t t-call="report.html_container"> <t t-foreach="docs" t-as="o"> <div class="page"> <div class="row"> <!-- Inner right --> <div class="col-xs-6 text-center" style="padding-left:7mm; border-left:1px dashed black; height: 149mm; -webkit-transform:rotate(180deg); padding-top: 7mm"> - <div> - <div class="row"> - <div class="col-xs-12"> - <div> - <span t-if="o.event_id.organizer_id.company_id.logo_web and o.event_id.organizer_id.is_company"> - <img t-att-src="'data:image/png;base64,%s' % o.event_id.organizer_id.company_id.logo_web" style="max-height:1cm; max-width:4cm;"/> - </span> - <div> - <h5 t-field="o.event_id.name"/> - </div> - <div> - <h5>June 4th - 6th , 2014</h5> - </div> - </div> - </div> + <div class="row"> + <div class="col-xs-12"> + <span t-if="o.event_id.organizer_id.company_id.logo_web and o.event_id.organizer_id.is_company"> + <img t-att-src="'data:image/png;base64,%s' % o.event_id.organizer_id.company_id.logo_web" style="max-height:1cm; max-width:4cm;"/> + </span> + <h5 t-field="o.event_id.name"/> + <h5>( <i class="fa fa-clock-o"></i> <span itemprop="startDate" t-field="o.event_id.date_begin" t-field-options='{"hide_seconds":"True"}'> </span> <i>to</i> <span itemprop="endDate" t-field="o.event_id.date_end" t-field-options='{"hide_seconds":"True"}'> </span> )</h5> </div> - <div class="row"> - <div class="col-xs-12 text-center"> - <t t-if="o.event_ticket_id"> - <div> - <strong><span t-field="o.event_ticket_id"/></strong> - </div> + <div class="col-xs-12 text-center"> + <t t-if="o.event_ticket_id"> + <div> + <strong><span t-field="o.event_ticket_id"/></strong> + </div> + </t> + <div> + <t t-if="o.partner_id.is_company == True"> + <small> + <h3 t-field="o.partner_id.name"/> + <h5 t-field="o.name"/> + </small> </t> - <div> - <t t-if="o.partner_id.is_company == True"> - <small> - <h3 t-field="o.partner_id.name"/> - <h5 t-field="o.name"/> - </small> - </t> - </div> </div> </div> - <br/> - <div class="row"> - <div class="col-xs-12 mt32 text-center" style="background: lightgrey;"> - <h3>Status</h3> - </div> - </div> + <div class="col-xs-12 mt32 text-center" style="background: lightgrey;"> + <h3>Status</h3> + </div> </div> </div> diff --git a/addons/event_sale/wizard/__init__.py b/addons/event_sale/wizard/__init__.py new file mode 100644 index 00000000000..f6bf2a429d9 --- /dev/null +++ b/addons/event_sale/wizard/__init__.py @@ -0,0 +1 @@ +import event_edit_registration diff --git a/addons/event_sale/wizard/event_edit_registration.py b/addons/event_sale/wizard/event_edit_registration.py new file mode 100644 index 00000000000..dfc43b0c4a2 --- /dev/null +++ b/addons/event_sale/wizard/event_edit_registration.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- + +from openerp import models, fields, api + + +class SaleOrderEventRegistration(models.TransientModel): + _name = "registration.editor" + + sale_order_id = fields.Many2one('sale.order', 'Sale Order', required=True) + event_registration_ids = fields.One2many('registration.editor.line', 'editor_id', string='Registrations to Edit') + + @api.model + def default_get(self, fields): + res = super(SaleOrderEventRegistration, self).default_get(fields) + if not res.get('sale_order_id'): + 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)]) + + 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 reg in existing_registrations: + attendee_list.append({ + 'event_id': reg.event_id.id, + 'event_ticket_id': reg.event_ticket_id.id, + 'registration_id': reg.id, + 'name': reg.name, + 'email': reg.email, + 'phone': reg.phone, + }) + 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, + }) + res['event_registration_ids'] = attendee_list + return res + + @api.multi + def action_make_registration(self): + Registration = self.env['event.registration'] + for wizard in self: + for wiz_registration in wizard.event_registration_ids: + if wiz_registration.registration_id: + wiz_registration.registration_id.write(wiz_registration.get_registration_data()[0]) + else: + Registration.create(wiz_registration.get_registration_data()[0]) + return {'type': 'ir.actions.act_window_close'} + + +class RegistrationEditorLine(models.TransientModel): + """Event Registration""" + _name = "registration.editor.line" + + editor_id = fields.Many2one('registration.editor') + 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') + email = fields.Char(string='Email') + phone = fields.Char(string='Phone') + name = fields.Char(string='Name', select=True) + + @api.one + def get_registration_data(self): + return { + 'event_id': self.event_id.id, + 'event_ticket_id': self.event_ticket_id.id, + 'partner_id': self.editor_id.sale_order_id.partner_id.id, + 'name': self.name or self.editor_id.sale_order_id.partner_id.name, + '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, + } diff --git a/addons/event_sale/wizard/event_edit_registration.xml b/addons/event_sale/wizard/event_edit_registration.xml new file mode 100644 index 00000000000..f9b572c2176 --- /dev/null +++ b/addons/event_sale/wizard/event_edit_registration.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data> + + <record id="view_event_registration_editor_form" model="ir.ui.view"> + <field name="name">registration.editor.form</field> + <field name="model">registration.editor</field> + <field name="arch" type="xml"> + <form string="Registration"> + <p>Before confirming <field name="sale_order_id" readonly="1" class="oe_inline"/> + please give details about the registrations</p> + <field name="event_registration_ids"> + <tree string="Registration" editable="top" create="false" delete="false"> + <field name="event_id" readonly='1'/> + <field name="registration_id" readonly='1'/> + <field name="event_ticket_id" domain="[('event_id', '=', event_id)]" readonly='1'/> + <field name="name"/> + <field name="email"/> + <field name="phone"/> + </tree> + </field> + <footer> + <button string="Apply" name="action_make_registration" type="object" class="oe_highlight"/> + or + <button string="Cancel" class="oe_link" special="cancel"/> + </footer> + </form> + </field> + </record> + + <record id="action_sale_order_event_registration" model="ir.actions.act_window"> + <field name="name">Event Registrations</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">registration.editor</field> + <field name="view_type">form</field> + <field name="view_mode">form</field> + <field name="view_id" ref="view_event_registration_editor_form"/> + <field name="target">new</field> + <field name="context">{'default_sale_order_id': context.get('default_sale_order_id')}</field> + </record> + + </data> +</openerp> diff --git a/addons/payment_transfer/models/payment_acquirer.py b/addons/payment_transfer/models/payment_acquirer.py index 63883b3df30..8b8dc7a9649 100644 --- a/addons/payment_transfer/models/payment_acquirer.py +++ b/addons/payment_transfer/models/payment_acquirer.py @@ -83,4 +83,4 @@ class TransferPaymentTransaction(osv.Model): def _transfer_form_validate(self, cr, uid, tx, data, context=None): _logger.info('Validated transfer payment for tx %s: set as pending' % (tx.reference)) - return tx.write({'state': 'pending'}) + return tx.write({'state': 'done'}) diff --git a/addons/website_event/controllers/main.py b/addons/website_event/controllers/main.py index da343c9fa3b..134371a3caf 100644 --- a/addons/website_event/controllers/main.py +++ b/addons/website_event/controllers/main.py @@ -4,9 +4,10 @@ import time import werkzeug.urls from datetime import datetime, timedelta from dateutil.relativedelta import relativedelta +from collections import OrderedDict from openerp import http -from openerp import tools +from openerp import tools, SUPERUSER_ID from openerp.addons.website.models.website import slug from openerp.http import request from openerp.tools.translate import _ @@ -195,6 +196,7 @@ class website_event(http.Controller): 'name': event_name, 'date_begin': date_begin.strftime('%Y-%m-%d'), 'date_end': (date_begin + timedelta(days=(1))).strftime('%Y-%m-%d'), + 'seats_available': 1000, } event_id = Event.create(request.cr, request.uid, vals, context=context) event = Event.browse(request.cr, request.uid, event_id, context=context) @@ -225,3 +227,48 @@ class website_event(http.Controller): "event": event, "url": event.website_url}) return request.website.render("website_event.country_events_list", result) + + def _process_tickets_details(self, data): + nb_register = int(data.get('nb_register-0', 0)) + if nb_register: + return [{'id': 0, 'name': 'Subscription', 'quantity': nb_register, 'price': 0}] + return [] + + @http.route(['/event/<model("event.event"):event>/registration/new'], type='json', auth="public", methods=['POST'], website=True) + def registration_new(self, event, **post): + tickets = self._process_tickets_details(post) + if not tickets: + return request.redirect("/event/%s" % slug(event)) + return request.website._render("website_event.registration_attendee_details", {'tickets': tickets, 'event': event}) + + def _process_registration_details(self, details): + ''' Process data posted from the attendee details form. ''' + registrations = {} + for key, value in details.iteritems(): + counter, field_name = key.split('-', 1) + registrations.setdefault(counter, dict())[field_name] = value + return registrations.values() + + @http.route(['/event/<model("event.event"):event>/registration/confirm'], type='http', auth="public", methods=['POST'], website=True) + def registration_confirm(self, event, **post): + cr, uid, context = request.cr, request.uid, request.context + Registration = request.registry['event.registration'] + registrations = self._process_registration_details(post) + + registration_ids = [] + user = request.registry.get('res.users').browse(cr, uid, uid, context=context) + for registration in registrations: + registration_ids.append( + Registration.create(cr, SUPERUSER_ID, { + 'name': registration.get('name', user.name), + 'phone': registration.get('phone', user.phone), + 'email': registration.get('email', user.email), + 'partner_id': user.partner_id.id, + 'event_id': event.id, + }, context=context)) + + attendees = Registration.browse(cr, uid, registration_ids, context=context) + return request.website.render("website_event.registration_complete", { + 'attendees': attendees, + 'event': event, + }) diff --git a/addons/website_event/models/event.py b/addons/website_event/models/event.py index 37b6d113aee..c633aae4cc2 100644 --- a/addons/website_event/models/event.py +++ b/addons/website_event/models/event.py @@ -4,6 +4,7 @@ from openerp import models, fields, api, _ # from openerp.osv import osv, fields from openerp import SUPERUSER_ID +from openerp.models import NewId # from openerp.tools.translate import _ import re @@ -31,11 +32,13 @@ class event(models.Model): @api.one @api.depends('name') def _website_url(self): - self.website_url = "/event/" + slug(self) + if isinstance(self.id, NewId): + self.website_url = '' + else: + self.website_url = "/event/" + slug(self) - @api.one def _default_hashtag(self): - self.twitter_hashtag = re.sub("[- \\.\\(\\)\\@\\#\\&]+", "", self.env.user.company_id.name).lower() + return re.sub("[- \\.\\(\\)\\@\\#\\&]+", "", self.env.user.company_id.name).lower() show_menu = fields.Boolean('Has Dedicated Menu', compute='_get_show_menu', inverse='_set_show_menu') menu_id = fields.Many2one('website.menu', 'Event Menu') diff --git a/addons/website_event/static/src/js/website_event.js b/addons/website_event/static/src/js/website_event.js new file mode 100644 index 00000000000..f6a4328def4 --- /dev/null +++ b/addons/website_event/static/src/js/website_event.js @@ -0,0 +1,23 @@ +$(document).ready(function () { + + // Catch registration form event, because of JS for attendee details + $('#registration_form .a-submit') + .off('click') + .removeClass('a-submit') + .click(function (ev) { + ev.preventDefault(); + ev.stopPropagation(); + var $form = $(ev.currentTarget).closest('form'); + var post = {}; + $("select").each(function() { + post[$(this)[0].name] = $(this).val(); + }); + openerp.jsonRpc($form.attr('action'), 'call', post).then(function (modal) { + var $modal = $(modal); + $modal.appendTo($form).modal() + $modal.on('click', '.js_goto_event', function () { + $modal.modal('hide'); + }); + }); + }); +}); diff --git a/addons/website_event/views/website_event.xml b/addons/website_event/views/website_event.xml index 5654a8eccb5..b0c8fbf64f7 100644 --- a/addons/website_event/views/website_event.xml +++ b/addons/website_event/views/website_event.xml @@ -5,6 +5,7 @@ <template id="event_script" inherit_id="website.assets_frontend" name="Country Events Snippet Script"> <xpath expr="/t" position="inside"> <script type="text/javascript" src="/website_event/static/src/js/website_geolocation.js"></script> + <script type="text/javascript" src="/website_event/static/src/js/website_event.js"></script> </xpath> </template> @@ -289,6 +290,9 @@ <template id="event_description_full"> <t t-call="website_event.event_details"> <div class="col-md-8"> + <t t-call="website_event.registration_template"/> + <div class="clearfix"/> + <hr/> <div itemprop="description" t-field="event.description"></div> <div class="clearfix"/> <ul class="media-list" id="comment"> @@ -367,6 +371,145 @@ </t> </template> +<!-- Registration Templates --> +<template id="registration_template"> + <form id="registration_form" t-attf-action="/event/#{slug(event)}/registration/new" method="post"> + <table itemprop="offers" class="table table-striped"> + <thead> + <tr> + <th>Ticket Type</th> + <th style="min-width: 100px">End of Subscription</th> + <th style="min-width: 100px">Price</th> + <th></th> + <th>Quantity</th> + </tr> + </thead> + <tbody> + <tr> + <td>Event Subscription</td> + <td> + <t t-if="event.date_begin"> + <span t-field="event.date_begin"/> + </t> + <t t-if="not event.date_begin"> + <span>Unlimited</span> + </t> + </td> + <td>Free</td> + <td></td> + <td> + <select t-if="event.seats_available" name="nb_register-0" class="form-control"> + <t t-foreach="range(0, event.seats_available > 9 and 10 or event.seats_available+1)" t-as="nb"> + <option t-esc="nb"/> + </t> + </select> + <span t-if="not event.seats_available">Sold Out</span> + </td> + </tr> + </tbody> + </table> + <t t-if="event.seats_available"> + <button type="submit" t-if="event.seats_available" class="btn btn-primary btn-lg pull-right a-submit" t-attf-id="#{event.id}">Register Now</button> + </t> + </form> +</template> + +<template id="registration_attendee_details" name="Registration Attendee Details"> + <div id="modal_attendees_registration" class="modal fade" tabindex="-1" role="dialog"> + <div class="modal-dialog modal-lg"> + <form id="attendee_registration" t-attf-action="/event/#{slug(event)}/registration/confirm" method="post"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button> + <h4 class="modal-title" id="myModalLabel"><strong>Attendees</strong></h4> + </div> + <div class="modal-body"> + <div class="container"> + <t t-set="counter" t-value="0"/> + <t t-foreach="tickets" t-as="ticket"> + <t t-foreach="range(1, ticket['quantity'] + 1)" t-as="att_counter"> + <t t-set="counter" t-value="counter + 1"/> + <div> + <h4><strong>Ticket #<t t-raw="counter"/>: <t t-esc="ticket['name']"/> ( + <t t-if="ticket['price'] > 0"><t t-esc="ticket['price']"/><t t-esc="website.pricelist_id.currency_id.symbol"/></t> + <t t-if="ticket['price'] == 0">Free</t> + )</strong></h4><hr/> + </div> + <div class="row"> + <div class="col-md-4"><strong>Name</strong></div> + <div class="col-md-4"><strong>Phone</strong></div> + <div class="col-md-4"><strong>Email</strong></div> + </div> + <div class="row"> + <div class="col-md-4"><input class='form-control input-sm' type='text' t-attf-name="#{counter}-name"/></div> + <div class="col-md-4"><input class='form-control input-sm' type='tel' t-attf-name="#{counter}-phone"/></div> + <div class="col-md-4"><input class='form-control input-sm' type='email' t-attf-name="#{counter}-email"/></div> + <input class='hidden' type='text' t-attf-name="#{counter}-ticket_id" t-attf-value="#{ticket['id']}"/> + </div><hr/> + </t> + <t t-set="counter" t-value="counter + ticket['quantity']"/> + </t> + </div> + </div> + <div class="modal-footer"> + <div class="pull-left"> + <button type="submit" class="btn btn-primary">Continue</button> or + <a class="js_goto_event"> Cancel Registration</a> + </div> + </div> + </div> + </form> + </div> + </div> +</template> + +<template id="registration_complete" name="Registration Completed"> + <t t-call="website.layout"> + <div class="container"> + <h3>We are glad to confirm your subscription to our event</h3> + <div class="row"> + <div class="col-md-3 mt16"> + <h4>Event Details</h4> + </div> + <div class="col-md-9 mt16"> + <h4><a t-attf-href="/event/#{slug(event)}"><t t-esc="event.name"/></a></h4> + <i class="fa fa-clock-o"/> <span itemprop="startDate" t-field="event.date_begin" t-field-options='{"hide_seconds":"True"}'> </span> <i>to</i> <span itemprop="endDate" t-field="event.date_end" t-field-options='{"hide_seconds":"True"}'> </span> + <div itemprop="location" class="mt16 mb8" t-field="event.address_id" t-field-options='{ + "widget": "contact", + "fields": ["address", "phone", "mobile", "fax", "email"] + }'/> + </div> + <div class="col-md-3 mt16"> + <h4>Your subscription</h4> + </div> + <div class="col-md-9 mt16"> + <table class='table table-bordered table-striped'> + <thead> + <tr> + <th>Reference</th> + <th>Name</th> + <th>E-mail</th> + <th>Phone</th> + </tr> + </thead> + <tbody> + <t t-foreach="attendees" t-as="attendee"> + <tr> + <td><t t-esc="attendee.id"/></td> + <td><i class="fa fa-user"></i> <t t-if='attendee.name'><t t-esc="attendee.name"/></t><t t-if='not attendee.name'>N/A</t></td> + <td><i class="fa fa-envelope"></i> <t t-if='attendee.email'><t t-esc="attendee.email"/></t><t t-if='not attendee.email'>N/A</t></td> + <td><i class="fa fa-phone"></i> <t t-if='attendee.phone'><t t-esc="attendee.phone"/></t><t t-if='not attendee.phone'>N/A</t></td> + </tr> + </t> + </tbody> + </table> + </div> + </div> + </div> + </t> +</template> + +<!-- Snippets --> <template id="country_events" name="Country Events" inherit_id="website.snippets"> <xpath expr="//div[@id='snippet_content']" position="inside"> <div> @@ -398,6 +541,7 @@ </xpath> </template> +<!-- Misc templates --> <template id="country_events_list" name="Country"> <t t-ignore="true"> <t t-if="events"> diff --git a/addons/website_event_sale/__openerp__.py b/addons/website_event_sale/__openerp__.py index e0ab12e0f78..d259d0f6a99 100644 --- a/addons/website_event_sale/__openerp__.py +++ b/addons/website_event_sale/__openerp__.py @@ -2,7 +2,7 @@ { 'name': "Online Event's Tickets", - 'category': 'Hidden', + 'category': 'Website', 'summary': "Sell Your Event's Tickets", 'website': 'https://www.odoo.com/page/events', 'version': '1.0', diff --git a/addons/website_event_sale/controllers/main.py b/addons/website_event_sale/controllers/main.py index 063e60c7df6..c84d9df1dc3 100644 --- a/addons/website_event_sale/controllers/main.py +++ b/addons/website_event_sale/controllers/main.py @@ -28,39 +28,51 @@ from openerp.tools.translate import _ class website_event(website_event): - @http.route(['/event/cart/update'], type='http', auth="public", methods=['POST'], website=True) - def cart_update(self, event_id, **post): + def _process_tickets_details(self, data): + ticket_post = {} + for key, value in data.iteritems(): + if not key.startswith('nb_register') or not '-' in key: + continue + items = key.split('-') + if len(items) < 2: + continue + ticket_post[int(items[1])] = int(value) + tickets = request.registry['event.event.ticket'].browse(request.cr, request.uid, ticket_post.keys(), request.context) + return [{'id': ticket.id, 'name': ticket.name, 'quantity': ticket_post[ticket.id], 'price': ticket.price} for ticket in tickets if ticket_post[ticket.id]] + + @http.route(['/event/<model("event.event"):event>/registration/confirm'], type='http', auth="public", methods=['POST'], website=True) + def registration_confirm(self, event, **post): cr, uid, context = request.cr, request.uid, request.context - ticket_obj = request.registry.get('event.event.ticket') + order = request.website.sale_get_order(force_create=1) - sale = False - for key, value in post.items(): - quantity = int(value or "0") - if not quantity: - continue - sale = True - ticket_id = key.split("-")[0] == 'ticket' and int(key.split("-")[1]) or None - ticket = ticket_obj.browse(cr, SUPERUSER_ID, ticket_id, context=context) - order = request.website.sale_get_order(force_create=1) - order.with_context(event_ticket_id=ticket.id)._cart_update(product_id=ticket.product_id.id, add_qty=quantity) + 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) - if not sale: - return request.redirect("/event/%s" % event_id) return request.redirect("/shop/checkout") def _add_event(self, event_name="New Event", context={}, **kwargs): try: dummy, res_id = request.registry.get('ir.model.data').get_object_reference(request.cr, request.uid, 'event_sale', 'product_product_event') - context['default_event_ticket_ids'] = [[0,0,{ + context['default_event_ticket_ids'] = [[0, 0, { 'name': _('Subscription'), 'product_id': res_id, - 'deadline' : False, + 'deadline': False, 'seats_max': 1000, 'price': 0, }]] except ValueError: pass return super(website_event, self)._add_event(event_name, context, **kwargs) - - - diff --git a/addons/website_event_sale/static/src/js/website.tour.event_sale.js b/addons/website_event_sale/static/src/js/website.tour.event_sale.js index 0a20686cd35..d9751f2a2d2 100644 --- a/addons/website_event_sale/static/src/js/website.tour.event_sale.js +++ b/addons/website_event_sale/static/src/js/website.tour.event_sale.js @@ -13,24 +13,40 @@ }, { waitNot: 'a[href*="/event"]:contains("Conference on Business Apps")', - title: "select 2 Standard tickets", + title: "select 1 Standard ticket", element: 'select:eq(0)', - sampleText: '2', + sampleText: '1', }, { - title: "select 3 VIP tickets", - waitFor: 'select:eq(0) option:contains(2):selected', + title: "select 2 VIP tickets", + waitFor: 'select:eq(0) option:contains(1):selected', element: 'select:eq(1)', - sampleText: '3', + sampleText: '2', }, { title: "Order Now", - waitFor: 'select:eq(1) option:contains(3):selected', + waitFor: 'select:eq(1) option:contains(2):selected', element: '.btn-primary:contains("Order Now")', }, { + title: "Add the details of attendees", + waitFor: 'form[id="attendee_registration"] .btn:contains("Continue")', + autoComplete: function (tour) { + $("input[name='1-name']").val("Att1"); + $("input[name='1-phone']").val("111 111"); + $("input[name='1-email']").val("att1@example.com"); + $("input[name='1-name']").val("Att2"); + $("input[name='1-phone']").val("222 222"); + $("input[name='1-email']").val("att2@example.com"); + }, + }, + { + title: "click in modal on 'Continue' button", + element: '.modal button:contains("Continue")', + }, + { title: "Check the cart", - element: '#top_menu .my_cart_quantity:contains(5)' + element: '#top_menu .my_cart_quantity:contains(3)' }, { title: "Check if the cart have 2 order lines and add one VIP ticket", @@ -39,7 +55,7 @@ }, { title: "Process Checkout", - waitFor: '#top_menu .my_cart_quantity:contains(6)', + waitFor: '#top_menu .my_cart_quantity:contains(4)', element: '.btn-primary:contains("Process Checkout")' }, { diff --git a/addons/website_event_sale/views/website_event_sale.xml b/addons/website_event_sale/views/website_event_sale.xml index cf68e99146d..7a763815d78 100644 --- a/addons/website_event_sale/views/website_event_sale.xml +++ b/addons/website_event_sale/views/website_event_sale.xml @@ -25,57 +25,61 @@ </xpath> </template> -<template id="event_description_full" inherit_id="website_event.event_description_full" customize_show="True" name="Event's Ticket form"> - <xpath expr="//div[@t-field='event.description']" position="before"> - <form t-attf-action="/event/cart/update?event_id=#{ event.id }" method="post" t-if="event.event_ticket_ids"> - <table itemprop="offers" class="table table-striped"> - <thead> - <tr> - <th>Ticket Type</th> - <th style="min-width: 100px">Sales End</th> - <th style="min-width: 100px">Price</th> - <th></th> - <th>Quantity</th> - </tr> - </thead> - <tbody> - <t t-foreach="event.event_ticket_ids" t-as="ticket"> - <tr itemscope="itemscope" itemtype="http://data-vocabulary.org/Offer" t-if="not ticket.is_expired"> - <td itemscope="itemscope" itemtype="http://data-vocabulary.org/Product"> - <div itemprop="name" t-field="ticket.name"/> - <div><small itemprop="description" t-field="ticket.product_id.description_sale"/></div> - </td> - <td><span itemprop="priceValidUntil" t-field="ticket.deadline"/></td> - <td> - <t t-if="ticket.price or editable"><span t-field="ticket.price" t-field-options='{ - "widget": "monetary", - "display_currency": "website.pricelist_id.currency_id" - }'/> - <span itemprop="price" style="display:none;" t-esc="ticket.price"/> - <span itemprop="priceCurrency" style="display:none;" t-esc="website.pricelist_id.currency_id.name"/> +<template id="registration_template" inherit_id="website_event.registration_template" customize_show="True" name="Event's Ticket form"> + <xpath expr="//tbody" position="replace"> + <tbody> + <t t-foreach="event.event_ticket_ids" t-as="ticket"> + <tr itemscope="itemscope" itemtype="http://data-vocabulary.org/Offer" t-if="not ticket.is_expired"> + <td itemscope="itemscope" itemtype="http://data-vocabulary.org/Product"> + <div itemprop="name" t-field="ticket.name"/> + <div><small itemprop="description" t-field="ticket.product_id.description_sale"/></div> + </td> + <td> + <t t-if="ticket.deadline"> + <span itemprop="priceValidUntil" t-field="ticket.deadline"/> + </t> + <t t-if="not ticket.deadline"> + <span>Unlimited</span> + </t> + </td> + <td> + <t t-if="ticket.price or editable"><span t-field="ticket.price" t-field-options='{ + "widget": "monetary", + "display_currency": "website.pricelist_id.currency_id" + }'/> + <span itemprop="price" style="display:none;" t-esc="ticket.price"/> + <span itemprop="priceCurrency" style="display:none;" t-esc="website.pricelist_id.currency_id.name"/> + </t> + <t t-if="not ticket.price and not editable"> + <span>Free</span> + </t> + </td> + <td> + <span t-if="ticket.seats_max and ((ticket.seats_reserved or 0)*100 / ticket.seats_max)>75" class="text-muted"> + <t t-esc="ticket.seats_max - ticket.seats_reserved"/> <span>left</span> + </span> + </td> + <td> + <select t-if="ticket.seats_available" t-attf-name="nb_register-#{ticket.id}" class="form-control"> + <t t-foreach="range(0, ticket.seats_available > 9 and 10 or ticket.seats_available+1 )" t-as="nb"> + <option t-esc="nb"/> </t> - <t t-if="not ticket.price and not editable"> - <span>Free</span> - </t> - </td> - <td> - <span t-if="ticket.seats_max and ((ticket.seats_reserved or 0)*100 / ticket.seats_max)>75" class="text-muted"> - <t t-esc="ticket.seats_max - ticket.seats_reserved"/> <span>left</span> - </span> - </td> - <td> - <select t-if="ticket.seats_available" t-attf-name="ticket-#{ ticket.id }" class="form-control"> - <t t-foreach="range(0, ticket.seats_available > 9 and 10 or ticket.seats_available+1 )" t-as="nb"><option t-esc="nb"/></t> - </select> - <span t-if="not ticket.seats_available">Sold Out</span> - </td> - </tr> - </t> - </tbody> - </table> - <button type="submit" class="btn btn-primary btn-lg pull-right" t-if="event.seats_available">Order Now</button> - <div class="clearfix"/> - <hr/> + </select> + <span t-if="not ticket.seats_available">Sold Out</span> + </td> + </tr> + </t> + </tbody> + </xpath> + <xpath expr="//button[@type='submit']" position="replace"> + <button type="submit" t-if="event.seats_available" class="btn btn-primary btn-lg pull-right a-submit" t-attf-id="#{event.id}">Order Now</button> + <form t-if="not event.event_ticket_ids"> + <div class="alert alert-info"> + Event registration not yet started. + <t t-if="uid"> + <i class="fa fa-plus-circle"><a t-attf-href="/web#id=#{event.id}&view_type=form&model=event.event"> <em>Configure Event Registration</em></a></i> + </t> + </div> </form> </xpath> </template> |
