[IMP] event: Split Ticket: registration are now for one attendee only.
authorParamjit Singh Sahota <psa@openerp.com>
Wed, 16 Jul 2014 09:08:10 +0000 (14:38 +0530)
committerThibault Delavallée <tde@openerp.com>
Thu, 9 Oct 2014 08:09:06 +0000 (10:09 +0200)
Registration are now for one attendee only. When buying several seats for an event
you have now one registration for each attendee.

event: nb_register field is removed; as well as unnecessary user_id and it subscribe /
unsubscribe behavior. Also slighly cleaned some views.
event_sale: do not auto confirm registrations linked to a draft sale order. Note: strange
origin is a char field, not a sale_order_id. Added a small wizard to edit attendees
data when confirming a sale order containing event related lines.
website_event: when buying free tickets, ask for attendee details. Added template,
controllers to handle that behavior.
website_event_sale: when buying tickets, ask for attendee details. Events ecommerce
should be better integrated with online events, using inheritance to add details
instead of being very different.

29 files changed:
addons/event/__openerp__.py
addons/event/event.py
addons/event/event_demo.xml
addons/event/event_report.xml [new file with mode: 0644]
addons/event/event_view.xml
addons/event/report/report_event_registration.py
addons/event/tests/test_event_flow.py
addons/event/views/report_registrationbadge.xml [new file with mode: 0644]
addons/event_sale/__init__.py
addons/event_sale/__openerp__.py
addons/event_sale/event_sale_report.xml [deleted file]
addons/event_sale/models/event.py
addons/event_sale/models/sale_order.py
addons/event_sale/security/ir.model.access.csv
addons/event_sale/test/confirm.yml
addons/event_sale/views/event.xml
addons/event_sale/views/report_registrationbadge.xml
addons/event_sale/wizard/__init__.py [new file with mode: 0644]
addons/event_sale/wizard/event_edit_registration.py [new file with mode: 0644]
addons/event_sale/wizard/event_edit_registration.xml [new file with mode: 0644]
addons/payment_transfer/models/payment_acquirer.py
addons/website_event/controllers/main.py
addons/website_event/models/event.py
addons/website_event/static/src/js/website_event.js [new file with mode: 0644]
addons/website_event/views/website_event.xml
addons/website_event_sale/__openerp__.py
addons/website_event_sale/controllers/main.py
addons/website_event_sale/static/src/js/website.tour.event_sale.js
addons/website_event_sale/views/website_event_sale.xml

index b98f9d4..f6336e7 100644 (file)
@@ -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',
index 322d7d6..454454b 100644 (file)
@@ -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
index 41aeaf2..8e2bccf 100644 (file)
         <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">
         <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">
         <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/event_report.xml b/addons/event/event_report.xml
new file mode 100644 (file)
index 0000000..cdbc36c
--- /dev/null
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+
+    <record id="paperformat_euro_lowmargin" model="report.paperformat">
+        <field name="name">European A4 low margin</field>
+        <field name="default" eval="True" />
+        <field name="format">A4</field>
+        <field name="page_height">0</field>
+        <field name="page_width">0</field>
+        <field name="orientation">Portrait</field>
+        <field name="margin_top">5</field>
+        <field name="margin_bottom">5</field>
+        <field name="margin_left">5</field>
+        <field name="margin_right">5</field>
+        <field name="header_line" eval="False" />
+        <field name="header_spacing">0</field>
+        <field name="dpi">80</field>
+    </record>
+
+    <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>
index 891a6da..9371dbe 100644 (file)
@@ -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">
                                         <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 ">&#47;
-                                                    <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 ">&#47;
+                                                <t t-esc="record.seats_reserved.raw_value + record.seats_unconfirmed.raw_value"/> Expected
                                                 <a name="action_event_registration_report" type="object" >&#40;Report&#41;</a>
                                             </t>
                                         </h4>
 
         <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>
                     <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>
                         <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>
             <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>
             <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'}"/>
         </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>
           <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"/>
 
index 5e56ce5..d1a9b5c 100644 (file)
@@ -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,
index 59f2970..3af0fb9 100644 (file)
@@ -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 (file)
index 0000000..69156f4
--- /dev/null
@@ -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>
index c7b5ac7..e2b122e 100644 (file)
@@ -1,3 +1,4 @@
 # -*- coding: utf-8 -*-
 
 import models
+import wizard
index d2300d0..42c528d 100644 (file)
@@ -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/event_sale_report.xml b/addons/event_sale/event_sale_report.xml
deleted file mode 100644 (file)
index 1d9e21b..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<openerp>
-    <data>
-
-    <record id="paperformat_euro_lowmargin" model="report.paperformat">
-        <field name="name">European A4 low margin</field>
-        <field name="default" eval="True" />
-        <field name="format">A4</field>
-        <field name="page_height">0</field>
-        <field name="page_width">0</field>
-        <field name="orientation">Portrait</field>
-        <field name="margin_top">5</field>
-        <field name="margin_bottom">5</field>
-        <field name="margin_left">5</field>
-        <field name="margin_right">5</field>
-        <field name="header_line" eval="False" />
-        <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"/>
-    </record>
-
-    </data>
-</openerp>
\ No newline at end of file
index 8fc46f5..5b66d89 100644 (file)
@@ -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
index 6705f00..e5d26b7 100644 (file)
@@ -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
index 410458d..8a49590 100644 (file)
@@ -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
index d7e3e61..1f5afbc 100644 (file)
          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."))
-
index dd4c439..5b8344f 100644 (file)
@@ -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>
index f9f33c9..93148ec 100644 (file)
@@ -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 (file)
index 0000000..f6bf2a4
--- /dev/null
@@ -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 (file)
index 0000000..dfc43b0
--- /dev/null
@@ -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 (file)
index 0000000..f9b572c
--- /dev/null
@@ -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>
index 63883b3..8b8dc7a 100644 (file)
@@ -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'})
index da343c9..134371a 100644 (file)
@@ -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,
+        })
index 37b6d11..c633aae 100644 (file)
@@ -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 (file)
index 0000000..f6a4328
--- /dev/null
@@ -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');
+                });
+            });
+        });
+});
index 5654a8e..b0c8fbf 100644 (file)
@@ -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>
 
 <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">
     </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>
     </xpath>
 </template>
 
+<!-- Misc templates -->
 <template id="country_events_list" name="Country">
     <t t-ignore="true">
         <t t-if="events">
index e0ab12e..d259d0f 100644 (file)
@@ -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',
index 063e60c..c84d9df 100644 (file)
@@ -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)
-
-
-
index 0a20686..d9751f2 100644 (file)
             },
             {
                 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")'
             },
             {
index cf68e99..7a76381 100644 (file)
     </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)&gt;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)&gt;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}&amp;view_type=form&amp;model=event.event"> <em>Configure Event Registration</em></a></i>
+                </t>
+            </div>
         </form>
     </xpath>
 </template>