Repository: fossasia/open-event-server

Issue:
Sales admin page of eventyay not accessible
The sales admin page of eventyay gives an error message and is not accessible.

Compare https://eventyay.com/admin/sales/live

![Screenshot from 2021-11-03 14-30-37](https://user-images.githubusercontent.com/1583873/140069204-02b1b973-3e51-4df1-8bfe-ac8825209f7e.png)

https://test.eventyay.com/admin/sales/live



Discussion:
please assign these issue to me

We don't assign issues to external contributors. Please comment that you're working on the issue after checking that no one else is, and then send a pull request for it. Thank You!
I am working on this issue

Resolving patch:
diff --git a/app/api/admin_sales/events.py b/app/api/admin_sales/events.py
--- a/app/api/admin_sales/events.py
+++ b/app/api/admin_sales/events.py
@@ -2,9 +2,9 @@
 from marshmallow_jsonapi import fields
 from marshmallow_jsonapi.flask import Schema
 
-from app.api.admin_sales.utils import summary
+from app.api.admin_sales.utils import summary_by_id
 from app.api.bootstrap import api
-from app.api.helpers.db import save_to_db
+from app.api.helpers.db import save_bulk_to_db
 from app.api.helpers.utilities import dasherize
 from app.models import db
 from app.models.event import Event
@@ -71,17 +71,43 @@ class AdminSalesByEventsList(ResourceList):
     def query(self, _):
         return Event.query
 
-    def before_get(self, args, kwargs):
-        events = Event.query.all()
-        for event in events:
-            sales = summary(event)
-            event.completed_order_sales = sales['completed']['sales_total']
-            event.placed_order_sales = sales['placed']['sales_total']
-            event.pending_order_sales = sales['pending']['sales_total']
-            event.completed_order_tickets = sales['completed']['ticket_count']
-            event.placed_order_tickets = sales['placed']['ticket_count']
-            event.pending_order_tickets = sales['pending']['ticket_count']
-            save_to_db(event)
+    def after_get(self, res):
+        """following the get event, compute the price"""
+        if 'data' in res:
+            data = res['data']
+            _events_id = map(lambda x: x['id'], data)
+
+            events = Event.query.filter(Event.id.in_(_events_id)).all()
+            for event in events:
+                sales = summary_by_id(event.id)
+
+                event.completed_order_sales = sales['completed']['sales_total']
+                event.placed_order_sales = sales['placed']['sales_total']
+                event.pending_order_sales = sales['pending']['sales_total']
+                event.completed_order_tickets = sales['completed']['ticket_count']
+                event.placed_order_tickets = sales['placed']['ticket_count']
+                event.pending_order_tickets = sales['pending']['ticket_count']
+                res_event_json = next((x for x in data if x['id'] == str(event.id)), None)
+                if res_event_json is not None:
+                    res_event_json['attributes']['completed-order-sales'] = sales[
+                        'completed'
+                    ]['sales_total']
+                    res_event_json['attributes']['placed-order-sales'] = sales['placed'][
+                        'sales_total'
+                    ]
+                    res_event_json['attributes']['pending-order-sales'] = sales[
+                        'pending'
+                    ]['sales_total']
+                    res_event_json['attributes']['completed-order-tickets'] = sales[
+                        'completed'
+                    ]['ticket_count']
+                    res_event_json['attributes']['placed-order-tickets'] = sales[
+                        'placed'
+                    ]['ticket_count']
+                    res_event_json['attributes']['pending-order-tickets'] = sales[
+                        'pending'
+                    ]['ticket_count']
+            save_bulk_to_db(events)
 
     methods = ['GET']
     decorators = (api.has_permission('is_admin'),)
@@ -89,5 +115,5 @@ def before_get(self, args, kwargs):
     data_layer = {
         'model': Event,
         'session': db.session,
-        'methods': {'query': query, 'before_get': before_get},
+        'methods': {'query': query, 'after_get': after_get},
     }
diff --git a/app/api/admin_sales/utils.py b/app/api/admin_sales/utils.py
--- a/app/api/admin_sales/utils.py
+++ b/app/api/admin_sales/utils.py
@@ -65,3 +65,22 @@ def summary(event):
     )
     status_codes = ['placed', 'completed', 'pending']
     return {s: status_summary(sales_summary, tickets_summary, s) for s in status_codes}
+
+
+def summary_by_id(event_id):
+    """Returns sales as dictionary for all status codes"""
+    sales_summary = (
+        Order.query.filter_by(event_id=event_id)
+        .with_entities(Order.status, func.sum(Order.amount))
+        .group_by(Order.status)
+        .all()
+    )
+    tickets_summary = (
+        TicketHolder.query.join(Order)
+        .filter(Order.event_id == event_id)
+        .with_entities(Order.status, func.count())
+        .group_by(Order.status)
+        .all()
+    )
+    status_codes = ['placed', 'completed', 'pending']
+    return {s: status_summary(sales_summary, tickets_summary, s) for s in status_codes}
diff --git a/app/api/helpers/db.py b/app/api/helpers/db.py
--- a/app/api/helpers/db.py
+++ b/app/api/helpers/db.py
@@ -6,7 +6,7 @@
 from flask import request
 from flask_rest_jsonapi.exceptions import ObjectNotFound
 from sqlalchemy import func
-from sqlalchemy.exc import IntegrityError
+from sqlalchemy.exc import IntegrityError, SQLAlchemyError
 from sqlalchemy.orm.exc import NoResultFound
 
 from app.models import db
@@ -168,3 +168,20 @@ def get_new_identifier(model=None, length=None):
     if not identifier.isdigit() and count == 0:
         return identifier
     return get_new_identifier(model)
+
+
+def save_bulk_to_db(items, msg="Saved to db"):
+    """Convenience function to wrap a proper DB save
+    :param item: will be saved to database
+    :param msg: Message to log
+    """
+    try:
+        logging.info(msg)
+        db.session.bulk_save_objects(items)
+        logging.info('added to session')
+        db.session.commit()
+        return True
+    except SQLAlchemyError:
+        logging.exception('DB Exception!')
+        db.session.rollback()
+        return False


Test patch: