Skip to content

Commit

Permalink
[FIX] website_event_sale: refined boundary check for sold-out tickets
Browse files Browse the repository at this point in the history
Reproduction:
1. Select an event, then set a certain maximum amount sold on a paid
ticket, set as 2 for simplicity
2. Go to the Event webpage, then buy all paid tickets
3. Fill out information and attempt to confirm the order
4. The shopping cart is cleared

Reason: We check the event ticket availability in function
_cart_update(). In V14 and V15, we added a new step to update the
pricelist when confirming an order, which will trigger _cart_update().
In V13 update_pricelist is False by default. In V14 and V15, it is
always set to True. The change is to make sure the pricelist is not
changed once the customer chooses the pricelist. As a result, for each
SO, it checks the ticket availability twice. First at when click
register or check out. Second at when submit the address information.
When selling the last ticket, the second check fails and leads to an
empty cart because the seats_reaserved is updated already at the first
step

Fix: add new checking conditions increased_quantity = new_qty > old_qty
to function _cart_update. We only check the availability again when
these new conditions are satisfied.

Current issue: This fix will restore the normal workflow for the last
ticket. But the workflow has its original issue, e.g. it doesn’t update
seats_reserved when increasing the number of tickets.

A solution in SaaS-15.3 is to forbid the customer to manually raise the
event ticket amount

For example:
1. Register 1 ticket, fill in the attendee form and click continue
2. Click review order, increase the number of tickets, then continue
3. The seats_reserved is not updated (can refresh the event page,
seats_reserved is the column confirmed)
If first register 2 tickets and then change to 1, seats_reserved will
change from 2 to 1

This is because seats_reserved is computed from the number of
registration. No new registration is needed when increase the ticket
number, but the registration will decrease when decrease the ticket
number. A solution for it is popping new registration form when the
ticket registration increases.

opw-2784720

closes odoo#90232

X-original-commit: fc689f0
Signed-off-by: Nicolas Lempereur (nle) <nle@odoo.com>
Signed-off-by: Liu Jinjiu (jili) <jili@odoo.com>
  • Loading branch information
