Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Expire initializing attendees automatically #7050

Merged
merged 3 commits into from
Jun 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 98 additions & 84 deletions app/api/helpers/scheduled_jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,104 +200,116 @@ def send_event_fee_notification_followup():

@celery.task(base=RequestContextTask, name='expire.pending.tickets')
def expire_pending_tickets():
from app.instance import current_app as app
Order.query.filter(
Order.status == 'pending',
(Order.created_at + datetime.timedelta(minutes=30))
<= datetime.datetime.now(datetime.timezone.utc),
).update({'status': 'expired'})
db.session.commit()


@celery.task(base=RequestContextTask, name='expire.initializing.tickets')
def expire_initializing_tickets():
order_expiry_time = get_settings()['order_expiry_time']
query = db.session.query(Order.id).filter(
Order.status == 'initializing',
Order.paid_via == None,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comparison to None should be 'if cond is None:'

(Order.created_at + datetime.timedelta(minutes=order_expiry_time))
<= datetime.datetime.now(datetime.timezone.utc),
)
# pytype: disable=attribute-error
TicketHolder.query.filter(TicketHolder.order_id.in_(query.subquery())).delete(
synchronize_session=False
)
# pytype: enable=attribute-error
query.update({'status': 'expired'})

with app.app_context():
db.session.query(Order).filter(
Order.status == 'pending',
(Order.created_at + datetime.timedelta(minutes=30))
<= datetime.datetime.now(),
).update({'status': 'expired'})
try:
db.session.commit()
except Exception:
db.session.rollback()
raise


@celery.task(base=RequestContextTask, name='delete.ticket.holders.no.order.id')
def delete_ticket_holders_no_order_id():
from app.instance import current_app as app

with app.app_context():
order_expiry_time = get_settings()['order_expiry_time']
TicketHolder.query.filter(
TicketHolder.order_id == None,
TicketHolder.deleted_at.is_(None),
TicketHolder.created_at + datetime.timedelta(minutes=order_expiry_time)
< datetime.datetime.utcnow(),
).delete(synchronize_session=False)
db.session.commit()
order_expiry_time = get_settings()['order_expiry_time']
TicketHolder.query.filter(
TicketHolder.order_id == None,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comparison to None should be 'if cond is None:'

TicketHolder.deleted_at.is_(None),
TicketHolder.created_at + datetime.timedelta(minutes=order_expiry_time)
< datetime.datetime.utcnow(),
).delete(synchronize_session=False)
db.session.commit()


@celery.task(base=RequestContextTask, name='event.invoices.mark.due')
def event_invoices_mark_due():
from app.instance import current_app as app

with app.app_context():
db.session.query(EventInvoice).filter(
EventInvoice.status == 'upcoming',
Event.id == EventInvoice.event_id,
Event.ends_at >= datetime.datetime.now(),
(
EventInvoice.created_at + datetime.timedelta(days=30)
<= datetime.datetime.now()
),
).update({EventInvoice.status: 'due'}, synchronize_session=False)
db.session.query(EventInvoice).filter(
EventInvoice.status == 'upcoming',
Event.id == EventInvoice.event_id,
Event.ends_at >= datetime.datetime.now(),
(
EventInvoice.created_at + datetime.timedelta(days=30)
<= datetime.datetime.now()
),
).update({EventInvoice.status: 'due'}, synchronize_session=False)


@celery.task(base=RequestContextTask, name='send.monthly.event.invoice')
def send_monthly_event_invoice():
from app.instance import current_app as app

with app.app_context():
events = Event.query.filter_by(deleted_at=None, state='published').all()
for event in events:
# calculate net & gross revenues
user = event.owner
admin_info = get_settings()
currency = event.payment_currency
try:
ticket_fee_object = (
db.session.query(TicketFees).filter_by(currency=currency).one()
)
except NoResultFound:
logger.error('Ticket Fee not found for event id {id}'.format(id=event.id))
continue

