diff --git a/app/__init__.py b/app/__init__.py index b0909f97e5..6c76abee3e 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -33,7 +33,7 @@ from app.api.helpers.auth import AuthManager from app.api.helpers.scheduled_jobs import send_after_event_mail, send_event_fee_notification, \ send_event_fee_notification_followup, change_session_state_on_event_completion, \ - expire_pending_tickets + expire_pending_tickets, send_monthly_event_invoice from app.models.event import Event from app.models.role_invite import RoleInvite from app.views.healthcheck import health_check_celery, health_check_db, health_check_migrations, check_migrations @@ -242,6 +242,7 @@ def update_sent_state(sender=None, headers=None, **kwargs): scheduler.add_job(send_event_fee_notification_followup, 'cron', day=15) scheduler.add_job(change_session_state_on_event_completion, 'cron', hour=5, minute=30) scheduler.add_job(expire_pending_tickets, 'cron', minute=45) +scheduler.add_job(send_monthly_event_invoice, 'cron', day=1, month='1-12') scheduler.start() diff --git a/app/api/helpers/event_invoices.py b/app/api/helpers/event_invoices.py new file mode 100644 index 0000000000..ac76f5c99b --- /dev/null +++ b/app/api/helpers/event_invoices.py @@ -0,0 +1,21 @@ +import datetime + +from app.models.event_invoice import EventInvoice + + +def fetch_event_invoices(invoice_status): + """ + Helper function to fetch event invoices based on status + """ + + if invoice_status == 'due': + event_invoices = EventInvoice.query.filter(EventInvoice.created_at + datetime.timedelta(days=30) <= + datetime.datetime.now(), + EventInvoice.paid_via is None).all() + elif invoice_status == 'paid': + event_invoices = EventInvoice.query.filter(EventInvoice.paid_via is not None).all() + elif invoice_status == 'upcoming': + event_invoices = EventInvoice.query.filter(EventInvoice.created_at + datetime.timedelta(days=30) > + datetime.datetime.now(), + EventInvoice.paid_via is None).all() + return event_invoices diff --git a/app/api/helpers/scheduled_jobs.py b/app/api/helpers/scheduled_jobs.py index 662aa838e8..7957578594 100644 --- a/app/api/helpers/scheduled_jobs.py +++ b/app/api/helpers/scheduled_jobs.py @@ -2,6 +2,7 @@ import pytz from dateutil.relativedelta import relativedelta +from flask import render_template from app.api.helpers.db import safe_query, save_to_db from app.api.helpers.mail import send_email_after_event, send_email_for_monthly_fee_payment, \ @@ -10,6 +11,8 @@ send_notif_after_event from app.api.helpers.query import get_upcoming_events, get_user_event_roles_by_role_name from app.api.helpers.utilities import monthdelta +from app.api.helpers.files import create_save_pdf +from app.api.helpers.storage import UPLOAD_PATHS from app.models import db from app.models.event import Event from app.models.event_invoice import EventInvoice @@ -17,7 +20,8 @@ from app.models.speaker import Speaker from app.models.session import Session from app.models.ticket import Ticket -from app.models.ticket_fee import get_fee +from app.models.ticket_fee import TicketFees, get_fee + from app.settings import get_settings @@ -150,3 +154,30 @@ def expire_pending_tickets(): (Order.created_at + datetime.timedelta(minutes=30)) <= datetime.datetime.now()).\ update({'status': 'expired'}) db.session.commit() + + +def send_monthly_event_invoice(): + from app import current_app as app + with app.app_context(): + events = Event.query.all() + for event in events: + # calculate net & gross revenues + currency = event.payment_currency + ticket_fee_object = db.session.query(TicketFees).filter_by(currency=currency).one() + 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() + ticket_fees = event.tickets_sold * ticket_fee_percentage + if ticket_fees > ticket_fee_maximum: + ticket_fees = ticket_fee_maximum + net_revenue = gross_revenue - ticket_fees + # save invoice as pdf + pdf = create_save_pdf(render_template('pdf/event_invoice.html', orders=orders, + ticket_fee_object=ticket_fee_object, gross_revenue=gross_revenue, + 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=net_revenue, invoice_pdf_url=pdf, event_id=event.id) + save_to_db(event_invoice) diff --git a/app/api/helpers/storage.py b/app/api/helpers/storage.py index efb4eb65f8..3f0e9e02b5 100644 --- a/app/api/helpers/storage.py +++ b/app/api/helpers/storage.py @@ -81,7 +81,8 @@ 'pdf': { 'ticket_attendee': 'attendees/tickets/pdf/{identifier}', 'order': 'orders/invoices/pdf/{identifier}', - 'tickets_all': 'orders/tickets/pdf/{identifier}' + 'tickets_all': 'orders/tickets/pdf/{identifier}', + 'event_invoice': 'events/organizer/invoices/pdf/{identifier}' } } diff --git a/app/models/event.py b/app/models/event.py index 08c0d4e5c3..18b2de2328 100644 --- a/app/models/event.py +++ b/app/models/event.py @@ -396,6 +396,16 @@ def calc_revenue(self): revenue = 0 return revenue + def calc_monthly_revenue(self): + """Returns revenue of current month. Invoice sent every 1st of the month for the previous month""" + previous_month = datetime.datetime.now().month - 1 + monthly_revenue = db.session.query(func.sum(Order.amount)).filter(Order.event_id == self.id, + Order.completed_at.month == previous_month, + Order.status == 'completed').scalar() + if monthly_revenue is None: + monthly_revenue = 0 + return monthly_revenue + @property def tickets_available(self): return self.calc_total_tickets_count() diff --git a/app/templates/pdf/event_invoice.html b/app/templates/pdf/event_invoice.html new file mode 100644 index 0000000000..e69de29bb2