From 4f638be874caf82d767ad2e1841c39f2d317caa7 Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@gmail.com>
Date: Mon, 7 Feb 2022 13:04:42 +1100
Subject: [PATCH] Handle errors when connecting to currency exchange

- Also adds timeout when connecting
---
 InvenTree/InvenTree/apps.py     | 13 ++++++++-----
 InvenTree/InvenTree/exchange.py | 20 ++++++++++++++++++++
 InvenTree/InvenTree/tasks.py    |  9 ++++++---
 3 files changed, 34 insertions(+), 8 deletions(-)

diff --git a/InvenTree/InvenTree/apps.py b/InvenTree/InvenTree/apps.py
index faef1a6cdb..da2e753952 100644
--- a/InvenTree/InvenTree/apps.py
+++ b/InvenTree/InvenTree/apps.py
@@ -118,20 +118,20 @@ class InvenTreeConfig(AppConfig):
             if last_update is not None:
                 delta = datetime.now().date() - last_update.date()
                 if delta > timedelta(days=1):
-                    print(f"Last update was {last_update}")
+                    logger.info(f"Last update was {last_update}")
                     update = True
             else:
                 # Never been updated
-                print("Exchange backend has never been updated")
+                logger.info("Exchange backend has never been updated")
                 update = True
 
             # Backend currency has changed?
             if not base_currency == backend.base_currency:
-                print(f"Base currency changed from {backend.base_currency} to {base_currency}")
+                logger.info(f"Base currency changed from {backend.base_currency} to {base_currency}")
                 update = True
 
         except (ExchangeBackend.DoesNotExist):
-            print("Exchange backend not found - updating")
+            logger.info("Exchange backend not found - updating")
             update = True
 
         except:
@@ -139,4 +139,7 @@ class InvenTreeConfig(AppConfig):
             return
 
         if update:
-            update_exchange_rates()
+            try:
+                update_exchange_rates()
+            except Exception as e:
+                logger.error(f"Error updating exchange rates: {e}")
diff --git a/InvenTree/InvenTree/exchange.py b/InvenTree/InvenTree/exchange.py
index 4b99953382..a79239568d 100644
--- a/InvenTree/InvenTree/exchange.py
+++ b/InvenTree/InvenTree/exchange.py
@@ -1,3 +1,7 @@
+import certifi
+import ssl
+from urllib.request import urlopen
+
 from common.settings import currency_code_default, currency_codes
 from urllib.error import URLError
 
@@ -24,6 +28,22 @@ class InvenTreeExchange(SimpleExchangeBackend):
         return {
         }
 
+    def get_response(self, **kwargs):
+        """
+        Custom code to get response from server.
+        Note: Adds a 5-second timeout
+        """
+
+        url = self.get_url(**kwargs)
+
+        try:
+            context = ssl.create_default_context(cafile=certifi.where())
+            response = urlopen(url, timeout=5, context=context)
+            return response.read()
+        except:
+            # Returning None here will raise an error upstream
+            return None
+
     def update_rates(self, base_currency=currency_code_default()):
 
         symbols = ','.join(currency_codes())
diff --git a/InvenTree/InvenTree/tasks.py b/InvenTree/InvenTree/tasks.py
index 0a098e5f8c..a76f766120 100644
--- a/InvenTree/InvenTree/tasks.py
+++ b/InvenTree/InvenTree/tasks.py
@@ -269,10 +269,13 @@ def update_exchange_rates():
 
     logger.info(f"Using base currency '{base}'")
 
-    backend.update_rates(base_currency=base)
+    try:
+        backend.update_rates(base_currency=base)
 
-    # Remove any exchange rates which are not in the provided currencies
-    Rate.objects.filter(backend="InvenTreeExchange").exclude(currency__in=currency_codes()).delete()
+        # Remove any exchange rates which are not in the provided currencies
+        Rate.objects.filter(backend="InvenTreeExchange").exclude(currency__in=currency_codes()).delete()
+    except Exception as e:
+        logger.error(f"Error updating exchange rates: {e}")
 
 
 def send_email(subject, body, recipients, from_email=None, html_message=None):