ticket_fee_percentage = ticket_fee_object.service_fee
ticket_fee_maximum = ticket_fee_object.maximum_fee
orders = Order.query.filter_by(event=event).all()
gross_revenue = event.calc_monthly_revenue()
invoice_amount = gross_revenue * (ticket_fee_percentage / 100)
if invoice_amount > ticket_fee_maximum:
invoice_amount = ticket_fee_maximum
net_revenue = gross_revenue - invoice_amount
payment_details = {
'tickets_sold': event.tickets_sold,
'gross_revenue': gross_revenue,
'net_revenue': net_revenue,
'amount_payable': invoice_amount,
}
# save invoice as pdf
pdf = create_save_pdf(
render_template(
'pdf/event_invoice.html',
orders=orders,
user=user,
admin_info=admin_info,
currency=currency,
event=event,
ticket_fee_object=ticket_fee_object,
payment_details=payment_details,
net_revenue=net_revenue,
),
UPLOAD_PATHS['pdf']['event_invoice'],
dir_path='/static/uploads/pdf/event_invoices/',
identifier=event.identifier,
events = Event.query.filter_by(deleted_at=None, state='published').all()

for event in events:
# calculate net & gross revenues
user = event.owner
admin_info = get_settings()
currency = event.payment_currency
try:
ticket_fee_object = (
db.session.query(TicketFees).filter_by(currency=currency).one()
)
# save event_invoice info to DB
except NoResultFound:
logger.error('Ticket Fee not found for event id {id}'.format(id=event.id))
continue

ticket_fee_percentage = ticket_fee_object.service_fee
ticket_fee_maximum = ticket_fee_object.maximum_fee
orders = Order.query.filter_by(event=event).all()
gross_revenue = event.calc_monthly_revenue()
invoice_amount = gross_revenue * (ticket_fee_percentage / 100)
if invoice_amount > ticket_fee_maximum:
invoice_amount = ticket_fee_maximum
net_revenue = gross_revenue - invoice_amount
payment_details = {
'tickets_sold': event.tickets_sold,
'gross_revenue': gross_revenue,
'net_revenue': net_revenue,
'amount_payable': invoice_amount,
}
# save invoice as pdf
pdf = create_save_pdf(
render_template(
'pdf/event_invoice.html',
orders=orders,
user=user,
admin_info=admin_info,
currency=currency,
event=event,
ticket_fee_object=ticket_fee_object,
payment_details=payment_details,
net_revenue=net_revenue,
),
UPLOAD_PATHS['pdf']['event_invoice'],
dir_path='/static/uploads/pdf/event_invoices/',
identifier=event.identifier,
)
# save event_invoice info to DB

event_invoice = EventInvoice(
amount=invoice_amount, invoice_pdf_url=pdf, event_id=event.id
)
save_to_db(event_invoice)
event_invoice = EventInvoice(
amount=invoice_amount, invoice_pdf_url=pdf, event_id=event.id
)
save_to_db(event_invoice)


@celery.on_after_configure.connect
Expand All @@ -318,13 +330,15 @@ def setup_scheduled_task(sender, **kwargs):
sender.add_periodic_task(
crontab(hour=5, minute=30), change_session_state_on_event_completion
)
# Every 45 minutes
sender.add_periodic_task(crontab(minute='*/45'), expire_pending_tickets)
# Every 1st day of month at 0:00
sender.add_periodic_task(
crontab(minute=0, hour=0, day_of_month=1), send_monthly_event_invoice
)
# Every day at 5:00
sender.add_periodic_task(crontab(minute=0, hour=5), event_invoices_mark_due)
# Every 25 minutes
sender.add_periodic_task(crontab(minute='*/25'), expire_pending_tickets)
# Every 10 minutes
sender.add_periodic_task(crontab(minute='*/10'), expire_initializing_tickets)
# Every 5 minutes
sender.add_periodic_task(crontab(minute='*/5'), delete_ticket_holders_no_order_id)
Loading