Jinjiu96 committed May 3, 2022
1 parent b35f127 commit 5c3bd52
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 2 deletions.
5 changes: 3 additions & 2 deletions addons/website_event_sale/models/sale_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,14 @@ def _cart_update(self, product_id=None, line_id=None, add_qty=0, set_qty=0, **kw

# case: buying tickets for a sold out ticket
values = {}
if ticket and ticket.seats_limited and ticket.seats_available <= 0:
increased_quantity = new_qty > old_qty
if ticket and ticket.seats_limited and ticket.seats_available <= 0 and increased_quantity:
values['warning'] = _('Sorry, The %(ticket)s tickets for the %(event)s event are sold out.') % {
'ticket': ticket.name,
'event': ticket.event_id.name}
new_qty, set_qty, add_qty = 0, 0, -old_qty
# case: buying tickets, too much attendees
elif ticket and ticket.seats_limited and new_qty > ticket.seats_available:
elif ticket and ticket.seats_limited and new_qty > ticket.seats_available and increased_quantity:
values['warning'] = _('Sorry, only %(remaining_seats)d seats are still available for the %(ticket)s ticket for the %(event)s event.') % {
'remaining_seats': ticket.seats_available,
'ticket': ticket.name,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
odoo.define('website_event_sale.tour.last_ticket', function (require) {
'use strict';

var tour = require('web_tour.tour');

tour.register('event_buy_last_ticket', {
test: true,
url: '/event',
},[{
content: "Open the Last ticket test event page",
trigger: '.o_wevent_events_list a:contains("Last ticket test")',
},
{
content: "Show available Tickets",
trigger: '.btn-primary:contains("Register")',
},
{
content: "Select 2 units of `VIP` ticket type",
extra_trigger: '#wrap:not(:has(a[href*="/event"]:contains("Last ticket test")))',
trigger: 'select:eq(0)',
run: 'text 2',
},
{
content: "Click on `Order Now` button",
extra_trigger: 'select:eq(0):has(option:contains(2):propSelected)',
trigger: '.a-submit:contains("Register")',
},
{
content: "Fill attendees details",
trigger: 'form[id="attendee_registration"] .btn:contains("Continue")',
run: function () {
$("input[name='1-name']").val("Att1");
$("input[name='1-phone']").val("111 111");
$("input[name='1-email']").val("att1@example.com");
$("input[name='2-name']").val("Att2");
$("input[name='2-phone']").val("222 222");
$("input[name='2-email']").val("att2@example.com");
},
},
{
content: "Validate attendees details",
extra_trigger: "input[name='1-name'], input[name='2-name']",
trigger: 'button:contains("Continue")',
},
{
content: "Fill address",
trigger: 'form.checkout_autoformat',
run: function () {
$("input[name='name']").val("test1");
$("input[name='email']").val("test@example.com");
$("input[name='phone']").val("111 111");
$("input[name='street']").val("street test 1");
$("input[name='city']").val("testCity");
$("input[name='zip']").val("123");
$('#country_id option:eq(1)').attr('selected', true);
},
},
{
content: "Validate address",
trigger: '.btn-primary:contains("Next")',
},
{
// if the seats_available checking logic is not correct,
// the shopping cart will be cleared when selling the last ticket
// the tour test will be failed here
content: "Select `Wire Transfer` payment method",
trigger: '#payment_method label:contains("Wire Transfer")',
},
// following steps are based on the website_sale_buy.js
{
content: "Pay",
//Either there are multiple payment methods, and one is checked, either there is only one, and therefore there are no radio inputs
extra_trigger: '#payment_method label:contains("Wire Transfer") input:checked,#payment_method:not(:has("input:radio:visible"))',
trigger: 'button[name="o_payment_submit_button"]:visible:not(:disabled)',
},
{
content: "payment finish",
trigger: '.oe_website_sale:contains("Please use the following transfer details")',
// Leave /shop/confirmation to prevent RPC loop to /shop/payment/get_status.
// The RPC could be handled in python while the tour is killed (and the session), leading to crashes
run: function () {
window.location.href = '/contactus'; // Redirect in JS to avoid the RPC loop (20x1sec)
},
timeout: 30000,
},
{
content: "wait page loaded",
trigger: 'h1:contains("Contact us")',
run: function () {}, // it's a check
},
]);
});
22 changes: 22 additions & 0 deletions addons/website_event_sale/tests/test_frontend_buy_tickets.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,27 @@ def setUp(self):
'price': 1500.0,
}])

self.event_3 = self.env['event.event'].create({
'name': 'Last ticket test',
'user_id': self.env.ref('base.user_admin').id,
'date_begin': (Datetime.today() + timedelta(days=5)).strftime('%Y-%m-%d 07:00:00'),
'date_end': (Datetime.today() + timedelta(days=5)).strftime('%Y-%m-%d 16:30:00'),
'website_published': True,
})

self.env['event.event.ticket'].create([{
'name': 'VIP',
'event_id': self.event_3.id,
'product_id': self.env.ref('event_sale.product_product_event').id,
'end_sale_datetime': (Datetime.today() + timedelta(90)).strftime('%Y-%m-%d'),
'price': 1500.0,
'seats_max': 2,
}])


# flush event to ensure having tickets available in the tests
self.event_2.flush()
self.event_3.flush()

(self.env.ref('base.partner_admin') + self.partner_demo).write({
'street': '215 Vine St',
Expand All @@ -65,4 +84,7 @@ def test_admin(self):
def test_demo(self):
self.start_tour("/", 'event_buy_tickets', login="demo")

def test_buy_last_ticket(self):
self.start_tour("/", 'event_buy_last_ticket')

# TO DO - add public test with new address when convert to web.tour format.
11 changes: 11 additions & 0 deletions addons/website_event_sale/views/assets.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>

<template id="assets_tests" inherit_id="web.assets_tests" name="Website Event Sale Assets Tests">
<xpath expr="." position="inside">
<script type="text/javascript" src="/website_event_sale/static/tests/tours/website_event_sale.js"></script>
<script type="text/javascript" src="/website_event_sale/static/tests/tours/website_event_sale_last_ticket.js"></script>
</xpath>
</template>

</odoo>

0 comments on commit 5c3bd52

Please sign in to comment.